BsFrameAlloc.cpp 5.7 KB

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