2
0

BsFrameAlloc.cpp 7.9 KB

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