mirror of
https://github.com/GPUOpen-LibrariesAndSDKs/D3D12MemoryAllocator.git
synced 2024-11-28 22:51:15 +00:00
Add virtual allocator - struct VIRTUAL_BLOCK_DESC, VIRTUAL_ALLOCATION_DESC, VIRTUAL_ALLOCATION_INFO, class VirtualBlock, function CreateVirtualBlock.
Also updated Doxyfile to Doxygen 1.8.18.
This commit is contained in:
parent
0478883903
commit
8dfdc0e7cd
@ -157,11 +157,11 @@ void D3D12MA_DELETE_ARRAY(const ALLOCATION_CALLBACKS& allocs, T* memory, size_t
|
||||
}
|
||||
}
|
||||
|
||||
static void SetupAllocationCallbacks(ALLOCATION_CALLBACKS& outAllocs, const ALLOCATOR_DESC& allocatorDesc)
|
||||
static void SetupAllocationCallbacks(ALLOCATION_CALLBACKS& outAllocs, const ALLOCATION_CALLBACKS* allocationCallbacks)
|
||||
{
|
||||
if(allocatorDesc.pAllocationCallbacks)
|
||||
if(allocationCallbacks)
|
||||
{
|
||||
outAllocs = *allocatorDesc.pAllocationCallbacks;
|
||||
outAllocs = *allocationCallbacks;
|
||||
D3D12MA_ASSERT(outAllocs.pAllocate != NULL && outAllocs.pFree != NULL);
|
||||
}
|
||||
else
|
||||
@ -1996,7 +1996,7 @@ struct Suballocation
|
||||
{
|
||||
UINT64 offset;
|
||||
UINT64 size;
|
||||
Allocation* allocation;
|
||||
void* userData;
|
||||
SuballocationType type;
|
||||
};
|
||||
|
||||
@ -2106,19 +2106,22 @@ in a single ID3D12Heap memory block.
|
||||
class BlockMetadata
|
||||
{
|
||||
public:
|
||||
BlockMetadata(const ALLOCATION_CALLBACKS* allocationCallbacks);
|
||||
BlockMetadata(const ALLOCATION_CALLBACKS* allocationCallbacks, bool isVirtual);
|
||||
virtual ~BlockMetadata() { }
|
||||
virtual void Init(UINT64 size) { m_Size = size; }
|
||||
|
||||
// Validates all data structures inside this object. If not valid, returns false.
|
||||
virtual bool Validate() const = 0;
|
||||
UINT64 GetSize() const { return m_Size; }
|
||||
bool IsVirtual() const { return m_IsVirtual; }
|
||||
virtual size_t GetAllocationCount() const = 0;
|
||||
virtual UINT64 GetSumFreeSize() const = 0;
|
||||
virtual UINT64 GetUnusedRangeSizeMax() const = 0;
|
||||
// Returns true if this block is empty - contains only single free suballocation.
|
||||
virtual bool IsEmpty() const = 0;
|
||||
|
||||
virtual void GetAllocationInfo(UINT64 offset, VIRTUAL_ALLOCATION_INFO& outInfo) const = 0;
|
||||
|
||||
// Tries to find a place for suballocation with given parameters inside this block.
|
||||
// If succeeded, fills pAllocationRequest and returns true.
|
||||
// If failed, returns false.
|
||||
@ -2131,11 +2134,14 @@ public:
|
||||
virtual void Alloc(
|
||||
const AllocationRequest& request,
|
||||
UINT64 allocSize,
|
||||
Allocation* Allocation) = 0;
|
||||
void* userData) = 0;
|
||||
|
||||
// Frees suballocation assigned to given memory region.
|
||||
virtual void Free(const Allocation* allocation) = 0;
|
||||
virtual void FreeAtOffset(UINT64 offset) = 0;
|
||||
// Frees all allocations.
|
||||
// Careful! Don't call it if there are Allocation objects owned by pUserData of of cleared allocations!
|
||||
virtual void Clear() = 0;
|
||||
|
||||
virtual void SetAllocationUserData(UINT64 offset, void* userData) = 0;
|
||||
|
||||
virtual void CalcAllocationStatInfo(StatInfo& outInfo) const = 0;
|
||||
virtual void WriteAllocationInfoToJson(JsonWriter& json) const = 0;
|
||||
@ -2145,6 +2151,7 @@ protected:
|
||||
|
||||
private:
|
||||
UINT64 m_Size;
|
||||
bool m_IsVirtual;
|
||||
const ALLOCATION_CALLBACKS* m_pAllocationCallbacks;
|
||||
|
||||
D3D12MA_CLASS_NO_COPY(BlockMetadata);
|
||||
@ -2153,7 +2160,7 @@ private:
|
||||
class BlockMetadata_Generic : public BlockMetadata
|
||||
{
|
||||
public:
|
||||
BlockMetadata_Generic(const ALLOCATION_CALLBACKS* allocationCallbacks);
|
||||
BlockMetadata_Generic(const ALLOCATION_CALLBACKS* allocationCallbacks, bool isVirtual);
|
||||
virtual ~BlockMetadata_Generic();
|
||||
virtual void Init(UINT64 size);
|
||||
|
||||
@ -2163,6 +2170,8 @@ public:
|
||||
virtual UINT64 GetUnusedRangeSizeMax() const;
|
||||
virtual bool IsEmpty() const;
|
||||
|
||||
virtual void GetAllocationInfo(UINT64 offset, VIRTUAL_ALLOCATION_INFO& outInfo) const;
|
||||
|
||||
virtual bool CreateAllocationRequest(
|
||||
UINT64 allocSize,
|
||||
UINT64 allocAlignment,
|
||||
@ -2171,10 +2180,12 @@ public:
|
||||
virtual void Alloc(
|
||||
const AllocationRequest& request,
|
||||
UINT64 allocSize,
|
||||
Allocation* hAllocation);
|
||||
void* userData);
|
||||
|
||||
virtual void Free(const Allocation* allocation);
|
||||
virtual void FreeAtOffset(UINT64 offset);
|
||||
virtual void Clear();
|
||||
|
||||
virtual void SetAllocationUserData(UINT64 offset, void* userData);
|
||||
|
||||
virtual void CalcAllocationStatInfo(StatInfo& outInfo) const;
|
||||
virtual void WriteAllocationInfoToJson(JsonWriter& json) const;
|
||||
@ -2617,8 +2628,9 @@ private:
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Private class BlockMetadata implementation
|
||||
|
||||
BlockMetadata::BlockMetadata(const ALLOCATION_CALLBACKS* allocationCallbacks) :
|
||||
BlockMetadata::BlockMetadata(const ALLOCATION_CALLBACKS* allocationCallbacks, bool isVirtual) :
|
||||
m_Size(0),
|
||||
m_IsVirtual(isVirtual),
|
||||
m_pAllocationCallbacks(allocationCallbacks)
|
||||
{
|
||||
D3D12MA_ASSERT(allocationCallbacks);
|
||||
@ -2627,8 +2639,8 @@ BlockMetadata::BlockMetadata(const ALLOCATION_CALLBACKS* allocationCallbacks) :
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Private class BlockMetadata_Generic implementation
|
||||
|
||||
BlockMetadata_Generic::BlockMetadata_Generic(const ALLOCATION_CALLBACKS* allocationCallbacks) :
|
||||
BlockMetadata(allocationCallbacks),
|
||||
BlockMetadata_Generic::BlockMetadata_Generic(const ALLOCATION_CALLBACKS* allocationCallbacks, bool isVirtual) :
|
||||
BlockMetadata(allocationCallbacks, isVirtual),
|
||||
m_FreeCount(0),
|
||||
m_SumFreeSize(0),
|
||||
m_Suballocations(*allocationCallbacks),
|
||||
@ -2653,7 +2665,7 @@ void BlockMetadata_Generic::Init(UINT64 size)
|
||||
suballoc.offset = 0;
|
||||
suballoc.size = size;
|
||||
suballoc.type = SUBALLOCATION_TYPE_FREE;
|
||||
suballoc.allocation = NULL;
|
||||
suballoc.userData = NULL;
|
||||
|
||||
D3D12MA_ASSERT(size > MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER);
|
||||
m_Suballocations.push_back(suballoc);
|
||||
@ -2691,7 +2703,11 @@ bool BlockMetadata_Generic::Validate() const
|
||||
// Two adjacent free suballocations are invalid. They should be merged.
|
||||
D3D12MA_VALIDATE(!prevFree || !currFree);
|
||||
|
||||
D3D12MA_VALIDATE(currFree == (subAlloc.allocation == NULL));
|
||||
const Allocation* const alloc = (Allocation*)subAlloc.userData;
|
||||
if(!IsVirtual())
|
||||
{
|
||||
D3D12MA_VALIDATE(currFree == (alloc == NULL));
|
||||
}
|
||||
|
||||
if(currFree)
|
||||
{
|
||||
@ -2707,8 +2723,11 @@ bool BlockMetadata_Generic::Validate() const
|
||||
}
|
||||
else
|
||||
{
|
||||
D3D12MA_VALIDATE(subAlloc.allocation->GetOffset() == subAlloc.offset);
|
||||
D3D12MA_VALIDATE(subAlloc.allocation->GetSize() == subAlloc.size);
|
||||
if(!IsVirtual())
|
||||
{
|
||||
D3D12MA_VALIDATE(alloc->GetOffset() == subAlloc.offset);
|
||||
D3D12MA_VALIDATE(alloc->GetSize() == subAlloc.size);
|
||||
}
|
||||
|
||||
// Margin required between allocations - previous allocation must be free.
|
||||
D3D12MA_VALIDATE(D3D12MA_DEBUG_MARGIN == 0 || prevFree);
|
||||
@ -2761,6 +2780,23 @@ bool BlockMetadata_Generic::IsEmpty() const
|
||||
return (m_Suballocations.size() == 1) && (m_FreeCount == 1);
|
||||
}
|
||||
|
||||
void BlockMetadata_Generic::GetAllocationInfo(UINT64 offset, VIRTUAL_ALLOCATION_INFO& outInfo) const
|
||||
{
|
||||
for(SuballocationList::const_iterator suballocItem = m_Suballocations.cbegin();
|
||||
suballocItem != m_Suballocations.cend();
|
||||
++suballocItem)
|
||||
{
|
||||
const Suballocation& suballoc = *suballocItem;
|
||||
if(suballoc.offset == offset)
|
||||
{
|
||||
outInfo.size = suballoc.size;
|
||||
outInfo.pUserData = suballoc.userData;
|
||||
return;
|
||||
}
|
||||
}
|
||||
D3D12MA_ASSERT(0 && "Not found!");
|
||||
}
|
||||
|
||||
bool BlockMetadata_Generic::CreateAllocationRequest(
|
||||
UINT64 allocSize,
|
||||
UINT64 allocAlignment,
|
||||
@ -2810,7 +2846,7 @@ bool BlockMetadata_Generic::CreateAllocationRequest(
|
||||
void BlockMetadata_Generic::Alloc(
|
||||
const AllocationRequest& request,
|
||||
UINT64 allocSize,
|
||||
Allocation* allocation)
|
||||
void* userData)
|
||||
{
|
||||
D3D12MA_ASSERT(request.item != m_Suballocations.end());
|
||||
Suballocation& suballoc = *request.item;
|
||||
@ -2829,7 +2865,7 @@ void BlockMetadata_Generic::Alloc(
|
||||
suballoc.offset = request.offset;
|
||||
suballoc.size = allocSize;
|
||||
suballoc.type = SUBALLOCATION_TYPE_ALLOCATION;
|
||||
suballoc.allocation = allocation;
|
||||
suballoc.userData = userData;
|
||||
|
||||
// If there are any free bytes remaining at the end, insert new free suballocation after current one.
|
||||
if(paddingEnd)
|
||||
@ -2872,23 +2908,6 @@ void BlockMetadata_Generic::Alloc(
|
||||
m_ZeroInitializedRange.MarkRangeAsUsed(request.offset, request.offset + allocSize);
|
||||
}
|
||||
|
||||
void BlockMetadata_Generic::Free(const Allocation* allocation)
|
||||
{
|
||||
for(SuballocationList::iterator suballocItem = m_Suballocations.begin();
|
||||
suballocItem != m_Suballocations.end();
|
||||
++suballocItem)
|
||||
{
|
||||
Suballocation& suballoc = *suballocItem;
|
||||
if(suballoc.allocation == allocation)
|
||||
{
|
||||
FreeSuballocation(suballocItem);
|
||||
D3D12MA_HEAVY_ASSERT(Validate());
|
||||
return;
|
||||
}
|
||||
}
|
||||
D3D12MA_ASSERT(0 && "Not found!");
|
||||
}
|
||||
|
||||
void BlockMetadata_Generic::FreeAtOffset(UINT64 offset)
|
||||
{
|
||||
for(SuballocationList::iterator suballocItem = m_Suballocations.begin();
|
||||
@ -2905,6 +2924,22 @@ void BlockMetadata_Generic::FreeAtOffset(UINT64 offset)
|
||||
D3D12MA_ASSERT(0 && "Not found!");
|
||||
}
|
||||
|
||||
void BlockMetadata_Generic::Clear()
|
||||
{
|
||||
m_FreeCount = 1;
|
||||
m_SumFreeSize = GetSize();
|
||||
|
||||
m_Suballocations.clear();
|
||||
Suballocation suballoc = {};
|
||||
suballoc.offset = 0;
|
||||
suballoc.size = GetSize();
|
||||
suballoc.type = SUBALLOCATION_TYPE_FREE;
|
||||
m_Suballocations.push_back(suballoc);
|
||||
|
||||
m_FreeSuballocationsBySize.clear();
|
||||
m_FreeSuballocationsBySize.push_back(m_Suballocations.begin());
|
||||
}
|
||||
|
||||
bool BlockMetadata_Generic::ValidateFreeSuballocationList() const
|
||||
{
|
||||
UINT64 lastSize = 0;
|
||||
@ -2997,7 +3032,7 @@ SuballocationList::iterator BlockMetadata_Generic::FreeSuballocation(Suballocati
|
||||
// Change this suballocation to be marked as free.
|
||||
Suballocation& suballoc = *suballocItem;
|
||||
suballoc.type = SUBALLOCATION_TYPE_FREE;
|
||||
suballoc.allocation = NULL;
|
||||
suballoc.userData = NULL;
|
||||
|
||||
// Update totals.
|
||||
++m_FreeCount;
|
||||
@ -3102,6 +3137,22 @@ void BlockMetadata_Generic::UnregisterFreeSuballocation(SuballocationList::itera
|
||||
//D3D12MA_HEAVY_ASSERT(ValidateFreeSuballocationList());
|
||||
}
|
||||
|
||||
void BlockMetadata_Generic::SetAllocationUserData(UINT64 offset, void* userData)
|
||||
{
|
||||
for(SuballocationList::iterator suballocItem = m_Suballocations.begin();
|
||||
suballocItem != m_Suballocations.end();
|
||||
++suballocItem)
|
||||
{
|
||||
Suballocation& suballoc = *suballocItem;
|
||||
if(suballoc.offset == offset)
|
||||
{
|
||||
suballoc.userData = userData;
|
||||
return;
|
||||
}
|
||||
}
|
||||
D3D12MA_ASSERT(0 && "Not found!");
|
||||
}
|
||||
|
||||
void BlockMetadata_Generic::CalcAllocationStatInfo(StatInfo& outInfo) const
|
||||
{
|
||||
outInfo.BlockCount = 1;
|
||||
@ -3164,9 +3215,21 @@ void BlockMetadata_Generic::WriteAllocationInfoToJson(JsonWriter& json) const
|
||||
json.WriteString(L"Size");
|
||||
json.WriteNumber(suballoc.size);
|
||||
}
|
||||
else if(IsVirtual())
|
||||
{
|
||||
json.WriteString(L"Type");
|
||||
json.WriteString(L"ALLOCATION");
|
||||
json.WriteString(L"Size");
|
||||
json.WriteNumber(suballoc.size);
|
||||
if(suballoc.userData)
|
||||
{
|
||||
json.WriteString(L"UserData");
|
||||
json.WriteNumber((uintptr_t)suballoc.userData);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const Allocation* const alloc = suballoc.allocation;
|
||||
const Allocation* const alloc = (const Allocation*)suballoc.userData;
|
||||
D3D12MA_ASSERT(alloc);
|
||||
json.AddAllocationToObject(*alloc);
|
||||
}
|
||||
@ -3212,7 +3275,7 @@ HRESULT NormalBlock::Init()
|
||||
return hr;
|
||||
}
|
||||
|
||||
m_pMetadata = D3D12MA_NEW(m_Allocator->GetAllocs(), BlockMetadata_Generic)(&m_Allocator->GetAllocs());
|
||||
m_pMetadata = D3D12MA_NEW(m_Allocator->GetAllocs(), BlockMetadata_Generic)(&m_Allocator->GetAllocs(), false);
|
||||
m_pMetadata->Init(m_Size);
|
||||
|
||||
return hr;
|
||||
@ -3501,7 +3564,7 @@ void BlockVector::Free(Allocation* hAllocation)
|
||||
|
||||
NormalBlock* pBlock = hAllocation->m_Placed.block;
|
||||
|
||||
pBlock->m_pMetadata->Free(hAllocation);
|
||||
pBlock->m_pMetadata->FreeAtOffset(hAllocation->GetOffset());
|
||||
D3D12MA_HEAVY_ASSERT(pBlock->Validate());
|
||||
|
||||
const size_t blockCount = m_Blocks.size();
|
||||
@ -5481,14 +5544,14 @@ void Allocator::GetBudget(Budget* pGpuBudget, Budget* pCpuBudget)
|
||||
m_Pimpl->GetBudget(pGpuBudget, pCpuBudget);
|
||||
}
|
||||
|
||||
void Allocator::BuildStatsString(WCHAR** ppStatsString, BOOL DetailedMap)
|
||||
void Allocator::BuildStatsString(WCHAR** ppStatsString, BOOL DetailedMap) const
|
||||
{
|
||||
D3D12MA_ASSERT(ppStatsString);
|
||||
D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK
|
||||
m_Pimpl->BuildStatsString(ppStatsString, DetailedMap);
|
||||
}
|
||||
|
||||
void Allocator::FreeStatsString(WCHAR* pStatsString)
|
||||
void Allocator::FreeStatsString(WCHAR* pStatsString) const
|
||||
{
|
||||
if (pStatsString != NULL)
|
||||
{
|
||||
@ -5497,6 +5560,169 @@ void Allocator::FreeStatsString(WCHAR* pStatsString)
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Private class VirtualBlockPimpl definition
|
||||
|
||||
class VirtualBlockPimpl
|
||||
{
|
||||
public:
|
||||
const ALLOCATION_CALLBACKS m_AllocationCallbacks;
|
||||
const UINT64 m_Size;
|
||||
BlockMetadata_Generic m_Metadata;
|
||||
|
||||
VirtualBlockPimpl(const ALLOCATION_CALLBACKS& allocationCallbacks, UINT64 size);
|
||||
~VirtualBlockPimpl();
|
||||
};
|
||||
|
||||
VirtualBlockPimpl::VirtualBlockPimpl(const ALLOCATION_CALLBACKS& allocationCallbacks, UINT64 size) :
|
||||
m_AllocationCallbacks(allocationCallbacks),
|
||||
m_Size(size),
|
||||
m_Metadata(&m_AllocationCallbacks,
|
||||
true) // isVirtual
|
||||
{
|
||||
m_Metadata.Init(m_Size);
|
||||
}
|
||||
|
||||
VirtualBlockPimpl::~VirtualBlockPimpl()
|
||||
{
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Public class VirtualBlock implementation
|
||||
|
||||
VirtualBlock::VirtualBlock(const ALLOCATION_CALLBACKS& allocationCallbacks, const VIRTUAL_BLOCK_DESC& desc) :
|
||||
m_Pimpl(D3D12MA_NEW(allocationCallbacks, VirtualBlockPimpl)(allocationCallbacks, desc.Size))
|
||||
{
|
||||
}
|
||||
|
||||
VirtualBlock::~VirtualBlock()
|
||||
{
|
||||
// THIS IS AN IMPORTANT ASSERT!
|
||||
// Hitting it means you have some memory leak - unreleased allocations in this virtual block.
|
||||
D3D12MA_ASSERT(m_Pimpl->m_Metadata.IsEmpty() && "Some allocations were not freed before destruction of this virtual block!");
|
||||
|
||||
D3D12MA_DELETE(m_Pimpl->m_AllocationCallbacks, m_Pimpl);
|
||||
}
|
||||
|
||||
void VirtualBlock::Release()
|
||||
{
|
||||
D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK
|
||||
|
||||
// Copy is needed because otherwise we would call destructor and invalidate the structure with callbacks before using it to free memory.
|
||||
const ALLOCATION_CALLBACKS allocationCallbacksCopy = m_Pimpl->m_AllocationCallbacks;
|
||||
D3D12MA_DELETE(allocationCallbacksCopy, this);
|
||||
}
|
||||
|
||||
BOOL VirtualBlock::IsEmpty() const
|
||||
{
|
||||
D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK
|
||||
|
||||
return m_Pimpl->m_Metadata.IsEmpty() ? TRUE : FALSE;
|
||||
}
|
||||
|
||||
void VirtualBlock::GetAllocationInfo(UINT64 offset, VIRTUAL_ALLOCATION_INFO* pInfo) const
|
||||
{
|
||||
D3D12MA_ASSERT(offset != UINT64_MAX && pInfo);
|
||||
|
||||
D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK
|
||||
|
||||
m_Pimpl->m_Metadata.GetAllocationInfo(offset, *pInfo);
|
||||
}
|
||||
|
||||
HRESULT VirtualBlock::Allocate(const VIRTUAL_ALLOCATION_DESC* pDesc, UINT64* pOffset)
|
||||
{
|
||||
if(!pDesc || !pOffset || pDesc->size == 0 || !IsPow2(pDesc->alignment))
|
||||
{
|
||||
D3D12MA_ASSERT(0 && "Invalid arguments passed to VirtualBlock::Allocate.");
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
*pOffset = UINT64_MAX;
|
||||
|
||||
D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK
|
||||
|
||||
const UINT64 alignment = pDesc->alignment != 0 ? pDesc->alignment : 1;
|
||||
AllocationRequest allocRequest = {};
|
||||
if(m_Pimpl->m_Metadata.CreateAllocationRequest(pDesc->size, alignment, &allocRequest))
|
||||
{
|
||||
m_Pimpl->m_Metadata.Alloc(allocRequest, pDesc->size, pDesc->pUserData);
|
||||
D3D12MA_HEAVY_ASSERT(m_Pimpl->m_Metadata.Validate());
|
||||
*pOffset = allocRequest.offset;
|
||||
return S_OK;
|
||||
}
|
||||
else
|
||||
{
|
||||
return E_OUTOFMEMORY;
|
||||
}
|
||||
}
|
||||
|
||||
void VirtualBlock::FreeAllocation(UINT64 offset)
|
||||
{
|
||||
D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK
|
||||
|
||||
D3D12MA_ASSERT(offset != UINT64_MAX);
|
||||
|
||||
m_Pimpl->m_Metadata.FreeAtOffset(offset);
|
||||
D3D12MA_HEAVY_ASSERT(m_Pimpl->m_Metadata.Validate());
|
||||
}
|
||||
|
||||
void VirtualBlock::Clear()
|
||||
{
|
||||
D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK
|
||||
|
||||
m_Pimpl->m_Metadata.Clear();
|
||||
D3D12MA_HEAVY_ASSERT(m_Pimpl->m_Metadata.Validate());
|
||||
}
|
||||
|
||||
void VirtualBlock::SetAllocationUserData(UINT64 offset, void* pUserData)
|
||||
{
|
||||
D3D12MA_ASSERT(offset != UINT64_MAX);
|
||||
|
||||
D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK
|
||||
|
||||
m_Pimpl->m_Metadata.SetAllocationUserData(offset, pUserData);
|
||||
}
|
||||
|
||||
void VirtualBlock::CalculateStats(StatInfo* pInfo) const
|
||||
{
|
||||
D3D12MA_ASSERT(pInfo);
|
||||
|
||||
D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK
|
||||
|
||||
D3D12MA_HEAVY_ASSERT(m_Pimpl->m_Metadata.Validate());
|
||||
m_Pimpl->m_Metadata.CalcAllocationStatInfo(*pInfo);
|
||||
}
|
||||
|
||||
void VirtualBlock::BuildStatsString(WCHAR** ppStatsString) const
|
||||
{
|
||||
D3D12MA_ASSERT(ppStatsString);
|
||||
|
||||
D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK
|
||||
|
||||
StringBuilder sb(m_Pimpl->m_AllocationCallbacks);
|
||||
{
|
||||
JsonWriter json(m_Pimpl->m_AllocationCallbacks, sb);
|
||||
D3D12MA_HEAVY_ASSERT(m_Pimpl->m_Metadata.Validate());
|
||||
m_Pimpl->m_Metadata.WriteAllocationInfoToJson(json);
|
||||
} // Scope for JsonWriter
|
||||
|
||||
const size_t length = sb.GetLength();
|
||||
WCHAR* result = AllocateArray<WCHAR>(m_Pimpl->m_AllocationCallbacks, length + 1);
|
||||
memcpy(result, sb.GetData(), length * sizeof(WCHAR));
|
||||
result[length] = L'\0';
|
||||
*ppStatsString = result;
|
||||
}
|
||||
|
||||
void VirtualBlock::FreeStatsString(WCHAR* pStatsString) const
|
||||
{
|
||||
if (pStatsString != NULL)
|
||||
{
|
||||
D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK
|
||||
D3D12MA::Free(m_Pimpl->m_AllocationCallbacks, pStatsString);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Public global functions
|
||||
|
||||
@ -5512,7 +5738,7 @@ HRESULT CreateAllocator(const ALLOCATOR_DESC* pDesc, Allocator** ppAllocator)
|
||||
D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK
|
||||
|
||||
ALLOCATION_CALLBACKS allocationCallbacks;
|
||||
SetupAllocationCallbacks(allocationCallbacks, *pDesc);
|
||||
SetupAllocationCallbacks(allocationCallbacks, pDesc->pAllocationCallbacks);
|
||||
|
||||
*ppAllocator = D3D12MA_NEW(allocationCallbacks, Allocator)(allocationCallbacks, *pDesc);
|
||||
HRESULT hr = (*ppAllocator)->m_Pimpl->Init(*pDesc);
|
||||
@ -5524,4 +5750,21 @@ HRESULT CreateAllocator(const ALLOCATOR_DESC* pDesc, Allocator** ppAllocator)
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT CreateVirtualBlock(const VIRTUAL_BLOCK_DESC* pDesc, VirtualBlock** ppVirtualBlock)
|
||||
{
|
||||
if(!pDesc || !ppVirtualBlock)
|
||||
{
|
||||
D3D12MA_ASSERT(0 && "Invalid arguments passed to CreateVirtualBlock.");
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK
|
||||
|
||||
ALLOCATION_CALLBACKS allocationCallbacks;
|
||||
SetupAllocationCallbacks(allocationCallbacks, pDesc->pAllocationCallbacks);
|
||||
|
||||
*ppVirtualBlock = D3D12MA_NEW(allocationCallbacks, VirtualBlock)(allocationCallbacks, *pDesc);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
} // namespace D3D12MA
|
||||
|
@ -24,7 +24,7 @@
|
||||
|
||||
/** \mainpage D3D12 Memory Allocator
|
||||
|
||||
<b>Version 2.0.0-development</b> (2020-05-25)
|
||||
<b>Version 2.0.0-development</b> (2020-06-15)
|
||||
|
||||
Copyright (c) 2019-2020 Advanced Micro Devices, Inc. All rights reserved. \n
|
||||
License: MIT
|
||||
@ -425,6 +425,7 @@ class PoolPimpl;
|
||||
class NormalBlock;
|
||||
class BlockVector;
|
||||
class JsonWriter;
|
||||
class VirtualBlockPimpl;
|
||||
/// \endcond
|
||||
|
||||
class Pool;
|
||||
@ -1074,10 +1075,10 @@ public:
|
||||
/** @param[out] ppStatsString Must be freed using Allocator::FreeStatsString.
|
||||
@param DetailedMap `TRUE` to include full list of allocations (can make the string quite long), `FALSE` to only return statistics.
|
||||
*/
|
||||
void BuildStatsString(WCHAR** ppStatsString, BOOL DetailedMap);
|
||||
void BuildStatsString(WCHAR** ppStatsString, BOOL DetailedMap) const;
|
||||
|
||||
/// Frees memory of a string returned from Allocator::BuildStatsString.
|
||||
void FreeStatsString(WCHAR* pStatsString);
|
||||
void FreeStatsString(WCHAR* pStatsString) const;
|
||||
|
||||
private:
|
||||
friend HRESULT CreateAllocator(const ALLOCATOR_DESC*, Allocator**);
|
||||
@ -1092,12 +1093,134 @@ private:
|
||||
D3D12MA_CLASS_NO_COPY(Allocator)
|
||||
};
|
||||
|
||||
/** \brief Creates new main Allocator object and returns it through `ppAllocator`.
|
||||
/// Parameters of created D3D12MA::VirtualBlock object to be passed to CreateVirtualBlock().
|
||||
struct VIRTUAL_BLOCK_DESC
|
||||
{
|
||||
/** \brief Total size of the block.
|
||||
|
||||
Sizes can be expressed in bytes or any units you want as long as you are consistent in using them.
|
||||
For example, if you allocate from some array of structures, 1 can mean single instance of entire structure.
|
||||
*/
|
||||
UINT64 Size;
|
||||
/** \brief Custom CPU memory allocation callbacks. Optional.
|
||||
|
||||
Optional, can be null. When specified, will be used for all CPU-side memory allocations.
|
||||
*/
|
||||
const ALLOCATION_CALLBACKS* pAllocationCallbacks;
|
||||
};
|
||||
|
||||
/// Parameters of created virtual allocation to be passed to VirtualBlock::Allocate().
|
||||
struct VIRTUAL_ALLOCATION_DESC
|
||||
{
|
||||
/** \brief Size of the allocation.
|
||||
|
||||
Cannot be zero.
|
||||
*/
|
||||
UINT64 size;
|
||||
/** \brief Required alignment of the allocation.
|
||||
|
||||
Must be power of two. Special value 0 has the same meaning as 1 - means no special alignment is required, so allocation can start at any offset.
|
||||
*/
|
||||
UINT64 alignment;
|
||||
/** \brief Custom pointer to be associated with the allocation.
|
||||
|
||||
It can be fetched or changed later.
|
||||
*/
|
||||
void* pUserData;
|
||||
};
|
||||
|
||||
/// Parameters of an existing virtual allocation, returned by VirtualBlock::GetAllocationInfo().
|
||||
struct VIRTUAL_ALLOCATION_INFO
|
||||
{
|
||||
/** \brief Size of the allocation.
|
||||
|
||||
Same value as passed in VIRTUAL_ALLOCATION_DESC::size.
|
||||
*/
|
||||
UINT64 size;
|
||||
/** \brief Custom pointer associated with the allocation.
|
||||
|
||||
Same value as passed in VIRTUAL_ALLOCATION_DESC::pUserData or VirtualBlock::SetAllocationUserData().
|
||||
*/
|
||||
void* pUserData;
|
||||
};
|
||||
|
||||
/** \brief Represents pure allocation algorithm and a data structure with allocations in some memory block, without actually allocating any GPU memory.
|
||||
|
||||
This class allows to use the core algorithm of the library custom allocations e.g. CPU memory or
|
||||
sub-allocation regions inside a single GPU buffer.
|
||||
|
||||
To create this object, fill in D3D12MA::VIRTUAL_BLOCK_DESC and call CreateVirtualBlock().
|
||||
To destroy it, call its method VirtualBlock::Release().
|
||||
*/
|
||||
class VirtualBlock
|
||||
{
|
||||
public:
|
||||
/** \brief Destroys this object and frees it from memory.
|
||||
|
||||
You need to free all the allocations within this block or call Clear() before destroying it.
|
||||
*/
|
||||
void Release();
|
||||
|
||||
/** \brief Returns true if the block is empty - contains 0 allocations.
|
||||
*/
|
||||
BOOL IsEmpty() const;
|
||||
/** \brief Returns information about an allocation at given offset - its size and custom pointer.
|
||||
*/
|
||||
void GetAllocationInfo(UINT64 offset, VIRTUAL_ALLOCATION_INFO* pInfo) const;
|
||||
|
||||
/** \brief Creates new allocation.
|
||||
\param pDesc
|
||||
\param[out] pOffset Offset of the new allocation, which can also be treated as an unique identifier of the allocation within this block. `UINT64_MAX` if allocation failed.
|
||||
\return `S_OK` if allocation succeeded, `E_OUTOFMEMORY` if it failed.
|
||||
*/
|
||||
HRESULT Allocate(const VIRTUAL_ALLOCATION_DESC* pDesc, UINT64* pOffset);
|
||||
/** \brief Frees the allocation at given offset.
|
||||
*/
|
||||
void FreeAllocation(UINT64 offset);
|
||||
/** \brief Frees all the allocations.
|
||||
*/
|
||||
void Clear();
|
||||
/** \brief Changes custom pointer for an allocation at given offset to a new value.
|
||||
*/
|
||||
void SetAllocationUserData(UINT64 offset, void* pUserData);
|
||||
|
||||
/** \brief Retrieves statistics from the current state of the block.
|
||||
*/
|
||||
void CalculateStats(StatInfo* pInfo) const;
|
||||
|
||||
/** \brief Builds and returns statistics as a string in JSON format, including the list of allocations with their parameters.
|
||||
@param[out] ppStatsString Must be freed using VirtualBlock::FreeStatsString.
|
||||
*/
|
||||
void BuildStatsString(WCHAR** ppStatsString) const;
|
||||
|
||||
/** \brief Frees memory of a string returned from VirtualBlock::BuildStatsString.
|
||||
*/
|
||||
void FreeStatsString(WCHAR* pStatsString) const;
|
||||
|
||||
private:
|
||||
friend HRESULT CreateVirtualBlock(const VIRTUAL_BLOCK_DESC*, VirtualBlock**);
|
||||
template<typename T> friend void D3D12MA_DELETE(const ALLOCATION_CALLBACKS&, T*);
|
||||
|
||||
VirtualBlockPimpl* m_Pimpl;
|
||||
|
||||
VirtualBlock(const ALLOCATION_CALLBACKS& allocationCallbacks, const VIRTUAL_BLOCK_DESC& desc);
|
||||
~VirtualBlock();
|
||||
|
||||
D3D12MA_CLASS_NO_COPY(VirtualBlock)
|
||||
};
|
||||
|
||||
/** \brief Creates new main D3D12MA::Allocator object and returns it through `ppAllocator`.
|
||||
|
||||
You normally only need to call it once and keep a single Allocator object for your `ID3D12Device`.
|
||||
*/
|
||||
HRESULT CreateAllocator(const ALLOCATOR_DESC* pDesc, Allocator** ppAllocator);
|
||||
|
||||
/** \brief Creates new D3D12MA::VirtualBlock object and returns it through `ppVirtualBlock`.
|
||||
|
||||
Note you don't need to create D3D12MA::Allocator to use virtual blocks.
|
||||
*/
|
||||
HRESULT CreateVirtualBlock(const VIRTUAL_BLOCK_DESC* pDesc, VirtualBlock** ppVirtualBlock);
|
||||
|
||||
} // namespace D3D12MA
|
||||
|
||||
/// \cond INTERNAL
|
||||
|
@ -49,6 +49,7 @@ static const bool ENABLE_DEBUG_LAYER = true;
|
||||
static const bool ENABLE_CPU_ALLOCATION_CALLBACKS = true;
|
||||
static const bool ENABLE_CPU_ALLOCATION_CALLBACKS_PRINT = false;
|
||||
static constexpr D3D12MA::ALLOCATOR_FLAGS g_AllocatorFlags = D3D12MA::ALLOCATOR_FLAG_NONE;
|
||||
static D3D12MA::ALLOCATION_CALLBACKS g_AllocationCallbacks = {}; // Used only when ENABLE_CPU_ALLOCATION_CALLBACKS
|
||||
|
||||
static HINSTANCE g_Instance;
|
||||
static HWND g_Wnd;
|
||||
@ -422,13 +423,12 @@ void InitD3D() // initializes direct3d 12
|
||||
desc.pDevice = device;
|
||||
desc.pAdapter = adapter;
|
||||
|
||||
D3D12MA::ALLOCATION_CALLBACKS allocationCallbacks = {};
|
||||
if(ENABLE_CPU_ALLOCATION_CALLBACKS)
|
||||
{
|
||||
allocationCallbacks.pAllocate = &CustomAllocate;
|
||||
allocationCallbacks.pFree = &CustomFree;
|
||||
allocationCallbacks.pUserData = CUSTOM_ALLOCATION_USER_DATA;
|
||||
desc.pAllocationCallbacks = &allocationCallbacks;
|
||||
g_AllocationCallbacks.pAllocate = &CustomAllocate;
|
||||
g_AllocationCallbacks.pFree = &CustomFree;
|
||||
g_AllocationCallbacks.pUserData = CUSTOM_ALLOCATION_USER_DATA;
|
||||
desc.pAllocationCallbacks = &g_AllocationCallbacks;
|
||||
}
|
||||
|
||||
CHECK_HR( D3D12MA::CreateAllocator(&desc, &g_Allocator) );
|
||||
@ -1373,6 +1373,7 @@ static void ExecuteTests()
|
||||
try
|
||||
{
|
||||
TestContext ctx = {};
|
||||
ctx.allocationCallbacks = &g_AllocationCallbacks;
|
||||
ctx.device = g_Device;
|
||||
ctx.allocator = g_Allocator;
|
||||
ctx.allocatorFlags = g_AllocatorFlags;
|
||||
|
55
src/Doxyfile
55
src/Doxyfile
@ -1,4 +1,4 @@
|
||||
# Doxyfile 1.8.16
|
||||
# Doxyfile 1.8.18
|
||||
|
||||
# This file describes the settings to be used by the documentation system
|
||||
# doxygen (www.doxygen.org) for a project.
|
||||
@ -263,12 +263,6 @@ TAB_SIZE = 4
|
||||
|
||||
ALIASES =
|
||||
|
||||
# This tag can be used to specify a number of word-keyword mappings (TCL only).
|
||||
# A mapping has the form "name=value". For example adding "class=itcl::class"
|
||||
# will allow you to use the command class in the itcl::class meaning.
|
||||
|
||||
TCL_SUBST =
|
||||
|
||||
# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
|
||||
# only. Doxygen will then generate output that is more tailored for C. For
|
||||
# instance, some of the names that are used will be different. The list of all
|
||||
@ -309,14 +303,14 @@ OPTIMIZE_OUTPUT_SLICE = NO
|
||||
# parses. With this tag you can assign which parser to use for a given
|
||||
# extension. Doxygen has a built-in mapping, but you can override or extend it
|
||||
# using this tag. The format is ext=language, where ext is a file extension, and
|
||||
# language is one of the parsers supported by doxygen: IDL, Java, Javascript,
|
||||
# Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice,
|
||||
# language is one of the parsers supported by doxygen: IDL, Java, JavaScript,
|
||||
# Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice, VHDL,
|
||||
# Fortran (fixed format Fortran: FortranFixed, free formatted Fortran:
|
||||
# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser
|
||||
# tries to guess whether the code is fixed or free formatted code, this is the
|
||||
# default for Fortran type files), VHDL, tcl. For instance to make doxygen treat
|
||||
# .inc files as Fortran files (default is PHP), and .f files as C (default is
|
||||
# Fortran), use: inc=Fortran f=C.
|
||||
# default for Fortran type files). For instance to make doxygen treat .inc files
|
||||
# as Fortran files (default is PHP), and .f files as C (default is Fortran),
|
||||
# use: inc=Fortran f=C.
|
||||
#
|
||||
# Note: For files without extension you can use no_extension as a placeholder.
|
||||
#
|
||||
@ -535,8 +529,8 @@ HIDE_UNDOC_MEMBERS = NO
|
||||
HIDE_UNDOC_CLASSES = NO
|
||||
|
||||
# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
|
||||
# (class|struct|union) declarations. If set to NO, these declarations will be
|
||||
# included in the documentation.
|
||||
# declarations. If set to NO, these declarations will be included in the
|
||||
# documentation.
|
||||
# The default value is: NO.
|
||||
|
||||
HIDE_FRIEND_COMPOUNDS = YES
|
||||
@ -851,8 +845,10 @@ INPUT_ENCODING = UTF-8
|
||||
# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp,
|
||||
# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,
|
||||
# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc,
|
||||
# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f95, *.f03, *.f08,
|
||||
# *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, *.qsf and *.ice.
|
||||
# *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C comment),
|
||||
# *.doc (to be provided as doxygen C comment), *.txt (to be provided as doxygen
|
||||
# C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd,
|
||||
# *.vhdl, *.ucf, *.qsf and *.ice.
|
||||
|
||||
FILE_PATTERNS = *.c \
|
||||
*.cc \
|
||||
@ -1294,9 +1290,9 @@ HTML_TIMESTAMP = NO
|
||||
|
||||
# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML
|
||||
# documentation will contain a main index with vertical navigation menus that
|
||||
# are dynamically created via Javascript. If disabled, the navigation index will
|
||||
# are dynamically created via JavaScript. If disabled, the navigation index will
|
||||
# consists of multiple levels of tabs that are statically embedded in every HTML
|
||||
# page. Disable this option to support browsers that do not have Javascript,
|
||||
# page. Disable this option to support browsers that do not have JavaScript,
|
||||
# like the Qt help browser.
|
||||
# The default value is: YES.
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
@ -1564,6 +1560,17 @@ TREEVIEW_WIDTH = 250
|
||||
|
||||
EXT_LINKS_IN_WINDOW = NO
|
||||
|
||||
# If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg
|
||||
# tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see
|
||||
# https://inkscape.org) to generate formulas as SVG images instead of PNGs for
|
||||
# the HTML output. These images will generally look nicer at scaled resolutions.
|
||||
# Possible values are: png The default and svg Looks nicer but requires the
|
||||
# pdf2svg tool.
|
||||
# The default value is: png.
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
|
||||
HTML_FORMULA_FORMAT = png
|
||||
|
||||
# Use this tag to change the font size of LaTeX formulas included as images in
|
||||
# the HTML documentation. When you change the font size after a successful
|
||||
# doxygen run you need to manually remove any form_*.png images from the HTML
|
||||
@ -1584,8 +1591,14 @@ FORMULA_FONTSIZE = 10
|
||||
|
||||
FORMULA_TRANSPARENT = YES
|
||||
|
||||
# The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands
|
||||
# to create new LaTeX commands to be used in formulas as building blocks. See
|
||||
# the section "Including formulas" for details.
|
||||
|
||||
FORMULA_MACROFILE =
|
||||
|
||||
# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
|
||||
# https://www.mathjax.org) which uses client side Javascript for the rendering
|
||||
# https://www.mathjax.org) which uses client side JavaScript for the rendering
|
||||
# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX
|
||||
# installed or if you want to formulas look prettier in the HTML output. When
|
||||
# enabled you may also need to install MathJax separately and configure the path
|
||||
@ -1613,7 +1626,7 @@ MATHJAX_FORMAT = HTML-CSS
|
||||
# Content Delivery Network so you can quickly see the result without installing
|
||||
# MathJax. However, it is strongly recommended to install a local copy of
|
||||
# MathJax from https://www.mathjax.org before deployment.
|
||||
# The default value is: https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/.
|
||||
# The default value is: https://cdn.jsdelivr.net/npm/mathjax@2.
|
||||
# This tag requires that the tag USE_MATHJAX is set to YES.
|
||||
|
||||
MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest
|
||||
@ -1655,7 +1668,7 @@ MATHJAX_CODEFILE =
|
||||
SEARCHENGINE = YES
|
||||
|
||||
# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
|
||||
# implemented using a web server instead of a web client using Javascript. There
|
||||
# implemented using a web server instead of a web client using JavaScript. There
|
||||
# are two flavors of web server based searching depending on the EXTERNAL_SEARCH
|
||||
# setting. When disabled, doxygen will generate a PHP script for searching and
|
||||
# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing
|
||||
|
128
src/Tests.cpp
128
src/Tests.cpp
@ -43,6 +43,7 @@ struct D3d12maObjDeleter
|
||||
|
||||
typedef std::unique_ptr<D3D12MA::Allocation, D3d12maObjDeleter<D3D12MA::Allocation>> AllocationUniquePtr;
|
||||
typedef std::unique_ptr<D3D12MA::Pool, D3d12maObjDeleter<D3D12MA::Pool>> PoolUniquePtr;
|
||||
typedef std::unique_ptr<D3D12MA::VirtualBlock, D3d12maObjDeleter<D3D12MA::VirtualBlock>> VirtualBlockUniquePtr;
|
||||
|
||||
struct ResourceWithAllocation
|
||||
{
|
||||
@ -118,6 +119,127 @@ static bool ValidateDataZero(const void* ptr, const UINT64 sizeInBytes)
|
||||
return true;
|
||||
}
|
||||
|
||||
static void TestVirtualBlocks(const TestContext& ctx)
|
||||
{
|
||||
wprintf(L"Test virtual blocks\n");
|
||||
|
||||
using namespace D3D12MA;
|
||||
|
||||
const UINT64 blockSize = 16 * MEGABYTE;
|
||||
const UINT64 alignment = 256;
|
||||
|
||||
// # Create block 16 MB
|
||||
|
||||
VirtualBlockUniquePtr block;
|
||||
VirtualBlock* blockPtr = nullptr;
|
||||
VIRTUAL_BLOCK_DESC blockDesc = {};
|
||||
blockDesc.pAllocationCallbacks = ctx.allocationCallbacks;
|
||||
blockDesc.Size = blockSize;
|
||||
CHECK_HR( CreateVirtualBlock(&blockDesc, &blockPtr) );
|
||||
CHECK_BOOL( blockPtr );
|
||||
block.reset(blockPtr);
|
||||
|
||||
// # Allocate 8 MB
|
||||
|
||||
VIRTUAL_ALLOCATION_DESC allocDesc = {};
|
||||
allocDesc.alignment = alignment;
|
||||
allocDesc.pUserData = (void*)(uintptr_t)1;
|
||||
allocDesc.size = 8 * MEGABYTE;
|
||||
UINT64 alloc0Offset;
|
||||
CHECK_HR( block->Allocate(&allocDesc, &alloc0Offset) );
|
||||
CHECK_BOOL( alloc0Offset < blockSize );
|
||||
|
||||
// # Validate the allocation
|
||||
|
||||
VIRTUAL_ALLOCATION_INFO allocInfo = {};
|
||||
block->GetAllocationInfo(alloc0Offset, &allocInfo);
|
||||
CHECK_BOOL( allocInfo.size == allocDesc.size );
|
||||
CHECK_BOOL( allocInfo.pUserData == allocDesc.pUserData );
|
||||
|
||||
// # Check SetUserData
|
||||
|
||||
block->SetAllocationUserData(alloc0Offset, (void*)(uintptr_t)2);
|
||||
block->GetAllocationInfo(alloc0Offset, &allocInfo);
|
||||
CHECK_BOOL( allocInfo.pUserData == (void*)(uintptr_t)2 );
|
||||
|
||||
// # Allocate 4 MB
|
||||
|
||||
allocDesc.size = 4 * MEGABYTE;
|
||||
allocDesc.alignment = alignment;
|
||||
UINT64 alloc1Offset;
|
||||
CHECK_HR( block->Allocate(&allocDesc, &alloc1Offset) );
|
||||
CHECK_BOOL( alloc1Offset < blockSize );
|
||||
CHECK_BOOL( alloc1Offset + 4 * MEGABYTE <= alloc0Offset || alloc0Offset + 8 * MEGABYTE <= alloc1Offset ); // Check if they don't overlap.
|
||||
|
||||
// # Allocate another 8 MB - it should fail
|
||||
|
||||
allocDesc.size = 8 * MEGABYTE;
|
||||
allocDesc.alignment = alignment;
|
||||
UINT64 alloc2Offset;
|
||||
CHECK_BOOL( FAILED(block->Allocate(&allocDesc, &alloc2Offset)) );
|
||||
CHECK_BOOL( alloc2Offset == UINT64_MAX );
|
||||
|
||||
// # Free the 4 MB block. Now allocation of 8 MB should succeed.
|
||||
|
||||
block->FreeAllocation(alloc1Offset);
|
||||
CHECK_HR( block->Allocate(&allocDesc, &alloc2Offset) );
|
||||
CHECK_BOOL( alloc2Offset < blockSize );
|
||||
CHECK_BOOL( alloc2Offset + 4 * MEGABYTE <= alloc0Offset || alloc0Offset + 8 * MEGABYTE <= alloc2Offset ); // Check if they don't overlap.
|
||||
|
||||
// # Calculate statistics
|
||||
|
||||
StatInfo statInfo = {};
|
||||
block->CalculateStats(&statInfo);
|
||||
CHECK_BOOL(statInfo.AllocationCount == 2);
|
||||
CHECK_BOOL(statInfo.BlockCount == 1);
|
||||
CHECK_BOOL(statInfo.UsedBytes == blockSize);
|
||||
CHECK_BOOL(statInfo.UnusedBytes + statInfo.UsedBytes == blockSize);
|
||||
|
||||
// # Generate JSON dump
|
||||
|
||||
WCHAR* json = nullptr;
|
||||
block->BuildStatsString(&json);
|
||||
{
|
||||
std::wstring str(json);
|
||||
CHECK_BOOL( str.find(L"\"UserData\": 1") != std::wstring::npos );
|
||||
CHECK_BOOL( str.find(L"\"UserData\": 2") != std::wstring::npos );
|
||||
}
|
||||
block->FreeStatsString(json);
|
||||
|
||||
// # Free alloc0, leave alloc2 unfreed.
|
||||
|
||||
block->FreeAllocation(alloc0Offset);
|
||||
|
||||
// # Test alignment
|
||||
|
||||
{
|
||||
constexpr size_t allocCount = 10;
|
||||
UINT64 allocOffset[allocCount] = {};
|
||||
for(size_t i = 0; i < allocCount; ++i)
|
||||
{
|
||||
const bool alignment0 = i == allocCount - 1;
|
||||
allocDesc.size = i * 3 + 15;
|
||||
allocDesc.alignment = alignment0 ? 0 : 8;
|
||||
CHECK_HR(block->Allocate(&allocDesc, &allocOffset[i]));
|
||||
if(!alignment0)
|
||||
{
|
||||
CHECK_BOOL(allocOffset[i] % allocDesc.alignment == 0);
|
||||
}
|
||||
}
|
||||
|
||||
for(size_t i = allocCount; i--; )
|
||||
{
|
||||
block->FreeAllocation(allocOffset[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// # Final cleanup
|
||||
|
||||
block->FreeAllocation(alloc2Offset);
|
||||
|
||||
//block->Clear();
|
||||
}
|
||||
|
||||
static void TestFrameIndexAndJson(const TestContext& ctx)
|
||||
{
|
||||
const UINT64 bufSize = 32ull * 1024;
|
||||
@ -1175,6 +1297,11 @@ static void TestMultithreading(const TestContext& ctx)
|
||||
}
|
||||
}
|
||||
|
||||
static void TestGroupVirtual(const TestContext& ctx)
|
||||
{
|
||||
TestVirtualBlocks(ctx);
|
||||
}
|
||||
|
||||
static void TestGroupBasics(const TestContext& ctx)
|
||||
{
|
||||
TestFrameIndexAndJson(ctx);
|
||||
@ -1202,6 +1329,7 @@ void Test(const TestContext& ctx)
|
||||
return;
|
||||
}
|
||||
|
||||
TestGroupVirtual(ctx);
|
||||
TestGroupBasics(ctx);
|
||||
|
||||
wprintf(L"TESTS END\n");
|
||||
|
@ -26,6 +26,7 @@
|
||||
|
||||
struct TestContext
|
||||
{
|
||||
D3D12MA::ALLOCATION_CALLBACKS* allocationCallbacks;
|
||||
ID3D12Device* device;
|
||||
D3D12MA::Allocator* allocator;
|
||||
D3D12MA::ALLOCATOR_FLAGS allocatorFlags;
|
||||
|
Loading…
Reference in New Issue
Block a user