BsFrameAlloc.cpp 5.2 KB

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