diff --git a/.github/workflows/.patches/dawn.diff b/.github/workflows/.patches/dawn.diff index 318b7816e..972c502a3 100644 --- a/.github/workflows/.patches/dawn.diff +++ b/.github/workflows/.patches/dawn.diff @@ -581,7 +581,7 @@ index 214fa67f8..d909d4d2a 100644 +ResultOrError> Device::CreateExternalAllocation( + ComPtr texture) { + ComPtr allocation; -+ DAWN_TRY(CheckHRESULT(mResourceAllocator->CreateResource(texture.Get(), &allocation), ++ DAWN_TRY(CheckHRESULT(mResourceAllocator->CreateResource({}, texture.Get(), &allocation), + "CreateResource failed")); + return allocation; +} diff --git a/include/gpgmm_d3d12.h b/include/gpgmm_d3d12.h index 156a86788..bcbd8c5b8 100644 --- a/include/gpgmm_d3d12.h +++ b/include/gpgmm_d3d12.h @@ -978,6 +978,14 @@ namespace gpgmm::d3d12 { Mostly used for debug and testing when certain allocation methods unexpectedly fail. */ ALLOCATION_FLAG_NEVER_FALLBACK = 0x40, + + /** \brief Disable residency management for the resource allocation. + + The flag disables residency management for the resource allocation. + + Mostly used when external resources are residency managed elsewhere. + */ + ALLOCATION_FLAG_DISABLE_RESIDENCY = 0x80, }; DEFINE_ENUM_FLAG_OPERATORS(ALLOCATION_FLAGS) @@ -1118,15 +1126,16 @@ namespace gpgmm::d3d12 { Allows externally created D3D12 resources to be used as a ResourceAllocation. - Residency is not supported for imported resources. - + @param allocationDescriptor A reference to ALLOCATION_DESC structure that provides + properties for the resource allocation. @param pCommittedResource A pointer to a committed ID3D12Resource. @param[out] ppResourceAllocationOut Pointer to a memory block that receives 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. */ - virtual HRESULT CreateResource(ID3D12Resource* pCommittedResource, + virtual HRESULT CreateResource(const ALLOCATION_DESC& allocationDescriptor, + ID3D12Resource* pCommittedResource, IResourceAllocation** ppResourceAllocationOut) = 0; /** \brief Return free memory back to the OS. diff --git a/src/gpgmm/d3d12/ResourceAllocatorD3D12.cpp b/src/gpgmm/d3d12/ResourceAllocatorD3D12.cpp index 37fe0e6e1..06b4629ed 100644 --- a/src/gpgmm/d3d12/ResourceAllocatorD3D12.cpp +++ b/src/gpgmm/d3d12/ResourceAllocatorD3D12.cpp @@ -1225,7 +1225,8 @@ namespace gpgmm::d3d12 { return S_OK; } - HRESULT ResourceAllocator::CreateResource(ID3D12Resource* pCommittedResource, + HRESULT ResourceAllocator::CreateResource(const ALLOCATION_DESC& allocationDescriptor, + ID3D12Resource* pCommittedResource, IResourceAllocation** ppResourceAllocationOut) { std::lock_guard lock(mMutex); @@ -1249,9 +1250,12 @@ namespace gpgmm::d3d12 { ImportResourceCallbackContext importResourceCallbackContext(resource); ComPtr resourceHeap; - ReturnIfFailed(Heap::CreateHeap(resourceHeapDesc, /*residencyManager*/ nullptr, - ImportResourceCallbackContext::GetHeap, - &importResourceCallbackContext, &resourceHeap)); + ReturnIfFailed(Heap::CreateHeap( + resourceHeapDesc, + (allocationDescriptor.Flags & ALLOCATION_FLAG_DISABLE_RESIDENCY) + ? nullptr + : mResidencyManager.Get(), + ImportResourceCallbackContext::GetHeap, &importResourceCallbackContext, &resourceHeap)); const uint64_t& allocationSize = resourceInfo.SizeInBytes; mStats.UsedMemoryUsage += allocationSize; diff --git a/src/gpgmm/d3d12/ResourceAllocatorD3D12.h b/src/gpgmm/d3d12/ResourceAllocatorD3D12.h index c97188d2f..b7b939628 100644 --- a/src/gpgmm/d3d12/ResourceAllocatorD3D12.h +++ b/src/gpgmm/d3d12/ResourceAllocatorD3D12.h @@ -90,7 +90,8 @@ namespace gpgmm::d3d12 { D3D12_RESOURCE_STATES initialResourceState, const D3D12_CLEAR_VALUE* pClearValue, IResourceAllocation** ppResourceAllocationOut) override; - HRESULT CreateResource(ID3D12Resource* pCommittedResource, + HRESULT CreateResource(const ALLOCATION_DESC& allocationDescriptor, + ID3D12Resource* pCommittedResource, IResourceAllocation** ppResourceAllocationOut) override; uint64_t ReleaseMemory(uint64_t bytesToRelease) override; RESOURCE_ALLOCATOR_STATS GetStats() const override; diff --git a/src/mvi/gpgmm_d3d12.cpp b/src/mvi/gpgmm_d3d12.cpp index 48ffb8857..f138225fc 100644 --- a/src/mvi/gpgmm_d3d12.cpp +++ b/src/mvi/gpgmm_d3d12.cpp @@ -380,9 +380,9 @@ namespace gpgmm::d3d12 { return S_OK; } - HRESULT ResourceAllocator::CreateResource( - ID3D12Resource* pCommittedResource, - IResourceAllocation** ppResourceAllocationOut) { + HRESULT ResourceAllocator::CreateResource(const ALLOCATION_DESC& allocationDescriptor, + ID3D12Resource* pCommittedResource, + IResourceAllocation** ppResourceAllocationOut) { return E_NOTIMPL; } diff --git a/src/mvi/gpgmm_d3d12.h b/src/mvi/gpgmm_d3d12.h index 9f7d79f44..fee048cea 100644 --- a/src/mvi/gpgmm_d3d12.h +++ b/src/mvi/gpgmm_d3d12.h @@ -212,7 +212,8 @@ namespace gpgmm::d3d12 { D3D12_RESOURCE_STATES initialResourceState, const D3D12_CLEAR_VALUE* pClearValue, IResourceAllocation** ppResourceAllocationOut) override; - HRESULT CreateResource(ID3D12Resource* pCommittedResource, + HRESULT CreateResource(const ALLOCATION_DESC& allocationDescriptor, + ID3D12Resource* pCommittedResource, IResourceAllocation** ppResourceAllocationOut) override; uint64_t ReleaseMemory(uint64_t bytesToRelease) override; RESOURCE_ALLOCATOR_STATS GetStats() const override; diff --git a/src/tests/end2end/D3D12ResidencyManagerTests.cpp b/src/tests/end2end/D3D12ResidencyManagerTests.cpp index 0e05cb905..66d746273 100644 --- a/src/tests/end2end/D3D12ResidencyManagerTests.cpp +++ b/src/tests/end2end/D3D12ResidencyManagerTests.cpp @@ -691,4 +691,64 @@ TEST_F(D3D12ResidencyManagerTests, ExecuteCommandListOverBudget) { for (auto& allocation : secondSetOfHeaps) { EXPECT_EQ(allocation->GetMemory()->GetInfo().Status, RESIDENCY_STATUS_CURRENT_RESIDENT); } +} + +TEST_F(D3D12ResidencyManagerTests, OverBudgetImported) { + RESIDENCY_DESC residencyDesc = CreateBasicResidencyDesc(kDefaultBudget); + + ComPtr residencyManager; + ASSERT_SUCCEEDED(CreateResidencyManager(residencyDesc, &residencyManager)); + + ComPtr resourceAllocator; + ASSERT_SUCCEEDED(CreateResourceAllocator(CreateBasicAllocatorDesc(), residencyManager.Get(), + &resourceAllocator)); + + constexpr uint64_t kBufferMemorySize = GPGMM_MB_TO_BYTES(1); + const D3D12_RESOURCE_DESC bufferDesc = CreateBasicBufferDesc(kBufferMemorySize); + + // Keep importing externally allocated resources until we reach the budget. + std::vector> allocationsBelowBudget = {}; + while (resourceAllocator->GetStats().UsedMemoryUsage + kBufferMemorySize <= kDefaultBudget) { + D3D12_HEAP_PROPERTIES heapProperties = {}; + heapProperties.Type = D3D12_HEAP_TYPE_DEFAULT; + + ComPtr resource; + ASSERT_SUCCEEDED(mDevice->CreateCommittedResource(&heapProperties, D3D12_HEAP_FLAG_NONE, + &bufferDesc, D3D12_RESOURCE_STATE_COMMON, + nullptr, IID_PPV_ARGS(&resource))); + + ComPtr allocation; + ASSERT_SUCCEEDED( + resourceAllocator->CreateResource({}, resource.Get(), &allocation)); // import + allocationsBelowBudget.push_back(std::move(allocation)); + } + + // Created allocations below the budget should become resident. + for (auto& allocation : allocationsBelowBudget) { + EXPECT_TRUE(allocation->GetMemory()->GetInfo().IsCachedForResidency); + } + + // Keep allocating |kMemoryOverBudget| over the budget. + constexpr uint64_t kMemoryOverBudget = GPGMM_MB_TO_BYTES(10); + + // Allocating the same amount over budget, where older allocations will be evicted. + std::vector> allocationsAboveBudget = {}; + const uint64_t currentMemoryUsage = resourceAllocator->GetStats().UsedMemoryUsage; + + while (currentMemoryUsage + kMemoryOverBudget > resourceAllocator->GetStats().UsedMemoryUsage) { + ComPtr allocation; + ASSERT_SUCCEEDED(resourceAllocator->CreateResource( + {}, bufferDesc, D3D12_RESOURCE_STATE_COMMON, nullptr, &allocation)); + allocationsAboveBudget.push_back(std::move(allocation)); + } + + // Created allocations above the budget should become resident. + for (auto& allocation : allocationsAboveBudget) { + EXPECT_TRUE(allocation->GetMemory()->GetInfo().IsCachedForResidency); + } + + // Created allocations below the budget should NOT become resident. + for (auto& allocation : allocationsBelowBudget) { + EXPECT_FALSE(allocation->GetMemory()->GetInfo().IsCachedForResidency); + } } \ No newline at end of file diff --git a/src/tests/end2end/D3D12ResourceAllocatorTests.cpp b/src/tests/end2end/D3D12ResourceAllocatorTests.cpp index f21395942..9de329d2a 100644 --- a/src/tests/end2end/D3D12ResourceAllocatorTests.cpp +++ b/src/tests/end2end/D3D12ResourceAllocatorTests.cpp @@ -818,7 +818,7 @@ TEST_F(D3D12ResourceAllocatorTests, CreateBufferImported) { // Importing a non-existent buffer should always fail. ComPtr externalAllocation; - ASSERT_FAILED(resourceAllocator->CreateResource(nullptr, &externalAllocation)); + ASSERT_FAILED(resourceAllocator->CreateResource({}, nullptr, &externalAllocation)); ASSERT_EQ(externalAllocation, nullptr); ALLOCATION_DESC allocationDesc = {}; @@ -831,19 +831,20 @@ TEST_F(D3D12ResourceAllocatorTests, CreateBufferImported) { ASSERT_NE(externalAllocation, nullptr); ComPtr internalAllocation; - ASSERT_SUCCEEDED( - resourceAllocator->CreateResource(externalAllocation->GetResource(), &internalAllocation)); + ASSERT_SUCCEEDED(resourceAllocator->CreateResource({}, externalAllocation->GetResource(), + &internalAllocation)); ASSERT_NE(internalAllocation, nullptr); // Underlying resource must stay the same. ASSERT_EQ(internalAllocation->GetResource(), externalAllocation->GetResource()); // Importing a buffer without creating an allocation should always succeed. - ASSERT_SUCCEEDED(resourceAllocator->CreateResource(externalAllocation->GetResource(), nullptr)); + ASSERT_SUCCEEDED( + resourceAllocator->CreateResource({}, externalAllocation->GetResource(), nullptr)); // Re-importing a buffer should create another allocation from the same resource. ComPtr internalAllocationAgain; - ASSERT_SUCCEEDED(resourceAllocator->CreateResource(externalAllocation->GetResource(), + ASSERT_SUCCEEDED(resourceAllocator->CreateResource({}, externalAllocation->GetResource(), &internalAllocationAgain)); // Underlying resource must stay the same.