Add custom pools

Added struct POOL_DESC, class Pool, function Allocator::CreatePool, member ALLOCATION_DESC::CustomPool.
diff --git a/src/D3D12MemAlloc.cpp b/src/D3D12MemAlloc.cpp
index 682acca..b78fa6f 100644
--- a/src/D3D12MemAlloc.cpp
+++ b/src/D3D12MemAlloc.cpp
@@ -741,6 +741,13 @@
     return result;

 }

 

+static inline bool IsHeapTypeValid(D3D12_HEAP_TYPE type)

+{

+    return type == D3D12_HEAP_TYPE_DEFAULT ||

+        type == D3D12_HEAP_TYPE_UPLOAD ||

+        type == D3D12_HEAP_TYPE_READBACK;

+}

+

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

 // Private class Vector

 

@@ -2334,6 +2341,17 @@
     void Free(

         Allocation* hAllocation);

 

+    HRESULT CreateResource(

+        UINT64 size,

+        UINT64 alignment,

+        const ALLOCATION_DESC& allocDesc,

+        const D3D12_RESOURCE_DESC& resourceDesc,

+        D3D12_RESOURCE_STATES InitialResourceState,

+        const D3D12_CLEAR_VALUE *pOptimizedClearValue,

+        Allocation** ppAllocation,

+        REFIID riidResource,

+        void** ppvResource);

+

     void AddStats(Stats& outpStats);

 

     void WriteBlockInfoToJson(JsonWriter& json);

@@ -2441,6 +2459,7 @@
     bool SupportsResourceHeapTier2() const { return m_D3D12Options.ResourceHeapTier >= D3D12_RESOURCE_HEAP_TIER_2; }

     bool UseMutex() const { return m_UseMutex; }

     AllocationObjectAllocator& GetAllocationObjectAllocator() { return m_AllocationObjectAllocator; }

+    bool HeapFlagsFulfillResourceHeapTier(D3D12_HEAP_FLAGS flags) const;

 

     HRESULT CreateResource(

         const ALLOCATION_DESC* pAllocDesc,

@@ -3502,6 +3521,46 @@
     }

 }

 

+HRESULT BlockVector::CreateResource(

+    UINT64 size,

+    UINT64 alignment,

+    const ALLOCATION_DESC& allocDesc,

+    const D3D12_RESOURCE_DESC& resourceDesc,

+    D3D12_RESOURCE_STATES InitialResourceState,

+    const D3D12_CLEAR_VALUE *pOptimizedClearValue,

+    Allocation** ppAllocation,

+    REFIID riidResource,

+    void** ppvResource)

+{

+    HRESULT hr = Allocate(size, alignment, allocDesc, 1, ppAllocation);

+    if(SUCCEEDED(hr))

+    {

+        ID3D12Resource* res = NULL;

+        hr = m_hAllocator->GetDevice()->CreatePlacedResource(

+            (*ppAllocation)->m_Placed.block->GetHeap(),

+            (*ppAllocation)->GetOffset(),

+            &resourceDesc,

+            InitialResourceState,

+            pOptimizedClearValue,

+            riidResource,

+            (void**)&res);

+        if(SUCCEEDED(hr))

+        {

+            (*ppAllocation)->SetResource(res, &resourceDesc);

+            if(ppvResource != NULL)

+            {

+                res->AddRef();

+                *ppvResource = res;

+            }

+        }

+        else

+        {

+            SAFE_RELEASE(*ppAllocation);

+        }

+    }

+    return hr;

+}

+

 UINT64 BlockVector::CalcMaxBlockSize() const

 {

     UINT64 result = 0;

@@ -3637,6 +3696,75 @@
 }

 

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

+// Private class PoolPimpl

+

+class PoolPimpl

