From 13acebe2d455d65b6cadea22e36d956fdb8e3d63 Mon Sep 17 00:00:00 2001 From: "Bernhart, Bryan" Date: Wed, 9 Aug 2023 10:42:40 -0700 Subject: [PATCH] Determine residency heap size when unspecified. --- include/gpgmm_d3d12.h | 28 +++---- src/gpgmm/d3d12/ResidencyHeapD3D12.cpp | 84 +++++++++++++++++-- .../end2end/D3D12ResidencyManagerTests.cpp | 53 +++++++----- 3 files changed, 123 insertions(+), 42 deletions(-) diff --git a/include/gpgmm_d3d12.h b/include/gpgmm_d3d12.h index 26088f9e..bc3721b6 100644 --- a/include/gpgmm_d3d12.h +++ b/include/gpgmm_d3d12.h @@ -99,21 +99,15 @@ namespace gpgmm::d3d12 { */ struct RESIDENCY_HEAP_INFO { /** \brief Created size, in bytes, of the heap. - - Must be non-zero. SizeInBytes is always a multiple of the alignment. */ UINT64 SizeInBytes; /** \brief Created alignment, in bytes, of the heap. - - Must be non-zero. */ UINT64 Alignment; - /** \brief Check if the heap is currently locked for residency. - - A locked heap means the heap is not eligible for eviction. - */ + /** \brief Determine if the heap is currently locked for residency. + */ bool IsLocked; /** \brief Determine if the heap is resident or not. @@ -151,24 +145,26 @@ namespace gpgmm::d3d12 { Specifies creation options for a residency managed heap. */ struct RESIDENCY_HEAP_DESC { + /** \brief Specifies the memory segment the heap will reside in. + + Required parameter. Must be local or non-local segment. + */ + DXGI_MEMORY_SEGMENT_GROUP HeapSegment; + /** \brief Created size of the heap, in bytes. - Must be non-zero. SizeInBytes is always a multiple of the alignment. + SizeInBytes is always a multiple of the alignment. + + Optional parameter. By default, the size is inferred by type of the created heap. */ UINT64 SizeInBytes; /** \brief Created alignment of the heap, in bytes. - Must be non-zero. + Optional parameter. By default, the alignment is inferred by type of the created heap. */ UINT64 Alignment; - /** \brief Specifies the memory segment the heap will reside in. - - Required parameter. - */ - DXGI_MEMORY_SEGMENT_GROUP HeapSegment; - /** \brief Specifies heaps options. Optional parameter. By default, no flags are specified or RESIDENCY_HEAP_FLAG_NONE. diff --git a/src/gpgmm/d3d12/ResidencyHeapD3D12.cpp b/src/gpgmm/d3d12/ResidencyHeapD3D12.cpp index 94b73e3b..76ac26a6 100644 --- a/src/gpgmm/d3d12/ResidencyHeapD3D12.cpp +++ b/src/gpgmm/d3d12/ResidencyHeapD3D12.cpp @@ -25,6 +25,10 @@ namespace gpgmm::d3d12 { + // D3D12 doesn't provide a allocation info definition for non-resource heap types (eg. + // descriptor heaps) but the info is the otherwise the same as resource heaps. + using HEAP_ALLOCATION_INFO = D3D12_RESOURCE_ALLOCATION_INFO; + namespace { // Returns the resource heap flags or E_INVALIDARG, when the memory type doesn't allow @@ -45,6 +49,53 @@ namespace gpgmm::d3d12 { return E_INVALIDARG; } + + HEAP_ALLOCATION_INFO GetResourceHeapAllocationInfo(ComPtr pageable) { + ComPtr heap; + if (SUCCEEDED(pageable.As(&heap))) { + const D3D12_HEAP_DESC desc = heap->GetDesc(); + return {desc.SizeInBytes, desc.Alignment}; + } + + ComPtr committedResource; + if (SUCCEEDED(pageable.As(&committedResource))) { + const D3D12_RESOURCE_DESC desc = committedResource->GetDesc(); + const D3D12_RESOURCE_ALLOCATION_INFO info = + GetDevice(committedResource.Get())->GetResourceAllocationInfo(0, 1, &desc); + return info; + } + + return {kInvalidSize, kInvalidSize}; + } + + HEAP_ALLOCATION_INFO GetDescriptorHeapAllocationInfo(ComPtr pageable) { + ComPtr heap; + if (SUCCEEDED(pageable.As(&heap))) { + const D3D12_DESCRIPTOR_HEAP_DESC desc = heap->GetDesc(); + const uint64_t sizePerDescriptor = + GetDevice(heap.Get())->GetDescriptorHandleIncrementSize(desc.Type); + return {desc.NumDescriptors * sizePerDescriptor, sizePerDescriptor}; + } + + return {kInvalidSize, kInvalidSize}; + } + + HEAP_ALLOCATION_INFO GetHeapAllocationInfo(ComPtr pageable) { + const D3D12_RESOURCE_ALLOCATION_INFO resourceHeapInfo = + GetResourceHeapAllocationInfo(pageable); + if (resourceHeapInfo.SizeInBytes != kInvalidSize) { + return resourceHeapInfo; + } + + const HEAP_ALLOCATION_INFO descriptorHeapInfo = + GetDescriptorHeapAllocationInfo(pageable); + if (descriptorHeapInfo.SizeInBytes != kInvalidSize) { + return descriptorHeapInfo; + } + + return {kInvalidSize, kInvalidSize}; + } + } // namespace RESIDENCY_HEAP_FLAGS GetHeapFlags(D3D12_HEAP_FLAGS heapFlags, bool alwaysCreatedInBudget) { @@ -85,8 +136,29 @@ namespace gpgmm::d3d12 { ResidencyManager* residencyManager = static_cast(pResidencyManager); const bool isResidencyDisabled = (pResidencyManager == nullptr); + RESIDENCY_HEAP_DESC newDescriptor = descriptor; + const HEAP_ALLOCATION_INFO heapInfo = GetHeapAllocationInfo(pPageable); + if (descriptor.SizeInBytes == 0) { + newDescriptor.SizeInBytes = heapInfo.SizeInBytes; + } + + if (newDescriptor.SizeInBytes == kInvalidSize) { + ErrorLog(MessageId::kInvalidArgument, true) << "Heap size for residency was invalid"; + return E_INVALIDARG; + } + + if (descriptor.Alignment == 0) { + newDescriptor.Alignment = heapInfo.Alignment; + } + + if (newDescriptor.Alignment == kInvalidSize) { + ErrorLog(MessageId::kInvalidArgument, true) + << "Heap alignment for residency was invalid."; + return E_INVALIDARG; + } + std::unique_ptr heap( - new ResidencyHeap(pPageable, descriptor, isResidencyDisabled)); + new ResidencyHeap(pPageable, newDescriptor, isResidencyDisabled)); if (!isResidencyDisabled) { // Check if the underlying memory was implicitly made resident. @@ -103,7 +175,7 @@ namespace gpgmm::d3d12 { // Heap created not resident requires no budget to be created. if (heap->GetInfo().Status == RESIDENCY_HEAP_STATUS_EVICTED && - (descriptor.Flags & RESIDENCY_HEAP_FLAG_CREATE_IN_BUDGET)) { + (newDescriptor.Flags & RESIDENCY_HEAP_FLAG_CREATE_IN_BUDGET)) { ErrorLog(heap.get(), MessageId::kInvalidArgument) << "Creating a heap always in budget cannot be used with " "D3D12_HEAP_FLAG_CREATE_NOT_RESIDENT."; @@ -118,7 +190,7 @@ namespace gpgmm::d3d12 { GPGMM_RETURN_IF_FAILED(residencyManager->InsertHeap(heap.get()), GetDevice(pPageable)); } else { - if (descriptor.Flags & RESIDENCY_HEAP_FLAG_CREATE_RESIDENT) { + if (newDescriptor.Flags & RESIDENCY_HEAP_FLAG_CREATE_RESIDENT) { GPGMM_RETURN_IF_FAILED(residencyManager->LockHeap(heap.get()), GetDevice(pPageable)); GPGMM_RETURN_IF_FAILED(residencyManager->UnlockHeap(heap.get()), @@ -127,15 +199,15 @@ namespace gpgmm::d3d12 { } } } else { - if (descriptor.Flags & RESIDENCY_HEAP_FLAG_CREATE_RESIDENT) { + if (newDescriptor.Flags & RESIDENCY_HEAP_FLAG_CREATE_RESIDENT) { WarnLog(heap.get(), MessageId::kInvalidArgument) << "RESIDENCY_HEAP_FLAG_CREATE_RESIDENT was specified but had no effect " "becauase residency management is not being used."; } } - GPGMM_RETURN_IF_FAILED(heap->SetDebugName(descriptor.DebugName), GetDevice(pPageable)); - GPGMM_TRACE_EVENT_OBJECT_SNAPSHOT(heap.get(), descriptor); + GPGMM_RETURN_IF_FAILED(heap->SetDebugName(newDescriptor.DebugName), GetDevice(pPageable)); + GPGMM_TRACE_EVENT_OBJECT_SNAPSHOT(heap.get(), newDescriptor); DebugLog(heap.get(), MessageId::kObjectCreated) << "Created heap, Size=" << heap->GetInfo().SizeInBytes diff --git a/src/tests/end2end/D3D12ResidencyManagerTests.cpp b/src/tests/end2end/D3D12ResidencyManagerTests.cpp index b2e157f4..ca9e4e6e 100644 --- a/src/tests/end2end/D3D12ResidencyManagerTests.cpp +++ b/src/tests/end2end/D3D12ResidencyManagerTests.cpp @@ -158,30 +158,29 @@ TEST_F(D3D12ResidencyManagerTests, CreateResourceHeap) { ComPtr residencyManager; ASSERT_SUCCEEDED(CreateResidencyManager(CreateBasicResidencyDesc(kDefaultBudget), mDevice.Get(), mAdapter.Get(), &residencyManager)); - - constexpr uint64_t kHeapSize = GPGMM_MB_TO_BYTES(10); + ASSERT_NE(residencyManager.Get(), nullptr); D3D12_HEAP_PROPERTIES heapProperties = {}; heapProperties.Type = D3D12_HEAP_TYPE_DEFAULT; D3D12_HEAP_DESC heapDesc = {}; heapDesc.Properties = heapProperties; - heapDesc.SizeInBytes = kHeapSize; + heapDesc.SizeInBytes = GPGMM_MB_TO_BYTES(10); // Assume tier 1, which all adapters support. heapDesc.Flags |= D3D12_HEAP_FLAG_ALLOW_ONLY_BUFFERS; - CreateResourceHeapCallbackContext createHeapContext(mDevice.Get(), &heapDesc); - BadCreateHeapCallbackContext badCreateHeapCallbackContext; - RESIDENCY_HEAP_DESC residencyHeapDesc = {}; - residencyHeapDesc.SizeInBytes = kHeapSize; residencyHeapDesc.HeapSegment = DXGI_MEMORY_SEGMENT_GROUP_LOCAL; - ASSERT_FAILED(CreateResidencyHeap(residencyHeapDesc, residencyManager.Get(), - BadCreateHeapCallbackContext::CreateHeap, - &badCreateHeapCallbackContext, nullptr)); + { + BadCreateHeapCallbackContext badCreateHeapCallbackContext; + ASSERT_FAILED(CreateResidencyHeap(residencyHeapDesc, residencyManager.Get(), + BadCreateHeapCallbackContext::CreateHeap, + &badCreateHeapCallbackContext, nullptr)); + } + CreateResourceHeapCallbackContext createHeapContext(mDevice.Get(), &heapDesc); ASSERT_SUCCEEDED(CreateResidencyHeap(residencyHeapDesc, residencyManager.Get(), CreateResourceHeapCallbackContext::CreateHeap, &createHeapContext, nullptr)); @@ -193,12 +192,7 @@ TEST_F(D3D12ResidencyManagerTests, CreateResourceHeap) { ASSERT_SUCCEEDED(CreateResidencyHeap(residencyHeapDesc, nullptr, pageable.Get(), nullptr)); } - // Create a resource heap without residency. - ComPtr resourceHeap; - ASSERT_SUCCEEDED(CreateResidencyHeap(residencyHeapDesc, nullptr, - CreateResourceHeapCallbackContext::CreateHeap, - &createHeapContext, &resourceHeap)); - + // Residency manager must exist to create the heap resident. { RESIDENCY_HEAP_DESC invalidResidencyHeapDesc = residencyHeapDesc; invalidResidencyHeapDesc.Flags |= RESIDENCY_HEAP_FLAG_CREATE_RESIDENT; @@ -208,6 +202,7 @@ TEST_F(D3D12ResidencyManagerTests, CreateResourceHeap) { &createHeapContext, nullptr)); } + // Residency manager must exist to create the heap in budget. { RESIDENCY_HEAP_DESC invalidResidencyHeapDesc = residencyHeapDesc; invalidResidencyHeapDesc.Flags |= RESIDENCY_HEAP_FLAG_CREATE_IN_BUDGET; @@ -217,6 +212,17 @@ TEST_F(D3D12ResidencyManagerTests, CreateResourceHeap) { &createHeapContext, nullptr)); } + // Create a resource heap without residency. + ComPtr resourceHeap; + ASSERT_SUCCEEDED(CreateResidencyHeap(residencyHeapDesc, nullptr, + CreateResourceHeapCallbackContext::CreateHeap, + &createHeapContext, &resourceHeap)); + ASSERT_NE(resourceHeap.Get(), nullptr); + + EXPECT_EQ(resourceHeap->GetInfo().SizeInBytes, heapDesc.SizeInBytes); + EXPECT_EQ(resourceHeap->GetInfo().Alignment, + static_cast(D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT)); + // Ensure the unmanaged resource heap state is always unknown. Even though D3D12 implicitly // creates heaps as resident, untrack resource heaps would never transition out from // RESIDENCY_HEAP_STATUS_RESIDENT and must be left RESIDENCY_HEAP_STATUS_UNKNOWN. @@ -229,11 +235,15 @@ TEST_F(D3D12ResidencyManagerTests, CreateResourceHeap) { &createHeapContext, &resourceHeap)); ASSERT_NE(resourceHeap, nullptr); + EXPECT_EQ(resourceHeap->GetInfo().SizeInBytes, heapDesc.SizeInBytes); + EXPECT_EQ(resourceHeap->GetInfo().Alignment, + static_cast(D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT)); + EXPECT_EQ(resourceHeap->GetInfo().Status, gpgmm::d3d12::RESIDENCY_HEAP_STATUS_RESIDENT); EXPECT_EQ(resourceHeap->GetInfo().IsLocked, false); // Residency status of resource heap types is always known. - EXPECT_EQ(GetStats(residencyManager).CurrentHeapUsage, residencyHeapDesc.SizeInBytes); + EXPECT_EQ(GetStats(residencyManager).CurrentHeapUsage, heapDesc.SizeInBytes); EXPECT_EQ(GetStats(residencyManager).CurrentHeapCount, 1u); ComPtr heap; @@ -245,7 +255,7 @@ TEST_F(D3D12ResidencyManagerTests, CreateResourceHeap) { EXPECT_EQ(resourceHeap->GetInfo().Status, gpgmm::d3d12::RESIDENCY_HEAP_STATUS_RESIDENT); EXPECT_EQ(resourceHeap->GetInfo().IsLocked, true); - EXPECT_EQ(GetStats(residencyManager).CurrentHeapUsage, residencyHeapDesc.SizeInBytes); + EXPECT_EQ(GetStats(residencyManager).CurrentHeapUsage, heapDesc.SizeInBytes); EXPECT_EQ(GetStats(residencyManager).CurrentHeapCount, 1u); ASSERT_SUCCEEDED(residencyManager->UnlockHeap(resourceHeap.Get())); @@ -254,7 +264,7 @@ TEST_F(D3D12ResidencyManagerTests, CreateResourceHeap) { EXPECT_EQ(resourceHeap->GetInfo().IsLocked, false); // Unlocking a heap does not evict it, the memory usage should not change. - EXPECT_EQ(GetStats(residencyManager).CurrentHeapUsage, residencyHeapDesc.SizeInBytes); + EXPECT_EQ(GetStats(residencyManager).CurrentHeapUsage, heapDesc.SizeInBytes); EXPECT_EQ(GetStats(residencyManager).CurrentHeapCount, 1u); ASSERT_SUCCEEDED(residencyManager->UnlockHeap(resourceHeap.Get())); // Not locked @@ -747,7 +757,7 @@ TEST_F(D3D12ResidencyManagerTests, OverBudgetExecuteCommandList) { ASSERT_SUCCEEDED(mDevice->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&commandAllocator))); - ComPtr commandList; + ComPtr commandList; ASSERT_SUCCEEDED(mDevice->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, commandAllocator.Get(), nullptr, IID_PPV_ARGS(&commandList))); @@ -756,6 +766,9 @@ TEST_F(D3D12ResidencyManagerTests, OverBudgetExecuteCommandList) { ComPtr queue; ASSERT_SUCCEEDED(mDevice->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&queue))); + // Command list must be closed before calling ExecuteCommandLists. + ASSERT_SUCCEEDED(commandList->Close()); + { IResidencyList* residencyLists[] = {firstSetOfHeapsWorkingSet.Get()}; ID3D12CommandList* commandLists[] = {commandList.Get()};