BsFrameAlloc.h 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  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 "BsPrerequisitesUtil.h"
  7. namespace BansheeEngine
  8. {
  9. /** @addtogroup Internal-Utility
  10. * @{
  11. */
  12. /** @addtogroup Memory-Internal
  13. * @{
  14. */
  15. /**
  16. * Frame allocator. Performs very fast allocations but can only free all of its memory at once. Perfect for allocations
  17. * that last just a single frame.
  18. *
  19. * @note Not thread safe with an exception. alloc() and clear() methods need to be called from the same thread.
  20. * dealloc() is thread safe and can be called from any thread.
  21. */
  22. class BS_UTILITY_EXPORT FrameAlloc
  23. {
  24. private:
  25. /** A single block of memory within a frame allocator. */
  26. class MemBlock
  27. {
  28. public:
  29. MemBlock(UINT32 size);
  30. ~MemBlock();
  31. /** Allocates a piece of memory within the block. Caller must ensure the block has enough empty space. */
  32. UINT8* alloc(UINT32 amount);
  33. /** Releases all allocations within a block but doesn't actually free the memory. */
  34. void clear();
  35. UINT8* mData;
  36. UINT32 mFreePtr;
  37. UINT32 mSize;
  38. };
  39. public:
  40. FrameAlloc(UINT32 blockSize = 1024 * 1024);
  41. ~FrameAlloc();
  42. /**
  43. * Allocates a new block of memory of the specified size.
  44. *
  45. * @param[in] amount Amount of memory to allocate, in bytes.
  46. *
  47. * @note Not thread safe.
  48. */
  49. UINT8* alloc(UINT32 amount);
  50. /**
  51. * Allocates a new block of memory of the specified size aligned to the specified boundary. If the aligment is less
  52. * or equal to 16 it is more efficient to use the allocAligned16() alternative of this method.
  53. *
  54. * @param[in] amount Amount of memory to allocate, in bytes.
  55. * @param[in] alignment Alignment of the allocated memory. Must be power of two.
  56. *
  57. * @note Not thread safe.
  58. */
  59. UINT8* allocAligned(UINT32 amount, UINT32 alignment);
  60. /**
  61. * Allocates and constructs a new object.
  62. *
  63. * @note Not thread safe.
  64. */
  65. template<class T, class... Args>
  66. T* alloc(Args &&...args)
  67. {
  68. return new ((T*)alloc(sizeof(T))) T(std::forward<Args>(args)...);
  69. }
  70. /**
  71. * Deallocates a previously allocated block of memory.
  72. *
  73. * @note
  74. * No deallocation is actually done here. This method is only used for debug purposes so it is easier to track
  75. * down memory leaks and corruption.
  76. * @note
  77. * Thread safe.
  78. */
  79. void dealloc(UINT8* data);
  80. /**
  81. * Deallocates and destructs a previously allocated object.
  82. *
  83. * @note
  84. * No deallocation is actually done here. This method is only used to call the destructor and for debug purposes
  85. * so it is easier to track down memory leaks and corruption.
  86. * @note
  87. * Thread safe.
  88. */
  89. template<class T>
  90. void dealloc(T* obj)
  91. {
  92. if (obj != nullptr)
  93. obj->~T();
  94. dealloc((UINT8*)obj);
  95. }
  96. /** Starts a new frame. Next call to clear() will only clear memory allocated past this point. */
  97. void markFrame();
  98. /**
  99. * Deallocates all allocated memory since the last call to markFrame() (or all the memory if there was no call
  100. * to markFrame()).
  101. *
  102. * @note Not thread safe.
  103. */
  104. void clear();
  105. /**
  106. * Changes the frame allocator owner thread. After the owner thread has changed only allocations from that thread
  107. * can be made.
  108. */
  109. void setOwnerThread(ThreadId thread);
  110. private:
  111. UINT32 mBlockSize;
  112. Vector<MemBlock*> mBlocks;
  113. MemBlock* mFreeBlock;
  114. UINT32 mNextBlockIdx;
  115. std::atomic<UINT32> mTotalAllocBytes;
  116. void* mLastFrame;
  117. #if BS_DEBUG_MODE
  118. ThreadId mOwnerThread;
  119. #endif
  120. /**
  121. * Allocates a dynamic block of memory of the wanted size. The exact allocation size might be slightly higher in
  122. * order to store block meta data.
  123. */
  124. MemBlock* allocBlock(UINT32 wantedSize);
  125. /** Frees a memory block. */
  126. void deallocBlock(MemBlock* block);
  127. };
  128. /** Allocator for the standard library that internally uses a frame allocator. */
  129. template <class T>
  130. class StdFrameAlloc
  131. {
  132. public:
  133. typedef T value_type;
  134. typedef value_type* pointer;
  135. typedef const value_type* const_pointer;
  136. typedef value_type& reference;
  137. typedef const value_type& const_reference;
  138. typedef std::size_t size_type;
  139. typedef std::ptrdiff_t difference_type;
  140. StdFrameAlloc() noexcept
  141. :mFrameAlloc(nullptr)
  142. { }
  143. StdFrameAlloc(FrameAlloc* alloc) noexcept
  144. :mFrameAlloc(alloc)
  145. { }
  146. template<class U> StdFrameAlloc(const StdFrameAlloc<U>& alloc) noexcept
  147. :mFrameAlloc(alloc.mFrameAlloc)
  148. { }
  149. template<class U> bool operator==(const StdFrameAlloc<U>&) const noexcept { return true; }
  150. template<class U> bool operator!=(const StdFrameAlloc<U>&) const noexcept { return false; }
  151. template<class U> class rebind { public: typedef StdFrameAlloc<U> other; };
  152. /** Allocate but don't initialize number elements of type T.*/
  153. T* allocate(const size_t num) const
  154. {
  155. if (num == 0)
  156. return nullptr;
  157. if (num > static_cast<size_t>(-1) / sizeof(T))
  158. return nullptr; // Error
  159. void* const pv = mFrameAlloc->alloc((UINT32)(num * sizeof(T)));
  160. if (!pv)
  161. return nullptr; // Error
  162. return static_cast<T*>(pv);
  163. }
  164. /** Deallocate storage p of deleted elements. */
  165. void deallocate(T* p, size_t num) const noexcept
  166. {
  167. mFrameAlloc->dealloc((UINT8*)p);
  168. }
  169. FrameAlloc* mFrameAlloc;
  170. size_t max_size() const { return std::numeric_limits<size_type>::max() / sizeof(T); }
  171. void construct(pointer p, const_reference t) { new (p) T(t); }
  172. void destroy(pointer p) { p->~T(); }
  173. template<class U, class... Args>
  174. void construct(U* p, Args&&... args) { new(p) U(std::forward<Args>(args)...); }
  175. };
  176. /** Return that all specializations of this allocator are interchangeable. */
  177. template <class T1, class T2>
  178. bool operator== (const StdFrameAlloc<T1>&,
  179. const StdFrameAlloc<T2>&) throw() {
  180. return true;
  181. }
  182. /** Return that all specializations of this allocator are interchangeable. */
  183. template <class T1, class T2>
  184. bool operator!= (const StdFrameAlloc<T1>&,
  185. const StdFrameAlloc<T2>&) throw() {
  186. return false;
  187. }
  188. /** @} */
  189. /** @} */
  190. }