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.
diff --git a/src/D3D12MemAlloc.cpp b/src/D3D12MemAlloc.cpp
index fdbbea9..5aee696 100644
--- a/src/D3D12MemAlloc.cpp
+++ b/src/D3D12MemAlloc.cpp
@@ -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 @@
 ////////////////////////////////////////////////////////////////////////////////

 // 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 @@
     #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 @@
 

 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 @@
 

     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 @@
 

     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 @@
     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 @@
     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 @@
 {

     if(m_Heap)

     {

+        m_Allocator->m_Budget.m_BlockBytes[HeapTypeToIndex(m_HeapType)] -= m_Size;

         m_Heap->Release();

     }

 }

@@ -2833,7 +2904,12 @@
     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 @@
         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 @@
             }

 

             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 @@
                     {

                         newBlockSize = smallerNewBlockSize;

                         ++newBlockSizeShift;

-                        hr = CreateBlock(newBlockSize, &newBlockIndex);

+                        hr = newBlockSize <= freeMemory ?

+                            CreateBlock(newBlockSize, &newBlockIndex) : E_OUTOFMEMORY;

                     }

                     else

                     {

@@ -3041,6 +3127,13 @@
 {

     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 @@
         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 @@
         }

         // 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 @@
             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 @@
     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 @@
     {

         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 @@
         // 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 @@
             }

             else

             {

-                (*ppAllocation)->Release();

-                *ppAllocation = NULL;

+                SAFE_RELEASE(*ppAllocation);

                 return hr;

             }

         }

@@ -3479,6 +3603,16 @@
         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 @@
         }

 

         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 @@
         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 @@
         (*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 @@
 {

     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 @@
     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 @@
 {

     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 @@
             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 @@
         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 @@
     {

         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 @@
             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 @@
     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 @@
 

     D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK

 

-    if(m_Resource)

-    {

-        m_Resource->Release();

-    }

+    SAFE_RELEASE(m_Resource);

 

     switch(m_Type)

     {

@@ -4147,6 +4470,16 @@
     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 @@
 

 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 @@
     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);

diff --git a/src/D3D12MemAlloc.h b/src/D3D12MemAlloc.h
index 64b1b8a..4951f66 100644
--- a/src/D3D12MemAlloc.h
+++ b/src/D3D12MemAlloc.h
@@ -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 @@
 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 @@
 

 */

 

+// 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 @@
     #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 @@
     /// 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 @@
     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 @@
     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 @@
     */

     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.

     */

diff --git a/src/D3D12Sample.cpp b/src/D3D12Sample.cpp
index aa7bfa0..9fb9e22 100644
--- a/src/D3D12Sample.cpp
+++ b/src/D3D12Sample.cpp
@@ -420,6 +420,7 @@
         D3D12MA::ALLOCATOR_DESC desc = {};

         desc.Flags = g_AllocatorFlags;

         desc.pDevice = device;

+        desc.pAdapter = adapter;

 

         D3D12MA::ALLOCATION_CALLBACKS allocationCallbacks = {};

         if(ENABLE_CPU_ALLOCATION_CALLBACKS)

diff --git a/src/Tests.cpp b/src/Tests.cpp
index 19e11c7..ee31e16 100644
--- a/src/Tests.cpp
+++ b/src/Tests.cpp
@@ -481,6 +481,8 @@
 

     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 @@
     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)