From fb7a530d506329aae42e55b6c661b6d4e1277500 Mon Sep 17 00:00:00 2001 From: "Bernhart, Bryan" Date: Fri, 7 Apr 2023 18:02:32 -0700 Subject: [PATCH] Disallow creation of heaps that exceed adapter limits. --- src/gpgmm/d3d12/CapsD3D12.cpp | 21 +++++ src/gpgmm/d3d12/CapsD3D12.h | 9 +- src/gpgmm/d3d12/ResourceAllocatorD3D12.cpp | 85 ++++++++++--------- .../end2end/D3D12ResourceAllocatorTests.cpp | 15 ++-- 4 files changed, 81 insertions(+), 49 deletions(-) diff --git a/src/gpgmm/d3d12/CapsD3D12.cpp b/src/gpgmm/d3d12/CapsD3D12.cpp index 8939b7e0b..c621a9acd 100644 --- a/src/gpgmm/d3d12/CapsD3D12.cpp +++ b/src/gpgmm/d3d12/CapsD3D12.cpp @@ -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(GPUVendor::kIntel_VkVendor)) { caps->mIsResourceAllocationWithinCoherent = true; @@ -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; } diff --git a/src/gpgmm/d3d12/CapsD3D12.h b/src/gpgmm/d3d12/CapsD3D12.h index c057dd99a..c7f8ce0f7 100644 --- a/src/gpgmm/d3d12/CapsD3D12.h +++ b/src/gpgmm/d3d12/CapsD3D12.h @@ -17,6 +17,7 @@ #include "gpgmm/common/GPUInfo.h" #include "gpgmm/d3d12/d3d12_platform.h" +#include "gpgmm/utils/Limits.h" #include @@ -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; diff --git a/src/gpgmm/d3d12/ResourceAllocatorD3D12.cpp b/src/gpgmm/d3d12/ResourceAllocatorD3D12.cpp index f9a98b757..065922181 100644 --- a/src/gpgmm/d3d12/ResourceAllocatorD3D12.cpp +++ b/src/gpgmm/d3d12/ResourceAllocatorD3D12.cpp @@ -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(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; } } diff --git a/src/tests/end2end/D3D12ResourceAllocatorTests.cpp b/src/tests/end2end/D3D12ResourceAllocatorTests.cpp index d81c08845..6e5229377 100644 --- a/src/tests/end2end/D3D12ResourceAllocatorTests.cpp +++ b/src/tests/end2end/D3D12ResourceAllocatorTests.cpp @@ -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 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) {