Skip to content

Commit

Permalink
Specify creation of residency heaps without managers is allowed. (#963)
Browse files Browse the repository at this point in the history
  • Loading branch information
bbernhar authored Jan 3, 2024
1 parent 1f62e26 commit 0a5ae1b
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 49 deletions.
29 changes: 15 additions & 14 deletions include/gpgmm_d3d12.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down
61 changes: 33 additions & 28 deletions src/gpgmm/d3d12/ResidencyHeapD3D12.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -130,9 +130,6 @@ namespace gpgmm::d3d12 {

GPGMM_RETURN_IF_NULL(pPageable);

ResidencyManager* residencyManager = static_cast<ResidencyManager*>(pResidencyManager);
const bool isResidencyDisabled = (pResidencyManager == nullptr);

RESIDENCY_HEAP_DESC newDescriptor = descriptor;
const HEAP_ALLOCATION_INFO heapInfo = GetHeapAllocationInfo(pPageable);
if (descriptor.SizeInBytes == 0) {
Expand All @@ -153,15 +150,16 @@ namespace gpgmm::d3d12 {
return E_INVALIDARG;
}

ResidencyManager* residencyManager = static_cast<ResidencyManager*>(pResidencyManager);

std::unique_ptr<ResidencyHeap> 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 {
Expand All @@ -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.";
Expand All @@ -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.";
Expand Down Expand Up @@ -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<ResidencyManager*>(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<ResidencyManager*>(pResidencyManager);

uint64_t bytesEvicted = descriptor.SizeInBytes;
GPGMM_RETURN_IF_FAILED(residencyManager->EvictInternal(
descriptor.SizeInBytes, descriptor.HeapSegment, &bytesEvicted));
Expand All @@ -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.";
}
}

Expand All @@ -282,13 +288,12 @@ namespace gpgmm::d3d12 {

ResidencyHeap::ResidencyHeap(ComPtr<ResidencyManager> residencyManager,
ComPtr<ID3D12Pageable> 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) {
Expand Down Expand Up @@ -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 {
Expand Down
11 changes: 6 additions & 5 deletions src/gpgmm/d3d12/ResidencyHeapD3D12.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,16 +72,17 @@ namespace gpgmm::d3d12 {

ResidencyHeap(ComPtr<ResidencyManager> residencyManager,
ComPtr<ID3D12Pageable> pageable,
const RESIDENCY_HEAP_DESC& descriptor,
bool isResidencyDisabled);
const RESIDENCY_HEAP_DESC& descriptor);

// Unknown interface
void DeleteThis() override;

// 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
Expand All @@ -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<ResidencyManager> mResidencyManager;
ComPtr<ID3D12Pageable> mPageable;

DXGI_MEMORY_SEGMENT_GROUP mHeapSegment;
RefCounted mResidencyLock;
RefCounted mResidencyLockCount;

// Protects thread-access to the mutable members below.
mutable std::mutex mMutex;
Expand Down
4 changes: 2 additions & 2 deletions src/gpgmm/d3d12/ResidencyManagerD3D12.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ namespace gpgmm::d3d12 {
}
}

heap->AddResidencyLockRef();
heap->IncrementResidencyLockCount();

return S_OK;
}
Expand All @@ -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()) {
Expand Down

0 comments on commit 0a5ae1b

Please sign in to comment.