mirror of
https://github.com/GPUOpen-LibrariesAndSDKs/D3D12MemoryAllocator.git
synced 2024-11-21 19:50:05 +00:00
Add big feature of budget management - struct Budget, Allocator::GetBudget, ALLOCATION_FLAG_WITHIN_BUDGET, ALLOCATOR_DESC::pAdapter.
Added macro D3D12MA_DXGI_1_4, D3D12MA_D3D12_HEADERS_ALREADY_INCLUDED. Some bug fixes and improvements.
This commit is contained in:
parent
87528d084a
commit
0db8cd41e4
@ -22,6 +22,13 @@
|
||||
|
||||
#include "D3D12MemAlloc.h"
|
||||
|
||||
#ifndef D3D12MA_D3D12_HEADERS_ALREADY_INCLUDED
|
||||
#include <dxgi.h>
|
||||
#if D3D12MA_DXGI_1_4
|
||||
#include <dxgi1_4.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include <mutex>
|
||||
#include <atomic>
|
||||
#include <algorithm>
|
||||
@ -165,6 +172,8 @@ static void SetupAllocationCallbacks(ALLOCATION_CALLBACKS& outAllocs, const ALLO
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Private globals - basic facilities
|
||||
|
||||
#define SAFE_RELEASE(ptr) do { if(ptr) { (ptr)->Release(); (ptr) = NULL; } } while(false)
|
||||
|
||||
#define D3D12MA_VALIDATE(cond) do { if(!(cond)) { \
|
||||
D3D12MA_ASSERT(0 && "Validation failed: " #cond); \
|
||||
return false; \
|
||||
@ -225,6 +234,10 @@ If providing your own implementation, you need to implement a subset of std::ato
|
||||
#define D3D12MA_ATOMIC_UINT32 std::atomic<UINT>
|
||||
#endif
|
||||
|
||||
#ifndef D3D12MA_ATOMIC_UINT64
|
||||
#define D3D12MA_ATOMIC_UINT64 std::atomic<UINT64>
|
||||
#endif
|
||||
|
||||
// Aligns given value up to nearest multiply of align value. For example: AlignUp(11, 8) = 16.
|
||||
// Use types like UINT, uint64_t as T.
|
||||
template <typename T>
|
||||
@ -2073,11 +2086,53 @@ private:
|
||||
|
||||
static const UINT DEFAULT_POOL_MAX_COUNT = 9;
|
||||
|
||||
struct CurrentBudgetData
|
||||
{
|
||||
D3D12MA_ATOMIC_UINT64 m_BlockBytes[HEAP_TYPE_COUNT];
|
||||
D3D12MA_ATOMIC_UINT64 m_AllocationBytes[HEAP_TYPE_COUNT];
|
||||
|
||||
D3D12MA_ATOMIC_UINT32 m_OperationsSinceBudgetFetch;
|
||||
D3D12MA_RW_MUTEX m_BudgetMutex;
|
||||
UINT64 m_D3D12UsageLocal, m_D3D12UsageNonLocal;
|
||||
UINT64 m_D3D12BudgetLocal, m_D3D12BudgetNonLocal;
|
||||
UINT64 m_BlockBytesAtBudgetFetch[HEAP_TYPE_COUNT];
|
||||
|
||||
CurrentBudgetData()
|
||||
{
|
||||
for(UINT i = 0; i < HEAP_TYPE_COUNT; ++i)
|
||||
{
|
||||
m_BlockBytes[i] = 0;
|
||||
m_AllocationBytes[i] = 0;
|
||||
m_BlockBytesAtBudgetFetch[i] = 0;
|
||||
}
|
||||
|
||||
m_D3D12UsageLocal = 0;
|
||||
m_D3D12UsageNonLocal = 0;
|
||||
m_D3D12BudgetLocal = 0;
|
||||
m_D3D12BudgetNonLocal = 0;
|
||||
m_OperationsSinceBudgetFetch = 0;
|
||||
}
|
||||
|
||||
void AddAllocation(UINT heapTypeIndex, UINT64 allocationSize)
|
||||
{
|
||||
m_AllocationBytes[heapTypeIndex] += allocationSize;
|
||||
++m_OperationsSinceBudgetFetch;
|
||||
}
|
||||
|
||||
void RemoveAllocation(UINT heapTypeIndex, UINT64 allocationSize)
|
||||
{
|
||||
m_AllocationBytes[heapTypeIndex] -= allocationSize;
|
||||
++m_OperationsSinceBudgetFetch;
|
||||
}
|
||||
};
|
||||
|
||||
class AllocatorPimpl
|
||||
{
|
||||
public:
|
||||
CurrentBudgetData m_Budget;
|
||||
|
||||
AllocatorPimpl(const ALLOCATION_CALLBACKS& allocationCallbacks, const ALLOCATOR_DESC& desc);
|
||||
HRESULT Init();
|
||||
HRESULT Init(const ALLOCATOR_DESC& desc);
|
||||
~AllocatorPimpl();
|
||||
|
||||
ID3D12Device* GetDevice() const { return m_Device; }
|
||||
@ -2118,6 +2173,9 @@ public:
|
||||
|
||||
void CalculateStats(Stats& outStats);
|
||||
|
||||
void GetBudget(Budget* outGpuBudget, Budget* outCpuBudget);
|
||||
void GetBudgetForHeapType(Budget& outBudget, D3D12_HEAP_TYPE heapType);
|
||||
|
||||
void BuildStatsString(WCHAR** ppStatsString, BOOL DetailedMap);
|
||||
|
||||
void FreeStatsString(WCHAR* pStatsString);
|
||||
@ -2133,11 +2191,15 @@ private:
|
||||
|
||||
const bool m_UseMutex;
|
||||
const bool m_AlwaysCommitted;
|
||||
ID3D12Device* m_Device;
|
||||
ID3D12Device* m_Device; // AddRef
|
||||
IDXGIAdapter* m_Adapter; // AddRef
|
||||
#if D3D12MA_DXGI_1_4
|
||||
IDXGIAdapter3* m_Adapter3; // AddRef, optional
|
||||
#endif
|
||||
UINT64 m_PreferredBlockSize;
|
||||
ALLOCATION_CALLBACKS m_AllocationCallbacks;
|
||||
D3D12MA_ATOMIC_UINT32 m_CurrentFrameIndex;
|
||||
|
||||
DXGI_ADAPTER_DESC m_AdapterDesc;
|
||||
D3D12_FEATURE_DATA_D3D12_OPTIONS m_D3D12Options;
|
||||
|
||||
typedef Vector<Allocation*> AllocationVectorType;
|
||||
@ -2193,6 +2255,11 @@ private:
|
||||
void RegisterCommittedAllocation(Allocation* alloc, D3D12_HEAP_TYPE heapType);
|
||||
// Unregisters Allocation object from m_pCommittedAllocations.
|
||||
void UnregisterCommittedAllocation(Allocation* alloc, D3D12_HEAP_TYPE heapType);
|
||||
|
||||
HRESULT UpdateD3D12Budget();
|
||||
|
||||
// Writes object { } with data of given budget.
|
||||
static void WriteBudgetToJson(JsonWriter& json, const Budget& budget);
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@ -2761,18 +2828,21 @@ NormalBlock::NormalBlock(
|
||||
UINT64 size,
|
||||
UINT id) :
|
||||
MemoryBlock(allocator, heapType, heapFlags, size, id),
|
||||
m_pMetadata(NULL),
|
||||
m_BlockVector(blockVector)
|
||||
{
|
||||
}
|
||||
|
||||
NormalBlock::~NormalBlock()
|
||||
{
|
||||
// THIS IS THE MOST IMPORTANT ASSERT IN THE ENTIRE LIBRARY!
|
||||
// Hitting it means you have some memory leak - unreleased Allocation objects.
|
||||
D3D12MA_ASSERT(m_pMetadata->IsEmpty() && "Some allocations were not freed before destruction of this memory block!");
|
||||
if(m_pMetadata != NULL)
|
||||
{
|
||||
// THIS IS THE MOST IMPORTANT ASSERT IN THE ENTIRE LIBRARY!
|
||||
// Hitting it means you have some memory leak - unreleased Allocation objects.
|
||||
D3D12MA_ASSERT(m_pMetadata->IsEmpty() && "Some allocations were not freed before destruction of this memory block!");
|
||||
|
||||
D3D12MA_DELETE(m_Allocator->GetAllocs(), m_pMetadata);
|
||||
m_pMetadata = NULL;
|
||||
D3D12MA_DELETE(m_Allocator->GetAllocs(), m_pMetadata);
|
||||
}
|
||||
}
|
||||
|
||||
HRESULT NormalBlock::Init()
|
||||
@ -2819,6 +2889,7 @@ MemoryBlock::~MemoryBlock()
|
||||
{
|
||||
if(m_Heap)
|
||||
{
|
||||
m_Allocator->m_Budget.m_BlockBytes[HeapTypeToIndex(m_HeapType)] -= m_Size;
|
||||
m_Heap->Release();
|
||||
}
|
||||
}
|
||||
@ -2833,7 +2904,12 @@ HRESULT MemoryBlock::Init()
|
||||
heapDesc.Alignment = HeapFlagsToAlignment(m_HeapFlags);
|
||||
heapDesc.Flags = m_HeapFlags;
|
||||
|
||||
return m_Allocator->GetDevice()->CreateHeap(&heapDesc, __uuidof(*m_Heap), (void**)&m_Heap);
|
||||
HRESULT hr = m_Allocator->GetDevice()->CreateHeap(&heapDesc, __uuidof(*m_Heap), (void**)&m_Heap);
|
||||
if(SUCCEEDED(hr))
|
||||
{
|
||||
m_Allocator->m_Budget.m_BlockBytes[HeapTypeToIndex(m_HeapType)] += m_Size;
|
||||
}
|
||||
return hr;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@ -2932,9 +3008,17 @@ HRESULT BlockVector::AllocatePage(
|
||||
return E_OUTOFMEMORY;
|
||||
}
|
||||
|
||||
UINT64 freeMemory;
|
||||
{
|
||||
Budget budget = {};
|
||||
m_hAllocator->GetBudgetForHeapType(budget, m_HeapType);
|
||||
freeMemory = (budget.Usage < budget.Budget) ? (budget.Budget - budget.Usage) : 0;
|
||||
}
|
||||
|
||||
const bool canCreateNewBlock =
|
||||
((createInfo.Flags & ALLOCATION_FLAG_NEVER_ALLOCATE) == 0) &&
|
||||
(m_Blocks.size() < m_MaxBlockCount);
|
||||
(m_Blocks.size() < m_MaxBlockCount) &&
|
||||
freeMemory >= size;
|
||||
|
||||
if(canCreateNewBlock)
|
||||
{
|
||||
@ -2990,7 +3074,8 @@ HRESULT BlockVector::AllocatePage(
|
||||
}
|
||||
|
||||
size_t newBlockIndex = 0;
|
||||
HRESULT hr = CreateBlock(newBlockSize, &newBlockIndex);
|
||||
HRESULT hr = newBlockSize <= freeMemory ?
|
||||
CreateBlock(newBlockSize, &newBlockIndex) : E_OUTOFMEMORY;
|
||||
// Allocation of this size failed? Try 1/2, 1/4, 1/8 of m_PreferredBlockSize.
|
||||
if(!m_ExplicitBlockSize)
|
||||
{
|
||||
@ -3001,7 +3086,8 @@ HRESULT BlockVector::AllocatePage(
|
||||
{
|
||||
newBlockSize = smallerNewBlockSize;
|
||||
++newBlockSizeShift;
|
||||
hr = CreateBlock(newBlockSize, &newBlockIndex);
|
||||
hr = newBlockSize <= freeMemory ?
|
||||
CreateBlock(newBlockSize, &newBlockIndex) : E_OUTOFMEMORY;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -3041,6 +3127,13 @@ void BlockVector::Free(Allocation* hAllocation)
|
||||
{
|
||||
NormalBlock* pBlockToDelete = NULL;
|
||||
|
||||
bool budgetExceeded = false;
|
||||
{
|
||||
Budget budget = {};
|
||||
m_hAllocator->GetBudgetForHeapType(budget, m_HeapType);
|
||||
budgetExceeded = budget.Usage >= budget.Budget;
|
||||
}
|
||||
|
||||
// Scope for lock.
|
||||
{
|
||||
MutexLockWrite lock(m_Mutex, m_hAllocator->UseMutex());
|
||||
@ -3050,11 +3143,12 @@ void BlockVector::Free(Allocation* hAllocation)
|
||||
pBlock->m_pMetadata->Free(hAllocation);
|
||||
D3D12MA_HEAVY_ASSERT(pBlock->Validate());
|
||||
|
||||
const bool canDeleteBlock = m_Blocks.size() > m_MinBlockCount;
|
||||
// pBlock became empty after this deallocation.
|
||||
if(pBlock->m_pMetadata->IsEmpty())
|
||||
{
|
||||
// Already has empty Allocation. We don't want to have two, so delete this one.
|
||||
if(m_HasEmptyBlock && m_Blocks.size() > m_MinBlockCount)
|
||||
if((m_HasEmptyBlock || budgetExceeded) && canDeleteBlock)
|
||||
{
|
||||
pBlockToDelete = pBlock;
|
||||
Remove(pBlock);
|
||||
@ -3067,7 +3161,7 @@ void BlockVector::Free(Allocation* hAllocation)
|
||||
}
|
||||
// pBlock didn't become empty, but we have another empty block - find and free that one.
|
||||
// (This is optional, heuristics.)
|
||||
else if(m_HasEmptyBlock)
|
||||
else if(m_HasEmptyBlock && canDeleteBlock)
|
||||
{
|
||||
NormalBlock* pLastBlock = m_Blocks.back();
|
||||
if(pLastBlock->m_pMetadata->IsEmpty() && m_Blocks.size() > m_MinBlockCount)
|
||||
@ -3157,6 +3251,7 @@ HRESULT BlockVector::AllocateFromBlock(
|
||||
alignment,
|
||||
pBlock);
|
||||
D3D12MA_HEAVY_ASSERT(pBlock->Validate());
|
||||
m_hAllocator->m_Budget.AddAllocation(HeapTypeToIndex(m_HeapType), size);
|
||||
return S_OK;
|
||||
}
|
||||
return E_OUTOFMEMORY;
|
||||
@ -3234,6 +3329,10 @@ AllocatorPimpl::AllocatorPimpl(const ALLOCATION_CALLBACKS& allocationCallbacks,
|
||||
m_UseMutex((desc.Flags & ALLOCATOR_FLAG_SINGLETHREADED) == 0),
|
||||
m_AlwaysCommitted((desc.Flags & ALLOCATOR_FLAG_ALWAYS_COMMITTED) != 0),
|
||||
m_Device(desc.pDevice),
|
||||
m_Adapter(desc.pAdapter),
|
||||
#if D3D12MA_DXGI_1_4
|
||||
m_Adapter3(NULL),
|
||||
#endif
|
||||
m_PreferredBlockSize(desc.PreferredBlockSize != 0 ? desc.PreferredBlockSize : D3D12MA_DEFAULT_BLOCK_SIZE),
|
||||
m_AllocationCallbacks(allocationCallbacks),
|
||||
m_CurrentFrameIndex(0)
|
||||
@ -3249,11 +3348,24 @@ AllocatorPimpl::AllocatorPimpl(const ALLOCATION_CALLBACKS& allocationCallbacks,
|
||||
{
|
||||
m_pCommittedAllocations[heapTypeIndex] = D3D12MA_NEW(GetAllocs(), AllocationVectorType)(GetAllocs());
|
||||
}
|
||||
|
||||
m_Device->AddRef();
|
||||
m_Adapter->AddRef();
|
||||
}
|
||||
|
||||
HRESULT AllocatorPimpl::Init()
|
||||
HRESULT AllocatorPimpl::Init(const ALLOCATOR_DESC& desc)
|
||||
{
|
||||
HRESULT hr = m_Device->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS, &m_D3D12Options, sizeof(m_D3D12Options));
|
||||
#if D3D12MA_DXGI_1_4
|
||||
desc.pAdapter->QueryInterface<IDXGIAdapter3>(&m_Adapter3);
|
||||
#endif
|
||||
|
||||
HRESULT hr = m_Adapter->GetDesc(&m_AdapterDesc);
|
||||
if(FAILED(hr))
|
||||
{
|
||||
return hr;
|
||||
}
|
||||
|
||||
hr = m_Device->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS, &m_D3D12Options, sizeof(m_D3D12Options));
|
||||
if(FAILED(hr))
|
||||
{
|
||||
return hr;
|
||||
@ -3277,11 +3389,24 @@ HRESULT AllocatorPimpl::Init()
|
||||
// No need to call m_pBlockVectors[i]->CreateMinBlocks here, becase minBlockCount is 0.
|
||||
}
|
||||
|
||||
#if D3D12MA_DXGI_1_4
|
||||
if(m_Adapter3)
|
||||
{
|
||||
UpdateD3D12Budget();
|
||||
}
|
||||
#endif
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
AllocatorPimpl::~AllocatorPimpl()
|
||||
{
|
||||
#if D3D12MA_DXGI_1_4
|
||||
SAFE_RELEASE(m_Adapter3);
|
||||
#endif
|
||||
SAFE_RELEASE(m_Adapter);
|
||||
SAFE_RELEASE(m_Device);
|
||||
|
||||
for(UINT i = DEFAULT_POOL_MAX_COUNT; i--; )
|
||||
{
|
||||
D3D12MA_DELETE(GetAllocs(), m_BlockVectors[i]);
|
||||
@ -3385,8 +3510,7 @@ HRESULT AllocatorPimpl::CreateResource(
|
||||
}
|
||||
else
|
||||
{
|
||||
(*ppAllocation)->Release();
|
||||
*ppAllocation = NULL;
|
||||
SAFE_RELEASE(*ppAllocation);
|
||||
return hr;
|
||||
}
|
||||
}
|
||||
@ -3479,6 +3603,16 @@ HRESULT AllocatorPimpl::AllocateCommittedResource(
|
||||
return E_OUTOFMEMORY;
|
||||
}
|
||||
|
||||
if((pAllocDesc->Flags & ALLOCATION_FLAG_WITHIN_BUDGET) != 0)
|
||||
{
|
||||
Budget budget = {};
|
||||
GetBudgetForHeapType(budget, pAllocDesc->HeapType);
|
||||
if(budget.Usage + resAllocInfo.SizeInBytes > budget.Budget)
|
||||
{
|
||||
return E_OUTOFMEMORY;
|
||||
}
|
||||
}
|
||||
|
||||
D3D12_HEAP_PROPERTIES heapProps = {};
|
||||
heapProps.Type = pAllocDesc->HeapType;
|
||||
ID3D12Resource* res = NULL;
|
||||
@ -3499,6 +3633,10 @@ HRESULT AllocatorPimpl::AllocateCommittedResource(
|
||||
}
|
||||
|
||||
RegisterCommittedAllocation(*ppAllocation, pAllocDesc->HeapType);
|
||||
|
||||
const UINT heapTypeIndex = HeapTypeToIndex(pAllocDesc->HeapType);
|
||||
m_Budget.AddAllocation(heapTypeIndex, resAllocInfo.SizeInBytes);
|
||||
m_Budget.m_BlockBytes[heapTypeIndex] += resAllocInfo.SizeInBytes;
|
||||
}
|
||||
return hr;
|
||||
}
|
||||
@ -3516,6 +3654,16 @@ HRESULT AllocatorPimpl::AllocateHeap(
|
||||
return E_OUTOFMEMORY;
|
||||
}
|
||||
|
||||
if((pAllocDesc->Flags & ALLOCATION_FLAG_WITHIN_BUDGET) != 0)
|
||||
{
|
||||
Budget budget = {};
|
||||
GetBudgetForHeapType(budget, pAllocDesc->HeapType);
|
||||
if(budget.Usage + allocInfo.SizeInBytes > budget.Budget)
|
||||
{
|
||||
return E_OUTOFMEMORY;
|
||||
}
|
||||
}
|
||||
|
||||
D3D12_HEAP_DESC heapDesc = {};
|
||||
heapDesc.SizeInBytes = allocInfo.SizeInBytes;
|
||||
heapDesc.Properties.Type = pAllocDesc->HeapType;
|
||||
@ -3529,6 +3677,10 @@ HRESULT AllocatorPimpl::AllocateHeap(
|
||||
(*ppAllocation) = D3D12MA_NEW(m_AllocationCallbacks, Allocation)();
|
||||
(*ppAllocation)->InitHeap(this, allocInfo.SizeInBytes, pAllocDesc->HeapType, heap);
|
||||
RegisterCommittedAllocation(*ppAllocation, pAllocDesc->HeapType);
|
||||
|
||||
const UINT heapTypeIndex = HeapTypeToIndex(pAllocDesc->HeapType);
|
||||
m_Budget.AddAllocation(heapTypeIndex, allocInfo.SizeInBytes);
|
||||
m_Budget.m_BlockBytes[heapTypeIndex] += allocInfo.SizeInBytes;
|
||||
}
|
||||
return hr;
|
||||
}
|
||||
@ -3676,6 +3828,11 @@ void AllocatorPimpl::FreeCommittedMemory(Allocation* allocation)
|
||||
{
|
||||
D3D12MA_ASSERT(allocation && allocation->m_Type == Allocation::TYPE_COMMITTED);
|
||||
UnregisterCommittedAllocation(allocation, allocation->m_Committed.heapType);
|
||||
|
||||
const UINT64 allocationSize = allocation->GetSize();
|
||||
const UINT heapTypeIndex = HeapTypeToIndex(allocation->m_Committed.heapType);
|
||||
m_Budget.RemoveAllocation(heapTypeIndex, allocationSize);
|
||||
m_Budget.m_BlockBytes[heapTypeIndex] -= allocationSize;
|
||||
}
|
||||
|
||||
void AllocatorPimpl::FreePlacedMemory(Allocation* allocation)
|
||||
@ -3686,6 +3843,7 @@ void AllocatorPimpl::FreePlacedMemory(Allocation* allocation)
|
||||
D3D12MA_ASSERT(block);
|
||||
BlockVector* const blockVector = block->GetBlockVector();
|
||||
D3D12MA_ASSERT(blockVector);
|
||||
m_Budget.RemoveAllocation(HeapTypeToIndex(block->GetHeapType()), allocation->GetSize());
|
||||
blockVector->Free(allocation);
|
||||
}
|
||||
|
||||
@ -3693,12 +3851,24 @@ void AllocatorPimpl::FreeHeapMemory(Allocation* allocation)
|
||||
{
|
||||
D3D12MA_ASSERT(allocation && allocation->m_Type == Allocation::TYPE_HEAP);
|
||||
UnregisterCommittedAllocation(allocation, allocation->m_Heap.heapType);
|
||||
allocation->m_Heap.heap->Release();
|
||||
SAFE_RELEASE(allocation->m_Heap.heap);
|
||||
|
||||
const UINT heapTypeIndex = HeapTypeToIndex(allocation->m_Heap.heapType);
|
||||
const UINT64 allocationSize = allocation->GetSize();
|
||||
m_Budget.m_BlockBytes[heapTypeIndex] -= allocationSize;
|
||||
m_Budget.RemoveAllocation(heapTypeIndex, allocationSize);
|
||||
}
|
||||
|
||||
void AllocatorPimpl::SetCurrentFrameIndex(UINT frameIndex)
|
||||
{
|
||||
m_CurrentFrameIndex.store(frameIndex);
|
||||
|
||||
#if D3D12MA_DXGI_1_4
|
||||
if(m_Adapter3)
|
||||
{
|
||||
UpdateD3D12Budget();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void AllocatorPimpl::CalculateStats(Stats& outStats)
|
||||
@ -3739,7 +3909,7 @@ void AllocatorPimpl::CalculateStats(Stats& outStats)
|
||||
statInfo.UnusedBytes = 0;
|
||||
statInfo.AllocationSizeMin = size;
|
||||
statInfo.AllocationSizeMax = size;
|
||||
statInfo.UnusedRangeSizeMin = 0;
|
||||
statInfo.UnusedRangeSizeMin = UINT64_MAX;
|
||||
statInfo.UnusedRangeSizeMax = 0;
|
||||
AddStatInfo(outStats.Total, statInfo);
|
||||
AddStatInfo(heapStatInfo, statInfo);
|
||||
@ -3752,6 +3922,94 @@ void AllocatorPimpl::CalculateStats(Stats& outStats)
|
||||
PostProcessStatInfo(outStats.HeapType[i]);
|
||||
}
|
||||
|
||||
void AllocatorPimpl::GetBudget(Budget* outGpuBudget, Budget* outCpuBudget)
|
||||
{
|
||||
if(outGpuBudget)
|
||||
{
|
||||
// Taking DEFAULT.
|
||||
outGpuBudget->BlockBytes = m_Budget.m_BlockBytes[0];
|
||||
outGpuBudget->AllocationBytes = m_Budget.m_AllocationBytes[0];
|
||||
}
|
||||
if(outCpuBudget)
|
||||
{
|
||||
// Taking UPLOAD + READBACK.
|
||||
outCpuBudget->BlockBytes = m_Budget.m_BlockBytes[1] + m_Budget.m_BlockBytes[2];
|
||||
outCpuBudget->AllocationBytes = m_Budget.m_AllocationBytes[1] + m_Budget.m_AllocationBytes[2];
|
||||
}
|
||||
|
||||
#if D3D12MA_DXGI_1_4
|
||||
if(m_Adapter3)
|
||||
{
|
||||
if(m_Budget.m_OperationsSinceBudgetFetch < 30)
|
||||
{
|
||||
MutexLockRead lockRead(m_Budget.m_BudgetMutex, m_UseMutex);
|
||||
if(outGpuBudget)
|
||||
{
|
||||
|
||||
if(m_Budget.m_D3D12UsageLocal + outGpuBudget->BlockBytes > m_Budget.m_BlockBytesAtBudgetFetch[0])
|
||||
{
|
||||
outGpuBudget->Usage = m_Budget.m_D3D12UsageLocal +
|
||||
outGpuBudget->BlockBytes - m_Budget.m_BlockBytesAtBudgetFetch[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
outGpuBudget->Usage = 0;
|
||||
}
|
||||
outGpuBudget->Budget = m_Budget.m_D3D12BudgetLocal;
|
||||
}
|
||||
if(outCpuBudget)
|
||||
{
|
||||
if(m_Budget.m_D3D12UsageNonLocal + outCpuBudget->BlockBytes > m_Budget.m_BlockBytesAtBudgetFetch[1] + m_Budget.m_BlockBytesAtBudgetFetch[2])
|
||||
{
|
||||
outCpuBudget->Usage = m_Budget.m_D3D12UsageNonLocal +
|
||||
outCpuBudget->BlockBytes - (m_Budget.m_BlockBytesAtBudgetFetch[1] + m_Budget.m_BlockBytesAtBudgetFetch[2]);
|
||||
}
|
||||
else
|
||||
{
|
||||
outCpuBudget->Usage = 0;
|
||||
}
|
||||
outCpuBudget->Budget = m_Budget.m_D3D12BudgetNonLocal;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
UpdateD3D12Budget(); // Outside of mutex lock
|
||||
GetBudget(outGpuBudget, outCpuBudget); // Recursion
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
if(outGpuBudget)
|
||||
{
|
||||
const UINT64 gpuMemorySize = m_AdapterDesc.DedicatedVideoMemory + m_AdapterDesc.DedicatedSystemMemory; // TODO: Is this right?
|
||||
outGpuBudget->Usage = outGpuBudget->BlockBytes;
|
||||
outGpuBudget->Budget = gpuMemorySize * 8 / 10; // 80% heuristics.
|
||||
}
|
||||
if(outCpuBudget)
|
||||
{
|
||||
const UINT64 cpuMemorySize = m_AdapterDesc.SharedSystemMemory; // TODO: Is this right?
|
||||
outCpuBudget->Usage = outCpuBudget->BlockBytes;
|
||||
outCpuBudget->Budget = cpuMemorySize * 8 / 10; // 80% heuristics.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AllocatorPimpl::GetBudgetForHeapType(Budget& outBudget, D3D12_HEAP_TYPE heapType)
|
||||
{
|
||||
switch(heapType)
|
||||
{
|
||||
case D3D12_HEAP_TYPE_DEFAULT:
|
||||
GetBudget(&outBudget, NULL);
|
||||
break;
|
||||
case D3D12_HEAP_TYPE_UPLOAD:
|
||||
case D3D12_HEAP_TYPE_READBACK:
|
||||
GetBudget(NULL, &outBudget);
|
||||
break;
|
||||
default: D3D12MA_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
static void AddStatInfoToJson(JsonWriter& json, const StatInfo& statInfo)
|
||||
{
|
||||
json.BeginObject();
|
||||
@ -3795,10 +4053,14 @@ void AllocatorPimpl::BuildStatsString(WCHAR** ppStatsString, BOOL DetailedMap)
|
||||
{
|
||||
JsonWriter json(GetAllocs(), sb);
|
||||
|
||||
Budget gpuBudget = {}, cpuBudget = {};
|
||||
GetBudget(&gpuBudget, &cpuBudget);
|
||||
|
||||
Stats stats;
|
||||
CalculateStats(stats);
|
||||
|
||||
json.BeginObject();
|
||||
|
||||
json.WriteString(L"Total");
|
||||
AddStatInfoToJson(json, stats.Total);
|
||||
for (size_t heapType = 0; heapType < HEAP_TYPE_COUNT; ++heapType)
|
||||
@ -3807,6 +4069,16 @@ void AllocatorPimpl::BuildStatsString(WCHAR** ppStatsString, BOOL DetailedMap)
|
||||
AddStatInfoToJson(json, stats.HeapType[heapType]);
|
||||
}
|
||||
|
||||
json.WriteString(L"Budget");
|
||||
json.BeginObject();
|
||||
{
|
||||
json.WriteString(L"GPU");
|
||||
WriteBudgetToJson(json, gpuBudget);
|
||||
json.WriteString(L"CPU");
|
||||
WriteBudgetToJson(json, cpuBudget);
|
||||
}
|
||||
json.EndObject();
|
||||
|
||||
if (DetailedMap)
|
||||
{
|
||||
json.WriteString(L"DetailedMap");
|
||||
@ -3905,6 +4177,60 @@ void AllocatorPimpl::FreeStatsString(WCHAR* pStatsString)
|
||||
Free(GetAllocs(), pStatsString);
|
||||
}
|
||||
|
||||
HRESULT AllocatorPimpl::UpdateD3D12Budget()
|
||||
{
|
||||
#if D3D12MA_DXGI_1_4
|
||||
D3D12MA_ASSERT(m_Adapter3);
|
||||
|
||||
DXGI_QUERY_VIDEO_MEMORY_INFO infoLocal = {};
|
||||
DXGI_QUERY_VIDEO_MEMORY_INFO infoNonLocal = {};
|
||||
HRESULT hrLocal = m_Adapter3->QueryVideoMemoryInfo(0, DXGI_MEMORY_SEGMENT_GROUP_LOCAL, &infoLocal);
|
||||
HRESULT hrNonLocal = m_Adapter3->QueryVideoMemoryInfo(0, DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL, &infoNonLocal);
|
||||
|
||||
{
|
||||
MutexLockWrite lockWrite(m_Budget.m_BudgetMutex, m_UseMutex);
|
||||
|
||||
if(SUCCEEDED(hrLocal))
|
||||
{
|
||||
m_Budget.m_D3D12UsageLocal = infoLocal.CurrentUsage;
|
||||
m_Budget.m_D3D12BudgetLocal = infoLocal.Budget;
|
||||
}
|
||||
if(SUCCEEDED(hrNonLocal))
|
||||
{
|
||||
m_Budget.m_D3D12UsageNonLocal = infoNonLocal.CurrentUsage;
|
||||
m_Budget.m_D3D12BudgetNonLocal = infoNonLocal.Budget;
|
||||
}
|
||||
|
||||
for(UINT i = 0; i < HEAP_TYPE_COUNT; ++i)
|
||||
{
|
||||
m_Budget.m_BlockBytesAtBudgetFetch[i] = m_Budget.m_BlockBytes[i].load();
|
||||
}
|
||||
|
||||
m_Budget.m_OperationsSinceBudgetFetch = 0;
|
||||
}
|
||||
|
||||
return FAILED(hrLocal) ? hrLocal : hrNonLocal;
|
||||
#else
|
||||
return S_OK;
|
||||
#endif
|
||||
}
|
||||
|
||||
void AllocatorPimpl::WriteBudgetToJson(JsonWriter& json, const Budget& budget)
|
||||
{
|
||||
json.BeginObject();
|
||||
{
|
||||
json.WriteString(L"BlockBytes");
|
||||
json.WriteNumber(budget.BlockBytes);
|
||||
json.WriteString(L"AllocationBytes");
|
||||
json.WriteNumber(budget.AllocationBytes);
|
||||
json.WriteString(L"Usage");
|
||||
json.WriteNumber(budget.Usage);
|
||||
json.WriteString(L"Budget");
|
||||
json.WriteNumber(budget.Budget);
|
||||
}
|
||||
json.EndObject();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Public class Allocation implementation
|
||||
|
||||
@ -3917,10 +4243,7 @@ void Allocation::Release()
|
||||
|
||||
D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK
|
||||
|
||||
if(m_Resource)
|
||||
{
|
||||
m_Resource->Release();
|
||||
}
|
||||
SAFE_RELEASE(m_Resource);
|
||||
|
||||
switch(m_Type)
|
||||
{
|
||||
@ -4147,6 +4470,16 @@ void Allocator::CalculateStats(Stats* pStats)
|
||||
m_Pimpl->CalculateStats(*pStats);
|
||||
}
|
||||
|
||||
void Allocator::GetBudget(Budget* pGpuBudget, Budget* pCpuBudget)
|
||||
{
|
||||
if(pGpuBudget == NULL && pCpuBudget == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK
|
||||
m_Pimpl->GetBudget(pGpuBudget, pCpuBudget);
|
||||
}
|
||||
|
||||
void Allocator::BuildStatsString(WCHAR** ppStatsString, BOOL DetailedMap)
|
||||
{
|
||||
D3D12MA_ASSERT(ppStatsString);
|
||||
@ -4168,7 +4501,7 @@ void Allocator::FreeStatsString(WCHAR* pStatsString)
|
||||
|
||||
HRESULT CreateAllocator(const ALLOCATOR_DESC* pDesc, Allocator** ppAllocator)
|
||||
{
|
||||
if(!pDesc || !ppAllocator || !pDesc->pDevice ||
|
||||
if(!pDesc || !ppAllocator || !pDesc->pDevice || !pDesc->pAdapter ||
|
||||
!(pDesc->PreferredBlockSize == 0 || (pDesc->PreferredBlockSize >= 16 && pDesc->PreferredBlockSize < 0x10000000000ull)))
|
||||
{
|
||||
D3D12MA_ASSERT(0 && "Invalid arguments passed to CreateAllocator.");
|
||||
@ -4181,7 +4514,7 @@ HRESULT CreateAllocator(const ALLOCATOR_DESC* pDesc, Allocator** ppAllocator)
|
||||
SetupAllocationCallbacks(allocationCallbacks, *pDesc);
|
||||
|
||||
*ppAllocator = D3D12MA_NEW(allocationCallbacks, Allocator)(allocationCallbacks, *pDesc);
|
||||
HRESULT hr = (*ppAllocator)->m_Pimpl->Init();
|
||||
HRESULT hr = (*ppAllocator)->m_Pimpl->Init(*pDesc);
|
||||
if(FAILED(hr))
|
||||
{
|
||||
D3D12MA_DELETE(allocationCallbacks, *ppAllocator);
|
||||
|
@ -24,7 +24,7 @@
|
||||
|
||||
/** \mainpage D3D12 Memory Allocator
|
||||
|
||||
<b>Version 1.0.0-development</b> (2019-10-09)
|
||||
<b>Version 2.0.0-development</b> (2019-11-20)
|
||||
|
||||
Copyright (c) 2019 Advanced Micro Devices, Inc. All rights reserved. \n
|
||||
License: MIT
|
||||
@ -307,8 +307,6 @@ Near future: feature parity with [Vulkan Memory Allocator](https://github.com/GP
|
||||
Later:
|
||||
|
||||
- Memory defragmentation
|
||||
- Query for memory budget using `IDXGIAdapter3::QueryVideoMemoryInfo` and
|
||||
sticking to this budget with allocations
|
||||
- Support for resource aliasing (overlap)
|
||||
- Support for multi-GPU (multi-adapter)
|
||||
|
||||
@ -325,10 +323,16 @@ Features deliberately excluded from the scope of this library:
|
||||
|
||||
*/
|
||||
|
||||
// Define this macro to 0 to disable usage of DXGI 1.4 (needed for IDXGIAdapter3 and query for memory budget).
|
||||
#ifndef D3D12MA_DXGI_1_4
|
||||
#define D3D12MA_DXGI_1_4 1
|
||||
#endif
|
||||
|
||||
// If using this library on a platform different than Windows PC, you should
|
||||
// include D3D12-compatible header before this library on your own.
|
||||
#ifdef _WIN32
|
||||
// include D3D12-compatible header before this library on your own and define this macro.
|
||||
#ifndef D3D12MA_D3D12_HEADERS_ALREADY_INCLUDED
|
||||
#include <d3d12.h>
|
||||
#include <dxgi.h>
|
||||
#endif
|
||||
|
||||
/// \cond INTERNAL
|
||||
@ -398,6 +402,10 @@ typedef enum ALLOCATION_FLAGS
|
||||
#ALLOCATION_FLAG_NEVER_ALLOCATE at the same time. It makes no sense.
|
||||
*/
|
||||
ALLOCATION_FLAG_NEVER_ALLOCATE = 0x2,
|
||||
|
||||
/** TODO
|
||||
*/
|
||||
ALLOCATION_FLAG_WITHIN_BUDGET = 0x4,
|
||||
} ALLOCATION_FLAGS;
|
||||
|
||||
/// \brief Parameters of created Allocation object. To be used with Allocator::CreateResource.
|
||||
@ -558,7 +566,10 @@ struct ALLOCATOR_DESC
|
||||
/// Flags.
|
||||
ALLOCATOR_FLAGS Flags;
|
||||
|
||||
/// Direct3D device object that the allocator should be attached to.
|
||||
/** Direct3D device object that the allocator should be attached to.
|
||||
|
||||
Allocator is doing AddRef/Release on this object.
|
||||
*/
|
||||
ID3D12Device* pDevice;
|
||||
|
||||
/** \brief Preferred size of a single `ID3D12Heap` block to be allocated.
|
||||
@ -572,6 +583,12 @@ struct ALLOCATOR_DESC
|
||||
Optional, can be null. When specified, will be used for all CPU-side memory allocations.
|
||||
*/
|
||||
const ALLOCATION_CALLBACKS* pAllocationCallbacks;
|
||||
|
||||
/** TODO
|
||||
|
||||
Allocator is doing AddRef/Release on this object.
|
||||
*/
|
||||
IDXGIAdapter* pAdapter;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -616,6 +633,44 @@ struct Stats
|
||||
StatInfo HeapType[HEAP_TYPE_COUNT];
|
||||
};
|
||||
|
||||
/** \brief Statistics of current memory usage and available budget, in bytes, for GPU or CPU memory.
|
||||
*/
|
||||
struct Budget
|
||||
{
|
||||
/** \brief Sum size of all memory blocks allocated from particular heap type, in bytes.
|
||||
*/
|
||||
UINT64 BlockBytes;
|
||||
|
||||
/** \brief Sum size of all allocations created in particular heap type, in bytes.
|
||||
|
||||
Always less or equal than `BlockBytes`.
|
||||
Difference `BlockBytes - AllocationBytes` is the amount of memory allocated but unused -
|
||||
available for new allocations or wasted due to fragmentation.
|
||||
*/
|
||||
UINT64 AllocationBytes;
|
||||
|
||||
/** \brief Estimated current memory usage of the program, in bytes.
|
||||
|
||||
Fetched from system using TODO if enabled.
|
||||
|
||||
It might be different than `BlockBytes` (usually higher) due to additional implicit objects
|
||||
also occupying the memory, like swapchain, pipeline state objects, descriptor heaps, command lists, or
|
||||
memory blocks allocated outside of this library, if any.
|
||||
*/
|
||||
UINT64 Usage;
|
||||
|
||||
/** \brief Estimated amount of memory available to the program, in bytes.
|
||||
|
||||
Fetched from system using TODO if enabled.
|
||||
|
||||
It might be different (most probably smaller) than TODO due to factors
|
||||
external to the program, like other programs also consuming system resources.
|
||||
Difference `Budget - Usage` is the amount of additional memory that can probably
|
||||
be allocated without problems. Exceeding the budget may result in various problems.
|
||||
*/
|
||||
UINT64 Budget;
|
||||
};
|
||||
|
||||
/**
|
||||
\brief Represents main object of this library initialized for particular `ID3D12Device`.
|
||||
|
||||
@ -702,6 +757,19 @@ public:
|
||||
*/
|
||||
void CalculateStats(Stats* pStats);
|
||||
|
||||
/** \brief Retrieves information about current memory budget.
|
||||
|
||||
\param[out] pGpuBudget Optional, can be null.
|
||||
\param[out] pCpuBudget Optional, can be null.
|
||||
|
||||
This function is called "get" not "calculate" because it is very fast, suitable to be called
|
||||
every frame or every allocation. For more detailed statistics use CalculateStats().
|
||||
|
||||
Note that when using allocator from multiple threads, returned information may immediately
|
||||
become outdated.
|
||||
*/
|
||||
void GetBudget(Budget* pGpuBudget, Budget* pCpuBudget);
|
||||
|
||||
/// Builds and returns statistics as a string in JSON format.
|
||||
/** @param[out] ppStatsString Must be freed using Allocator::FreeStatsString.
|
||||
*/
|
||||
|
@ -420,6 +420,7 @@ void InitD3D() // initializes direct3d 12
|
||||
D3D12MA::ALLOCATOR_DESC desc = {};
|
||||
desc.Flags = g_AllocatorFlags;
|
||||
desc.pDevice = device;
|
||||
desc.pAdapter = adapter;
|
||||
|
||||
D3D12MA::ALLOCATION_CALLBACKS allocationCallbacks = {};
|
||||
if(ENABLE_CPU_ALLOCATION_CALLBACKS)
|
||||
|
@ -481,6 +481,8 @@ static void TestStats(const TestContext& ctx)
|
||||
|
||||
for(UINT i = 0; i < count; ++i)
|
||||
{
|
||||
if(i == count / 2)
|
||||
allocDesc.Flags |= D3D12MA::ALLOCATION_FLAG_COMMITTED;
|
||||
D3D12MA::Allocation* alloc = nullptr;
|
||||
CHECK_HR( ctx.allocator->CreateResource(
|
||||
&allocDesc,
|
||||
@ -514,6 +516,18 @@ static void TestStats(const TestContext& ctx)
|
||||
CheckStatInfo(endStats.HeapType[0]);
|
||||
CheckStatInfo(endStats.HeapType[1]);
|
||||
CheckStatInfo(endStats.HeapType[2]);
|
||||
|
||||
D3D12MA::Budget gpuBudget = {}, cpuBudget = {};
|
||||
ctx.allocator->GetBudget(&gpuBudget, &cpuBudget);
|
||||
|
||||
CHECK_BOOL(gpuBudget.AllocationBytes <= gpuBudget.BlockBytes);
|
||||
CHECK_BOOL(gpuBudget.AllocationBytes == endStats.HeapType[0].UsedBytes);
|
||||
CHECK_BOOL(gpuBudget.BlockBytes == endStats.HeapType[0].UsedBytes + endStats.HeapType[0].UnusedBytes);
|
||||
|
||||
CHECK_BOOL(cpuBudget.AllocationBytes <= cpuBudget.BlockBytes);
|
||||
CHECK_BOOL(cpuBudget.AllocationBytes == endStats.HeapType[1].UsedBytes + endStats.HeapType[2].UsedBytes);
|
||||
CHECK_BOOL(cpuBudget.BlockBytes == endStats.HeapType[1].UsedBytes + endStats.HeapType[1].UnusedBytes +
|
||||
endStats.HeapType[2].UsedBytes + endStats.HeapType[2].UnusedBytes);
|
||||
}
|
||||
|
||||
static void TestTransfer(const TestContext& ctx)
|
||||
|
Loading…
Reference in New Issue
Block a user