BsFrameAlloc.h 4.9 KB

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