Add documentation chapter "Resource aliasing (overlap)"
diff --git a/README.md b/README.md
index 184d67b..59b04c9 100644
--- a/README.md
+++ b/README.md
@@ -26,6 +26,7 @@
Additional features:
+- Support for resource aliasing (overlap).
- Virtual allocator - possibility to use core allocation algorithm without using real GPU memory, to allocate your own stuff, e.g. sub-allocate pieces of one large buffer.
- Well-documented - description of all classes and functions provided, along with chapters that contain general description and example code.
- Thread-safety: Library is designed to be used in multithreaded code.
diff --git a/docs/gfx/Aliasing.png b/docs/gfx/Aliasing.png
new file mode 100644
index 0000000..5f37edb
--- /dev/null
+++ b/docs/gfx/Aliasing.png
Binary files differ
diff --git a/src/D3D12MemAlloc.h b/src/D3D12MemAlloc.h
index 84a1fbb..a3d60fe 100644
--- a/src/D3D12MemAlloc.h
+++ b/src/D3D12MemAlloc.h
@@ -38,6 +38,7 @@
- [Project setup](@ref quick_start_project_setup)
- [Creating resources](@ref quick_start_creating_resources)
- [Mapping memory](@ref quick_start_mapping_memory)
+ - \subpage resource_aliasing
- \subpage reserving_memory
- \subpage virtual_allocator
- \subpage configuration
@@ -237,6 +238,120 @@
\endcode
+\page resource_aliasing Resource aliasing (overlap)
+
+New explicit graphics APIs (Vulkan and Direct3D 12), thanks to manual memory
+management, give an opportunity to alias (overlap) multiple resources in the
+same region of memory - a feature not available in the old APIs (Direct3D 11, OpenGL).
+It can be useful to save video memory, but it must be used with caution.
+
+For example, if you know the flow of your whole render frame in advance, you
+are going to use some intermediate textures or buffers only during a small range of render passes,
+and you know these ranges don't overlap in time, you can create these resources in
+the same place in memory, even if they have completely different parameters (width, height, format etc.).
+
+![Resource aliasing (overlap)](../gfx/Aliasing.png)
+
+Such scenario is possible using D3D12MA, but you need to create your resources
+using special function D3D12MA::Allocator::CreateAliasingResource.
+Before that, you need to allocate memory with parameters calculated using formula:
+
+- allocation size = max(size of each resource)
+- allocation alignment = max(alignment of each resource)
+
+Following example shows two different textures created in the same place in memory,
+allocated to fit largest of them.
+
+\code
+D3D12_RESOURCE_DESC resDesc1 = {};
+resDesc1.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
+resDesc1.Alignment = 0;
+resDesc1.Width = 1920;
+resDesc1.Height = 1080;
+resDesc1.DepthOrArraySize = 1;
+resDesc1.MipLevels = 1;
+resDesc1.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
+resDesc1.SampleDesc.Count = 1;
+resDesc1.SampleDesc.Quality = 0;
+resDesc1.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
+resDesc1.Flags = D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET | D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
+
+D3D12_RESOURCE_DESC resDesc2 = {};
+resDesc2.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
+resDesc2.Alignment = 0;
+resDesc2.Width = 1024;
+resDesc2.Height = 1024;
+resDesc2.DepthOrArraySize = 1;
+resDesc2.MipLevels = 0;
+resDesc2.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
+resDesc2.SampleDesc.Count = 1;
+resDesc2.SampleDesc.Quality = 0;
+resDesc2.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
+resDesc2.Flags = D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET;
+
+const D3D12_RESOURCE_ALLOCATION_INFO allocInfo1 =
+ device->GetResourceAllocationInfo(0, 1, &resDesc1);
+const D3D12_RESOURCE_ALLOCATION_INFO allocInfo2 =
+ device->GetResourceAllocationInfo(0, 1, &resDesc2);
+
+D3D12_RESOURCE_ALLOCATION_INFO finalAllocInfo = {};
+finalAllocInfo.Alignment = std::max(allocInfo1.Alignment, allocInfo2.Alignment);
+finalAllocInfo.SizeInBytes = std::max(allocInfo1.SizeInBytes, allocInfo2.SizeInBytes);
+
+D3D12MA::ALLOCATION_DESC allocDesc = {};
+allocDesc.HeapType = D3D12_HEAP_TYPE_DEFAULT;
+allocDesc.ExtraHeapFlags = D3D12_HEAP_FLAG_ALLOW_ONLY_RT_DS_TEXTURES;
+
+D3D12MA::Allocation* alloc;
+hr = allocator->AllocateMemory(&allocDesc, &finalAllocInfo, &alloc);
+assert(alloc != NULL && alloc->GetHeap() != NULL);
+
+ID3D12Resource* res1;
+hr = allocator->CreateAliasingResource(
+ alloc,
+ 0, // AllocationLocalOffset
+ &resDesc1,
+ D3D12_RESOURCE_STATE_COMMON,
+ NULL, // pOptimizedClearValue
+ IID_PPV_ARGS(&res1));
+
+ID3D12Resource* res2;
+hr = allocator->CreateAliasingResource(
+ alloc,
+ 0, // AllocationLocalOffset
+ &resDesc2,
+ D3D12_RESOURCE_STATE_COMMON,
+ NULL, // pOptimizedClearValue
+ IID_PPV_ARGS(&res2));
+
+// You can use res1 and res2, but not at the same time!
+
+res2->Release();
+res1->Release();
+alloc->Release();
+\endcode
+
+Remember that using resouces that alias in memory requires proper synchronization.
+You need to issue a special barrier of type `D3D12_RESOURCE_BARRIER_TYPE_ALIASING`.
+You also need to treat a resource after aliasing as uninitialized - containing garbage data.
+For example, if you use `res1` and then want to use `res2`, you need to first initialize `res2`
+using either Clear, Discard, or Copy to the entire resource.
+
+Additional considerations:
+
+- D3D12 also allows to interpret contents of memory between aliasing resources consistently in some cases,
+ which is called "data inheritance". For details, see
+ Microsoft documentation, chapter [Memory Aliasing and Data Inheritance](https://docs.microsoft.com/en-us/windows/win32/direct3d12/memory-aliasing-and-data-inheritance).
+- You can create more complex layout where different textures and buffers are bound
+ at different offsets inside one large allocation. For example, one can imagine
+ a big texture used in some render passes, aliasing with a set of many small buffers
+ used in some further passes. To bind a resource at non-zero offset of an allocation,
+ call D3D12MA::Allocator::CreateAliasingResource with appropriate value of `AllocationLocalOffset` parameter.
+- Resources of the three categories: buffers, textures with `RENDER_TARGET` or `DEPTH_STENCIL` flags, and all other textures,
+ can be placed in the same memory only when `allocator->GetD3D12Options().ResourceHeapTier >= D3D12_RESOURCE_HEAP_TIER_2`.
+ Otherwise they must be placed in different memory heap types, and thus aliasing them is not possible.
+
+
\page reserving_memory Reserving minimum amount of memory
The library automatically allocates and frees memory heaps.
diff --git a/src/Tests.cpp b/src/Tests.cpp
index 4f5a3fc..3152a94 100644
--- a/src/Tests.cpp
+++ b/src/Tests.cpp
@@ -705,71 +705,74 @@
{
wprintf(L"Test aliasing memory\n");
+ D3D12_RESOURCE_DESC resDesc1 = {};
+ resDesc1.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
+ resDesc1.Alignment = 0;
+ resDesc1.Width = 1920;
+ resDesc1.Height = 1080;
+ resDesc1.DepthOrArraySize = 1;
+ resDesc1.MipLevels = 1;
+ resDesc1.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
+ resDesc1.SampleDesc.Count = 1;
+ resDesc1.SampleDesc.Quality = 0;
+ resDesc1.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
+ resDesc1.Flags = D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET | D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
+
+ D3D12_RESOURCE_DESC resDesc2 = {};
+ resDesc2.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
+ resDesc2.Alignment = 0;
+ resDesc2.Width = 1024;
+ resDesc2.Height = 1024;
+ resDesc2.DepthOrArraySize = 1;
+ resDesc2.MipLevels = 0;
+ resDesc2.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
+ resDesc2.SampleDesc.Count = 1;
+ resDesc2.SampleDesc.Quality = 0;
+ resDesc2.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
+ resDesc2.Flags = D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET;
+
+ const D3D12_RESOURCE_ALLOCATION_INFO allocInfo1 =
+ ctx.device->GetResourceAllocationInfo(0, 1, &resDesc1);
+ const D3D12_RESOURCE_ALLOCATION_INFO allocInfo2 =
+ ctx.device->GetResourceAllocationInfo(0, 1, &resDesc2);
+
+ D3D12_RESOURCE_ALLOCATION_INFO finalAllocInfo = {};
+ finalAllocInfo.Alignment = std::max(allocInfo1.Alignment, allocInfo2.Alignment);
+ finalAllocInfo.SizeInBytes = std::max(allocInfo1.SizeInBytes, allocInfo2.SizeInBytes);
+
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;
- resourceDesc1.Alignment = 0;
- resourceDesc1.Width = 1920;
- resourceDesc1.Height = 1080;
- resourceDesc1.DepthOrArraySize = 1;
- resourceDesc1.MipLevels = 1;
- resourceDesc1.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
- resourceDesc1.SampleDesc.Count = 1;
- resourceDesc1.SampleDesc.Quality = 0;
- resourceDesc1.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
- resourceDesc1.Flags = D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET | D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
+ D3D12MA::Allocation* alloc = NULL;
+ CHECK_HR( ctx.allocator->AllocateMemory(&allocDesc, &finalAllocInfo, &alloc) );
+ CHECK_BOOL(alloc != NULL && alloc->GetHeap() != NULL);
- D3D12_RESOURCE_DESC resourceDesc2 = {};
- resourceDesc2.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
- resourceDesc2.Alignment = 0;
- resourceDesc2.Width = 1024;
- resourceDesc2.Height = 1024;
- resourceDesc2.DepthOrArraySize = 1;
- resourceDesc2.MipLevels = 0;
- resourceDesc2.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
- resourceDesc2.SampleDesc.Count = 1;
- resourceDesc2.SampleDesc.Quality = 0;
- resourceDesc2.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
- resourceDesc2.Flags = D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET;
-
- const D3D12_RESOURCE_ALLOCATION_INFO allocInfo1 = ctx.device->GetResourceAllocationInfo(0, 1, &resourceDesc1);
- const D3D12_RESOURCE_ALLOCATION_INFO allocInfo2 = ctx.device->GetResourceAllocationInfo(0, 1, &resourceDesc2);
-
- D3D12_RESOURCE_ALLOCATION_INFO allocInfo = {};
- allocInfo.Alignment = std::max(allocInfo1.Alignment, allocInfo2.Alignment);
- allocInfo.SizeInBytes = AlignUp(std::max(allocInfo1.SizeInBytes, allocInfo2.SizeInBytes), 64ull * 1024);
-
- D3D12MA::Allocation* allocPtr = NULL;
- CHECK_HR( ctx.allocator->AllocateMemory(
- &allocDesc,
- &allocInfo,
- &allocPtr) );
- AllocationUniquePtr alloc(allocPtr);
- CHECK_BOOL(allocPtr != NULL && allocPtr->GetHeap() != NULL);
-
- ID3D12Resource* resPtr = NULL;
+ ID3D12Resource* res1 = NULL;
CHECK_HR( ctx.allocator->CreateAliasingResource(
- alloc.get(),
+ alloc,
0, // AllocationLocalOffset
- &resourceDesc1,
+ &resDesc1,
D3D12_RESOURCE_STATE_COMMON,
NULL, // pOptimizedClearValue
- IID_PPV_ARGS(&resPtr)) );
- CComPtr<ID3D12Resource> res1(resPtr);
- CHECK_BOOL(resPtr != NULL);
+ IID_PPV_ARGS(&res1)) );
+ CHECK_BOOL(res1 != NULL);
+ ID3D12Resource* res2 = NULL;
CHECK_HR( ctx.allocator->CreateAliasingResource(
- alloc.get(),
+ alloc,
0, // AllocationLocalOffset
- &resourceDesc2,
+ &resDesc2,
D3D12_RESOURCE_STATE_COMMON,
NULL, // pOptimizedClearValue
- IID_PPV_ARGS(&resPtr)) );
- CComPtr<ID3D12Resource> res2(resPtr);
- CHECK_BOOL(resPtr != NULL);
+ IID_PPV_ARGS(&res2)) );
+ CHECK_BOOL(res2 != NULL);
+
+ // You can use res1 and res2, but not at the same time!
+
+ res2->Release();
+ res1->Release();
+ alloc->Release();
}
static void TestMapping(const TestContext& ctx)