BsFrameAlloc.cpp 6.8 KB

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