TempAllocator.h 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. // Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
  2. // SPDX-FileCopyrightText: 2021 Jorrit Rouwe
  3. // SPDX-License-Identifier: MIT
  4. #pragma once
  5. #include <Jolt/Core/NonCopyable.h>
  6. JPH_NAMESPACE_BEGIN
  7. /// Allocator for temporary allocations.
  8. /// This allocator works as a stack: The blocks must always be freed in the reverse order as they are allocated.
  9. /// Note that allocations and frees can take place from different threads, but the order is guaranteed though
  10. /// job dependencies, so it is not needed to use any form of locking.
  11. class JPH_EXPORT TempAllocator : public NonCopyable
  12. {
  13. public:
  14. JPH_OVERRIDE_NEW_DELETE
  15. /// If this allocator needs to fall back to aligned allocations because JPH_RVECTOR_ALIGNMENT is bigger than the platform default
  16. static constexpr bool needs_aligned_allocate = JPH_RVECTOR_ALIGNMENT > JPH_DEFAULT_ALLOCATE_ALIGNMENT;
  17. /// Destructor
  18. virtual ~TempAllocator() = default;
  19. /// Allocates inSize bytes of memory, returned memory address must be JPH_RVECTOR_ALIGNMENT byte aligned
  20. virtual void * Allocate(uint inSize) = 0;
  21. /// Frees inSize bytes of memory located at inAddress
  22. virtual void Free(void *inAddress, uint inSize) = 0;
  23. };
  24. /// Default implementation of the temp allocator that allocates a large block through malloc upfront
  25. class JPH_EXPORT TempAllocatorImpl final : public TempAllocator
  26. {
  27. public:
  28. JPH_OVERRIDE_NEW_DELETE
  29. /// Constructs the allocator with a maximum allocatable size of inSize
  30. explicit TempAllocatorImpl(size_t inSize) : mSize(inSize)
  31. {
  32. if constexpr (needs_aligned_allocate)
  33. mBase = static_cast<uint8 *>(AlignedAllocate(inSize, JPH_RVECTOR_ALIGNMENT));
  34. else
  35. mBase = static_cast<uint8 *>(JPH::Allocate(inSize));
  36. }
  37. /// Destructor, frees the block
  38. virtual ~TempAllocatorImpl() override
  39. {
  40. JPH_ASSERT(mTop == 0);
  41. if constexpr (needs_aligned_allocate)
  42. AlignedFree(mBase);
  43. else
  44. JPH::Free(mBase);
  45. }
  46. // See: TempAllocator
  47. virtual void * Allocate(uint inSize) override
  48. {
  49. if (inSize == 0)
  50. {
  51. return nullptr;
  52. }
  53. else
  54. {
  55. size_t new_top = mTop + AlignUp(inSize, JPH_RVECTOR_ALIGNMENT);
  56. if (new_top > mSize)
  57. {
  58. Trace("TempAllocator: Out of memory trying to allocate %u bytes", inSize);
  59. std::abort();
  60. }
  61. void *address = mBase + mTop;
  62. mTop = new_top;
  63. return address;
  64. }
  65. }
  66. // See: TempAllocator
  67. virtual void Free(void *inAddress, uint inSize) override
  68. {
  69. if (inAddress == nullptr)
  70. {
  71. JPH_ASSERT(inSize == 0);
  72. }
  73. else
  74. {
  75. mTop -= AlignUp(inSize, JPH_RVECTOR_ALIGNMENT);
  76. if (mBase + mTop != inAddress)
  77. {
  78. Trace("TempAllocator: Freeing in the wrong order");
  79. std::abort();
  80. }
  81. }
  82. }
  83. /// Check if no allocations have been made
  84. bool IsEmpty() const
  85. {
  86. return mTop == 0;
  87. }
  88. /// Get the total size of the fixed buffer
  89. size_t GetSize() const
  90. {
  91. return mSize;
  92. }
  93. /// Get current usage in bytes of the buffer
  94. size_t GetUsage() const
  95. {
  96. return mTop;
  97. }
  98. /// Check if an allocation of inSize can be made in this fixed buffer allocator
  99. bool CanAllocate(uint inSize) const
  100. {
  101. return mTop + AlignUp(inSize, JPH_RVECTOR_ALIGNMENT) <= mSize;
  102. }
  103. /// Check if memory block at inAddress is owned by this allocator
  104. bool OwnsMemory(const void *inAddress) const
  105. {
  106. return inAddress >= mBase && inAddress < mBase + mSize;
  107. }
  108. private:
  109. uint8 * mBase; ///< Base address of the memory block
  110. size_t mSize; ///< Size of the memory block
  111. size_t mTop = 0; ///< End of currently allocated area
  112. };
  113. /// Implementation of the TempAllocator that just falls back to malloc/free
  114. /// Note: This can be quite slow when running in the debugger as large memory blocks need to be initialized with 0xcd
  115. class JPH_EXPORT TempAllocatorMalloc final : public TempAllocator
  116. {
  117. public:
  118. JPH_OVERRIDE_NEW_DELETE
  119. // See: TempAllocator
  120. virtual void * Allocate(uint inSize) override
  121. {
  122. if (inSize > 0)
  123. {
  124. if constexpr (needs_aligned_allocate)
  125. return AlignedAllocate(inSize, JPH_RVECTOR_ALIGNMENT);
  126. else
  127. return JPH::Allocate(inSize);
  128. }
  129. else
  130. return nullptr;
  131. }
  132. // See: TempAllocator
  133. virtual void Free(void *inAddress, [[maybe_unused]] uint inSize) override
  134. {
  135. if (inAddress != nullptr)
  136. {
  137. if constexpr (needs_aligned_allocate)
  138. AlignedFree(inAddress);
  139. else
  140. JPH::Free(inAddress);
  141. }
  142. }
  143. };
  144. /// Implementation of the TempAllocator that tries to allocate from a large preallocated block, but falls back to malloc when it is exhausted
  145. class JPH_EXPORT TempAllocatorImplWithMallocFallback final : public TempAllocator
  146. {
  147. public:
  148. JPH_OVERRIDE_NEW_DELETE
  149. /// Constructs the allocator with an initial fixed block if inSize
  150. explicit TempAllocatorImplWithMallocFallback(uint inSize) :
  151. mAllocator(inSize)
  152. {
  153. }
  154. // See: TempAllocator
  155. virtual void * Allocate(uint inSize) override
  156. {
  157. if (mAllocator.CanAllocate(inSize))
  158. return mAllocator.Allocate(inSize);
  159. else
  160. return mFallbackAllocator.Allocate(inSize);
  161. }
  162. // See: TempAllocator
  163. virtual void Free(void *inAddress, uint inSize) override
  164. {
  165. if (inAddress == nullptr)
  166. {
  167. JPH_ASSERT(inSize == 0);
  168. }
  169. else
  170. {
  171. if (mAllocator.OwnsMemory(inAddress))
  172. mAllocator.Free(inAddress, inSize);
  173. else
  174. mFallbackAllocator.Free(inAddress, inSize);
  175. }
  176. }
  177. private:
  178. TempAllocatorImpl mAllocator;
  179. TempAllocatorMalloc mFallbackAllocator;
  180. };
  181. JPH_NAMESPACE_END