From aa784af50098daff759fd9be13f20dff86581335 Mon Sep 17 00:00:00 2001 From: Bryan Bernhart Date: Tue, 2 Aug 2022 09:53:16 -0700 Subject: [PATCH] Add option to pad resource heaps. Adds ALLOCATION_DESC::RequireResourceHeapPadding to append extra bytes to resource heap to workaround driver bugs. --- src/gpgmm/d3d12/ResourceAllocatorD3D12.cpp | 20 ++++++- src/gpgmm/d3d12/ResourceAllocatorD3D12.h | 12 ++++ .../end2end/D3D12ResourceAllocatorTests.cpp | 55 +++++++++++++++++++ 3 files changed, 86 insertions(+), 1 deletion(-) diff --git a/src/gpgmm/d3d12/ResourceAllocatorD3D12.cpp b/src/gpgmm/d3d12/ResourceAllocatorD3D12.cpp index f5c9494f3..6bb86ce41 100644 --- a/src/gpgmm/d3d12/ResourceAllocatorD3D12.cpp +++ b/src/gpgmm/d3d12/ResourceAllocatorD3D12.cpp @@ -770,11 +770,13 @@ namespace gpgmm::d3d12 { isAlwaysCommitted = true; } - const bool neverSubAllocate = + bool neverSubAllocate = allocationDescriptor.Flags & ALLOCATION_FLAG_NEVER_SUBALLOCATE_MEMORY; const bool isMSAA = resourceDescriptor.SampleDesc.Count > 1; + const bool requiresPadding = allocationDescriptor.RequireResourceHeapPadding > 0; + // Attempt to allocate using the most effective allocator.; MemoryAllocator* allocator = nullptr; @@ -795,6 +797,16 @@ namespace gpgmm::d3d12 { request.AlwaysCacheSize = (allocationDescriptor.Flags & ALLOCATION_FLAG_ALWAYS_CACHE_SIZE); request.AvailableForAllocation = mCaps->GetMaxResourceHeapSize(); + // Apply extra padding to the resource heap size, if specified. + // Padding can only be applied to standalone non-committed resources. + if (GPGMM_UNLIKELY(requiresPadding)) { + request.SizeInBytes += allocationDescriptor.RequireResourceHeapPadding; + if (!neverSubAllocate) { + DebugLog() << "Sub-allocation disabled when padding is requested."; + neverSubAllocate = true; + } + } + // Limit available memory to unused budget when residency is enabled. if (mResidencyManager != nullptr) { const DXGI_MEMORY_SEGMENT_GROUP segment = @@ -958,6 +970,12 @@ namespace gpgmm::d3d12 { return E_OUTOFMEMORY; } + // Committed resources cannot specify resource heap size. + if (GPGMM_UNLIKELY(requiresPadding)) { + ErrorLog() << "A padding was specified but no resource allocator could be used."; + return E_FAIL; + } + if (!isAlwaysCommitted) { InfoEvent(GetTypename(), EventMessageId::AllocatorFailed) << "Unable to allocate by using a heap, falling back to a committed resource."; diff --git a/src/gpgmm/d3d12/ResourceAllocatorD3D12.h b/src/gpgmm/d3d12/ResourceAllocatorD3D12.h index 78e4b4160..79094d906 100644 --- a/src/gpgmm/d3d12/ResourceAllocatorD3D12.h +++ b/src/gpgmm/d3d12/ResourceAllocatorD3D12.h @@ -335,6 +335,18 @@ namespace gpgmm::d3d12 { */ D3D12_HEAP_FLAGS ExtraRequiredHeapFlags = D3D12_HEAP_FLAG_NONE; + /** \brief Require additional bytes to be appended to the resource allocation. + + Resource heap size is guarenteed to increase by at-least this number of bytes. + Specifying a padding will disable committed resources and sub-allocated + heaps. + + Used to workaround driver bugs related to the heap size being insufficent for the resource. + + Optional parameter. No extra padding is applied by default. + */ + uint64_t RequireResourceHeapPadding = 0; + /** \brief Associates a name with the given allocation. Optional parameter. By default, no name is associated. diff --git a/src/tests/end2end/D3D12ResourceAllocatorTests.cpp b/src/tests/end2end/D3D12ResourceAllocatorTests.cpp index 1be557f3f..07bd2c594 100644 --- a/src/tests/end2end/D3D12ResourceAllocatorTests.cpp +++ b/src/tests/end2end/D3D12ResourceAllocatorTests.cpp @@ -1320,6 +1320,61 @@ TEST_F(D3D12ResourceAllocatorTests, CreateBufferCacheSize) { } } +// Verify two buffers, with and without padding, allocate the correct size. +TEST_F(D3D12ResourceAllocatorTests, CreateBufferWithPadding) { + ComPtr resourceAllocator; + ASSERT_SUCCEEDED( + ResourceAllocator::CreateAllocator(CreateBasicAllocatorDesc(), &resourceAllocator)); + ASSERT_NE(resourceAllocator, nullptr); + + constexpr uint64_t kBufferSize = GPGMM_MB_TO_BYTES(1); + + ALLOCATION_DESC allocationDesc = {}; + ComPtr allocationWithoutPadding; + ASSERT_SUCCEEDED(resourceAllocator->CreateResource( + allocationDesc, CreateBasicBufferDesc(kBufferSize), D3D12_RESOURCE_STATE_GENERIC_READ, + nullptr, &allocationWithoutPadding)); + + allocationDesc.RequireResourceHeapPadding = 63; + ComPtr allocationWithPadding; + ASSERT_SUCCEEDED(resourceAllocator->CreateResource( + allocationDesc, CreateBasicBufferDesc(kBufferSize), D3D12_RESOURCE_STATE_GENERIC_READ, + nullptr, &allocationWithPadding)); + + EXPECT_GE(allocationWithPadding->GetSize() - allocationWithoutPadding->GetSize(), + allocationDesc.RequireResourceHeapPadding); + + // Padded resources are only supported for standalone allocations. + EXPECT_EQ(allocationWithPadding->GetMethod(), gpgmm::AllocationMethod::kStandalone); +} + +// Verify two textures, with and without padding, allocate the correct size. +TEST_F(D3D12ResourceAllocatorTests, CreateTextureWithPadding) { + ComPtr resourceAllocator; + ASSERT_SUCCEEDED( + ResourceAllocator::CreateAllocator(CreateBasicAllocatorDesc(), &resourceAllocator)); + ASSERT_NE(resourceAllocator, nullptr); + + ALLOCATION_DESC allocationDesc = {}; + + ComPtr allocationWithoutPadding; + ASSERT_SUCCEEDED(resourceAllocator->CreateResource( + allocationDesc, CreateBasicTextureDesc(DXGI_FORMAT_R8G8B8A8_UNORM, 1, 1), + D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, &allocationWithoutPadding)); + + allocationDesc.RequireResourceHeapPadding = 63; + ComPtr allocationWithPadding; + ASSERT_SUCCEEDED(resourceAllocator->CreateResource( + allocationDesc, CreateBasicTextureDesc(DXGI_FORMAT_R8G8B8A8_UNORM, 1, 1), + D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, &allocationWithPadding)); + + EXPECT_GE(allocationWithPadding->GetSize() - allocationWithoutPadding->GetSize(), + allocationDesc.RequireResourceHeapPadding); + + // Padded resources are only supported for standalone allocations. + EXPECT_EQ(allocationWithPadding->GetMethod(), gpgmm::AllocationMethod::kStandalone); +} + TEST_F(D3D12ResourceAllocatorTests, CheckFeatureSupport) { ComPtr resourceAllocator; ASSERT_SUCCEEDED(