From 331aac73f283a8ebd88d267e9bdbd95bcb185a4b Mon Sep 17 00:00:00 2001 From: MS Date: Wed, 13 Mar 2024 21:44:07 -0400 Subject: [PATCH] Implement MxMemoryPool (#671) * Implement MxMemoryPool * Naming fix * Annotations and size asserts * hex padding --- LEGO1/omni/include/mxbitset.h | 95 +++++++++++++++++++++++++++ LEGO1/omni/include/mxdsbuffer.h | 4 +- LEGO1/omni/include/mxmemorypool.h | 86 +++++++++++++++++++++++++ LEGO1/omni/include/mxstreamer.h | 87 +++++++++++++------------ LEGO1/omni/src/stream/mxdsbuffer.cpp | 96 +++++----------------------- LEGO1/omni/src/stream/mxstreamer.cpp | 16 ++--- 6 files changed, 252 insertions(+), 132 deletions(-) create mode 100644 LEGO1/omni/include/mxbitset.h create mode 100644 LEGO1/omni/include/mxmemorypool.h diff --git a/LEGO1/omni/include/mxbitset.h b/LEGO1/omni/include/mxbitset.h new file mode 100644 index 00000000..415470d8 --- /dev/null +++ b/LEGO1/omni/include/mxbitset.h @@ -0,0 +1,95 @@ +#ifndef MXBITSET_H +#define MXBITSET_H + +#pragma warning(disable : 4237) + +#include "mxtypes.h" + +#include +#include // CHAR_BIT + +template +class MxBitset { +public: + MxBitset() { Tidy(); } + + // SIZE 0x08 + class Reference { + friend class MxBitset; + + public: + Reference& Flip() + { + m_bitset->Flip(m_offset); + return (*this); + } + bool operator~() const { return (!m_bitset->Test(m_offset)); } + operator bool() const { return (m_bitset->Test(m_offset)); } + + private: + Reference(MxBitset& p_bitset, size_t p_offset) : m_bitset(&p_bitset), m_offset(p_offset) {} + MxBitset* m_bitset; // 0x00 + size_t m_offset; // 0x04 + }; + + Reference operator[](size_t p_bit) { return (Reference(*this, p_bit)); } + + MxBitset& Flip(size_t p_bit) + { + if (N <= p_bit) { + Xran(); + } + m_blocks[p_bit / e_bitsPerBlock] ^= 1 << p_bit % e_bitsPerBlock; + return (*this); + } + + size_t Count() + { + // debug only, intentionally unimplemented + return 0; + } + + bool Test(MxU32 p_bit) + { + if (p_bit >= N) { + Xran(); + } + + return (m_blocks[p_bit / e_bitsPerBlock] & (1 << p_bit % e_bitsPerBlock)) != 0; + } + + MxU32 Size() const { return N; } + +private: + void Tidy(MxU32 p_value = 0) + { + for (MxS32 i = e_blocksRequired; i >= 0; --i) { + m_blocks[i] = p_value; + } + + // No need to trim if all bits were zeroed out + if (p_value != 0) { + Trim(); + } + } + + // Apply bit mask to most significant block + void Trim() + { + if (N % e_bitsPerBlock != 0) { + m_blocks[e_blocksRequired] &= ((1 << (N % e_bitsPerBlock)) - 1); + } + } + + void Xran() { assert("invalid MxBitset position" == NULL); } + + // Not a real enum. This is how STL BITSET defines these constants. + enum { + e_bitsPerBlock = CHAR_BIT * sizeof(MxU32), + e_blocksRequired = N == 0 ? 0 : (N - 1) / e_bitsPerBlock + }; + + MxU32 m_blocks[e_blocksRequired + 1]; // 0x00 +}; + +#endif // MXBITSET_H diff --git a/LEGO1/omni/include/mxdsbuffer.h b/LEGO1/omni/include/mxdsbuffer.h index 79dee7d7..47c7bb07 100644 --- a/LEGO1/omni/include/mxdsbuffer.h +++ b/LEGO1/omni/include/mxdsbuffer.h @@ -66,7 +66,7 @@ class MxDSBuffer : public MxCore { inline MxU8* GetBuffer() { return m_pBuffer; } inline MxU8** GetBufferRef() { return &m_pBuffer; } inline undefined4 GetUnknown14() { return m_unk0x14; } - inline MxU16 GetRefCount() { return m_refcount; } + inline MxU16 GetRefCount() { return m_referenceCount; } inline Type GetMode() { return m_mode; } inline MxU32 GetWriteOffset() { return m_writeOffset; } inline MxU32 GetBytesRemaining() { return m_bytesRemaining; } @@ -85,7 +85,7 @@ class MxDSBuffer : public MxCore { undefined4 m_unk0x14; // 0x14 undefined4 m_unk0x18; // 0x18 undefined4 m_unk0x1c; // 0x1c - MxU16 m_refcount; // 0x20 + MxU16 m_referenceCount; // 0x20 Type m_mode; // 0x24 MxU32 m_writeOffset; // 0x28 MxU32 m_bytesRemaining; // 0x2c diff --git a/LEGO1/omni/include/mxmemorypool.h b/LEGO1/omni/include/mxmemorypool.h new file mode 100644 index 00000000..a5023457 --- /dev/null +++ b/LEGO1/omni/include/mxmemorypool.h @@ -0,0 +1,86 @@ +#ifndef MXMEMORYPOOL_H +#define MXMEMORYPOOL_H + +#include "decomp.h" +#include "mxbitset.h" +#include "mxtypes.h" + +#include + +template +class MxMemoryPool { +public: + MxMemoryPool() : m_pool(NULL), m_blockSize(BS) {} + ~MxMemoryPool() { delete[] m_pool; } + + MxResult Allocate(); + MxU8* Get(); + void Release(MxU8*); + + MxU32 GetPoolSize() const { return m_blockRef.Size(); } + +private: + MxU8* m_pool; // 0x00 + MxU32 m_blockSize; // 0x04 + MxBitset m_blockRef; // 0x08 +}; + +template +MxResult MxMemoryPool::Allocate() +{ + assert(m_pool == NULL); + assert(m_blockSize); + assert(m_blockRef.Size()); + + m_pool = new MxU8[GetPoolSize() * m_blockSize * 1024]; + assert(m_pool); + + return m_pool ? SUCCESS : FAILURE; +} + +template +MxU8* MxMemoryPool::Get() +{ + assert(m_pool != NULL); + assert(m_blockSize); + assert(m_blockRef.Size()); + + for (MxU32 i = 0; i < GetPoolSize(); i++) { + if (!m_blockRef[i]) { + m_blockRef[i].Flip(); + +#ifdef _DEBUG + // TODO: This is actually some debug print function, but + // we just need any func with variatic args to eliminate diff noise. + printf("Get> %d pool: busy %d blocks\n", m_blockSize, m_blockRef.Count()); +#endif + + return &m_pool[i * m_blockSize * 1024]; + } + } + + return NULL; +} + +template +void MxMemoryPool::Release(MxU8* p_buf) +{ + assert(m_pool != NULL); + assert(m_blockSize); + assert(m_blockRef.Size()); + + MxU32 i = (MxU32) (p_buf - m_pool) / (m_blockSize * 1024); + + assert(i >= 0 && i < GetPoolSize()); + assert(m_blockRef[i]); + + if (m_blockRef[i]) { + m_blockRef[i].Flip(); + } + +#ifdef _DEBUG + printf("Release> %d pool: busy %d blocks\n", m_blockSize, m_blockRef.Count()); +#endif +} + +#endif // MXMEMORYPOOL_H diff --git a/LEGO1/omni/include/mxstreamer.h b/LEGO1/omni/include/mxstreamer.h index 2308981d..f0dd9b7e 100644 --- a/LEGO1/omni/include/mxstreamer.h +++ b/LEGO1/omni/include/mxstreamer.h @@ -4,50 +4,16 @@ #include "decomp.h" #include "mxcore.h" #include "mxdsobject.h" +#include "mxmemorypool.h" #include "mxnotificationparam.h" #include "mxstreamcontroller.h" #include "mxtypes.h" +#include #include -// NOTE: This feels like some kind of templated class, maybe something from the -// STL. But I haven't figured out what yet (it's definitely not a vector). -class MxStreamerSubClass1 { -public: - inline MxStreamerSubClass1(undefined4 p_size) - { - m_buffer = NULL; - m_size = p_size; - undefined4* ptr = &m_unk0x08; - for (int i = 0; i >= 0; i--) { - ptr[i] = 0; - } - } - - // FUNCTION: LEGO1 0x100b9110 - ~MxStreamerSubClass1() { delete[] m_buffer; } - - undefined4 GetSize() const { return m_size; } - - void SetBuffer(undefined* p_buf) { m_buffer = p_buf; } - inline undefined* GetBuffer() const { return m_buffer; } - inline undefined* GetUnk08Ref() const { return (undefined*) &m_unk0x08; } - -private: - undefined* m_buffer; - undefined4 m_size; - undefined4 m_unk0x08; -}; - -class MxStreamerSubClass2 : public MxStreamerSubClass1 { -public: - inline MxStreamerSubClass2() : MxStreamerSubClass1(0x40) {} -}; - -class MxStreamerSubClass3 : public MxStreamerSubClass1 { -public: - inline MxStreamerSubClass3() : MxStreamerSubClass1(0x80) {} -}; +typedef MxMemoryPool<64, 22> MxMemoryPool64; +typedef MxMemoryPool<128, 2> MxMemoryPool128; // VTABLE: LEGO1 0x100dc760 class MxStreamerNotification : public MxNotificationParam { @@ -105,13 +71,44 @@ class MxStreamer : public MxCore { MxResult FUN_100b99b0(MxDSAction* p_action); MxResult DeleteObject(MxDSAction* p_dsAction); - inline const MxStreamerSubClass2& GetSubclass1() { return m_subclass1; } - inline const MxStreamerSubClass3& GetSubclass2() { return m_subclass2; } + MxU8* GetMemoryBlock(MxU32 p_blockSize) + { + switch (p_blockSize) { + case 0x40: + return m_pool64.Get(); + + case 0x80: + return m_pool128.Get(); + + default: + assert("Invalid block size for memory pool" == NULL); + break; + } + + return NULL; + } + + void ReleaseMemoryBlock(MxU8* p_block, MxU32 p_blockSize) + { + switch (p_blockSize) { + case 0x40: + m_pool64.Release(p_block); + break; + + case 0x80: + m_pool128.Release(p_block); + break; + + default: + assert("Invalid block size for memory pool" == NULL); + break; + } + } private: list m_openStreams; // 0x08 - MxStreamerSubClass2 m_subclass1; // 0x14 - MxStreamerSubClass3 m_subclass2; // 0x20 + MxMemoryPool64 m_pool64; // 0x14 + MxMemoryPool128 m_pool128; // 0x20 }; // clang-format off @@ -119,6 +116,12 @@ class MxStreamer : public MxCore { // list >::~list > // clang-format on +// TEMPLATE: LEGO1 0x100b9100 +// MxMemoryPool<64,22>::~MxMemoryPool<64,22> + +// TEMPLATE: LEGO1 0x100b9110 +// MxMemoryPool<128,2>::~MxMemoryPool<128,2> + // SYNTHETIC: LEGO1 0x100b9120 // MxStreamer::`scalar deleting destructor' diff --git a/LEGO1/omni/src/stream/mxdsbuffer.cpp b/LEGO1/omni/src/stream/mxdsbuffer.cpp index a42577c7..d3f075dd 100644 --- a/LEGO1/omni/src/stream/mxdsbuffer.cpp +++ b/LEGO1/omni/src/stream/mxdsbuffer.cpp @@ -14,7 +14,7 @@ DECOMP_SIZE_ASSERT(MxDSBuffer, 0x34); // FUNCTION: LEGO1 0x100c6470 MxDSBuffer::MxDSBuffer() { - m_refcount = 0; + m_referenceCount = 0; m_pBuffer = NULL; m_pIntoBuffer = NULL; m_pIntoBuffer2 = NULL; @@ -30,6 +30,8 @@ MxDSBuffer::MxDSBuffer() // FUNCTION: LEGO1 0x100c6530 MxDSBuffer::~MxDSBuffer() { + assert(m_referenceCount == 0); + if (m_pBuffer != NULL) { switch (m_mode) { case e_allocate: @@ -37,39 +39,12 @@ MxDSBuffer::~MxDSBuffer() delete[] m_pBuffer; break; - case e_chunk: { - MxU32 offset = m_writeOffset / 1024; - MxStreamer* streamer = Streamer(); + case e_chunk: + Streamer()->ReleaseMemoryBlock(m_pBuffer, m_writeOffset / 1024); + break; - switch (offset) { - case 0x40: { - MxU32 a = - (m_pBuffer - streamer->GetSubclass1().GetBuffer()) / (streamer->GetSubclass1().GetSize() << 10); - - MxU32 bit = 1 << ((MxU8) a & 0x1f); - MxU32 index = (a & ~0x18u) >> 3; - - if ((*(MxU32*) (&streamer->GetSubclass1().GetUnk08Ref()[index])) & bit) { - MxU32* ptr = (MxU32*) (&streamer->GetSubclass1().GetUnk08Ref()[index]); - *ptr = *ptr ^ bit; - } - break; - } - case 0x80: { - MxU32 a = - (m_pBuffer - streamer->GetSubclass2().GetBuffer()) / (streamer->GetSubclass2().GetSize() << 10); - - MxU32 bit = 1 << ((MxU8) a & 0x1f); - MxU32 index = (a & ~0x18u) >> 3; - - if ((*(MxU32*) (&streamer->GetSubclass2().GetUnk08Ref()[index])) & bit) { - MxU32* ptr = (MxU32*) (&streamer->GetSubclass2().GetUnk08Ref()[index]); - *ptr = *ptr ^ bit; - } - break; - } - } - } + case e_preallocated: + break; } } @@ -85,58 +60,21 @@ MxResult MxDSBuffer::AllocateBuffer(MxU32 p_bufferSize, Type p_mode) switch (p_mode) { case e_allocate: m_pBuffer = new MxU8[p_bufferSize]; + assert(m_pBuffer); // m_firstRiffChunk? break; - case e_chunk: { - MxStreamer* streamer = Streamer(); - - switch (p_bufferSize / 1024) { - case 0x40: { - for (MxU32 i = 0; i < 22; i++) { - if (((1 << (i & 0x1f)) & (*(MxU32*) &streamer->GetSubclass1().GetUnk08Ref()[(i & ~0x18u) >> 3])) == 0) { - MxU32* ptr = (MxU32*) &streamer->GetSubclass1().GetUnk08Ref()[(i & 0xffffffe7) >> 3]; - - *ptr = *ptr ^ 1 << (i & 0x1f); - - m_pBuffer = - (MxU8*) (streamer->GetSubclass1().GetSize() * i * 0x400 + streamer->GetSubclass1().GetBuffer()); - goto done; - } - } - - m_pBuffer = NULL; - break; - } - case 0x80: { - for (MxU32 i = 0; i < 2; i++) { - if (((1 << (i & 0x1f)) & (*(MxU32*) &streamer->GetSubclass2().GetUnk08Ref()[(i & ~0x18u) >> 3])) == 0) { - MxU32* ptr = (MxU32*) &streamer->GetSubclass2().GetUnk08Ref()[(i & 0xffffffe7) >> 3]; - - *ptr = *ptr ^ 1 << (i & 0x1f); - - m_pBuffer = - (MxU8*) (streamer->GetSubclass2().GetSize() * i * 0x400 + streamer->GetSubclass2().GetBuffer()); - goto done; - } - } - - m_pBuffer = NULL; - break; - } - default: - m_pBuffer = NULL; - } - } + case e_chunk: + m_pBuffer = Streamer()->GetMemoryBlock(p_bufferSize / 1024); + break; } -done: m_pIntoBuffer = m_pBuffer; m_pIntoBuffer2 = m_pBuffer; if (m_pBuffer != NULL) { - m_mode = p_mode; m_bytesRemaining = p_bufferSize; - m_writeOffset = p_bufferSize; + m_writeOffset = m_bytesRemaining; + m_mode = p_mode; result = SUCCESS; } @@ -444,8 +382,8 @@ MxU8* MxDSBuffer::SkipToData() // FUNCTION: LEGO1 0x100c6ec0 MxU8 MxDSBuffer::ReleaseRef(MxDSChunk*) { - if (m_refcount != 0) { - m_refcount--; + if (m_referenceCount != 0) { + m_referenceCount--; } return 0; } @@ -454,7 +392,7 @@ MxU8 MxDSBuffer::ReleaseRef(MxDSChunk*) void MxDSBuffer::AddRef(MxDSChunk* p_chunk) { if (p_chunk) { - m_refcount++; + m_referenceCount++; } } diff --git a/LEGO1/omni/src/stream/mxstreamer.cpp b/LEGO1/omni/src/stream/mxstreamer.cpp index 1cede5a7..9b03fd47 100644 --- a/LEGO1/omni/src/stream/mxstreamer.cpp +++ b/LEGO1/omni/src/stream/mxstreamer.cpp @@ -8,6 +8,10 @@ #include DECOMP_SIZE_ASSERT(MxStreamer, 0x2c); +DECOMP_SIZE_ASSERT(MxMemoryPool64, 0x0c); +DECOMP_SIZE_ASSERT(MxMemoryPool128, 0x0c); +DECOMP_SIZE_ASSERT(MxBitset<22>, 0x04); +DECOMP_SIZE_ASSERT(MxBitset<2>, 0x04); // FUNCTION: LEGO1 0x100b8f00 MxStreamer::MxStreamer() @@ -18,17 +22,11 @@ MxStreamer::MxStreamer() // FUNCTION: LEGO1 0x100b9190 MxResult MxStreamer::Create() { - undefined* b = new undefined[m_subclass1.GetSize() * 0x5800]; - m_subclass1.SetBuffer(b); - if (b) { - b = new undefined[m_subclass2.GetSize() * 0x800]; - m_subclass2.SetBuffer(b); - if (b) { - return SUCCESS; - } + if (m_pool64.Allocate() || m_pool128.Allocate()) { + return FAILURE; } - return FAILURE; + return SUCCESS; } // FUNCTION: LEGO1 0x100b91d0