+{

+public:

+    PoolPimpl(AllocatorPimpl* allocator, const POOL_DESC& desc) :

+        m_Allocator(allocator),

+        m_Desc(desc),

+        m_BlockVector(D3D12MA_NEW(allocator->GetAllocs(), BlockVector)(

+            allocator, desc.HeapType, desc.HeapFlags,

+            desc.BlockSize != 0 ? desc.BlockSize : D3D12MA_DEFAULT_BLOCK_SIZE, // preferredBlockSize

+            desc.MinBlockCount, desc.MaxBlockCount,

+            desc.BlockSize != 0)) // explicitBlockSize

+    {

+    }

+    ~PoolPimpl()

+    {

+        D3D12MA_DELETE(m_Allocator->GetAllocs(), m_BlockVector);

+    }

+    HRESULT Init();

+

+    AllocatorPimpl* GetAllocator() const { return m_Allocator; }

+    const POOL_DESC& GetDesc() const { return m_Desc; }

+    BlockVector* GetBlockVector() { return m_BlockVector; }

+

+private:

+    friend class Allocator;

+

+    AllocatorPimpl* m_Allocator; // Externally owned object.

+    POOL_DESC m_Desc;

+    BlockVector* m_BlockVector; // Owned object.

+};

+

+HRESULT PoolPimpl::Init()

+{

+    return m_BlockVector->CreateMinBlocks();

+}

+

+////////////////////////////////////////////////////////////////////////////////

+// Public class Pool implementation

+

+void Pool::Release()

+{

+    if(this == NULL)

+    {

+        return;

+    }

+

+    D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK

+

+    D3D12MA_DELETE(m_Pimpl->GetAllocator()->GetAllocs(), this);

+}

+

+POOL_DESC Pool::GetDesc()

+{

+    return m_Pimpl->GetDesc();

+}

+

+Pool::Pool(Allocator* allocator, const POOL_DESC &desc) :

+    m_Pimpl(D3D12MA_NEW(allocator->m_Pimpl->GetAllocs(), PoolPimpl)(allocator->m_Pimpl, desc))

+{

+}

+

+Pool::~Pool()

+{

+    D3D12MA_DELETE(m_Pimpl->GetAllocator()->GetAllocs(), m_Pimpl);

+}

+

+////////////////////////////////////////////////////////////////////////////////

 // Private class AllocatorPimpl implementation

 

 AllocatorPimpl::AllocatorPimpl(const ALLOCATION_CALLBACKS& allocationCallbacks, const ALLOCATOR_DESC& desc) :

@@ -3738,6 +3866,22 @@
     }

 }

 

+bool AllocatorPimpl::HeapFlagsFulfillResourceHeapTier(D3D12_HEAP_FLAGS flags) const

+{

+    if(SupportsResourceHeapTier2())

+    {

+        return true;

+    }

+    else

+    {

+        const bool allowBuffers         = (flags & D3D12_HEAP_FLAG_DENY_BUFFERS           ) == 0;

+        const bool allowRtDsTextures    = (flags & D3D12_HEAP_FLAG_DENY_RT_DS_TEXTURES    ) == 0;

+        const bool allowNonRtDsTextures = (flags & D3D12_HEAP_FLAG_DENY_NON_RT_DS_TEXTURES) == 0;

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

+        return allowedGroupCount == 1;

+    }

+}

+

 HRESULT AllocatorPimpl::CreateResource(

     const ALLOCATION_DESC* pAllocDesc,

     const D3D12_RESOURCE_DESC* pResourceDesc,

@@ -3753,57 +3897,36 @@
         *ppvResource = NULL;

     }

 

-    if(pAllocDesc->HeapType != D3D12_HEAP_TYPE_DEFAULT &&

-        pAllocDesc->HeapType != D3D12_HEAP_TYPE_UPLOAD &&

-        pAllocDesc->HeapType != D3D12_HEAP_TYPE_READBACK)

