Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions src/gpgmm/d3d12/CapsD3D12.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,9 @@ namespace gpgmm::d3d12 {
DXGI_ADAPTER_DESC adapterDesc;
GPGMM_RETURN_IF_FAILED(adapter->GetDesc(&adapterDesc));

caps->mSharedSegmentSize = adapterDesc.SharedSystemMemory;
caps->mDedicatedSegmentSize = adapterDesc.DedicatedVideoMemory;

// D3D12 has no feature to detect support and must be set manually.
if (adapterDesc.VendorId == static_cast<uint32_t>(GPUVendor::kIntel_VkVendor)) {
caps->mIsResourceAllocationWithinCoherent = true;
Expand All @@ -129,6 +132,24 @@ namespace gpgmm::d3d12 {
return mMaxResourceHeapSize;
}

uint64_t Caps::GetMaxSegmentSize(DXGI_MEMORY_SEGMENT_GROUP heapSegment) const {
if (mIsAdapterUMA) {
return mSharedSegmentSize;
}

switch (heapSegment) {
case DXGI_MEMORY_SEGMENT_GROUP_LOCAL:
return mDedicatedSegmentSize;

case DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL:
return mSharedSegmentSize;

default:
UNREACHABLE();
return kInvalidSize;
}
}

bool Caps::IsCreateHeapNotResidentSupported() const {
return mIsCreateHeapNotResidentSupported;
}
Expand Down
9 changes: 7 additions & 2 deletions src/gpgmm/d3d12/CapsD3D12.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

#include "gpgmm/common/GPUInfo.h"
#include "gpgmm/d3d12/d3d12_platform.h"
#include "gpgmm/utils/Limits.h"

#include <cstdint>

Expand Down Expand Up @@ -47,11 +48,15 @@ namespace gpgmm::d3d12 {
// Specifies if a texture and buffer can belong in the same heap.
D3D12_RESOURCE_HEAP_TIER GetMaxResourceHeapTierSupported() const;

uint64_t GetMaxSegmentSize(DXGI_MEMORY_SEGMENT_GROUP heapSegment) const;

private:
Caps() = default;

uint64_t mMaxResourceSize = 0;
uint64_t mMaxResourceHeapSize = 0;
uint64_t mMaxResourceSize = kInvalidSize;
uint64_t mMaxResourceHeapSize = kInvalidSize;
uint64_t mSharedSegmentSize = kInvalidSize;
uint64_t mDedicatedSegmentSize = kInvalidSize;
D3D12_RESOURCE_HEAP_TIER mMaxResourceHeapTier = D3D12_RESOURCE_HEAP_TIER_1;
bool mIsCreateHeapNotResidentSupported = false;
bool mIsResourceAllocationWithinCoherent = false;
Expand Down
85 changes: 44 additions & 41 deletions src/gpgmm/d3d12/ResourceAllocatorD3D12.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1104,50 +1104,53 @@ namespace gpgmm::d3d12 {
D3D12_HEAP_PROPERTIES heapProperties =
GetHeapProperties(mDevice, heapType, mIsCustomHeapsDisabled);

// Limit available memory to unused budget when residency is enabled.
// Available memory acts like a hint to the allocator to avoid creating new larger heaps
// then the budget allows where older, small heaps would get immediately evicited to make
// room.
if (IsResidencyEnabled()) {
ResidencyManager* residencyManager =
static_cast<ResidencyManager*>(mResidencyManager.Get());
// Memory pool maps to the memory segment the allocation will belong to.
// But since D3D12 requires the pool to be specified for the given heap type at
// allocation-time, it must be set here and again, when a resource heap is created.
heapProperties.MemoryPoolPreference =
GetMemoryPool(heapProperties, residencyManager->IsUMA());
const bool isUMA =
(IsResidencyEnabled()) ? mResidencyManager->IsUMA() : mCaps->IsAdapterUMA();

// Memory pool maps to the memory segment the allocation will belong to.
// But since D3D12 requires the pool to be specified for the given heap type at
// allocation-time, it must be set here and again, when a resource heap is created.
heapProperties.MemoryPoolPreference = GetMemoryPool(heapProperties, isUMA);

const DXGI_MEMORY_SEGMENT_GROUP segment =
GetHeapSegment(heapProperties.MemoryPoolPreference, residencyManager->IsUMA());
const DXGI_MEMORY_SEGMENT_GROUP heapSegment =
GetHeapSegment(heapProperties.MemoryPoolPreference, isUMA);

const uint64_t maxSegmentSize = mCaps->GetMaxSegmentSize(heapSegment);
if (request.SizeInBytes > maxSegmentSize) {
ErrorLog(MessageId::kSizeExceeded, true, WCharToUTF8(GetDebugName()), this)
<< "Unable to create resource allocation because the resource size exceeded "
"the capabilities of the adapter: "
<< GPGMM_BYTES_TO_GB(request.SizeInBytes) << " vs "
<< GPGMM_BYTES_TO_GB(maxSegmentSize) << " GBs.";
return E_OUTOFMEMORY;
}

// If the allocation must be created within the budget, restrict the amount of memory
// to prevent OOM to free memory only or to the amount of budget left. The allocator
// checks this amount to determine if its appropriate to pre-allocate more memory or
// not.
if (IsResidencyEnabled() && !IsCreateHeapNotResident()) {
DXGI_QUERY_VIDEO_MEMORY_INFO* currentVideoInfo =
residencyManager->GetVideoMemoryInfo(segment);

// If the allocation must be created within the budget, restrict the amount of memory
// to prevent OOM to free memory only or to the amount of budget left. The allocator
// checks this amount to determine if its appropriate to pre-allocate more memory or
// not.
if (!IsCreateHeapNotResident()) {
// If over-budget, only free memory is considered available.
// TODO: Consider optimizing GetStatsInternal().
if (currentVideoInfo->CurrentUsage + request.SizeInBytes >
currentVideoInfo->Budget) {
RESOURCE_ALLOCATOR_STATS allocationStats = {};
GPGMM_RETURN_IF_FAILED(QueryStatsInternal(&allocationStats));

request.AvailableForAllocation = allocationStats.FreeHeapUsage;

DebugLog(MessageId::kBudgetExceeded, true, WCharToUTF8(GetDebugName()), this)
<< "Current usage exceeded budget: "
<< GPGMM_BYTES_TO_MB(currentVideoInfo->CurrentUsage) << " vs "
<< GPGMM_BYTES_TO_MB(currentVideoInfo->Budget) << " MBs ("
<< GPGMM_BYTES_TO_MB(request.AvailableForAllocation) << " MBs free).";

// Otherwise, only memory in budget is considered available.
} else {
request.AvailableForAllocation =
currentVideoInfo->Budget - currentVideoInfo->CurrentUsage;
}
mResidencyManager->GetVideoMemoryInfo(heapSegment);

// If over-budget, only free memory is considered available.
// TODO: Consider optimizing GetStatsInternal().
if (currentVideoInfo->CurrentUsage + request.SizeInBytes > currentVideoInfo->Budget) {
RESOURCE_ALLOCATOR_STATS allocationStats = {};
GPGMM_RETURN_IF_FAILED(QueryStatsInternal(&allocationStats));

request.AvailableForAllocation = allocationStats.FreeHeapUsage;

DebugLog(MessageId::kBudgetExceeded, true, WCharToUTF8(GetDebugName()), this)
<< "Current usage exceeded budget: "
<< GPGMM_BYTES_TO_MB(currentVideoInfo->CurrentUsage) << " vs "
<< GPGMM_BYTES_TO_MB(currentVideoInfo->Budget) << " MBs ("
<< GPGMM_BYTES_TO_MB(request.AvailableForAllocation) << " MBs free).";

} else {
// Otherwise, only memory in budget is considered available.
request.AvailableForAllocation =
currentVideoInfo->Budget - currentVideoInfo->CurrentUsage;
}
}

Expand Down
15 changes: 9 additions & 6 deletions src/tests/end2end/D3D12ResourceAllocatorTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -269,12 +269,15 @@ TEST_F(D3D12ResourceAllocatorTests, CreateBufferOversized) {
mAdapter.Get(), &resourceAllocator, nullptr));
ASSERT_NE(resourceAllocator, nullptr);

constexpr uint64_t kOversizedBuffer = GPGMM_GB_TO_BYTES(32);
ComPtr<IResourceAllocation> allocation;
ASSERT_FAILED(resourceAllocator->CreateResource({}, CreateBasicBufferDesc(kOversizedBuffer + 1),
D3D12_RESOURCE_STATE_COMMON, nullptr,
&allocation));
ASSERT_EQ(allocation, nullptr);
// Exceeds adapter limit
EXPECT_FAILED(resourceAllocator->CreateResource({},
CreateBasicBufferDesc(GPGMM_GB_TO_BYTES(32)),
D3D12_RESOURCE_STATE_COMMON, nullptr, nullptr));

// Exceeds device limit
EXPECT_FAILED(
resourceAllocator->CreateResource({}, CreateBasicBufferDesc(GPGMM_GB_TO_BYTES(1024 * 1024)),
D3D12_RESOURCE_STATE_COMMON, nullptr, nullptr));
}

TEST_F(D3D12ResourceAllocatorTests, CreateBufferSubAllocated) {
Expand Down