BsMemStack.h 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  1. #pragma once
  2. #include <stack>
  3. #include <assert.h>
  4. #include "BsStdHeaders.h"
  5. #include "BsThreadDefines.h"
  6. namespace BansheeEngine
  7. {
  8. /**
  9. * @brief Describes a memory stack of a certain block capacity. See "MemoryStack" for more information.
  10. *
  11. * @tparam BlockCapacity Minimum size of a block. Larger blocks mean less memory allocations, but also potentially
  12. * more wasted memory. If an allocation requests more bytes than BlockCapacity, first largest multiple is
  13. * used instead.
  14. */
  15. template <int BlockCapacity = 1024 * 1024>
  16. class MemStackInternal
  17. {
  18. private:
  19. /**
  20. * @brief A single block of memory of "BlockCapacity" size. A pointer to the first
  21. * free address is stored, and a remaining size.
  22. */
  23. class MemBlock
  24. {
  25. public:
  26. MemBlock(UINT32 size)
  27. :mData(nullptr), mFreePtr(0), mSize(size)
  28. { }
  29. ~MemBlock()
  30. { }
  31. /**
  32. * @brief Returns the first free address and increments the free pointer.
  33. * Caller needs to ensure the remaining block size is adequate before
  34. * calling.
  35. */
  36. UINT8* alloc(UINT32 amount)
  37. {
  38. UINT8* freePtr = &mData[mFreePtr];
  39. mFreePtr += amount;
  40. return freePtr;
  41. }
  42. /**
  43. * @brief Deallocates the provided pointer. Deallocation must happen in opposite order
  44. * from allocation otherwise corruption will occur.
  45. *
  46. * @note Pointer to "data" isn't actually needed, but is provided for debug purposes in order to more
  47. * easily track out-of-order deallocations.
  48. */
  49. void dealloc(UINT8* data, UINT32 amount)
  50. {
  51. mFreePtr -= amount;
  52. assert((&mData[mFreePtr]) == data && "Out of order stack deallocation detected. Deallocations need to happen in order opposite of allocations.");
  53. }
  54. UINT8* mData;
  55. UINT32 mFreePtr;
  56. UINT32 mSize;
  57. };
  58. public:
  59. MemStackInternal()
  60. { }
  61. ~MemStackInternal()
  62. {
  63. assert(mBlocks.size() == 0 && "Not all blocks were released before shutting down the stack allocator.");
  64. while(!mBlocks.empty())
  65. {
  66. MemBlock* curPtr = mBlocks.top();
  67. mBlocks.pop();
  68. deallocBlock(curPtr);
  69. }
  70. }
  71. /**
  72. * @brief Allocates the given amount of memory on the stack.
  73. *
  74. * @param amount The amount to allocate in bytes.
  75. *
  76. * @note Allocates the memory in the currently active block if it is large enough,
  77. * otherwise a new block is allocated. If the allocation is larger than
  78. * default block size a separate block will be allocated only for that allocation,
  79. * making it essentially a slower heap allocator.
  80. *
  81. * Each allocation comes with a 4 byte overhead.
  82. */
  83. UINT8* alloc(UINT32 amount)
  84. {
  85. amount += sizeof(UINT32);
  86. MemBlock* topBlock;
  87. if(mBlocks.size() == 0)
  88. topBlock = allocBlock(amount);
  89. else
  90. topBlock = mBlocks.top();
  91. MemBlock* memBlock = nullptr;
  92. UINT32 freeMem = topBlock->mSize - topBlock->mFreePtr;
  93. if(amount <= freeMem)
  94. memBlock = topBlock;
  95. else
  96. memBlock = allocBlock(amount);
  97. UINT8* data = memBlock->alloc(amount);
  98. UINT32* storedSize = reinterpret_cast<UINT32*>(data);
  99. *storedSize = amount;
  100. return data + sizeof(UINT32);
  101. }
  102. /**
  103. * @brief Deallocates the given memory. Data must be deallocated in opposite
  104. * order then when it was allocated.
  105. */
  106. void dealloc(UINT8* data)
  107. {
  108. data -= sizeof(UINT32);
  109. UINT32* storedSize = reinterpret_cast<UINT32*>(data);
  110. MemBlock* topBlock = mBlocks.top();
  111. topBlock->dealloc(data, *storedSize);
  112. if(topBlock->mFreePtr == 0)
  113. {
  114. deallocBlock(topBlock);
  115. mBlocks.pop();
  116. }
  117. }
  118. private:
  119. std::stack<MemBlock*> mBlocks;
  120. /**
  121. * @brief Allocates a new block of memory using a heap allocator. Block will never be
  122. * smaller than "BlockCapacity" no matter the "wantedSize".
  123. */
  124. MemBlock* allocBlock(UINT32 wantedSize)
  125. {
  126. UINT32 blockSize = BlockCapacity;
  127. if(wantedSize > blockSize)
  128. blockSize = wantedSize;
  129. UINT8* data = (UINT8*)reinterpret_cast<UINT8*>(bs_alloc(blockSize + sizeof(MemBlock)));
  130. MemBlock* newBlock = new (data) MemBlock(blockSize);
  131. data += sizeof(MemBlock);
  132. newBlock->mData = data;
  133. mBlocks.push(newBlock);
  134. return newBlock;
  135. }
  136. /**
  137. * @brief Deallocates a block of memory.
  138. */
  139. void deallocBlock(MemBlock* block)
  140. {
  141. block->~MemBlock();
  142. bs_free(block);
  143. }
  144. };
  145. /**
  146. * @brief One of the fastest, but also very limiting type of allocator. All deallocations
  147. * must happen in opposite order from allocations.
  148. *
  149. * @note It's mostly useful when you need to allocate something temporarily on the heap,
  150. * usually something that gets allocated and freed within the same method.
  151. *
  152. * Each allocation comes with a pretty hefty 4 byte memory overhead, so don't use it for small allocations.
  153. *
  154. * Thread safe. But you cannot allocate on one thread and deallocate on another. Threads will keep
  155. * separate stacks internally. Make sure to call beginThread/endThread for any thread this stack is used on.
  156. */
  157. class MemStack
  158. {
  159. public:
  160. /**
  161. * @brief Sets up the stack with the currently active thread. You need to call this
  162. * on any thread before doing any allocations or deallocations
  163. */
  164. static BS_UTILITY_EXPORT void beginThread();
  165. /**
  166. * @brief Cleans up the stack for the current thread. You may not perform any allocations or deallocations
  167. * after this is called, unless you call beginThread again.
  168. */
  169. static BS_UTILITY_EXPORT void endThread();
  170. /**
  171. * @copydoc MemoryStackInternal::alloc
  172. */
  173. static BS_UTILITY_EXPORT UINT8* alloc(UINT32 numBytes);
  174. /**
  175. * @copydoc MemoryStackInternal::dealloc
  176. */
  177. static BS_UTILITY_EXPORT void deallocLast(UINT8* data);
  178. private:
  179. static BS_THREADLOCAL MemStackInternal<1024 * 1024>* ThreadMemStack;
  180. };
  181. /**
  182. * @copydoc MemoryStackInternal::alloc
  183. */
  184. BS_UTILITY_EXPORT inline void* stackAlloc(UINT32 numBytes);
  185. /**
  186. * @brief Allocates enough memory to hold the specified type, on the stack, but
  187. * does not initialize the object.
  188. *
  189. * @see MemoryStackInternal::alloc()
  190. */
  191. template<class T>
  192. T* stackAlloc()
  193. {
  194. return (T*)MemStack::alloc(sizeof(T));
  195. }
  196. /**
  197. * @brief Allocates enough memory to hold N objects of the specified type,
  198. * on the stack, but does not initialize the objects.
  199. *
  200. * @see MemoryStackInternal::alloc()
  201. */
  202. template<class T>
  203. T* stackAllocN(UINT32 count)
  204. {
  205. return (T*)MemStack::alloc(sizeof(T) * count);
  206. }
  207. /**
  208. * @brief Allocates enough memory to hold the specified type, on the stack,
  209. * and initializes the object using the parameterless constructor.
  210. *
  211. * @see MemoryStackInternal::alloc()
  212. */
  213. template<class T>
  214. T* stackConstructN(UINT32 count)
  215. {
  216. T* data = stackAllocN<T>(count);
  217. for(unsigned int i = 0; i < count; i++)
  218. new ((void*)&data[i]) T;
  219. return data;
  220. }
  221. /**
  222. * @brief Destructs and deallocates last allocated entry currently located on stack.
  223. *
  224. * @see MemoryStackInternal::dealloc()
  225. */
  226. template<class T>
  227. void stackDestruct(T* data)
  228. {
  229. data->~T();
  230. MemStack::deallocLast((UINT8*)data);
  231. }
  232. /**
  233. * @brief Destructs an array of objects and deallocates last allocated
  234. * entry currently located on stack.
  235. *
  236. * @see MemoryStackInternal::dealloc()
  237. */
  238. template<class T>
  239. void stackDestructN(T* data, UINT32 count)
  240. {
  241. for(unsigned int i = 0; i < count; i++)
  242. data[i].~T();
  243. MemStack::deallocLast((UINT8*)data);
  244. }
  245. /**
  246. * @copydoc MemoryStackInternal::dealloc()
  247. */
  248. BS_UTILITY_EXPORT inline void stackDeallocLast(void* data);
  249. /**
  250. * @brief Allows use of a stack allocator by using normal new/delete/free/dealloc operators.
  251. *
  252. * @see MemStack
  253. */
  254. class StackAlloc
  255. { };
  256. /**
  257. * @brief Specialized memory allocator implementations that allows use of a
  258. * stack allocator in normal new/delete/free/dealloc operators.
  259. *
  260. * @see MemStack
  261. */
  262. template<>
  263. class MemoryAllocator<StackAlloc> : public MemoryAllocatorBase
  264. {
  265. public:
  266. static inline void* allocate(size_t bytes)
  267. {
  268. stackAlloc((UINT32)bytes);
  269. }
  270. static inline void* allocateArray(size_t bytes, UINT32 count)
  271. {
  272. return stackAlloc((UINT32)(bytes * count));
  273. }
  274. static inline void free(void* ptr)
  275. {
  276. stackDeallocLast(ptr);
  277. }
  278. static inline void freeArray(void* ptr, UINT32 count)
  279. {
  280. stackDeallocLast(ptr);
  281. }
  282. };
  283. }