Add member ALLOCATION_DESC::ExtraHeapFlags, remove parameter Allocator::AllocateMemory heapFlags (compatibility breaking!)

Makes it possible to specify custom HeapFlags when allocating memory or creating resources. These are always created as separate allocations / committed resources.
Also added tests for it.
diff --git a/src/D3D12MemAlloc.cpp b/src/D3D12MemAlloc.cpp
index 4eef684..cfe1616 100644
--- a/src/D3D12MemAlloc.cpp
+++ b/src/D3D12MemAlloc.cpp
@@ -731,6 +731,16 @@
     return tileCount <= 16;

 }

 

+static D3D12_HEAP_FLAGS GetExtraHeapFlagsToIgnore()

+{

+    D3D12_HEAP_FLAGS result =

+        D3D12_HEAP_FLAG_DENY_BUFFERS | D3D12_HEAP_FLAG_DENY_RT_DS_TEXTURES | D3D12_HEAP_FLAG_DENY_NON_RT_DS_TEXTURES;

+#if D3D12MA_ALLOW_SHADER_ATOMICS

+    result |= D3D12_HEAP_FLAG_ALLOW_SHADER_ATOMICS;

+#endif

+    return result;

+}

+

 ////////////////////////////////////////////////////////////////////////////////

 // Private class Vector

 

@@ -2383,7 +2393,6 @@
 

     HRESULT AllocateMemory(

         const ALLOCATION_DESC* pAllocDesc,

-        D3D12_HEAP_FLAGS heapFlags,

         const D3D12_RESOURCE_ALLOCATION_INFO* pAllocInfo,

         Allocation** ppAllocation);

 

@@ -2456,7 +2465,6 @@
     // Creates and returns Allocation object.

     HRESULT AllocateHeap(

         const ALLOCATION_DESC* pAllocDesc,

-        D3D12_HEAP_FLAGS heapFlags,

         const D3D12_RESOURCE_ALLOCATION_INFO& allocInfo,

         Allocation** ppAllocation);

 

@@ -2479,7 +2487,7 @@
     UINT CalcDefaultPoolCount() const;

     UINT CalcDefaultPoolIndex(const ALLOCATION_DESC& allocDesc, const D3D12_RESOURCE_DESC& resourceDesc) const;

     // This one returns UINT32_MAX if nonstandard heap flags are used and index cannot be calculcated.

-    UINT CalcDefaultPoolIndex(const ALLOCATION_DESC& allocDesc, const D3D12_HEAP_FLAGS heapFlags) const;

+    UINT CalcDefaultPoolIndex(const ALLOCATION_DESC& allocDesc) const;

     void CalcDefaultPoolParams(D3D12_HEAP_TYPE& outHeapType, D3D12_HEAP_FLAGS& outHeapFlags, UINT index) const;

 

     // Registers Allocation object in m_pCommittedAllocations.

@@ -3688,7 +3696,20 @@
     D3D12MA_ASSERT(resAllocInfo.SizeInBytes > 0);

 

     const UINT defaultPoolIndex = CalcDefaultPoolIndex(*pAllocDesc, resourceDesc2);

-    D3D12MA_ASSERT(defaultPoolIndex != UINT32_MAX);

+    const bool requireCommittedMemory = defaultPoolIndex == UINT32_MAX;

+    if(requireCommittedMemory)

+    {

+        return AllocateCommittedResource(

+            &finalAllocDesc,

+            &resourceDesc2,

+            resAllocInfo,

+            InitialResourceState,

+            pOptimizedClearValue,

+            ppAllocation,

+            riidResource,

+            ppvResource);

+    }

+

     BlockVector* blockVector = m_BlockVectors[defaultPoolIndex];

     D3D12MA_ASSERT(blockVector);

 