+    if(pAllocDesc->CustomPool == NULL)

     {

-        return E_INVALIDARG;

+        if(!IsHeapTypeValid(pAllocDesc->HeapType))

+        {

+            return E_INVALIDARG;

+        }

     }

 

     ALLOCATION_DESC finalAllocDesc = *pAllocDesc;

 

-    D3D12_RESOURCE_DESC resourceDesc2 = *pResourceDesc;

-    D3D12_RESOURCE_ALLOCATION_INFO resAllocInfo = GetResourceAllocationInfo(resourceDesc2);

+    D3D12_RESOURCE_DESC finalResourceDesc = *pResourceDesc;

+    D3D12_RESOURCE_ALLOCATION_INFO resAllocInfo = GetResourceAllocationInfo(finalResourceDesc);

     resAllocInfo.Alignment = D3D12MA_MAX<UINT64>(resAllocInfo.Alignment, D3D12MA_DEBUG_ALIGNMENT);

     D3D12MA_ASSERT(IsPow2(resAllocInfo.Alignment));

     D3D12MA_ASSERT(resAllocInfo.SizeInBytes > 0);

 

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

-    const bool requireCommittedMemory = defaultPoolIndex == UINT32_MAX;

-    if(requireCommittedMemory)

+    if(pAllocDesc->CustomPool != NULL)

     {

-        return AllocateCommittedResource(

-            &finalAllocDesc,

-            &resourceDesc2,

-            resAllocInfo,

-            InitialResourceState,

-            pOptimizedClearValue,

-            ppAllocation,

-            riidResource,

-            ppvResource);

-    }

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

+        {

+            return E_INVALIDARG;

+        }

 

-    BlockVector* blockVector = m_BlockVectors[defaultPoolIndex];

-    D3D12MA_ASSERT(blockVector);

-

-    const UINT64 preferredBlockSize = blockVector->GetPreferredBlockSize();

-    bool preferCommittedMemory =

-        m_AlwaysCommitted ||

-        PrefersCommittedAllocation(resourceDesc2) ||

-        // Heuristics: Allocate committed memory if requested size if greater than half of preferred block size.

-        resAllocInfo.SizeInBytes > preferredBlockSize / 2;

-    if(preferCommittedMemory &&

-        (finalAllocDesc.Flags & ALLOCATION_FLAG_NEVER_ALLOCATE) == 0)

-    {

-        finalAllocDesc.Flags |= ALLOCATION_FLAG_COMMITTED;

-    }

-

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

