BsFrameAlloc.cpp 6.8 KB

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