@@ -3766,7 +3787,6 @@
 

 HRESULT AllocatorPimpl::AllocateMemory(

     const ALLOCATION_DESC* pAllocDesc,

-    D3D12_HEAP_FLAGS heapFlags,

     const D3D12_RESOURCE_ALLOCATION_INFO* pAllocInfo,

     Allocation** ppAllocation)

 {

@@ -3779,11 +3799,11 @@
 

     ALLOCATION_DESC finalAllocDesc = *pAllocDesc;

 

-    const UINT defaultPoolIndex = CalcDefaultPoolIndex(*pAllocDesc, heapFlags);

+    const UINT defaultPoolIndex = CalcDefaultPoolIndex(*pAllocDesc);

     bool requireCommittedMemory = (defaultPoolIndex == UINT32_MAX);

     if(requireCommittedMemory)

     {

-        return AllocateHeap(&finalAllocDesc, heapFlags, *pAllocInfo, ppAllocation);

+        return AllocateHeap(&finalAllocDesc, *pAllocInfo, ppAllocation);

     }

 

     BlockVector* blockVector = m_BlockVectors[defaultPoolIndex];

@@ -3802,7 +3822,7 @@
 

     if((finalAllocDesc.Flags & ALLOCATION_FLAG_COMMITTED) != 0)

     {

-        return AllocateHeap(&finalAllocDesc, heapFlags, *pAllocInfo, ppAllocation);

+        return AllocateHeap(&finalAllocDesc, *pAllocInfo, ppAllocation);

     }

     else

     {

@@ -3817,7 +3837,7 @@
             return hr;

         }

 

-        return AllocateHeap(&finalAllocDesc, heapFlags, *pAllocInfo, ppAllocation);

+        return AllocateHeap(&finalAllocDesc, *pAllocInfo, ppAllocation);

     }

 }

 

@@ -3855,7 +3875,7 @@
     D3D12_HEAP_PROPERTIES heapProps = {};

     heapProps.Type = pAllocDesc->HeapType;

 

-    D3D12_HEAP_FLAGS heapFlags = D3D12_HEAP_FLAG_NONE;

+    D3D12_HEAP_FLAGS heapFlags = pAllocDesc->ExtraHeapFlags;

 #if D3D12MA_ALLOW_SHADER_ATOMICS

     if((pResourceDesc->Flags & D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS) != 0)

     {

@@ -3892,7 +3912,6 @@
 

 HRESULT AllocatorPimpl::AllocateHeap(

     const ALLOCATION_DESC* pAllocDesc,

-    D3D12_HEAP_FLAGS heapFlags,

     const D3D12_RESOURCE_ALLOCATION_INFO& allocInfo,

     Allocation** ppAllocation)

 {

@@ -3913,6 +3932,7 @@
         }

     }

 

