BsFrameAlloc.cpp 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  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. UINT8* FrameAlloc::allocAligned(UINT32 amount, UINT32 alignment)
  62. {
  63. #if BS_DEBUG_MODE
  64. assert(mOwnerThread == BS_THREAD_CURRENT_ID && "Frame allocator called from invalid thread.");
  65. amount += sizeof(UINT32);
  66. #endif
  67. UINT32 alignOffset = alignment - mFreeBlock->mFreePtr & (alignment - 1);
  68. UINT32 freeMem = mFreeBlock->mSize - mFreeBlock->mFreePtr;
  69. if ((amount + alignOffset) > freeMem)
  70. {
  71. // New blocks are allocated on a 16 byte boundary, ensure we enough space is allocated taking into account
  72. // the requested alignment
  73. if (alignment > 16)
  74. alignOffset = alignment - 16;
  75. else
  76. alignOffset = 0;
  77. allocBlock(amount + alignOffset);
  78. }
  79. amount += alignOffset;
  80. UINT8* data = mFreeBlock->alloc(amount);
  81. #if BS_DEBUG_MODE
  82. mTotalAllocBytes += amount;
  83. UINT32* storedSize = reinterpret_cast<UINT32*>(data + alignOffset);
  84. *storedSize = amount;
  85. return data + sizeof(UINT32) + alignOffset;
  86. #else
  87. return data + alignOffset;
  88. #endif
  89. }
  90. void FrameAlloc::dealloc(UINT8* data)
  91. {
  92. // Dealloc is only used for debug and can be removed if needed. All the actual deallocation
  93. // happens in clear()
  94. #if BS_DEBUG_MODE
  95. data -= sizeof(UINT32);
  96. UINT32* storedSize = reinterpret_cast<UINT32*>(data);
  97. mTotalAllocBytes -= *storedSize;
  98. #endif
  99. }
  100. void FrameAlloc::markFrame()
  101. {
  102. void** framePtr = (void**)alloc(sizeof(void*));
  103. *framePtr = mLastFrame;
  104. mLastFrame = framePtr;
  105. }
  106. void FrameAlloc::clear()
  107. {
  108. #if BS_DEBUG_MODE
  109. assert(mOwnerThread == BS_THREAD_CURRENT_ID && "Frame allocator called from invalid thread.");
  110. #endif
  111. if(mLastFrame != nullptr)
  112. {
  113. assert(mBlocks.size() > 0 && mNextBlockIdx > 0);
  114. dealloc(mLastFrame);
  115. UINT8* framePtr = (UINT8*)mLastFrame;
  116. mLastFrame = *(void**)mLastFrame;
  117. #if BS_DEBUG_MODE
  118. framePtr -= sizeof(UINT32);
  119. #endif
  120. UINT32 startBlockIdx = mNextBlockIdx - 1;
  121. UINT32 numFreedBlocks = 0;
  122. for (UINT32 i = startBlockIdx; i >= 0; i--)
  123. {
  124. MemBlock* curBlock = mBlocks[i];
  125. UINT8* blockEnd = curBlock->mData + curBlock->mSize;
  126. if (framePtr >= curBlock->mData && framePtr < blockEnd)
  127. {
  128. UINT8* dataEnd = curBlock->mData + curBlock->mFreePtr;
  129. UINT32 sizeInBlock = (UINT32)(dataEnd - framePtr);
  130. assert(sizeInBlock <= curBlock->mFreePtr);
  131. curBlock->mFreePtr -= sizeInBlock;
  132. if (curBlock->mFreePtr == 0)
  133. {
  134. numFreedBlocks++;
  135. // Reset block counter if we're gonna reallocate this one
  136. if (numFreedBlocks > 1)
  137. mNextBlockIdx = i;
  138. }
  139. break;
  140. }
  141. else
  142. {
  143. curBlock->mFreePtr = 0;
  144. mNextBlockIdx = i;
  145. numFreedBlocks++;
  146. }
  147. }
  148. if (numFreedBlocks > 1)
  149. {
  150. UINT32 totalBytes = 0;
  151. for (UINT32 i = 0; i < numFreedBlocks; i++)
  152. {
  153. MemBlock* curBlock = mBlocks[mNextBlockIdx];
  154. totalBytes += curBlock->mSize;
  155. deallocBlock(curBlock);
  156. mBlocks.erase(mBlocks.begin() + mNextBlockIdx);
  157. }
  158. UINT32 oldNextBlockIdx = mNextBlockIdx;
  159. allocBlock(totalBytes);
  160. // Point to the first non-full block, or if none available then point the the block we just allocated
  161. if (oldNextBlockIdx > 0)
  162. mFreeBlock = mBlocks[oldNextBlockIdx - 1];
  163. }
  164. else
  165. {
  166. mFreeBlock = mBlocks[mNextBlockIdx - 1];
  167. }
  168. }
  169. else
  170. {
  171. #if BS_DEBUG_MODE
  172. if (mTotalAllocBytes.load() > 0)
  173. BS_EXCEPT(InvalidStateException, "Not all frame allocated bytes were properly released.");
  174. #endif
  175. if (mBlocks.size() > 1)
  176. {
  177. // Merge all blocks into one
  178. UINT32 totalBytes = 0;
  179. for (auto& block : mBlocks)
  180. {
  181. totalBytes += block->mSize;
  182. deallocBlock(block);
  183. }
  184. mBlocks.clear();
  185. mNextBlockIdx = 0;
  186. allocBlock(totalBytes);
  187. }
  188. }
  189. }
  190. FrameAlloc::MemBlock* FrameAlloc::allocBlock(UINT32 wantedSize)
  191. {
  192. UINT32 blockSize = mBlockSize;
  193. if(wantedSize > blockSize)
  194. blockSize = wantedSize;
  195. MemBlock* newBlock = nullptr;
  196. while (mNextBlockIdx < mBlocks.size())
  197. {
  198. MemBlock* curBlock = mBlocks[mNextBlockIdx];
  199. if (blockSize <= curBlock->mSize)
  200. {
  201. newBlock = curBlock;
  202. mNextBlockIdx++;
  203. break;
  204. }
  205. else
  206. {
  207. // Found an empty block that doesn't fit our data, delete it
  208. deallocBlock(curBlock);
  209. mBlocks.erase(mBlocks.begin() + mNextBlockIdx);
  210. }
  211. }
  212. if (newBlock == nullptr)
  213. {
  214. UINT8* data = (UINT8*)reinterpret_cast<UINT8*>(bs_alloc_aligned16(blockSize + sizeof(MemBlock)));
  215. newBlock = new (data) MemBlock(blockSize);
  216. data += sizeof(MemBlock);
  217. newBlock->mData = data;
  218. mBlocks.push_back(newBlock);
  219. mNextBlockIdx++;
  220. }
  221. mFreeBlock = newBlock; // If previous block had some empty space it is lost until next "clear"
  222. return newBlock;
  223. }
  224. void FrameAlloc::deallocBlock(MemBlock* block)
  225. {
  226. block->~MemBlock();
  227. bs_free_aligned(block);
  228. }
  229. void FrameAlloc::setOwnerThread(BS_THREAD_ID_TYPE thread)
  230. {
  231. #if BS_DEBUG_MODE
  232. mOwnerThread = thread;
  233. #endif
  234. }
  235. }