BsFrameAlloc.cpp 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  1. //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
  2. //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
  3. #include "Prerequisites/BsPrerequisitesUtil.h"
  4. #include "Allocators/BsFrameAlloc.h"
  5. #include "Error/BsException.h"
  6. namespace bs
  7. {
  8. FrameAlloc::MemBlock::MemBlock(UINT32 size)
  9. :mData(nullptr), mFreePtr(0), mSize(size)
  10. { }
  11. FrameAlloc::MemBlock::~MemBlock()
  12. { }
  13. UINT8* FrameAlloc::MemBlock::alloc(UINT32 amount)
  14. {
  15. UINT8* freePtr = &mData[mFreePtr];
  16. mFreePtr += amount;
  17. return freePtr;
  18. }
  19. void FrameAlloc::MemBlock::clear()
  20. {
  21. mFreePtr = 0;
  22. }
  23. #if BS_DEBUG_MODE
  24. FrameAlloc::FrameAlloc(UINT32 blockSize)
  25. :mBlockSize(blockSize), mFreeBlock(nullptr), mNextBlockIdx(0), mTotalAllocBytes(0),
  26. mLastFrame(nullptr)
  27. {
  28. }
  29. #else
  30. FrameAlloc::FrameAlloc(UINT32 blockSize)
  31. : mBlockSize(blockSize), mFreeBlock(nullptr), mNextBlockIdx(0), mTotalAllocBytes(0), mLastFrame(nullptr)
  32. {
  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. amount += sizeof(UINT32);
  44. #endif
  45. UINT32 freeMem = 0;
  46. if(mFreeBlock != nullptr)
  47. 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. amount += sizeof(UINT32);
  64. #endif
  65. UINT32 freeMem = 0;
  66. UINT32 freePtr = 0;
  67. if(mFreeBlock != nullptr)
  68. {
  69. freeMem = mFreeBlock->mSize - mFreeBlock->mFreePtr;
  70. #if BS_DEBUG_MODE
  71. freePtr = mFreeBlock->mFreePtr + sizeof(UINT32);
  72. #else
  73. freePtr = mFreeBlock->mFreePtr;
  74. #endif
  75. }
  76. UINT32 alignOffset = (alignment - (freePtr & (alignment - 1))) & (alignment - 1);
  77. if ((amount + alignOffset) > freeMem)
  78. {
  79. // New blocks are allocated on a 16 byte boundary, ensure enough space is allocated taking into account
  80. // the requested alignment
  81. #if BS_DEBUG_MODE
  82. alignOffset = (alignment - (sizeof(UINT32) & (alignment - 1))) & (alignment - 1);
  83. #else
  84. if (alignment > 16)
  85. alignOffset = alignment - 16;
  86. else
  87. alignOffset = 0;
  88. #endif
  89. allocBlock(amount + alignOffset);
  90. }
  91. amount += alignOffset;
  92. UINT8* data = mFreeBlock->alloc(amount);
  93. #if BS_DEBUG_MODE
  94. mTotalAllocBytes += amount;
  95. UINT32* storedSize = reinterpret_cast<UINT32*>(data + alignOffset);
  96. *storedSize = amount;
  97. return data + sizeof(UINT32) + alignOffset;
  98. #else
  99. return data + alignOffset;
  100. #endif
  101. }
  102. void FrameAlloc::free(UINT8* data)
  103. {
  104. // Dealloc is only used for debug and can be removed if needed. All the actual deallocation
  105. // happens in clear()
  106. #if BS_DEBUG_MODE
  107. data -= sizeof(UINT32);
  108. UINT32* storedSize = reinterpret_cast<UINT32*>(data);
  109. mTotalAllocBytes -= *storedSize;
  110. #endif
  111. }
  112. void FrameAlloc::markFrame()
  113. {
  114. void** framePtr = (void**)alloc(sizeof(void*));
  115. *framePtr = mLastFrame;
  116. mLastFrame = framePtr;
  117. }
  118. void FrameAlloc::clear()
  119. {
  120. if(mLastFrame != nullptr)
  121. {
  122. assert(mBlocks.size() > 0 && mNextBlockIdx > 0);
  123. free((UINT8*)mLastFrame);
  124. UINT8* framePtr = (UINT8*)mLastFrame;
  125. mLastFrame = *(void**)mLastFrame;
  126. #if BS_DEBUG_MODE
  127. framePtr -= sizeof(UINT32);
  128. #endif
  129. UINT32 startBlockIdx = mNextBlockIdx - 1;
  130. UINT32 numFreedBlocks = 0;
  131. for (INT32 i = startBlockIdx; i >= 0; i--)
  132. {
  133. MemBlock* curBlock = mBlocks[i];
  134. UINT8* blockEnd = curBlock->mData + curBlock->mSize;
  135. if (framePtr >= curBlock->mData && framePtr < blockEnd)
  136. {
  137. UINT8* dataEnd = curBlock->mData + curBlock->mFreePtr;
  138. UINT32 sizeInBlock = (UINT32)(dataEnd - framePtr);
  139. assert(sizeInBlock <= curBlock->mFreePtr);
  140. curBlock->mFreePtr -= sizeInBlock;
  141. if (curBlock->mFreePtr == 0)
  142. {
  143. numFreedBlocks++;
  144. // Reset block counter if we're gonna reallocate this one
  145. if (numFreedBlocks > 1)
  146. mNextBlockIdx = (UINT32)i;
  147. }
  148. break;
  149. }
  150. else
  151. {
  152. curBlock->mFreePtr = 0;
  153. mNextBlockIdx = (UINT32)i;
  154. numFreedBlocks++;
  155. }
  156. }
  157. if (numFreedBlocks > 1)
  158. {
  159. UINT32 totalBytes = 0;
  160. for (UINT32 i = 0; i < numFreedBlocks; i++)
  161. {
  162. MemBlock* curBlock = mBlocks[mNextBlockIdx];
  163. totalBytes += curBlock->mSize;
  164. deallocBlock(curBlock);
  165. mBlocks.erase(mBlocks.begin() + mNextBlockIdx);
  166. }
  167. UINT32 oldNextBlockIdx = mNextBlockIdx;
  168. allocBlock(totalBytes);
  169. // Point to the first non-full block, or if none available then point the the block we just allocated
  170. if (oldNextBlockIdx > 0)
  171. mFreeBlock = mBlocks[oldNextBlockIdx - 1];
  172. }
  173. else
  174. {
  175. mFreeBlock = mBlocks[mNextBlockIdx - 1];
  176. }
  177. }
  178. else
  179. {
  180. #if BS_DEBUG_MODE
  181. if (mTotalAllocBytes.load() > 0)
  182. BS_EXCEPT(InvalidStateException, "Not all frame allocated bytes were properly released.");
  183. #endif
  184. if (mBlocks.size() > 1)
  185. {
  186. // Merge all blocks into one
  187. UINT32 totalBytes = 0;
  188. for (auto& block : mBlocks)
  189. {
  190. totalBytes += block->mSize;
  191. deallocBlock(block);
  192. }
  193. mBlocks.clear();
  194. mNextBlockIdx = 0;
  195. allocBlock(totalBytes);
  196. }
  197. else if(mBlocks.size() > 0)
  198. mBlocks[0]->mFreePtr = 0;
  199. }
  200. }
  201. FrameAlloc::MemBlock* FrameAlloc::allocBlock(UINT32 wantedSize)
  202. {
  203. UINT32 blockSize = mBlockSize;
  204. if(wantedSize > blockSize)
  205. blockSize = wantedSize;
  206. MemBlock* newBlock = nullptr;
  207. while (mNextBlockIdx < mBlocks.size())
  208. {
  209. MemBlock* curBlock = mBlocks[mNextBlockIdx];
  210. if (blockSize <= curBlock->mSize)
  211. {
  212. newBlock = curBlock;
  213. mNextBlockIdx++;
  214. break;
  215. }
  216. else
  217. {
  218. // Found an empty block that doesn't fit our data, delete it
  219. deallocBlock(curBlock);
  220. mBlocks.erase(mBlocks.begin() + mNextBlockIdx);
  221. }
  222. }
  223. if (newBlock == nullptr)
  224. {
  225. UINT32 alignOffset = 16 - (sizeof(MemBlock) & (16 - 1));
  226. UINT8* data = (UINT8*)reinterpret_cast<UINT8*>(bs_alloc_aligned16(blockSize + sizeof(MemBlock) + alignOffset));
  227. newBlock = new (data) MemBlock(blockSize);
  228. data += sizeof(MemBlock) + alignOffset;
  229. newBlock->mData = data;
  230. mBlocks.push_back(newBlock);
  231. mNextBlockIdx++;
  232. }
  233. mFreeBlock = newBlock; // If previous block had some empty space it is lost until next "clear"
  234. return newBlock;
  235. }
  236. void FrameAlloc::deallocBlock(MemBlock* block)
  237. {
  238. block->~MemBlock();
  239. bs_free_aligned(block);
  240. }
  241. void FrameAlloc::setOwnerThread(ThreadId thread)
  242. {
  243. }
  244. BS_THREADLOCAL FrameAlloc* _GlobalFrameAlloc = nullptr;
  245. BS_UTILITY_EXPORT FrameAlloc& gFrameAlloc()
  246. {
  247. if (_GlobalFrameAlloc == nullptr)
  248. {
  249. // Note: This will leak memory but since it should exist throughout the entirety
  250. // of runtime it should only leak on shutdown when the OS will free it anyway.
  251. _GlobalFrameAlloc = new FrameAlloc();
  252. }
  253. return *_GlobalFrameAlloc;
  254. }
  255. BS_UTILITY_EXPORT UINT8* bs_frame_alloc(UINT32 numBytes)
  256. {
  257. return gFrameAlloc().alloc(numBytes);
  258. }
  259. BS_UTILITY_EXPORT UINT8* bs_frame_alloc_aligned(UINT32 count, UINT32 align)
  260. {
  261. return gFrameAlloc().allocAligned(count, align);
  262. }
  263. BS_UTILITY_EXPORT void bs_frame_free(void* data)
  264. {
  265. gFrameAlloc().free((UINT8*)data);
  266. }
  267. BS_UTILITY_EXPORT void bs_frame_free_aligned(void* data)
  268. {
  269. gFrameAlloc().free((UINT8*)data);
  270. }
  271. BS_UTILITY_EXPORT void bs_frame_mark()
  272. {
  273. gFrameAlloc().markFrame();
  274. }
  275. BS_UTILITY_EXPORT void bs_frame_clear()
  276. {
  277. gFrameAlloc().clear();
  278. }
  279. }