From 30f93c2e0eee06217f6df6b49c9f737351333a36 Mon Sep 17 00:00:00 2001 From: Bryan Bernhart Date: Fri, 24 Jun 2022 11:51:07 -0700 Subject: [PATCH] Support explicitly creating ResidencyManager. Allows ResidencyManager to be created and configured seperately from ResourceAllocator. This allows for more configurability (and prevent duplicatation between ALLOCATOR_DESC and RESIDENCY_DESC). --- src/gpgmm/d3d12/JSONSerializerD3D12.cpp | 1 - src/gpgmm/d3d12/ResourceAllocatorD3D12.cpp | 131 ++++++++++-------- src/gpgmm/d3d12/ResourceAllocatorD3D12.h | 79 +++++------ .../D3D12EventTraceReplay.cpp | 1 - .../end2end/D3D12ResidencyManagerTests.cpp | 53 ++++++- 5 files changed, 158 insertions(+), 107 deletions(-) diff --git a/src/gpgmm/d3d12/JSONSerializerD3D12.cpp b/src/gpgmm/d3d12/JSONSerializerD3D12.cpp index c2b3974a9..ec2d74221 100644 --- a/src/gpgmm/d3d12/JSONSerializerD3D12.cpp +++ b/src/gpgmm/d3d12/JSONSerializerD3D12.cpp @@ -45,7 +45,6 @@ namespace gpgmm::d3d12 { dict.AddItem("MaxResourceHeapSize", desc.MaxResourceHeapSize); dict.AddItem("MaxVideoMemoryBudget", desc.MaxVideoMemoryBudget); dict.AddItem("Budget", desc.Budget); - dict.AddItem("EvictBatchSize", desc.EvictBatchSize); dict.AddItem("MemoryFragmentationLimit", desc.MemoryFragmentationLimit); return dict; } diff --git a/src/gpgmm/d3d12/ResourceAllocatorD3D12.cpp b/src/gpgmm/d3d12/ResourceAllocatorD3D12.cpp index 381cd842a..4698ae03d 100644 --- a/src/gpgmm/d3d12/ResourceAllocatorD3D12.cpp +++ b/src/gpgmm/d3d12/ResourceAllocatorD3D12.cpp @@ -295,33 +295,65 @@ namespace gpgmm::d3d12 { } // namespace // static - HRESULT ResourceAllocator::CreateAllocator(const ALLOCATOR_DESC& descriptor, - ResourceAllocator** resourceAllocatorOut, - ResidencyManager** residencyManagerOut) { - if (descriptor.Adapter == nullptr || descriptor.Device == nullptr) { + HRESULT ResourceAllocator::CreateAllocator(const ALLOCATOR_DESC& allocatorDescriptor, + ResourceAllocator** ppResourceAllocatorOut, + ResidencyManager** ppResidencyManagerOut) { + ComPtr residencyManager; + if (ppResidencyManagerOut != nullptr) { + RESIDENCY_DESC residencyDesc = {}; + residencyDesc.Device = allocatorDescriptor.Device; + residencyDesc.IsUMA = allocatorDescriptor.IsUMA; + residencyDesc.VideoMemoryBudget = allocatorDescriptor.MaxVideoMemoryBudget; + residencyDesc.Budget = allocatorDescriptor.Budget; + ReturnIfFailed(allocatorDescriptor.Adapter.As(&residencyDesc.Adapter)); + + ReturnIfFailed( + ResidencyManager::CreateResidencyManager(residencyDesc, &residencyManager)); + } + + ComPtr resourceAllocator; + ReturnIfFailed( + CreateAllocator(allocatorDescriptor, residencyManager.Get(), &resourceAllocator)); + + if (ppResourceAllocatorOut != nullptr) { + *ppResourceAllocatorOut = resourceAllocator.Detach(); + } + + if (ppResidencyManagerOut != nullptr) { + *ppResidencyManagerOut = residencyManager.Detach(); + } + + return S_OK; + } + + // static + HRESULT ResourceAllocator::CreateAllocator(const ALLOCATOR_DESC& allocatorDescriptor, + ResidencyManager* pResidencyManager, + ResourceAllocator** ppResourceAllocatorOut) { + if (allocatorDescriptor.Adapter == nullptr || allocatorDescriptor.Device == nullptr) { return E_INVALIDARG; } std::unique_ptr caps; { Caps* ptr = nullptr; - ReturnIfFailed( - Caps::CreateCaps(descriptor.Device.Get(), descriptor.Adapter.Get(), &ptr)); + ReturnIfFailed(Caps::CreateCaps(allocatorDescriptor.Device.Get(), + allocatorDescriptor.Adapter.Get(), &ptr)); caps.reset(ptr); } - ALLOCATOR_DESC newDescriptor = descriptor; - newDescriptor.MemoryGrowthFactor = (descriptor.MemoryGrowthFactor >= 1.0) - ? descriptor.MemoryGrowthFactor + ALLOCATOR_DESC newDescriptor = allocatorDescriptor; + newDescriptor.MemoryGrowthFactor = (allocatorDescriptor.MemoryGrowthFactor >= 1.0) + ? allocatorDescriptor.MemoryGrowthFactor : kDefaultMemoryGrowthFactor; newDescriptor.MaxResourceHeapSize = - (descriptor.MaxResourceHeapSize > 0) - ? std::min(descriptor.MaxResourceHeapSize, caps->GetMaxResourceHeapSize()) + (allocatorDescriptor.MaxResourceHeapSize > 0) + ? std::min(allocatorDescriptor.MaxResourceHeapSize, caps->GetMaxResourceHeapSize()) : caps->GetMaxResourceHeapSize(); - newDescriptor.MemoryFragmentationLimit = (descriptor.MemoryFragmentationLimit > 0) - ? descriptor.MemoryFragmentationLimit + newDescriptor.MemoryFragmentationLimit = (allocatorDescriptor.MemoryFragmentationLimit > 0) + ? allocatorDescriptor.MemoryFragmentationLimit : kDefaultFragmentationLimit; if (newDescriptor.PreferredResourceHeapSize > newDescriptor.MaxResourceHeapSize) { @@ -330,9 +362,9 @@ namespace gpgmm::d3d12 { if (newDescriptor.RecordOptions.Flags != ALLOCATOR_RECORD_FLAG_NONE) { StartupEventTrace( - descriptor.RecordOptions.TraceFile, + allocatorDescriptor.RecordOptions.TraceFile, static_cast(~newDescriptor.RecordOptions.Flags | 0), - descriptor.RecordOptions.EventScope & ALLOCATOR_RECORD_SCOPE_PER_PROCESS); + allocatorDescriptor.RecordOptions.EventScope & ALLOCATOR_RECORD_SCOPE_PER_PROCESS); SetEventMessageLevel(GetLogSeverity(newDescriptor.RecordOptions.MinMessageLevel)); } @@ -350,33 +382,15 @@ namespace gpgmm::d3d12 { } #endif - ComPtr residencyManager; - if (residencyManagerOut != nullptr) { - RESIDENCY_DESC residencyDesc = {}; - residencyDesc.Device = newDescriptor.Device; - residencyDesc.IsUMA = newDescriptor.IsUMA; - residencyDesc.VideoMemoryBudget = newDescriptor.MaxVideoMemoryBudget; - residencyDesc.Budget = newDescriptor.Budget; - residencyDesc.EvictBatchSize = newDescriptor.EvictBatchSize; - ReturnIfFailed(newDescriptor.Adapter.As(&residencyDesc.Adapter)); - - ReturnIfFailed( - ResidencyManager::CreateResidencyManager(residencyDesc, &residencyManager)); - } - - if (newDescriptor.Flags & ALLOCATOR_FLAG_ALWAYS_IN_BUDGET && !residencyManager) { + if (newDescriptor.Flags & ALLOCATOR_FLAG_ALWAYS_IN_BUDGET && !pResidencyManager) { gpgmm::WarningLog() << "Residency must be specified and enabled to use " "ALLOCATOR_FLAG_ALWAYS_IN_BUDGET."; } - if (resourceAllocatorOut != nullptr) { - *resourceAllocatorOut = - new ResourceAllocator(newDescriptor, residencyManager, std::move(caps)); - GPGMM_TRACE_EVENT_OBJECT_SNAPSHOT(*resourceAllocatorOut, newDescriptor); - } - - if (residencyManagerOut != nullptr) { - *residencyManagerOut = residencyManager.Detach(); + if (ppResourceAllocatorOut != nullptr) { + *ppResourceAllocatorOut = + new ResourceAllocator(newDescriptor, pResidencyManager, std::move(caps)); + GPGMM_TRACE_EVENT_OBJECT_SNAPSHOT(*ppResourceAllocatorOut, newDescriptor); } return S_OK; @@ -670,31 +684,31 @@ namespace gpgmm::d3d12 { HRESULT ResourceAllocator::CreateResource(const ALLOCATION_DESC& allocationDescriptor, const D3D12_RESOURCE_DESC& resourceDescriptor, D3D12_RESOURCE_STATES initialResourceState, - const D3D12_CLEAR_VALUE* clearValue, - ResourceAllocation** resourceAllocationOut) { - if (!resourceAllocationOut) { + const D3D12_CLEAR_VALUE* pClearValue, + ResourceAllocation** ppResourceAllocationOut) { + if (!ppResourceAllocationOut) { return E_POINTER; } GPGMM_TRACE_EVENT_OBJECT_CALL( "ResourceAllocator.CreateResource", (CREATE_RESOURCE_DESC{allocationDescriptor, resourceDescriptor, initialResourceState, - clearValue})); + pClearValue})); std::lock_guard lock(mMutex); ReturnIfFailed(CreateResourceInternal(allocationDescriptor, resourceDescriptor, - initialResourceState, clearValue, - resourceAllocationOut)); + initialResourceState, pClearValue, + ppResourceAllocationOut)); if (!allocationDescriptor.DebugName.empty()) { - (*resourceAllocationOut)->SetDebugName(allocationDescriptor.DebugName); + (*ppResourceAllocationOut)->SetDebugName(allocationDescriptor.DebugName); } // Insert a new (debug) allocator layer into the allocation so it can report details used // during leak checks. Since we don't want to use it unless we are debugging, we hide it // behind a macro. #if defined(GPGMM_ENABLE_ALLOCATOR_CHECKS) - mDebugAllocator->AddLiveAllocation(*resourceAllocationOut); + mDebugAllocator->AddLiveAllocation(*ppResourceAllocationOut); #endif // Update the current usage counters. @@ -705,11 +719,12 @@ namespace gpgmm::d3d12 { return S_OK; } - HRESULT ResourceAllocator::CreateResourceInternal(const ALLOCATION_DESC& allocationDescriptor, - const D3D12_RESOURCE_DESC& resourceDescriptor, - D3D12_RESOURCE_STATES initialResourceState, - const D3D12_CLEAR_VALUE* clearValue, - ResourceAllocation** resourceAllocationOut) { + HRESULT ResourceAllocator::CreateResourceInternal( + const ALLOCATION_DESC& allocationDescriptor, + const D3D12_RESOURCE_DESC& resourceDescriptor, + D3D12_RESOURCE_STATES initialResourceState, + const D3D12_CLEAR_VALUE* clearValue, + ResourceAllocation** ppResourceAllocationOut) { TRACE_EVENT0(TraceEventCategory::Default, "ResourceAllocator.CreateResource"); // If d3d tells us the resource size is invalid, treat the error as OOM. @@ -838,7 +853,7 @@ namespace gpgmm::d3d12 { allocationDesc.OffsetFromResource = subAllocation.GetOffset(); allocationDesc.ResourceHeap = resourceHeap; - *resourceAllocationOut = new ResourceAllocation{ + *ppResourceAllocationOut = new ResourceAllocation{ allocationDesc, mResidencyManager.Get(), subAllocation.GetAllocator(), subAllocation.GetBlock(), std::move(committedResource)}; @@ -877,7 +892,7 @@ namespace gpgmm::d3d12 { allocationDesc.OffsetFromResource = 0; allocationDesc.ResourceHeap = resourceHeap; - *resourceAllocationOut = new ResourceAllocation{ + *ppResourceAllocationOut = new ResourceAllocation{ allocationDesc, mResidencyManager.Get(), subAllocation.GetAllocator(), subAllocation.GetBlock(), std::move(placedResource)}; @@ -916,7 +931,7 @@ namespace gpgmm::d3d12 { allocationDesc.OffsetFromResource = 0; allocationDesc.ResourceHeap = resourceHeap; - *resourceAllocationOut = new ResourceAllocation{ + *ppResourceAllocationOut = new ResourceAllocation{ allocationDesc, mResidencyManager.Get(), allocation.GetAllocator(), allocation.GetBlock(), std::move(placedResource)}; @@ -957,17 +972,17 @@ namespace gpgmm::d3d12 { allocationDesc.OffsetFromResource = 0; allocationDesc.ResourceHeap = resourceHeap; - *resourceAllocationOut = new ResourceAllocation{ + *ppResourceAllocationOut = new ResourceAllocation{ allocationDesc, mResidencyManager.Get(), this, nullptr, std::move(committedResource)}; return S_OK; } HRESULT ResourceAllocator::CreateResource(ComPtr resource, - ResourceAllocation** resourceAllocationOut) { + ResourceAllocation** ppResourceAllocationOut) { std::lock_guard lock(mMutex); - if (!resourceAllocationOut) { + if (!ppResourceAllocationOut) { return E_POINTER; } @@ -1005,7 +1020,7 @@ namespace gpgmm::d3d12 { allocationDesc.OffsetFromResource = 0; allocationDesc.ResourceHeap = resourceHeap; - *resourceAllocationOut = + *ppResourceAllocationOut = new ResourceAllocation{allocationDesc, nullptr, this, nullptr, std::move(resource)}; return S_OK; diff --git a/src/gpgmm/d3d12/ResourceAllocatorD3D12.h b/src/gpgmm/d3d12/ResourceAllocatorD3D12.h index ec3d3cb9d..72ef50b45 100644 --- a/src/gpgmm/d3d12/ResourceAllocatorD3D12.h +++ b/src/gpgmm/d3d12/ResourceAllocatorD3D12.h @@ -318,30 +318,6 @@ namespace gpgmm::d3d12 { */ uint64_t MaxResourceHeapSize; - /** \brief Maximum video memory available to budget by the allocator, expressed as a - percentage. - - Optional parameter. When 0 is specified, the API will automatically set the max video - memory budget to 95%, leaving 5% for the OS and other applications. - */ - float MaxVideoMemoryBudget; - - /** \brief Specify the budget, in bytes, for residency. - - Allows a fixed budget to be artifically set for testing purposes. - - Optional parameter. When 0 is specified, the API will not restrict the budget. - */ - uint64_t Budget; - - /** \brief Specifies the amount of resource heaps, in bytes, to evict from residency at - once, should there not be enough budget left. - - Optional parameter. When 0 is specified, the API will automatically set the video memory - evict size to 50MB. - */ - uint64_t EvictBatchSize; - /** \brief Memory fragmentation limit, expressed as a percentage of the resource heap size, that is acceptable to be wasted due to fragmentation. @@ -375,6 +351,13 @@ namespace gpgmm::d3d12 { Optional parameter. When 0 is specified, the default of 1.25 is used (or 25% growth). */ double MemoryGrowthFactor; + + /* + * \deprecated Access by creating a ResidencyManager directly, not through CreateAllocator. + Will be removed in a future version of GPGMM. + */ + float MaxVideoMemoryBudget; + uint64_t Budget; }; /** \enum ALLOCATION_FLAGS @@ -514,22 +497,36 @@ namespace gpgmm::d3d12 { **/ class GPGMM_EXPORT ResourceAllocator final : public MemoryAllocator, public IUnknownImpl { public: - /** \brief Create allocator and optional residency manager used to create and manage video - memory for the App specified device and adapter. + /** \brief Create allocator with residency. - Residency manager only exists if this adapter at-least supports DXGI 1.4. + Residency requires at-least DXGI version 1.4. - @param descriptor A reference to ALLOCATOR_DESC structure that describes the allocator. - @param[out] resourceAllocatorOut Pointer to a memory block that recieves a pointer to the + @param allocatorDescriptor A reference to ALLOCATOR_DESC structure that describes the + allocator. + @param[out] ppResourceAllocatorOut Pointer to a memory block that recieves a pointer to the resource allocator. Pass NULL to test if allocator creation would succeed, but not actually create the allocator. - @param[out] residencyManagerOut Pointer to a memory block that recieves a pointer to the + @param[out] ppResidencyManagerOut Pointer to a memory block that recieves a pointer to the residency manager. If NULL is passed, the allocator will be created without using - residency for resources. + residency. + */ + static HRESULT CreateAllocator(const ALLOCATOR_DESC& allocatorDescriptor, + ResourceAllocator** ppResourceAllocatorOut, + ResidencyManager** ppResidencyManagerOut = nullptr); + + /** \brief Create allocator using a specified residency manager. + + @param allocatorDescriptor A reference to ALLOCATOR_DESC structure that describes the + allocator. + @param pResidencyManager Pointer to a memory block that recieves a pointer to the + residency manager. + @param[out] ppResourceAllocatorOut Pointer to a memory block that recieves a pointer to the + resource allocator. Pass NULL to test if allocator creation would succeed, but not actually + create the allocator. */ - static HRESULT CreateAllocator(const ALLOCATOR_DESC& descriptor, - ResourceAllocator** resourceAllocatorOut, - ResidencyManager** residencyManagerOut = nullptr); + static HRESULT CreateAllocator(const ALLOCATOR_DESC& allocatorDescriptor, + ResidencyManager* pResidencyManager, + ResourceAllocator** ppResourceAllocatorOut); ~ResourceAllocator() override; @@ -549,16 +546,16 @@ namespace gpgmm::d3d12 { the resource. @param initialResourceState The initial state of the resource, a bitwise OR'd combination of D3D12_RESOURCE_STATES enumeration constants. - @param clearValue Specifies a D3D12_CLEAR_VALUE structure that describes the default value + @param pClearValue A pointer tp D3D12_CLEAR_VALUE structure that describes the default value for a clear color. - @param[out] resourceAllocationOut An optional pointer to a memory block that recieves the + @param[out] ppResourceAllocationOut An optional pointer to a memory block that recieves the required interface pointer to the created resource allocation object. */ HRESULT CreateResource(const ALLOCATION_DESC& allocationDescriptor, const D3D12_RESOURCE_DESC& resourceDescriptor, D3D12_RESOURCE_STATES initialResourceState, - const D3D12_CLEAR_VALUE* clearValue, - ResourceAllocation** resourceAllocationOut); + const D3D12_CLEAR_VALUE* pClearValue, + ResourceAllocation** ppResourceAllocationOut); /** \brief Imports an existing D3D12 resource. @@ -567,13 +564,13 @@ namespace gpgmm::d3d12 { Residency is not supported for imported resources. @param committedResource A COM managed pointer to a D3D12 committed resource. - @param[out] resourceAllocationOut Pointer to a memory block that recieves a pointer to the + @param[out] ppResourceAllocationOut Pointer to a memory block that recieves a pointer to the resource allocation. Pass NULL to test if resource allocation creation would succeed, but not actually create the resource allocation. If NULL is passed and resource allocation creation would succeed, S_FALSE is returned. */ HRESULT CreateResource(ComPtr committedResource, - ResourceAllocation** resourceAllocationOut); + ResourceAllocation** ppResourceAllocationOut); /** \brief Recycle resource heaps held internally by GPGMM. @@ -632,7 +629,7 @@ namespace gpgmm::d3d12 { const D3D12_RESOURCE_DESC& resourceDescriptor, D3D12_RESOURCE_STATES initialResourceState, const D3D12_CLEAR_VALUE* clearValue, - ResourceAllocation** resourceAllocationOut); + ResourceAllocation** ppResourceAllocationOut); ResourceAllocator(const ALLOCATOR_DESC& descriptor, ComPtr residencyManager, diff --git a/src/tests/capture_replay_tests/D3D12EventTraceReplay.cpp b/src/tests/capture_replay_tests/D3D12EventTraceReplay.cpp index dae57dcbe..eaccdb596 100644 --- a/src/tests/capture_replay_tests/D3D12EventTraceReplay.cpp +++ b/src/tests/capture_replay_tests/D3D12EventTraceReplay.cpp @@ -379,7 +379,6 @@ class D3D12EventTraceReplay : public D3D12TestBase, public CaptureReplayTestWith allocatorDesc.MaxVideoMemoryBudget = snapshot["MaxVideoMemoryBudget"].asFloat(); allocatorDesc.Budget = snapshot["Budget"].asUInt64(); - allocatorDesc.EvictBatchSize = snapshot["EvictBatchSize"].asUInt64(); allocatorDesc.MemoryFragmentationLimit = snapshot["MemoryFragmentationLimit"].asDouble(); } else if (envParams.AllocatorProfile == diff --git a/src/tests/end2end/D3D12ResidencyManagerTests.cpp b/src/tests/end2end/D3D12ResidencyManagerTests.cpp index 5f1db3548..2e1ebef26 100644 --- a/src/tests/end2end/D3D12ResidencyManagerTests.cpp +++ b/src/tests/end2end/D3D12ResidencyManagerTests.cpp @@ -44,6 +44,18 @@ class D3D12ResidencyManagerTests : public D3D12TestBase, public ::testing::Test return desc; } + + RESIDENCY_DESC CreateBasicResidencyDesc(uint64_t budget = 0) const { + RESIDENCY_DESC desc = {}; + desc.Budget = budget; + + // Required + desc.IsUMA = mIsUMA; + desc.Adapter = mAdapter; + desc.Device = mDevice; + + return desc; + } }; TEST_F(D3D12ResidencyManagerTests, CreateResidencySet) { @@ -81,22 +93,51 @@ TEST_F(D3D12ResidencyManagerTests, CreateResidencySet) { } TEST_F(D3D12ResidencyManagerTests, CreateResidencyManager) { - ComPtr residencyManager; - ComPtr resourceAllocator; - ASSERT_SUCCEEDED(ResourceAllocator::CreateAllocator(CreateBasicAllocatorDesc(), - &resourceAllocator, &residencyManager)); - ASSERT_NE(resourceAllocator, nullptr); - EXPECT_NE(residencyManager, nullptr); + // Create allocator with residency support, together. + { + ComPtr residencyManager; + ComPtr resourceAllocator; + ASSERT_SUCCEEDED(ResourceAllocator::CreateAllocator(CreateBasicAllocatorDesc(), + &resourceAllocator, &residencyManager)); + EXPECT_NE(resourceAllocator, nullptr); + EXPECT_NE(residencyManager, nullptr); + } + + // Create allocator with residency, seperately. + { + ComPtr residencyManager; + ASSERT_SUCCEEDED(ResidencyManager::CreateResidencyManager(CreateBasicResidencyDesc(), + &residencyManager)); + + ComPtr resourceAllocator; + ASSERT_SUCCEEDED(ResourceAllocator::CreateAllocator( + CreateBasicAllocatorDesc(), residencyManager.Get(), &resourceAllocator)); + EXPECT_NE(resourceAllocator, nullptr); + EXPECT_NE(residencyManager, nullptr); + } } TEST_F(D3D12ResidencyManagerTests, CreateResidencyManagerNoLeak) { GPGMM_TEST_MEMORY_LEAK_START(); + + // Create allocator with residency support, together. { ComPtr residencyManager; ComPtr resourceAllocator; ResourceAllocator::CreateAllocator(CreateBasicAllocatorDesc(), &resourceAllocator, &residencyManager); } + + // Create allocator with residency, seperately. + { + ComPtr residencyManager; + ResidencyManager::CreateResidencyManager(CreateBasicResidencyDesc(), &residencyManager); + + ComPtr resourceAllocator; + ResourceAllocator::CreateAllocator(CreateBasicAllocatorDesc(), residencyManager.Get(), + &resourceAllocator); + } + GPGMM_TEST_MEMORY_LEAK_END(); }