-    {

-        return AllocateCommittedResource(

-            &finalAllocDesc,

-            &resourceDesc2,

-            resAllocInfo,

+        BlockVector* blockVector = pAllocDesc->CustomPool->m_Pimpl->GetBlockVector();

+        D3D12MA_ASSERT(blockVector);

+        return blockVector->CreateResource(

+            resAllocInfo.SizeInBytes,

+            resAllocInfo.Alignment,

+            finalAllocDesc,

+            finalResourceDesc,

             InitialResourceState,

             pOptimizedClearValue,

             ppAllocation,

@@ -3812,49 +3935,75 @@
     }

     else

     {

-        HRESULT hr = blockVector->Allocate(

-            resAllocInfo.SizeInBytes,

-            resAllocInfo.Alignment,

-            finalAllocDesc,

-            1,

-            (Allocation**)ppAllocation);

-        if(SUCCEEDED(hr))

+        const UINT defaultPoolIndex = CalcDefaultPoolIndex(*pAllocDesc, finalResourceDesc);

+        const bool requireCommittedMemory = defaultPoolIndex == UINT32_MAX;

+        if(requireCommittedMemory)

         {

-            ID3D12Resource* res = NULL;

-            hr = m_Device->CreatePlacedResource(

-                (*ppAllocation)->m_Placed.block->GetHeap(),

-                (*ppAllocation)->GetOffset(),

-                &resourceDesc2,

+            return AllocateCommittedResource(

+                &finalAllocDesc,

+                &finalResourceDesc,

+                resAllocInfo,

                 InitialResourceState,

                 pOptimizedClearValue,

+                ppAllocation,

                 riidResource,

-                (void**)&res);

-            if(SUCCEEDED(hr))

-            {

-                (*ppAllocation)->SetResource(res, &resourceDesc2);

-                if(ppvResource != NULL)

-                {

-                    res->AddRef();

-                    *ppvResource = res;

-                }

-                return hr;

-            }

-            else

-            {

-                SAFE_RELEASE(*ppAllocation);

-                return hr;

-            }

+                ppvResource);

         }

 

-        return AllocateCommittedResource(

-            &finalAllocDesc,

-            &resourceDesc2,

-            resAllocInfo,

-            InitialResourceState,

-            pOptimizedClearValue,

-            ppAllocation,

-            riidResource,

-            ppvResource);

+        BlockVector* blockVector = m_BlockVectors[defaultPoolIndex];

+        D3D12MA_ASSERT(blockVector);

+

+        const UINT64 preferredBlockSize = blockVector->GetPreferredBlockSize();

+        bool preferCommittedMemory =

+            m_AlwaysCommitted ||

+            PrefersCommittedAllocation(finalResourceDesc) ||

+            // Heuristics: Allocate committed memory if requested size if greater than half of preferred block size.

+            resAllocInfo.SizeInBytes > preferredBlockSize / 2;

+        if(preferCommittedMemory &&

+            (finalAllocDesc.Flags & ALLOCATION_FLAG_NEVER_ALLOCATE) == 0)

+        {

+            finalAllocDesc.Flags |= ALLOCATION_FLAG_COMMITTED;

+        }

+

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

+        {

+            return AllocateCommittedResource(

+                &finalAllocDesc,

+                &finalResourceDesc,

+                resAllocInfo,

+                InitialResourceState,

+                pOptimizedClearValue,

+                ppAllocation,

+                riidResource,

+                ppvResource);

+        }

+        else

+        {

+            HRESULT hr = blockVector->CreateResource(

+                resAllocInfo.SizeInBytes,

+                resAllocInfo.Alignment,

+                finalAllocDesc,

+                finalResourceDesc,

+                InitialResourceState,

+                pOptimizedClearValue,

+                ppAllocation,

+                riidResource,

+                ppvResource);

+            if(SUCCEEDED(hr))

+            {

+                return hr;

+            }

+

+            return AllocateCommittedResource(

+                &finalAllocDesc,

+                &finalResourceDesc,

+                resAllocInfo,

+                InitialResourceState,

+                pOptimizedClearValue,

+                ppAllocation,

+                riidResource,

+                ppvResource);

+        }

     }

 }

 

@@ -3865,9 +4014,7 @@
 {

     *ppAllocation = NULL;

 

-    if(pAllocDesc->HeapType != D3D12_HEAP_TYPE_DEFAULT &&

-        pAllocDesc->HeapType != D3D12_HEAP_TYPE_UPLOAD &&

-        pAllocDesc->HeapType != D3D12_HEAP_TYPE_READBACK)

+    if(!IsHeapTypeValid(pAllocDesc->HeapType))

     {

         return E_INVALIDARG;

     }

@@ -4944,6 +5091,33 @@
     return m_Pimpl->CreateAliasingResource(pAllocation, AllocationLocalOffset, pResourceDesc, InitialResourceState, pOptimizedClearValue, riidResource, ppvResource);

 }

 

+HRESULT Allocator::CreatePool(

+    const POOL_DESC* pPoolDesc,

+    Pool** ppPool)

+{

+    if(!pPoolDesc || !ppPool ||

+        !IsHeapTypeValid(pPoolDesc->HeapType) ||

+        pPoolDesc->MinBlockCount > pPoolDesc->MaxBlockCount)

+    {

+        D3D12MA_ASSERT(0 && "Invalid arguments passed to Allocator::CreatePool.");

+        return E_INVALIDARG;

+    }

+    if(!m_Pimpl->HeapFlagsFulfillResourceHeapTier(pPoolDesc->HeapFlags))

+    {

+        D3D12MA_ASSERT(0 && "Invalid pPoolDesc->HeapFlags passed to Allocator::CreatePool. Did you forget to handle ResourceHeapTier=1?");

+        return E_INVALIDARG;

+    }

+    D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK

+    *ppPool = D3D12MA_NEW(m_Pimpl->GetAllocs(), Pool)(this, *pPoolDesc);

+    HRESULT hr = (*ppPool)->m_Pimpl->Init();

+    if(FAILED(hr))

+    {

+        D3D12MA_DELETE(m_Pimpl->GetAllocs(), *ppPool);

+        *ppPool = NULL;

+    }

+    return hr;

+}

+

 void Allocator::SetCurrentFrameIndex(UINT frameIndex)

 {

     D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK

diff --git a/src/D3D12MemAlloc.h b/src/D3D12MemAlloc.h
index 9bdcd5d..3466397 100644
--- a/src/D3D12MemAlloc.h
+++ b/src/D3D12MemAlloc.h
@@ -375,11 +375,15 @@
 

 /// \cond INTERNAL

 class AllocatorPimpl;

+class PoolPimpl;

 class NormalBlock;

 class BlockVector;

 class JsonWriter;

 /// \endcond

 

+class Pool;

+class Allocator;

+

 /// Pointer to custom callback function that allocates CPU memory.

 typedef void* (*ALLOCATE_FUNC_PTR)(size_t Size, size_t Alignment, void* pUserData);

 /**

@@ -438,6 +442,8 @@
     /** \brief The type of memory heap where the new allocation should be placed.

 

     It must be one of: `D3D12_HEAP_TYPE_DEFAULT`, `D3D12_HEAP_TYPE_UPLOAD`, `D3D12_HEAP_TYPE_READBACK`.

+

+    When D3D12MA::ALLOCATION_DESC::CustomPool != NULL this member is ignored.

     */

     D3D12_HEAP_TYPE HeapType;

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

@@ -455,8 +461,16 @@
       `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.

+

+    When D3D12MA::ALLOCATION_DESC::CustomPool != NULL this member is ignored.

     */

     D3D12_HEAP_FLAGS ExtraHeapFlags;

+    /** \brief Custom pool to place the new resource in. Optional.

+

+    When not NULL, the resource will be created inside specified custom pool.

+    It will then never be created as committed.

+    */

+    Pool* CustomPool;

 };

 

 /** \brief Represents single memory allocation.

@@ -621,6 +635,69 @@
     D3D12MA_CLASS_NO_COPY(Allocation)

 };

 

+struct POOL_DESC

+{

+    /** \brief The type of memory heap where allocations of this pool should be placed.

+

+    It must be one of: `D3D12_HEAP_TYPE_DEFAULT`, `D3D12_HEAP_TYPE_UPLOAD`, `D3D12_HEAP_TYPE_READBACK`.

+    */

+    D3D12_HEAP_TYPE HeapType;

+    /** \brief Heap flags to be used when allocating heaps of this pool.

+

+    If ResourceHeapTier = 1, it must contain one of these values, depending on type

+    of resources you are going to create in this heap:

+    `D3D12_HEAP_FLAG_ALLOW_ONLY_BUFFERS`,

+    `D3D12_HEAP_FLAG_ALLOW_ONLY_NON_RT_DS_TEXTURES`,

+    `D3D12_HEAP_FLAG_ALLOW_ONLY_RT_DS_TEXTURES`.

+    If ResourceHeapTier = 2, it may be `D3D12_HEAP_FLAG_ALLOW_ALL_BUFFERS_AND_TEXTURES` = 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.

+    */

+    D3D12_HEAP_FLAGS HeapFlags;

+    /** \brief Size of a single heap (memory block) to be allocated as part of this pool, in bytes. Optional.

+

+    Specify nonzero to set explicit, constant size of memory blocks used by this pool.

+    Leave 0 to use default and let the library manage block sizes automatically.

+    Then sizes of particular blocks may vary.

+    */

+    UINT64 BlockSize;

+    /** \brief Minimum number of heaps (memory blocks) to be always allocated in this pool, even if they stay empty.

+

+    Set to 0 to have no preallocated blocks and allow the pool be completely empty.

+    */

+    UINT MinBlockCount;

+    /** \brief Maximum number of heaps (memory blocks) that can be allocated in this pool. Optional.

+

+    Set to 0 to use default, which is `UINT64_MAX`, which means no limit.

+

+    Set to same value as D3D12MA::POOL_DESC::MinBlockCount to have fixed amount of memory allocated

+    throughout whole lifetime of this pool.

+    */

+    UINT MaxBlockCount;

+};

+

+class Pool

+{

+public:

+    void Release();

+    POOL_DESC GetDesc();

+

+private:

+    friend class Allocator;

+    friend class AllocatorPimpl;

+    template<typename T> friend void D3D12MA_DELETE(const ALLOCATION_CALLBACKS&, T*);

+

+    PoolPimpl* m_Pimpl;

+

+    Pool(Allocator* allocator, const POOL_DESC &desc);

+    ~Pool();

+

+    D3D12MA_CLASS_NO_COPY(Pool)

+};

+

 /// \brief Bit flags to be used with ALLOCATOR_DESC::Flags.

 typedef enum ALLOCATOR_FLAGS

 {

@@ -864,6 +941,10 @@
         REFIID riidResource,

         void** ppvResource);

 

+    HRESULT CreatePool(

+        const POOL_DESC* pPoolDesc,

+        Pool** ppPool);

+

     /** \brief Sets the index of the current frame.

 

     This function is used to set the frame index in the allocator when a new game frame begins.

@@ -899,6 +980,7 @@
 private:

     friend HRESULT CreateAllocator(const ALLOCATOR_DESC*, Allocator**);

     template<typename T> friend void D3D12MA_DELETE(const ALLOCATION_CALLBACKS&, T*);

+    friend class Pool;

 

     Allocator(const ALLOCATION_CALLBACKS& allocationCallbacks, const ALLOCATOR_DESC& desc);

     ~Allocator();

diff --git a/src/Tests.cpp b/src/Tests.cpp
index 954caed..7cfda89 100644
--- a/src/Tests.cpp
+++ b/src/Tests.cpp
@@ -27,9 +27,12 @@
 extern ID3D12GraphicsCommandList* BeginCommandList();

 extern void EndCommandList(ID3D12GraphicsCommandList* cmdList);

 

-struct AllocationDeleter

+static constexpr UINT64 MEGABYTE = 1024 * 1024;

+

+template<typename T>

+struct D3d12maObjDeleter

 {

-    void operator()(D3D12MA::Allocation* obj) const

+    void operator()(T* obj) const

     {

         if(obj)

         {

@@ -38,7 +41,8 @@
     }

 };

 

-typedef std::unique_ptr<D3D12MA::Allocation, AllocationDeleter> AllocationUniquePtr;

+typedef std::unique_ptr<D3D12MA::Allocation, D3d12maObjDeleter<D3D12MA::Allocation>> AllocationUniquePtr;

+typedef std::unique_ptr<D3D12MA::Pool, D3d12maObjDeleter<D3D12MA::Pool>> PoolUniquePtr;

 

 struct ResourceWithAllocation

 {

@@ -399,6 +403,84 @@
     renderTargetRes.allocation.reset(alloc);

 }

 

+static void TestCustomPools(const TestContext& ctx)

+{

+    wprintf(L"Test custom pools\n");

+

+    // # Create pool, 1..2 blocks of 11 MB

+    

+    D3D12MA::POOL_DESC poolDesc = {};

+    poolDesc.HeapType = D3D12_HEAP_TYPE_DEFAULT;

+    poolDesc.HeapFlags = D3D12_HEAP_FLAG_ALLOW_ONLY_BUFFERS;

+    poolDesc.BlockSize = 11 * MEGABYTE;

+    poolDesc.MinBlockCount = 1;

+    poolDesc.MaxBlockCount = 2;

+

+    D3D12MA::Pool* poolPtr;

+    CHECK_HR( ctx.allocator->CreatePool(&poolDesc, &poolPtr) );

+    PoolUniquePtr pool{poolPtr};

+

+    // # Create buffers 2x 5 MB

+

+    D3D12MA::ALLOCATION_DESC allocDesc = {};

+    allocDesc.CustomPool = pool.get();

+    allocDesc.ExtraHeapFlags = (D3D12_HEAP_FLAGS)0xCDCDCDCD; // Should be ignored.

+    allocDesc.HeapType = (D3D12_HEAP_TYPE)0xCDCDCDCD; // Should be ignored.

+

+    D3D12_RESOURCE_DESC resDesc;

+    FillResourceDescForBuffer(resDesc, 5 * MEGABYTE);

+

+    AllocationUniquePtr allocs[4];

+    for(uint32_t i = 0; i < 2; ++i)

+    {

+        D3D12MA::Allocation* allocPtr;

+        CHECK_HR( ctx.allocator->CreateResource(&allocDesc, &resDesc,

+            D3D12_RESOURCE_STATE_GENERIC_READ,

+            NULL, // pOptimizedClearValue

+            &allocPtr,

+            __uuidof(ID3D12Resource), NULL) ); // riidResource, ppvResource

+        allocs[i].reset(allocPtr);

+    }

+

+    // # NEVER_ALLOCATE and COMMITTED should fail

+

+    for(uint32_t i = 0; i < 2; ++i)

+    {

+        allocDesc.Flags = i == 0 ?

+            D3D12MA::ALLOCATION_FLAG_NEVER_ALLOCATE:

+            D3D12MA::ALLOCATION_FLAG_COMMITTED;

+        D3D12MA::Allocation* allocPtr;

+        const HRESULT hr = ctx.allocator->CreateResource(&allocDesc, &resDesc,

+            D3D12_RESOURCE_STATE_GENERIC_READ,

+            NULL, // pOptimizedClearValue

+            &allocPtr,

+            __uuidof(ID3D12Resource), NULL); // riidResource, ppvResource

+        CHECK_BOOL( FAILED(hr) );

+    }

+

+    // # 3 more buffers. 3rd should fail.

+

+    allocDesc.Flags = D3D12MA::ALLOCATION_FLAG_NONE;

+    for(uint32_t i = 2; i < 5; ++i)

+    {

+        D3D12MA::Allocation* allocPtr;

+        HRESULT hr = ctx.allocator->CreateResource(&allocDesc, &resDesc,

+            D3D12_RESOURCE_STATE_GENERIC_READ,

+            NULL, // pOptimizedClearValue

+            &allocPtr,

+            __uuidof(ID3D12Resource), NULL); // riidResource, ppvResource

+        if(i < 4)

+        {

+            CHECK_HR( hr );

+            allocs[i].reset(allocPtr);

+        }

+        else

+        {

+            CHECK_BOOL( FAILED(hr) );

+        }

+    }

+}

+

 static void TestAliasingMemory(const TestContext& ctx)

 {

     wprintf(L"Test aliasing memory\n");

@@ -1002,6 +1084,7 @@
     TestCommittedResourcesAndJson(ctx);

     TestCustomHeapFlags(ctx);

     TestPlacedResources(ctx);

+    TestCustomPools(ctx);

     TestAliasingMemory(ctx);

     TestMapping(ctx);

     TestStats(ctx);