Add Allocation::CreateAliasingResource
diff --git a/src/D3D12MemAlloc.cpp b/src/D3D12MemAlloc.cpp
index 7e8b2a4..cbe5d24 100644
--- a/src/D3D12MemAlloc.cpp
+++ b/src/D3D12MemAlloc.cpp
@@ -2456,6 +2456,15 @@
         const D3D12_RESOURCE_ALLOCATION_INFO* pAllocInfo,

         Allocation** ppAllocation);

 

+    HRESULT CreateAliasingResource(

+        Allocation* pAllocation,

+        UINT64 AllocationLocalOffset,

+        const D3D12_RESOURCE_DESC* pResourceDesc,

+        D3D12_RESOURCE_STATES InitialResourceState,

+        const D3D12_CLEAR_VALUE *pOptimizedClearValue,

+        REFIID riidResource,

+        void** ppvResource);

+

     // Unregisters allocation from the collection of dedicated allocations.

     // Allocation object must be deleted externally afterwards.

     void FreeCommittedMemory(Allocation* allocation);

@@ -3742,6 +3751,12 @@
     REFIID riidResource,

     void** ppvResource)

 {

+    *ppAllocation = NULL;

+    if(ppvResource)

+    {

+        *ppvResource = NULL;

+    }

+

     if(pAllocDesc->HeapType != D3D12_HEAP_TYPE_DEFAULT &&

         pAllocDesc->HeapType != D3D12_HEAP_TYPE_UPLOAD &&

         pAllocDesc->HeapType != D3D12_HEAP_TYPE_READBACK)

@@ -3751,11 +3766,6 @@
 

     ALLOCATION_DESC finalAllocDesc = *pAllocDesc;

 

-    if(ppvResource)

-    {

-        *ppvResource = NULL;

-    }

-

     D3D12_RESOURCE_DESC resourceDesc2 = *pResourceDesc;

     D3D12_RESOURCE_ALLOCATION_INFO resAllocInfo = GetResourceAllocationInfo(resourceDesc2);

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

@@ -3857,6 +3867,8 @@
     const D3D12_RESOURCE_ALLOCATION_INFO* pAllocInfo,

     Allocation** ppAllocation)

 {

+    *ppAllocation = NULL;

+

     if(pAllocDesc->HeapType != D3D12_HEAP_TYPE_DEFAULT &&

         pAllocDesc->HeapType != D3D12_HEAP_TYPE_UPLOAD &&

         pAllocDesc->HeapType != D3D12_HEAP_TYPE_READBACK)

@@ -3908,6 +3920,44 @@
     }

 }

 

+HRESULT AllocatorPimpl::CreateAliasingResource(

+    Allocation* pAllocation,

+    UINT64 AllocationLocalOffset,

+    const D3D12_RESOURCE_DESC* pResourceDesc,

+    D3D12_RESOURCE_STATES InitialResourceState,

+    const D3D12_CLEAR_VALUE *pOptimizedClearValue,

+    REFIID riidResource,

+    void** ppvResource)

+{

+    *ppvResource = NULL;

+

+    D3D12_RESOURCE_DESC resourceDesc2 = *pResourceDesc;

+    D3D12_RESOURCE_ALLOCATION_INFO resAllocInfo = GetResourceAllocationInfo(resourceDesc2);

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

+    D3D12MA_ASSERT(IsPow2(resAllocInfo.Alignment));

+    D3D12MA_ASSERT(resAllocInfo.SizeInBytes > 0);

+

+    ID3D12Heap* const existingHeap = pAllocation->GetHeap();

+    const UINT64 existingOffset = pAllocation->GetOffset();

+    const UINT64 existingSize = pAllocation->GetSize();

+    const UINT64 newOffset = existingOffset + AllocationLocalOffset;

+

+    if(AllocationLocalOffset + resAllocInfo.SizeInBytes > existingSize ||

+        newOffset % resAllocInfo.Alignment != 0)

+    {

+        return E_INVALIDARG;

+    }

+

+    return m_Device->CreatePlacedResource(

+        existingHeap,

+        newOffset,

+        &resourceDesc2,

+        InitialResourceState,

+        pOptimizedClearValue,

+        riidResource,

+        ppvResource);

+}

+

 bool AllocatorPimpl::PrefersCommittedAllocation(const D3D12_RESOURCE_DESC& resourceDesc)

 {

     // Intentional. It may change in the future.

@@ -4879,6 +4929,24 @@
     return m_Pimpl->AllocateMemory(pAllocDesc, pAllocInfo, ppAllocation);

 }

 

