From 0a5ae1b89829399a70713229b0dcce798208649a Mon Sep 17 00:00:00 2001 From: Bryan B Date: Wed, 3 Jan 2024 15:03:14 -0800 Subject: [PATCH] Specify creation of residency heaps without managers is allowed. (#963) --- include/gpgmm_d3d12.h | 29 +++++------ src/gpgmm/d3d12/ResidencyHeapD3D12.cpp | 61 ++++++++++++----------- src/gpgmm/d3d12/ResidencyHeapD3D12.h | 11 ++-- src/gpgmm/d3d12/ResidencyManagerD3D12.cpp | 4 +- 4 files changed, 56 insertions(+), 49 deletions(-) diff --git a/include/gpgmm_d3d12.h b/include/gpgmm_d3d12.h index 01de6471..2f108e52 100644 --- a/include/gpgmm_d3d12.h +++ b/include/gpgmm_d3d12.h @@ -186,15 +186,15 @@ namespace gpgmm::d3d12 { GPGMM_INTERFACE IResidencyManager; - /** \brief Heap represents a residency-managed ID3D12Pageable object. + /** \brief IResidencyHeap represents a ID3D12Pageable object used for residency management. - For example, a Heap could represent a "resource heap" (ID3D12Heap or committed ID3D12Resource) - or ID3D12DescriptorHeap and so on. + For example, a IResidencyHeap could represent a "resource heap" (ID3D12Heap or committed + ID3D12Resource) or ID3D12DescriptorHeap and so on. - Heap serves as a node within the ResidencyManager's residency cache. This node is inserted into - the cache when it is first created, and any time it is scheduled to be used by the GPU. This - node is removed from the cache when it is evicted from video memory due to budget constraints, - or when the memory is released. + IResidencyHeap serves as a node within the IResidencyManager's residency cache. This node is + inserted into the cache when it is first created, and any time it is scheduled to be used by the + GPU. This node is removed from the cache when it is evicted from video memory due to budget + constraints, or when the memory is released. */ GPGMM_INTERFACE IResidencyHeap : public IDebugObject { /** \brief Returns information about this heap. @@ -230,12 +230,12 @@ namespace gpgmm::d3d12 { /** \brief Create a residency managed heap. - Unlike a normal D3D12 heap, a heap managed by GPGMM means it will be managed for residency - purposes. A heap managed by GPGMM represents either a 1) committed resource backed by - implicit D3D12 heap OR 2) an explicit D3D12 heap used with placed resources. + Unlike a D3D12 heap, a IResidencyHeap means it can be managed for residency + purposes. @param descriptor A reference to RESIDENCY_HEAP_DESC structure that describes the heap. - @param pResidencyManager A pointer to the ResidencyManager used to manage this heap. + @param pResidencyManager A pointer to the ResidencyManager used to manage this heap. If NULL, + the residency heap will be created without residency management. @param createHeapFn A callback function which creates a ID3D12Pageable derived type. @param pCreateHeapContext A pointer to a class designed to implement the actual heap creation function and store any necessary variables. @@ -280,10 +280,11 @@ namespace gpgmm::d3d12 { /** \brief Create a residency managed heap. This version of CreateResidencyHeap is a simpler way to create residency heaps by disallowing - use of RESIDENCY_HEAP_FLAG_CREATE_IN_BUDGET by specifying the pageable instead. + use of RESIDENCY_HEAP_FLAG_CREATE_IN_BUDGET by specifying the ID3D12Pageable. @param descriptor A reference to RESIDENCY_HEAP_DESC structure that describes the heap. - @param pResidencyManager A pointer to the ResidencyManager used to manage this heap. + @param pResidencyManager A pointer to the ResidencyManager used to manage this heap. If NULL, + the residency heap will be created without residency management. @param pPageable A pointer to the pageable object that represents the heap. @param[out] ppResidencyHeapOut Pointer to a memory block that receives a pointer to the heap. @@ -431,7 +432,7 @@ namespace gpgmm::d3d12 { /** \brief Specifies if unified memory architecture (UMA) is always disabled, even if the adapter supports UMA. - By default, UMA is enabled when the adapter supports the architecture. + By default, UMA is enabled when the adapter supports the unified memory architecture. UMA allows the residency manager to budget using a single memory segment. Otherwise, the residency manager will have two budgets for local and non-local memory segments, respectively. diff --git a/src/gpgmm/d3d12/ResidencyHeapD3D12.cpp b/src/gpgmm/d3d12/ResidencyHeapD3D12.cpp index ce5349a6..963504f0 100644 --- a/src/gpgmm/d3d12/ResidencyHeapD3D12.cpp +++ b/src/gpgmm/d3d12/ResidencyHeapD3D12.cpp @@ -130,9 +130,6 @@ namespace gpgmm::d3d12 { GPGMM_RETURN_IF_NULL(pPageable); - 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) { @@ -153,15 +150,16 @@ namespace gpgmm::d3d12 { return E_INVALIDARG; } + ResidencyManager* residencyManager = static_cast(pResidencyManager); + std::unique_ptr heap( - new ResidencyHeap(residencyManager, pPageable, newDescriptor, isResidencyDisabled)); + new ResidencyHeap(residencyManager, pPageable, newDescriptor)); - if (!isResidencyDisabled) { - // Check if the underlying memory was implicitly made resident. + if (residencyManager != nullptr) { + // Resource heaps created without the "create not resident" flag are always + // resident. D3D12_HEAP_FLAGS resourceHeapFlags = D3D12_HEAP_FLAG_NONE; if (SUCCEEDED(GetResourceHeapFlags(pPageable, &resourceHeapFlags))) { - // Resource heaps created without the "create not resident" flag are always - // resident. if (!(resourceHeapFlags & D3D12_HEAP_FLAG_CREATE_NOT_RESIDENT)) { heap->SetResidencyStatus(RESIDENCY_HEAP_STATUS_RESIDENT); } else { @@ -171,7 +169,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(ErrorCode::kInvalidArgument, heap.get()) << "Creating a heap always in budget cannot be used with " "D3D12_HEAP_FLAG_CREATE_NOT_RESIDENT."; @@ -192,19 +190,20 @@ namespace gpgmm::d3d12 { } } - if (descriptor.Flags & RESIDENCY_HEAP_FLAG_CREATE_LOCKED) { + if (newDescriptor.Flags & RESIDENCY_HEAP_FLAG_CREATE_LOCKED) { GPGMM_RETURN_IF_FAILED(heap->Lock()); + ASSERT(heap->GetInfo().Status == RESIDENCY_HEAP_STATUS_RESIDENT); } } else { - if (descriptor.Flags & RESIDENCY_HEAP_FLAG_CREATE_RESIDENT) { + if (newDescriptor.Flags & RESIDENCY_HEAP_FLAG_CREATE_RESIDENT) { WarnLog(MessageId::kPerformanceWarning, heap.get()) << "RESIDENCY_HEAP_FLAG_CREATE_RESIDENT was specified but had no effect " "becauase residency management is not being used."; } - // Heap created not resident requires no budget to be created. - if (descriptor.Flags & RESIDENCY_HEAP_FLAG_CREATE_LOCKED) { + // Locking heaps requires residency management. + if (newDescriptor.Flags & RESIDENCY_HEAP_FLAG_CREATE_LOCKED) { ErrorLog(ErrorCode::kInvalidArgument, heap.get()) << "RESIDENCY_HEAP_FLAG_CREATE_LOCKED cannot be specified without a residency " "manager."; @@ -233,25 +232,26 @@ namespace gpgmm::d3d12 { IResidencyHeap** ppResidencyHeapOut) { GPGMM_RETURN_IF_NULL(pCreateHeapContext); - const bool isResidencyDisabled = (pResidencyManager == nullptr); - // Validate residency resource heap flags must also have a residency manager. - if (isResidencyDisabled && descriptor.Flags & RESIDENCY_HEAP_FLAG_CREATE_IN_BUDGET) { + if (pResidencyManager == nullptr && + descriptor.Flags & RESIDENCY_HEAP_FLAG_CREATE_IN_BUDGET) { ErrorLog(ErrorCode::kInvalidArgument) << "Creating a heap always in budget requires a residency manager to exist."; return E_INVALIDARG; } - if (isResidencyDisabled && descriptor.Flags & RESIDENCY_HEAP_FLAG_CREATE_RESIDENT) { + if (pResidencyManager == nullptr && + descriptor.Flags & RESIDENCY_HEAP_FLAG_CREATE_RESIDENT) { ErrorLog(ErrorCode::kInvalidArgument) << "Creating a heap always residency requires a residency manager to exist."; return E_INVALIDARG; } - ResidencyManager* residencyManager = static_cast(pResidencyManager); - // Ensure enough budget exists before creating the heap to avoid an out-of-memory error. - if (!isResidencyDisabled && (descriptor.Flags & RESIDENCY_HEAP_FLAG_CREATE_IN_BUDGET)) { + if (pResidencyManager != nullptr && + descriptor.Flags & RESIDENCY_HEAP_FLAG_CREATE_IN_BUDGET) { + ResidencyManager* residencyManager = static_cast(pResidencyManager); + uint64_t bytesEvicted = descriptor.SizeInBytes; GPGMM_RETURN_IF_FAILED(residencyManager->EvictInternal( descriptor.SizeInBytes, descriptor.HeapSegment, &bytesEvicted)); @@ -270,6 +270,12 @@ namespace gpgmm::d3d12 { << ") and RESIDENCY_HEAP_FLAG_CREATE_IN_BUDGET was specified."; } return E_OUTOFMEMORY; + } else if (bytesEvicted > descriptor.SizeInBytes) { + WarnLog(MessageId::kPerformanceWarning) + << "Residency manager evicted more bytes than the size of heap created (" + << GetBytesToSizeInUnits(bytesEvicted) << " vs " + << GetBytesToSizeInUnits(descriptor.SizeInBytes) + << "). Evicting more memory than required may lead to excessive paging."; } } @@ -282,13 +288,12 @@ namespace gpgmm::d3d12 { ResidencyHeap::ResidencyHeap(ComPtr residencyManager, ComPtr pageable, - const RESIDENCY_HEAP_DESC& descriptor, - bool isResidencyDisabled) + const RESIDENCY_HEAP_DESC& descriptor) : MemoryBase(descriptor.SizeInBytes, descriptor.Alignment), mResidencyManager(std::move(residencyManager)), mPageable(std::move(pageable)), mHeapSegment(descriptor.HeapSegment), - mResidencyLock(0), + mResidencyLockCount(0), mState(RESIDENCY_HEAP_STATUS_UNKNOWN) { ASSERT(mPageable != nullptr); if (residencyManager != nullptr) { @@ -334,16 +339,16 @@ namespace gpgmm::d3d12 { return mHeapSegment; } - void ResidencyHeap::AddResidencyLockRef() { - mResidencyLock.Ref(); + void ResidencyHeap::IncrementResidencyLockCount() { + mResidencyLockCount.Ref(); } - void ResidencyHeap::ReleaseResidencyLock() { - mResidencyLock.Unref(); + void ResidencyHeap::DecrementResidencyLockCount() { + mResidencyLockCount.Unref(); } bool ResidencyHeap::IsResidencyLocked() const { - return mResidencyLock.GetRefCount() > 0; + return mResidencyLockCount.GetRefCount() > 0; } RESIDENCY_HEAP_INFO ResidencyHeap::GetInfo() const { diff --git a/src/gpgmm/d3d12/ResidencyHeapD3D12.h b/src/gpgmm/d3d12/ResidencyHeapD3D12.h index 308c7b83..ae623413 100644 --- a/src/gpgmm/d3d12/ResidencyHeapD3D12.h +++ b/src/gpgmm/d3d12/ResidencyHeapD3D12.h @@ -72,8 +72,7 @@ namespace gpgmm::d3d12 { ResidencyHeap(ComPtr residencyManager, ComPtr pageable, - const RESIDENCY_HEAP_DESC& descriptor, - bool isResidencyDisabled); + const RESIDENCY_HEAP_DESC& descriptor); // Unknown interface void DeleteThis() override; @@ -81,7 +80,9 @@ namespace gpgmm::d3d12 { // ObjectBase interface DEFINE_OBJECT_BASE_OVERRIDES(IHeap) + // DebugObject interface HRESULT SetDebugNameImpl(LPCWSTR name) override; + DXGI_MEMORY_SEGMENT_GROUP GetHeapSegment() const; // The residency manager must know the last fence value that any portion of the pageable was @@ -96,14 +97,14 @@ namespace gpgmm::d3d12 { // Locks residency to ensure the heap cannot be evicted (ex. shader-visible descriptor // heaps or mapping resources). - void AddResidencyLockRef(); - void ReleaseResidencyLock(); + void IncrementResidencyLockCount(); + void DecrementResidencyLockCount(); ComPtr mResidencyManager; ComPtr mPageable; DXGI_MEMORY_SEGMENT_GROUP mHeapSegment; - RefCounted mResidencyLock; + RefCounted mResidencyLockCount; // Protects thread-access to the mutable members below. mutable std::mutex mMutex; diff --git a/src/gpgmm/d3d12/ResidencyManagerD3D12.cpp b/src/gpgmm/d3d12/ResidencyManagerD3D12.cpp index 7ed2b3dc..55497b6e 100644 --- a/src/gpgmm/d3d12/ResidencyManagerD3D12.cpp +++ b/src/gpgmm/d3d12/ResidencyManagerD3D12.cpp @@ -227,7 +227,7 @@ namespace gpgmm::d3d12 { } } - heap->AddResidencyLockRef(); + heap->IncrementResidencyLockCount(); return S_OK; } @@ -254,7 +254,7 @@ namespace gpgmm::d3d12 { return GetErrorResult(ErrorCode::kBadOperation); } - heap->ReleaseResidencyLock(); + heap->DecrementResidencyLockCount(); // If another lock still exists on the heap, nothing further should be done. if (heap->IsResidencyLocked()) {