210 lines
5.3 KiB
C++
Vendored
210 lines
5.3 KiB
C++
Vendored
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
|
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
#pragma once
|
|
|
|
#include <Jolt/Core/NonCopyable.h>
|
|
|
|
JPH_NAMESPACE_BEGIN
|
|
|
|
/// Allocator for temporary allocations.
|
|
/// This allocator works as a stack: The blocks must always be freed in the reverse order as they are allocated.
|
|
/// Note that allocations and frees can take place from different threads, but the order is guaranteed though
|
|
/// job dependencies, so it is not needed to use any form of locking.
|
|
class JPH_EXPORT TempAllocator : public NonCopyable
|
|
{
|
|
public:
|
|
JPH_OVERRIDE_NEW_DELETE
|
|
|
|
/// If this allocator needs to fall back to aligned allocations because JPH_RVECTOR_ALIGNMENT is bigger than the platform default
|
|
static constexpr bool needs_aligned_allocate = JPH_RVECTOR_ALIGNMENT > JPH_DEFAULT_ALLOCATE_ALIGNMENT;
|
|
|
|
/// Destructor
|
|
virtual ~TempAllocator() = default;
|
|
|
|
/// Allocates inSize bytes of memory, returned memory address must be JPH_RVECTOR_ALIGNMENT byte aligned
|
|
virtual void * Allocate(uint inSize) = 0;
|
|
|
|
/// Frees inSize bytes of memory located at inAddress
|
|
virtual void Free(void *inAddress, uint inSize) = 0;
|
|
};
|
|
|
|
/// Default implementation of the temp allocator that allocates a large block through malloc upfront
|
|
class JPH_EXPORT TempAllocatorImpl final : public TempAllocator
|
|
{
|
|
public:
|
|
JPH_OVERRIDE_NEW_DELETE
|
|
|
|
/// Constructs the allocator with a maximum allocatable size of inSize
|
|
explicit TempAllocatorImpl(size_t inSize) : mSize(inSize)
|
|
{
|
|
if constexpr (needs_aligned_allocate)
|
|
mBase = static_cast<uint8 *>(AlignedAllocate(inSize, JPH_RVECTOR_ALIGNMENT));
|
|
else
|
|
mBase = static_cast<uint8 *>(JPH::Allocate(inSize));
|
|
}
|
|
|
|
/// Destructor, frees the block
|
|
virtual ~TempAllocatorImpl() override
|
|
{
|
|
JPH_ASSERT(mTop == 0);
|
|
if constexpr (needs_aligned_allocate)
|
|
AlignedFree(mBase);
|
|
else
|
|
JPH::Free(mBase);
|
|
}
|
|
|
|
// See: TempAllocator
|
|
virtual void * Allocate(uint inSize) override
|
|
{
|
|
if (inSize == 0)
|
|
{
|
|
return nullptr;
|
|
}
|
|
else
|
|
{
|
|
size_t new_top = mTop + AlignUp(inSize, JPH_RVECTOR_ALIGNMENT);
|
|
if (new_top > mSize)
|
|
{
|
|
Trace("TempAllocator: Out of memory trying to allocate %u bytes", inSize);
|
|
std::abort();
|
|
}
|
|
void *address = mBase + mTop;
|
|
mTop = new_top;
|
|
return address;
|
|
}
|
|
}
|
|
|
|
// See: TempAllocator
|
|
virtual void Free(void *inAddress, uint inSize) override
|
|
{
|
|
if (inAddress == nullptr)
|
|
{
|
|
JPH_ASSERT(inSize == 0);
|
|
}
|
|
else
|
|
{
|
|
mTop -= AlignUp(inSize, JPH_RVECTOR_ALIGNMENT);
|
|
if (mBase + mTop != inAddress)
|
|
{
|
|
Trace("TempAllocator: Freeing in the wrong order");
|
|
std::abort();
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Check if no allocations have been made
|
|
bool IsEmpty() const
|
|
{
|
|
return mTop == 0;
|
|
}
|
|
|
|
/// Get the total size of the fixed buffer
|
|
size_t GetSize() const
|
|
{
|
|
return mSize;
|
|
}
|
|
|
|
/// Get current usage in bytes of the buffer
|
|
size_t GetUsage() const
|
|
{
|
|
return mTop;
|
|
}
|
|
|
|
/// Check if an allocation of inSize can be made in this fixed buffer allocator
|
|
bool CanAllocate(uint inSize) const
|
|
{
|
|
return mTop + AlignUp(inSize, JPH_RVECTOR_ALIGNMENT) <= mSize;
|
|
}
|
|
|
|
/// Check if memory block at inAddress is owned by this allocator
|
|
bool OwnsMemory(const void *inAddress) const
|
|
{
|
|
return inAddress >= mBase && inAddress < mBase + mSize;
|
|
}
|
|
|
|
private:
|
|
uint8 * mBase; ///< Base address of the memory block
|
|
size_t mSize; ///< Size of the memory block
|
|
size_t mTop = 0; ///< End of currently allocated area
|
|
};
|
|
|
|
/// Implementation of the TempAllocator that just falls back to malloc/free
|
|
/// Note: This can be quite slow when running in the debugger as large memory blocks need to be initialized with 0xcd
|
|
class JPH_EXPORT TempAllocatorMalloc final : public TempAllocator
|
|
{
|
|
public:
|
|
JPH_OVERRIDE_NEW_DELETE
|
|
|
|
// See: TempAllocator
|
|
virtual void * Allocate(uint inSize) override
|
|
{
|
|
if (inSize > 0)
|
|
{
|
|
if constexpr (needs_aligned_allocate)
|
|
return AlignedAllocate(inSize, JPH_RVECTOR_ALIGNMENT);
|
|
else
|
|
return JPH::Allocate(inSize);
|
|
}
|
|
else
|
|
return nullptr;
|
|
}
|
|
|
|
// See: TempAllocator
|
|
virtual void Free(void *inAddress, [[maybe_unused]] uint inSize) override
|
|
{
|
|
if (inAddress != nullptr)
|
|
{
|
|
if constexpr (needs_aligned_allocate)
|
|
AlignedFree(inAddress);
|
|
else
|
|
JPH::Free(inAddress);
|
|
}
|
|
}
|
|
};
|
|
|
|
/// Implementation of the TempAllocator that tries to allocate from a large preallocated block, but falls back to malloc when it is exhausted
|
|
class JPH_EXPORT TempAllocatorImplWithMallocFallback final : public TempAllocator
|
|
{
|
|
public:
|
|
JPH_OVERRIDE_NEW_DELETE
|
|
|
|
/// Constructs the allocator with an initial fixed block if inSize
|
|
explicit TempAllocatorImplWithMallocFallback(uint inSize) :
|
|
mAllocator(inSize)
|
|
{
|
|
}
|
|
|
|
// See: TempAllocator
|
|
virtual void * Allocate(uint inSize) override
|
|
{
|
|
if (mAllocator.CanAllocate(inSize))
|
|
return mAllocator.Allocate(inSize);
|
|
else
|
|
return mFallbackAllocator.Allocate(inSize);
|
|
}
|
|
|
|
// See: TempAllocator
|
|
virtual void Free(void *inAddress, uint inSize) override
|
|
{
|
|
if (inAddress == nullptr)
|
|
{
|
|
JPH_ASSERT(inSize == 0);
|
|
}
|
|
else
|
|
{
|
|
if (mAllocator.OwnsMemory(inAddress))
|
|
mAllocator.Free(inAddress, inSize);
|
|
else
|
|
mFallbackAllocator.Free(inAddress, inSize);
|
|
}
|
|
}
|
|
|
|
private:
|
|
TempAllocatorImpl mAllocator;
|
|
TempAllocatorMalloc mFallbackAllocator;
|
|
};
|
|
|
|
JPH_NAMESPACE_END
|