diff --git a/include/gpgmm_d3d12.h b/include/gpgmm_d3d12.h index 4a0f7e61..01de6471 100644 --- a/include/gpgmm_d3d12.h +++ b/include/gpgmm_d3d12.h @@ -1136,10 +1136,10 @@ namespace gpgmm::d3d12 { /** \brief Forces use of the resource allocator or E_FAIL. - The flag disables the fall-back behavior of reverting to the D3D12 runtime/driver provided - allocator (CreateCommittedResource) when resource allocation fails. + The flag disables the fall-back behavior of using + RESOURCE_ALLOCATION_FLAG_ALWAYS_COMMITTED as a last resort. - Mostly used for debug and testing when certain allocation methods unexpectedly fail. + Mostly used for debug and testing when certain allocations unexpectedly fail. */ RESOURCE_ALLOCATION_FLAG_NEVER_FALLBACK = 0x40, @@ -1160,6 +1160,14 @@ namespace gpgmm::d3d12 { is normally not tracked. */ RESOURCE_ALLOCATION_FLAG_ALWAYS_WARN_ON_ALIGNMENT_MISMATCH = 0x100, + + /** \brief Disable re-use of resource heap. + + A committed resource is allocated through D3D12 instead of GPGMM. This could be favorable + for large static resources. Otherwise, this is mostly used for debugging and testing + purposes. + */ + RESOURCE_ALLOCATION_FLAG_ALWAYS_COMMITTED = 0x200, }; DEFINE_ENUM_FLAG_OPERATORS(RESOURCE_ALLOCATION_FLAGS) diff --git a/src/gpgmm/d3d12/ResourceAllocationD3D12.cpp b/src/gpgmm/d3d12/ResourceAllocationD3D12.cpp index 0ef3b0d6..d6b865d0 100644 --- a/src/gpgmm/d3d12/ResourceAllocationD3D12.cpp +++ b/src/gpgmm/d3d12/ResourceAllocationD3D12.cpp @@ -225,6 +225,36 @@ namespace gpgmm::d3d12 { return DebugObject::SetDebugName(Name); } + HRESULT STDMETHODCALLTYPE ResourceAllocation::QueryInterface(REFIID riid, void** ppvObject) { + ResidencyHeap* residencyHeap = static_cast(GetMemory()); + + // Only committed resources can be exported. Committed resources are created + // without explicit heaps. + ComPtr heap; + if (SUCCEEDED(residencyHeap->QueryInterface(IID_PPV_ARGS(&heap)))) { + ErrorLog(ErrorCode::kBadOperation, this) + << "QueryInterface is not supported for this resource allocation."; + return E_INVALIDARG; + } + + // Exported resource cannot remain managed for residency. + if (SUCCEEDED(residencyHeap->GetResidencyManager(nullptr))) { + ErrorLog(ErrorCode::kBadOperation, this) << "QueryInterface does not supported resource " + "allocations under residency management."; + return E_INVALIDARG; + } + + return mResource->QueryInterface(riid, ppvObject); + } + + ULONG STDMETHODCALLTYPE ResourceAllocation::AddRef() { + return Unknown::AddRef(); + } + + ULONG STDMETHODCALLTYPE ResourceAllocation::Release() { + return Unknown::Release(); + } + HRESULT ResourceAllocation::GetResourceAllocator( IResourceAllocator** ppResourceAllocatorOut) const { ComPtr resourceAllocator(mResourceAllocator); diff --git a/src/gpgmm/d3d12/ResourceAllocationD3D12.h b/src/gpgmm/d3d12/ResourceAllocationD3D12.h index d20a2c11..f4549865 100644 --- a/src/gpgmm/d3d12/ResourceAllocationD3D12.h +++ b/src/gpgmm/d3d12/ResourceAllocationD3D12.h @@ -60,12 +60,15 @@ namespace gpgmm::d3d12 { IResidencyHeap* GetMemory() const override; HRESULT GetResourceAllocator(IResourceAllocator** ppResourceAllocatorOut) const override; - DEFINE_UNKNOWN_OVERRIDES() - // IDebugObject interface LPCWSTR GetDebugName() const override; HRESULT SetDebugName(LPCWSTR Name) override; + // IUnknown interface + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject) override; + ULONG STDMETHODCALLTYPE AddRef() override; + ULONG STDMETHODCALLTYPE Release() override; + private: friend ResourceAllocator; diff --git a/src/gpgmm/d3d12/ResourceAllocatorD3D12.cpp b/src/gpgmm/d3d12/ResourceAllocatorD3D12.cpp index c6b76bc2..39e9db4c 100644 --- a/src/gpgmm/d3d12/ResourceAllocatorD3D12.cpp +++ b/src/gpgmm/d3d12/ResourceAllocatorD3D12.cpp @@ -1177,14 +1177,15 @@ namespace gpgmm::d3d12 { // Resource is always committed when heaps flags are incompatible with the resource heap // type or if specified by the flag. - bool isAlwaysCommitted = mIsAlwaysCommitted; + bool isAlwaysCommitted = mIsAlwaysCommitted || (allocationDescriptor.Flags & + RESOURCE_ALLOCATION_FLAG_ALWAYS_COMMITTED); // Check memory requirements. D3D12_HEAP_FLAGS heapFlags = GetHeapFlags(resourceHeapType, IsCreateHeapNotResidentEnabled()); if (!HasAllFlags(heapFlags, allocationDescriptor.ExtraRequiredHeapFlags)) { WarnLog(MessageId::kPerformanceWarning, this) - << "RESOURCE_ALLOCATOR_FLAG_ALWAYS_COMMITTED was not requested but enabled anyway " + << "RESOURCE_ALLOCATION_FLAG_ALWAYS_COMMITTED was not requested but enabled anyway " "because " "the required heap flags were incompatible with resource heap type (" << std::to_string(allocationDescriptor.ExtraRequiredHeapFlags) << " vs " diff --git a/src/tests/end2end/D3D12ResourceAllocatorTests.cpp b/src/tests/end2end/D3D12ResourceAllocatorTests.cpp index bfc5f4e6..2acf6e88 100644 --- a/src/tests/end2end/D3D12ResourceAllocatorTests.cpp +++ b/src/tests/end2end/D3D12ResourceAllocatorTests.cpp @@ -909,6 +909,28 @@ TEST_F(D3D12ResourceAllocatorTests, CreateBufferImported) { ASSERT_EQ(internalAllocation->GetResource(), internalAllocationAgain->GetResource()); } +TEST_F(D3D12ResourceAllocatorTests, CreateBufferExported) { + ComPtr resourceAllocator; + ASSERT_SUCCEEDED(CreateResourceAllocator(CreateBasicAllocatorDesc(), mDevice.Get(), + mAdapter.Get(), &resourceAllocator, nullptr)); + ASSERT_NE(resourceAllocator, nullptr); + + RESOURCE_ALLOCATION_DESC externalAllocationDesc = {}; + externalAllocationDesc.Flags |= RESOURCE_ALLOCATION_FLAG_ALWAYS_COMMITTED; + + ComPtr internalAllocation; + ASSERT_SUCCEEDED(resourceAllocator->CreateResource( + externalAllocationDesc, CreateBasicBufferDesc(kBufferOf4MBAllocationSize), {}, nullptr, + &internalAllocation)); + + ComPtr externalResource; + ASSERT_SUCCEEDED(internalAllocation.As(&externalResource)); + + internalAllocation = nullptr; + EXPECT_NE(externalResource, nullptr); + EXPECT_REFCOUNT_EQ(externalResource.Get(), 1); +} + TEST_F(D3D12ResourceAllocatorTests, CreateBufferInvalid) { ComPtr resourceAllocator; ASSERT_SUCCEEDED(CreateResourceAllocator(CreateBasicAllocatorDesc(), mDevice.Get(),