TempAllocator.h 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  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. /// Destructor
  16. virtual ~TempAllocator() = default;
  17. /// Allocates inSize bytes of memory, returned memory address must be JPH_RVECTOR_ALIGNMENT byte aligned
  18. virtual void * Allocate(uint inSize) = 0;
  19. /// Frees inSize bytes of memory located at inAddress
  20. virtual void Free(void *inAddress, uint inSize) = 0;
  21. };
  22. /// Default implementation of the temp allocator that allocates a large block through malloc upfront
  23. class JPH_EXPORT TempAllocatorImpl final : public TempAllocator
  24. {
  25. public:
  26. JPH_OVERRIDE_NEW_DELETE
  27. /// Constructs the allocator with a maximum allocatable size of inSize
  28. explicit TempAllocatorImpl(uint inSize) :
  29. mBase(static_cast<uint8 *>(AlignedAllocate(inSize, JPH_RVECTOR_ALIGNMENT))),
  30. mSize(inSize)
  31. {
  32. }
  33. /// Destructor, frees the block
  34. virtual ~TempAllocatorImpl() override
  35. {
  36. JPH_ASSERT(mTop == 0);
  37. AlignedFree(mBase);
  38. }
  39. // See: TempAllocator
  40. virtual void * Allocate(uint inSize) override
  41. {
  42. if (inSize == 0)
  43. {
  44. return nullptr;
  45. }
  46. else
  47. {
  48. uint new_top = mTop + AlignUp(inSize, JPH_RVECTOR_ALIGNMENT);
  49. if (new_top > mSize)
  50. {
  51. Trace("TempAllocator: Out of memory");
  52. std::abort();
  53. }
  54. void *address = mBase + mTop;
  55. mTop = new_top;
  56. return address;
  57. }
  58. }
  59. // See: TempAllocator
  60. virtual void Free(void *inAddress, uint inSize) override
  61. {
  62. if (inAddress == nullptr)
  63. {
  64. JPH_ASSERT(inSize == 0);
  65. }
  66. else
  67. {
  68. mTop -= AlignUp(inSize, JPH_RVECTOR_ALIGNMENT);
  69. if (mBase + mTop != inAddress)
  70. {
  71. Trace("TempAllocator: Freeing in the wrong order");
  72. std::abort();
  73. }
  74. }
  75. }
  76. /// Check if no allocations have been made
  77. bool IsEmpty() const
  78. {
  79. return mTop == 0;
  80. }
  81. /// Get the total size of the fixed buffer
  82. uint GetSize() const
  83. {
  84. return mSize;
  85. }
  86. /// Get current usage in bytes of the buffer
  87. uint GetUsage() const
  88. {
  89. return mTop;
  90. }
  91. /// Check if an allocation of inSize can be made in this fixed buffer allocator
  92. bool CanAllocate(uint inSize) const
  93. {
  94. return mTop + AlignUp(inSize, JPH_RVECTOR_ALIGNMENT) <= mSize;
  95. }
  96. /// Check if memory block at inAddress is owned by this allocator
  97. bool OwnsMemory(const void *inAddress) const
  98. {
  99. return inAddress >= mBase && inAddress < mBase + mSize;
  100. }
  101. private:
  102. uint8 * mBase; ///< Base address of the memory block
  103. uint mSize; ///< Size of the memory block
  104. uint mTop = 0; ///< End of currently allocated area
  105. };
  106. /// Implementation of the TempAllocator that just falls back to malloc/free
  107. /// Note: This can be quite slow when running in the debugger as large memory blocks need to be initialized with 0xcd
  108. class JPH_EXPORT TempAllocatorMalloc final : public TempAllocator
  109. {
  110. public:
  111. JPH_OVERRIDE_NEW_DELETE
  112. // See: TempAllocator
  113. virtual void * Allocate(uint inSize) override
  114. {
  115. return inSize > 0? AlignedAllocate(inSize, JPH_RVECTOR_ALIGNMENT) : nullptr;
  116. }
  117. // See: TempAllocator
  118. virtual void Free(void *inAddress, [[maybe_unused]] uint inSize) override
  119. {
  120. if (inAddress != nullptr)
  121. AlignedFree(inAddress);
  122. }
  123. };
  124. /// Implementation of the TempAllocator that tries to allocate from a large preallocated block, but falls back to malloc when it is exhausted
  125. class JPH_EXPORT TempAllocatorImplWithMallocFallback final : public TempAllocator
  126. {
  127. public:
  128. JPH_OVERRIDE_NEW_DELETE
  129. /// Constructs the allocator with an initial fixed block if inSize
  130. explicit TempAllocatorImplWithMallocFallback(uint inSize) :
  131. mAllocator(inSize)
  132. {
  133. }
  134. // See: TempAllocator
  135. virtual void * Allocate(uint inSize) override
  136. {
  137. if (mAllocator.CanAllocate(inSize))
  138. return mAllocator.Allocate(inSize);
  139. else
  140. return mFallbackAllocator.Allocate(inSize);
  141. }
  142. // See: TempAllocator
  143. virtual void Free(void *inAddress, uint inSize) override
  144. {
  145. if (inAddress == nullptr)
  146. {
  147. JPH_ASSERT(inSize == 0);
  148. }
  149. else
  150. {
  151. if (mAllocator.OwnsMemory(inAddress))
  152. mAllocator.Free(inAddress, inSize);
  153. else
  154. mFallbackAllocator.Free(inAddress, inSize);
  155. }
  156. }
  157. private:
  158. TempAllocatorImpl mAllocator;
  159. TempAllocatorMalloc mFallbackAllocator;
  160. };
  161. JPH_NAMESPACE_END