diff --git a/.github/workflows/.patches/dawn.diff b/.github/workflows/.patches/dawn.diff index c56049bbe..270fe80b4 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 580de22b9..b3d4786e1 100644 --- a/include/gpgmm_d3d12.h +++ b/include/gpgmm_d3d12.h @@ -1050,6 +1050,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) @@ -1190,15 +1198,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 4095b0c55..6241a155b 100644 --- a/src/gpgmm/d3d12/ResourceAllocatorD3D12.cpp +++ b/src/gpgmm/d3d12/ResourceAllocatorD3D12.cpp @@ -1224,7 +1224,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); @@ -1248,9 +1249,13 @@ namespace gpgmm::d3d12 { ImportResourceCallbackContext importResourceCallbackContext(resource); ComPtr resourceHeap; - ReturnIfFailed(Heap::CreateHeap(resourceHeapDesc, /*residencyManager*/ nullptr, - ImportResourceCallbackContext::CreateHeap, - &importResourceCallbackContext, &resourceHeap)); + ReturnIfFailed( + Heap::CreateHeap(resourceHeapDesc, + (allocationDescriptor.Flags & ALLOCATION_FLAG_DISABLE_RESIDENCY) + ? nullptr + : mResidencyManager.Get(), + ImportResourceCallbackContext::CreateHeap, + &importResourceCallbackContext, &resourceHeap)); const uint64_t& allocationSize = resourceInfo.SizeInBytes; mStats.UsedMemoryUsage += allocationSize; @@ -1261,7 +1266,6 @@ namespace gpgmm::d3d12 { allocationDesc.HeapOffset = kInvalidSize; allocationDesc.SizeInBytes = allocationSize; allocationDesc.Method = AllocationMethod::kStandalone; - allocationDesc.OffsetFromResource = 0; if (ppResourceAllocationOut != nullptr) { *ppResourceAllocationOut = new ResourceAllocation( diff --git a/src/gpgmm/d3d12/ResourceAllocatorD3D12.h b/src/gpgmm/d3d12/ResourceAllocatorD3D12.h index 0919eec57..9e5b8305e 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 09881f214..9a108dc88 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..5d7b52130 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.Detach(), &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 428365c64..fb576a677 100644 --- a/src/tests/end2end/D3D12ResourceAllocatorTests.cpp +++ b/src/tests/end2end/D3D12ResourceAllocatorTests.cpp @@ -817,7 +817,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 = {}; @@ -830,19 +830,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.