+    D3D12_HEAP_FLAGS heapFlags = pAllocDesc->ExtraHeapFlags;

 #if D3D12MA_ALLOW_SHADER_ATOMICS

     if(pAllocDesc->HeapType == D3D12_HEAP_TYPE_DEFAULT)

     {

@@ -3955,6 +3975,12 @@
 

 UINT AllocatorPimpl::CalcDefaultPoolIndex(const ALLOCATION_DESC& allocDesc, const D3D12_RESOURCE_DESC& resourceDesc) const

 {

+    const D3D12_HEAP_FLAGS extraHeapFlags = allocDesc.ExtraHeapFlags & ~GetExtraHeapFlagsToIgnore();

+    if(extraHeapFlags != 0)

+    {

+        return UINT32_MAX;

+    }

+

     UINT poolIndex = UINT_MAX;

     switch(allocDesc.HeapType)

     {

@@ -3982,15 +4008,10 @@
     return poolIndex;

 }

 

-UINT AllocatorPimpl::CalcDefaultPoolIndex(const ALLOCATION_DESC& allocDesc, const D3D12_HEAP_FLAGS heapFlags) const

+UINT AllocatorPimpl::CalcDefaultPoolIndex(const ALLOCATION_DESC& allocDesc) const

 {

-    D3D12_HEAP_FLAGS heapFlagsToIgnore =

-        D3D12_HEAP_FLAG_DENY_BUFFERS | D3D12_HEAP_FLAG_DENY_RT_DS_TEXTURES | D3D12_HEAP_FLAG_DENY_NON_RT_DS_TEXTURES;

-#if D3D12MA_ALLOW_SHADER_ATOMICS

-    heapFlagsToIgnore |= D3D12_HEAP_FLAG_ALLOW_SHADER_ATOMICS;

-#endif

-    const D3D12_HEAP_FLAGS additionalHeapFlags = heapFlags & ~heapFlagsToIgnore;

-    if(additionalHeapFlags != 0)

+    const D3D12_HEAP_FLAGS extraHeapFlags = allocDesc.ExtraHeapFlags & ~GetExtraHeapFlagsToIgnore();

+    if(extraHeapFlags != 0)

     {

         return UINT32_MAX;

     }

@@ -4008,9 +4029,9 @@
     {

         poolIndex *= 3;

 

-        const bool allowBuffers = (heapFlags & D3D12_HEAP_FLAG_DENY_BUFFERS) == 0;

-        const bool allowRtDsTextures = (heapFlags & D3D12_HEAP_FLAG_DENY_RT_DS_TEXTURES) == 0;

-        const bool allowNonRtDsTextures = (heapFlags & D3D12_HEAP_FLAG_DENY_NON_RT_DS_TEXTURES) == 0;

+        const bool allowBuffers = (allocDesc.ExtraHeapFlags & D3D12_HEAP_FLAG_DENY_BUFFERS) == 0;

+        const bool allowRtDsTextures = (allocDesc.ExtraHeapFlags & D3D12_HEAP_FLAG_DENY_RT_DS_TEXTURES) == 0;

+        const bool allowNonRtDsTextures = (allocDesc.ExtraHeapFlags & D3D12_HEAP_FLAG_DENY_NON_RT_DS_TEXTURES) == 0;

 

         const uint8_t allowedGroupCount = (allowBuffers ? 1 : 0) + (allowRtDsTextures ? 1 : 0) + (allowNonRtDsTextures ? 1 : 0);

         if(allowedGroupCount != 1)

@@ -4765,7 +4786,6 @@
 

 HRESULT Allocator::AllocateMemory(

     const ALLOCATION_DESC* pAllocDesc,

-    D3D12_HEAP_FLAGS heapFlags,

     const D3D12_RESOURCE_ALLOCATION_INFO* pAllocInfo,

     Allocation** ppAllocation)

 {

@@ -4782,7 +4802,7 @@
         return E_INVALIDARG;

     }

     D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK

-    return m_Pimpl->AllocateMemory(pAllocDesc, heapFlags, pAllocInfo, ppAllocation);

+    return m_Pimpl->AllocateMemory(pAllocDesc, pAllocInfo, ppAllocation);

 }

 

 void Allocator::SetCurrentFrameIndex(UINT frameIndex)

diff --git a/src/D3D12MemAlloc.h b/src/D3D12MemAlloc.h
index df1c0d1..56bab99 100644
--- a/src/D3D12MemAlloc.h
+++ b/src/D3D12MemAlloc.h
@@ -440,6 +440,23 @@
     It must be one of: `D3D12_HEAP_TYPE_DEFAULT`, `D3D12_HEAP_TYPE_UPLOAD`, `D3D12_HEAP_TYPE_READBACK`.

     */

     D3D12_HEAP_TYPE HeapType;

+    /** \brief Additional heap flags to be used when allocating memory.

+

+    In most cases it can be 0.

+    

+    - If you use D3D12MA::Allocator::CreateResource(), you don't need to care.

+      In case of D3D12MA::Allocator::GetD3D12Options()`.ResourceHeapTier == D3D12_RESOURCE_HEAP_TIER_1`,

+      necessary flag `D3D12_HEAP_FLAG_ALLOW_ONLY_BUFFERS`, `D3D12_HEAP_FLAG_ALLOW_ONLY_NON_RT_DS_TEXTURES`,

+      or `D3D12_HEAP_FLAG_ALLOW_ONLY_RT_DS_TEXTURES` is added automatically.

+    - If you use D3D12MA::Allocator::AllocateMemory() and

+      D3D12MA::Allocator::GetD3D12Options()`.ResourceHeapTier == D3D12_RESOURCE_HEAP_TIER_1`,

+      you must specify one of those `ALLOW_ONLY` flags. When it's `TIER_2`, you can leave it 0.

+    - If configuration macro `D3D12MA_ALLOW_SHADER_ATOMICS` is set to 1 (which is the default),

+      `D3D12_HEAP_FLAG_ALLOW_SHADER_ATOMICS` is added automatically wherever it might be needed.

+    - You can specify additional flags if needed. Then the memory will always be allocated as

+      separate block using `D3D12Device::CreateCommittedResource` or `CreateHeap`, not as part of an existing larget block.

+    */

+    D3D12_HEAP_FLAGS ExtraHeapFlags;

 };

 

 /** \brief Represents single memory allocation.

@@ -793,7 +810,6 @@
     */

     HRESULT AllocateMemory(

         const ALLOCATION_DESC* pAllocDesc,

-        D3D12_HEAP_FLAGS heapFlags,

         const D3D12_RESOURCE_ALLOCATION_INFO* pAllocInfo,

         Allocation** ppAllocation);

 

diff --git a/src/Tests.cpp b/src/Tests.cpp
index 817aea0..7d189de 100644
--- a/src/Tests.cpp
+++ b/src/Tests.cpp
@@ -217,23 +217,59 @@
 {

     wprintf(L"Test custom heap flags\n");

 

-    D3D12MA::ALLOCATION_DESC allocDesc = {};

-    allocDesc.HeapType = D3D12_HEAP_TYPE_DEFAULT;

+    // 1. Just memory heap with custom flags

+    {

+        D3D12MA::ALLOCATION_DESC allocDesc = {};

+        allocDesc.HeapType = D3D12_HEAP_TYPE_DEFAULT;

+        allocDesc.ExtraHeapFlags = D3D12_HEAP_FLAG_ALLOW_ONLY_NON_RT_DS_TEXTURES |

+            D3D12_HEAP_FLAG_SHARED; // Extra flag.

 

-    const D3D12_HEAP_FLAGS heapFlags = D3D12_HEAP_FLAG_ALLOW_ONLY_NON_RT_DS_TEXTURES |

-        D3D12_HEAP_FLAG_SHARED; // Extra flag.

+        D3D12_RESOURCE_ALLOCATION_INFO resAllocInfo = {};

+        resAllocInfo.SizeInBytes = D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT;

+        resAllocInfo.Alignment = D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT;

 

-    D3D12_RESOURCE_ALLOCATION_INFO resAllocInfo = {};

-    resAllocInfo.SizeInBytes = D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT;

-    resAllocInfo.Alignment = D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT;

+        D3D12MA::Allocation* alloc = nullptr;

+        CHECK_HR( ctx.allocator->AllocateMemory(&allocDesc, &resAllocInfo, &alloc) );

+        ResourceWithAllocation res;

+        res.allocation.reset(alloc);

 

-    D3D12MA::Allocation* alloc = nullptr;

-    CHECK_HR( ctx.allocator->AllocateMemory(&allocDesc, heapFlags, &resAllocInfo, &alloc) );

-    ResourceWithAllocation res;

-    res.allocation.reset(alloc);

+        // Must be created as separate allocation.

+        CHECK_BOOL( res.allocation->GetOffset() == 0 );

+    }

 

-    // Must be created as separate allocation.

-    CHECK_BOOL( res.allocation->GetOffset() == 0 );

+    // 2. Committed resource with custom flags

+    {

+        D3D12_RESOURCE_DESC resourceDesc = {};

+        resourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;

+        resourceDesc.Alignment = 0;

+        resourceDesc.Width = 1920;

+        resourceDesc.Height = 1080;

+        resourceDesc.DepthOrArraySize = 1;

+        resourceDesc.MipLevels = 1;

+        resourceDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;

+        resourceDesc.SampleDesc.Count = 1;

+        resourceDesc.SampleDesc.Quality = 0;

+        resourceDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;

+        resourceDesc.Flags = D3D12_RESOURCE_FLAG_ALLOW_CROSS_ADAPTER;

+

+        D3D12MA::ALLOCATION_DESC allocDesc = {};

+        allocDesc.HeapType = D3D12_HEAP_TYPE_DEFAULT;

+        allocDesc.ExtraHeapFlags = D3D12_HEAP_FLAG_SHARED | D3D12_HEAP_FLAG_SHARED_CROSS_ADAPTER; // Extra flags.

+

+        ResourceWithAllocation res;

+        D3D12MA::Allocation* alloc = nullptr;

+        CHECK_HR( ctx.allocator->CreateResource(

+            &allocDesc,

+            &resourceDesc,

+            D3D12_RESOURCE_STATE_COMMON,

+            NULL,

+            &alloc,

+            IID_PPV_ARGS(&res.resource)) );

+        res.allocation.reset(alloc);

+

+        // Must be created as committed.

+        CHECK_BOOL( res.allocation->GetHeap() == NULL );

+    }

 }

 

 static void TestPlacedResources(const TestContext& ctx)

@@ -346,6 +382,7 @@
 

     D3D12MA::ALLOCATION_DESC allocDesc = {};

     allocDesc.HeapType = D3D12_HEAP_TYPE_DEFAULT;

+    allocDesc.ExtraHeapFlags = D3D12_HEAP_FLAG_ALLOW_ONLY_RT_DS_TEXTURES;

 

     D3D12_RESOURCE_DESC resourceDesc1 = {};

     resourceDesc1.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;

@@ -383,7 +420,6 @@
     D3D12MA::Allocation* allocPtr = NULL;

     CHECK_HR( ctx.allocator->AllocateMemory(

         &allocDesc,

-        D3D12_HEAP_FLAG_ALLOW_ONLY_RT_DS_TEXTURES,

         &allocInfo,

         &allocPtr) );

     AllocationUniquePtr alloc(allocPtr);