2
0

BsFrameAlloc.cpp 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. #include "BsFrameAlloc.h"
  2. #include "BsException.h"
  3. namespace BansheeEngine
  4. {
  5. FrameAlloc::MemBlock::MemBlock(UINT32 size)
  6. :mData(nullptr), mFreePtr(0), mSize(size)
  7. { }
  8. FrameAlloc::MemBlock::~MemBlock()
  9. { }
  10. UINT8* FrameAlloc::MemBlock::alloc(UINT32 amount)
  11. {
  12. UINT8* freePtr = &mData[mFreePtr];
  13. mFreePtr += amount;
  14. return freePtr;
  15. }
  16. void FrameAlloc::MemBlock::clear()
  17. {
  18. mFreePtr = 0;
  19. }
  20. FrameAlloc::FrameAlloc(UINT32 blockSize)
  21. :mTotalAllocBytes(0), mFreeBlock(nullptr), mBlockSize(blockSize),
  22. mOwnerThread(BS_THREAD_CURRENT_ID), mLastFrame(nullptr), mNextBlockIdx(0)
  23. {
  24. allocBlock(mBlockSize);
  25. }
  26. FrameAlloc::~FrameAlloc()
  27. {
  28. for(auto& block : mBlocks)
  29. deallocBlock(block);
  30. }
  31. UINT8* FrameAlloc::alloc(UINT32 amount)
  32. {
  33. #if BS_DEBUG_MODE
  34. assert(mOwnerThread == BS_THREAD_CURRENT_ID && "Frame allocator called from invalid thread.");
  35. amount += sizeof(UINT32);
  36. #endif
  37. UINT32 freeMem = mFreeBlock->mSize - mFreeBlock->mFreePtr;
  38. if(amount > freeMem)
  39. allocBlock(amount);
  40. UINT8* data = mFreeBlock->alloc(amount);
  41. #if BS_DEBUG_MODE
  42. mTotalAllocBytes += amount;
  43. UINT32* storedSize = reinterpret_cast<UINT32*>(data);
  44. *storedSize = amount;
  45. return data + sizeof(UINT32);
  46. #else
  47. return data;
  48. #endif
  49. }
  50. void FrameAlloc::dealloc(UINT8* data)
  51. {
  52. // Dealloc is only used for debug and can be removed if needed. All the actual deallocation
  53. // happens in ::clear
  54. #if BS_DEBUG_MODE
  55. data -= sizeof(UINT32);
  56. UINT32* storedSize = reinterpret_cast<UINT32*>(data);
  57. mTotalAllocBytes -= *storedSize;
  58. #endif
  59. }
  60. void FrameAlloc::markFrame()
  61. {
  62. void** framePtr = (void**)alloc(sizeof(void*));
  63. *framePtr = mLastFrame;
  64. mLastFrame = framePtr;
  65. }
  66. void FrameAlloc::clear()
  67. {
  68. assert(mOwnerThread == BS_THREAD_CURRENT_ID && "Frame allocator called from invalid thread.");
  69. if(mLastFrame != nullptr)
  70. {
  71. assert(mBlocks.size() > 0 && mNextBlockIdx > 0);
  72. dealloc(mLastFrame);
  73. UINT8* framePtr = (UINT8*)mLastFrame;
  74. mLastFrame = *(void**)mLastFrame;
  75. #if BS_DEBUG_MODE
  76. framePtr -= sizeof(UINT32);
  77. #endif
  78. UINT32 startBlockIdx = mNextBlockIdx - 1;
  79. UINT32 numFreedBlocks = 0;
  80. for (UINT32 i = startBlockIdx; i >= 0; i--)
  81. {
  82. MemBlock* curBlock = mBlocks[i];
  83. UINT8* blockEnd = curBlock->mData + curBlock->mSize;
  84. if (framePtr >= curBlock->mData && framePtr < blockEnd)
  85. {
  86. UINT8* dataEnd = curBlock->mData + curBlock->mFreePtr;
  87. UINT32 sizeInBlock = (UINT32)(dataEnd - framePtr);
  88. assert(sizeInBlock <= curBlock->mFreePtr);
  89. curBlock->mFreePtr -= sizeInBlock;
  90. if (curBlock->mFreePtr == 0)
  91. {
  92. numFreedBlocks++;
  93. // Reset block counter if we're gonna reallocate this one
  94. if (numFreedBlocks > 1)
  95. mNextBlockIdx = i;
  96. }
  97. break;
  98. }
  99. else
  100. {
  101. curBlock->mFreePtr = 0;
  102. mNextBlockIdx = i;
  103. numFreedBlocks++;
  104. }
  105. }
  106. if (numFreedBlocks > 1)
  107. {
  108. UINT32 totalBytes = 0;
  109. for (UINT32 i = 0; i < numFreedBlocks; i++)
  110. {
  111. MemBlock* curBlock = mBlocks[mNextBlockIdx];
  112. totalBytes += curBlock->mSize;
  113. deallocBlock(curBlock);
  114. mBlocks.erase(mBlocks.begin() + mNextBlockIdx);
  115. }
  116. UINT32 oldNextBlockIdx = mNextBlockIdx;
  117. allocBlock(totalBytes);
  118. // Point to the first non-full block, or if none available then point the the block we just allocated
  119. if (oldNextBlockIdx > 0)
  120. mFreeBlock = mBlocks[oldNextBlockIdx - 1];
  121. }
  122. else
  123. {
  124. mFreeBlock = mBlocks[mNextBlockIdx - 1];
  125. }
  126. }
  127. else
  128. {
  129. #if BS_DEBUG_MODE
  130. if (mTotalAllocBytes.load() > 0)
  131. BS_EXCEPT(InvalidStateException, "Not all frame allocated bytes were properly released.");
  132. #endif
  133. // Merge all blocks into one
  134. UINT32 totalBytes = 0;
  135. for (auto& block : mBlocks)
  136. {
  137. totalBytes += block->mSize;
  138. deallocBlock(block);
  139. }
  140. mBlocks.clear();
  141. mNextBlockIdx = 0;
  142. allocBlock(totalBytes);
  143. }
  144. }
  145. FrameAlloc::MemBlock* FrameAlloc::allocBlock(UINT32 wantedSize)
  146. {
  147. UINT32 blockSize = mBlockSize;
  148. if(wantedSize > blockSize)
  149. blockSize = wantedSize;
  150. MemBlock* newBlock = nullptr;
  151. while (mNextBlockIdx < mBlocks.size())
  152. {
  153. MemBlock* curBlock = mBlocks[mNextBlockIdx];
  154. if (blockSize <= curBlock->mSize)
  155. {
  156. newBlock = curBlock;
  157. mNextBlockIdx++;
  158. break;
  159. }
  160. else
  161. {
  162. // Found an empty block that doesn't fit our data, delete it
  163. deallocBlock(curBlock);
  164. mBlocks.erase(mBlocks.begin() + mNextBlockIdx);
  165. }
  166. }
  167. if (newBlock == nullptr)
  168. {
  169. UINT8* data = (UINT8*)reinterpret_cast<UINT8*>(bs_alloc(blockSize + sizeof(MemBlock)));
  170. newBlock = new (data) MemBlock(blockSize);
  171. data += sizeof(MemBlock);
  172. newBlock->mData = data;
  173. mBlocks.push_back(newBlock);
  174. mNextBlockIdx++;
  175. }
  176. mFreeBlock = newBlock; // If previous block had some empty space it is lost until next "clear"
  177. return newBlock;
  178. }
  179. void FrameAlloc::deallocBlock(MemBlock* block)
  180. {
  181. block->~MemBlock();
  182. bs_free(block);
  183. }
  184. void FrameAlloc::setOwnerThread(BS_THREAD_ID_TYPE thread)
  185. {
  186. #if BS_DEBUG_MODE
  187. mOwnerThread = thread;
  188. #endif
  189. }
  190. }