From 2c80f0b337b9b29e84fcf4a8c08683b55aeaabcd Mon Sep 17 00:00:00 2001 From: Bryan Bernhart Date: Mon, 19 Sep 2022 12:12:15 -0700 Subject: [PATCH] Enable allocation for any heap type on tier 1 hardware Previously, only default heaps could be specified when using tier 1 hardware. This adds the other heap types for allocation. Also, fixes a validation-check when attempting to use tier 2 hardware on a non-supported adapter. --- src/gpgmm/d3d12/ResourceAllocatorD3D12.cpp | 63 ++++++++++---- src/gpgmm/d3d12/ResourceAllocatorD3D12.h | 2 +- .../end2end/D3D12ResourceAllocatorTests.cpp | 84 +++++++++++++++++++ 3 files changed, 131 insertions(+), 18 deletions(-) diff --git a/src/gpgmm/d3d12/ResourceAllocatorD3D12.cpp b/src/gpgmm/d3d12/ResourceAllocatorD3D12.cpp index 2d8dd834b..9ac593d35 100644 --- a/src/gpgmm/d3d12/ResourceAllocatorD3D12.cpp +++ b/src/gpgmm/d3d12/ResourceAllocatorD3D12.cpp @@ -44,18 +44,24 @@ namespace gpgmm::d3d12 { enum RESOURCE_HEAP_TYPE { // Resource heap tier 2 // Resource heaps contain all buffer and textures types. - RESOURCE_HEAP_TYPE_READBACK_ALLOW_ALL_BUFFERS_AND_TEXTURES = 0x0, - RESOURCE_HEAP_TYPE_UPLOAD_ALLOW_ALL_BUFFERS_AND_TEXTURES = 0x1, - RESOURCE_HEAP_TYPE_DEFAULT_ALLOW_ALL_BUFFERS_AND_TEXTURES = 0x2, + RESOURCE_HEAP_TYPE_READBACK_ALLOW_ALL_BUFFERS_AND_TEXTURES = 0, + RESOURCE_HEAP_TYPE_UPLOAD_ALLOW_ALL_BUFFERS_AND_TEXTURES = 1, + RESOURCE_HEAP_TYPE_DEFAULT_ALLOW_ALL_BUFFERS_AND_TEXTURES = 2, // Resource heap tier 1 // Resource heaps contain buffers or textures but not both. - RESOURCE_HEAP_TYPE_READBACK_ALLOW_ONLY_BUFFERS = 0x3, - RESOURCE_HEAP_TYPE_UPLOAD_ALLOW_ONLY_BUFFERS = 0x4, - RESOURCE_HEAP_TYPE_DEFAULT_ALLOW_ONLY_BUFFERS = 0x5, - - RESOURCE_HEAP_TYPE_DEFAULT_ALLOW_ONLY_NON_RT_OR_DS_TEXTURES = 0x6, - RESOURCE_HEAP_TYPE_DEFAULT_ALLOW_ONLY_RT_OR_DS_TEXTURES = 0x7, + RESOURCE_HEAP_TYPE_READBACK_ALLOW_ONLY_BUFFERS = 3, + RESOURCE_HEAP_TYPE_UPLOAD_ALLOW_ONLY_BUFFERS = 4, + RESOURCE_HEAP_TYPE_DEFAULT_ALLOW_ONLY_BUFFERS = 5, + + // Further heap attribution is required for tier 1: textures are categorized into render + // target or depth-stencil textures but not both. + RESOURCE_HEAP_TYPE_READBACK_ALLOW_ONLY_NON_RT_OR_DS_TEXTURES = 6, + RESOURCE_HEAP_TYPE_READBACK_ALLOW_ONLY_RT_OR_DS_TEXTURES = 7, + RESOURCE_HEAP_TYPE_UPLOAD_ALLOW_ONLY_NON_RT_OR_DS_TEXTURES = 8, + RESOURCE_HEAP_TYPE_UPLOAD_ALLOW_ONLY_RT_OR_DS_TEXTURES = 9, + RESOURCE_HEAP_TYPE_DEFAULT_ALLOW_ONLY_NON_RT_OR_DS_TEXTURES = 10, + RESOURCE_HEAP_TYPE_DEFAULT_ALLOW_ONLY_RT_OR_DS_TEXTURES = 11, RESOURCE_HEAP_TYPE_INVALID, }; @@ -105,6 +111,8 @@ namespace gpgmm::d3d12 { switch (resourceHeapType) { case RESOURCE_HEAP_TYPE_READBACK_ALLOW_ONLY_BUFFERS: case RESOURCE_HEAP_TYPE_READBACK_ALLOW_ALL_BUFFERS_AND_TEXTURES: + case RESOURCE_HEAP_TYPE_READBACK_ALLOW_ONLY_NON_RT_OR_DS_TEXTURES: + case RESOURCE_HEAP_TYPE_READBACK_ALLOW_ONLY_RT_OR_DS_TEXTURES: return D3D12_HEAP_TYPE_READBACK; case RESOURCE_HEAP_TYPE_DEFAULT_ALLOW_ALL_BUFFERS_AND_TEXTURES: case RESOURCE_HEAP_TYPE_DEFAULT_ALLOW_ONLY_BUFFERS: @@ -113,6 +121,8 @@ namespace gpgmm::d3d12 { return D3D12_HEAP_TYPE_DEFAULT; case RESOURCE_HEAP_TYPE_UPLOAD_ALLOW_ONLY_BUFFERS: case RESOURCE_HEAP_TYPE_UPLOAD_ALLOW_ALL_BUFFERS_AND_TEXTURES: + case RESOURCE_HEAP_TYPE_UPLOAD_ALLOW_ONLY_NON_RT_OR_DS_TEXTURES: + case RESOURCE_HEAP_TYPE_UPLOAD_ALLOW_ONLY_RT_OR_DS_TEXTURES: return D3D12_HEAP_TYPE_UPLOAD; default: UNREACHABLE(); @@ -133,8 +143,12 @@ namespace gpgmm::d3d12 { case RESOURCE_HEAP_TYPE_UPLOAD_ALLOW_ONLY_BUFFERS: return createHeapFlags | D3D12_HEAP_FLAG_ALLOW_ONLY_BUFFERS; case RESOURCE_HEAP_TYPE_DEFAULT_ALLOW_ONLY_NON_RT_OR_DS_TEXTURES: + case RESOURCE_HEAP_TYPE_READBACK_ALLOW_ONLY_NON_RT_OR_DS_TEXTURES: + case RESOURCE_HEAP_TYPE_UPLOAD_ALLOW_ONLY_NON_RT_OR_DS_TEXTURES: return createHeapFlags | D3D12_HEAP_FLAG_ALLOW_ONLY_NON_RT_DS_TEXTURES; case RESOURCE_HEAP_TYPE_DEFAULT_ALLOW_ONLY_RT_OR_DS_TEXTURES: + case RESOURCE_HEAP_TYPE_READBACK_ALLOW_ONLY_RT_OR_DS_TEXTURES: + case RESOURCE_HEAP_TYPE_UPLOAD_ALLOW_ONLY_RT_OR_DS_TEXTURES: return createHeapFlags | D3D12_HEAP_FLAG_ALLOW_ONLY_RT_DS_TEXTURES; default: UNREACHABLE(); @@ -193,6 +207,14 @@ namespace gpgmm::d3d12 { case D3D12_RESOURCE_DIMENSION_TEXTURE2D: case D3D12_RESOURCE_DIMENSION_TEXTURE3D: { switch (heapType) { + case D3D12_HEAP_TYPE_UPLOAD: { + if ((flags & D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL) || + (flags & D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET)) { + return RESOURCE_HEAP_TYPE_UPLOAD_ALLOW_ONLY_RT_OR_DS_TEXTURES; + } + return RESOURCE_HEAP_TYPE_UPLOAD_ALLOW_ONLY_NON_RT_OR_DS_TEXTURES; + } + case D3D12_HEAP_TYPE_DEFAULT: { if ((flags & D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL) || (flags & D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET)) { @@ -201,6 +223,15 @@ namespace gpgmm::d3d12 { return RESOURCE_HEAP_TYPE_DEFAULT_ALLOW_ONLY_NON_RT_OR_DS_TEXTURES; } + case D3D12_HEAP_TYPE_READBACK: { + if ((flags & D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL) || + (flags & D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET)) { + return RESOURCE_HEAP_TYPE_READBACK_ALLOW_ONLY_RT_OR_DS_TEXTURES; + } + return RESOURCE_HEAP_TYPE_READBACK_ALLOW_ONLY_NON_RT_OR_DS_TEXTURES; + } + + case D3D12_HEAP_TYPE_CUSTOM: default: return RESOURCE_HEAP_TYPE_INVALID; } @@ -388,14 +419,12 @@ namespace gpgmm::d3d12 { caps.reset(ptr); } - if (allocatorDescriptor.ResourceHeapTier != caps->GetMaxResourceHeapTierSupported()) { - gpgmm::WarningLog() - << "Resource heap tier does not match capabilities of the adapter " - "(ResourceHeapTier:" - << allocatorDescriptor.ResourceHeapTier << " vs " - << caps->GetMaxResourceHeapTierSupported() - << "). This is probably not what the developer intended. Please use " - "CheckFeatureSupport instead."; + if (allocatorDescriptor.ResourceHeapTier > caps->GetMaxResourceHeapTierSupported()) { + gpgmm::ErrorLog() << "Resource heap tier does not match capabilities of the adapter " + "(ResourceHeapTier:" + << allocatorDescriptor.ResourceHeapTier << " vs " + << caps->GetMaxResourceHeapTierSupported() << ")."; + return E_FAIL; } ALLOCATOR_DESC newDescriptor = allocatorDescriptor; diff --git a/src/gpgmm/d3d12/ResourceAllocatorD3D12.h b/src/gpgmm/d3d12/ResourceAllocatorD3D12.h index 669b62594..d270cf4b3 100644 --- a/src/gpgmm/d3d12/ResourceAllocatorD3D12.h +++ b/src/gpgmm/d3d12/ResourceAllocatorD3D12.h @@ -689,7 +689,7 @@ namespace gpgmm::d3d12 { const bool mUseDetailedTimingEvents; const bool mIsCustomHeapsDisabled; - static constexpr uint64_t kNumOfResourceHeapTypes = 8u; + static constexpr uint64_t kNumOfResourceHeapTypes = 12u; std::array, kNumOfResourceHeapTypes> mDedicatedResourceAllocatorOfType; diff --git a/src/tests/end2end/D3D12ResourceAllocatorTests.cpp b/src/tests/end2end/D3D12ResourceAllocatorTests.cpp index 7bf9a5f7e..2f7b4fbd0 100644 --- a/src/tests/end2end/D3D12ResourceAllocatorTests.cpp +++ b/src/tests/end2end/D3D12ResourceAllocatorTests.cpp @@ -93,6 +93,18 @@ TEST_F(D3D12ResourceAllocatorTests, CreateAllocator) { EXPECT_EQ(resourceAllocator, nullptr); } + // Creating an allocator with the wrong resource heap tier should always fail. + { + // Tier 3 doesn't exist in D3D12. + ALLOCATOR_DESC desc = CreateBasicAllocatorDesc(); + desc.ResourceHeapTier = + static_cast(D3D12_RESOURCE_HEAP_TIER_2 + 1); + + ComPtr resourceAllocator; + EXPECT_FAILED(ResourceAllocator::CreateAllocator(desc, &resourceAllocator)); + EXPECT_EQ(resourceAllocator, nullptr); + } + // Creating a NULL allocator should always succeed. EXPECT_SUCCEEDED(ResourceAllocator::CreateAllocator(CreateBasicAllocatorDesc(), nullptr)); @@ -153,6 +165,78 @@ TEST_F(D3D12ResourceAllocatorTests, CreateBufferNoLeak) { GPGMM_TEST_MEMORY_LEAK_END(); } +// Exceeding the max resource heap size should always fail. +TEST_F(D3D12ResourceAllocatorTests, CreateBufferAndTextureInSameHeap) { + ALLOCATOR_DESC allocatorDesc = CreateBasicAllocatorDesc(); + + // Heaps of only the same size can be reused between resource types. + allocatorDesc.PreferredResourceHeapSize = kBufferOf4MBAllocationSize; + + // Adapter must support mixing of resource types in same heap. + GPGMM_SKIP_TEST_IF(allocatorDesc.ResourceHeapTier < D3D12_RESOURCE_HEAP_TIER_2); + + ComPtr resourceAllocator; + ASSERT_SUCCEEDED(ResourceAllocator::CreateAllocator(allocatorDesc, &resourceAllocator)); + + // Create memory for buffer in Heap A. + { + ComPtr bufferAllocation; + ASSERT_SUCCEEDED(resourceAllocator->CreateResource( + {}, CreateBasicBufferDesc(kBufferOf4MBAllocationSize), D3D12_RESOURCE_STATE_COMMON, + nullptr, &bufferAllocation)); + } + + EXPECT_EQ(resourceAllocator->GetInfo().FreeMemoryUsage, kBufferOf4MBAllocationSize); + + // Reuse memory for texture in Heap A. + { + ComPtr textureAllocation; + ASSERT_SUCCEEDED(resourceAllocator->CreateResource( + {}, CreateBasicTextureDesc(DXGI_FORMAT_R8G8B8A8_UNORM, 1, 1), + D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, &textureAllocation)); + } + + EXPECT_EQ(resourceAllocator->GetInfo().FreeMemoryUsage, kBufferOf4MBAllocationSize); +} + +// Exceeding the max resource heap size should always fail. +TEST_F(D3D12ResourceAllocatorTests, CreateBufferAndTextureInSeperateHeap) { + ALLOCATOR_DESC allocatorDesc = CreateBasicAllocatorDesc(); + allocatorDesc.ResourceHeapTier = D3D12_RESOURCE_HEAP_TIER_1; + + // Heaps of only the same size can be reused between resource types. + allocatorDesc.PreferredResourceHeapSize = kBufferOf4MBAllocationSize; + + ComPtr resourceAllocator; + ASSERT_SUCCEEDED(ResourceAllocator::CreateAllocator(allocatorDesc, &resourceAllocator)); + + // Create memory for buffer in Heap A. + { + ComPtr bufferAllocation; + ASSERT_SUCCEEDED(resourceAllocator->CreateResource( + {}, CreateBasicBufferDesc(kBufferOf4MBAllocationSize), D3D12_RESOURCE_STATE_COMMON, + nullptr, &bufferAllocation)); + + EXPECT_EQ(bufferAllocation->GetMemory()->GetSize(), + allocatorDesc.PreferredResourceHeapSize); + } + + EXPECT_EQ(resourceAllocator->GetInfo().FreeMemoryUsage, kBufferOf4MBAllocationSize); + + // Reuse memory for texture in Heap A. + { + ComPtr textureAllocation; + ASSERT_SUCCEEDED(resourceAllocator->CreateResource( + {}, CreateBasicTextureDesc(DXGI_FORMAT_R8G8B8A8_UNORM, 1, 1), + D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, &textureAllocation)); + + EXPECT_EQ(textureAllocation->GetMemory()->GetSize(), + allocatorDesc.PreferredResourceHeapSize); + } + + EXPECT_EQ(resourceAllocator->GetInfo().FreeMemoryUsage, kBufferOf4MBAllocationSize * 2); +} + // Exceeding the max resource heap size should always fail. TEST_F(D3D12ResourceAllocatorTests, CreateBufferOversized) { ComPtr resourceAllocator;