+HRESULT Allocator::CreateAliasingResource(

+    Allocation* pAllocation,

+    UINT64 AllocationLocalOffset,

+    const D3D12_RESOURCE_DESC* pResourceDesc,

+    D3D12_RESOURCE_STATES InitialResourceState,

+    const D3D12_CLEAR_VALUE *pOptimizedClearValue,

+    REFIID riidResource,

+    void** ppvResource)

+{

+    if(!pAllocation || !pResourceDesc || riidResource == IID_NULL || !ppvResource)

+    {

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

+        return E_INVALIDARG;

+    }

+    D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK

+    return m_Pimpl->CreateAliasingResource(pAllocation, AllocationLocalOffset, pResourceDesc, InitialResourceState, pOptimizedClearValue, riidResource, ppvResource);

+}

+

 void Allocator::SetCurrentFrameIndex(UINT frameIndex)

 {

     D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK

diff --git a/src/D3D12MemAlloc.h b/src/D3D12MemAlloc.h
index c440e3f..9bdcd5d 100644
--- a/src/D3D12MemAlloc.h
+++ b/src/D3D12MemAlloc.h
@@ -833,6 +833,37 @@
         const D3D12_RESOURCE_ALLOCATION_INFO* pAllocInfo,

         Allocation** ppAllocation);

 

+    /** \brief Creates a new resource in place of an existing allocation. This is useful for memory aliasing.

+

+    \param pAllocation Existing allocation indicating the memory where the new resource should be created.

+        It can be created using D3D12MA::Allocator::CreateResource and already have a resource bound to it,

+        or can be a raw memory allocated with D3D12MA::Allocator::AllocateMemory.

+        It must not be created as committed so that `ID3D12Heap` is available and not implicit.

+    \param AllocationLocalOffset Additional offset in bytes to be applied when allocating the resource.

+        Local from the start of `pAllocation`, not the beginning of the whole `ID3D12Heap`!

+        If the new resource should start from the beginning of the `pAllocation` it should be 0.

+    \param pResourceDesc Description of the new resource to be created.

+    \param InitialResourceState

+    \param pOptimizedClearValue

+    \param riidResource

+    \param[out] ppvResource Returns pointer to the new resource.

+        The resource is not bound with `pAllocation`.

+        This pointer must not be null - you must get the resource pointer and `Release` it when no longer needed.

+

+    Memory requirements of the new resource are checked for validation.

+    If its size exceeds the end of `pAllocation` or required alignment is not fulfilled

+    considering `pAllocation->GetOffset() + AllocationLocalOffset`, the function

+    returns `E_INVALIDARG`.

+    */

+    HRESULT CreateAliasingResource(

+        Allocation* pAllocation,

+        UINT64 AllocationLocalOffset,

+        const D3D12_RESOURCE_DESC* pResourceDesc,

+        D3D12_RESOURCE_STATES InitialResourceState,

+        const D3D12_CLEAR_VALUE *pOptimizedClearValue,

+        REFIID riidResource,

+        void** ppvResource);

+

     /** \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.

@@ -858,6 +889,7 @@
 

     /// Builds and returns statistics as a string in JSON format.

     /** @param[out] ppStatsString Must be freed using Allocator::FreeStatsString.

+    @param DetailedMap `TRUE` to include full list of allocations (can make the string quite long), `FALSE` to only return statistics.

     */

     void BuildStatsString(WCHAR** ppStatsString, BOOL DetailedMap);

 

diff --git a/src/Tests.cpp b/src/Tests.cpp
index 2c1bfb3..954caed 100644
--- a/src/Tests.cpp
+++ b/src/Tests.cpp
@@ -449,9 +449,9 @@
     CHECK_BOOL(allocPtr != NULL && allocPtr->GetHeap() != NULL);

 

     ID3D12Resource* resPtr = NULL;

-    CHECK_HR( ctx.device->CreatePlacedResource(

-        alloc->GetHeap(),

-        alloc->GetOffset(),

+    CHECK_HR( ctx.allocator->CreateAliasingResource(

+        alloc.get(),

+        0, // AllocationLocalOffset

         &resourceDesc1,

         D3D12_RESOURCE_STATE_COMMON,

         NULL, // pOptimizedClearValue

@@ -459,9 +459,9 @@
     CComPtr<ID3D12Resource> res1(resPtr);

     CHECK_BOOL(resPtr != NULL);

 

-    CHECK_HR( ctx.device->CreatePlacedResource(

-        alloc->GetHeap(),

-        alloc->GetOffset(),

+    CHECK_HR( ctx.allocator->CreateAliasingResource(

+        alloc.get(),

+        0, // AllocationLocalOffset

         &resourceDesc2,

         D3D12_RESOURCE_STATE_COMMON,

         NULL, // pOptimizedClearValue