BsFrameAlloc.h 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472
  1. //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
  2. //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
  3. #pragma once
  4. #include <limits>
  5. #include <new> /* For 'placement new' */
  6. #include "Prerequisites/BsPlatformDefines.h"
  7. #include "Prerequisites/BsTypes.h"
  8. #include "Prerequisites/BsStdHeaders.h"
  9. #include "Threading/BsThreadDefines.h"
  10. namespace bs
  11. {
  12. /** @addtogroup Internal-Utility
  13. * @{
  14. */
  15. /** @addtogroup Memory-Internal
  16. * @{
  17. */
  18. /**
  19. * Frame allocator. Performs very fast allocations but can only free all of its memory at once. Perfect for allocations
  20. * that last just a single frame.
  21. *
  22. * @note Not thread safe with an exception. alloc() and clear() methods need to be called from the same thread.
  23. * dealloc() is thread safe and can be called from any thread.
  24. */
  25. class BS_UTILITY_EXPORT FrameAlloc
  26. {
  27. private:
  28. /** A single block of memory within a frame allocator. */
  29. class MemBlock
  30. {
  31. public:
  32. MemBlock(UINT32 size);
  33. ~MemBlock();
  34. /** Allocates a piece of memory within the block. Caller must ensure the block has enough empty space. */
  35. UINT8* alloc(UINT32 amount);
  36. /** Releases all allocations within a block but doesn't actually free the memory. */
  37. void clear();
  38. UINT8* mData;
  39. UINT32 mFreePtr;
  40. UINT32 mSize;
  41. };
  42. public:
  43. FrameAlloc(UINT32 blockSize = 1024 * 1024);
  44. ~FrameAlloc();
  45. /**
  46. * Allocates a new block of memory of the specified size.
  47. *
  48. * @param[in] amount Amount of memory to allocate, in bytes.
  49. *
  50. * @note Not thread safe.
  51. */
  52. UINT8* alloc(UINT32 amount);
  53. /**
  54. * Allocates a new block of memory of the specified size aligned to the specified boundary. If the aligment is less
  55. * or equal to 16 it is more efficient to use the allocAligned16() alternative of this method.
  56. *
  57. * @param[in] amount Amount of memory to allocate, in bytes.
  58. * @param[in] alignment Alignment of the allocated memory. Must be power of two.
  59. *
  60. * @note Not thread safe.
  61. */
  62. UINT8* allocAligned(UINT32 amount, UINT32 alignment);
  63. /**
  64. * Allocates and constructs a new object.
  65. *
  66. * @note Not thread safe.
  67. */
  68. template<class T, class... Args>
  69. T* construct(Args &&...args)
  70. {
  71. return new ((T*)alloc(sizeof(T))) T(std::forward<Args>(args)...);
  72. }
  73. /**
  74. * Deallocates a previously allocated block of memory.
  75. *
  76. * @note
  77. * No deallocation is actually done here. This method is only used for debug purposes so it is easier to track
  78. * down memory leaks and corruption.
  79. * @note
  80. * Thread safe.
  81. */
  82. void free(UINT8* data);
  83. /**
  84. * Deallocates and destructs a previously allocated object.
  85. *
  86. * @note
  87. * No deallocation is actually done here. This method is only used to call the destructor and for debug purposes
  88. * so it is easier to track down memory leaks and corruption.
  89. * @note
  90. * Thread safe.
  91. */
  92. template<class T>
  93. void free(T* obj)
  94. {
  95. if (obj != nullptr)
  96. obj->~T();
  97. free((UINT8*)obj);
  98. }
  99. /** Starts a new frame. Next call to clear() will only clear memory allocated past this point. */
  100. void markFrame();
  101. /**
  102. * Deallocates all allocated memory since the last call to markFrame() (or all the memory if there was no call
  103. * to markFrame()).
  104. *
  105. * @note Not thread safe.
  106. */
  107. void clear();
  108. /**
  109. * Changes the frame allocator owner thread. After the owner thread has changed only allocations from that thread
  110. * can be made.
  111. */
  112. void setOwnerThread(ThreadId thread);
  113. private:
  114. UINT32 mBlockSize;
  115. Vector<MemBlock*> mBlocks;
  116. MemBlock* mFreeBlock;
  117. UINT32 mNextBlockIdx;
  118. std::atomic<UINT32> mTotalAllocBytes;
  119. void* mLastFrame;
  120. #if BS_DEBUG_MODE
  121. ThreadId mOwnerThread;
  122. #endif
  123. /**
  124. * Allocates a dynamic block of memory of the wanted size. The exact allocation size might be slightly higher in
  125. * order to store block meta data.
  126. */
  127. MemBlock* allocBlock(UINT32 wantedSize);
  128. /** Frees a memory block. */
  129. void deallocBlock(MemBlock* block);
  130. };
  131. /**
  132. * Version of FrameAlloc that allows blocks size to be provided through the template argument instead of the
  133. * constructor. */
  134. template<int BlockSize>
  135. class TFrameAlloc : public FrameAlloc
  136. {
  137. public:
  138. TFrameAlloc()
  139. :FrameAlloc(BlockSize)
  140. { }
  141. };
  142. /** Allocator for the standard library that internally uses a frame allocator. */
  143. template <class T>
  144. class StdFrameAlloc
  145. {
  146. public:
  147. typedef T value_type;
  148. typedef value_type* pointer;
  149. typedef const value_type* const_pointer;
  150. typedef value_type& reference;
  151. typedef const value_type& const_reference;
  152. typedef std::size_t size_type;
  153. typedef std::ptrdiff_t difference_type;
  154. StdFrameAlloc() noexcept
  155. :mFrameAlloc(nullptr)
  156. { }
  157. StdFrameAlloc(FrameAlloc* alloc) noexcept
  158. :mFrameAlloc(alloc)
  159. { }
  160. template<class U> StdFrameAlloc(const StdFrameAlloc<U>& alloc) noexcept
  161. :mFrameAlloc(alloc.mFrameAlloc)
  162. { }
  163. template<class U> bool operator==(const StdFrameAlloc<U>&) const noexcept { return true; }
  164. template<class U> bool operator!=(const StdFrameAlloc<U>&) const noexcept { return false; }
  165. template<class U> class rebind { public: typedef StdFrameAlloc<U> other; };
  166. /** Allocate but don't initialize number elements of type T.*/
  167. T* allocate(const size_t num) const
  168. {
  169. if (num == 0)
  170. return nullptr;
  171. if (num > static_cast<size_t>(-1) / sizeof(T))
  172. return nullptr; // Error
  173. void* const pv = mFrameAlloc->alloc((UINT32)(num * sizeof(T)));
  174. if (!pv)
  175. return nullptr; // Error
  176. return static_cast<T*>(pv);
  177. }
  178. /** Deallocate storage p of deleted elements. */
  179. void deallocate(T* p, size_t num) const noexcept
  180. {
  181. mFrameAlloc->free((UINT8*)p);
  182. }
  183. FrameAlloc* mFrameAlloc;
  184. size_t max_size() const { return std::numeric_limits<size_type>::max() / sizeof(T); }
  185. void construct(pointer p, const_reference t) { new (p) T(t); }
  186. void destroy(pointer p) { p->~T(); }
  187. template<class U, class... Args>
  188. void construct(U* p, Args&&... args) { new(p) U(std::forward<Args>(args)...); }
  189. };
  190. /** Return that all specializations of this allocator are interchangeable. */
  191. template <class T1, class T2>
  192. bool operator== (const StdFrameAlloc<T1>&,
  193. const StdFrameAlloc<T2>&) throw() {
  194. return true;
  195. }
  196. /** Return that all specializations of this allocator are interchangeable. */
  197. template <class T1, class T2>
  198. bool operator!= (const StdFrameAlloc<T1>&,
  199. const StdFrameAlloc<T2>&) throw() {
  200. return false;
  201. }
  202. /** @} */
  203. /** @} */
  204. /** @addtogroup Memory
  205. * @{
  206. */
  207. /**
  208. * Returns a global, application wide FrameAlloc. Each thread gets its own frame allocator.
  209. *
  210. * @note Thread safe.
  211. */
  212. BS_UTILITY_EXPORT FrameAlloc& gFrameAlloc();
  213. /**
  214. * Allocates some memory using the global frame allocator.
  215. *
  216. * @param[in] numBytes Number of bytes to allocate.
  217. */
  218. BS_UTILITY_EXPORT UINT8* bs_frame_alloc(UINT32 numBytes);
  219. /**
  220. * Allocates the specified number of bytes aligned to the provided boundary, using the global frame allocator. Boundary
  221. * is in bytes and must be a power of two.
  222. */
  223. BS_UTILITY_EXPORT UINT8* bs_frame_alloc_aligned(UINT32 count, UINT32 align);
  224. /**
  225. * Deallocates memory allocated with the global frame allocator.
  226. *
  227. * @note Must be called on the same thread the memory was allocated on.
  228. */
  229. BS_UTILITY_EXPORT void bs_frame_free(void* data);
  230. /**
  231. * Frees memory previously allocated with bs_frame_alloc_aligned().
  232. *
  233. * @note Must be called on the same thread the memory was allocated on.
  234. */
  235. BS_UTILITY_EXPORT void bs_frame_free_aligned(void* data);
  236. /**
  237. * Allocates enough memory to hold the object of specified type using the global frame allocator, but does not
  238. * construct the object.
  239. */
  240. template<class T>
  241. T* bs_frame_alloc()
  242. {
  243. return (T*)bs_frame_alloc(sizeof(T));
  244. }
  245. /**
  246. * Allocates enough memory to hold N objects of specified type using the global frame allocator, but does not
  247. * construct the object.
  248. */
  249. template<class T>
  250. T* bs_frame_alloc(UINT32 count)
  251. {
  252. return (T*)bs_frame_alloc(sizeof(T) * count);
  253. }
  254. /**
  255. * Allocates enough memory to hold the object(s) of specified type using the global frame allocator,
  256. * and constructs them.
  257. */
  258. template<class T>
  259. T* bs_frame_new(UINT32 count = 0)
  260. {
  261. T* data = bs_frame_alloc<T>(count);
  262. for(unsigned int i = 0; i < count; i++)
  263. new ((void*)&data[i]) T;
  264. return data;
  265. }
  266. /**
  267. * Allocates enough memory to hold the object(s) of specified type using the global frame allocator, and constructs them.
  268. */
  269. template<class T, class... Args>
  270. T* bs_frame_new(Args &&...args, UINT32 count = 0)
  271. {
  272. T* data = bs_frame_alloc<T>(count);
  273. for(unsigned int i = 0; i < count; i++)
  274. new ((void*)&data[i]) T(std::forward<Args>(args)...);
  275. return data;
  276. }
  277. /**
  278. * Destructs and deallocates an object allocated with the global frame allocator.
  279. *
  280. * @note Must be called on the same thread the memory was allocated on.
  281. */
  282. template<class T>
  283. void bs_frame_delete(T* data)
  284. {
  285. data->~T();
  286. bs_frame_free((UINT8*)data);
  287. }
  288. /**
  289. * Destructs and deallocates an array of objects allocated with the global frame allocator.
  290. *
  291. * @note Must be called on the same thread the memory was allocated on.
  292. */
  293. template<class T>
  294. void bs_frame_delete(T* data, UINT32 count)
  295. {
  296. for(unsigned int i = 0; i < count; i++)
  297. data[i].~T();
  298. bs_frame_free((UINT8*)data);
  299. }
  300. /** @copydoc FrameAlloc::markFrame */
  301. BS_UTILITY_EXPORT void bs_frame_mark();
  302. /** @copydoc FrameAlloc::clear */
  303. BS_UTILITY_EXPORT void bs_frame_clear();
  304. /** String allocated with a frame allocator. */
  305. typedef std::basic_string<char, std::char_traits<char>, StdAlloc<char, FrameAlloc>> FrameString;
  306. /** WString allocated with a frame allocator. */
  307. typedef std::basic_string<wchar_t, std::char_traits<wchar_t>, StdAlloc<wchar_t, FrameAlloc>> FrameWString;
  308. /** Vector allocated with a frame allocator. */
  309. template <typename T, typename A = StdAlloc<T, FrameAlloc>>
  310. using FrameVector = std::vector < T, A > ;
  311. /** Stack allocated with a frame allocator. */
  312. template <typename T, typename A = StdAlloc<T, FrameAlloc>>
  313. using FrameStack = std::stack < T, std::deque<T, A> > ;
  314. /** Queue allocated with a frame allocator. */
  315. template <typename T, typename A = StdAlloc<T, FrameAlloc>>
  316. using FrameQueue = std::queue<T, std::deque<T, A>>;
  317. /** Set allocated with a frame allocator. */
  318. template <typename T, typename P = std::less<T>, typename A = StdAlloc<T, FrameAlloc>>
  319. using FrameSet = std::set < T, P, A > ;
  320. /** Map allocated with a frame allocator. */
  321. template <typename K, typename V, typename P = std::less<K>, typename A = StdAlloc<std::pair<const K, V>, FrameAlloc>>
  322. using FrameMap = std::map < K, V, P, A >;
  323. /** UnorderedSet allocated with a frame allocator. */
  324. template <typename T, typename H = std::hash<T>, typename C = std::equal_to<T>, typename A = StdAlloc<T, FrameAlloc>>
  325. using FrameUnorderedSet = std::unordered_set < T, H, C, A >;
  326. /** UnorderedMap allocated with a frame allocator. */
  327. template <typename K, typename V, typename H = std::hash<K>, typename C = std::equal_to<K>, typename A = StdAlloc<std::pair<const K, V>, FrameAlloc>>
  328. using FrameUnorderedMap = std::unordered_map < K, V, H, C, A >;
  329. /** @} */
  330. /** @addtogroup Internal-Utility
  331. * @{
  332. */
  333. /** @addtogroup Memory-Internal
  334. * @{
  335. */
  336. extern BS_THREADLOCAL FrameAlloc* _GlobalFrameAlloc;
  337. /**
  338. * Specialized memory allocator implementations that allows use of a global frame allocator in normal
  339. * new/delete/free/dealloc operators.
  340. */
  341. template<>
  342. class MemoryAllocator<FrameAlloc> : public MemoryAllocatorBase
  343. {
  344. public:
  345. /** @copydoc MemoryAllocator::allocate */
  346. static void* allocate(size_t bytes)
  347. {
  348. return bs_frame_alloc((UINT32)bytes);
  349. }
  350. /** @copydoc MemoryAllocator::allocateAligned */
  351. static void* allocateAligned(size_t bytes, size_t alignment)
  352. {
  353. #if BS_PROFILING_ENABLED
  354. incAllocCount();
  355. #endif
  356. return bs_frame_alloc_aligned((UINT32)bytes, (UINT32)alignment);
  357. }
  358. /** @copydoc MemoryAllocator::allocateAligned16 */
  359. static void* allocateAligned16(size_t bytes)
  360. {
  361. #if BS_PROFILING_ENABLED
  362. incAllocCount();
  363. #endif
  364. return bs_frame_alloc_aligned((UINT32)bytes, 16);
  365. }
  366. /** @copydoc MemoryAllocator::free */
  367. static void free(void* ptr)
  368. {
  369. bs_frame_free(ptr);
  370. }
  371. /** @copydoc MemoryAllocator::freeAligned */
  372. static void freeAligned(void* ptr)
  373. {
  374. #if BS_PROFILING_ENABLED
  375. incFreeCount();
  376. #endif
  377. bs_frame_free_aligned(ptr);
  378. }
  379. /** @copydoc MemoryAllocator::freeAligned16 */
  380. static void freeAligned16(void* ptr)
  381. {
  382. #if BS_PROFILING_ENABLED
  383. incFreeCount();
  384. #endif
  385. bs_frame_free_aligned(ptr);
  386. }
  387. };
  388. /** @} */
  389. /** @} */
  390. }