From 3ab2245dc6fc9342b987134da5ac3e9417d40821 Mon Sep 17 00:00:00 2001 From: Bryan Bernhart Date: Fri, 30 Sep 2022 15:06:33 -0700 Subject: [PATCH] Decouple GPGMM interface from impl. --- .github/workflows/.patches/dawn.diff | 123 +- Doxyfile | 2 +- README.md | 19 +- src/fuzzers/D3D12Fuzzer.h | 2 + src/fuzzers/D3D12ResidencyManagerFuzzer.cpp | 19 +- src/fuzzers/D3D12ResourceAllocatorFuzzer.cpp | 8 +- src/gpgmm/common/Backend.h | 7 - src/gpgmm/common/BuddyMemoryAllocator.cpp | 6 +- src/gpgmm/common/Memory.cpp | 4 +- src/gpgmm/common/Memory.h | 41 +- src/gpgmm/common/MemoryAllocation.cpp | 6 +- src/gpgmm/common/MemoryAllocation.h | 40 +- src/gpgmm/common/MemoryAllocator.h | 46 +- src/gpgmm/common/MemoryPool.h | 3 +- src/gpgmm/common/PooledMemoryAllocator.cpp | 4 +- src/gpgmm/common/SegmentedMemoryAllocator.cpp | 9 +- src/gpgmm/common/SlabMemoryAllocator.cpp | 4 +- src/gpgmm/d3d12/BackendD3D12.h | 2 - src/gpgmm/d3d12/BufferAllocatorD3D12.cpp | 2 +- src/gpgmm/d3d12/DebugObjectD3D12.h | 29 +- src/gpgmm/d3d12/EventRecordD3D12.h | 115 -- src/gpgmm/d3d12/HeapD3D12.cpp | 59 +- src/gpgmm/d3d12/HeapD3D12.h | 171 +-- src/gpgmm/d3d12/IUnknownImplD3D12.h | 8 + src/gpgmm/d3d12/JSONSerializerD3D12.cpp | 3 +- src/gpgmm/d3d12/JSONSerializerD3D12.h | 17 +- src/gpgmm/d3d12/ResidencyListD3D12.cpp | 9 +- src/gpgmm/d3d12/ResidencyListD3D12.h | 40 +- src/gpgmm/d3d12/ResidencyManagerD3D12.cpp | 59 +- src/gpgmm/d3d12/ResidencyManagerD3D12.h | 219 +-- src/gpgmm/d3d12/ResourceAllocationD3D12.cpp | 18 +- src/gpgmm/d3d12/ResourceAllocationD3D12.h | 143 +- src/gpgmm/d3d12/ResourceAllocatorD3D12.cpp | 84 +- src/gpgmm/d3d12/ResourceAllocatorD3D12.h | 582 +------- .../d3d12/ResourceHeapAllocatorD3D12.cpp | 13 +- src/gpgmm/d3d12/ResourceHeapAllocatorD3D12.h | 6 +- src/gpgmm/vk/DeviceMemoryAllocatorVk.cpp | 3 +- src/gpgmm/vk/DeviceMemoryVk.cpp | 24 + src/gpgmm/vk/DeviceMemoryVk.h | 10 +- src/gpgmm/vk/ResourceAllocatorVk.cpp | 6 +- src/gpgmm/vk/ResourceAllocatorVk.h | 327 +---- src/include/gpgmm.h | 136 ++ src/include/gpgmm_d3d12.h | 1217 ++++++++++++++++- src/include/gpgmm_vk.h | 333 ++++- src/include/min/gpgmm.h | 108 -- src/include/min/gpgmm_d3d12.h | 354 ----- .../gpgmm_d3d12_mvi.cpp} | 181 ++- src/mvi/gpgmm_d3d12_mvi.h | 227 +++ .../min/gpgmm.cpp => mvi/gpgmm_mvi.cpp} | 51 +- src/mvi/gpgmm_mvi.h | 66 + src/samples/BUILD.gn | 19 +- src/samples/D3D12Sample.cpp | 4 +- src/tests/D3D12Test.h | 2 - src/tests/DummyMemoryAllocator.h | 33 +- .../D3D12EventTraceReplay.cpp | 47 +- .../end2end/D3D12ResidencyManagerTests.cpp | 223 +-- .../end2end/D3D12ResourceAllocatorTests.cpp | 563 ++++---- .../unittests/BuddyMemoryAllocatorTests.cpp | 4 +- .../unittests/SlabMemoryAllocatorTests.cpp | 2 +- 59 files changed, 3031 insertions(+), 2831 deletions(-) delete mode 100644 src/gpgmm/d3d12/EventRecordD3D12.h create mode 100644 src/include/gpgmm.h delete mode 100644 src/include/min/gpgmm.h delete mode 100644 src/include/min/gpgmm_d3d12.h rename src/{include/min/gpgmm_d3d12.cpp => mvi/gpgmm_d3d12_mvi.cpp} (68%) create mode 100644 src/mvi/gpgmm_d3d12_mvi.h rename src/{include/min/gpgmm.cpp => mvi/gpgmm_mvi.cpp} (52%) create mode 100644 src/mvi/gpgmm_mvi.h diff --git a/.github/workflows/.patches/dawn.diff b/.github/workflows/.patches/dawn.diff index 14ce13b68..190569d8e 100644 --- a/.github/workflows/.patches/dawn.diff +++ b/.github/workflows/.patches/dawn.diff @@ -1,4 +1,4 @@ -From f453c4cb653c4296501ac3aa41779fca2f0947e8 Mon Sep 17 00:00:00 2001 +From 432dc113fe27f5b0b4f027cad368e30af66db994 Mon Sep 17 00:00:00 2001 From: Bryan Bernhart Date: Tue, 15 Feb 2022 17:25:29 -0800 Subject: [PATCH] Use GPGMM for D3D12 backend. @@ -18,7 +18,7 @@ Change-Id: I47708462a1d9dd0166120c3a6af93451aae54a07 src/dawn/native/d3d12/BufferD3D12.cpp | 52 ++++---- src/dawn/native/d3d12/BufferD3D12.h | 7 +- src/dawn/native/d3d12/CommandBufferD3D12.cpp | 9 +- - .../native/d3d12/CommandRecordingContext.cpp | 19 ++- + .../native/d3d12/CommandRecordingContext.cpp | 26 ++-- .../native/d3d12/CommandRecordingContext.h | 5 +- src/dawn/native/d3d12/D3D12Backend.cpp | 10 +- src/dawn/native/d3d12/DeviceD3D12.cpp | 124 +++++++++++++++--- @@ -32,7 +32,7 @@ Change-Id: I47708462a1d9dd0166120c3a6af93451aae54a07 src/dawn/native/d3d12/UtilsD3D12.cpp | 11 ++ src/dawn/native/d3d12/UtilsD3D12.h | 2 + .../tests/white_box/D3D12ResidencyTests.cpp | 17 +-- - 27 files changed, 329 insertions(+), 166 deletions(-) + 27 files changed, 336 insertions(+), 166 deletions(-) create mode 100644 build_overrides/gpgmm.gni diff --git a/.gitignore b/.gitignore @@ -212,7 +212,7 @@ index e23830e89..144ea3e22 100644 // Create a retrieval filter with a deny list to suppress messages. diff --git a/src/dawn/native/d3d12/BufferD3D12.cpp b/src/dawn/native/d3d12/BufferD3D12.cpp -index 0488fce6a..db8f39de5 100644 +index 0488fce6a..88e8f9cc4 100644 --- a/src/dawn/native/d3d12/BufferD3D12.cpp +++ b/src/dawn/native/d3d12/BufferD3D12.cpp @@ -153,9 +153,15 @@ MaybeError Buffer::Initialize(bool mappedAtCreation) { @@ -323,7 +323,7 @@ index 0488fce6a..db8f39de5 100644 -bool Buffer::CheckAllocationMethodForTesting(AllocationMethod allocationMethod) const { - return mResourceAllocation.GetInfo().mMethod == allocationMethod; +bool Buffer::CheckAllocationMethodForTesting(gpgmm::AllocationMethod allocationMethod) const { -+ return mResourceAllocation->GetMethod() == allocationMethod; ++ return mResourceAllocation->GetInfo().Method == allocationMethod; } MaybeError Buffer::EnsureDataInitialized(CommandRecordingContext* commandContext) { @@ -338,7 +338,7 @@ index 0488fce6a..db8f39de5 100644 MaybeError Buffer::InitializeToZero(CommandRecordingContext* commandContext) { diff --git a/src/dawn/native/d3d12/BufferD3D12.h b/src/dawn/native/d3d12/BufferD3D12.h -index cb36a851a..ca5577930 100644 +index cb36a851a..0ab8e639c 100644 --- a/src/dawn/native/d3d12/BufferD3D12.h +++ b/src/dawn/native/d3d12/BufferD3D12.h @@ -22,6 +22,8 @@ @@ -372,7 +372,7 @@ index cb36a851a..ca5577930 100644 uint64_t size = 0); - ResourceHeapAllocation mResourceAllocation; -+ ComPtr mResourceAllocation; ++ ComPtr mResourceAllocation; bool mFixedResourceState = false; wgpu::BufferUsage mLastUsage = wgpu::BufferUsage::None; ExecutionSerial mLastUsedSerial = std::numeric_limits::max(); @@ -404,7 +404,7 @@ index 08aad3418..b063f6225 100644 uploadHandle.startOffset, size); break; diff --git a/src/dawn/native/d3d12/CommandRecordingContext.cpp b/src/dawn/native/d3d12/CommandRecordingContext.cpp -index 1f7e2766f..d9d1202b7 100644 +index 1f7e2766f..68d7e7c3b 100644 --- a/src/dawn/native/d3d12/CommandRecordingContext.cpp +++ b/src/dawn/native/d3d12/CommandRecordingContext.cpp @@ -45,6 +45,7 @@ MaybeError CommandRecordingContext::Open(ID3D12Device* d3d12Device, @@ -415,7 +415,19 @@ index 1f7e2766f..d9d1202b7 100644 DAWN_TRY(std::move(error)); } } else { -@@ -81,8 +82,6 @@ MaybeError CommandRecordingContext::ExecuteCommandList(Device* device) { +@@ -59,6 +60,11 @@ MaybeError CommandRecordingContext::Open(ID3D12Device* d3d12Device, + mD3d12CommandList.As(&mD3d12CommandList4); + } + ++ ComPtr residencyList; ++ DAWN_TRY(CheckHRESULT(gpgmm::d3d12::CreateResidencyList(&residencyList), ++ "D3D12 creating a residency list")); ++ mResidencyList = std::move(residencyList); ++ + mIsOpen = true; + + return {}; +@@ -81,8 +87,6 @@ MaybeError CommandRecordingContext::ExecuteCommandList(Device* device) { Release(); DAWN_TRY(std::move(error)); } @@ -424,12 +436,12 @@ index 1f7e2766f..d9d1202b7 100644 if (device->IsToggleEnabled(Toggle::RecordDetailedTimingInTraceEvents)) { uint64_t gpuTimestamp; -@@ -121,7 +120,11 @@ MaybeError CommandRecordingContext::ExecuteCommandList(Device* device) { +@@ -121,7 +125,11 @@ MaybeError CommandRecordingContext::ExecuteCommandList(Device* device) { } ID3D12CommandList* d3d12CommandList = GetCommandList(); - device->GetCommandQueue()->ExecuteCommandLists(1, &d3d12CommandList); -+ gpgmm::d3d12::ResidencyList* residencyList = GetResidencyList(); ++ gpgmm::d3d12::IResidencyList* residencyList = GetResidencyList(); + DAWN_TRY( + CheckHRESULT(device->GetResidencyManager()->ExecuteCommandLists( + device->GetCommandQueue().Get(), &d3d12CommandList, &residencyList, 1), @@ -437,7 +449,7 @@ index 1f7e2766f..d9d1202b7 100644 for (Texture* texture : mSharedTextures) { texture->SynchronizeImportedTextureAfterUse(); -@@ -131,17 +134,13 @@ MaybeError CommandRecordingContext::ExecuteCommandList(Device* device) { +@@ -131,17 +139,14 @@ MaybeError CommandRecordingContext::ExecuteCommandList(Device* device) { mSharedTextures.clear(); mHeapsPendingUsage.clear(); mTempBuffers.clear(); @@ -453,13 +465,22 @@ index 1f7e2766f..d9d1202b7 100644 - heap->SetLastUsage(serial); - mHeapsPendingUsage.push_back(heap); - } -+gpgmm::d3d12::ResidencyList* CommandRecordingContext::GetResidencyList() { -+ return &mResidencyList; ++gpgmm::d3d12::IResidencyList* CommandRecordingContext::GetResidencyList() { ++ ASSERT(mResidencyList != nullptr); ++ return mResidencyList.Get(); } ID3D12GraphicsCommandList* CommandRecordingContext::GetCommandList() const { +@@ -161,6 +166,7 @@ ID3D12GraphicsCommandList4* CommandRecordingContext::GetCommandList4() const { + void CommandRecordingContext::Release() { + mD3d12CommandList.Reset(); + mD3d12CommandList4.Reset(); ++ mResidencyList.Reset(); + mIsOpen = false; + mSharedTextures.clear(); + mHeapsPendingUsage.clear(); diff --git a/src/dawn/native/d3d12/CommandRecordingContext.h b/src/dawn/native/d3d12/CommandRecordingContext.h -index 80b6204e1..2a23de23a 100644 +index 80b6204e1..181354a7b 100644 --- a/src/dawn/native/d3d12/CommandRecordingContext.h +++ b/src/dawn/native/d3d12/CommandRecordingContext.h @@ -22,6 +22,8 @@ @@ -476,7 +497,7 @@ index 80b6204e1..2a23de23a 100644 MaybeError ExecuteCommandList(Device* device); - void TrackHeapUsage(Heap* heap, ExecutionSerial serial); -+ gpgmm::d3d12::ResidencyList* GetResidencyList(); ++ gpgmm::d3d12::IResidencyList* GetResidencyList(); void AddToTempBuffers(Ref tempBuffer); @@ -484,7 +505,7 @@ index 80b6204e1..2a23de23a 100644 bool mIsOpen = false; std::set mSharedTextures; std::vector mHeapsPendingUsage; -+ gpgmm::d3d12::ResidencyList mResidencyList; ++ ComPtr mResidencyList; std::vector> mTempBuffers; }; @@ -510,7 +531,7 @@ index 05ddd6b4c..acf1b659d 100644 AdapterDiscoveryOptions::AdapterDiscoveryOptions() diff --git a/src/dawn/native/d3d12/DeviceD3D12.cpp b/src/dawn/native/d3d12/DeviceD3D12.cpp -index 214fa67f8..909e7d810 100644 +index 214fa67f8..4f0d60c6f 100644 --- a/src/dawn/native/d3d12/DeviceD3D12.cpp +++ b/src/dawn/native/d3d12/DeviceD3D12.cpp @@ -132,8 +132,43 @@ MaybeError Device::Initialize(const DeviceDescriptor* descriptor) { @@ -548,12 +569,12 @@ index 214fa67f8..909e7d810 100644 + + if (IsToggleEnabled(Toggle::UseD3D12ResidencyManagement)) { + residencyDesc.MaxPctOfVideoMemoryToBudget = 0.95; // Use up to 95% -+ DAWN_TRY(CheckHRESULT(gpgmm::d3d12::ResidencyManager::CreateResidencyManager( -+ residencyDesc, &mResidencyManager), -+ "D3D12 create residency manager")); ++ DAWN_TRY( ++ CheckHRESULT(gpgmm::d3d12::CreateResidencyManager(residencyDesc, &mResidencyManager), ++ "D3D12 create residency manager")); + } + -+ DAWN_TRY(CheckHRESULT(gpgmm::d3d12::ResourceAllocator::CreateResourceAllocator( ++ DAWN_TRY(CheckHRESULT(gpgmm::d3d12::CreateResourceAllocator( + allocatorDesc, mResidencyManager.Get(), &mResourceAllocator), + "D3D12 create resource allocator")); @@ -565,7 +586,7 @@ index 214fa67f8..909e7d810 100644 -ResidencyManager* Device::GetResidencyManager() const { - return mResidencyManager.get(); -+gpgmm::d3d12::ResidencyManager* Device::GetResidencyManager() const { ++gpgmm::d3d12::IResidencyManager* Device::GetResidencyManager() const { + return mResidencyManager.Get(); } @@ -597,7 +618,7 @@ index 214fa67f8..909e7d810 100644 -void Device::DeallocateMemory(ResourceHeapAllocation& allocation) { - mResourceAllocatorManager->DeallocateMemory(allocation); -+void Device::DeallocateMemory(ComPtr allocation) { ++void Device::DeallocateMemory(ComPtr allocation) { + if (allocation == nullptr) { + return; + } @@ -610,15 +631,15 @@ index 214fa67f8..909e7d810 100644 } -ResultOrError Device::AllocateMemory( -+ResultOrError> Device::CreateExternalAllocation( ++ResultOrError> Device::CreateExternalAllocation( + ComPtr texture) { -+ ComPtr allocation; ++ ComPtr allocation; + DAWN_TRY(CheckHRESULT(mResourceAllocator->CreateResource(texture, &allocation), + "CreateResource failed")); + return allocation; +} + -+ResultOrError> Device::AllocateMemory( ++ResultOrError> Device::AllocateMemory( D3D12_HEAP_TYPE heapType, const D3D12_RESOURCE_DESC& resourceDescriptor, - D3D12_RESOURCE_STATES initialUsage) { @@ -660,7 +681,7 @@ index 214fa67f8..909e7d810 100644 + } + } + -+ ComPtr allocation; ++ ComPtr allocation; + DAWN_TRY(CheckOutOfMemoryHRESULT( + mResourceAllocator->CreateResource(allocationDesc, resourceDescriptor, initialUsage, + optimizedClearValue, &allocation), @@ -691,7 +712,7 @@ index 214fa67f8..909e7d810 100644 mUsedComObjectRefs.ClearUpTo(std::numeric_limits::max()); diff --git a/src/dawn/native/d3d12/DeviceD3D12.h b/src/dawn/native/d3d12/DeviceD3D12.h -index 0373cf9d8..8b0bffdff 100644 +index 0373cf9d8..4cea643f8 100644 --- a/src/dawn/native/d3d12/DeviceD3D12.h +++ b/src/dawn/native/d3d12/DeviceD3D12.h @@ -25,14 +25,14 @@ @@ -716,7 +737,7 @@ index 0373cf9d8..8b0bffdff 100644 CommandAllocatorManager* GetCommandAllocatorManager() const; - ResidencyManager* GetResidencyManager() const; -+ gpgmm::d3d12::ResidencyManager* GetResidencyManager() const; ++ gpgmm::d3d12::IResidencyManager* GetResidencyManager() const; const PlatformFunctions* GetFunctions() const; ComPtr GetFactory() const; @@ -725,17 +746,17 @@ index 0373cf9d8..8b0bffdff 100644 const Extent3D& copySizePixels) override; - ResultOrError AllocateMemory( -+ ResultOrError> AllocateMemory( ++ ResultOrError> AllocateMemory( D3D12_HEAP_TYPE heapType, const D3D12_RESOURCE_DESC& resourceDescriptor, - D3D12_RESOURCE_STATES initialUsage); + D3D12_RESOURCE_STATES initialUsage, + gpgmm::d3d12::ALLOCATION_FLAGS allocationFlags = gpgmm::d3d12::ALLOCATION_FLAG_NONE); + -+ void DeallocateMemory(ComPtr allocation); ++ void DeallocateMemory(ComPtr allocation); - void DeallocateMemory(ResourceHeapAllocation& allocation); -+ ResultOrError> CreateExternalAllocation( ++ ResultOrError> CreateExternalAllocation( + ComPtr texture); ShaderVisibleDescriptorAllocator* GetViewShaderVisibleDescriptorAllocator() const; @@ -746,13 +767,13 @@ index 0373cf9d8..8b0bffdff 100644 std::unique_ptr mCommandAllocatorManager; - std::unique_ptr mResourceAllocatorManager; - std::unique_ptr mResidencyManager; -+ ComPtr mResourceAllocator; -+ ComPtr mResidencyManager; ++ ComPtr mResourceAllocator; ++ ComPtr mResidencyManager; static constexpr uint32_t kMaxSamplerDescriptorsPerBindGroup = 3 * kMaxSamplersPerShaderStage; static constexpr uint32_t kMaxViewDescriptorsPerBindGroup = diff --git a/src/dawn/native/d3d12/ShaderVisibleDescriptorAllocatorD3D12.cpp b/src/dawn/native/d3d12/ShaderVisibleDescriptorAllocatorD3D12.cpp -index fe99a63ac..033620b99 100644 +index fe99a63ac..6cc22c93e 100644 --- a/src/dawn/native/d3d12/ShaderVisibleDescriptorAllocatorD3D12.cpp +++ b/src/dawn/native/d3d12/ShaderVisibleDescriptorAllocatorD3D12.cpp @@ -93,7 +93,8 @@ ShaderVisibleDescriptorAllocator::ShaderVisibleDescriptorAllocator( @@ -812,11 +833,11 @@ index fe99a63ac..033620b99 100644 - // We must track the allocation in the LRU when it is created, otherwise the residency - // manager will see the allocation as non-resident in the later call to LockAllocation. - mDevice->GetResidencyManager()->TrackResidentAllocation(descriptorHeap.get()); -+ ComPtr descriptorHeap; -+ DAWN_TRY(CheckOutOfMemoryHRESULT( -+ gpgmm::d3d12::Heap::CreateHeap(heapDesc, mDevice->GetResidencyManager(), createHeapFn, -+ &descriptorHeap), -+ "Unable to create descriptor heap")); ++ ComPtr descriptorHeap; ++ DAWN_TRY( ++ CheckOutOfMemoryHRESULT(gpgmm::d3d12::CreateHeap(heapDesc, mDevice->GetResidencyManager(), ++ createHeapFn, &descriptorHeap), ++ "Unable to create descriptor heap")); - return std::move(descriptorHeap); + return std::make_unique(std::move(descriptorHeap)); @@ -870,7 +891,7 @@ index fe99a63ac..033620b99 100644 - uint64_t size) - : Pageable(d3d12DescriptorHeap, MemorySegment::Local, size), - mD3d12DescriptorHeap(std::move(d3d12DescriptorHeap)) {} -+ShaderVisibleDescriptorHeap::ShaderVisibleDescriptorHeap(ComPtr descriptorHeap) ++ShaderVisibleDescriptorHeap::ShaderVisibleDescriptorHeap(ComPtr descriptorHeap) + : mDescriptorHeap(std::move(descriptorHeap)) {} ID3D12DescriptorHeap* ShaderVisibleDescriptorHeap::GetD3D12DescriptorHeap() const { @@ -881,13 +902,13 @@ index fe99a63ac..033620b99 100644 + return descriptorHeap.Get(); +} + -+gpgmm::d3d12::Heap* ShaderVisibleDescriptorHeap::GetHeap() const { ++gpgmm::d3d12::IHeap* ShaderVisibleDescriptorHeap::GetHeap() const { + return mDescriptorHeap.Get(); } + } // namespace dawn::native::d3d12 diff --git a/src/dawn/native/d3d12/ShaderVisibleDescriptorAllocatorD3D12.h b/src/dawn/native/d3d12/ShaderVisibleDescriptorAllocatorD3D12.h -index cf09f9d50..86a661c4a 100644 +index cf09f9d50..e76561d2c 100644 --- a/src/dawn/native/d3d12/ShaderVisibleDescriptorAllocatorD3D12.h +++ b/src/dawn/native/d3d12/ShaderVisibleDescriptorAllocatorD3D12.h @@ -24,6 +24,8 @@ @@ -907,13 +928,13 @@ index cf09f9d50..86a661c4a 100644 +class ShaderVisibleDescriptorHeap { public: - ShaderVisibleDescriptorHeap(ComPtr d3d12DescriptorHeap, uint64_t size); -+ ShaderVisibleDescriptorHeap(ComPtr descriptorHeap); ++ ShaderVisibleDescriptorHeap(ComPtr descriptorHeap); ID3D12DescriptorHeap* GetD3D12DescriptorHeap() const; -+ gpgmm::d3d12::Heap* GetHeap() const; ++ gpgmm::d3d12::IHeap* GetHeap() const; private: - ComPtr mD3d12DescriptorHeap; -+ ComPtr mDescriptorHeap; ++ ComPtr mDescriptorHeap; }; class ShaderVisibleDescriptorAllocator { @@ -971,7 +992,7 @@ index edaa2cff4..7753c25cf 100644 } } // namespace dawn::native::d3d12 diff --git a/src/dawn/native/d3d12/StagingBufferD3D12.h b/src/dawn/native/d3d12/StagingBufferD3D12.h -index dcbe7dfed..b855e38c2 100644 +index dcbe7dfed..57bcbcea9 100644 --- a/src/dawn/native/d3d12/StagingBufferD3D12.h +++ b/src/dawn/native/d3d12/StagingBufferD3D12.h @@ -19,6 +19,8 @@ @@ -988,7 +1009,7 @@ index dcbe7dfed..b855e38c2 100644 private: Device* mDevice; - ResourceHeapAllocation mUploadHeap; -+ ComPtr mUploadHeap; ++ ComPtr mUploadHeap; }; } // namespace dawn::native::d3d12 @@ -1100,7 +1121,7 @@ index 41bc0a099..a8d7caf5f 100644 void Texture::SetLabelImpl() { diff --git a/src/dawn/native/d3d12/TextureD3D12.h b/src/dawn/native/d3d12/TextureD3D12.h -index 3e49bc232..cc65566c6 100644 +index 3e49bc232..bfdbddcd3 100644 --- a/src/dawn/native/d3d12/TextureD3D12.h +++ b/src/dawn/native/d3d12/TextureD3D12.h @@ -26,6 +26,8 @@ @@ -1117,7 +1138,7 @@ index 3e49bc232..cc65566c6 100644 D3D12_RESOURCE_FLAGS mD3D12ResourceFlags; - ResourceHeapAllocation mResourceAllocation; -+ ComPtr mResourceAllocation; ++ ComPtr mResourceAllocation; // TODO(dawn:1460): Encapsulate imported image fields e.g. std::unique_ptr. ComPtr mD3D12Fence; diff --git a/Doxyfile b/Doxyfile index d55d474a1..5cc7e74bf 100644 --- a/Doxyfile +++ b/Doxyfile @@ -864,7 +864,7 @@ WARN_LOGFILE = # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # Note: If this tag is empty the current directory is searched. -INPUT = src/gpgmm +INPUT = src # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses diff --git a/README.md b/README.md index 9d0fc4050..4b9acdfbb 100644 --- a/README.md +++ b/README.md @@ -33,9 +33,9 @@ allocatorDesc.Adapter = Adapter; // Use CheckFeatureSupport allocatorDesc.ResourceHeapTier = D3D12_RESOURCE_HEAP_TIER_1; -ComPtr residency; // Optional -ComPtr allocator; -gpgmm::d3d12::ResourceAllocator::CreateResourceAllocator(desc, &allocator, &residency); +ComPtr residency; // Optional +ComPtr allocator; +gpgmm::d3d12::CreateResourceAllocator(desc, &allocator, &residency); ``` ```cpp @@ -43,7 +43,7 @@ D3D12_RESOURCE_DESC& resourceDesc = {}; D3D12_RESOURCE_STATES initialState = {} gpgmm::d3d12::ALLOCATION_DESC allocationDesc = {}; -ComPtr allocation; +ComPtr allocation; allocator->CreateResource(allocationDesc, resourceDesc, initialState, nullptr, &allocation); ``` @@ -51,13 +51,14 @@ GPUs do not support page-faulting, so it's up the GPU application to avoid using physical GPU memory. GPGMM integrates residency into the resource allocators to simplify and optimize allocation: ```cpp -gpgmm::d3d12::ResidencyList list; -list.Add(allocation->GetMemory()); +ComPtr list; +CreateResidencyList(&list); +list->Add(allocation->GetMemory()); residency->ExecuteCommandList(&queue, &commandList, &list, 1); // Prepare for next frame. -list.Reset(); +list->Reset(); ``` Residency also works for non-resources too: @@ -67,8 +68,8 @@ gpgmm::d3d12::HEAP_DESC shaderVisibleHeap = {}; shaderVisibleHeap.SizeInBytes = kHeapSize; shaderVisibleHeap.MemorySegmentGroup = DXGI_MEMORY_SEGMENT_GROUP_LOCAL; -ComPtr descriptorHeap; -gpgmm::d3d12::Heap::CreateHeap(shaderVisibleHeap, residencyManager, +ComPtr descriptorHeap; +gpgmm::d3d12::CreateHeap(shaderVisibleHeap, residencyManager, [&](ID3D12Pageable** ppPageableOut) -> HRESULT { ComPtr heap; mDevice->CreateDescriptorHeap(&heapDesc, IID_PPV_ARGS(&heap)); diff --git a/src/fuzzers/D3D12Fuzzer.h b/src/fuzzers/D3D12Fuzzer.h index c46ccd02d..56cd93a26 100644 --- a/src/fuzzers/D3D12Fuzzer.h +++ b/src/fuzzers/D3D12Fuzzer.h @@ -17,6 +17,8 @@ #include +using Microsoft::WRL::ComPtr; + uint64_t UInt8ToUInt64(const uint8_t* src); HRESULT CreateResourceAllocatorDesc(gpgmm::d3d12::ALLOCATOR_DESC* allocatorDesc); diff --git a/src/fuzzers/D3D12ResidencyManagerFuzzer.cpp b/src/fuzzers/D3D12ResidencyManagerFuzzer.cpp index 85833b190..bef687375 100644 --- a/src/fuzzers/D3D12ResidencyManagerFuzzer.cpp +++ b/src/fuzzers/D3D12ResidencyManagerFuzzer.cpp @@ -22,11 +22,11 @@ namespace { - ComPtr gResourceAllocator; - ComPtr gResidencyManager; - std::vector> gAllocationsBelowBudget = {}; + ComPtr gResourceAllocator; + ComPtr gResidencyManager; + std::vector> gAllocationsBelowBudget = {}; - uint64_t GetBudgetLeft(gpgmm::d3d12::ResidencyManager* const residencyManager, + uint64_t GetBudgetLeft(gpgmm::d3d12::IResidencyManager* const residencyManager, const DXGI_MEMORY_SEGMENT_GROUP& memorySegmentGroup) { if (residencyManager == nullptr) { return 0; @@ -67,13 +67,12 @@ extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv) { residencyDesc.IsUMA = arch.UMA; - if (FAILED(gpgmm::d3d12::ResidencyManager::CreateResidencyManager(residencyDesc, - &gResidencyManager))) { + if (FAILED(gpgmm::d3d12::CreateResidencyManager(residencyDesc, &gResidencyManager))) { return 0; } - if (FAILED(gpgmm::d3d12::ResourceAllocator::CreateResourceAllocator( - allocatorDesc, gResidencyManager.Get(), &gResourceAllocator))) { + if (FAILED(gpgmm::d3d12::CreateResourceAllocator(allocatorDesc, gResidencyManager.Get(), + &gResourceAllocator))) { return 0; } @@ -92,7 +91,7 @@ extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv) { // Keep allocating until we reach the budget. uint64_t memoryUnderBudget = GetBudgetLeft(gResidencyManager.Get(), bufferMemorySegment); while (gResourceAllocator->GetInfo().UsedMemoryUsage + kBufferMemorySize < memoryUnderBudget) { - ComPtr allocation; + ComPtr allocation; if (FAILED(gResourceAllocator->CreateResource({}, bufferDesc, D3D12_RESOURCE_STATE_COMMON, nullptr, &allocation))) { return 0; @@ -112,7 +111,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { gpgmm::d3d12::ALLOCATION_DESC allocationDesc = {}; allocationDesc.HeapType = D3D12_HEAP_TYPE_DEFAULT; - ComPtr allocationOverBudget; + ComPtr allocationOverBudget; gResourceAllocator->CreateResource(allocationDesc, CreateBufferDesc(UInt8ToUInt64(data)), D3D12_RESOURCE_STATE_COMMON, nullptr, &allocationOverBudget); return 0; diff --git a/src/fuzzers/D3D12ResourceAllocatorFuzzer.cpp b/src/fuzzers/D3D12ResourceAllocatorFuzzer.cpp index 823a08884..82020fd9f 100644 --- a/src/fuzzers/D3D12ResourceAllocatorFuzzer.cpp +++ b/src/fuzzers/D3D12ResourceAllocatorFuzzer.cpp @@ -20,7 +20,7 @@ namespace { - ComPtr gResourceAllocator; + ComPtr gResourceAllocator; } // namespace @@ -30,8 +30,8 @@ extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv) { return 0; } - if (FAILED(gpgmm::d3d12::ResourceAllocator::CreateResourceAllocator(allocatorDesc, - &gResourceAllocator))) { + if (FAILED( + gpgmm::d3d12::CreateResourceAllocator(allocatorDesc, &gResourceAllocator, nullptr))) { return 0; } @@ -46,7 +46,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { gpgmm::d3d12::ALLOCATION_DESC allocationDesc = {}; allocationDesc.HeapType = D3D12_HEAP_TYPE_DEFAULT; - ComPtr allocation; + ComPtr allocation; gResourceAllocator->CreateResource(allocationDesc, CreateBufferDesc(UInt8ToUInt64(data)), D3D12_RESOURCE_STATE_COMMON, nullptr, &allocation); return 0; diff --git a/src/gpgmm/common/Backend.h b/src/gpgmm/common/Backend.h index 8cfd0b3b9..47bb70200 100644 --- a/src/gpgmm/common/Backend.h +++ b/src/gpgmm/common/Backend.h @@ -18,19 +18,12 @@ namespace gpgmm { // Forward declare common types. - class MemoryBase; class MemoryAllocation; template struct CommonTrait; // Define common types. - - template - struct CommonTrait { - using CommonType = typename BackendTrait::MemoryType; - }; - template struct CommonTrait { using CommonType = typename BackendTrait::AllocationType; diff --git a/src/gpgmm/common/BuddyMemoryAllocator.cpp b/src/gpgmm/common/BuddyMemoryAllocator.cpp index de850f35d..f5a81cd4e 100644 --- a/src/gpgmm/common/BuddyMemoryAllocator.cpp +++ b/src/gpgmm/common/BuddyMemoryAllocator.cpp @@ -60,7 +60,7 @@ namespace gpgmm { GPGMM_TRY_ASSIGN(TrySubAllocateMemory( &mBuddyBlockAllocator, allocationSize, request.Alignment, request.NeverAllocate, - [&](const auto& block) -> MemoryBase* { + [&](const auto& block) -> IMemoryObject* { const uint64_t memoryIndex = GetMemoryIndex(block->Offset); MemoryAllocation memoryAllocation = mUsedPool.AcquireFromPool(memoryIndex); @@ -78,7 +78,7 @@ namespace gpgmm { memoryAllocation = *memoryAllocationPtr; } - MemoryBase* memory = memoryAllocation.GetMemory(); + IMemoryObject* memory = memoryAllocation.GetMemory(); mUsedPool.ReturnToPool(memoryAllocation, memoryIndex); return memory; @@ -113,7 +113,7 @@ namespace gpgmm { MemoryAllocation memoryAllocation = mUsedPool.AcquireFromPool(memoryIndex); - MemoryBase* memory = memoryAllocation.GetMemory(); + IMemoryObject* memory = memoryAllocation.GetMemory(); ASSERT(memory != nullptr); if (memory->RemoveSubAllocationRef()) { diff --git a/src/gpgmm/common/Memory.cpp b/src/gpgmm/common/Memory.cpp index e38cb02de..5abc0885c 100644 --- a/src/gpgmm/common/Memory.cpp +++ b/src/gpgmm/common/Memory.cpp @@ -36,11 +36,11 @@ namespace gpgmm { return mAlignment; } - MemoryPool* MemoryBase::GetPool() const { + IMemoryPool* MemoryBase::GetPool() const { return mPool; } - void MemoryBase::SetPool(MemoryPool* pool) { + void MemoryBase::SetPool(IMemoryPool* pool) { ASSERT(pool != nullptr); mPool = pool; } diff --git a/src/gpgmm/common/Memory.h b/src/gpgmm/common/Memory.h index 9fb45a5cd..25ced1231 100644 --- a/src/gpgmm/common/Memory.h +++ b/src/gpgmm/common/Memory.h @@ -17,55 +17,22 @@ #include "gpgmm/common/Object.h" #include "gpgmm/utils/RefCount.h" +#include "include/gpgmm.h" namespace gpgmm { - class MemoryPool; - - /** \brief Represents a memory object. - - When memory is sub-allocated, it will have a non-zero refcount. - */ class MemoryBase : public ObjectBase { public: - /** \brief Constructs a memory object of the specified size and alignment. - - @param size Size, in bytes, of the memory object. - @param alignment Alignment, in bytes, of the memory object. - */ explicit MemoryBase(uint64_t size, uint64_t alignment); virtual ~MemoryBase() override; - /** \brief Return the size of the memory object. - - \return Size, in bytes, of the memory object. - */ uint64_t GetSize() const; - - /** \brief Return the alignment of the memory object. - - \return Alignment, in bytes, of the memory object. - */ uint64_t GetAlignment() const; - /** \brief Get the memory pool managing the object. - - \return A pointer to MemoryPool managing this memory object. - */ - MemoryPool* GetPool() const; - - /** \brief Set the memory pool to manage this object. + IMemoryPool* GetPool() const; + void SetPool(IMemoryPool* pool); - @param pool A pointer to MemoryPool used to manage this object. - */ - void SetPool(MemoryPool* pool); - - /** \brief Increments the sub-allocation reference count on the heap. - */ void AddSubAllocationRef(); - - /** \brief Decrements the sub-allocation reference count on the heap. - */ bool RemoveSubAllocationRef(); private: @@ -76,7 +43,7 @@ namespace gpgmm { const uint64_t mSize; const uint64_t mAlignment; - MemoryPool* mPool = nullptr; // nullptr means no pool is assigned. + IMemoryPool* mPool = nullptr; // nullptr means no pool is assigned. }; } // namespace gpgmm diff --git a/src/gpgmm/common/MemoryAllocation.cpp b/src/gpgmm/common/MemoryAllocation.cpp index 2228a3a16..533395114 100644 --- a/src/gpgmm/common/MemoryAllocation.cpp +++ b/src/gpgmm/common/MemoryAllocation.cpp @@ -31,7 +31,7 @@ namespace gpgmm { } MemoryAllocation::MemoryAllocation(MemoryAllocator* allocator, - MemoryBase* memory, + IMemoryObject* memory, uint64_t offset, AllocationMethod method, MemoryBlock* block, @@ -49,7 +49,7 @@ namespace gpgmm { } MemoryAllocation::MemoryAllocation(MemoryAllocator* allocator, - MemoryBase* memory, + IMemoryObject* memory, uint64_t requestSize, uint8_t* mappedPointer) : mAllocator(allocator), @@ -76,7 +76,7 @@ namespace gpgmm { return {GetSize(), GetAlignment()}; } - MemoryBase* MemoryAllocation::GetMemory() const { + IMemoryObject* MemoryAllocation::GetMemory() const { return mMemory; } diff --git a/src/gpgmm/common/MemoryAllocation.h b/src/gpgmm/common/MemoryAllocation.h index 6339fcc88..0704d0d54 100644 --- a/src/gpgmm/common/MemoryAllocation.h +++ b/src/gpgmm/common/MemoryAllocation.h @@ -17,42 +17,12 @@ #define GPGMM_COMMON_MEMORYALLOCATION_H_ #include "gpgmm/utils/Limits.h" +#include "include/gpgmm.h" #include "include/gpgmm_export.h" namespace gpgmm { - /** \enum AllocationMethod - Represents how memory was allocated. - */ - enum class AllocationMethod { - - /** \brief Not yet allocated or invalid. - - This is an invalid state that assigned temporary before the actual method is known. - */ - kUndefined = 0, - - /** \brief Not sub-divided. - - One and only one allocation exists for the memory. - */ - kStandalone = 1, - - /** \brief Sub-divided using one or more allocations. - - Underlying memory will be broken up into one or more memory allocations. - */ - kSubAllocated = 2, - - /** \brief Sub-divided within a single memory allocation. - - A single memory allocation will be broken into one or more sub-allocations. - */ - kSubAllocatedWithin = 3, - }; - struct MemoryBlock; - class MemoryBase; class MemoryAllocator; /** \struct MemoryAllocationInfo @@ -98,7 +68,7 @@ namespace gpgmm { @param mappedPointer A pointer to uint8_t which is mapped by the allocation. */ MemoryAllocation(MemoryAllocator* allocator, - MemoryBase* memory, + IMemoryObject* memory, uint64_t offset, AllocationMethod method, MemoryBlock* block, @@ -116,7 +86,7 @@ namespace gpgmm { @param mappedPointer A pointer to uint8_t which is mapped by the allocation. */ MemoryAllocation(MemoryAllocator* allocator, - MemoryBase* memory, + IMemoryObject* memory, uint64_t requestSize, uint8_t* mappedPointer = nullptr); @@ -137,7 +107,7 @@ namespace gpgmm { \return A pointer to the MemoryBase used by this allocation. */ - MemoryBase* GetMemory() const; + IMemoryObject* GetMemory() const; /** \brief Get the byte addressable pointer mapped by this allocation. @@ -195,7 +165,7 @@ namespace gpgmm { MemoryAllocator* mAllocator; private: - MemoryBase* mMemory; + IMemoryObject* mMemory; uint64_t mOffset; // Offset always local to the memory. AllocationMethod mMethod; MemoryBlock* mBlock; diff --git a/src/gpgmm/common/MemoryAllocator.h b/src/gpgmm/common/MemoryAllocator.h index 8de3d37d2..0291931ad 100644 --- a/src/gpgmm/common/MemoryAllocator.h +++ b/src/gpgmm/common/MemoryAllocator.h @@ -25,55 +25,13 @@ #include "gpgmm/utils/Limits.h" #include "gpgmm/utils/LinkedList.h" #include "gpgmm/utils/Log.h" +#include "include/gpgmm.h" #include #include namespace gpgmm { - /** \struct MemoryAllocatorInfo - Information about the memory allocator. - */ - struct MemoryAllocatorInfo { - /** \brief Number of used sub-allocated blocks within the same memory. - */ - uint32_t UsedBlockCount; - - /** \brief Total size, in bytes, of used sub-allocated blocks. - */ - uint64_t UsedBlockUsage; - - /** \brief Number of used memory allocations. - */ - uint32_t UsedMemoryCount; - - /** \brief Total size, in bytes, of used memory. - */ - uint64_t UsedMemoryUsage; - - /** \brief Total size, in bytes, of free memory. - */ - uint64_t FreeMemoryUsage; - - /** \brief Cache misses not eliminated by prefetching. - */ - uint64_t PrefetchedMemoryMisses; - - /** \brief Cache misses eliminated because of prefetching. - */ - uint64_t PrefetchedMemoryMissesEliminated; - - /** \brief Requested size was NOT cached. - */ - uint64_t SizeCacheMisses; - - /** \brief Requested size was cached. - */ - uint64_t SizeCacheHits; - - MemoryAllocatorInfo& operator+=(const MemoryAllocatorInfo& rhs); - }; - class AllocateMemoryTask; /** \brief MemoryAllocationEvent @@ -293,7 +251,7 @@ namespace gpgmm { MemoryBlock* block = nullptr; GPGMM_TRY_ASSIGN(allocator->TryAllocateBlock(requestSize, alignment), block); - MemoryBase* memory = GetOrCreateMemory(block); + IMemoryObject* memory = GetOrCreateMemory(block); if (memory == nullptr) { // NeverAllocate always fails, so suppress it. if (!neverAllocate) { diff --git a/src/gpgmm/common/MemoryPool.h b/src/gpgmm/common/MemoryPool.h index 046ee77c2..bba3f4c5a 100644 --- a/src/gpgmm/common/MemoryPool.h +++ b/src/gpgmm/common/MemoryPool.h @@ -17,6 +17,7 @@ #include "gpgmm/common/MemoryAllocation.h" #include "gpgmm/utils/Limits.h" +#include "include/gpgmm.h" #include @@ -40,7 +41,7 @@ namespace gpgmm { To shrink the pool, existing allocations can removed out by AcquireFromPool() or de-allocated together by ReleasePool(). */ - class MemoryPool { + class MemoryPool : public IMemoryPool { public: /** \brief Constructs a pool for memory of the specified size. diff --git a/src/gpgmm/common/PooledMemoryAllocator.cpp b/src/gpgmm/common/PooledMemoryAllocator.cpp index 6bed848d8..1fd150cf5 100644 --- a/src/gpgmm/common/PooledMemoryAllocator.cpp +++ b/src/gpgmm/common/PooledMemoryAllocator.cpp @@ -54,7 +54,7 @@ namespace gpgmm { mInfo.UsedMemoryCount++; mInfo.UsedMemoryUsage += allocation.GetSize(); - MemoryBase* memory = allocation.GetMemory(); + IMemoryObject* memory = allocation.GetMemory(); ASSERT(memory != nullptr); return std::make_unique(this, memory, allocation.GetRequestSize()); @@ -70,7 +70,7 @@ namespace gpgmm { mInfo.UsedMemoryCount--; mInfo.UsedMemoryUsage -= allocationSize; - MemoryBase* memory = allocation->GetMemory(); + IMemoryObject* memory = allocation->GetMemory(); ASSERT(memory != nullptr); mPool->ReturnToPool( diff --git a/src/gpgmm/common/SegmentedMemoryAllocator.cpp b/src/gpgmm/common/SegmentedMemoryAllocator.cpp index c50e73bef..348d80b3a 100644 --- a/src/gpgmm/common/SegmentedMemoryAllocator.cpp +++ b/src/gpgmm/common/SegmentedMemoryAllocator.cpp @@ -143,9 +143,8 @@ namespace gpgmm { mInfo.UsedMemoryCount++; mInfo.UsedMemoryUsage += allocation.GetSize(); - MemoryBase* memory = allocation.GetMemory(); + IMemoryObject* memory = allocation.GetMemory(); ASSERT(memory != nullptr); - memory->SetPool(segment); return std::make_unique(this, memory, request.SizeInBytes); @@ -163,13 +162,13 @@ namespace gpgmm { mInfo.UsedMemoryCount--; mInfo.UsedMemoryUsage -= allocationSize; - MemoryBase* memory = allocation->GetMemory(); + IMemoryObject* memory = allocation->GetMemory(); ASSERT(memory != nullptr); - MemoryPool* pool = memory->GetPool(); + IMemoryPool* pool = memory->GetPool(); ASSERT(pool != nullptr); - pool->ReturnToPool( + static_cast(pool)->ReturnToPool( MemoryAllocation(GetNextInChain(), memory, allocation->GetRequestSize())); } diff --git a/src/gpgmm/common/SlabMemoryAllocator.cpp b/src/gpgmm/common/SlabMemoryAllocator.cpp index 330b4e1ce..e68eed44c 100644 --- a/src/gpgmm/common/SlabMemoryAllocator.cpp +++ b/src/gpgmm/common/SlabMemoryAllocator.cpp @@ -257,7 +257,7 @@ namespace gpgmm { GPGMM_TRY_ASSIGN( TrySubAllocateMemory( &pFreeSlab->Allocator, mBlockSize, request.Alignment, request.NeverAllocate, - [&](const auto& block) -> MemoryBase* { + [&](const auto& block) -> IMemoryObject* { // Re-use memory from the free slab. if (pFreeSlab->Allocation.GetMemory() != nullptr) { return pFreeSlab->Allocation.GetMemory(); @@ -407,7 +407,7 @@ namespace gpgmm { pSlab->Allocator.DeallocateBlock(blockInSlab); pSlab->UsedBlocksPerSlab--; - MemoryBase* slabMemory = subAllocation->GetMemory(); + IMemoryObject* slabMemory = subAllocation->GetMemory(); ASSERT(slabMemory != nullptr); slabMemory->RemoveSubAllocationRef(); diff --git a/src/gpgmm/d3d12/BackendD3D12.h b/src/gpgmm/d3d12/BackendD3D12.h index e334f513d..5d7119db8 100644 --- a/src/gpgmm/d3d12/BackendD3D12.h +++ b/src/gpgmm/d3d12/BackendD3D12.h @@ -19,11 +19,9 @@ namespace gpgmm::d3d12 { - class Heap; class ResourceAllocation; struct BackendTrait { - using MemoryType = Heap; using AllocationType = ResourceAllocation; }; diff --git a/src/gpgmm/d3d12/BufferAllocatorD3D12.cpp b/src/gpgmm/d3d12/BufferAllocatorD3D12.cpp index a1199fd1c..1e759f0e9 100644 --- a/src/gpgmm/d3d12/BufferAllocatorD3D12.cpp +++ b/src/gpgmm/d3d12/BufferAllocatorD3D12.cpp @@ -72,7 +72,7 @@ namespace gpgmm::d3d12 { resourceDescriptor.Flags = mResourceFlags; // Optimized clear is not supported for buffers. - Heap* resourceHeap = nullptr; + IHeap* resourceHeap = nullptr; if (FAILED(mResourceAllocator->CreateCommittedResource( mHeapProperties, mHeapFlags, info, &resourceDescriptor, /*pOptimizedClearValue*/ nullptr, mInitialResourceState, /*resourceOut*/ nullptr, diff --git a/src/gpgmm/d3d12/DebugObjectD3D12.h b/src/gpgmm/d3d12/DebugObjectD3D12.h index fe351358c..a2f1e9148 100644 --- a/src/gpgmm/d3d12/DebugObjectD3D12.h +++ b/src/gpgmm/d3d12/DebugObjectD3D12.h @@ -15,40 +15,23 @@ #ifndef GPGMM_D3D12_DEBUGOBJECTD3D12_H_ #define GPGMM_D3D12_DEBUGOBJECTD3D12_H_ +#include "gpgmm/d3d12/IUnknownImplD3D12.h" + #include "gpgmm/d3d12/d3d12_platform.h" #include "include/gpgmm_export.h" #include namespace gpgmm::d3d12 { - - /** \brief Debug object associates additional information for D3D objects using SetPrivateData. - - Since a single D3D object could be re-used by one or more GPGMM objects, debug information must - be stored and retireved seperately. - */ - class GPGMM_EXPORT DebugObject { + class GPGMM_EXPORT DebugObject : public IUnknownImpl { public: - DebugObject() = default; - virtual ~DebugObject() = default; - - DebugObject(const DebugObject&) = default; - DebugObject& operator=(const DebugObject&) = default; + virtual ~DebugObject() override = default; - /** \brief Get the debug name. - - \return A NULL-terminated UNICODE string that contains the name to associate with the debug - object. - */ LPCWSTR GetDebugName() const; - - /** \brief Associate a debug name. - - @param Name A NULL-terminated UNICODE string that contains the name to associate with the - debug object. - */ HRESULT SetDebugName(LPCWSTR Name); + DEFINE_IUNKNOWNIMPL_OVERRIDES() + protected: // Derived classes should override to associate the name with the containing ID3D12Object. virtual HRESULT SetDebugNameImpl(LPCWSTR name) = 0; diff --git a/src/gpgmm/d3d12/EventRecordD3D12.h b/src/gpgmm/d3d12/EventRecordD3D12.h deleted file mode 100644 index 5d5ab6c7a..000000000 --- a/src/gpgmm/d3d12/EventRecordD3D12.h +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright 2022 The GPGMM Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef GPGMM_D3D12_EVENTRECORDD3D12_H_ -#define GPGMM_D3D12_EVENTRECORDD3D12_H_ - -#include "gpgmm/d3d12/d3d12_platform.h" -#include "gpgmm/utils/EnumFlags.h" - -namespace gpgmm::d3d12 { - - /** \enum EVENT_RECORD_FLAGS - Represents different event categories to record. - */ - enum EVENT_RECORD_FLAGS { - - /** \brief Record nothing. - */ - EVENT_RECORD_FLAG_NONE = 0x0, - - /** \brief Record lifetimes of API objects created by GPGMM. - */ - EVENT_RECORD_FLAG_API_OBJECTS = 0x1, - - /** \brief Record API calls made to GPGMM. - */ - EVENT_RECORD_FLAG_API_CALLS = 0x2, - - /** \brief Record duration of GPGMM API calls. - */ - EVENT_RECORD_FLAG_API_TIMINGS = 0x4, - - /** \brief Record metrics made to GPGMM API calls. - */ - EVENT_RECORD_FLAG_COUNTERS = 0x8, - - /** \brief Record events required for playback. - - Bitwise OR'd combination of EVENT_RECORD_FLAG_API_OBJECTS and - EVENT_RECORD_FLAG_API_CALLS. - */ - EVENT_RECORD_FLAG_CAPTURE = 0x3, - - /** \brief Record everything. - */ - EVENT_RECORD_FLAG_ALL_EVENTS = 0xFF, - }; - - DEFINE_ENUM_FLAG_OPERATORS(EVENT_RECORD_FLAGS) - - /** \enum EVENT_RECORD_SCOPE - Represents recording scopes to limit event recording. - */ - enum EVENT_RECORD_SCOPE { - - /** \brief Scopes events per process (or multiple instances). - */ - EVENT_RECORD_SCOPE_PER_PROCESS = 0, - - /** \brief Scopes events per instance. - */ - EVENT_RECORD_SCOPE_PER_INSTANCE = 1, - }; - - /** \struct EVENT_RECORD_OPTIONS - Represents additional controls for recording. - */ - struct EVENT_RECORD_OPTIONS { - /** \brief Flags used to decide what to record. - - Optional parameter. By default, nothing is recorded. - */ - EVENT_RECORD_FLAGS Flags; - - /** \brief Minimum severity level to record messages. - - Messages with lower severity will be ignored. - - Optional parameter. By default, the minimum severity level is WARN. - */ - D3D12_MESSAGE_SEVERITY MinMessageLevel; - - /** \brief Specifies the scope of the events. - - Optional parameter. By default, recording is per process. - */ - EVENT_RECORD_SCOPE EventScope; - - /** \brief Record detailed timing events. - - Optional parameter. By default, detailed timing events are disabled. - */ - bool UseDetailedTimingEvents; - - /** \brief Path to trace file. - - Optional parameter. By default, a trace file is created for you. - */ - const char* TraceFile; - }; - -} // namespace gpgmm::d3d12 - -#endif // GPGMM_D3D12_EVENTRECORDD3D12_H_ diff --git a/src/gpgmm/d3d12/HeapD3D12.cpp b/src/gpgmm/d3d12/HeapD3D12.cpp index 993493190..1cef740b4 100644 --- a/src/gpgmm/d3d12/HeapD3D12.cpp +++ b/src/gpgmm/d3d12/HeapD3D12.cpp @@ -44,17 +44,26 @@ namespace gpgmm::d3d12 { } } // namespace + HRESULT CreateHeap(const HEAP_DESC& descriptor, + IResidencyManager* const pResidencyManager, + CreateHeapFn&& createHeapFn, + IHeap** ppHeapOut) { + return Heap::CreateHeap(descriptor, pResidencyManager, std::move(createHeapFn), ppHeapOut); + } + // static HRESULT Heap::CreateHeap(const HEAP_DESC& descriptor, - ResidencyManager* const pResidencyManager, + IResidencyManager* const pResidencyManager, CreateHeapFn&& createHeapFn, - Heap** ppHeapOut) { + IHeap** ppHeapOut) { const bool isResidencyDisabled = (pResidencyManager == nullptr); + ResidencyManager* residencyManager = static_cast(pResidencyManager); + // Ensure enough budget exists before creating the heap to avoid an out-of-memory error. if (!isResidencyDisabled && (descriptor.Flags & HEAP_FLAG_ALWAYS_IN_BUDGET)) { - ReturnIfFailed(pResidencyManager->EnsureInBudget(descriptor.SizeInBytes, - descriptor.MemorySegmentGroup)); + ReturnIfFailed(residencyManager->EnsureInBudget(descriptor.SizeInBytes, + descriptor.MemorySegmentGroup)); } ComPtr pageable; @@ -97,7 +106,7 @@ namespace gpgmm::d3d12 { // descriptor heap), they must be manually locked and unlocked to be inserted into the // residency cache. if (heap->mState != RESIDENCY_STATUS_UNKNOWN) { - ReturnIfFailed(pResidencyManager->InsertHeap(heap.get())); + ReturnIfFailed(residencyManager->InsertHeap(heap.get())); } } @@ -181,6 +190,14 @@ namespace gpgmm::d3d12 { return mPageable->QueryInterface(riid, ppvObject); } + ULONG STDMETHODCALLTYPE Heap::AddRef() { + return IUnknownImpl::AddRef(); + } + + ULONG STDMETHODCALLTYPE Heap::Release() { + return IUnknownImpl::Release(); + } + void Heap::SetResidencyState(RESIDENCY_STATUS newStatus) { mState = newStatus; } @@ -193,4 +210,36 @@ namespace gpgmm::d3d12 { return IsResidencyLocked(); } + uint64_t Heap::GetSize() const { + return MemoryBase::GetSize(); + } + + uint64_t Heap::GetAlignment() const { + return MemoryBase::GetAlignment(); + } + + void Heap::AddSubAllocationRef() { + return MemoryBase::AddSubAllocationRef(); + } + + bool Heap::RemoveSubAllocationRef() { + return MemoryBase::RemoveSubAllocationRef(); + } + + LPCWSTR Heap::GetDebugName() const { + return DebugObject::GetDebugName(); + } + + HRESULT Heap::SetDebugName(LPCWSTR Name) { + return DebugObject::SetDebugName(Name); + } + + IMemoryPool* Heap::GetPool() const { + return MemoryBase::GetPool(); + } + + void Heap::SetPool(IMemoryPool* pool) { + return MemoryBase::SetPool(pool); + } + } // namespace gpgmm::d3d12 diff --git a/src/gpgmm/d3d12/HeapD3D12.h b/src/gpgmm/d3d12/HeapD3D12.h index 6bad55910..2bb202469 100644 --- a/src/gpgmm/d3d12/HeapD3D12.h +++ b/src/gpgmm/d3d12/HeapD3D12.h @@ -21,6 +21,7 @@ #include "gpgmm/d3d12/IUnknownImplD3D12.h" #include "gpgmm/utils/Limits.h" #include "gpgmm/utils/LinkedList.h" +#include "include/gpgmm_d3d12.h" #include "include/gpgmm_export.h" #include // for std::function @@ -29,168 +30,40 @@ namespace gpgmm::d3d12 { class ResidencyManager; - class ResourceAllocator; - - /** \enum RESIDENCY_STATUS - - D3D12 allows heaps to be explicitly created resident or not. This means the expected residency - status of the heap cannot be solely determined by checking for the existence in a residency - cache. - - Heaps are in one of three exclusive states: never made resident or unknown, about to become - resident or pending residency, and currently resident. When a heap gets evicted or paged-out, it - transitions from currently resident to pending residency. Paged-in is the reverse of this, - pending residency to currently resident. If the heap was known to be created resident by D3D12, - it will immediately become currently resident. If the heap becomes locked, it will stay - currently resident until it is evicted, then back to pending residency. - */ - enum RESIDENCY_STATUS { - /** \brief Heap residency status is not known and cannot be made resident. - Heap must become locked to be managed for residency. - */ - RESIDENCY_STATUS_UNKNOWN = 0, - - /** \brief Heap is about to be made resident. - Heap must be previously locked, evicted, or currently resident at creation. - */ - RESIDENCY_STATUS_PENDING_RESIDENCY = 1, - - /** \brief Heap was made resident and can be evicted. - Heaps that stay locked will always be currently resident. - */ - RESIDENCY_STATUS_CURRENT_RESIDENT = 2, - }; - - /** \struct HEAP_INFO - Additional information about the heap. - */ - struct HEAP_INFO { - /** \brief Check if the heap currently locked for residency. - */ - bool IsLocked; - - /** \brief Check if the heap was made resident or not. - */ - RESIDENCY_STATUS Status; - }; - - /** \enum HEAPS_FLAGS - Specify creation options to configure the heap. - */ - enum HEAPS_FLAGS { - - /** \brief Disables all option flags. - */ - HEAPS_FLAG_NONE = 0x0, - - /** \brief Requires the heap to be created in budget. - */ - HEAP_FLAG_ALWAYS_IN_BUDGET = 0x1, - }; - - DEFINE_ENUM_FLAG_OPERATORS(HEAPS_FLAGS) - - /** \struct HEAP_DESC - Specifies creation options for a residency managed heap. - */ - struct HEAP_DESC { - /** \brief Created size of the heap, in bytes. - - Must be non-zero. SizeInBytes is always a multiple of the alignment. - */ - uint64_t SizeInBytes; - - /** \brief Created alignment of the heap, in bytes. - - Must be non-zero. - */ - uint64_t Alignment; - /** \brief Specifies heaps options. - */ - HEAPS_FLAGS Flags; - - /** \brief Specifies the memory segment to use for residency. - - Allows any heap to specify a segment which does not have a attributed heap type. - */ - DXGI_MEMORY_SEGMENT_GROUP MemorySegmentGroup; - - /** \brief Debug name associated with the heap. - */ - LPCWSTR DebugName; - }; - - /** \brief Callback function used to create a ID3D12Pageable. - - For example, to create a ID3D12Heap: - - \code - auto callback = [heapDesc](ID3D12Pageable** ppPageableOut) -> HRESULT { - ComPtr heap; - ReturnIfFailed(mDevice->CreateHeap(&heapDesc, IID_PPV_ARGS(&heap))); - *ppPageableOut = heap.Detach(); - }; - \endcode - */ - using CreateHeapFn = std::function; - - /** \brief Heap is used to represent managed ID3D12Heap or ID3D12Resource that has an implicit - heap (owned by D3D) for a committed resource, in the ResidencyManager's residency cache. - - 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. - */ class GPGMM_EXPORT Heap final : public MemoryBase, public DebugObject, - public IUnknownImpl, - public LinkNode { + public LinkNode, + public IHeap { public: - /** \brief Create a heap managed by GPGMM. - - Unlike a normal D3D12 heap, a heap managed by GPGMM means it will be tracked 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. - - @param descriptor A reference to HEAP_DESC structure that describes the heap. - @param pResidencyManager A pointer to the ResidencyManager used to manage this heap. - @param createHeapFn A callback function which creates a ID3D12Pageable derived type. - @param[out] ppHeapOut Pointer to a memory block that recieves a pointer to the - heap. - */ static HRESULT CreateHeap(const HEAP_DESC& descriptor, - ResidencyManager* const pResidencyManager, + IResidencyManager* const pResidencyManager, CreateHeapFn&& createHeapFn, - Heap** ppHeapOut); + IHeap** ppHeapOut); ~Heap() override; - /** \brief Returns a ComPtr object that represents the interface specified. - - For example, to get a ID3D12Heap: + // IHeap interface + HEAP_INFO GetInfo() const override; + bool IsInResidencyLRUCacheForTesting() const override; + bool IsResidencyLockedForTesting() const override; - \code - ComPtr heap; - HRESULT hr = resourceHeap.As(&heap); - \endcode + // IMemoryObject + uint64_t GetSize() const override; + uint64_t GetAlignment() const override; + void AddSubAllocationRef() override; + bool RemoveSubAllocationRef() override; + IMemoryPool* GetPool() const override; + void SetPool(IMemoryPool* pool) override; - \return Error HRESULT if the specified interface was not represented by the - heap. - */ + // IUnknown interface HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject) override; + ULONG STDMETHODCALLTYPE AddRef() override; + ULONG STDMETHODCALLTYPE Release() override; - - /** \brief Get information about the heap. - - \return HEAP_INFO with the latest information. - */ - HEAP_INFO GetInfo() const; - - // Testing only. - bool IsInResidencyLRUCacheForTesting() const; - bool IsResidencyLockedForTesting() const; + // IDebugObject interface + LPCWSTR GetDebugName() const override; + HRESULT SetDebugName(LPCWSTR Name) override; private: friend ResidencyManager; diff --git a/src/gpgmm/d3d12/IUnknownImplD3D12.h b/src/gpgmm/d3d12/IUnknownImplD3D12.h index cdc8e433d..5bb5ba117 100644 --- a/src/gpgmm/d3d12/IUnknownImplD3D12.h +++ b/src/gpgmm/d3d12/IUnknownImplD3D12.h @@ -50,6 +50,14 @@ namespace gpgmm::d3d12 { GPGMM_REFCOUNT_TYPE mRefCount; // Maintains the COM ref-count of this object. }; +// Helper to provide IUnknown-based object types the required overrides to use IUnknownImpl. +#define DEFINE_IUNKNOWNIMPL_OVERRIDES() \ + inline HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject) override { \ + return IUnknownImpl::QueryInterface(riid, ppvObject); \ + } \ + inline ULONG STDMETHODCALLTYPE AddRef() override { return IUnknownImpl::AddRef(); } \ + inline ULONG STDMETHODCALLTYPE Release() override { return IUnknownImpl::Release(); } + } // namespace gpgmm::d3d12 #endif // GPGMM_D3D12_IUNKNOWNIMPLD3D12_H_ diff --git a/src/gpgmm/d3d12/JSONSerializerD3D12.cpp b/src/gpgmm/d3d12/JSONSerializerD3D12.cpp index 629ed921d..b16b6dd59 100644 --- a/src/gpgmm/d3d12/JSONSerializerD3D12.cpp +++ b/src/gpgmm/d3d12/JSONSerializerD3D12.cpp @@ -233,7 +233,8 @@ namespace gpgmm::d3d12 { for (uint64_t i = 0; i < desc.Count; i++) { JSONDict residencyListDict; JSONArray heapArray; - for (const auto& heap : *desc.ResidencyLists[i]) { + ResidencyList* residencyList = static_cast(desc.ResidencyLists[i]); + for (const auto& heap : *residencyList) { heapArray.AddItem(gpgmm::JSONSerializer::Serialize(heap)); } if (!heapArray.IsEmpty()) { diff --git a/src/gpgmm/d3d12/JSONSerializerD3D12.h b/src/gpgmm/d3d12/JSONSerializerD3D12.h index 45756e14b..c1bac4388 100644 --- a/src/gpgmm/d3d12/JSONSerializerD3D12.h +++ b/src/gpgmm/d3d12/JSONSerializerD3D12.h @@ -17,23 +17,10 @@ #include "gpgmm/common/JSONSerializer.h" #include "gpgmm/d3d12/d3d12_platform.h" +#include "include/gpgmm_d3d12.h" namespace gpgmm::d3d12 { - // Forward declare backend types. - struct ALLOCATION_DESC; - struct ALLOCATOR_DESC; - struct EVENT_RECORD_OPTIONS; - struct HEAP_DESC; - struct HEAP_INFO; - struct RESOURCE_ALLOCATION_DESC; - struct RESOURCE_ALLOCATION_INFO; - class ResidencyList; - struct RESIDENCY_DESC; - - // Declare backend aliases. - using RESOURCE_ALLOCATOR_INFO = MemoryAllocatorInfo; - struct CREATE_RESOURCE_DESC { const ALLOCATION_DESC& allocationDescriptor; const D3D12_RESOURCE_DESC& resourceDescriptor; @@ -47,7 +34,7 @@ namespace gpgmm::d3d12 { }; struct EXECUTE_COMMAND_LISTS_DESC { - ResidencyList* const* ResidencyLists; + IResidencyList* const* ResidencyLists; uint32_t Count; }; diff --git a/src/gpgmm/d3d12/ResidencyListD3D12.cpp b/src/gpgmm/d3d12/ResidencyListD3D12.cpp index 66e99146e..499cf3cf1 100644 --- a/src/gpgmm/d3d12/ResidencyListD3D12.cpp +++ b/src/gpgmm/d3d12/ResidencyListD3D12.cpp @@ -18,6 +18,13 @@ namespace gpgmm::d3d12 { + HRESULT CreateResidencyList(IResidencyList** ppResidencyList) { + if (ppResidencyList != nullptr) { + *ppResidencyList = new ResidencyList(); + } + return S_OK; + } + ResidencyList::ResidencyList() { GPGMM_TRACE_EVENT_OBJECT_NEW(this); } @@ -26,7 +33,7 @@ namespace gpgmm::d3d12 { GPGMM_TRACE_EVENT_OBJECT_DESTROY(this); } - HRESULT ResidencyList::Add(Heap* pHeap) { + HRESULT ResidencyList::Add(IHeap* pHeap) { if (pHeap == nullptr) { return E_INVALIDARG; } diff --git a/src/gpgmm/d3d12/ResidencyListD3D12.h b/src/gpgmm/d3d12/ResidencyListD3D12.h index 2219f267e..934e0823b 100644 --- a/src/gpgmm/d3d12/ResidencyListD3D12.h +++ b/src/gpgmm/d3d12/ResidencyListD3D12.h @@ -15,7 +15,9 @@ #ifndef GPGMM_D3D12_RESIDENCYLISTD3D12_H_ #define GPGMM_D3D12_RESIDENCYLISTD3D12_H_ +#include "gpgmm/d3d12/IUnknownImplD3D12.h" #include "gpgmm/d3d12/d3d12_platform.h" +#include "include/gpgmm_d3d12.h" #include "include/gpgmm_export.h" #include @@ -26,47 +28,21 @@ namespace gpgmm::d3d12 { class JSONSerializer; class ResidencyManager; - /** \brief Represents a list of heaps which will be "made resident" upon executing a - command-list. - - A residency list helps track heaps for residency which will be referenced together by a - command-list. The application uses a ResidencyList by inserting heaps, by calling - ResourceAllocation::GetMemory, into the list. Once ResidencyManager::ExecuteCommandLists is - called, the list can be reset or cleared for the next frame or compute job. - - Without ResidencyList, the application would need to call ResidencyManager::LockHeap and - ResidencyManager::UnlockHeap for each heap before and after every GPU command or command-list - being executed. - */ - class GPGMM_EXPORT ResidencyList final { + class GPGMM_EXPORT ResidencyList final : public IResidencyList, public IUnknownImpl { public: - /** \brief Create a residency list or collection of heaps to manage together for residency. - */ ResidencyList(); - ~ResidencyList(); - - ResidencyList(const ResidencyList&) = default; - ResidencyList& operator=(const ResidencyList&) = default; - - /** \brief Adds a heap to the residency list. - - @param pHeap A pointer to Heap about to be added. - - \return S_OK if heap was added, else error. - */ - HRESULT Add(Heap* pHeap); + ~ResidencyList() override; - /** \brief Reset this residency list. + HRESULT Add(IHeap* pHeap) override; + HRESULT Reset() override; - Removes all heaps from the list so the list can be re-used. - */ - HRESULT Reset(); + DEFINE_IUNKNOWNIMPL_OVERRIDES() private: friend JSONSerializer; friend ResidencyManager; - using UnderlyingType = std::vector; + using UnderlyingType = std::vector; UnderlyingType::const_iterator begin() const; UnderlyingType::const_iterator end() const; diff --git a/src/gpgmm/d3d12/ResidencyManagerD3D12.cpp b/src/gpgmm/d3d12/ResidencyManagerD3D12.cpp index 36f548368..f754654e1 100644 --- a/src/gpgmm/d3d12/ResidencyManagerD3D12.cpp +++ b/src/gpgmm/d3d12/ResidencyManagerD3D12.cpp @@ -149,9 +149,14 @@ namespace gpgmm::d3d12 { std::shared_ptr mEvent; }; + HRESULT CreateResidencyManager(const RESIDENCY_DESC& descriptor, + IResidencyManager** ppResidencyManagerOut) { + return ResidencyManager::CreateResidencyManager(descriptor, ppResidencyManagerOut); + } + // static HRESULT ResidencyManager::CreateResidencyManager(const RESIDENCY_DESC& descriptor, - ResidencyManager** ppResidencyManagerOut) { + IResidencyManager** ppResidencyManagerOut) { if (descriptor.Adapter == nullptr || descriptor.Device == nullptr) { return E_INVALIDARG; } @@ -301,74 +306,75 @@ namespace gpgmm::d3d12 { } // Increments number of locks on a heap to ensure the heap remains resident. - HRESULT ResidencyManager::LockHeap(Heap* pHeap) { + HRESULT ResidencyManager::LockHeap(IHeap* pHeap) { std::lock_guard lock(mMutex); - if (pHeap == nullptr) { + Heap* heap = static_cast(pHeap); + if (heap == nullptr) { return E_INVALIDARG; } - if (!pHeap->IsInList() && !pHeap->IsResidencyLocked()) { + if (!heap->IsInList() && !heap->IsResidencyLocked()) { ComPtr pageable; - ReturnIfFailed(pHeap->QueryInterface(IID_PPV_ARGS(&pageable))); - ReturnIfFailed(MakeResident(pHeap->GetMemorySegmentGroup(), pHeap->GetSize(), 1, + ReturnIfFailed(heap->QueryInterface(IID_PPV_ARGS(&pageable))); + ReturnIfFailed(MakeResident(heap->GetMemorySegmentGroup(), heap->GetSize(), 1, pageable.GetAddressOf())); - pHeap->SetResidencyState(RESIDENCY_STATUS_CURRENT_RESIDENT); + heap->SetResidencyState(RESIDENCY_STATUS_CURRENT_RESIDENT); // Untracked heaps, created not resident, are not already attributed toward residency // usage because they are not in the residency cache. mInfo.CurrentMemoryCount++; - mInfo.CurrentMemoryUsage += pHeap->GetSize(); + mInfo.CurrentMemoryUsage += heap->GetSize(); } // Since we can't evict the heap, it's unnecessary to track the heap in the LRU Cache. - if (pHeap->IsInList()) { - pHeap->RemoveFromList(); + if (heap->IsInList()) { + heap->RemoveFromList(); // Untracked heaps, previously made resident, are not attributed toward residency usage // because they will be removed from the residency cache. - if (pHeap->mState == RESIDENCY_STATUS_CURRENT_RESIDENT) { + if (heap->mState == RESIDENCY_STATUS_CURRENT_RESIDENT) { mInfo.CurrentMemoryCount++; - mInfo.CurrentMemoryUsage += pHeap->GetSize(); + mInfo.CurrentMemoryUsage += heap->GetSize(); } } - pHeap->AddResidencyLockRef(); + heap->AddResidencyLockRef(); return S_OK; } // Decrements number of locks on a heap. When the number of locks becomes zero, the heap is // inserted into the LRU cache and becomes eligible for eviction. - HRESULT ResidencyManager::UnlockHeap(Heap* pHeap) { + HRESULT ResidencyManager::UnlockHeap(IHeap* pHeap) { std::lock_guard lock(mMutex); - - if (pHeap == nullptr) { + Heap* heap = static_cast(pHeap); + if (heap == nullptr) { return E_INVALIDARG; } - if (!pHeap->IsResidencyLocked()) { + if (!heap->IsResidencyLocked()) { return E_FAIL; } - if (pHeap->IsInList()) { + if (heap->IsInList()) { return E_FAIL; } - pHeap->ReleaseResidencyLock(); + heap->ReleaseResidencyLock(); // If another lock still exists on the heap, nothing further should be done. - if (pHeap->IsResidencyLocked()) { + if (heap->IsResidencyLocked()) { return S_OK; } // When all locks have been removed, the resource remains resident and becomes tracked in // the corresponding LRU. - ReturnIfFailed(InsertHeapInternal(pHeap)); + ReturnIfFailed(InsertHeapInternal(heap)); // Heaps inserted into the residency cache are already attributed in residency usage. mInfo.CurrentMemoryCount--; - mInfo.CurrentMemoryUsage -= pHeap->GetSize(); + mInfo.CurrentMemoryUsage -= heap->GetSize(); return S_OK; } @@ -674,7 +680,7 @@ namespace gpgmm::d3d12 { // usage. HRESULT ResidencyManager::ExecuteCommandLists(ID3D12CommandQueue* pQueue, ID3D12CommandList* const* ppCommandLists, - ResidencyList* const* ppResidencyLists, + IResidencyList* const* ppResidencyLists, uint32_t count) { TRACE_EVENT0(TraceEventCategory::kDefault, "ResidencyManager.ExecuteCommandLists"); @@ -689,7 +695,7 @@ namespace gpgmm::d3d12 { return E_NOTIMPL; } - ResidencyList* residencyList = ppResidencyLists[0]; + ResidencyList* residencyList = static_cast(ppResidencyLists[0]); std::vector localHeapsToMakeResident; std::vector nonLocalHeapsToMakeResident; @@ -697,7 +703,10 @@ namespace gpgmm::d3d12 { uint64_t nonLocalSizeToMakeResident = 0; std::vector heapsToMakeResident; - for (Heap* heap : *residencyList) { + for (IHeap* pHeap : *residencyList) { + Heap* heap = static_cast(pHeap); + ASSERT(heap != nullptr); + // Heaps that are locked resident are not tracked in the LRU cache. if (heap->IsResidencyLocked()) { continue; diff --git a/src/gpgmm/d3d12/ResidencyManagerD3D12.h b/src/gpgmm/d3d12/ResidencyManagerD3D12.h index 9df68ec71..40129e582 100644 --- a/src/gpgmm/d3d12/ResidencyManagerD3D12.h +++ b/src/gpgmm/d3d12/ResidencyManagerD3D12.h @@ -16,9 +16,10 @@ #ifndef GPGMM_D3D12_RESIDENCYMANAGERD3D12_H_ #define GPGMM_D3D12_RESIDENCYMANAGERD3D12_H_ -#include "gpgmm/d3d12/EventRecordD3D12.h" #include "gpgmm/d3d12/IUnknownImplD3D12.h" +#include "gpgmm/utils/EnumFlags.h" #include "gpgmm/utils/LinkedList.h" +#include "include/gpgmm_d3d12.h" #include "include/gpgmm_export.h" #include @@ -31,229 +32,37 @@ namespace gpgmm { namespace gpgmm::d3d12 { class BudgetUpdateTask; + class BudgetUpdateEvent; class Fence; class Heap; - class ResidencyList; class ResourceAllocator; class ResourceHeapAllocator; - /** \enum RESIDENCY_FLAGS - Specify options to configure the residency manager. - */ - enum RESIDENCY_FLAGS { - - /** \brief Disables all option flags. - */ - RESIDENCY_FLAG_NONE = 0x0, - - /** \brief Disables automatic background memory budget updates by OS notifications. - - By default, memory budget updates will be pushed by the OS using a background thread. If - the OS does not support push notifications or budget updates are not frequent enough, this - mechanism can be disabled where a pull-based method is used instead. - */ - RESIDENCY_FLAG_NEVER_UPDATE_BUDGET_ON_WORKER_THREAD = 0x1, - }; - - DEFINE_ENUM_FLAG_OPERATORS(RESIDENCY_FLAGS) - - /** \struct RESIDENCY_DESC - Specify parameters when creating a residency manager. - */ - struct RESIDENCY_DESC { - /** \brief Specifies the device used by this residency manager. - Required parameter. Use CreateDevice get the device. - */ - Microsoft::WRL::ComPtr Device; - - /** \brief Specifies the adapter used by this residency manager. - - Requires DXGI 1.4 due to IDXGIAdapter3::QueryVideoMemoryInfo. - - Required parameter. Use EnumAdapters to get the adapter. - */ - Microsoft::WRL::ComPtr Adapter; - - /** \brief Specifies if unified memory architecture (UMA) is enabled. - - When UMA is enabled, the residency manager will budget using a single memory segment. - Else, when UMA is false, the residency manager will have two budgets for local and non-local - memory segments, respectively. If IsUMA is false and the adapter is discrete, this will - effectively double the amount of memory bandwidth. If IsUMA is true and the adapter is UMA, - using a single budget can reduce residency and memory overhead. - - Required parameter. Use CheckFeatureSupport to determine if supported. - */ - bool IsUMA; - - /** \brief Specifies residency options. - */ - RESIDENCY_FLAGS Flags; - - /** \brief Minimum severity level to log messages to console. - - Messages with lower severity will be ignored. - - Optional parameter. By default, will log only corruption messages. - */ - D3D12_MESSAGE_SEVERITY MinLogLevel; - - /** \brief Specifies recording options. - - For example, what events to record, and where to record them. - - Optional parameter. By default, no options are specified for recording. - */ - EVENT_RECORD_OPTIONS RecordOptions; - - /** \brief Maximum amount of budgeted memory, expressed as a percentage of video memory, - that can be budgeted. - - If a non-zero MaxBudgetInBytes is specified, MaxPctOfVideoMemoryToBudget is ignored. - - Optional parameter. By default, the API will automatically set the budget to 95% of video - memory, leaving 5% for the OS and other applications. - */ - float MaxPctOfVideoMemoryToBudget; - - /** \brief Lowest amount of budgeted memory, expressed as a percentage, that can be - reserved. - - If SetVideoMemoryReservation is used a set a reservation larger then the budget, this amount - is used instead so the application can make forward progress. - - Optional parameter. By default, the API restricts the residency manager reservation to never - go below 50% of the budget. - */ - float MinPctOfBudgetToReserve; - - /** \brief Maximum amount of budgeted memory, in bytes, that can be budgeted. - - Allows a fixed budget to be artifically set for testing purposes. - - Optional parameter. By default, the API will not further restrict the residency manager - budget. - */ - uint64_t MaxBudgetInBytes; - - /** \brief Size of memory, in bytes, to evict from residency at once, - should there not be enough budget left. - - Optional parameter. When 0 is specified, the API will use a evict size of 50MB. - */ - uint64_t EvictSizeInBytes; - - /** \brief Initial fence value to use when managing heaps for residency. - - Fence value gets assigned to each managed heap and increments each time ExecuteCommandList() - is called. When over budget, these fence values are compared to determine which heaps can be - evicted. - - Optional parameter. Zero by default. - */ - uint64_t InitialFenceValue; - }; - - /** \struct RESIDENCY_INFO - Additional information about the residency manager. - */ - struct RESIDENCY_INFO { - /** \brief Amount of memory, in bytes, currently resident. - */ - uint64_t CurrentMemoryUsage; - - /** \brief Number of heaps, currently resident. - */ - uint64_t CurrentMemoryCount; - }; - - class BudgetUpdateEvent; - - /** \brief ResidencyManager tracks and maintains one or more Heap within a residency cache. - - A Heap is considered "resident" when it is accessible by the GPU. A Heap can be made explicitly - resident by calling ResidencyManager::LockHeap or implicitly resident by using the Heap with a - ResidencyList upon calling ResidencyManager::ExecuteCommandLists or through a - operation that always requires the Heap to be resident (eg. Map, Unmap). - - Internally, the ResidencyManager keeps the application in-budget by calling ID3D12Device::Evict - and ID3D12Device::MakeResident to page-out or page-in heaps, respectively. - **/ - class GPGMM_EXPORT ResidencyManager final : public IUnknownImpl { + class GPGMM_EXPORT ResidencyManager final : public IUnknownImpl, public IResidencyManager { public: - /** \brief Create residency residency manager to manage video memory. - - @param descriptor A reference to RESIDENCY_DESC structure that describes the residency - manager. - @param[out] ppResidencyManagerOut Pointer to a memory block that recieves a pointer to the - residency manager. Pass NULL to test if residency Manager creation would succeed, but not - actually create the residency Manager. If NULL is passed and residency manager creating - would succeed, S_FALSE is returned. - */ static HRESULT CreateResidencyManager(const RESIDENCY_DESC& descriptor, - ResidencyManager** ppResidencyManagerOut); + IResidencyManager** ppResidencyManagerOut); ~ResidencyManager() override; - /** \brief Locks the specified heap. - - Locking a heap means the residency manager will never evict it when over budget. - - @param pHeap A pointer to the heap being locked. - */ - HRESULT LockHeap(Heap* pHeap); - - /** \brief Unlocks the specified heap. - - Unlocking a heap allows the residency manager will evict it when over budget. - - @param pHeap A pointer to the heap being unlocked. - */ - HRESULT UnlockHeap(Heap* pHeap); - - /** \brief Execute command lists using residency managed heaps. - - Submits an array of command lists and residency lists for the specified command queue. - - @param pQueue The command queue to submit to. - @param ppCommandLists The array of ID3D12CommandList command lists to be executed. - @param ppResidencyLists The array of ResidencyList residency lists to make resident. - @param count The size of commandLists and residencyLists arrays. - */ + // IResidencyManager interface + HRESULT LockHeap(IHeap* pHeap) override; + HRESULT UnlockHeap(IHeap* pHeap) override; HRESULT ExecuteCommandLists(ID3D12CommandQueue* pQueue, ID3D12CommandList* const* ppCommandLists, - ResidencyList* const* ppResidencyLists, - uint32_t count); + IResidencyList* const* ppResidencyLists, + uint32_t count) override; - /** \brief Sets video memory reservation. - - A reservation is the lowest amount of physical memory the application need to continue - operation safely. - - @param memorySegmentGroup Memory segment to reserve. - @param availableForReservation Amount of memory to reserve, in bytes. - @param[out] pCurrentReservationOut the amount of memory reserved, which may be less then the - |reservation| when under video memory pressure. A value of nullptr will update but not - return the current reservation. - */ HRESULT SetVideoMemoryReservation(const DXGI_MEMORY_SEGMENT_GROUP& memorySegmentGroup, uint64_t availableForReservation, - uint64_t* pCurrentReservationOut = nullptr); - - /** \brief Get the current budget and memory usage. + uint64_t* pCurrentReservationOut = nullptr) override; - @param memorySegmentGroup Memory segment to retrieve info from. - @param[out] pVideoMemoryInfoOut Pointer to DXGI_QUERY_VIDEO_MEMORY_INFO to populate. A value - of nullptr will update but not return the current info. - */ HRESULT QueryVideoMemoryInfo(const DXGI_MEMORY_SEGMENT_GROUP& memorySegmentGroup, - DXGI_QUERY_VIDEO_MEMORY_INFO* pVideoMemoryInfoOut); + DXGI_QUERY_VIDEO_MEMORY_INFO* pVideoMemoryInfoOut) override; - /** \brief Return the current residency manager usage. + RESIDENCY_INFO GetInfo() const override; - \return A RESIDENCY_INFO struct. - */ - RESIDENCY_INFO GetInfo() const; + DEFINE_IUNKNOWNIMPL_OVERRIDES() private: friend Heap; diff --git a/src/gpgmm/d3d12/ResourceAllocationD3D12.cpp b/src/gpgmm/d3d12/ResourceAllocationD3D12.cpp index 7ca0bc704..ea62afc4e 100644 --- a/src/gpgmm/d3d12/ResourceAllocationD3D12.cpp +++ b/src/gpgmm/d3d12/ResourceAllocationD3D12.cpp @@ -42,9 +42,9 @@ namespace gpgmm::d3d12 { } // namespace ResourceAllocation::ResourceAllocation(const RESOURCE_ALLOCATION_DESC& desc, - ResidencyManager* residencyManager, + IResidencyManager* residencyManager, MemoryAllocator* allocator, - Heap* resourceHeap, + IHeap* resourceHeap, MemoryBlock* block, ComPtr resource) : MemoryAllocation(allocator, @@ -137,15 +137,15 @@ namespace gpgmm::d3d12 { } RESOURCE_ALLOCATION_INFO ResourceAllocation::GetInfo() const { - return {GetSize(), GetAlignment()}; + return {GetSize(), GetAlignment(), GetMethod()}; } const char* ResourceAllocation::GetTypename() const { return "ResourceAllocation"; } - Heap* ResourceAllocation::GetMemory() const { - return ToBackend(MemoryAllocation::GetMemory()); + IHeap* ResourceAllocation::GetMemory() const { + return static_cast(MemoryAllocation::GetMemory()); } void ResourceAllocation::SetDebugAllocator(MemoryAllocator* allocator) { @@ -161,4 +161,12 @@ namespace gpgmm::d3d12 { return SetDebugObjectName(mResource.Get(), name); } + LPCWSTR ResourceAllocation::GetDebugName() const { + return DebugObject::GetDebugName(); + } + + HRESULT ResourceAllocation::SetDebugName(LPCWSTR Name) { + return DebugObject::SetDebugName(Name); + } + } // namespace gpgmm::d3d12 diff --git a/src/gpgmm/d3d12/ResourceAllocationD3D12.h b/src/gpgmm/d3d12/ResourceAllocationD3D12.h index 41e96ee87..e155982e1 100644 --- a/src/gpgmm/d3d12/ResourceAllocationD3D12.h +++ b/src/gpgmm/d3d12/ResourceAllocationD3D12.h @@ -20,156 +20,47 @@ #include "gpgmm/d3d12/DebugObjectD3D12.h" #include "gpgmm/d3d12/IUnknownImplD3D12.h" #include "gpgmm/d3d12/d3d12_platform.h" +#include "include/gpgmm_d3d12.h" #include "include/gpgmm_export.h" namespace gpgmm::d3d12 { class DebugResourceAllocator; - class Heap; class ResidencyManager; - class ResidencyList; class ResourceAllocator; - /** \struct RESOURCE_ALLOCATION_DESC - Describes a resource allocation. - */ - struct RESOURCE_ALLOCATION_DESC { - /** \brief Requested size, in bytes, of the resource allocation. - - The requested size is the non-zero allocation size before being subjected to allocator - alignment. - */ - uint64_t SizeInBytes; - - /** \brief Offset, in bytes, of the resource in the heap. - */ - uint64_t HeapOffset; - - /** \brief Offset, in bytes, of the allocation, from the start of the - resource. - - Always zero when the resource is placed in a heap or created with it's own heap. - */ - uint64_t OffsetFromResource; - - /** \brief Method to describe how the allocation was created. - - The Method determines how to figure out the size of the allocation. - */ - AllocationMethod Method; - - /** \brief Debug name associated with the resource allocation. - */ - LPCWSTR DebugName; - }; - - /** \struct RESOURCE_ALLOCATION_INFO - Additional information about the resource allocation. - */ - struct RESOURCE_ALLOCATION_INFO { - /** \brief Created size, in bytes, of the resource allocation. - - Must be non-zero. SizeInBytes is always a multiple of the alignment. - */ - uint64_t SizeInBytes; - - /** \brief Created alignment, in bytes, of the resource allocation. - - Must be non-zero. - */ - uint64_t Alignment; - }; - - /** \brief ResourceAllocation is MemoryAllocation that contains a ID3D12Resource. - - It can represent a allocation using a resource in one of three ways: 1) ID3D12Resource "placed" - in a ID3D12Heap, 2) a ID3D12Resource at a specific offset, or 3) a ID3D12Resource without a - ID3D12Heap (called a committed resource). - - It is recommend to use ResourceAllocation instead of ID3D12Resource (1:1) for perfoming D3D12 - operations with it (eg. Map, Unmap, etc). - */ class GPGMM_EXPORT ResourceAllocation final : public MemoryAllocation, public DebugObject, - public IUnknownImpl { + public IResourceAllocation { public: ~ResourceAllocation() override; - /** \brief Maps the resource allocation. - - Gets the CPU pointer to the specificed subresource of the resource allocation. - - If sub-allocated within the resource, the read or write range and - pointer value will start from the allocation instead of the resource. - - @param subresource Specifies the index number of the subresource. - @param pReadRange A pointer to a D3D12_RANGE structure that describes the range of memory to - access. - @param[out] ppDataOut A pointer to a memory block that receives a pointer to the resource - data. - */ + // IResourceAllocation interface HRESULT Map(uint32_t subresource = 0, const D3D12_RANGE* pReadRange = nullptr, - void** ppDataOut = nullptr); - - /** \brief Unmaps the resource allocation. - - Invalidates the CPU pointer to the specified subresource in the resource. - - @param subresource Specifies the index number of the subresource. - @param pWrittenRange A pointer to a D3D12_RANGE structure that describes the range of memory - to unmap. - */ - void Unmap(uint32_t subresource = 0, const D3D12_RANGE* pWrittenRange = nullptr); - - /** \brief Returns the resource owned by this allocation. - - \return Pointer to ID3D12Resource, owned by this allocation. - */ - ID3D12Resource* GetResource() const; - - /** \brief Returns the GPU virtual address of the resource allocation. + void** ppDataOut = nullptr) override; + void Unmap(uint32_t subresource = 0, const D3D12_RANGE* pWrittenRange = nullptr) override; + ID3D12Resource* GetResource() const override; + D3D12_GPU_VIRTUAL_ADDRESS GetGPUVirtualAddress() const override; + uint64_t GetOffsetFromResource() const override; + RESOURCE_ALLOCATION_INFO GetInfo() const override; + IHeap* GetMemory() const override; - If sub-allocated within the resource, the GPU virtual address will - start from the allocation instead of the resource. + DEFINE_IUNKNOWNIMPL_OVERRIDES() - \return A D3D12_GPU_VIRTUAL_ADDRESS, equal to UINT64, to represent a location in GPU memory. - */ - D3D12_GPU_VIRTUAL_ADDRESS GetGPUVirtualAddress() const; + // IDebugObject interface + LPCWSTR GetDebugName() const override; + HRESULT SetDebugName(LPCWSTR Name) override; - /** \brief Returns the start of the allocation in the resource. - - If sub-allocated within the resource, the offset could be greater than zero. - - \return A offset, in bytes, of the start of this allocation in the resource. - */ - uint64_t GetOffsetFromResource() const; - - /** \brief Returns information about this resource allocation. - - \return A RESOURCE_ALLOCATION_INFO struct containing the information. - */ - RESOURCE_ALLOCATION_INFO GetInfo() const; - - /** \brief Returns the class name of this allocation. - - \return A pointer to a C character string with data, "ResourceAllocation". - */ const char* GetTypename() const; - /** \brief Returns the heap assigned to this resource allocation. - - \return A pointer to the Heap used by this resource allocation. - */ - Heap* GetMemory() const; - private: friend ResourceAllocator; ResourceAllocation(const RESOURCE_ALLOCATION_DESC& desc, - ResidencyManager* residencyManager, + IResidencyManager* residencyManager, MemoryAllocator* allocator, - Heap* resourceHeap, + IHeap* resourceHeap, MemoryBlock* block, ComPtr resource); @@ -181,7 +72,7 @@ namespace gpgmm::d3d12 { void DeleteThis() override; - ResidencyManager* const mResidencyManager; + IResidencyManager* const mResidencyManager; ComPtr mResource; const uint64_t mOffsetFromResource; diff --git a/src/gpgmm/d3d12/ResourceAllocatorD3D12.cpp b/src/gpgmm/d3d12/ResourceAllocatorD3D12.cpp index 0b9eb4038..dd38f862c 100644 --- a/src/gpgmm/d3d12/ResourceAllocatorD3D12.cpp +++ b/src/gpgmm/d3d12/ResourceAllocatorD3D12.cpp @@ -278,7 +278,7 @@ namespace gpgmm::d3d12 { // RAII wrapper to lock/unlock heap from the residency cache. class ScopedResidencyLock final { public: - ScopedResidencyLock(ResidencyManager* const residencyManager, Heap* const heap) + ScopedResidencyLock(IResidencyManager* const residencyManager, IHeap* const heap) : mResidencyManager(residencyManager), mHeap(heap) { ASSERT(heap != nullptr); if (mResidencyManager != nullptr) { @@ -293,8 +293,8 @@ namespace gpgmm::d3d12 { } private: - ResidencyManager* const mResidencyManager; - Heap* const mHeap; + IResidencyManager* const mResidencyManager; + IHeap* const mHeap; }; // Combines AllocatorMemory and Create*Resource into a single call. @@ -363,15 +363,29 @@ namespace gpgmm::d3d12 { } // namespace + HRESULT CreateResourceAllocator(const ALLOCATOR_DESC& allocatorDescriptor, + IResourceAllocator** ppResourceAllocatorOut, + IResidencyManager** ppResidencyManagerOut = nullptr) { + return ResourceAllocator::CreateResourceAllocator( + allocatorDescriptor, ppResourceAllocatorOut, ppResidencyManagerOut); + } + + HRESULT CreateResourceAllocator(const ALLOCATOR_DESC& allocatorDescriptor, + IResidencyManager* pResidencyManager, + IResourceAllocator** ppResourceAllocatorOut) { + return ResourceAllocator::CreateResourceAllocator(allocatorDescriptor, pResidencyManager, + ppResourceAllocatorOut); + } + // static HRESULT ResourceAllocator::CreateResourceAllocator(const ALLOCATOR_DESC& allocatorDescriptor, - ResourceAllocator** ppResourceAllocatorOut, - ResidencyManager** ppResidencyManagerOut) { + IResourceAllocator** ppResourceAllocatorOut, + IResidencyManager** ppResidencyManagerOut) { if (allocatorDescriptor.Device == nullptr || allocatorDescriptor.Adapter == nullptr) { return E_INVALIDARG; } - ComPtr residencyManager; + ComPtr residencyManager; if (ppResidencyManagerOut != nullptr) { std::unique_ptr caps; { @@ -392,7 +406,7 @@ namespace gpgmm::d3d12 { ResidencyManager::CreateResidencyManager(residencyDesc, &residencyManager)); } - ComPtr resourceAllocator; + ComPtr resourceAllocator; ReturnIfFailed(CreateResourceAllocator(allocatorDescriptor, residencyManager.Get(), &resourceAllocator)); @@ -408,9 +422,10 @@ namespace gpgmm::d3d12 { } // static - HRESULT ResourceAllocator::CreateResourceAllocator(const ALLOCATOR_DESC& allocatorDescriptor, - ResidencyManager* pResidencyManager, - ResourceAllocator** ppResourceAllocatorOut) { + HRESULT ResourceAllocator::CreateResourceAllocator( + const ALLOCATOR_DESC& allocatorDescriptor, + IResidencyManager* pResidencyManager, + IResourceAllocator** ppResourceAllocatorOut) { if (allocatorDescriptor.Adapter == nullptr || allocatorDescriptor.Device == nullptr) { return E_INVALIDARG; } @@ -536,10 +551,10 @@ namespace gpgmm::d3d12 { } ResourceAllocator::ResourceAllocator(const ALLOCATOR_DESC& descriptor, - ComPtr residencyManager, + IResidencyManager* pResidencyManager, std::unique_ptr caps) : mDevice(std::move(descriptor.Device)), - mResidencyManager(std::move(residencyManager)), + mResidencyManager(pResidencyManager), mCaps(std::move(caps)), mResourceHeapTier(descriptor.ResourceHeapTier), mIsAlwaysCommitted(descriptor.Flags & ALLOCATOR_FLAG_ALWAYS_COMMITED), @@ -554,8 +569,11 @@ namespace gpgmm::d3d12 { mDebugAllocator = std::make_unique(); } + ResidencyManager* residencyManager = + static_cast(mResidencyManager.Get()); + const bool isUMA = - (IsResidencyEnabled()) ? mResidencyManager->IsUMA() : mCaps->IsAdapterUMA(); + (IsResidencyEnabled()) ? residencyManager->IsUMA() : mCaps->IsAdapterUMA(); for (uint32_t resourceHeapTypeIndex = 0; resourceHeapTypeIndex < kNumOfResourceHeapTypes; resourceHeapTypeIndex++) { @@ -842,14 +860,14 @@ namespace gpgmm::d3d12 { const D3D12_RESOURCE_DESC& resourceDescriptor, D3D12_RESOURCE_STATES initialResourceState, const D3D12_CLEAR_VALUE* pClearValue, - ResourceAllocation** ppResourceAllocationOut) { + IResourceAllocation** ppResourceAllocationOut) { GPGMM_TRACE_EVENT_OBJECT_CALL( "ResourceAllocator.CreateResource", (CREATE_RESOURCE_DESC{allocationDescriptor, resourceDescriptor, initialResourceState, pClearValue})); std::lock_guard lock(mMutex); - ComPtr allocation; + ComPtr allocation; ReturnIfFailed(CreateResourceInternal(allocationDescriptor, resourceDescriptor, initialResourceState, pClearValue, &allocation)); @@ -858,7 +876,7 @@ namespace gpgmm::d3d12 { // behind a macro. if (mDebugAllocator) { ReturnIfFailed(allocation->SetDebugName(allocationDescriptor.DebugName)); - mDebugAllocator->AddLiveAllocation(allocation.Get()); + mDebugAllocator->AddLiveAllocation(static_cast(allocation.Get())); } // Update the current usage counters. @@ -868,7 +886,7 @@ namespace gpgmm::d3d12 { #if defined(GPGMM_ENABLE_MEMORY_ALIGN_CHECKS) // Allocation is subject to alignment requirements per allocator or allocation method. - CheckAndReportAllocationMisalignment(*allocation.Get()); + CheckAndReportAllocationMisalignment(*static_cast(allocation.Get())); #endif if (ppResourceAllocationOut != nullptr) { @@ -883,7 +901,7 @@ namespace gpgmm::d3d12 { const D3D12_RESOURCE_DESC& resourceDescriptor, D3D12_RESOURCE_STATES initialResourceState, const D3D12_CLEAR_VALUE* clearValue, - ResourceAllocation** ppResourceAllocationOut) { + IResourceAllocation** ppResourceAllocationOut) { TRACE_EVENT0(TraceEventCategory::kDefault, "ResourceAllocator.CreateResource"); // If d3d tells us the resource size is invalid, treat the error as OOM. @@ -995,17 +1013,19 @@ namespace gpgmm::d3d12 { // 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, mResidencyManager->IsUMA()); + GetMemoryPool(heapProperties, residencyManager->IsUMA()); const DXGI_MEMORY_SEGMENT_GROUP segment = GetMemorySegmentGroup( - heapProperties.MemoryPoolPreference, mResidencyManager->IsUMA()); + heapProperties.MemoryPoolPreference, residencyManager->IsUMA()); DXGI_QUERY_VIDEO_MEMORY_INFO* currentVideoInfo = - mResidencyManager->GetVideoMemoryInfo(segment); + residencyManager->GetVideoMemoryInfo(segment); // If over-budget, only free memory is considered available. // TODO: Consider optimizing GetInfoInternal(). @@ -1057,7 +1077,7 @@ namespace gpgmm::d3d12 { // Committed resource implicitly creates a resource heap which can be // used for sub-allocation. ComPtr committedResource; - Heap* resourceHeap = ToBackend(subAllocation.GetMemory()); + IHeap* resourceHeap = static_cast(subAllocation.GetMemory()); ReturnIfFailed(resourceHeap->QueryInterface(IID_PPV_ARGS(&committedResource))); RESOURCE_ALLOCATION_DESC allocationDesc = {}; @@ -1094,7 +1114,7 @@ namespace gpgmm::d3d12 { // Each allocation maps to a disjoint (physical) address range so no physical // memory is can be aliased or will overlap. ComPtr placedResource; - Heap* resourceHeap = ToBackend(subAllocation.GetMemory()); + IHeap* resourceHeap = static_cast(subAllocation.GetMemory()); ReturnIfFailed(CreatePlacedResource(resourceHeap, subAllocation.GetOffset(), &newResourceDesc, clearValue, initialResourceState, &placedResource)); @@ -1133,7 +1153,7 @@ namespace gpgmm::d3d12 { ReturnIfSucceeded(TryAllocateResource( mDevice.Get(), allocator, request, [&](const auto& allocation) -> HRESULT { - Heap* resourceHeap = ToBackend(allocation.GetMemory()); + IHeap* resourceHeap = static_cast(allocation.GetMemory()); ComPtr placedResource; ReturnIfFailed(CreatePlacedResource(resourceHeap, allocation.GetOffset(), &newResourceDesc, clearValue, @@ -1177,7 +1197,7 @@ namespace gpgmm::d3d12 { } ComPtr committedResource; - Heap* resourceHeap = nullptr; + IHeap* resourceHeap = nullptr; ReturnIfFailed(CreateCommittedResource(heapProperties, heapFlags, resourceInfo, &newResourceDesc, clearValue, initialResourceState, &committedResource, &resourceHeap)); @@ -1204,7 +1224,7 @@ namespace gpgmm::d3d12 { } HRESULT ResourceAllocator::CreateResource(ComPtr resource, - ResourceAllocation** ppResourceAllocationOut) { + IResourceAllocation** ppResourceAllocationOut) { std::lock_guard lock(mMutex); if (!ppResourceAllocationOut) { @@ -1226,7 +1246,7 @@ namespace gpgmm::d3d12 { resourceHeapDesc.SizeInBytes = resourceInfo.SizeInBytes; resourceHeapDesc.Alignment = resourceInfo.Alignment; - Heap* resourceHeap = nullptr; + IHeap* resourceHeap = nullptr; ReturnIfFailed(Heap::CreateHeap( resourceHeapDesc, /*residencyManager*/ nullptr, [&](ID3D12Pageable** ppPageableOut) -> HRESULT { @@ -1256,7 +1276,7 @@ namespace gpgmm::d3d12 { return S_OK; } - HRESULT ResourceAllocator::CreatePlacedResource(Heap* const resourceHeap, + HRESULT ResourceAllocator::CreatePlacedResource(IHeap* const resourceHeap, uint64_t resourceOffset, const D3D12_RESOURCE_DESC* resourceDescriptor, const D3D12_CLEAR_VALUE* clearValue, @@ -1290,7 +1310,7 @@ namespace gpgmm::d3d12 { const D3D12_CLEAR_VALUE* clearValue, D3D12_RESOURCE_STATES initialResourceState, ID3D12Resource** commitedResourceOut, - Heap** resourceHeapOut) { + IHeap** resourceHeapOut) { TRACE_EVENT0(TraceEventCategory::kDefault, "ResourceAllocator.CreateCommittedResource"); HEAP_DESC resourceHeapDesc = {}; @@ -1300,13 +1320,15 @@ namespace gpgmm::d3d12 { resourceHeapDesc.Flags |= (heapFlags & D3D12_HEAP_FLAG_CREATE_NOT_RESIDENT) ? HEAPS_FLAG_NONE : HEAP_FLAG_ALWAYS_IN_BUDGET; + ResidencyManager* residencyManager = + static_cast(mResidencyManager.Get()); if (IsResidencyEnabled()) { resourceHeapDesc.MemorySegmentGroup = GetMemorySegmentGroup( - heapProperties.MemoryPoolPreference, mResidencyManager->IsUMA()); + heapProperties.MemoryPoolPreference, residencyManager->IsUMA()); } // Since residency is per heap, every committed resource is wrapped in a heap object. - Heap* resourceHeap = nullptr; + IHeap* resourceHeap = nullptr; ComPtr committedResource; ReturnIfFailed(Heap::CreateHeap( diff --git a/src/gpgmm/d3d12/ResourceAllocatorD3D12.h b/src/gpgmm/d3d12/ResourceAllocatorD3D12.h index 2ae894042..61ece82d4 100644 --- a/src/gpgmm/d3d12/ResourceAllocatorD3D12.h +++ b/src/gpgmm/d3d12/ResourceAllocatorD3D12.h @@ -17,8 +17,9 @@ #define GPGMM_D3D12_RESOURCEALLOCATORD3D12_H_ #include "gpgmm/common/MemoryAllocator.h" -#include "gpgmm/d3d12/EventRecordD3D12.h" #include "gpgmm/d3d12/IUnknownImplD3D12.h" +#include "gpgmm/utils/EnumFlags.h" +#include "include/gpgmm_d3d12.h" #include "include/gpgmm_export.h" #include @@ -38,577 +39,38 @@ namespace gpgmm::d3d12 { class ResidencyManager; class ResourceAllocation; - /** \enum ALLOCATOR_FLAGS - Specify creation options for allocator. - */ - enum ALLOCATOR_FLAGS { - - /** \brief Disables all option flags. - */ - ALLOCATOR_FLAG_NONE = 0x0, - - /** \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. - */ - ALLOCATOR_FLAG_ALWAYS_COMMITED = 0x1, - - /** \brief Creates resource within budget. - - By default (and when residency is used), resources will not be created resident unless an - operation is performed on the allocation that requires them to be (ex. Map). Otherwise, the - resource will become resident once ExecuteCommandList() is called. However, this flag can be - used to change this behavior by requiring resource heaps to be always resident at resource - creation. When residency is not used, ALLOCATOR_FLAG_ALWAYS_IN_BUDGET is implicitly enabled - through the GPU/driver instead of explicitly through GPGMM. - */ - ALLOCATOR_FLAG_ALWAYS_IN_BUDGET = 0x2, - - /** \brief Disables pre-fetching of GPU memory. - - Should be only used for debugging and testing purposes. - */ - ALLOCATOR_FLAG_DISABLE_MEMORY_PREFETCH = 0x4, - - /** \brief Disables recycling of GPU memory. - - Forces the creation of new heaps and to de-allocate heaps immediately once no longer needed - (instead of re-using it). - - This is very slow and not recommended for general use but may be useful for running with the - minimal possible GPU memory footprint, avoiding out-of-memory, or debugging possible - corruption of heaps. - */ - ALLOCATOR_FLAG_ALWAYS_ON_DEMAND = 0x8, - - /** \brief Disables use of D3D12_HEAP_TYPE_CUSTOM. - - Used to workaround issues when a custom-equivalent heap is not considered equal to - the corresponding heap type. - */ - ALLOCATOR_FLAG_DISABLE_CUSTOM_HEAPS = 0x10, - - /** \brief Report leaks of resource allocations. - - Used to track outstanding allocations made with this allocator. When the allocator is about - to be released, it will report details on any leaked allocations as log messages. - */ - ALLOCATOR_FLAG_NEVER_LEAK_MEMORY = 0x20, - }; - - DEFINE_ENUM_FLAG_OPERATORS(ALLOCATOR_FLAGS) - - /** \enum ALLOCATOR_ALGORITHM - Specify the algorithms used for allocation. - */ - enum ALLOCATOR_ALGORITHM { - /** \brief Use default allocation mechanism. - - Relies on internal heuristics to automatically determine the best allocation mechanism. The - selection of algorithm depends on: - - 1. The heap properties or flags specified by the user. - 2. The size the resource being created. - 3. The amount of available memory. - - In general, the most-efficent resource allocator will be attempted first (efficent - being defined as fastest service-time to allocate/deallocate with smallest memory - footprint), subject to other constraints. However, since it's impossible to predict all - future memory accesses, allocation techniques that rely on amortization of GPU heaps may not - prove to be faster as expected. Further experimentation is recommended. - */ - ALLOCATOR_ALGORITHM_DEFAULT = 0, - - /** \brief Use the slab allocation mechanism. - - Slab allocation allocates/deallocates in O(1) time using O(N * pageSize) space. - - Slab allocation does not suffer from internal fragmentation but could externally fragment - when many unique request sizes are used. - */ - ALLOCATOR_ALGORITHM_SLAB = 1, - - /** \brief Use the buddy system mechanism. - - Buddy system allocate/deallocates in O(Log2) time using O(1) space. - - Buddy system suffers from internal fragmentation (ie. resources are not a power-of-two) but - does not suffer from external fragmentation as much since the resource heap size does not - change. - - It is recommend to specify a PreferredResourceHeapSize large enough such that multiple - requests can fit within the specified PreferredResourceHeapSize but not too large where - creating the larger resource heap becomes a bigger bottleneck. - */ - ALLOCATOR_ALGORITHM_BUDDY_SYSTEM = 2, - - /** \brief Recycles resource heaps of a size being specified. - - Fixed pools allocate/deallocate in O(1) time using O(N) space. - - Fixed-size pool limits recycling to resource heaps equal to - PreferredResourceHeapSize. A PreferredResourceHeapSize of zero is effectively - equivelent to ALLOCATOR_FLAG_ALWAYS_ON_DEMAND. - */ - ALLOCATOR_ALGORITHM_FIXED_POOL = 3, - - /** \brief Recycles resource heaps of any size using multiple pools. - - Segmented pool allocate/deallocates in O(Log2) time using O(N * K) space. - */ - ALLOCATOR_ALGORITHM_SEGMENTED_POOL = 4, - - /** \brief Use the dedicated allocation mechanism. - - Allows resources to be created as a dedicated allocation, rather than sub-allocated. - - A dedicated allocation allocates exactly what is needed for the resource and nothing more. - - Internally, dedicated allocations are "placed resources" which allows the heap to be - recycled by GPGMM. Otherwise, ALLOCATOR_FLAG_ALWAYS_COMMITED is equivelent to a "dedicated - allocation" but without heaps being recycled by GPGMM. - - Dedicated allocation allocates/deallocates in O(1) time using O(N * pageSize) space. - */ - ALLOCATOR_ALGORITHM_DEDICATED = 5, - }; - - /** \struct ALLOCATOR_DESC - Specify parameters for creating allocators. - */ - struct ALLOCATOR_DESC { - /** \brief Specifies the device used by this allocator. - - Required parameter. Use CreateDevice get the device. - */ - Microsoft::WRL::ComPtr Device; - - /** \brief Specifies the adapter used by this allocator. - - Required parameter. Use EnumAdapters to get the adapter. - */ - Microsoft::WRL::ComPtr Adapter; - - /** \brief Specifies allocator options. - - For example, whether the allocator can reuse memory, or resources should be resident upon - creation. - */ - ALLOCATOR_FLAGS Flags; - - /** \brief Minimum severity level to log messages to console. - - Messages with lower severity will be ignored. - */ - D3D12_MESSAGE_SEVERITY MinLogLevel; - - /** \brief Specifies recording options. - - For example, what events to record, and where to record them. - */ - EVENT_RECORD_OPTIONS RecordOptions; - - /** \brief Specifies the adapter's tier of resource heap support. - - Used to determine if resource categories (texture and buffers) can co-exist in the - same resource heap. - - Required parameter. Use CheckFeatureSupport to get supported tier. - */ - D3D12_RESOURCE_HEAP_TIER ResourceHeapTier; - - /** \brief Specifies the algorithm to use for sub-allocation. - - Used to evaluate how allocation implementations perform with various algorithms that - sub-divide resource heaps. - - Optional parameter. By default, the slab allocator is used. - */ - ALLOCATOR_ALGORITHM SubAllocationAlgorithm; - - /** \brief Specifies the algorithm to use for resource heap pooling. - - Used to evaluate how allocation implementations perform with various algorithms that - sub-divide resource heaps. - - Optional parameter. By default, the slab allocator is used. - */ - ALLOCATOR_ALGORITHM PoolAlgorithm; - - /** \brief Specifies the preferred size of the resource heap. - - The preferred size of the resource heap is the minimum heap size to sub-allocate from. - A larger resource heap consumes more memory but could be faster for sub-allocation. - - Optional parameter. When 0 is specified, the API will automatically set the preferred - resource heap size to be a multiple of minimum resource heap size allowed by D3D12. - */ - uint64_t PreferredResourceHeapSize; - - /** \brief Maximum size of the resource heap allowed. - - The maximum resource heap size is equal to the total virtual address range of memory - available to the allocator. - - Optional parameter. When 0 is specified, the API will automatically set the max resource - heap size based on the adapter's GPU virtual address range. If the max resource size - exceeds the adapter's GPU virtual address range, it will default to the smaller range. - */ - uint64_t MaxResourceHeapSize; - - /** \brief Memory fragmentation limit, expressed as a percentage of the resource heap size, - that is acceptable to be wasted due to fragmentation. - - Fragmentation occurs when the allocation is larger then the resource size. - This occurs when the type of resource (buffer or texture) and allocator have different - alignment requirements. For example, a 192KB resource may need to allocate 256KB of - allocated space, which is equivalent to a fragmentation limit of 33%. - - When PreferredResourceHeapSize is non-zero, the MemoryFragmentationLimit could be - exceeded. Also, the MemoryFragmentationLimit should never be zero, as some fragmentation - can occur. - - Optional parameter. When 0 is specified, the default fragmentation limit is 1/8th the - resource heap size. - */ - double MemoryFragmentationLimit; - - /** \brief Memory growth factor, expressed as a multipler of the resource heap size - that will monotonically increase. - - A factor value of 1.0 specifies no growth, where the resource heap size is always determined - by other limits or constraints. If no factor gets specified (or a value less than 1 is - specified), GPGMM will allocate a resource heap size with enough space to fit exactly one - resource. - - Memory growth avoids the need to specify |PreferredResourceHeapSize|, which - especially helps in situations where the resource size cannot be predicated (eg. - user-defined), by allowing the resource heap size to gradually increase in size - per demand to achieve a balance of memory usage and performance. - - Optional parameter. When 0 is specified, the default of 1.25 is used (or 25% growth). - */ - double MemoryGrowthFactor; - }; - - /** \enum ALLOCATION_FLAGS - Additional controls that modify allocations. - */ - enum ALLOCATION_FLAGS { - - /** \brief Disables all allocation flags. - - Enabled by default. - */ - ALLOCATION_FLAG_NONE = 0x0, - - /** \brief Disallow creating a new resource heap when creating a resource. - - The created resource must use an existing resource heap or E_OUTOFMEMORY. Effectively - disables creating standalone allocations whose memory cannot be reused. - */ - ALLOCATION_FLAG_NEVER_ALLOCATE_MEMORY = 0x1, - - /** \brief Sub-allocate a resource allocation within the same resource. - - The resource alignment is allowed to be byte-aligned instead of being resource-aligned, - which significantly reduces app memory usage (1B vs 64KB per allocation). Since the resource - can only be in one state at a time, this is mostly restricted to constant buffers (index and - vertex buffers which will stay read-only after creation). The app developer must use offsets - from the start of the allocation (vs subresource index) by using - ResourceAllocation::GetOffsetFromResource(). - - The app developer must either check if the allocator supports sub-allocation within resource - beforehand (via ResourceAllocator::CheckFeatureSupport) OR simply ensure only a command - single queue is used since not all devices guarentee command queue accesses are coherent - between sub-allocations within the same resource. - */ - ALLOCATION_FLAG_ALLOW_SUBALLOCATE_WITHIN_RESOURCE = 0x2, - - /** \brief Disallow allowing the creation of multiple resources using the same resource - heap. - - When this flag is used, the created resource will always be allocated with it's own resource - heap. - */ - ALLOCATION_FLAG_NEVER_SUBALLOCATE_MEMORY = 0x4, - - /** \brief Prefetch memory for the next resource allocation. - - The call to prefetch is deferred to a seperate background thread by GPGMM which runs - when the current allocation requested is completed. By default, GPGMM will automatically - trigger prefetching based on heurstics. Prefetching enables more performance when - allocating for contiguous allocations or many resources of the same size. - - Should not be used with ALLOCATION_FLAG_NEVER_ALLOCATE_MEMORY. - */ - ALLOCATION_FLAG_ALWAYS_PREFETCH_MEMORY = 0x8, - - /** \brief Cache the request size. - - Allow internal data structures used for resource allocation to be cached in-memory. - */ - ALLOCATION_FLAG_ALWAYS_CACHE_SIZE = 0x10, - - /** \brief Requires heaps to be always attributed by D3D12_HEAP_TYPE. - - With cache-coherent UMA adapters, a single custom-equivelent heap will be used everywhere. - This enables better resource optimization during allocation. However, certain heap flags or - access-patterns may require or beneifit from D3D12_HEAP_TYPE. For example, - D3D12_HEAP_FLAG_SHARED requires D3D12_HEAP_TYPE_READBACK or D3D12_HEAP_TYPE_UPLOAD, - as well as frequent CPU reads would beneifit from D3D12_HEAP_TYPE_READBACK since the CPU - properties are always write-combined. - - If ALLOCATOR_FLAG_DISABLE_CUSTOM_HEAPS was specified, heap type was - D3D12_HEAP_TYPE_READBACK, or the adapter is not cache-coherent UMA, this flag has no effect. - */ - ALLOCATION_FLAG_ALWAYS_ATTRIBUTE_HEAPS = 0x20, - - /** \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. - - Mostly used for debug and testing when certain allocation methods unexpectedly fail. - */ - ALLOCATION_FLAG_NEVER_FALLBACK = 0x40, - }; - - DEFINE_ENUM_FLAG_OPERATORS(ALLOCATION_FLAGS) - - /** \struct ALLOCATION_FLAGS - Specifies how allocations should be created. - */ - struct ALLOCATION_DESC { - /** \brief Flags used to control how the resource will be allocated. - - Optional parameter. By default, GPGMM will decide automatically. - */ - ALLOCATION_FLAGS Flags; - - /** \brief Heap type that the resource to be allocated requires. - - It is recommended to not specifiy the heap type or equivalently specify - D3D12_HEAP_TYPE_CUSTOM. This enables better resource optimization for UMA adapters by using - a custom-equivelent upload heap everywhere. However, since UMA adapters use write-combined - memory for CPU writes, a heap type of D3D12_HEAP_TYPE_READBACK could have better - performance. - - Optional parameter. If the heap type is not provided or D3D12_HEAP_TYPE_CUSTOM, the heap - type will be inferred by using adapter properties and the initial resource state. - */ - D3D12_HEAP_TYPE HeapType; - - /** \brief Additional heap flags that the resource requires. - - By default, GPGMM infers the required heap flags based on the required - fields in the D3D12_RESOURCE_DESC, ALLOCATOR_DESC and ALLOCATION_DESC. - But if additional heap flags are required, they can also be specified. - - It is recommended to only specify D3D12_HEAP_FLAG_NONE since not all - allocation methods are guarenteed to be supported. - - Optional parameter. - */ - D3D12_HEAP_FLAGS ExtraRequiredHeapFlags; - - /** \brief Require additional bytes to be appended to the resource allocation. - - Resource heap size is guarenteed to increase by at-least this number of bytes. - Specifying a padding will disable committed resources and sub-allocated - heaps. - - Used to workaround driver bugs related to the heap size being insufficent for the resource. - - Optional parameter. No extra padding is applied by default. - */ - uint64_t RequireResourceHeapPadding; - - /** \brief Associates a name with the given allocation. - - Optional parameter. By default, no name is associated. - */ - LPCWSTR DebugName; - }; - - /** \struct FEATURE_DATA_RESOURCE_ALLOCATION_SUPPORT - - Details the resource allocator limitations, including if sharing resources between command - queues is coherent. - */ - struct FEATURE_DATA_RESOURCE_ALLOCATION_SUPPORT { - /** \brief Describes resource within coherency behavior between command-queues. - - For example, if two allocations belong to the same resource where each allocation is - referenced with a different command-queue, will accessing one stomp over the other. D3D12 - does not guarentee such behavior is safe but is it well-defined behavior based on the GPU - vendor. - */ - bool IsResourceAllocationWithinCoherent; - }; - - /** \enum ALLOCATOR_FEATURE - - Defines constants that specify a resource allocator feature to query about. When you - want to query for the level to which an allocator supports a feature, pass one of these values - to ResourceAllocator::CheckFeatureSupport. - */ - enum ALLOCATOR_FEATURE { - /** \brief Indicates a query for the level of support for allocated resources. The - corresponding data structure for this value is FEATURE_DATA_RESOURCE_ALLOCATION_SUPPORT - */ - ALLOCATOR_FEATURE_RESOURCE_ALLOCATION_SUPPORT, - }; - - using RESOURCE_ALLOCATOR_INFO = MemoryAllocatorInfo; - - /** \brief ResourceAllocator is a MemoryAllocator that creates ID3D12Resources in a - ResourceAllocation. - - Internally, ResourceAllocator creates a request, by determining the - resource allocation requirements, then finds a MemoryAllocator able to service the request. - - If the first MemoryAllocator attempt fails, it will try a second MemoryAllocator, and so on. - MemoryAllocator attempts are greedy: re-use of resources > re-use of heaps > - re-use by pools > no re-use, in order of maximizing performance while minimizing memory - footprint. - - ResourceAllocator also uses ResidencyManager to determine available memory - (or budget left) when creating the request. This is because residency is managed - per heap and not per resource). A larger Heap could be ideal for allocation but only if there is - budget. And similarly, a smaller Heap allows for finer grained residency but could increase - overall memory usage for allocation. - **/ - class GPGMM_EXPORT ResourceAllocator final : public MemoryAllocator, public IUnknownImpl { + class GPGMM_EXPORT ResourceAllocator final : public IUnknownImpl, + public IResourceAllocator, + public MemoryAllocator { public: - /** \brief Create a resource allocator with residency. - - Residency requires at-least DXGI version 1.4. - - @param allocatorDescriptor A reference to ALLOCATOR_DESC structure that describes the - allocator. - @param[out] ppResourceAllocatorOut Pointer to a memory block that recieves a pointer to the - resource allocator. Pass NULL to test if allocator creation would succeed, but not actually - create the allocator. - @param[out] ppResidencyManagerOut Pointer to a memory block that recieves a pointer to the - residency manager. If NULL is passed, the allocator will be created without using - residency. - */ static HRESULT CreateResourceAllocator(const ALLOCATOR_DESC& allocatorDescriptor, - ResourceAllocator** ppResourceAllocatorOut, - ResidencyManager** ppResidencyManagerOut = nullptr); - - /** \brief Create a resource allocator using a specified residency manager. - - @param allocatorDescriptor A reference to ALLOCATOR_DESC structure that describes the - allocator. - @param pResidencyManager Pointer to a memory block that recieves a pointer to the - residency manager. - @param[out] ppResourceAllocatorOut Pointer to a memory block that recieves a pointer to the - resource allocator. Pass NULL to test if allocator creation would succeed, but not actually - create the allocator. - */ + IResourceAllocator** ppResourceAllocatorOut, + IResidencyManager** ppResidencyManagerOut); + static HRESULT CreateResourceAllocator(const ALLOCATOR_DESC& allocatorDescriptor, - ResidencyManager* pResidencyManager, - ResourceAllocator** ppResourceAllocatorOut); + IResidencyManager* pResidencyManager, + IResourceAllocator** ppResourceAllocatorOut); ~ResourceAllocator() override; - /** \brief Allocates memory and creates a D3D12 resource using it. - - Returns a ResourceAllocation which represents a resource allocated at a specific - location in memory. The resource could be allocated within a resource heap, within the - resource itself, or seperately using it's own memory (resource heap). - - Unlike a D3D12 resource, a resource allocation can made resident. It is recommended but not - strictly required to use the D3D12 resource equivalent methods (ex. Map, Unmap) through the - returned ResourceAllocation. - - @param allocationDescriptor A reference to ALLOCATION_DESC structure that provides - properties for the resource allocation. - @param resourceDescriptor A reference to the D3D12_RESOURCE_DESC structure that describes - the resource. - @param initialResourceState The initial state of the resource, a bitwise OR'd combination of - D3D12_RESOURCE_STATES enumeration constants. - @param pClearValue A pointer tp D3D12_CLEAR_VALUE structure that describes the default value - for a clear color. - @param[out] ppResourceAllocationOut An optional pointer to a memory block that recieves the - required interface pointer to the created resource allocation object. - */ + // IResourceAllocator interface HRESULT CreateResource(const ALLOCATION_DESC& allocationDescriptor, const D3D12_RESOURCE_DESC& resourceDescriptor, D3D12_RESOURCE_STATES initialResourceState, const D3D12_CLEAR_VALUE* pClearValue, - ResourceAllocation** ppResourceAllocationOut); - - /** \brief Imports an existing D3D12 resource. - - Allows externally created D3D12 resources to be used as ResourceAllocations. - - Residency is not supported for imported resources. - - @param committedResource A COM managed pointer to a D3D12 committed resource. - @param[out] ppResourceAllocationOut Pointer to a memory block that recieves a pointer to the - resource allocation. Pass NULL to test if resource allocation creation would succeed, but - not actually create the resource allocation. If NULL is passed and resource allocation - creation would succeed, S_FALSE is returned. - */ + IResourceAllocation** ppResourceAllocationOut) override; HRESULT CreateResource(ComPtr committedResource, - ResourceAllocation** ppResourceAllocationOut); - - /** \brief Return free memory back to the OS. - - When pooling is enabled, the allocator will retain resource heaps in order to speed-up - subsequent resource allocation requests. These resource allocations count against the - app's memory usage and in general, will lead to increased memory usage by the overall - system. Apps should call ReleaseMemory() when going idle for a period of time since there is - a brief performance hit when the internal resource heaps get reallocated by the OS. - - @param bytesToRelease Amount of memory to release, in bytes. A value of UINT64_MAX - releases ALL memory held by the allocator. - - \return Amount of memory, in bytes, released. The released size might be smaller then - bytesToRelease if there was not enough memory or larger if releasable memory doesn't exactly - total up to the amount. - */ + IResourceAllocation** ppResourceAllocationOut) override; uint64_t ReleaseMemory(uint64_t bytesToRelease) override; - - /** \brief Return the current allocator usage. - - Returned info can be used to monitor memory usage per allocator. - For example, the amount of internal fragmentation is equal to UsedBlockUsage / - UsedMemoryUsage. Or the percent of recycled memory is equal to FreeMemoryUsage / - (UsedMemoryUsage + FreeMemoryUsage) * 100%. - - */ RESOURCE_ALLOCATOR_INFO GetInfo() const override; + HRESULT CheckFeatureSupport(ALLOCATOR_FEATURE feature, + void* pFeatureSupportData, + uint32_t featureSupportDataSize) const override; - /** \brief Identifies the allocator type. + DEFINE_IUNKNOWNIMPL_OVERRIDES() - The type is used for profiling and debugging purposes only. - */ const char* GetTypename() const override; - /** \brief Gets information about the features that are supported by the resource allocator. - - @param feature A constant from the ALLOCATOR_FEATURE enumeration describing the feature(s) - that you want to query for support. - @param pFeatureSupportData A pointer to the data structure that corresponds to the value of - the feature parameter. To determine the corresponding data structure for each constant, see - FEATURE. - @param featureSupportDataSize The sie of the structure pointed by the pFeatureSupportData - parameter. - - \return Returns S_OK if successful. Returns E_INVALIDARG if unsupported data type is passed - to pFeatureSupportData or if a size mismatch is detected for the featureSupportDataSize - parameter. - */ - HRESULT CheckFeatureSupport(ALLOCATOR_FEATURE feature, - void* pFeatureSupportData, - uint32_t featureSupportDataSize) const; - private: friend BufferAllocator; friend ResourceAllocation; @@ -617,10 +79,10 @@ namespace gpgmm::d3d12 { const D3D12_RESOURCE_DESC& resourceDescriptor, D3D12_RESOURCE_STATES initialResourceState, const D3D12_CLEAR_VALUE* clearValue, - ResourceAllocation** ppResourceAllocationOut); + IResourceAllocation** ppResourceAllocationOut); ResourceAllocator(const ALLOCATOR_DESC& descriptor, - ComPtr residencyManager, + IResidencyManager* pResidencyManager, std::unique_ptr caps); std::unique_ptr CreateResourceAllocator( @@ -652,7 +114,7 @@ namespace gpgmm::d3d12 { bool isPrefetchAllowed, std::unique_ptr underlyingAllocator); - HRESULT CreatePlacedResource(Heap* const resourceHeap, + HRESULT CreatePlacedResource(IHeap* const resourceHeap, uint64_t resourceOffset, const D3D12_RESOURCE_DESC* resourceDescriptor, const D3D12_CLEAR_VALUE* clearValue, @@ -666,7 +128,7 @@ namespace gpgmm::d3d12 { const D3D12_CLEAR_VALUE* clearValue, D3D12_RESOURCE_STATES initialResourceState, ID3D12Resource** commitedResourceOut, - Heap** resourceHeapOut); + IHeap** resourceHeapOut); static HRESULT ReportLiveDeviceObjects(ComPtr device); @@ -679,7 +141,7 @@ namespace gpgmm::d3d12 { RESOURCE_ALLOCATOR_INFO GetInfoInternal() const; ComPtr mDevice; - ComPtr mResidencyManager; + ComPtr mResidencyManager; std::unique_ptr mCaps; diff --git a/src/gpgmm/d3d12/ResourceHeapAllocatorD3D12.cpp b/src/gpgmm/d3d12/ResourceHeapAllocatorD3D12.cpp index 02828b76b..e78eb3cae 100644 --- a/src/gpgmm/d3d12/ResourceHeapAllocatorD3D12.cpp +++ b/src/gpgmm/d3d12/ResourceHeapAllocatorD3D12.cpp @@ -26,14 +26,14 @@ namespace gpgmm::d3d12 { - ResourceHeapAllocator::ResourceHeapAllocator(ResidencyManager* residencyManager, + ResourceHeapAllocator::ResourceHeapAllocator(IResidencyManager* residencyManager, ID3D12Device* device, D3D12_HEAP_PROPERTIES heapProperties, D3D12_HEAP_FLAGS heapFlags) : mResidencyManager(residencyManager), mDevice(device), mHeapProperties(heapProperties), - mHeapFlags(heapFlags){ + mHeapFlags(heapFlags) { } std::unique_ptr ResourceHeapAllocator::TryAllocateMemory( @@ -55,14 +55,15 @@ namespace gpgmm::d3d12 { resourceHeapDesc.DebugName = L"Resource heap"; resourceHeapDesc.Flags |= (mHeapFlags & D3D12_HEAP_FLAG_CREATE_NOT_RESIDENT) ? HEAPS_FLAG_NONE : HEAP_FLAG_ALWAYS_IN_BUDGET; - if (mResidencyManager != nullptr) { + ResidencyManager* residencyManager = static_cast(mResidencyManager); + if (residencyManager != nullptr) { resourceHeapDesc.MemorySegmentGroup = GetMemorySegmentGroup( - mHeapProperties.MemoryPoolPreference, mResidencyManager->IsUMA()); + mHeapProperties.MemoryPoolPreference, residencyManager->IsUMA()); } - Heap* resourceHeap = nullptr; + IHeap* resourceHeap = nullptr; if (FAILED(Heap::CreateHeap( - resourceHeapDesc, mResidencyManager, + resourceHeapDesc, residencyManager, [&](ID3D12Pageable** ppPageableOut) -> HRESULT { D3D12_HEAP_DESC heapDesc = {}; heapDesc.Properties = mHeapProperties; diff --git a/src/gpgmm/d3d12/ResourceHeapAllocatorD3D12.h b/src/gpgmm/d3d12/ResourceHeapAllocatorD3D12.h index 393e5e3d4..96cffcb3c 100644 --- a/src/gpgmm/d3d12/ResourceHeapAllocatorD3D12.h +++ b/src/gpgmm/d3d12/ResourceHeapAllocatorD3D12.h @@ -21,12 +21,12 @@ namespace gpgmm::d3d12 { - class ResidencyManager; + class IResidencyManager; // Wrapper to allocate a D3D12 heap for resources of any type. class ResourceHeapAllocator final : public MemoryAllocator { public: - ResourceHeapAllocator(ResidencyManager* residencyManager, + ResourceHeapAllocator(IResidencyManager* residencyManager, ID3D12Device* device, D3D12_HEAP_PROPERTIES heapProperties, D3D12_HEAP_FLAGS heapFlags); @@ -40,7 +40,7 @@ namespace gpgmm::d3d12 { const char* GetTypename() const override; private: - ResidencyManager* const mResidencyManager; + IResidencyManager* const mResidencyManager; ID3D12Device* const mDevice; const D3D12_HEAP_PROPERTIES mHeapProperties; const D3D12_HEAP_FLAGS mHeapFlags; diff --git a/src/gpgmm/vk/DeviceMemoryAllocatorVk.cpp b/src/gpgmm/vk/DeviceMemoryAllocatorVk.cpp index e2c27cd4c..40cf90e4e 100644 --- a/src/gpgmm/vk/DeviceMemoryAllocatorVk.cpp +++ b/src/gpgmm/vk/DeviceMemoryAllocatorVk.cpp @@ -74,7 +74,8 @@ namespace gpgmm::vk { void DeviceMemoryAllocator::DeallocateMemory(std::unique_ptr allocation) { TRACE_EVENT0(TraceEventCategory::kDefault, "DeviceMemoryAllocator.DeallocateMemory"); - VkDeviceMemory deviceMemory = ToBackend(allocation->GetMemory())->GetDeviceMemory(); + VkDeviceMemory deviceMemory = + static_cast(allocation->GetMemory())->GetDeviceMemory(); mResourceAllocator->GetFunctions().FreeMemory(mResourceAllocator->GetDevice(), deviceMemory, /*allocationCallbacks*/ nullptr); diff --git a/src/gpgmm/vk/DeviceMemoryVk.cpp b/src/gpgmm/vk/DeviceMemoryVk.cpp index 2da14a8f6..51183828f 100644 --- a/src/gpgmm/vk/DeviceMemoryVk.cpp +++ b/src/gpgmm/vk/DeviceMemoryVk.cpp @@ -31,4 +31,28 @@ namespace gpgmm::vk { return mMemoryTypeIndex; } + uint64_t DeviceMemory::GetSize() const { + return MemoryBase::GetSize(); + } + + uint64_t DeviceMemory::GetAlignment() const { + return MemoryBase::GetAlignment(); + } + + void DeviceMemory::AddSubAllocationRef() { + return MemoryBase::AddSubAllocationRef(); + } + + bool DeviceMemory::RemoveSubAllocationRef() { + return MemoryBase::RemoveSubAllocationRef(); + } + + IMemoryPool* DeviceMemory::GetPool() const { + return MemoryBase::GetPool(); + } + + void DeviceMemory::SetPool(IMemoryPool* pool) { + return MemoryBase::SetPool(pool); + } + } // namespace gpgmm::vk diff --git a/src/gpgmm/vk/DeviceMemoryVk.h b/src/gpgmm/vk/DeviceMemoryVk.h index 9101b852d..ab07a11cd 100644 --- a/src/gpgmm/vk/DeviceMemoryVk.h +++ b/src/gpgmm/vk/DeviceMemoryVk.h @@ -21,7 +21,7 @@ namespace gpgmm::vk { - class DeviceMemory final : public MemoryBase { + class DeviceMemory final : public MemoryBase, public IMemoryObject { public: DeviceMemory(VkDeviceMemory memory, uint32_t memoryTypeIndex, @@ -32,6 +32,14 @@ namespace gpgmm::vk { VkDeviceMemory GetDeviceMemory() const; uint32_t GetMemoryTypeIndex() const; + // IMemoryObject + uint64_t GetSize() const override; + uint64_t GetAlignment() const override; + void AddSubAllocationRef() override; + bool RemoveSubAllocationRef() override; + IMemoryPool* GetPool() const override; + void SetPool(IMemoryPool* pool) override; + private: VkDeviceMemory mMemory = VK_NULL_HANDLE; uint32_t mMemoryTypeIndex = 0; diff --git a/src/gpgmm/vk/ResourceAllocatorVk.cpp b/src/gpgmm/vk/ResourceAllocatorVk.cpp index c88f7f425..1fedb1219 100644 --- a/src/gpgmm/vk/ResourceAllocatorVk.cpp +++ b/src/gpgmm/vk/ResourceAllocatorVk.cpp @@ -77,7 +77,8 @@ namespace gpgmm::vk { // Associate memory with the buffer. result = allocator->GetFunctions().BindBufferMemory( - allocator->GetDevice(), buffer, ToBackend(allocation->GetMemory())->GetDeviceMemory(), + allocator->GetDevice(), buffer, + static_cast(allocation->GetMemory())->GetDeviceMemory(), allocation->GetOffset()); if (result != VK_SUCCESS) { allocator->GetFunctions().DestroyBuffer(allocator->GetDevice(), buffer, @@ -145,7 +146,8 @@ namespace gpgmm::vk { // Associate memory with the buffer. result = allocator->GetFunctions().BindImageMemory( - allocator->GetDevice(), image, ToBackend(allocation->GetMemory())->GetDeviceMemory(), + allocator->GetDevice(), image, + static_cast(allocation->GetMemory())->GetDeviceMemory(), allocation->GetOffset()); if (result != VK_SUCCESS) { allocator->GetFunctions().DestroyImage(allocator->GetDevice(), image, diff --git a/src/gpgmm/vk/ResourceAllocatorVk.h b/src/gpgmm/vk/ResourceAllocatorVk.h index d8c4f5f69..07ea17515 100644 --- a/src/gpgmm/vk/ResourceAllocatorVk.h +++ b/src/gpgmm/vk/ResourceAllocatorVk.h @@ -18,341 +18,18 @@ #include "gpgmm/common/MemoryAllocator.h" #include "gpgmm/vk/FunctionsVk.h" #include "include/gpgmm_export.h" +#include "include/gpgmm_vk.h" #include -namespace gpgmm { - class MemoryAllocation; -} // namespace gpgmm - namespace gpgmm::vk { - /** \struct GpResourceAllocator - \brief Opaque handle to a allocator object. - */ - VK_DEFINE_HANDLE(GpResourceAllocator) - - /** \struct GpResourceAllocation - \brief Opaque handle to a resource allocation object. - */ - VK_DEFINE_HANDLE(GpResourceAllocation) - - /** \enum GpAllocatorCreateFlags - \brief Configures how allocators should be created. - */ - enum GpAllocatorCreateFlags { - /** \brief Disables all allocator flags. - */ - GP_ALLOCATOR_CREATE_NONE = 0x0, - - /** \brief Disables pre-fetching of GPU memory. - - Should be only used for debugging and testing purposes. - */ - GP_ALLOCATOR_CREATE_DISABLE_MEMORY_PREFETCH = 0x4, - - /** \brief Tell GPGMM to allocate exactly what is needed, and to de-allocate - memory immediately once no longer needed (instead of re-using it). - - This is very slow and not recommended for general use but may be useful for running with the - minimal possible GPU memory footprint or debugging OOM failures. - */ - GP_ALLOCATOR_CREATE_ALWAYS_ON_DEMAND = 0x8, - - /** \brief Creates resource within budget. - - Requires the device extension VK_EXT_memory_budget to be supported before use. - The instance specified in GpAllocatorCreateInfo is also required to support - VK_KHR_get_physical_device_properties2. - */ - GP_ALLOCATOR_CREATE_ALWAYS_IN_BUDGET = 0x10, - }; - - /** \enum GpAllocatorAlgorithm - Specify the algorithms used for allocation. - */ - enum GpAllocatorAlgorithm { - /** \brief Use default allocation mechanism. - - Relies on internal heuristics to automatically determine the best allocation mechanism. The - selection of algorithm depends on: - - 1. The memory properties or flags specified by the user. - 2. The size the resource being created. - 3. The amount of available memory. - - In general, the most-efficent resource allocator will be attempted first (efficent - being defined as fastest service-time to allocate/deallocate with smallest memory - footprint), subject to other constraints. However, since it's impossible to predict all - future memory accesses, allocation techniques that rely on amortization of GPU heaps may not - prove to be faster as expected. Further experimentation is recommended. - */ - GP_ALLOCATOR_ALGORITHM_DEFAULT = 0, - - /** \brief Use the slab allocation mechanism. - - Slab allocation allocates/deallocates in O(1) time using O(N * pageSize) space. - - Slab allocation does not suffer from internal fragmentation but could externally fragment - when many unique request sizes are used. - */ - GP_ALLOCATOR_ALGORITHM_SLAB = 1, - - /** \brief Use the buddy system mechanism. - - Buddy system allocate/deallocates in O(Log2) time using O(1) space. - - Buddy system suffers from internal fragmentation (ie. resources are not a power-of-two) but - does not suffer from external fragmentation as much since the device memory size does not - change. - - It is recommend to specify a preferredDeviceMemorySize large enough such that multiple - requests can fit within the specified preferredDeviceMemorySize but not too large where - creating the larger device memory becomes a bigger bottleneck. - */ - GP_ALLOCATOR_ALGORITHM_BUDDY_SYSTEM = 2, - - /** \brief Recycles device memory of a size being specified. - - Fixed pools allocate/deallocate in O(1) time using O(N) space. - - Fixed-size pool limits recycling to device memorys equal to - preferredDeviceMemorySize. A preferredDeviceMemorySize of zero is effectively - equivelent to ALLOCATOR_FLAG_ALWAYS_ON_DEMAND. - */ - GP_ALLOCATOR_ALGORITHM_FIXED_POOL = 3, - - /** \brief Recycles device memory of any size using multiple pools. - - Segmented pool allocate/deallocates in O(Log2) time using O(N * K) space. - */ - GP_ALLOCATOR_ALGORITHM_SEGMENTED_POOL = 4, - }; - - /** \struct GpAllocatorCreateInfo - \brief Used to create allocator. - */ - struct GpAllocatorCreateInfo { - /** \brief Function pointer to Vulkan functions. - - There are 3 ways to specify Vulkan functions. - 1. Specify `gpgmm_vk_static_functions = true` and statically link agaisn't the Vulkan - loader provided by GPGMM. - 2. Load Vulkan functions dynamically by specifying `gpgmm_vk_static_functions = false` and - ONLY provide the instance and device functions, `vkGetInstanceProcAddr` and - `vkGetDeviceProcAddr`. GPGMM will use those to load the remaining. - 3. Specify ALL the Vulkan functions. GPGMM will not import or load Vulkan function itself. - */ - const VulkanFunctions* pVulkanFunctions = nullptr; - - /** \brief Handle to Vulkan physical device object. - */ - VkPhysicalDevice physicalDevice; - - /** \brief Handle to Vulkan device object. - */ - VkDevice device; - - /** \brief Handle to Vulkan instance object. - */ - VkInstance instance; - - /** \brief Vulkan version return by VK_MAKE_VERSION. - */ - uint32_t vulkanApiVersion; - - /** \brief Flags used to configure allocator. - */ - GpAllocatorCreateFlags flags; - - /** \brief Specifies the algorithm to use for sub-allocation. - - Used to evaluate how allocation implementations perform with various algorithms that - sub-divide devie memory. - - Optional parameter. By default, the slab allocator is used. - */ - GpAllocatorAlgorithm subAllocationAlgorithm = GP_ALLOCATOR_ALGORITHM_SLAB; - - /** \brief Specifies the algorithm to use for device memory pooling. - - Used to evaluate how allocation implementations perform with various algorithms that - sub-divide device memorys. - - Optional parameter. By default, the slab allocator is used. - */ - GpAllocatorAlgorithm poolAlgorithm = GP_ALLOCATOR_ALGORITHM_SEGMENTED_POOL; - - /** \brief Specifies the preferred size of device memory. - - The preferred size of the device memory is the minimum memory size to sub-allocate from. - A larger device memory consumes more memory but could be faster for sub-allocation. - - Optional parameter. When 0 is specified, the API will automatically set the preferred - device memory size to be a multiple of minimum device memory size allowed by Vulkan. - */ - uint64_t preferredDeviceMemorySize; - - /** \brief Memory fragmentation limit, expressed as a percentage of the device memory size, - that is acceptable to be wasted due to fragmentation. - - Fragmentation occurs when the allocation is larger then the resource size. - This occurs when the type of resource (buffer or texture) and allocator have different - alignment requirements. For example, a 192KB resource may need to allocate 256KB of - allocated space, which is equivalent to a fragmentation limit of 33%. - - When |preferredDeviceMemorySize| is non-zero, the memoryFragmentationLimit could be - exceeded. Also, the memoryFragmentationLimit should never be zero, as some fragmentation - can occur. - - Optional parameter. When 0 is specified, the default fragmentation limit is 1/8th the - device memory size. - */ - double memoryFragmentationLimit; - - /** \brief Memory growth factor, expressed as a multipler of the device memory size - that will monotonically increase. - - A factor value of 1.0 specifies no growth, where the device memory size is always determined - by other limits or constraints. If no factor gets specified (or a value less than 1 is - specified), GPGMM will allocate a device memory size with enough space to fit exactly one - resource. - - Memory growth avoids the need to specify |preferredDeviceMemorySize|, which - especially helps in situations where the resource size cannot be predicated (eg. - user-defined), by allowing the device memory size to gradually increase in size - per demand to achieve a balance of memory usage and performance. - - Optional parameter. When 0 is specified, the default of 1.25 is used (or 25% growth). - */ - double memoryGrowthFactor; - }; - - /** \enum GpResourceAllocationCreateFlags - Additional controls that modify allocations. - */ - enum GpResourceAllocationCreateFlags { - - /** \brief Disables all allocation flags. - - Enabled by default. - */ - GP_ALLOCATION_CREATE_NONE = 0x0, - - /** \brief Disallow creating new device memory when creating a resource. - - Forbids creating new device memory when creating a resource. The created resource - must use existing device memory or error. Effectively disables creating - standalone allocations whose memory cannot be reused. - */ - GP_ALLOCATION_CREATE_NEVER_ALLOCATE_MEMORY = 0x1, - - /** \brief Disallow creating multiple resource allocations from the same device memory. - - The created resource will always be allocated with it's own device memory. - */ - GP_ALLOCATION_CREATE_NEVER_SUBALLOCATE_MEMORY = 0x4, - - /** \brief Prefetch memory for the next resource allocation. - - The call to prefetch is deferred to a seperate background thread by GPGMM which runs - when the current allocation requested is completed. By default, GPGMM will automatically - trigger prefetching based on heurstics. Prefetching enables more performance when - allocating for large contiguous allocations. Should not be used with - ALLOCATION_FLAG_NEVER_ALLOCATE_MEMORY. - */ - GP_ALLOCATION_CREATE_ALWAYS_PREFETCH_MEMORY = 0x8, - }; - - /** \struct GpResourceAllocationCreateInfo - Specifies how allocations should be created. - */ - struct GpResourceAllocationCreateInfo { - /** \brief Flags used to control how the resource will be allocated. - */ - GpResourceAllocationCreateFlags flags; - - /** \brief Bitmask to specify required memory properties for the allocation. - */ - VkMemoryPropertyFlags requiredPropertyFlags; - }; - - /** \brief Create allocator used to create and manage video memory for the App specified device - and instance. - - @param info A reference to GpAllocatorCreateInfo structure that describes the allocator. - @param[out] allocatorOut Pointer to a memory block that recieves a pointer to the - resource allocator. Pass NULL to test if allocator creation would succeed, but not actually - create the allocator. If NULL is passed and allocator creating would succeed, VK_INCOMPLETE is - returned. - */ - GPGMM_EXPORT VkResult gpCreateResourceAllocator(const GpAllocatorCreateInfo& info, - GpResourceAllocator* allocatorOut); - - /** \brief Destroy allocator. - - @param allocator A GpResourceAllocator to destroy. - */ - GPGMM_EXPORT void gpDestroyResourceAllocator(GpResourceAllocator allocator); - - /** \brief Create a buffer allocation. - - @param allocator A GpResourceAllocator used to create the buffer and allocation. - @param pBufferCreateInfo A pointer to a VkBufferCreateInfo that describes the buffer to create. - @param pBuffer A pointer to a VkBuffer that will be created using the allocation. - @param pAllocationCreateInfo A pointer to a GpResourceAllocationCreateInfo that describes the - allocation. - @param[out] allocationOut A pointer to GpResourceAllocation that represents the buffer - allocation. - */ - GPGMM_EXPORT VkResult - gpCreateBuffer(GpResourceAllocator allocator, - const VkBufferCreateInfo* pBufferCreateInfo, - VkBuffer* pBuffer, - const GpResourceAllocationCreateInfo* pAllocationCreateInfo, - GpResourceAllocation* allocationOut); - - /** \brief Destroy buffer allocation. - - @param allocator A GpResourceAllocator used to create the buffer and allocation. - @param buffer A VkBuffer that was also created by the allocator. - @param allocation A GpResourceAllocation that was created by the allocator. - */ - GPGMM_EXPORT void gpDestroyBuffer(GpResourceAllocator allocator, - VkBuffer buffer, - GpResourceAllocation allocation); - - /** \brief Create a image allocation. - - @param allocator A GpResourceAllocator used to create the image and allocation. - @param pImageCreateInfo A pointer to a VkImageCreateInfo that describes the image to create. - @param pImage A pointer to a VkImage that will be created using the allocation. - @param pAllocationCreateInfo A pointer to a GpResourceAllocationCreateInfo that describes the - allocation. - @param[out] allocationOut A pointer to GpResourceAllocation that represents the image - allocation. - */ - GPGMM_EXPORT VkResult gpCreateImage(GpResourceAllocator allocator, - const VkImageCreateInfo* pImageCreateInfo, - VkImage* pImage, - const GpResourceAllocationCreateInfo* pAllocationCreateInfo, - GpResourceAllocation* allocationOut); - - /** \brief Destroy image allocation. - - @param allocator A GpResourceAllocator used to create the image and allocation. - @param image A VkImage that was also created by the allocator. - @param allocation A GpResourceAllocation that was created by the allocator. - */ - GPGMM_EXPORT void gpDestroyImage(GpResourceAllocator allocator, - VkImage image, - GpResourceAllocation allocation); - struct GpResourceAllocation_T final : public MemoryAllocation { GpResourceAllocation_T(const MemoryAllocation& allocation); }; class Caps; + struct GpResourceAllocator_T { public: static VkResult CreateResourceAllocator(const GpAllocatorCreateInfo& info, diff --git a/src/include/gpgmm.h b/src/include/gpgmm.h new file mode 100644 index 000000000..c4b63cf84 --- /dev/null +++ b/src/include/gpgmm.h @@ -0,0 +1,136 @@ +// Copyright 2022 The GPGMM Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef INCLUDE_GPGMM_H_ +#define INCLUDE_GPGMM_H_ + +#include + +namespace gpgmm { + + /** \brief Stores a collection of memory allocations. + */ + class IMemoryPool {}; + + class IMemoryObject { + public: + virtual ~IMemoryObject() = default; + + /** \brief Return the size of the memory object. + + \return Size, in bytes, of the memory object. + */ + virtual uint64_t GetSize() const = 0; + + /** \brief Return the alignment of the memory object. + + \return Alignment, in bytes, of the memory object. + */ + virtual uint64_t GetAlignment() const = 0; + + /** \brief Increments the sub-allocation reference count on the heap. + */ + virtual void AddSubAllocationRef() = 0; + + /** \brief Decrements the sub-allocation reference count on the heap. + */ + virtual bool RemoveSubAllocationRef() = 0; + + /** \brief Get the pool this memory is contained in. + */ + virtual IMemoryPool* GetPool() const = 0; + + /** \brief Assign the pool this memory is contained in. + */ + virtual void SetPool(IMemoryPool* pool) = 0; + }; + + /** \struct MemoryAllocatorInfo + Information about the memory allocator. + */ + struct MemoryAllocatorInfo { + /** \brief Number of used sub-allocated blocks within the same memory. + */ + uint32_t UsedBlockCount; + + /** \brief Total size, in bytes, of used sub-allocated blocks. + */ + uint64_t UsedBlockUsage; + + /** \brief Number of used memory allocations. + */ + uint32_t UsedMemoryCount; + + /** \brief Total size, in bytes, of used memory. + */ + uint64_t UsedMemoryUsage; + + /** \brief Total size, in bytes, of free memory. + */ + uint64_t FreeMemoryUsage; + + /** \brief Cache misses not eliminated by prefetching. + */ + uint64_t PrefetchedMemoryMisses; + + /** \brief Cache misses eliminated because of prefetching. + */ + uint64_t PrefetchedMemoryMissesEliminated; + + /** \brief Requested size was NOT cached. + */ + uint64_t SizeCacheMisses; + + /** \brief Requested size was cached. + */ + uint64_t SizeCacheHits; + + /** \brief Adds or sums together two infos. + */ + MemoryAllocatorInfo& operator+=(const MemoryAllocatorInfo& rhs); + }; + + /** \enum AllocationMethod + Represents how memory was allocated. + */ + enum class AllocationMethod { + + /** \brief Not yet allocated or invalid. + + This is an invalid state that assigned temporary before the actual method is known. + */ + kUndefined = 0, + + /** \brief Not sub-divided. + + One and only one allocation exists for the memory. + */ + kStandalone = 1, + + /** \brief Sub-divided using one or more allocations. + + Underlying memory will be broken up into one or more memory allocations. + */ + kSubAllocated = 2, + + /** \brief Sub-divided within a single memory allocation. + + A single memory allocation will be broken into one or more sub-allocations. + */ + kSubAllocatedWithin = 3, + }; + +} // namespace gpgmm + +#endif // INCLUDE_GPGMM_H_ diff --git a/src/include/gpgmm_d3d12.h b/src/include/gpgmm_d3d12.h index 6742b6421..4bd5aad31 100644 --- a/src/include/gpgmm_d3d12.h +++ b/src/include/gpgmm_d3d12.h @@ -15,10 +15,1217 @@ #ifndef INCLUDE_GPGMM_D3D12_H_ #define INCLUDE_GPGMM_D3D12_H_ -#include "gpgmm/d3d12/HeapD3D12.h" -#include "gpgmm/d3d12/ResidencyListD3D12.h" -#include "gpgmm/d3d12/ResidencyManagerD3D12.h" -#include "gpgmm/d3d12/ResourceAllocationD3D12.h" -#include "gpgmm/d3d12/ResourceAllocatorD3D12.h" +// User should decide to define the following macros: +// - GPGMM_D3D12_HEADERS_ALREADY_INCLUDED: D3D12 platform headers will be already included before +// this header and does not need to be re-included. +// - GPGMM_WINDOWS_HEADERS_ALREADY_INCLUDED: Windows.h will be already included before this header +// and does not need to be re-included. + +#ifndef GPGMM_D3D12_HEADERS_ALREADY_INCLUDED +# include +# include +# include +#endif + +#ifndef GPGMM_WINDOWS_HEADERS_ALREADY_INCLUDED +# include // for DEFINE_ENUM_FLAG_OPERATORS +#endif + +#include + +#include "gpgmm.h" +#include "gpgmm_export.h" + +namespace gpgmm::d3d12 { + + /** \brief Debug object associates additional information for D3D objects using SetPrivateData. + + Since a single D3D object could be re-used by one or more GPGMM objects, debug information must + be stored and retrieved seperately. + */ + class IDebugObject : public IUnknown { + public: + /** \brief Get the debug name. + + \return A NULL-terminated UNICODE string that contains the name to associate with the debug + object. + */ + virtual LPCWSTR GetDebugName() const = 0; + + /** \brief Associate a debug name. + + @param Name A NULL-terminated UNICODE string that contains the name to associate with the + debug object. + */ + virtual HRESULT SetDebugName(LPCWSTR Name) = 0; + }; + + /** \enum RESIDENCY_STATUS + + D3D12 allows heaps to be explicitly created resident or not. This means the expected + residency status of the heap cannot be solely determined by checking for the existence in a + residency cache. + + Heaps are in one of three exclusive states: never made resident or unknown, about to become + resident or pending residency, and currently resident. When a heap gets evicted or paged-out, + it transitions from currently resident to pending residency. Paged-in is the reverse of this, + pending residency to currently resident. If the heap was known to be created resident by + D3D12, it will immediately become currently resident. If the heap becomes locked, it will + stay currently resident until it is evicted, then back to pending residency. + */ + enum RESIDENCY_STATUS { + /** \brief Heap residency status is not known and cannot be made resident. + Heap must become locked to be managed for residency. + */ + RESIDENCY_STATUS_UNKNOWN = 0, + + /** \brief Heap is about to be made resident. + Heap must be previously locked, evicted, or currently resident at creation. + */ + RESIDENCY_STATUS_PENDING_RESIDENCY = 1, + + /** \brief Heap was made resident and can be evicted. + Heaps that stay locked will always be currently resident. + */ + RESIDENCY_STATUS_CURRENT_RESIDENT = 2, + }; + + /** \struct HEAP_INFO + Additional information about the heap. + */ + struct HEAP_INFO { + /** \brief Check if the heap currently locked for residency. + */ + bool IsLocked; + + /** \brief Check if the heap was made resident or not. + */ + RESIDENCY_STATUS Status; + }; + + /** \enum HEAPS_FLAGS + Specify creation options to configure the heap. + */ + enum HEAPS_FLAGS { + + /** \brief Disables all option flags. + */ + HEAPS_FLAG_NONE = 0x0, + + /** \brief Requires the heap to be created in budget. + */ + HEAP_FLAG_ALWAYS_IN_BUDGET = 0x1, + }; + + DEFINE_ENUM_FLAG_OPERATORS(HEAPS_FLAGS) + + /** \struct HEAP_DESC + Specifies creation options for a residency managed heap. + */ + struct HEAP_DESC { + /** \brief Created size of the heap, in bytes. + + Must be non-zero. SizeInBytes is always a multiple of the alignment. + */ + uint64_t SizeInBytes; + + /** \brief Created alignment of the heap, in bytes. + + Must be non-zero. + */ + uint64_t Alignment; + + /** \brief Specifies heaps options. + */ + HEAPS_FLAGS Flags; + + /** \brief Specifies the memory segment to use for residency. + + Allows any heap to specify a segment which does not have a attributed heap type. + */ + DXGI_MEMORY_SEGMENT_GROUP MemorySegmentGroup; + + /** \brief Debug name associated with the heap. + */ + LPCWSTR DebugName; + }; + + /** \brief Callback function used to create a ID3D12Pageable. + + For example, to create a ID3D12Heap: + + \code + auto callback = [heapDesc](ID3D12Pageable** ppPageableOut) -> HRESULT { + ComPtr heap; + ReturnIfFailed(mDevice->CreateHeap(&heapDesc, IID_PPV_ARGS(&heap))); + *ppPageableOut = heap.Detach(); + }; + \endcode + */ + using CreateHeapFn = std::function; + + class IResidencyManager; + + /** \brief Heap is used to represent managed ID3D12Heap or ID3D12Resource that has an implicit + heap (owned by D3D) for a committed resource, in the ResidencyManager's residency cache. + + 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. + */ + class IHeap : public IDebugObject, public IMemoryObject { + public: + virtual HEAP_INFO GetInfo() const = 0; + virtual bool IsInResidencyLRUCacheForTesting() const = 0; + virtual bool IsResidencyLockedForTesting() const = 0; + }; + + /** \brief Create a heap managed by GPGMM. + + Unlike a normal D3D12 heap, a heap managed by GPGMM means it will be tracked 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. + + @param descriptor A reference to HEAP_DESC structure that describes the heap. + @param pResidencyManager A pointer to the ResidencyManager used to manage this heap. + @param createHeapFn A callback function which creates a ID3D12Pageable derived type. + @param[out] ppHeapOut Pointer to a memory block that recieves a pointer to the + heap. + */ + GPGMM_EXPORT HRESULT CreateHeap(const HEAP_DESC& descriptor, + IResidencyManager* const pResidencyManager, + CreateHeapFn&& createHeapFn, + IHeap** ppHeapOut); + + /** \brief Represents a list of heaps which will be "made resident" upon executing a + command-list. + + A residency list helps track heaps for residency which will be referenced together by a + command-list. The application uses a ResidencyList by inserting heaps, by calling + ResourceAllocation::GetMemory, into the list. Once ResidencyManager::ExecuteCommandLists is + called, the list can be reset or cleared for the next frame or compute job. + + Without ResidencyList, the application would need to call ResidencyManager::LockHeap and + ResidencyManager::UnlockHeap for each heap before and after every GPU command or command-list + being executed. + */ + class IResidencyList : public IUnknown { + public: + /** \brief Adds a heap to the residency list. + + @param pHeap A pointer to Heap about to be added. + + \return S_OK if heap was added, else error. + */ + virtual HRESULT Add(IHeap* pHeap) = 0; + + virtual HRESULT Reset() = 0; + }; + + /** \brief Create a residency list or collection of heaps to manage together for residency. + */ + GPGMM_EXPORT HRESULT CreateResidencyList(IResidencyList** ppResidencyList); + + /** \enum EVENT_RECORD_FLAGS + Represents different event categories to record. + */ + enum EVENT_RECORD_FLAGS { + + /** \brief Record nothing. + */ + EVENT_RECORD_FLAG_NONE = 0x0, + + /** \brief Record lifetimes of API objects created by GPGMM. + */ + EVENT_RECORD_FLAG_API_OBJECTS = 0x1, + + /** \brief Record API calls made to GPGMM. + */ + EVENT_RECORD_FLAG_API_CALLS = 0x2, + + /** \brief Record duration of GPGMM API calls. + */ + EVENT_RECORD_FLAG_API_TIMINGS = 0x4, + + /** \brief Record metrics made to GPGMM API calls. + */ + EVENT_RECORD_FLAG_COUNTERS = 0x8, + + /** \brief Record events required for playback. + + Bitwise OR'd combination of EVENT_RECORD_FLAG_API_OBJECTS and + EVENT_RECORD_FLAG_API_CALLS. + */ + EVENT_RECORD_FLAG_CAPTURE = 0x3, + + /** \brief Record everything. + */ + EVENT_RECORD_FLAG_ALL_EVENTS = 0xFF, + }; + + DEFINE_ENUM_FLAG_OPERATORS(EVENT_RECORD_FLAGS) + + /** \enum EVENT_RECORD_SCOPE + Represents recording scopes to limit event recording. + */ + enum EVENT_RECORD_SCOPE { + + /** \brief Scopes events per process (or multiple instances). + */ + EVENT_RECORD_SCOPE_PER_PROCESS = 0, + + /** \brief Scopes events per instance. + */ + EVENT_RECORD_SCOPE_PER_INSTANCE = 1, + }; + + /** \struct EVENT_RECORD_OPTIONS + Represents additional controls for recording. + */ + struct EVENT_RECORD_OPTIONS { + /** \brief Flags used to decide what to record. + + Optional parameter. By default, nothing is recorded. + */ + EVENT_RECORD_FLAGS Flags; + + /** \brief Minimum severity level to record messages. + + Messages with lower severity will be ignored. + + Optional parameter. By default, the minimum severity level is WARN. + */ + D3D12_MESSAGE_SEVERITY MinMessageLevel; + + /** \brief Specifies the scope of the events. + + Optional parameter. By default, recording is per process. + */ + EVENT_RECORD_SCOPE EventScope; + + /** \brief Record detailed timing events. + + Optional parameter. By default, detailed timing events are disabled. + */ + bool UseDetailedTimingEvents; + + /** \brief Path to trace file. + + Optional parameter. By default, a trace file is created for you. + */ + const char* TraceFile; + }; + + /** \enum RESIDENCY_FLAGS + Specify options to configure the residency manager. + */ + enum RESIDENCY_FLAGS { + + /** \brief Disables all option flags. + */ + RESIDENCY_FLAG_NONE = 0x0, + + /** \brief Disables automatic background memory budget updates by OS notifications. + + By default, memory budget updates will be pushed by the OS using a background thread. If + the OS does not support push notifications or budget updates are not frequent enough, this + mechanism can be disabled where a pull-based method is used instead. + */ + RESIDENCY_FLAG_NEVER_UPDATE_BUDGET_ON_WORKER_THREAD = 0x1, + }; + + DEFINE_ENUM_FLAG_OPERATORS(RESIDENCY_FLAGS) + + /** \struct RESIDENCY_DESC + Specify parameters when creating a residency manager. + */ + struct RESIDENCY_DESC { + /** \brief Specifies the device used by this residency manager. + Required parameter. Use CreateDevice get the device. + */ + Microsoft::WRL::ComPtr Device; + + /** \brief Specifies the adapter used by this residency manager. + + Requires DXGI 1.4 due to IDXGIAdapter3::QueryVideoMemoryInfo. + + Required parameter. Use EnumAdapters to get the adapter. + */ + Microsoft::WRL::ComPtr Adapter; + + /** \brief Specifies if unified memory architecture (UMA) is enabled. + + When UMA is enabled, the residency manager will budget using a single memory segment. + Else, when UMA is false, the residency manager will have two budgets for local and non-local + memory segments, respectively. If IsUMA is false and the adapter is discrete, this will + effectively double the amount of memory bandwidth. If IsUMA is true and the adapter is UMA, + using a single budget can reduce residency and memory overhead. + + Required parameter. Use CheckFeatureSupport to determine if supported. + */ + bool IsUMA; + + /** \brief Specifies residency options. + */ + RESIDENCY_FLAGS Flags; + + /** \brief Minimum severity level to log messages to console. + + Messages with lower severity will be ignored. + + Optional parameter. By default, will log only corruption messages. + */ + D3D12_MESSAGE_SEVERITY MinLogLevel; + + /** \brief Specifies recording options. + + For example, what events to record, and where to record them. + + Optional parameter. By default, no options are specified for recording. + */ + EVENT_RECORD_OPTIONS RecordOptions; + + /** \brief Maximum amount of budgeted memory, expressed as a percentage of video memory, + that can be budgeted. + + If a non-zero MaxBudgetInBytes is specified, MaxPctOfVideoMemoryToBudget is ignored. + + Optional parameter. By default, the API will automatically set the budget to 95% of video + memory, leaving 5% for the OS and other applications. + */ + float MaxPctOfVideoMemoryToBudget; + + /** \brief Lowest amount of budgeted memory, expressed as a percentage, that can be + reserved. + + If SetVideoMemoryReservation is used a set a reservation larger then the budget, this amount + is used instead so the application can make forward progress. + + Optional parameter. By default, the API restricts the residency manager reservation to never + go below 50% of the budget. + */ + float MinPctOfBudgetToReserve; + + /** \brief Maximum amount of budgeted memory, in bytes, that can be budgeted. + + Allows a fixed budget to be artifically set for testing purposes. + + Optional parameter. By default, the API will not further restrict the residency manager + budget. + */ + uint64_t MaxBudgetInBytes; + + /** \brief Size of memory, in bytes, to evict from residency at once, + should there not be enough budget left. + + Optional parameter. When 0 is specified, the API will use a evict size of 50MB. + */ + uint64_t EvictSizeInBytes; + + /** \brief Initial fence value to use when managing heaps for residency. + + Fence value gets assigned to each managed heap and increments each time ExecuteCommandList() + is called. When over budget, these fence values are compared to determine which heaps can be + evicted. + + Optional parameter. Zero by default. + */ + uint64_t InitialFenceValue; + }; + + /** \struct RESIDENCY_INFO + Additional information about the residency manager. + */ + struct RESIDENCY_INFO { + /** \brief Amount of memory, in bytes, currently resident. + */ + uint64_t CurrentMemoryUsage; + + /** \brief Number of heaps, currently resident. + */ + uint64_t CurrentMemoryCount; + }; + + /** \brief ResidencyManager tracks and maintains one or more Heap within a residency cache. + + A Heap is considered "resident" when it is accessible by the GPU. A Heap can be made explicitly + resident by calling ResidencyManager::LockHeap or implicitly resident by using the Heap with a + ResidencyList upon calling ResidencyManager::ExecuteCommandLists or through a + operation that always requires the Heap to be resident (eg. Map, Unmap). + + Internally, the ResidencyManager keeps the application in-budget by calling ID3D12Device::Evict + and ID3D12Device::MakeResident to page-out or page-in heaps, respectively. + **/ + class IResidencyManager : public IUnknown { + public: + /** \brief Locks the specified heap. + + Locking a heap means the residency manager will never evict it when over budget. + + @param pHeap A pointer to the heap being locked. + */ + virtual HRESULT LockHeap(IHeap* pHeap) = 0; + + /** \brief Unlocks the specified heap. + + Unlocking a heap allows the residency manager will evict it when over budget. + + @param pHeap A pointer to the heap being unlocked. + */ + virtual HRESULT UnlockHeap(IHeap* pHeap) = 0; + + /** \brief Execute command lists using residency managed heaps. + + Submits an array of command lists and residency lists for the specified command queue. + + @param pQueue The command queue to submit to. + @param ppCommandLists The array of ID3D12CommandList command lists to be executed. + @param ppResidencyLists The array of ResidencyList residency lists to make resident. + @param count The size of commandLists and residencyLists arrays. + */ + virtual HRESULT ExecuteCommandLists(ID3D12CommandQueue* pQueue, + ID3D12CommandList* const* ppCommandLists, + IResidencyList* const* ppResidencyLists, + uint32_t count) = 0; + + /** \brief Sets video memory reservation. + + A reservation is the lowest amount of physical memory the application need to continue + operation safely. + + @param memorySegmentGroup Memory segment to reserve. + @param availableForReservation Amount of memory to reserve, in bytes. + @param[out] pCurrentReservationOut the amount of memory reserved, which may be less then the + |reservation| when under video memory pressure. A value of nullptr will update but not + return the current reservation. + */ + virtual HRESULT SetVideoMemoryReservation( + const DXGI_MEMORY_SEGMENT_GROUP& memorySegmentGroup, + uint64_t availableForReservation, + uint64_t* pCurrentReservationOut = nullptr) = 0; + + /** \brief Get the current budget and memory usage. + + @param memorySegmentGroup Memory segment to retrieve info from. + @param[out] pVideoMemoryInfoOut Pointer to DXGI_QUERY_VIDEO_MEMORY_INFO to populate. A value + of nullptr will update but not return the current info. + */ + virtual HRESULT QueryVideoMemoryInfo(const DXGI_MEMORY_SEGMENT_GROUP& memorySegmentGroup, + DXGI_QUERY_VIDEO_MEMORY_INFO* pVideoMemoryInfoOut) = 0; + + /** \brief Return the current residency manager usage. + + \return A RESIDENCY_INFO struct. + */ + virtual RESIDENCY_INFO GetInfo() const = 0; + }; + + /** \brief Create residency residency manager to manage video memory. + + @param descriptor A reference to RESIDENCY_DESC structure that describes the residency + manager. + @param[out] ppResidencyManagerOut Pointer to a memory block that recieves a pointer to the + residency manager. Pass NULL to test if residency Manager creation would succeed, but not + actually create the residency Manager. If NULL is passed and residency manager creating + would succeed, S_FALSE is returned. + */ + GPGMM_EXPORT HRESULT CreateResidencyManager(const RESIDENCY_DESC& descriptor, + IResidencyManager** ppResidencyManagerOut); + + /** \struct RESOURCE_ALLOCATION_DESC + Describes a resource allocation. + */ + struct RESOURCE_ALLOCATION_DESC { + /** \brief Requested size, in bytes, of the resource allocation. + + The requested size is the non-zero allocation size before being subjected to allocator + alignment. + */ + uint64_t SizeInBytes; + + /** \brief Offset, in bytes, of the resource in the heap. + */ + uint64_t HeapOffset; + + /** \brief Offset, in bytes, of the allocation, from the start of the + resource. + + Always zero when the resource is placed in a heap or created with it's own heap. + */ + uint64_t OffsetFromResource; + + /** \brief Method to describe how the allocation was created. + + The Method determines how to figure out the size of the allocation. + */ + AllocationMethod Method; + + /** \brief Debug name associated with the resource allocation. + */ + LPCWSTR DebugName; + }; + + /** \struct RESOURCE_ALLOCATION_INFO + Additional information about the resource allocation. + */ + struct RESOURCE_ALLOCATION_INFO { + /** \brief Created size, in bytes, of the resource allocation. + + Must be non-zero. SizeInBytes is always a multiple of the alignment. + */ + uint64_t SizeInBytes; + + /** \brief Created alignment, in bytes, of the resource allocation. + + Must be non-zero. + */ + uint64_t Alignment; + + /** \brief Method used to allocate memory for the resource. + */ + AllocationMethod Method; + }; + + /** \brief ResourceAllocation is MemoryAllocation that contains a ID3D12Resource. + + It can represent a allocation using a resource in one of three ways: 1) ID3D12Resource "placed" + in a ID3D12Heap, 2) a ID3D12Resource at a specific offset, or 3) a ID3D12Resource without a + ID3D12Heap (called a committed resource). + + It is recommend to use ResourceAllocation instead of ID3D12Resource (1:1) for perfoming D3D12 + operations with it (eg. Map, Unmap, etc). + */ + class IResourceAllocation : public IDebugObject { + public: + /** \brief Maps the resource allocation. + + Gets the CPU pointer to the specificed subresource of the resource allocation. + + If sub-allocated within the resource, the read or write range and + pointer value will start from the allocation instead of the resource. + + @param subresource Specifies the index number of the subresource. + @param pReadRange A pointer to a D3D12_RANGE structure that describes the range of memory to + access. + @param[out] ppDataOut A pointer to a memory block that receives a pointer to the resource + data. + */ + virtual HRESULT Map(uint32_t subresource = 0, + const D3D12_RANGE* pReadRange = nullptr, + void** ppDataOut = nullptr) = 0; + + /** \brief Unmaps the resource allocation. + + Invalidates the CPU pointer to the specified subresource in the resource. + + @param subresource Specifies the index number of the subresource. + @param pWrittenRange A pointer to a D3D12_RANGE structure that describes the range of memory + to unmap. + */ + virtual void Unmap(uint32_t subresource = 0, + const D3D12_RANGE* pWrittenRange = nullptr) = 0; + + /** \brief Returns the resource owned by this allocation. + + \return Pointer to ID3D12Resource, owned by this allocation. + */ + virtual ID3D12Resource* GetResource() const = 0; + + /** \brief Returns the GPU virtual address of the resource allocation. + + If sub-allocated within the resource, the GPU virtual address will + start from the allocation instead of the resource. + + \return A D3D12_GPU_VIRTUAL_ADDRESS, equal to UINT64, to represent a location in GPU memory. + */ + virtual D3D12_GPU_VIRTUAL_ADDRESS GetGPUVirtualAddress() const = 0; + + /** \brief Returns the start of the allocation in the resource. + + If sub-allocated within the resource, the offset could be greater than zero. + + \return A offset, in bytes, of the start of this allocation in the resource. + */ + virtual uint64_t GetOffsetFromResource() const = 0; + + /** \brief Returns information about this resource allocation. + + \return A RESOURCE_ALLOCATION_INFO struct containing the information. + */ + virtual RESOURCE_ALLOCATION_INFO GetInfo() const = 0; + + /** \brief Returns the heap assigned to this resource allocation. + + \return A pointer to the Heap used by this resource allocation. + */ + virtual IHeap* GetMemory() const = 0; + }; + + /** \enum ALLOCATOR_FLAGS + Specify creation options for allocator. + */ + enum ALLOCATOR_FLAGS { + + /** \brief Disables all option flags. + */ + ALLOCATOR_FLAG_NONE = 0x0, + + /** \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. + */ + ALLOCATOR_FLAG_ALWAYS_COMMITED = 0x1, + + /** \brief Creates resource within budget. + + By default (and when residency is used), resources will not be created resident unless an + operation is performed on the allocation that requires them to be (ex. Map). Otherwise, the + resource will become resident once ExecuteCommandList() is called. However, this flag can be + used to change this behavior by requiring resource heaps to be always resident at resource + creation. When residency is not used, ALLOCATOR_FLAG_ALWAYS_IN_BUDGET is implicitly enabled + through the GPU/driver instead of explicitly through GPGMM. + */ + ALLOCATOR_FLAG_ALWAYS_IN_BUDGET = 0x2, + + /** \brief Disables pre-fetching of GPU memory. + + Should be only used for debugging and testing purposes. + */ + ALLOCATOR_FLAG_DISABLE_MEMORY_PREFETCH = 0x4, + + /** \brief Disables recycling of GPU memory. + + Forces the creation of new heaps and to de-allocate heaps immediately once no longer needed + (instead of re-using it). + + This is very slow and not recommended for general use but may be useful for running with the + minimal possible GPU memory footprint, avoiding out-of-memory, or debugging possible + corruption of heaps. + */ + ALLOCATOR_FLAG_ALWAYS_ON_DEMAND = 0x8, + + /** \brief Disables use of D3D12_HEAP_TYPE_CUSTOM. + + Used to workaround issues when a custom-equivalent heap is not considered equal to + the corresponding heap type. + */ + ALLOCATOR_FLAG_DISABLE_CUSTOM_HEAPS = 0x10, + + /** \brief Report leaks of resource allocations. + + Used to track outstanding allocations made with this allocator. When the allocator is about + to be released, it will report details on any leaked allocations as log messages. + */ + ALLOCATOR_FLAG_NEVER_LEAK_MEMORY = 0x20, + }; + + DEFINE_ENUM_FLAG_OPERATORS(ALLOCATOR_FLAGS) + + /** \enum ALLOCATOR_ALGORITHM + Specify the algorithms used for allocation. + */ + enum ALLOCATOR_ALGORITHM { + /** \brief Use default allocation mechanism. + + Relies on internal heuristics to automatically determine the best allocation mechanism. The + selection of algorithm depends on: + + 1. The heap properties or flags specified by the user. + 2. The size the resource being created. + 3. The amount of available memory. + + In general, the most-efficent resource allocator will be attempted first (efficent + being defined as fastest service-time to allocate/deallocate with smallest memory + footprint), subject to other constraints. However, since it's impossible to predict all + future memory accesses, allocation techniques that rely on amortization of GPU heaps may not + prove to be faster as expected. Further experimentation is recommended. + */ + ALLOCATOR_ALGORITHM_DEFAULT = 0, + + /** \brief Use the slab allocation mechanism. + + Slab allocation allocates/deallocates in O(1) time using O(N * pageSize) space. + + Slab allocation does not suffer from internal fragmentation but could externally fragment + when many unique request sizes are used. + */ + ALLOCATOR_ALGORITHM_SLAB = 1, + + /** \brief Use the buddy system mechanism. + + Buddy system allocate/deallocates in O(Log2) time using O(1) space. + + Buddy system suffers from internal fragmentation (ie. resources are not a power-of-two) but + does not suffer from external fragmentation as much since the resource heap size does not + change. + + It is recommend to specify a PreferredResourceHeapSize large enough such that multiple + requests can fit within the specified PreferredResourceHeapSize but not too large where + creating the larger resource heap becomes a bigger bottleneck. + */ + ALLOCATOR_ALGORITHM_BUDDY_SYSTEM = 2, + + /** \brief Recycles resource heaps of a size being specified. + + Fixed pools allocate/deallocate in O(1) time using O(N) space. + + Fixed-size pool limits recycling to resource heaps equal to + PreferredResourceHeapSize. A PreferredResourceHeapSize of zero is effectively + equivelent to ALLOCATOR_FLAG_ALWAYS_ON_DEMAND. + */ + ALLOCATOR_ALGORITHM_FIXED_POOL = 3, + + /** \brief Recycles resource heaps of any size using multiple pools. + + Segmented pool allocate/deallocates in O(Log2) time using O(N * K) space. + */ + ALLOCATOR_ALGORITHM_SEGMENTED_POOL = 4, + + /** \brief Use the dedicated allocation mechanism. + + Allows resources to be created as a dedicated allocation, rather than sub-allocated. + + A dedicated allocation allocates exactly what is needed for the resource and nothing more. + + Internally, dedicated allocations are "placed resources" which allows the heap to be + recycled by GPGMM. Otherwise, ALLOCATOR_FLAG_ALWAYS_COMMITED is equivelent to a "dedicated + allocation" but without heaps being recycled by GPGMM. + + Dedicated allocation allocates/deallocates in O(1) time using O(N * pageSize) space. + */ + ALLOCATOR_ALGORITHM_DEDICATED = 5, + }; + + /** \struct ALLOCATOR_DESC + Specify parameters for creating allocators. + */ + struct ALLOCATOR_DESC { + /** \brief Specifies the device used by this allocator. + + Required parameter. Use CreateDevice get the device. + */ + Microsoft::WRL::ComPtr Device; + + /** \brief Specifies the adapter used by this allocator. + + Required parameter. Use EnumAdapters to get the adapter. + */ + Microsoft::WRL::ComPtr Adapter; + + /** \brief Specifies allocator options. + + For example, whether the allocator can reuse memory, or resources should be resident upon + creation. + */ + ALLOCATOR_FLAGS Flags; + + /** \brief Minimum severity level to log messages to console. + + Messages with lower severity will be ignored. + */ + D3D12_MESSAGE_SEVERITY MinLogLevel; + + /** \brief Specifies recording options. + + For example, what events to record, and where to record them. + */ + EVENT_RECORD_OPTIONS RecordOptions; + + /** \brief Specifies the adapter's tier of resource heap support. + + Used to determine if resource categories (texture and buffers) can co-exist in the + same resource heap. + + Required parameter. Use CheckFeatureSupport to get supported tier. + */ + D3D12_RESOURCE_HEAP_TIER ResourceHeapTier; + + /** \brief Specifies the algorithm to use for sub-allocation. + + Used to evaluate how allocation implementations perform with various algorithms that + sub-divide resource heaps. + + Optional parameter. By default, the slab allocator is used. + */ + ALLOCATOR_ALGORITHM SubAllocationAlgorithm; + + /** \brief Specifies the algorithm to use for resource heap pooling. + + Used to evaluate how allocation implementations perform with various algorithms that + sub-divide resource heaps. + + Optional parameter. By default, the slab allocator is used. + */ + ALLOCATOR_ALGORITHM PoolAlgorithm; + + /** \brief Specifies the preferred size of the resource heap. + + The preferred size of the resource heap is the minimum heap size to sub-allocate from. + A larger resource heap consumes more memory but could be faster for sub-allocation. + + Optional parameter. When 0 is specified, the API will automatically set the preferred + resource heap size to be a multiple of minimum resource heap size allowed by D3D12. + */ + uint64_t PreferredResourceHeapSize; + + /** \brief Maximum size of the resource heap allowed. + + The maximum resource heap size is equal to the total virtual address range of memory + available to the allocator. + + Optional parameter. When 0 is specified, the API will automatically set the max resource + heap size based on the adapter's GPU virtual address range. If the max resource size + exceeds the adapter's GPU virtual address range, it will default to the smaller range. + */ + uint64_t MaxResourceHeapSize; + + /** \brief Memory fragmentation limit, expressed as a percentage of the resource heap size, + that is acceptable to be wasted due to fragmentation. + + Fragmentation occurs when the allocation is larger then the resource size. + This occurs when the type of resource (buffer or texture) and allocator have different + alignment requirements. For example, a 192KB resource may need to allocate 256KB of + allocated space, which is equivalent to a fragmentation limit of 33%. + + When PreferredResourceHeapSize is non-zero, the MemoryFragmentationLimit could be + exceeded. Also, the MemoryFragmentationLimit should never be zero, as some fragmentation + can occur. + + Optional parameter. When 0 is specified, the default fragmentation limit is 1/8th the + resource heap size. + */ + double MemoryFragmentationLimit; + + /** \brief Memory growth factor, expressed as a multipler of the resource heap size + that will monotonically increase. + + A factor value of 1.0 specifies no growth, where the resource heap size is always determined + by other limits or constraints. If no factor gets specified (or a value less than 1 is + specified), GPGMM will allocate a resource heap size with enough space to fit exactly one + resource. + + Memory growth avoids the need to specify |PreferredResourceHeapSize|, which + especially helps in situations where the resource size cannot be predicated (eg. + user-defined), by allowing the resource heap size to gradually increase in size + per demand to achieve a balance of memory usage and performance. + + Optional parameter. When 0 is specified, the default of 1.25 is used (or 25% growth). + */ + double MemoryGrowthFactor; + }; + + /** \enum ALLOCATION_FLAGS + Additional controls that modify allocations. + */ + enum ALLOCATION_FLAGS { + + /** \brief Disables all allocation flags. + + Enabled by default. + */ + ALLOCATION_FLAG_NONE = 0x0, + + /** \brief Disallow creating a new resource heap when creating a resource. + + The created resource must use an existing resource heap or E_OUTOFMEMORY. Effectively + disables creating standalone allocations whose memory cannot be reused. + */ + ALLOCATION_FLAG_NEVER_ALLOCATE_MEMORY = 0x1, + + /** \brief Sub-allocate a resource allocation within the same resource. + + The resource alignment is allowed to be byte-aligned instead of being resource-aligned, + which significantly reduces app memory usage (1B vs 64KB per allocation). Since the resource + can only be in one state at a time, this is mostly restricted to constant buffers (index and + vertex buffers which will stay read-only after creation). The app developer must use offsets + from the start of the allocation (vs subresource index) by using + ResourceAllocation::GetOffsetFromResource(). + + The app developer must either check if the allocator supports sub-allocation within resource + beforehand (via ResourceAllocator::CheckFeatureSupport) OR simply ensure only a command + single queue is used since not all devices guarentee command queue accesses are coherent + between sub-allocations within the same resource. + */ + ALLOCATION_FLAG_ALLOW_SUBALLOCATE_WITHIN_RESOURCE = 0x2, + + /** \brief Disallow allowing the creation of multiple resources using the same resource + heap. + + When this flag is used, the created resource will always be allocated with it's own resource + heap. + */ + ALLOCATION_FLAG_NEVER_SUBALLOCATE_MEMORY = 0x4, + + /** \brief Prefetch memory for the next resource allocation. + + The call to prefetch is deferred to a seperate background thread by GPGMM which runs + when the current allocation requested is completed. By default, GPGMM will automatically + trigger prefetching based on heurstics. Prefetching enables more performance when + allocating for contiguous allocations or many resources of the same size. + + Should not be used with ALLOCATION_FLAG_NEVER_ALLOCATE_MEMORY. + */ + ALLOCATION_FLAG_ALWAYS_PREFETCH_MEMORY = 0x8, + + /** \brief Cache the request size. + + Allow internal data structures used for resource allocation to be cached in-memory. + */ + ALLOCATION_FLAG_ALWAYS_CACHE_SIZE = 0x10, + + /** \brief Requires heaps to be always attributed by D3D12_HEAP_TYPE. + + With cache-coherent UMA adapters, a single custom-equivelent heap will be used everywhere. + This enables better resource optimization during allocation. However, certain heap flags or + access-patterns may require or beneifit from D3D12_HEAP_TYPE. For example, + D3D12_HEAP_FLAG_SHARED requires D3D12_HEAP_TYPE_READBACK or D3D12_HEAP_TYPE_UPLOAD, + as well as frequent CPU reads would beneifit from D3D12_HEAP_TYPE_READBACK since the CPU + properties are always write-combined. + + If ALLOCATOR_FLAG_DISABLE_CUSTOM_HEAPS was specified, heap type was + D3D12_HEAP_TYPE_READBACK, or the adapter is not cache-coherent UMA, this flag has no effect. + */ + ALLOCATION_FLAG_ALWAYS_ATTRIBUTE_HEAPS = 0x20, + + /** \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. + + Mostly used for debug and testing when certain allocation methods unexpectedly fail. + */ + ALLOCATION_FLAG_NEVER_FALLBACK = 0x40, + }; + + DEFINE_ENUM_FLAG_OPERATORS(ALLOCATION_FLAGS) + + /** \struct ALLOCATION_FLAGS + Specifies how allocations should be created. + */ + struct ALLOCATION_DESC { + /** \brief Flags used to control how the resource will be allocated. + + Optional parameter. By default, GPGMM will decide automatically. + */ + ALLOCATION_FLAGS Flags; + + /** \brief Heap type that the resource to be allocated requires. + + It is recommended to not specifiy the heap type or equivalently specify + D3D12_HEAP_TYPE_CUSTOM. This enables better resource optimization for UMA adapters by using + a custom-equivelent upload heap everywhere. However, since UMA adapters use write-combined + memory for CPU writes, a heap type of D3D12_HEAP_TYPE_READBACK could have better + performance. + + Optional parameter. If the heap type is not provided or D3D12_HEAP_TYPE_CUSTOM, the heap + type will be inferred by using adapter properties and the initial resource state. + */ + D3D12_HEAP_TYPE HeapType; + + /** \brief Additional heap flags that the resource requires. + + By default, GPGMM infers the required heap flags based on the required + fields in the D3D12_RESOURCE_DESC, ALLOCATOR_DESC and ALLOCATION_DESC. + But if additional heap flags are required, they can also be specified. + + It is recommended to only specify D3D12_HEAP_FLAG_NONE since not all + allocation methods are guarenteed to be supported. + + Optional parameter. + */ + D3D12_HEAP_FLAGS ExtraRequiredHeapFlags; + + /** \brief Require additional bytes to be appended to the resource allocation. + + Resource heap size is guarenteed to increase by at-least this number of bytes. + Specifying a padding will disable committed resources and sub-allocated + heaps. + + Used to workaround driver bugs related to the heap size being insufficent for the resource. + + Optional parameter. No extra padding is applied by default. + */ + uint64_t RequireResourceHeapPadding; + + /** \brief Associates a name with the given allocation. + + Optional parameter. By default, no name is associated. + */ + LPCWSTR DebugName; + }; + + /** \struct FEATURE_DATA_RESOURCE_ALLOCATION_SUPPORT + + Details the resource allocator limitations, including if sharing resources between command + queues is coherent. + */ + struct FEATURE_DATA_RESOURCE_ALLOCATION_SUPPORT { + /** \brief Describes resource within coherency behavior between command-queues. + + For example, if two allocations belong to the same resource where each allocation is + referenced with a different command-queue, will accessing one stomp over the other. D3D12 + does not guarentee such behavior is safe but is it well-defined behavior based on the GPU + vendor. + */ + bool IsResourceAllocationWithinCoherent; + }; + + /** \enum ALLOCATOR_FEATURE + + Defines constants that specify a resource allocator feature to query about. When you + want to query for the level to which an allocator supports a feature, pass one of these values + to ResourceAllocator::CheckFeatureSupport. + */ + enum ALLOCATOR_FEATURE { + /** \brief Indicates a query for the level of support for allocated resources. The + corresponding data structure for this value is FEATURE_DATA_RESOURCE_ALLOCATION_SUPPORT + */ + ALLOCATOR_FEATURE_RESOURCE_ALLOCATION_SUPPORT, + }; + + using RESOURCE_ALLOCATOR_INFO = MemoryAllocatorInfo; + + /** \brief ResourceAllocator is a MemoryAllocator that creates ID3D12Resources in a + ResourceAllocation. + + Internally, ResourceAllocator creates a request, by determining the + resource allocation requirements, then finds a MemoryAllocator able to service the request. + + If the first MemoryAllocator attempt fails, it will try a second MemoryAllocator, and so on. + MemoryAllocator attempts are greedy: re-use of resources > re-use of heaps > + re-use by pools > no re-use, in order of maximizing performance while minimizing memory + footprint. + + ResourceAllocator also uses ResidencyManager to determine available memory + (or budget left) when creating the request. This is because residency is managed + per heap and not per resource). A larger Heap could be ideal for allocation but only if there is + budget. And similarly, a smaller Heap allows for finer grained residency but could increase + overall memory usage for allocation. + **/ + class IResourceAllocator : public IUnknown { + public: + /** \brief Allocates memory and creates a D3D12 resource using it. + + Returns a ResourceAllocation which represents a resource allocated at a specific + location in memory. The resource could be allocated within a resource heap, within the + resource itself, or seperately using it's own memory (resource heap). + + Unlike a D3D12 resource, a resource allocation can made resident. It is recommended but not + strictly required to use the D3D12 resource equivalent methods (ex. Map, Unmap) through the + returned ResourceAllocation. + + @param allocationDescriptor A reference to ALLOCATION_DESC structure that provides + properties for the resource allocation. + @param resourceDescriptor A reference to the D3D12_RESOURCE_DESC structure that describes + the resource. + @param initialResourceState The initial state of the resource, a bitwise OR'd combination of + D3D12_RESOURCE_STATES enumeration constants. + @param pClearValue A pointer tp D3D12_CLEAR_VALUE structure that describes the default value + for a clear color. + @param[out] ppResourceAllocationOut An optional pointer to a memory block that recieves the + required interface pointer to the created resource allocation object. + */ + virtual HRESULT CreateResource(const ALLOCATION_DESC& allocationDescriptor, + const D3D12_RESOURCE_DESC& resourceDescriptor, + D3D12_RESOURCE_STATES initialResourceState, + const D3D12_CLEAR_VALUE* pClearValue, + IResourceAllocation** ppResourceAllocationOut) = 0; + + /** \brief Imports an existing D3D12 resource. + + Allows externally created D3D12 resources to be used as ResourceAllocations. + + Residency is not supported for imported resources. + + @param committedResource A COM managed pointer to a D3D12 committed resource. + @param[out] ppResourceAllocationOut Pointer to a memory block that recieves a pointer to the + resource allocation. Pass NULL to test if resource allocation creation would succeed, but + not actually create the resource allocation. If NULL is passed and resource allocation + creation would succeed, S_FALSE is returned. + */ + virtual HRESULT CreateResource(Microsoft::WRL::ComPtr committedResource, + IResourceAllocation** ppResourceAllocationOut) = 0; + + /** \brief Return free memory back to the OS. + + When pooling is enabled, the allocator will retain resource heaps in order to speed-up + subsequent resource allocation requests. These resource allocations count against the + app's memory usage and in general, will lead to increased memory usage by the overall + system. Apps should call ReleaseMemory() when going idle for a period of time since there is + a brief performance hit when the internal resource heaps get reallocated by the OS. + + @param bytesToRelease Amount of memory to release, in bytes. A value of UINT64_MAX + releases ALL memory held by the allocator. + + \return Amount of memory, in bytes, released. The released size might be smaller then + bytesToRelease if there was not enough memory or larger if releasable memory doesn't exactly + total up to the amount. + */ + virtual uint64_t ReleaseMemory(uint64_t bytesToRelease) = 0; + + /** \brief Return the current allocator usage. + + Returned info can be used to monitor memory usage per allocator. + For example, the amount of internal fragmentation is equal to UsedBlockUsage / + UsedMemoryUsage. Or the percent of recycled memory is equal to FreeMemoryUsage / + (UsedMemoryUsage + FreeMemoryUsage) * 100%. + + */ + virtual RESOURCE_ALLOCATOR_INFO GetInfo() const = 0; + + /** \brief Gets information about the features that are supported by the resource allocator. + + @param feature A constant from the ALLOCATOR_FEATURE enumeration describing the feature(s) + that you want to query for support. + @param pFeatureSupportData A pointer to the data structure that corresponds to the value of + the feature parameter. To determine the corresponding data structure for each constant, see + FEATURE. + @param featureSupportDataSize The sie of the structure pointed by the pFeatureSupportData + parameter. + + \return Returns S_OK if successful. Returns E_INVALIDARG if unsupported data type is passed + to pFeatureSupportData or if a size mismatch is detected for the featureSupportDataSize + parameter. + */ + virtual HRESULT CheckFeatureSupport(ALLOCATOR_FEATURE feature, + void* pFeatureSupportData, + uint32_t featureSupportDataSize) const = 0; + }; + + /** \brief Create a resource allocator with residency. + + Residency requires at-least DXGI version 1.4. + + @param allocatorDescriptor A reference to ALLOCATOR_DESC structure that describes the + allocator. + @param[out] ppResourceAllocatorOut Pointer to a memory block that recieves a pointer to the + resource allocator. Pass NULL to test if allocator creation would succeed, but not actually + create the allocator. + @param[out] ppResidencyManagerOut Pointer to a memory block that recieves a pointer to the + residency manager. If NULL is passed, the allocator will be created without using + residency. + */ + GPGMM_EXPORT HRESULT CreateResourceAllocator(const ALLOCATOR_DESC& allocatorDescriptor, + IResourceAllocator** ppResourceAllocatorOut, + IResidencyManager** ppResidencyManagerOut); + + /** \brief Create a resource allocator using a specified residency manager. + + @param allocatorDescriptor A reference to ALLOCATOR_DESC structure that describes the + allocator. + @param pResidencyManager Pointer to a memory block that recieves a pointer to the + residency manager. + @param[out] ppResourceAllocatorOut Pointer to a memory block that recieves a pointer to the + resource allocator. Pass NULL to test if allocator creation would succeed, but not actually + create the allocator. + */ + GPGMM_EXPORT HRESULT CreateResourceAllocator(const ALLOCATOR_DESC& allocatorDescriptor, + IResidencyManager* pResidencyManager, + IResourceAllocator** ppResourceAllocatorOut); + +} // namespace gpgmm::d3d12 #endif // INCLUDE_GPGMM_D3D12_H_ diff --git a/src/include/gpgmm_vk.h b/src/include/gpgmm_vk.h index bc40b01c5..654fdc7ad 100644 --- a/src/include/gpgmm_vk.h +++ b/src/include/gpgmm_vk.h @@ -15,6 +15,337 @@ #ifndef INCLUDE_GPGMM_VK_H_ #define INCLUDE_GPGMM_VK_H_ -#include "gpgmm/vk/ResourceAllocatorVk.h" +#ifndef GPGMM_VK_HEADERS_ALREADY_INCLUDED +# include +#endif + +#include "gpgmm.h" + +namespace gpgmm::vk { + + /** \struct GpResourceAllocator + \brief Opaque handle to a allocator object. + */ + VK_DEFINE_HANDLE(GpResourceAllocator) + + /** \struct GpResourceAllocation + \brief Opaque handle to a resource allocation object. + */ + VK_DEFINE_HANDLE(GpResourceAllocation) + + /** \enum GpAllocatorCreateFlags + \brief Configures how allocators should be created. + */ + enum GpAllocatorCreateFlags { + /** \brief Disables all allocator flags. + */ + GP_ALLOCATOR_CREATE_NONE = 0x0, + + /** \brief Disables pre-fetching of GPU memory. + + Should be only used for debugging and testing purposes. + */ + GP_ALLOCATOR_CREATE_DISABLE_MEMORY_PREFETCH = 0x4, + + /** \brief Tell GPGMM to allocate exactly what is needed, and to de-allocate + memory immediately once no longer needed (instead of re-using it). + + This is very slow and not recommended for general use but may be useful for running with the + minimal possible GPU memory footprint or debugging OOM failures. + */ + GP_ALLOCATOR_CREATE_ALWAYS_ON_DEMAND = 0x8, + + /** \brief Creates resource within budget. + + Requires the device extension VK_EXT_memory_budget to be supported before use. + The instance specified in GpAllocatorCreateInfo is also required to support + VK_KHR_get_physical_device_properties2. + */ + GP_ALLOCATOR_CREATE_ALWAYS_IN_BUDGET = 0x10, + }; + + /** \enum GpAllocatorAlgorithm + Specify the algorithms used for allocation. + */ + enum GpAllocatorAlgorithm { + /** \brief Use default allocation mechanism. + + Relies on internal heuristics to automatically determine the best allocation mechanism. The + selection of algorithm depends on: + + 1. The memory properties or flags specified by the user. + 2. The size the resource being created. + 3. The amount of available memory. + + In general, the most-efficent resource allocator will be attempted first (efficent + being defined as fastest service-time to allocate/deallocate with smallest memory + footprint), subject to other constraints. However, since it's impossible to predict all + future memory accesses, allocation techniques that rely on amortization of GPU heaps may not + prove to be faster as expected. Further experimentation is recommended. + */ + GP_ALLOCATOR_ALGORITHM_DEFAULT = 0, + + /** \brief Use the slab allocation mechanism. + + Slab allocation allocates/deallocates in O(1) time using O(N * pageSize) space. + + Slab allocation does not suffer from internal fragmentation but could externally fragment + when many unique request sizes are used. + */ + GP_ALLOCATOR_ALGORITHM_SLAB = 1, + + /** \brief Use the buddy system mechanism. + + Buddy system allocate/deallocates in O(Log2) time using O(1) space. + + Buddy system suffers from internal fragmentation (ie. resources are not a power-of-two) but + does not suffer from external fragmentation as much since the device memory size does not + change. + + It is recommend to specify a preferredDeviceMemorySize large enough such that multiple + requests can fit within the specified preferredDeviceMemorySize but not too large where + creating the larger device memory becomes a bigger bottleneck. + */ + GP_ALLOCATOR_ALGORITHM_BUDDY_SYSTEM = 2, + + /** \brief Recycles device memory of a size being specified. + + Fixed pools allocate/deallocate in O(1) time using O(N) space. + + Fixed-size pool limits recycling to device memorys equal to + preferredDeviceMemorySize. A preferredDeviceMemorySize of zero is effectively + equivelent to ALLOCATOR_FLAG_ALWAYS_ON_DEMAND. + */ + GP_ALLOCATOR_ALGORITHM_FIXED_POOL = 3, + + /** \brief Recycles device memory of any size using multiple pools. + + Segmented pool allocate/deallocates in O(Log2) time using O(N * K) space. + */ + GP_ALLOCATOR_ALGORITHM_SEGMENTED_POOL = 4, + }; + + struct VulkanFunctions; + + /** \struct GpAllocatorCreateInfo + \brief Used to create allocator. + */ + struct GpAllocatorCreateInfo { + /** \brief Function pointer to Vulkan functions. + + There are 3 ways to specify Vulkan functions. + 1. Specify `gpgmm_vk_static_functions = true` and statically link agaisn't the Vulkan + loader provided by GPGMM. + 2. Load Vulkan functions dynamically by specifying `gpgmm_vk_static_functions = false` and + ONLY provide the instance and device functions, `vkGetInstanceProcAddr` and + `vkGetDeviceProcAddr`. GPGMM will use those to load the remaining. + 3. Specify ALL the Vulkan functions. GPGMM will not import or load Vulkan function itself. + */ + const VulkanFunctions* pVulkanFunctions = nullptr; + + /** \brief Handle to Vulkan physical device object. + */ + VkPhysicalDevice physicalDevice; + + /** \brief Handle to Vulkan device object. + */ + VkDevice device; + + /** \brief Handle to Vulkan instance object. + */ + VkInstance instance; + + /** \brief Vulkan version return by VK_MAKE_VERSION. + */ + uint32_t vulkanApiVersion; + + /** \brief Flags used to configure allocator. + */ + GpAllocatorCreateFlags flags; + + /** \brief Specifies the algorithm to use for sub-allocation. + + Used to evaluate how allocation implementations perform with various algorithms that + sub-divide devie memory. + + Optional parameter. By default, the slab allocator is used. + */ + GpAllocatorAlgorithm subAllocationAlgorithm = GP_ALLOCATOR_ALGORITHM_SLAB; + + /** \brief Specifies the algorithm to use for device memory pooling. + + Used to evaluate how allocation implementations perform with various algorithms that + sub-divide device memorys. + + Optional parameter. By default, the slab allocator is used. + */ + GpAllocatorAlgorithm poolAlgorithm = GP_ALLOCATOR_ALGORITHM_SEGMENTED_POOL; + + /** \brief Specifies the preferred size of device memory. + + The preferred size of the device memory is the minimum memory size to sub-allocate from. + A larger device memory consumes more memory but could be faster for sub-allocation. + + Optional parameter. When 0 is specified, the API will automatically set the preferred + device memory size to be a multiple of minimum device memory size allowed by Vulkan. + */ + uint64_t preferredDeviceMemorySize; + + /** \brief Memory fragmentation limit, expressed as a percentage of the device memory size, + that is acceptable to be wasted due to fragmentation. + + Fragmentation occurs when the allocation is larger then the resource size. + This occurs when the type of resource (buffer or texture) and allocator have different + alignment requirements. For example, a 192KB resource may need to allocate 256KB of + allocated space, which is equivalent to a fragmentation limit of 33%. + + When |preferredDeviceMemorySize| is non-zero, the memoryFragmentationLimit could be + exceeded. Also, the memoryFragmentationLimit should never be zero, as some fragmentation + can occur. + + Optional parameter. When 0 is specified, the default fragmentation limit is 1/8th the + device memory size. + */ + double memoryFragmentationLimit; + + /** \brief Memory growth factor, expressed as a multipler of the device memory size + that will monotonically increase. + + A factor value of 1.0 specifies no growth, where the device memory size is always determined + by other limits or constraints. If no factor gets specified (or a value less than 1 is + specified), GPGMM will allocate a device memory size with enough space to fit exactly one + resource. + + Memory growth avoids the need to specify |preferredDeviceMemorySize|, which + especially helps in situations where the resource size cannot be predicated (eg. + user-defined), by allowing the device memory size to gradually increase in size + per demand to achieve a balance of memory usage and performance. + + Optional parameter. When 0 is specified, the default of 1.25 is used (or 25% growth). + */ + double memoryGrowthFactor; + }; + + /** \enum GpResourceAllocationCreateFlags + Additional controls that modify allocations. + */ + enum GpResourceAllocationCreateFlags { + + /** \brief Disables all allocation flags. + + Enabled by default. + */ + GP_ALLOCATION_CREATE_NONE = 0x0, + + /** \brief Disallow creating new device memory when creating a resource. + + Forbids creating new device memory when creating a resource. The created resource + must use existing device memory or error. Effectively disables creating + standalone allocations whose memory cannot be reused. + */ + GP_ALLOCATION_CREATE_NEVER_ALLOCATE_MEMORY = 0x1, + + /** \brief Disallow creating multiple resource allocations from the same device memory. + + The created resource will always be allocated with it's own device memory. + */ + GP_ALLOCATION_CREATE_NEVER_SUBALLOCATE_MEMORY = 0x4, + + /** \brief Prefetch memory for the next resource allocation. + + The call to prefetch is deferred to a seperate background thread by GPGMM which runs + when the current allocation requested is completed. By default, GPGMM will automatically + trigger prefetching based on heurstics. Prefetching enables more performance when + allocating for large contiguous allocations. Should not be used with + ALLOCATION_FLAG_NEVER_ALLOCATE_MEMORY. + */ + GP_ALLOCATION_CREATE_ALWAYS_PREFETCH_MEMORY = 0x8, + }; + + /** \struct GpResourceAllocationCreateInfo + Specifies how allocations should be created. + */ + struct GpResourceAllocationCreateInfo { + /** \brief Flags used to control how the resource will be allocated. + */ + GpResourceAllocationCreateFlags flags; + + /** \brief Bitmask to specify required memory properties for the allocation. + */ + VkMemoryPropertyFlags requiredPropertyFlags; + }; + + /** \brief Create allocator used to create and manage video memory for the App specified device + and instance. + + @param info A reference to GpAllocatorCreateInfo structure that describes the allocator. + @param[out] allocatorOut Pointer to a memory block that recieves a pointer to the + resource allocator. Pass NULL to test if allocator creation would succeed, but not actually + create the allocator. If NULL is passed and allocator creating would succeed, VK_INCOMPLETE is + returned. + */ + GPGMM_EXPORT VkResult gpCreateResourceAllocator(const GpAllocatorCreateInfo& info, + GpResourceAllocator* allocatorOut); + + /** \brief Destroy allocator. + + @param allocator A GpResourceAllocator to destroy. + */ + GPGMM_EXPORT void gpDestroyResourceAllocator(GpResourceAllocator allocator); + + /** \brief Create a buffer allocation. + + @param allocator A GpResourceAllocator used to create the buffer and allocation. + @param pBufferCreateInfo A pointer to a VkBufferCreateInfo that describes the buffer to create. + @param pBuffer A pointer to a VkBuffer that will be created using the allocation. + @param pAllocationCreateInfo A pointer to a GpResourceAllocationCreateInfo that describes the + allocation. + @param[out] allocationOut A pointer to GpResourceAllocation that represents the buffer + allocation. + */ + GPGMM_EXPORT VkResult + gpCreateBuffer(GpResourceAllocator allocator, + const VkBufferCreateInfo* pBufferCreateInfo, + VkBuffer* pBuffer, + const GpResourceAllocationCreateInfo* pAllocationCreateInfo, + GpResourceAllocation* allocationOut); + + /** \brief Destroy buffer allocation. + + @param allocator A GpResourceAllocator used to create the buffer and allocation. + @param buffer A VkBuffer that was also created by the allocator. + @param allocation A GpResourceAllocation that was created by the allocator. + */ + GPGMM_EXPORT void gpDestroyBuffer(GpResourceAllocator allocator, + VkBuffer buffer, + GpResourceAllocation allocation); + + /** \brief Create a image allocation. + + @param allocator A GpResourceAllocator used to create the image and allocation. + @param pImageCreateInfo A pointer to a VkImageCreateInfo that describes the image to create. + @param pImage A pointer to a VkImage that will be created using the allocation. + @param pAllocationCreateInfo A pointer to a GpResourceAllocationCreateInfo that describes the + allocation. + @param[out] allocationOut A pointer to GpResourceAllocation that represents the image + allocation. + */ + GPGMM_EXPORT VkResult gpCreateImage(GpResourceAllocator allocator, + const VkImageCreateInfo* pImageCreateInfo, + VkImage* pImage, + const GpResourceAllocationCreateInfo* pAllocationCreateInfo, + GpResourceAllocation* allocationOut); + + /** \brief Destroy image allocation. + + @param allocator A GpResourceAllocator used to create the image and allocation. + @param image A VkImage that was also created by the allocator. + @param allocation A GpResourceAllocation that was created by the allocator. + */ + GPGMM_EXPORT void gpDestroyImage(GpResourceAllocator allocator, + VkImage image, + GpResourceAllocation allocation); + +} // namespace gpgmm::vk #endif // INCLUDE_GPGMM_VK_H_ diff --git a/src/include/min/gpgmm.h b/src/include/min/gpgmm.h deleted file mode 100644 index c1a38d78c..000000000 --- a/src/include/min/gpgmm.h +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright 2022 The GPGMM Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef INCLUDE_MIN_GPGMM_H_ -#define INCLUDE_MIN_GPGMM_H_ - -#include -#include - -namespace gpgmm { - - class MemoryBase { - public: - MemoryBase(uint64_t size, uint64_t alignment); - virtual ~MemoryBase(); - - uint64_t GetSize() const; - uint64_t GetAlignment() const; - - private: - const uint64_t mSize; - const uint64_t mAlignment; - }; - - struct MemoryAllocatorInfo { - uint32_t UsedBlockCount; - uint64_t UsedBlockUsage; - uint32_t UsedMemoryCount; - uint64_t UsedMemoryUsage; - uint64_t FreeMemoryUsage; - uint64_t PrefetchedMemoryMisses; - uint64_t PrefetchedMemoryMissesEliminated; - uint64_t SizeCacheMisses; - uint64_t SizeCacheHits; - }; - - class MemoryAllocation; - - class MemoryAllocator { - public: - virtual void DeallocateMemory(std::unique_ptr allocation) = 0; - virtual uint64_t ReleaseMemory(uint64_t bytesToRelease); - virtual MemoryAllocatorInfo GetInfo() const; - - protected: - MemoryAllocatorInfo mInfo = {}; - }; - - struct MemoryAllocationInfo { - uint64_t SizeInBytes; - uint64_t Alignment; - }; - - enum class AllocationMethod { - kUndefined = 0, - kStandalone = 1, - kSubAllocated = 2, - kSubAllocatedWithin = 3, - }; - - struct MemoryBlock { - uint64_t Offset; - uint64_t Size; - }; - - class MemoryAllocation { - public: - MemoryAllocation(MemoryAllocator* allocator, MemoryBase* memory, uint64_t requestSize); - - virtual ~MemoryAllocation(); - - MemoryAllocation(const MemoryAllocation&); - MemoryAllocation& operator=(const MemoryAllocation&); - bool operator==(const MemoryAllocation&) const; - bool operator!=(const MemoryAllocation& other) const; - - MemoryAllocationInfo GetInfo() const; - MemoryBase* GetMemory() const; - uint8_t* GetMappedPointer() const; - MemoryAllocator* GetAllocator() const; - uint64_t GetSize() const; - uint64_t GetRequestSize() const; - uint64_t GetAlignment() const; - uint64_t GetOffset() const; - AllocationMethod GetMethod() const; - MemoryBlock* GetBlock() const; - - protected: - MemoryAllocator* mAllocator; - - private: - MemoryBase* mMemory; - }; - -} // namespace gpgmm - -#endif // INCLUDE_MIN_GPGMM_H_ diff --git a/src/include/min/gpgmm_d3d12.h b/src/include/min/gpgmm_d3d12.h deleted file mode 100644 index a40429ad6..000000000 --- a/src/include/min/gpgmm_d3d12.h +++ /dev/null @@ -1,354 +0,0 @@ -// Copyright 2022 The GPGMM Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef INCLUDE_MIN_GPGMM_D3D12_H_ -#define INCLUDE_MIN_GPGMM_D3D12_H_ - -// GPGMM minimum viable implementation (MVI). -// -// GPGMM MVI allows users to leverage GPGMM's portable GMM interface without -// requiring to build the full GPGMM implementation for incremental enabling during -// development. -// -// GPGMM MVI specifically, -// * Is not thread-safe. -// * Is functionally-equivelent to calling ID3D12Device::CreateCommittedResource. -// * Does not perform residency management or call ID3D12Device::MakeResident. -// * GMM functionality will otherwise "no-op" or pass-through. -// -// User should decide to define the following macros: -// - GPGMM_D3D12_HEADERS_ALREADY_INCLUDED: D3D12 platform headers will be already included before -// this header and does not need to be re-included. -// - GPGMM_WINDOWS_HEADERS_ALREADY_INCLUDED: Windows.h will be already included before this header -// and does not need to be re-included. -// - GPGMM_REFCOUNT_TYPE : Allows a user-defined ref-count type to be used instead of -// the STL-provided one. The increment, decrement, and equals operator must be defined. -#ifndef GPGMM_D3D12_HEADERS_ALREADY_INCLUDED -# include -# include -# include -#endif - -#ifndef GPGMM_WINDOWS_HEADERS_ALREADY_INCLUDED -# include // for DEFINE_ENUM_FLAG_OPERATORS -#endif - -#if !defined(GPGMM_REFCOUNT_TYPE) -# include -#endif - -#ifndef GPGMM_REFCOUNT_TYPE -# define GPGMM_REFCOUNT_TYPE std::atomic -#endif - -#include - -#include "gpgmm.h" - -namespace gpgmm::d3d12 { - - class IUnknownImpl : public IUnknown { - public: - IUnknownImpl(); - virtual ~IUnknownImpl(); - - HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject) override; - ULONG STDMETHODCALLTYPE AddRef() override; - ULONG STDMETHODCALLTYPE Release() override; - - protected: - virtual void DeleteThis(); - - private: - GPGMM_REFCOUNT_TYPE mRefCount; - }; - - enum RESIDENCY_STATUS { - RESIDENCY_STATUS_UNKNOWN = 0, - RESIDENCY_STATUS_PENDING_RESIDENCY = 1, - RESIDENCY_STATUS_CURRENT_RESIDENT = 2, - }; - - struct HEAP_INFO { - bool IsLocked; - RESIDENCY_STATUS Status; - }; - - enum HEAPS_FLAGS { - HEAPS_FLAG_NONE = 0x0, - HEAP_FLAG_ALWAYS_IN_BUDGET = 0x1, - }; - - DEFINE_ENUM_FLAG_OPERATORS(HEAPS_FLAGS) - - struct HEAP_DESC { - uint64_t SizeInBytes; - uint64_t Alignment; - HEAPS_FLAGS Flags; - DXGI_MEMORY_SEGMENT_GROUP MemorySegmentGroup; - LPCWSTR DebugName; - }; - - using CreateHeapFn = std::function; - - class ResidencyManager; - class ResourceAllocator; - - class Heap final : public MemoryBase, public IUnknownImpl { - public: - static HRESULT CreateHeap(const HEAP_DESC& descriptor, - ResidencyManager* const pResidencyManager, - CreateHeapFn&& createHeapFn, - Heap** ppHeapOut); - - HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject) override; - HEAP_INFO GetInfo() const; - - private: - Heap(Microsoft::WRL::ComPtr pageable, - const HEAP_DESC& descriptor, - bool isResidencyDisabled); - - Microsoft::WRL::ComPtr mPageable; - }; - - class ResidencyList final { - public: - ResidencyList(); - - HRESULT Add(Heap* pHeap); - HRESULT Reset(); - }; - - enum EVENT_RECORD_FLAGS { - EVENT_RECORD_FLAG_NONE = 0x0, - EVENT_RECORD_FLAG_API_OBJECTS = 0x1, - EVENT_RECORD_FLAG_API_CALLS = 0x2, - EVENT_RECORD_FLAG_API_TIMINGS = 0x4, - EVENT_RECORD_FLAG_COUNTERS = 0x8, - EVENT_RECORD_FLAG_CAPTURE = 0x3, - EVENT_RECORD_FLAG_ALL_EVENTS = 0xFF, - }; - - DEFINE_ENUM_FLAG_OPERATORS(EVENT_RECORD_FLAGS) - - enum EVENT_RECORD_SCOPE { - EVENT_RECORD_SCOPE_PER_PROCESS = 0, - EVENT_RECORD_SCOPE_PER_INSTANCE = 1, - }; - - struct EVENT_RECORD_OPTIONS { - EVENT_RECORD_FLAGS Flags; - D3D12_MESSAGE_SEVERITY MinMessageLevel; - EVENT_RECORD_SCOPE EventScope; - bool UseDetailedTimingEvents; - const char* TraceFile; - }; - - enum RESIDENCY_FLAGS { - RESIDENCY_FLAG_NONE = 0x0, - RESIDENCY_FLAG_NEVER_UPDATE_BUDGET_ON_WORKER_THREAD = 0x1, - }; - - DEFINE_ENUM_FLAG_OPERATORS(RESIDENCY_FLAGS) - - struct RESIDENCY_DESC { - Microsoft::WRL::ComPtr Device; - Microsoft::WRL::ComPtr Adapter; - bool IsUMA; - RESIDENCY_FLAGS Flags; - D3D12_MESSAGE_SEVERITY MinLogLevel; - EVENT_RECORD_OPTIONS RecordOptions; - float MaxPctOfVideoMemoryToBudget; - float MinPctOfBudgetToReserve; - uint64_t MaxBudgetInBytes; - uint64_t EvictSizeInBytes; - uint64_t InitialFenceValue; - }; - - struct RESIDENCY_INFO { - uint64_t CurrentMemoryUsage; - uint64_t CurrentMemoryCount; - }; - - class ResidencyManager final : public IUnknownImpl { - public: - static HRESULT CreateResidencyManager(const RESIDENCY_DESC& descriptor, - ResidencyManager** ppResidencyManagerOut); - - ~ResidencyManager() override; - - HRESULT LockHeap(Heap* pHeap); - HRESULT UnlockHeap(Heap* pHeap); - HRESULT ExecuteCommandLists(ID3D12CommandQueue* pQueue, - ID3D12CommandList* const* ppCommandLists, - ResidencyList* const* ppResidencyLists, - uint32_t count); - HRESULT SetVideoMemoryReservation(const DXGI_MEMORY_SEGMENT_GROUP& memorySegmentGroup, - uint64_t availableForReservation, - uint64_t* pCurrentReservationOut = nullptr); - HRESULT QueryVideoMemoryInfo(const DXGI_MEMORY_SEGMENT_GROUP& memorySegmentGroup, - DXGI_QUERY_VIDEO_MEMORY_INFO* pVideoMemoryInfoOut); - RESIDENCY_INFO GetInfo() const; - - private: - ResidencyManager(const RESIDENCY_DESC& descriptor); - - Microsoft::WRL::ComPtr mDevice; - Microsoft::WRL::ComPtr mAdapter; - }; - - struct RESOURCE_ALLOCATION_DESC { - uint64_t SizeInBytes; - uint64_t HeapOffset; - uint64_t OffsetFromResource; - AllocationMethod Method; - LPCWSTR DebugName; - }; - - struct RESOURCE_ALLOCATION_INFO { - uint64_t SizeInBytes; - uint64_t Alignment; - }; - - class ResourceAllocation final : public MemoryAllocation, public IUnknownImpl { - public: - HRESULT Map(uint32_t subresource = 0, - const D3D12_RANGE* pReadRange = nullptr, - void** ppDataOut = nullptr); - void Unmap(uint32_t subresource = 0, const D3D12_RANGE* pWrittenRange = nullptr); - ID3D12Resource* GetResource() const; - D3D12_GPU_VIRTUAL_ADDRESS GetGPUVirtualAddress() const; - uint64_t GetOffsetFromResource() const; - RESOURCE_ALLOCATION_INFO GetInfo() const; - Heap* GetMemory() const; - - private: - friend ResourceAllocator; - - ResourceAllocation(const RESOURCE_ALLOCATION_DESC& desc, - MemoryAllocator* allocator, - Heap* resourceHeap, - Microsoft::WRL::ComPtr resource); - - void DeleteThis() override; - - Microsoft::WRL::ComPtr mResource; - }; - - enum ALLOCATOR_FLAGS { - ALLOCATOR_FLAG_NONE = 0x0, - ALLOCATOR_FLAG_ALWAYS_COMMITED = 0x1, - ALLOCATOR_FLAG_ALWAYS_IN_BUDGET = 0x2, - ALLOCATOR_FLAG_DISABLE_MEMORY_PREFETCH = 0x4, - ALLOCATOR_FLAG_ALWAYS_ON_DEMAND = 0x8, - ALLOCATOR_FLAG_DISABLE_CUSTOM_HEAPS = 0x10, - ALLOCATOR_FLAG_NEVER_LEAK_MEMORY = 0x20, - }; - - DEFINE_ENUM_FLAG_OPERATORS(ALLOCATOR_FLAGS) - - enum ALLOCATOR_ALGORITHM { - ALLOCATOR_ALGORITHM_DEFAULT = 0, - ALLOCATOR_ALGORITHM_SLAB = 1, - ALLOCATOR_ALGORITHM_BUDDY_SYSTEM = 2, - ALLOCATOR_ALGORITHM_FIXED_POOL = 3, - ALLOCATOR_ALGORITHM_SEGMENTED_POOL = 4, - ALLOCATOR_ALGORITHM_DEDICATED = 5, - }; - - DEFINE_ENUM_FLAG_OPERATORS(ALLOCATOR_ALGORITHM) - - struct ALLOCATOR_DESC { - Microsoft::WRL::ComPtr Device; - Microsoft::WRL::ComPtr Adapter; - ALLOCATOR_FLAGS Flags; - D3D12_MESSAGE_SEVERITY MinLogLevel; - EVENT_RECORD_OPTIONS RecordOptions; - D3D12_RESOURCE_HEAP_TIER ResourceHeapTier; - ALLOCATOR_ALGORITHM SubAllocationAlgorithm; - ALLOCATOR_ALGORITHM PoolAlgorithm; - uint64_t PreferredResourceHeapSize; - uint64_t MaxResourceHeapSize; - double MemoryFragmentationLimit; - double MemoryGrowthFactor; - }; - - enum ALLOCATION_FLAGS { - ALLOCATION_FLAG_NONE = 0x0, - ALLOCATION_FLAG_NEVER_ALLOCATE_MEMORY = 0x1, - ALLOCATION_FLAG_ALLOW_SUBALLOCATE_WITHIN_RESOURCE = 0x2, - ALLOCATION_FLAG_NEVER_SUBALLOCATE_MEMORY = 0x4, - ALLOCATION_FLAG_ALWAYS_PREFETCH_MEMORY = 0x8, - ALLOCATION_FLAG_ALWAYS_CACHE_SIZE = 0x10, - ALLOCATION_FLAG_ALWAYS_ATTRIBUTE_HEAPS = 0x20, - ALLOCATION_FLAG_NEVER_FALLBACK = 0x40, - }; - - DEFINE_ENUM_FLAG_OPERATORS(ALLOCATION_FLAGS) - - struct ALLOCATION_DESC { - ALLOCATION_FLAGS Flags; - D3D12_HEAP_TYPE HeapType; - D3D12_HEAP_FLAGS ExtraRequiredHeapFlags; - uint64_t RequireResourceHeapPadding; - LPCWSTR DebugName; - }; - - enum ALLOCATOR_FEATURE { - ALLOCATOR_FEATURE_RESOURCE_ALLOCATION_SUPPORT, - }; - - using RESOURCE_ALLOCATOR_INFO = MemoryAllocatorInfo; - - class ResourceAllocator final : public MemoryAllocator, public IUnknownImpl { - public: - static HRESULT CreateResourceAllocator(const ALLOCATOR_DESC& allocatorDescriptor, - ResourceAllocator** ppResourceAllocatorOut, - ResidencyManager** ppResidencyManagerOut = nullptr); - - static HRESULT CreateResourceAllocator(const ALLOCATOR_DESC& allocatorDescriptor, - ResidencyManager* pResidencyManager, - ResourceAllocator** ppResourceAllocatorOut); - - HRESULT CreateResource(const ALLOCATION_DESC& allocationDescriptor, - const D3D12_RESOURCE_DESC& resourceDescriptor, - D3D12_RESOURCE_STATES initialResourceState, - const D3D12_CLEAR_VALUE* pClearValue, - ResourceAllocation** ppResourceAllocationOut); - - HRESULT CreateResource(Microsoft::WRL::ComPtr committedResource, - ResourceAllocation** ppResourceAllocationOut); - - uint64_t ReleaseMemory(uint64_t bytesToRelease) override; - - RESOURCE_ALLOCATOR_INFO GetInfo() const override; - - HRESULT CheckFeatureSupport(ALLOCATOR_FEATURE feature, - void* pFeatureSupportData, - uint32_t featureSupportDataSize) const; - - private: - ResourceAllocator(const ALLOCATOR_DESC& descriptor, - Microsoft::WRL::ComPtr residencyManager); - - void DeallocateMemory(std::unique_ptr allocation) override; - - Microsoft::WRL::ComPtr mDevice; - Microsoft::WRL::ComPtr mResidencyManager; - }; - -} // namespace gpgmm::d3d12 - -#endif // INCLUDE_MIN_GPGMM_D3D12_H_ diff --git a/src/include/min/gpgmm_d3d12.cpp b/src/mvi/gpgmm_d3d12_mvi.cpp similarity index 68% rename from src/include/min/gpgmm_d3d12.cpp rename to src/mvi/gpgmm_d3d12_mvi.cpp index 49f6b0cf9..e9b2563a3 100644 --- a/src/include/min/gpgmm_d3d12.cpp +++ b/src/mvi/gpgmm_d3d12_mvi.cpp @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "gpgmm_d3d12.h" +#include "gpgmm_d3d12_mvi.h" #include @@ -74,9 +74,9 @@ namespace gpgmm::d3d12 { // static HRESULT Heap::CreateHeap(const HEAP_DESC& descriptor, - ResidencyManager* const pResidencyManager, + IResidencyManager* const pResidencyManager, CreateHeapFn&& createHeapFn, - Heap** ppHeapOut) { + IHeap** ppHeapOut) { Microsoft::WRL::ComPtr pageable; ReturnIfFailed(createHeapFn(&pageable)); @@ -87,25 +87,78 @@ namespace gpgmm::d3d12 { return S_OK; } - HRESULT STDMETHODCALLTYPE Heap::QueryInterface(REFIID riid, void** ppvObject) { - return mPageable->QueryInterface(riid, ppvObject); + Heap::Heap(Microsoft::WRL::ComPtr pageable, + const HEAP_DESC& descriptor, + bool isResidencyDisabled) + : MemoryBase(descriptor.SizeInBytes, descriptor.Alignment), mPageable(std::move(pageable)) { } HEAP_INFO Heap::GetInfo() const { return {}; } - Heap::Heap(Microsoft::WRL::ComPtr pageable, - const HEAP_DESC& descriptor, - bool isResidencyDisabled) - : MemoryBase(descriptor.SizeInBytes, descriptor.Alignment), mPageable(std::move(pageable)) { + bool Heap::IsInResidencyLRUCacheForTesting() const { + return false; + } + + bool Heap::IsResidencyLockedForTesting() const { + return false; + } + + HRESULT STDMETHODCALLTYPE Heap::QueryInterface(REFIID riid, void** ppvObject) { + return mPageable->QueryInterface(riid, ppvObject); + } + + ULONG STDMETHODCALLTYPE Heap::AddRef() { + return IUnknownImpl::AddRef(); + } + + ULONG STDMETHODCALLTYPE Heap::Release() { + return IUnknownImpl::Release(); + } + + uint64_t Heap::GetSize() const { + return MemoryBase::GetSize(); + } + + uint64_t Heap::GetAlignment() const { + return MemoryBase::GetAlignment(); + } + + void Heap::AddSubAllocationRef() { + } + + bool Heap::RemoveSubAllocationRef() { + return true; + } + + LPCWSTR Heap::GetDebugName() const { + return nullptr; + } + + HRESULT Heap::SetDebugName(LPCWSTR Name) { + return E_NOTIMPL; + } + + IMemoryPool* Heap::GetPool() const { + return nullptr; + } + + void Heap::SetPool(IMemoryPool* pool) { } // ResidencyList + HRESULT CreateResidencyList(IResidencyList** ppResidencyList) { + if (ppResidencyList != nullptr) { + *ppResidencyList = new ResidencyList(); + } + return S_OK; + } + ResidencyList::ResidencyList() = default; - HRESULT ResidencyList::Add(Heap* pHeap) { + HRESULT ResidencyList::Add(IHeap* pHeap) { return S_OK; } @@ -113,11 +166,28 @@ namespace gpgmm::d3d12 { return S_OK; } + HRESULT STDMETHODCALLTYPE ResidencyList::QueryInterface(REFIID riid, void** ppvObject) { + return IUnknownImpl::QueryInterface(riid, ppvObject); + } + + ULONG STDMETHODCALLTYPE ResidencyList::AddRef() { + return IUnknownImpl::AddRef(); + } + + ULONG STDMETHODCALLTYPE ResidencyList::Release() { + return IUnknownImpl::Release(); + } + // ResidencyManager + HRESULT CreateResidencyManager(const RESIDENCY_DESC& descriptor, + IResidencyManager** ppResidencyManagerOut) { + return ResidencyManager::CreateResidencyManager(descriptor, ppResidencyManagerOut); + } + // static HRESULT ResidencyManager::CreateResidencyManager(const RESIDENCY_DESC& descriptor, - ResidencyManager** ppResidencyManagerOut) { + IResidencyManager** ppResidencyManagerOut) { if (ppResidencyManagerOut != nullptr) { *ppResidencyManagerOut = new ResidencyManager(descriptor); } @@ -127,17 +197,17 @@ namespace gpgmm::d3d12 { ResidencyManager::~ResidencyManager() = default; - HRESULT ResidencyManager::LockHeap(Heap* pHeap) { + HRESULT ResidencyManager::LockHeap(IHeap* pHeap) { return S_OK; } - HRESULT ResidencyManager::UnlockHeap(Heap* pHeap) { + HRESULT ResidencyManager::UnlockHeap(IHeap* pHeap) { return S_OK; } HRESULT ResidencyManager::ExecuteCommandLists(ID3D12CommandQueue* pQueue, ID3D12CommandList* const* ppCommandLists, - ResidencyList* const* ppResidencyLists, + IResidencyList* const* ppResidencyLists, uint32_t count) { pQueue->ExecuteCommandLists(count, ppCommandLists); return S_OK; @@ -164,6 +234,18 @@ namespace gpgmm::d3d12 { : mDevice(std::move(descriptor.Device)), mAdapter(std::move(descriptor.Adapter)) { } + HRESULT STDMETHODCALLTYPE ResidencyManager::QueryInterface(REFIID riid, void** ppvObject) { + return IUnknownImpl::QueryInterface(riid, ppvObject); + } + + ULONG STDMETHODCALLTYPE ResidencyManager::AddRef() { + return IUnknownImpl::AddRef(); + } + + ULONG STDMETHODCALLTYPE ResidencyManager::Release() { + return IUnknownImpl::Release(); + } + // ResourceAllocation void ResourceAllocation::DeleteThis() { @@ -196,29 +278,55 @@ namespace gpgmm::d3d12 { return {GetSize(), GetAlignment()}; } - Heap* ResourceAllocation::GetMemory() const { + IHeap* ResourceAllocation::GetMemory() const { return static_cast(MemoryAllocation::GetMemory()); } ResourceAllocation::ResourceAllocation(const RESOURCE_ALLOCATION_DESC& desc, MemoryAllocator* allocator, - Heap* resourceHeap, + IHeap* resourceHeap, Microsoft::WRL::ComPtr resource) - : MemoryAllocation(allocator, resourceHeap, desc.SizeInBytes), - mResource(std::move(resource)) { + : MemoryAllocation(allocator, resourceHeap), mResource(std::move(resource)) { + } + + HRESULT STDMETHODCALLTYPE ResourceAllocation::QueryInterface(REFIID riid, void** ppvObject) { + return IUnknownImpl::QueryInterface(riid, ppvObject); + } + + ULONG STDMETHODCALLTYPE ResourceAllocation::AddRef() { + return IUnknownImpl::AddRef(); + } + + ULONG STDMETHODCALLTYPE ResourceAllocation::Release() { + return IUnknownImpl::Release(); + } + + LPCWSTR ResourceAllocation::GetDebugName() const { + return nullptr; + } + + HRESULT ResourceAllocation::SetDebugName(LPCWSTR Name) { + return E_NOTIMPL; } // ResourceAllocator + HRESULT CreateResourceAllocator(const ALLOCATOR_DESC& allocatorDescriptor, + IResourceAllocator** ppResourceAllocatorOut, + IResidencyManager** ppResidencyManagerOut) { + return ResourceAllocator::CreateResourceAllocator( + allocatorDescriptor, ppResourceAllocatorOut, ppResidencyManagerOut); + } + // static HRESULT ResourceAllocator::CreateResourceAllocator(const ALLOCATOR_DESC& allocatorDescriptor, - ResourceAllocator** ppResourceAllocatorOut, - ResidencyManager** ppResidencyManagerOut) { + IResourceAllocator** ppResourceAllocatorOut, + IResidencyManager** ppResidencyManagerOut) { if (allocatorDescriptor.Device == nullptr || allocatorDescriptor.Adapter == nullptr) { return E_INVALIDARG; } - Microsoft::WRL::ComPtr residencyManager; + Microsoft::WRL::ComPtr residencyManager; if (ppResidencyManagerOut != nullptr) { RESIDENCY_DESC residencyDesc = {}; residencyDesc.Device = allocatorDescriptor.Device; @@ -234,7 +342,7 @@ namespace gpgmm::d3d12 { ResidencyManager::CreateResidencyManager(residencyDesc, &residencyManager)); } - Microsoft::WRL::ComPtr resourceAllocator; + Microsoft::WRL::ComPtr resourceAllocator; ReturnIfFailed(CreateResourceAllocator(allocatorDescriptor, residencyManager.Get(), &resourceAllocator)); @@ -250,9 +358,10 @@ namespace gpgmm::d3d12 { } // static - HRESULT ResourceAllocator::CreateResourceAllocator(const ALLOCATOR_DESC& allocatorDescriptor, - ResidencyManager* pResidencyManager, - ResourceAllocator** ppResourceAllocatorOut) { + HRESULT ResourceAllocator::CreateResourceAllocator( + const ALLOCATOR_DESC& allocatorDescriptor, + IResidencyManager* pResidencyManager, + IResourceAllocator** ppResourceAllocatorOut) { if (ppResourceAllocatorOut != nullptr) { *ppResourceAllocatorOut = new ResourceAllocator(allocatorDescriptor, pResidencyManager); } @@ -264,8 +373,8 @@ namespace gpgmm::d3d12 { const D3D12_RESOURCE_DESC& resourceDescriptor, D3D12_RESOURCE_STATES initialResourceState, const D3D12_CLEAR_VALUE* pClearValue, - ResourceAllocation** ppResourceAllocationOut) { - Heap* resourceHeap = nullptr; + IResourceAllocation** ppResourceAllocationOut) { + IHeap* resourceHeap = nullptr; Microsoft::WRL::ComPtr committedResource; const D3D12_RESOURCE_ALLOCATION_INFO resourceInfo = @@ -310,7 +419,7 @@ namespace gpgmm::d3d12 { HRESULT ResourceAllocator::CreateResource( Microsoft::WRL::ComPtr committedResource, - ResourceAllocation** ppResourceAllocationOut) { + IResourceAllocation** ppResourceAllocationOut) { return E_NOTIMPL; } @@ -329,8 +438,8 @@ namespace gpgmm::d3d12 { } ResourceAllocator::ResourceAllocator(const ALLOCATOR_DESC& descriptor, - Microsoft::WRL::ComPtr residencyManager) - : mDevice(std::move(descriptor.Device)), mResidencyManager(std::move(residencyManager)) { + IResidencyManager* pResidencyManager) + : mDevice(std::move(descriptor.Device)), mResidencyManager(pResidencyManager) { } void ResourceAllocator::DeallocateMemory(std::unique_ptr allocation) { @@ -341,4 +450,16 @@ namespace gpgmm::d3d12 { delete allocation->GetMemory(); } + HRESULT STDMETHODCALLTYPE ResourceAllocator::QueryInterface(REFIID riid, void** ppvObject) { + return IUnknownImpl::QueryInterface(riid, ppvObject); + } + + ULONG STDMETHODCALLTYPE ResourceAllocator::AddRef() { + return IUnknownImpl::AddRef(); + } + + ULONG STDMETHODCALLTYPE ResourceAllocator::Release() { + return IUnknownImpl::Release(); + } + } // namespace gpgmm::d3d12 diff --git a/src/mvi/gpgmm_d3d12_mvi.h b/src/mvi/gpgmm_d3d12_mvi.h new file mode 100644 index 000000000..b89193b1c --- /dev/null +++ b/src/mvi/gpgmm_d3d12_mvi.h @@ -0,0 +1,227 @@ +// Copyright 2022 The GPGMM Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef INCLUDE_GPGMM_D3D12_MVI_H_ +#define INCLUDE_GPGMM_D3D12_MVI_H_ + +// GPGMM minimum viable implementation (MVI). +// +// GPGMM MVI allows users to leverage GPGMM's portable GMM interface without +// requiring to build the full GPGMM implementation for incremental enabling during +// development. +// +// GPGMM MVI specifically, +// * Is not thread-safe. +// * Is functionally-equivelent to calling ID3D12Device::CreateCommittedResource. +// * Does not perform residency management or call ID3D12Device::MakeResident. +// * GMM functionality will otherwise "no-op" or pass-through. +// +// User should decide to define the following macros: +// - GPGMM_REFCOUNT_TYPE : Allows a user-defined ref-count type to be used instead of +// the STL-provided one. The increment, decrement, and equals operator must be defined. +#if !defined(GPGMM_REFCOUNT_TYPE) +# include +#endif + +#ifndef GPGMM_REFCOUNT_TYPE +# define GPGMM_REFCOUNT_TYPE std::atomic +#endif + +#include + +#include "gpgmm_mvi.h" + +namespace gpgmm::d3d12 { + + class IUnknownImpl : public IUnknown { + public: + IUnknownImpl(); + virtual ~IUnknownImpl(); + + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject) override; + ULONG STDMETHODCALLTYPE AddRef() override; + ULONG STDMETHODCALLTYPE Release() override; + + protected: + virtual void DeleteThis(); + + private: + GPGMM_REFCOUNT_TYPE mRefCount; + }; + + class Heap final : public MemoryBase, public IUnknownImpl, public IHeap { + public: + static HRESULT CreateHeap(const HEAP_DESC& descriptor, + IResidencyManager* const pResidencyManager, + CreateHeapFn&& createHeapFn, + IHeap** ppHeapOut); + + // IHeap interface + HEAP_INFO GetInfo() const override; + bool IsInResidencyLRUCacheForTesting() const override; + bool IsResidencyLockedForTesting() const override; + + // IMemoryObject + uint64_t GetSize() const override; + uint64_t GetAlignment() const override; + void AddSubAllocationRef() override; + bool RemoveSubAllocationRef() override; + IMemoryPool* GetPool() const override; + void SetPool(IMemoryPool* pool) override; + + // IUnknown interface + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject) override; + ULONG STDMETHODCALLTYPE AddRef() override; + ULONG STDMETHODCALLTYPE Release() override; + + // IDebugObject interface + LPCWSTR GetDebugName() const override; + HRESULT SetDebugName(LPCWSTR Name) override; + + private: + Heap(Microsoft::WRL::ComPtr pageable, + const HEAP_DESC& descriptor, + bool isResidencyDisabled); + + Microsoft::WRL::ComPtr mPageable; + }; + + class ResidencyList final : public IResidencyList, public IUnknownImpl { + public: + ResidencyList(); + + HRESULT Add(IHeap* pHeap) override; + HRESULT Reset() override; + + // IUnknown interface + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject) override; + ULONG STDMETHODCALLTYPE AddRef() override; + ULONG STDMETHODCALLTYPE Release() override; + }; + + class ResidencyManager final : public IUnknownImpl, public IResidencyManager { + public: + static HRESULT CreateResidencyManager(const RESIDENCY_DESC& descriptor, + IResidencyManager** ppResidencyManagerOut); + + ~ResidencyManager() override; + + // IResidencyManager interface + HRESULT LockHeap(IHeap* pHeap) override; + HRESULT UnlockHeap(IHeap* pHeap) override; + HRESULT ExecuteCommandLists(ID3D12CommandQueue* pQueue, + ID3D12CommandList* const* ppCommandLists, + IResidencyList* const* ppResidencyLists, + uint32_t count) override; + HRESULT SetVideoMemoryReservation(const DXGI_MEMORY_SEGMENT_GROUP& memorySegmentGroup, + uint64_t availableForReservation, + uint64_t* pCurrentReservationOut = nullptr) override; + HRESULT QueryVideoMemoryInfo(const DXGI_MEMORY_SEGMENT_GROUP& memorySegmentGroup, + DXGI_QUERY_VIDEO_MEMORY_INFO* pVideoMemoryInfoOut) override; + RESIDENCY_INFO GetInfo() const override; + + // IUnknown interface + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject) override; + ULONG STDMETHODCALLTYPE AddRef() override; + ULONG STDMETHODCALLTYPE Release() override; + + private: + ResidencyManager(const RESIDENCY_DESC& descriptor); + + Microsoft::WRL::ComPtr mDevice; + Microsoft::WRL::ComPtr mAdapter; + }; + + class ResourceAllocator; + + class ResourceAllocation final : public MemoryAllocation, + public IUnknownImpl, + public IResourceAllocation { + public: + // IResourceAllocation interface + HRESULT Map(uint32_t subresource = 0, + const D3D12_RANGE* pReadRange = nullptr, + void** ppDataOut = nullptr) override; + void Unmap(uint32_t subresource = 0, const D3D12_RANGE* pWrittenRange = nullptr) override; + ID3D12Resource* GetResource() const override; + D3D12_GPU_VIRTUAL_ADDRESS GetGPUVirtualAddress() const override; + uint64_t GetOffsetFromResource() const override; + RESOURCE_ALLOCATION_INFO GetInfo() const override; + IHeap* GetMemory() const override; + + // IUnknown interface + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject) override; + ULONG STDMETHODCALLTYPE AddRef() override; + ULONG STDMETHODCALLTYPE Release() override; + + // IDebugObject interface + LPCWSTR GetDebugName() const override; + HRESULT SetDebugName(LPCWSTR Name) override; + + private: + friend ResourceAllocator; + + ResourceAllocation(const RESOURCE_ALLOCATION_DESC& desc, + MemoryAllocator* allocator, + IHeap* resourceHeap, + Microsoft::WRL::ComPtr resource); + + void DeleteThis() override; + + Microsoft::WRL::ComPtr mResource; + }; + + class ResourceAllocator final : public MemoryAllocator, + public IUnknownImpl, + public IResourceAllocator { + public: + static HRESULT CreateResourceAllocator(const ALLOCATOR_DESC& allocatorDescriptor, + IResourceAllocator** ppResourceAllocatorOut, + IResidencyManager** ppResidencyManagerOut); + + static HRESULT CreateResourceAllocator(const ALLOCATOR_DESC& allocatorDescriptor, + IResidencyManager* pResidencyManager, + IResourceAllocator** ppResourceAllocatorOut); + + // IResourceAllocator interface + HRESULT CreateResource(const ALLOCATION_DESC& allocationDescriptor, + const D3D12_RESOURCE_DESC& resourceDescriptor, + D3D12_RESOURCE_STATES initialResourceState, + const D3D12_CLEAR_VALUE* pClearValue, + IResourceAllocation** ppResourceAllocationOut) override; + HRESULT CreateResource(Microsoft::WRL::ComPtr committedResource, + IResourceAllocation** ppResourceAllocationOut) override; + uint64_t ReleaseMemory(uint64_t bytesToRelease) override; + RESOURCE_ALLOCATOR_INFO GetInfo() const override; + HRESULT CheckFeatureSupport(ALLOCATOR_FEATURE feature, + void* pFeatureSupportData, + uint32_t featureSupportDataSize) const override; + + // IUnknown interface + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject) override; + ULONG STDMETHODCALLTYPE AddRef() override; + ULONG STDMETHODCALLTYPE Release() override; + + private: + ResourceAllocator(const ALLOCATOR_DESC& descriptor, IResidencyManager* pResidencyManager); + + void DeallocateMemory(std::unique_ptr allocation) override; + + Microsoft::WRL::ComPtr mDevice; + Microsoft::WRL::ComPtr mResidencyManager; + }; + +} // namespace gpgmm::d3d12 + +#endif // INCLUDE_GPGMM_D3D12_MVI_H_ diff --git a/src/include/min/gpgmm.cpp b/src/mvi/gpgmm_mvi.cpp similarity index 52% rename from src/include/min/gpgmm.cpp rename to src/mvi/gpgmm_mvi.cpp index 8d6fa5380..24dba73ff 100644 --- a/src/include/min/gpgmm.cpp +++ b/src/mvi/gpgmm_mvi.cpp @@ -12,19 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "gpgmm.h" +#include "gpgmm_mvi.h" namespace gpgmm { - static constexpr uint64_t kInvalidSize = std::numeric_limits::max(); - // MemoryBase MemoryBase::MemoryBase(uint64_t size, uint64_t alignment) : mSize(size), mAlignment(alignment) { } - MemoryBase::~MemoryBase() = default; - uint64_t MemoryBase::GetSize() const { return mSize; } @@ -40,42 +36,19 @@ namespace gpgmm { } MemoryAllocatorInfo MemoryAllocator::GetInfo() const { - return {}; + return mInfo; } // MemoryAllocation - MemoryAllocation::MemoryAllocation(MemoryAllocator* allocator, - MemoryBase* memory, - uint64_t requestSize) + MemoryAllocation::MemoryAllocation(MemoryAllocator* allocator, IMemoryObject* memory) : mAllocator(allocator), mMemory(memory) { } - MemoryAllocation::~MemoryAllocation() = default; - - MemoryAllocation::MemoryAllocation(const MemoryAllocation&) = default; - MemoryAllocation& MemoryAllocation::operator=(const MemoryAllocation&) = default; - - bool MemoryAllocation::operator==(const MemoryAllocation& other) const { - return (other.mAllocator == mAllocator && other.mMemory == mMemory); - } - - bool MemoryAllocation::operator!=(const MemoryAllocation& other) const { - return !operator==(other); - } - - MemoryAllocationInfo MemoryAllocation::GetInfo() const { - return {GetSize(), GetAlignment()}; - } - - MemoryBase* MemoryAllocation::GetMemory() const { + IMemoryObject* MemoryAllocation::GetMemory() const { return mMemory; } - uint8_t* MemoryAllocation::GetMappedPointer() const { - return nullptr; - } - MemoryAllocator* MemoryAllocation::GetAllocator() const { return mAllocator; } @@ -84,24 +57,8 @@ namespace gpgmm { return mMemory->GetSize(); } - uint64_t MemoryAllocation::GetRequestSize() const { - return kInvalidSize; - } - uint64_t MemoryAllocation::GetAlignment() const { return mMemory->GetAlignment(); } - uint64_t MemoryAllocation::GetOffset() const { - return 0; - } - - AllocationMethod MemoryAllocation::GetMethod() const { - return AllocationMethod::kStandalone; - } - - MemoryBlock* MemoryAllocation::GetBlock() const { - return nullptr; - } - } // namespace gpgmm diff --git a/src/mvi/gpgmm_mvi.h b/src/mvi/gpgmm_mvi.h new file mode 100644 index 000000000..c90a82d87 --- /dev/null +++ b/src/mvi/gpgmm_mvi.h @@ -0,0 +1,66 @@ +// Copyright 2022 The GPGMM Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef INCLUDE_GPGMM_MVI_H_ +#define INCLUDE_GPGMM_MVI_H_ + +#include + +#include + +namespace gpgmm { + + class MemoryBase { + public: + MemoryBase(uint64_t size, uint64_t alignment); + + uint64_t GetSize() const; + uint64_t GetAlignment() const; + + private: + const uint64_t mSize; + const uint64_t mAlignment; + }; + + class MemoryAllocation; + + class MemoryAllocator { + public: + virtual void DeallocateMemory(std::unique_ptr allocation) = 0; + virtual uint64_t ReleaseMemory(uint64_t bytesToRelease); + virtual MemoryAllocatorInfo GetInfo() const; + + protected: + MemoryAllocatorInfo mInfo = {}; + }; + + class MemoryAllocation { + public: + MemoryAllocation(MemoryAllocator* allocator, IMemoryObject* memory); + + MemoryAllocator* GetAllocator() const; + uint64_t GetSize() const; + uint64_t GetAlignment() const; + IMemoryObject* GetMemory() const; + + protected: + MemoryAllocator* mAllocator; + + private: + IMemoryObject* mMemory; + }; + +} // namespace gpgmm + +#endif // INCLUDE_GPGMM_MVI_H_ diff --git a/src/samples/BUILD.gn b/src/samples/BUILD.gn index 7ecf0f39e..a73d8a621 100644 --- a/src/samples/BUILD.gn +++ b/src/samples/BUILD.gn @@ -21,7 +21,7 @@ group("samples") { if (gpgmm_enable_d3d12) { deps = [ ":D3D12Sample", - ":D3D12SampleMin", + ":D3D12SampleMVI", ] } } @@ -35,20 +35,21 @@ executable("D3D12Sample") { ] } -source_set("min_gpgmm") { +source_set("gpgmm_mvi") { sources = [ - "${gpgmm_root_dir}/src/include/min/gpgmm.cpp", - "${gpgmm_root_dir}/src/include/min/gpgmm.h", - "${gpgmm_root_dir}/src/include/min/gpgmm_d3d12.cpp", - "${gpgmm_root_dir}/src/include/min/gpgmm_d3d12.h", + "${gpgmm_root_dir}/src/mvi/gpgmm_d3d12_mvi.cpp", + "${gpgmm_root_dir}/src/mvi/gpgmm_d3d12_mvi.h", + "${gpgmm_root_dir}/src/mvi/gpgmm_mvi.cpp", + "${gpgmm_root_dir}/src/mvi/gpgmm_mvi.h", ] + include_dirs = [ "${gpgmm_root_dir}/src/include" ] } -executable("D3D12SampleMin") { - deps = [ ":min_gpgmm" ] +executable("D3D12SampleMVI") { + deps = [ ":gpgmm_mvi" ] defines = [ "GPGMM_D3D12_HEADERS_ALREADY_INCLUDED" ] sources = [ "D3D12Sample.cpp" ] - include_dirs = [ "${gpgmm_root_dir}/src/include/min" ] + include_dirs = [ "${gpgmm_root_dir}/src/include" ] libs = [ "d3d12.lib", "dxgi.lib", diff --git a/src/samples/D3D12Sample.cpp b/src/samples/D3D12Sample.cpp index e26f00d74..db6600e8f 100644 --- a/src/samples/D3D12Sample.cpp +++ b/src/samples/D3D12Sample.cpp @@ -61,8 +61,8 @@ HRESULT Init() { desc.Device = device; desc.ResourceHeapTier = options.ResourceHeapTier; - Microsoft::WRL::ComPtr resourceAllocator; - hr = gpgmm::d3d12::ResourceAllocator::CreateResourceAllocator(desc, &resourceAllocator); + Microsoft::WRL::ComPtr resourceAllocator; + hr = gpgmm::d3d12::CreateResourceAllocator(desc, &resourceAllocator, nullptr); if (FAILED(hr)) { return hr; } diff --git a/src/tests/D3D12Test.h b/src/tests/D3D12Test.h index 8fa194d94..5bb07c46c 100644 --- a/src/tests/D3D12Test.h +++ b/src/tests/D3D12Test.h @@ -35,8 +35,6 @@ namespace gpgmm::d3d12 { struct RESIDENCY_DESC; class Caps; - class ResourceAllocator; - class ResourceAllocation; D3D12_MESSAGE_SEVERITY GetMessageSeverity(LogSeverity logSeverity); diff --git a/src/tests/DummyMemoryAllocator.h b/src/tests/DummyMemoryAllocator.h index eb4878446..5aa4e35a5 100644 --- a/src/tests/DummyMemoryAllocator.h +++ b/src/tests/DummyMemoryAllocator.h @@ -21,6 +21,37 @@ namespace gpgmm { + class DummyMemory : public IMemoryObject, public MemoryBase { + public: + DummyMemory(uint64_t size, uint64_t alignment) : MemoryBase(size, alignment) { + } + + // IMemoryObject + uint64_t GetSize() const override { + return MemoryBase::GetSize(); + } + + uint64_t GetAlignment() const override { + return MemoryBase::GetAlignment(); + } + + void AddSubAllocationRef() override { + return MemoryBase::AddSubAllocationRef(); + } + + bool RemoveSubAllocationRef() override { + return MemoryBase::RemoveSubAllocationRef(); + } + + IMemoryPool* GetPool() const override { + return MemoryBase::GetPool(); + } + + void SetPool(IMemoryPool* pool) override { + return MemoryBase::SetPool(pool); + } + }; + class DummyMemoryAllocator : public MemoryAllocator { public: DummyMemoryAllocator() = default; @@ -43,7 +74,7 @@ namespace gpgmm { mInfo.UsedMemoryUsage += request.SizeInBytes; return std::make_unique( - this, new MemoryBase(request.SizeInBytes, request.Alignment), request.SizeInBytes); + this, new DummyMemory(request.SizeInBytes, request.Alignment), request.SizeInBytes); } void DeallocateMemory(std::unique_ptr allocation) override { diff --git a/src/tests/capture_replay_tests/D3D12EventTraceReplay.cpp b/src/tests/capture_replay_tests/D3D12EventTraceReplay.cpp index 5b68d2226..bfa8d7737 100644 --- a/src/tests/capture_replay_tests/D3D12EventTraceReplay.cpp +++ b/src/tests/capture_replay_tests/D3D12EventTraceReplay.cpp @@ -156,18 +156,18 @@ class D3D12EventTraceReplay : public D3D12TestBase, public CaptureReplayTestWith struct PlaybackExecutionContext { using InstanceID = std::string; - ComPtr CurrentAllocationWithoutID; - ComPtr CurrentHeapWithoutID; + ComPtr CurrentAllocationWithoutID; + ComPtr CurrentHeapWithoutID; - std::unordered_map> CreatedAllocatorsToID; - std::unordered_map> CreatedResidencyManagersToID; - std::unordered_map> CreatedAllocationsToID; - std::unordered_map> CreatedHeapsToID; + std::unordered_map> CreatedAllocatorsToID; + std::unordered_map> CreatedResidencyManagersToID; + std::unordered_map> CreatedAllocationsToID; + std::unordered_map> CreatedHeapsToID; InstanceID currentAllocatorID; InstanceID currentResidencyID; - std::vector currentResidencyLists; + std::vector> currentResidencyLists; }; void RunTest(const TraceFile& traceFile, @@ -221,22 +221,23 @@ class D3D12EventTraceReplay : public D3D12TestBase, public CaptureReplayTestWith } // Create ResidencyLists. - std::vector residencyListPtrs; + std::vector residencyListPtrs; for (auto& setJson : args["ResidencyLists"]) { - ResidencyList list = {}; + ComPtr list; + ASSERT_SUCCEEDED(CreateResidencyList(&list)); for (auto heap : setJson["Heaps"]) { const std::string heapId = heap["id_ref"].asString(); if (playbackContext.CreatedHeapsToID.find(heapId) == playbackContext.CreatedHeapsToID.end()) { break; } - list.Add(playbackContext.CreatedHeapsToID[heapId].Get()); + list->Add(playbackContext.CreatedHeapsToID[heapId].Get()); } - residencyListPtrs.push_back(&list); + residencyListPtrs.push_back(list.Get()); playbackContext.currentResidencyLists.push_back(std::move(list)); } - ResidencyManager* residencyManager = + IResidencyManager* residencyManager = playbackContext .CreatedResidencyManagersToID[playbackContext.currentResidencyID] .Get(); @@ -300,7 +301,7 @@ class D3D12EventTraceReplay : public D3D12TestBase, public CaptureReplayTestWith playbackContext.currentAllocatorID); ASSERT_TRUE(it != playbackContext.CreatedAllocatorsToID.end()); - ResourceAllocator* resourceAllocator = + IResourceAllocator* resourceAllocator = playbackContext .CreatedAllocatorsToID[playbackContext.currentAllocatorID] .Get(); @@ -396,9 +397,9 @@ class D3D12EventTraceReplay : public D3D12TestBase, public CaptureReplayTestWith newResidencyDesc = ConvertAndApplyToResidencyDesc(snapshot, newResidencyDesc); - ComPtr residencyManager; - ASSERT_SUCCEEDED(ResidencyManager::CreateResidencyManager( - newResidencyDesc, &residencyManager)); + ComPtr residencyManager; + ASSERT_SUCCEEDED( + CreateResidencyManager(newResidencyDesc, &residencyManager)); ASSERT_TRUE(playbackContext.CreatedResidencyManagersToID .insert({residencyManagerID, std::move(residencyManager)}) @@ -471,7 +472,7 @@ class D3D12EventTraceReplay : public D3D12TestBase, public CaptureReplayTestWith 0.125; // 1/8th of 4MB } - ComPtr residencyManager; + ComPtr residencyManager; if (playbackContext.CreatedResidencyManagersToID.find( playbackContext.currentResidencyID) != playbackContext.CreatedResidencyManagersToID.end()) { @@ -479,8 +480,8 @@ class D3D12EventTraceReplay : public D3D12TestBase, public CaptureReplayTestWith [playbackContext.currentResidencyID]; } - ComPtr resourceAllocator; - ASSERT_SUCCEEDED(ResourceAllocator::CreateResourceAllocator( + ComPtr resourceAllocator; + ASSERT_SUCCEEDED(CreateResourceAllocator( allocatorDescOfProfile, residencyManager.Get(), &resourceAllocator)); ASSERT_TRUE(playbackContext.CreatedAllocatorsToID @@ -532,14 +533,14 @@ class D3D12EventTraceReplay : public D3D12TestBase, public CaptureReplayTestWith resourceHeapDesc = ConvertAndApplyToHeapDesc(args["Heap"], resourceHeapDesc); - ResidencyManager* residencyManager = + IResidencyManager* residencyManager = playbackContext .CreatedResidencyManagersToID[playbackContext.currentResidencyID] .Get(); ASSERT_NE(residencyManager, nullptr); - ComPtr resourceHeap; - ASSERT_SUCCEEDED(Heap::CreateHeap( + ComPtr resourceHeap; + ASSERT_SUCCEEDED(CreateHeap( resourceHeapDesc, residencyManager, [&](ID3D12Pageable** ppPageableOut) -> HRESULT { D3D12_HEAP_DESC heapDesc = {}; @@ -592,7 +593,7 @@ class D3D12EventTraceReplay : public D3D12TestBase, public CaptureReplayTestWith continue; } - Heap* heap = it->second.Get(); + IHeap* heap = it->second.Get(); ASSERT_NE(heap, nullptr); mCapturedMemoryStats.CurrentUsage -= heap->GetSize(); diff --git a/src/tests/end2end/D3D12ResidencyManagerTests.cpp b/src/tests/end2end/D3D12ResidencyManagerTests.cpp index 90bd028a4..0c641c1c5 100644 --- a/src/tests/end2end/D3D12ResidencyManagerTests.cpp +++ b/src/tests/end2end/D3D12ResidencyManagerTests.cpp @@ -68,7 +68,7 @@ class D3D12ResidencyManagerTests : public D3D12TestBase, public ::testing::Test return residencyDesc; } - uint64_t GetBudgetLeft(ResidencyManager* residencyManager, + uint64_t GetBudgetLeft(IResidencyManager* residencyManager, const DXGI_MEMORY_SEGMENT_GROUP& memorySegmentGroup) { DXGI_QUERY_VIDEO_MEMORY_INFO segment = {}; residencyManager->QueryVideoMemoryInfo(memorySegmentGroup, &segment); @@ -81,9 +81,9 @@ TEST_F(D3D12ResidencyManagerTests, CreateResourceHeapNotResident) { // Adapters that do not support creating heaps will ignore D3D12_HEAP_FLAG_CREATE_NOT_RESIDENT. GPGMM_SKIP_TEST_IF(!mCaps->IsCreateHeapNotResidentSupported()); - ComPtr residencyManager; - ASSERT_SUCCEEDED(ResidencyManager::CreateResidencyManager( - CreateBasicResidencyDesc(kDefaultBudget), &residencyManager)); + ComPtr residencyManager; + ASSERT_SUCCEEDED( + CreateResidencyManager(CreateBasicResidencyDesc(kDefaultBudget), &residencyManager)); constexpr uint64_t kHeapSize = GPGMM_MB_TO_BYTES(10); @@ -113,14 +113,14 @@ TEST_F(D3D12ResidencyManagerTests, CreateResourceHeapNotResident) { return S_OK; }; - ASSERT_FAILED(Heap::CreateHeap(resourceHeapAlwaysInBudgetDesc, residencyManager.Get(), - createHeapNotResidentFn, nullptr)); + ASSERT_FAILED(CreateHeap(resourceHeapAlwaysInBudgetDesc, residencyManager.Get(), + createHeapNotResidentFn, nullptr)); } TEST_F(D3D12ResidencyManagerTests, CreateResourceHeap) { - ComPtr residencyManager; - ASSERT_SUCCEEDED(ResidencyManager::CreateResidencyManager( - CreateBasicResidencyDesc(kDefaultBudget), &residencyManager)); + ComPtr residencyManager; + ASSERT_SUCCEEDED( + CreateResidencyManager(CreateBasicResidencyDesc(kDefaultBudget), &residencyManager)); constexpr uint64_t kHeapSize = GPGMM_MB_TO_BYTES(10); @@ -152,15 +152,13 @@ TEST_F(D3D12ResidencyManagerTests, CreateResourceHeap) { resourceHeapDesc.SizeInBytes = kHeapSize; resourceHeapDesc.MemorySegmentGroup = DXGI_MEMORY_SEGMENT_GROUP_LOCAL; - ASSERT_FAILED( - Heap::CreateHeap(resourceHeapDesc, residencyManager.Get(), badCreateHeapFn, nullptr)); + ASSERT_FAILED(CreateHeap(resourceHeapDesc, residencyManager.Get(), badCreateHeapFn, nullptr)); - ASSERT_SUCCEEDED( - Heap::CreateHeap(resourceHeapDesc, residencyManager.Get(), createHeapFn, nullptr)); + ASSERT_SUCCEEDED(CreateHeap(resourceHeapDesc, residencyManager.Get(), createHeapFn, nullptr)); // Create a resource heap without residency. - ComPtr resourceHeap; - ASSERT_SUCCEEDED(Heap::CreateHeap(resourceHeapDesc, nullptr, createHeapFn, &resourceHeap)); + ComPtr resourceHeap; + ASSERT_SUCCEEDED(CreateHeap(resourceHeapDesc, nullptr, createHeapFn, &resourceHeap)); // Ensure the unmanaged resource heap state is always unknown. Even though D3D12 implicitly // creates heaps as resident, untrack resource heaps would never transition out from @@ -170,7 +168,7 @@ TEST_F(D3D12ResidencyManagerTests, CreateResourceHeap) { // Create a resource heap with residency. ASSERT_SUCCEEDED( - Heap::CreateHeap(resourceHeapDesc, residencyManager.Get(), createHeapFn, &resourceHeap)); + CreateHeap(resourceHeapDesc, residencyManager.Get(), createHeapFn, &resourceHeap)); ASSERT_NE(resourceHeap, nullptr); EXPECT_EQ(resourceHeap->GetInfo().Status, gpgmm::d3d12::RESIDENCY_STATUS_CURRENT_RESIDENT); @@ -205,9 +203,9 @@ TEST_F(D3D12ResidencyManagerTests, CreateResourceHeap) { } TEST_F(D3D12ResidencyManagerTests, CreateDescriptorHeap) { - ComPtr residencyManager; - ASSERT_SUCCEEDED(ResidencyManager::CreateResidencyManager( - CreateBasicResidencyDesc(kDefaultBudget), &residencyManager)); + ComPtr residencyManager; + ASSERT_SUCCEEDED( + CreateResidencyManager(CreateBasicResidencyDesc(kDefaultBudget), &residencyManager)); D3D12_DESCRIPTOR_HEAP_DESC heapDesc = {}; heapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV; @@ -228,9 +226,9 @@ TEST_F(D3D12ResidencyManagerTests, CreateDescriptorHeap) { return S_OK; }; - ComPtr descriptorHeap; - ASSERT_SUCCEEDED(Heap::CreateHeap(descriptorHeapDesc, residencyManager.Get(), createHeapFn, - &descriptorHeap)); + ComPtr descriptorHeap; + ASSERT_SUCCEEDED( + CreateHeap(descriptorHeapDesc, residencyManager.Get(), createHeapFn, &descriptorHeap)); EXPECT_EQ(descriptorHeap->GetInfo().Status, gpgmm::d3d12::RESIDENCY_STATUS_UNKNOWN); EXPECT_EQ(descriptorHeap->GetInfo().IsLocked, false); @@ -265,38 +263,46 @@ TEST_F(D3D12ResidencyManagerTests, CreateDescriptorHeap) { } TEST_F(D3D12ResidencyManagerTests, CreateResidencyList) { - ComPtr resourceAllocator; - ASSERT_SUCCEEDED(ResourceAllocator::CreateResourceAllocator(CreateBasicAllocatorDesc(), - &resourceAllocator, nullptr)); + ComPtr resourceAllocator; + ASSERT_SUCCEEDED( + CreateResourceAllocator(CreateBasicAllocatorDesc(), &resourceAllocator, nullptr)); ALLOCATION_DESC allocationDesc = {}; allocationDesc.HeapType = D3D12_HEAP_TYPE_DEFAULT; - ComPtr allocation; + ComPtr allocation; ASSERT_SUCCEEDED(resourceAllocator->CreateResource(allocationDesc, CreateBasicBufferDesc(1), D3D12_RESOURCE_STATE_COMMON, nullptr, &allocation)); // Inserting a non-existant heap should always fail { - ResidencyList list; - Heap* invalid = nullptr; - ASSERT_FAILED(list.Add(invalid)); + ComPtr list; + ASSERT_SUCCEEDED(CreateResidencyList(&list)); + IHeap* invalid = nullptr; + ASSERT_FAILED(list->Add(invalid)); } // Inserting from a valid allocation should always succeed. { - ResidencyList list; - ASSERT_SUCCEEDED(list.Add(allocation->GetMemory())); - ASSERT_SUCCEEDED(list.Add(allocation->GetMemory())); + ComPtr list; + ASSERT_SUCCEEDED(CreateResidencyList(&list)); + + ASSERT_SUCCEEDED(list->Add(allocation->GetMemory())); + ASSERT_SUCCEEDED(list->Add(allocation->GetMemory())); } // Re-inserting allocation between two sets should always succeed. { - ResidencyList listA; - ASSERT_SUCCEEDED(listA.Add(allocation->GetMemory())); - ResidencyList listB(listA); - EXPECT_EQ(listB.Add(allocation->GetMemory()), S_OK); + ComPtr listA; + ASSERT_SUCCEEDED(CreateResidencyList(&listA)); + + ASSERT_SUCCEEDED(listA->Add(allocation->GetMemory())); + + ComPtr listB; + ASSERT_SUCCEEDED(CreateResidencyList(&listB)); + + EXPECT_EQ(listB->Add(allocation->GetMemory()), S_OK); } } @@ -306,7 +312,7 @@ TEST_F(D3D12ResidencyManagerTests, CreateResidencyManager) { RESIDENCY_DESC residencyDesc = CreateBasicResidencyDesc(kDefaultBudget); residencyDesc.Adapter = nullptr; - ASSERT_FAILED(ResidencyManager::CreateResidencyManager(residencyDesc, nullptr)); + ASSERT_FAILED(CreateResidencyManager(residencyDesc, nullptr)); } // Create residency without device must always fail. @@ -314,35 +320,34 @@ TEST_F(D3D12ResidencyManagerTests, CreateResidencyManager) { RESIDENCY_DESC residencyDesc = CreateBasicResidencyDesc(kDefaultBudget); residencyDesc.Device = nullptr; - ASSERT_FAILED(ResidencyManager::CreateResidencyManager(residencyDesc, nullptr)); + ASSERT_FAILED(CreateResidencyManager(residencyDesc, nullptr)); } // Create residency alone. { - ComPtr residencyManager; - ASSERT_SUCCEEDED(ResidencyManager::CreateResidencyManager( - CreateBasicResidencyDesc(kDefaultBudget), nullptr)); + ComPtr residencyManager; + ASSERT_SUCCEEDED(CreateResidencyManager(CreateBasicResidencyDesc(kDefaultBudget), nullptr)); } // Create allocator with residency support, together. { - ComPtr residencyManager; - ComPtr resourceAllocator; - ASSERT_SUCCEEDED(ResourceAllocator::CreateResourceAllocator( - CreateBasicAllocatorDesc(), &resourceAllocator, &residencyManager)); + ComPtr residencyManager; + ComPtr resourceAllocator; + ASSERT_SUCCEEDED(CreateResourceAllocator(CreateBasicAllocatorDesc(), &resourceAllocator, + &residencyManager)); EXPECT_NE(resourceAllocator, nullptr); EXPECT_NE(residencyManager, nullptr); } // Create allocator with residency, seperately. { - ComPtr residencyManager; - ASSERT_SUCCEEDED(ResidencyManager::CreateResidencyManager( - CreateBasicResidencyDesc(kDefaultBudget), &residencyManager)); + ComPtr residencyManager; + ASSERT_SUCCEEDED( + CreateResidencyManager(CreateBasicResidencyDesc(kDefaultBudget), &residencyManager)); - ComPtr resourceAllocator; - ASSERT_SUCCEEDED(ResourceAllocator::CreateResourceAllocator( - CreateBasicAllocatorDesc(), residencyManager.Get(), &resourceAllocator)); + ComPtr resourceAllocator; + ASSERT_SUCCEEDED(CreateResourceAllocator(CreateBasicAllocatorDesc(), residencyManager.Get(), + &resourceAllocator)); EXPECT_NE(resourceAllocator, nullptr); EXPECT_NE(residencyManager, nullptr); } @@ -353,21 +358,19 @@ TEST_F(D3D12ResidencyManagerTests, CreateResidencyManagerNoLeak) { // Create allocator with residency support, together. { - ComPtr residencyManager; - ComPtr resourceAllocator; - ResourceAllocator::CreateResourceAllocator(CreateBasicAllocatorDesc(), &resourceAllocator, - &residencyManager); + ComPtr residencyManager; + ComPtr resourceAllocator; + CreateResourceAllocator(CreateBasicAllocatorDesc(), &resourceAllocator, &residencyManager); } // Create allocator with residency, seperately. { - ComPtr residencyManager; - ResidencyManager::CreateResidencyManager(CreateBasicResidencyDesc(kDefaultBudget), - &residencyManager); + ComPtr residencyManager; + CreateResidencyManager(CreateBasicResidencyDesc(kDefaultBudget), &residencyManager); - ComPtr resourceAllocator; - ResourceAllocator::CreateResourceAllocator(CreateBasicAllocatorDesc(), - residencyManager.Get(), &resourceAllocator); + ComPtr resourceAllocator; + CreateResourceAllocator(CreateBasicAllocatorDesc(), residencyManager.Get(), + &resourceAllocator); } GPGMM_TEST_MEMORY_LEAK_END(); @@ -378,12 +381,12 @@ TEST_F(D3D12ResidencyManagerTests, CreateResidencyManagerNoLeak) { TEST_F(D3D12ResidencyManagerTests, OverBudget) { RESIDENCY_DESC residencyDesc = CreateBasicResidencyDesc(kDefaultBudget); - ComPtr residencyManager; - ASSERT_SUCCEEDED(ResidencyManager::CreateResidencyManager(residencyDesc, &residencyManager)); + ComPtr residencyManager; + ASSERT_SUCCEEDED(CreateResidencyManager(residencyDesc, &residencyManager)); - ComPtr resourceAllocator; - ASSERT_SUCCEEDED(ResourceAllocator::CreateResourceAllocator( - CreateBasicAllocatorDesc(), residencyManager.Get(), &resourceAllocator)); + ComPtr resourceAllocator; + ASSERT_SUCCEEDED(CreateResourceAllocator(CreateBasicAllocatorDesc(), residencyManager.Get(), + &resourceAllocator)); constexpr uint64_t kBufferMemorySize = GPGMM_MB_TO_BYTES(1); const D3D12_RESOURCE_DESC bufferDesc = CreateBasicBufferDesc(kBufferMemorySize); @@ -392,9 +395,9 @@ TEST_F(D3D12ResidencyManagerTests, OverBudget) { bufferAllocationDesc.HeapType = D3D12_HEAP_TYPE_DEFAULT; // Keep allocating until we reach the budget. - std::vector> allocationsBelowBudget = {}; + std::vector> allocationsBelowBudget = {}; while (resourceAllocator->GetInfo().UsedMemoryUsage + kBufferMemorySize <= kDefaultBudget) { - ComPtr allocation; + ComPtr allocation; ASSERT_SUCCEEDED(resourceAllocator->CreateResource( bufferAllocationDesc, bufferDesc, D3D12_RESOURCE_STATE_COMMON, nullptr, &allocation)); allocationsBelowBudget.push_back(std::move(allocation)); @@ -409,11 +412,11 @@ TEST_F(D3D12ResidencyManagerTests, OverBudget) { constexpr uint64_t kMemoryOverBudget = GPGMM_MB_TO_BYTES(10); // Allocating the same amount over budget, where older allocations will be evicted. - std::vector> allocationsAboveBudget = {}; + std::vector> allocationsAboveBudget = {}; const uint64_t currentMemoryUsage = resourceAllocator->GetInfo().UsedMemoryUsage; while (currentMemoryUsage + kMemoryOverBudget > resourceAllocator->GetInfo().UsedMemoryUsage) { - ComPtr allocation; + ComPtr allocation; ASSERT_SUCCEEDED(resourceAllocator->CreateResource( bufferAllocationDesc, bufferDesc, D3D12_RESOURCE_STATE_COMMON, nullptr, &allocation)); allocationsAboveBudget.push_back(std::move(allocation)); @@ -436,12 +439,12 @@ TEST_F(D3D12ResidencyManagerTests, OverBudgetAsync) { RESIDENCY_DESC residencyDesc = CreateBasicResidencyDesc(kBudgetIsDeterminedByOS); residencyDesc.Flags ^= RESIDENCY_FLAG_NEVER_UPDATE_BUDGET_ON_WORKER_THREAD; - ComPtr residencyManager; - ASSERT_SUCCEEDED(ResidencyManager::CreateResidencyManager(residencyDesc, &residencyManager)); + ComPtr residencyManager; + ASSERT_SUCCEEDED(CreateResidencyManager(residencyDesc, &residencyManager)); - ComPtr resourceAllocator; - ASSERT_SUCCEEDED(ResourceAllocator::CreateResourceAllocator( - CreateBasicAllocatorDesc(), residencyManager.Get(), &resourceAllocator)); + ComPtr resourceAllocator; + ASSERT_SUCCEEDED(CreateResourceAllocator(CreateBasicAllocatorDesc(), residencyManager.Get(), + &resourceAllocator)); constexpr uint64_t kBufferMemorySize = GPGMM_MB_TO_BYTES(1); const D3D12_RESOURCE_DESC bufferDesc = CreateBasicBufferDesc(kBufferMemorySize); @@ -459,10 +462,10 @@ TEST_F(D3D12ResidencyManagerTests, OverBudgetAsync) { // Keep allocating until we reach the budget. Should a budget change occur, we must also // terminate the loop since we cannot guarantee all allocations will be created resident. - std::vector> allocations = {}; + std::vector> allocations = {}; while (resourceAllocator->GetInfo().UsedMemoryUsage + kBufferMemorySize < memoryUnderBudget && GetBudgetLeft(residencyManager.Get(), bufferMemorySegment) >= kBufferMemorySize) { - ComPtr allocation; + ComPtr allocation; ASSERT_SUCCEEDED(resourceAllocator->CreateResource( bufferAllocationDesc, bufferDesc, D3D12_RESOURCE_STATE_COMMON, nullptr, &allocation)); @@ -480,18 +483,18 @@ TEST_F(D3D12ResidencyManagerTests, OverBudgetAsync) { TEST_F(D3D12ResidencyManagerTests, OverBudgetDisablesGrowth) { RESIDENCY_DESC residencyDesc = CreateBasicResidencyDesc(kDefaultBudget); - ComPtr residencyManager; - ASSERT_SUCCEEDED(ResidencyManager::CreateResidencyManager(residencyDesc, &residencyManager)); + ComPtr residencyManager; + ASSERT_SUCCEEDED(CreateResidencyManager(residencyDesc, &residencyManager)); ALLOCATOR_DESC allocatorDesc = CreateBasicAllocatorDesc(); allocatorDesc.MemoryGrowthFactor = 2; - ComPtr resourceAllocator; - ASSERT_SUCCEEDED(ResourceAllocator::CreateResourceAllocator( - CreateBasicAllocatorDesc(), residencyManager.Get(), &resourceAllocator)); + ComPtr resourceAllocator; + ASSERT_SUCCEEDED(CreateResourceAllocator(CreateBasicAllocatorDesc(), residencyManager.Get(), + &resourceAllocator)); - std::vector> allocations = {}; - std::vector resourceHeaps = {}; + std::vector> allocations = {}; + std::vector resourceHeaps = {}; constexpr uint64_t kBufferMemorySize = GPGMM_MB_TO_BYTES(1); const D3D12_RESOURCE_DESC bufferDesc = CreateBasicBufferDesc(kBufferMemorySize); @@ -500,7 +503,7 @@ TEST_F(D3D12ResidencyManagerTests, OverBudgetDisablesGrowth) { bufferAllocationDesc.HeapType = D3D12_HEAP_TYPE_DEFAULT; while (resourceAllocator->GetInfo().UsedMemoryUsage + kBufferMemorySize <= kDefaultBudget) { - ComPtr allocation; + ComPtr allocation; ASSERT_SUCCEEDED(resourceAllocator->CreateResource( bufferAllocationDesc, bufferDesc, D3D12_RESOURCE_STATE_COMMON, nullptr, &allocation)); @@ -524,12 +527,12 @@ TEST_F(D3D12ResidencyManagerTests, OverBudgetDisablesGrowth) { TEST_F(D3D12ResidencyManagerTests, OverBudgetWithLockedHeaps) { RESIDENCY_DESC residencyDesc = CreateBasicResidencyDesc(kDefaultBudget); - ComPtr residencyManager; - ASSERT_SUCCEEDED(ResidencyManager::CreateResidencyManager(residencyDesc, &residencyManager)); + ComPtr residencyManager; + ASSERT_SUCCEEDED(CreateResidencyManager(residencyDesc, &residencyManager)); - ComPtr resourceAllocator; - ASSERT_SUCCEEDED(ResourceAllocator::CreateResourceAllocator( - CreateBasicAllocatorDesc(), residencyManager.Get(), &resourceAllocator)); + ComPtr resourceAllocator; + ASSERT_SUCCEEDED(CreateResourceAllocator(CreateBasicAllocatorDesc(), residencyManager.Get(), + &resourceAllocator)); constexpr uint64_t kBufferMemorySize = GPGMM_MB_TO_BYTES(1); const D3D12_RESOURCE_DESC bufferDesc = CreateBasicBufferDesc(kBufferMemorySize); @@ -538,9 +541,9 @@ TEST_F(D3D12ResidencyManagerTests, OverBudgetWithLockedHeaps) { bufferAllocationDesc.HeapType = D3D12_HEAP_TYPE_DEFAULT; // Keep allocating until we reach the budget. - std::vector> allocationsBelowBudget = {}; + std::vector> allocationsBelowBudget = {}; while (resourceAllocator->GetInfo().UsedMemoryUsage + kBufferMemorySize <= kDefaultBudget) { - ComPtr allocation; + ComPtr allocation; ASSERT_SUCCEEDED(resourceAllocator->CreateResource( bufferAllocationDesc, bufferDesc, D3D12_RESOURCE_STATE_COMMON, nullptr, &allocation)); @@ -572,21 +575,21 @@ TEST_F(D3D12ResidencyManagerTests, OverBudgetWithLockedHeaps) { // swaps the residency status using ExecuteCommandList: first set gets paged-in again, second set // gets paged-out. TEST_F(D3D12ResidencyManagerTests, ExecuteCommandListOverBudget) { - ComPtr residencyManager; - ASSERT_SUCCEEDED(ResidencyManager::CreateResidencyManager( - CreateBasicResidencyDesc(kDefaultBudget), &residencyManager)); + ComPtr residencyManager; + ASSERT_SUCCEEDED( + CreateResidencyManager(CreateBasicResidencyDesc(kDefaultBudget), &residencyManager)); - ComPtr resourceAllocator; - ASSERT_SUCCEEDED(ResourceAllocator::CreateResourceAllocator( - CreateBasicAllocatorDesc(), residencyManager.Get(), &resourceAllocator)); + ComPtr resourceAllocator; + ASSERT_SUCCEEDED(CreateResourceAllocator(CreateBasicAllocatorDesc(), residencyManager.Get(), + &resourceAllocator)); constexpr uint64_t kBufferMemorySize = GPGMM_MB_TO_BYTES(1); const D3D12_RESOURCE_DESC bufferDesc = CreateBasicBufferDesc(kBufferMemorySize); // Create the first set of heaps below the budget. - std::vector> firstSetOfHeaps = {}; + std::vector> firstSetOfHeaps = {}; while (resourceAllocator->GetInfo().UsedMemoryUsage + kBufferMemorySize <= kDefaultBudget) { - ComPtr allocation; + ComPtr allocation; ASSERT_SUCCEEDED(resourceAllocator->CreateResource( {}, bufferDesc, D3D12_RESOURCE_STATE_COMMON, nullptr, &allocation)); EXPECT_EQ(allocation->GetMemory()->GetInfo().Status, RESIDENCY_STATUS_CURRENT_RESIDENT); @@ -594,9 +597,9 @@ TEST_F(D3D12ResidencyManagerTests, ExecuteCommandListOverBudget) { } // Create the second set of heaps above the budget, the first set will be evicted. - std::vector> secondSetOfHeaps = {}; + std::vector> secondSetOfHeaps = {}; for (uint64_t i = 0; i < kDefaultBudget / kBufferMemorySize; i++) { - ComPtr allocation; + ComPtr allocation; ASSERT_SUCCEEDED(resourceAllocator->CreateResource( {}, bufferDesc, D3D12_RESOURCE_STATE_COMMON, nullptr, &allocation)); EXPECT_EQ(allocation->GetMemory()->GetInfo().Status, RESIDENCY_STATUS_CURRENT_RESIDENT); @@ -604,9 +607,11 @@ TEST_F(D3D12ResidencyManagerTests, ExecuteCommandListOverBudget) { } // Page-in the first set of heaps using ExecuteCommandLists (and page-out the second set). - ResidencyList firstSetOfHeapsWorkingSet; + ComPtr firstSetOfHeapsWorkingSet; + ASSERT_SUCCEEDED(CreateResidencyList(&firstSetOfHeapsWorkingSet)); + for (auto& allocation : firstSetOfHeaps) { - firstSetOfHeapsWorkingSet.Add(allocation->GetMemory()); + firstSetOfHeapsWorkingSet->Add(allocation->GetMemory()); } ComPtr commandAllocator; @@ -623,7 +628,7 @@ TEST_F(D3D12ResidencyManagerTests, ExecuteCommandListOverBudget) { ASSERT_SUCCEEDED(mDevice->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&queue))); { - ResidencyList* residencyLists[] = {&firstSetOfHeapsWorkingSet}; + IResidencyList* residencyLists[] = {firstSetOfHeapsWorkingSet.Get()}; ID3D12CommandList* commandLists[] = {commandList.Get()}; ASSERT_SUCCEEDED( residencyManager->ExecuteCommandLists(queue.Get(), commandLists, residencyLists, 1)); @@ -640,13 +645,15 @@ TEST_F(D3D12ResidencyManagerTests, ExecuteCommandListOverBudget) { } // Page-in the second set of heaps using ExecuteCommandLists (and page-out the first set). - ResidencyList secondSetOfHeapsWorkingSet; + ComPtr secondSetOfHeapsWorkingSet; + ASSERT_SUCCEEDED(CreateResidencyList(&secondSetOfHeapsWorkingSet)); + for (auto& allocation : secondSetOfHeaps) { - secondSetOfHeapsWorkingSet.Add(allocation->GetMemory()); + secondSetOfHeapsWorkingSet->Add(allocation->GetMemory()); } { - ResidencyList* residencyLists[] = {&secondSetOfHeapsWorkingSet}; + IResidencyList* residencyLists[] = {secondSetOfHeapsWorkingSet.Get()}; ID3D12CommandList* commandLists[] = {commandList.Get()}; ASSERT_SUCCEEDED( residencyManager->ExecuteCommandLists(queue.Get(), commandLists, residencyLists, 1)); diff --git a/src/tests/end2end/D3D12ResourceAllocatorTests.cpp b/src/tests/end2end/D3D12ResourceAllocatorTests.cpp index 6b3c22797..a0fe3cfc0 100644 --- a/src/tests/end2end/D3D12ResourceAllocatorTests.cpp +++ b/src/tests/end2end/D3D12ResourceAllocatorTests.cpp @@ -79,8 +79,8 @@ class D3D12ResourceAllocatorTests : public D3D12TestBase, public ::testing::Test TEST_F(D3D12ResourceAllocatorTests, CreateResourceAllocator) { // Creating an invalid allocator should always fail. { - ComPtr resourceAllocator; - EXPECT_FAILED(ResourceAllocator::CreateResourceAllocator({}, &resourceAllocator)); + ComPtr resourceAllocator; + EXPECT_FAILED(CreateResourceAllocator({}, &resourceAllocator, nullptr)); EXPECT_EQ(resourceAllocator, nullptr); } @@ -89,8 +89,8 @@ TEST_F(D3D12ResourceAllocatorTests, CreateResourceAllocator) { ALLOCATOR_DESC desc = CreateBasicAllocatorDesc(); desc.Device = nullptr; - ComPtr resourceAllocator; - EXPECT_FAILED(ResourceAllocator::CreateResourceAllocator(desc, &resourceAllocator)); + ComPtr resourceAllocator; + EXPECT_FAILED(CreateResourceAllocator(desc, &resourceAllocator, nullptr)); EXPECT_EQ(resourceAllocator, nullptr); } @@ -101,30 +101,26 @@ TEST_F(D3D12ResourceAllocatorTests, CreateResourceAllocator) { desc.ResourceHeapTier = static_cast(D3D12_RESOURCE_HEAP_TIER_2 + 1); - ComPtr resourceAllocator; - EXPECT_FAILED(ResourceAllocator::CreateResourceAllocator(desc, &resourceAllocator)); + ComPtr resourceAllocator; + EXPECT_FAILED(CreateResourceAllocator(desc, &resourceAllocator, nullptr)); EXPECT_EQ(resourceAllocator, nullptr); } - // Creating a NULL allocator should always succeed. - EXPECT_SUCCEEDED( - ResourceAllocator::CreateResourceAllocator(CreateBasicAllocatorDesc(), nullptr)); - // Creating an allocator without an adapter should always fail. { ALLOCATOR_DESC desc = CreateBasicAllocatorDesc(); desc.Adapter = nullptr; - ComPtr resourceAllocator; - EXPECT_FAILED(ResourceAllocator::CreateResourceAllocator(desc, &resourceAllocator)); + ComPtr resourceAllocator; + EXPECT_FAILED(CreateResourceAllocator(desc, &resourceAllocator, nullptr)); EXPECT_EQ(resourceAllocator, nullptr); } // Creating a new allocator using the defaults should always succeed. { - ComPtr resourceAllocator; - EXPECT_SUCCEEDED(ResourceAllocator::CreateResourceAllocator(CreateBasicAllocatorDesc(), - &resourceAllocator)); + ComPtr resourceAllocator; + EXPECT_SUCCEEDED( + CreateResourceAllocator(CreateBasicAllocatorDesc(), &resourceAllocator, nullptr)); EXPECT_NE(resourceAllocator, nullptr); } @@ -135,8 +131,8 @@ TEST_F(D3D12ResourceAllocatorTests, CreateResourceAllocator) { desc.PreferredResourceHeapSize = kBufferOf4MBAllocationSize; desc.MaxResourceHeapSize = kBufferOf4MBAllocationSize / 2; - ComPtr resourceAllocator; - EXPECT_FAILED(ResourceAllocator::CreateResourceAllocator(desc, &resourceAllocator)); + ComPtr resourceAllocator; + EXPECT_FAILED(CreateResourceAllocator(desc, &resourceAllocator, nullptr)); EXPECT_EQ(resourceAllocator, nullptr); } } @@ -144,8 +140,8 @@ TEST_F(D3D12ResourceAllocatorTests, CreateResourceAllocator) { TEST_F(D3D12ResourceAllocatorTests, CreateResourceAllocatorNoLeak) { GPGMM_TEST_MEMORY_LEAK_START(); { - ComPtr resourceAllocator; - ResourceAllocator::CreateResourceAllocator(CreateBasicAllocatorDesc(), &resourceAllocator); + ComPtr resourceAllocator; + CreateResourceAllocator(CreateBasicAllocatorDesc(), &resourceAllocator, nullptr); } GPGMM_TEST_MEMORY_LEAK_END(); } @@ -153,10 +149,10 @@ TEST_F(D3D12ResourceAllocatorTests, CreateResourceAllocatorNoLeak) { TEST_F(D3D12ResourceAllocatorTests, CreateBufferNoLeak) { GPGMM_TEST_MEMORY_LEAK_START(); { - ComPtr resourceAllocator; - ResourceAllocator::CreateResourceAllocator(CreateBasicAllocatorDesc(), &resourceAllocator); + ComPtr resourceAllocator; + CreateResourceAllocator(CreateBasicAllocatorDesc(), &resourceAllocator, nullptr); for (auto& bufferAllocationExpectation : GenerateBufferAllocations()) { - ComPtr allocation; + ComPtr allocation; resourceAllocator->CreateResource( {}, CreateBasicBufferDesc(bufferAllocationExpectation.size, @@ -177,12 +173,12 @@ TEST_F(D3D12ResourceAllocatorTests, CreateBufferAndTextureInSameHeap) { // Adapter must support mixing of resource types in same heap. GPGMM_SKIP_TEST_IF(allocatorDesc.ResourceHeapTier < D3D12_RESOURCE_HEAP_TIER_2); - ComPtr resourceAllocator; - ASSERT_SUCCEEDED(ResourceAllocator::CreateResourceAllocator(allocatorDesc, &resourceAllocator)); + ComPtr resourceAllocator; + ASSERT_SUCCEEDED(CreateResourceAllocator(allocatorDesc, &resourceAllocator, nullptr)); // Create memory for buffer in Heap A. { - ComPtr bufferAllocation; + ComPtr bufferAllocation; ASSERT_SUCCEEDED(resourceAllocator->CreateResource( {}, CreateBasicBufferDesc(kBufferOf4MBAllocationSize), D3D12_RESOURCE_STATE_COMMON, nullptr, &bufferAllocation)); @@ -192,7 +188,7 @@ TEST_F(D3D12ResourceAllocatorTests, CreateBufferAndTextureInSameHeap) { // Reuse memory for texture in Heap A. { - ComPtr textureAllocation; + ComPtr textureAllocation; ASSERT_SUCCEEDED(resourceAllocator->CreateResource( {}, CreateBasicTextureDesc(DXGI_FORMAT_R8G8B8A8_UNORM, 1, 1), D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, &textureAllocation)); @@ -209,12 +205,12 @@ TEST_F(D3D12ResourceAllocatorTests, CreateBufferAndTextureInSeperateHeap) { // Heaps of only the same size can be reused between resource types. allocatorDesc.PreferredResourceHeapSize = kBufferOf4MBAllocationSize; - ComPtr resourceAllocator; - ASSERT_SUCCEEDED(ResourceAllocator::CreateResourceAllocator(allocatorDesc, &resourceAllocator)); + ComPtr resourceAllocator; + ASSERT_SUCCEEDED(CreateResourceAllocator(allocatorDesc, &resourceAllocator, nullptr)); // Create memory for buffer in Heap A. { - ComPtr bufferAllocation; + ComPtr bufferAllocation; ASSERT_SUCCEEDED(resourceAllocator->CreateResource( {}, CreateBasicBufferDesc(kBufferOf4MBAllocationSize), D3D12_RESOURCE_STATE_COMMON, nullptr, &bufferAllocation)); @@ -227,7 +223,7 @@ TEST_F(D3D12ResourceAllocatorTests, CreateBufferAndTextureInSeperateHeap) { // Reuse memory for texture in Heap A. { - ComPtr textureAllocation; + ComPtr textureAllocation; ASSERT_SUCCEEDED(resourceAllocator->CreateResource( {}, CreateBasicTextureDesc(DXGI_FORMAT_R8G8B8A8_UNORM, 1, 1), D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, &textureAllocation)); @@ -241,13 +237,13 @@ TEST_F(D3D12ResourceAllocatorTests, CreateBufferAndTextureInSeperateHeap) { // Exceeding the max resource heap size should always fail. TEST_F(D3D12ResourceAllocatorTests, CreateBufferOversized) { - ComPtr resourceAllocator; + ComPtr resourceAllocator; ASSERT_SUCCEEDED( - ResourceAllocator::CreateResourceAllocator(CreateBasicAllocatorDesc(), &resourceAllocator)); + CreateResourceAllocator(CreateBasicAllocatorDesc(), &resourceAllocator, nullptr)); ASSERT_NE(resourceAllocator, nullptr); constexpr uint64_t kOversizedBuffer = GPGMM_GB_TO_BYTES(32); - ComPtr allocation; + ComPtr allocation; ASSERT_FAILED(resourceAllocator->CreateResource({}, CreateBasicBufferDesc(kOversizedBuffer + 1), D3D12_RESOURCE_STATE_COMMON, nullptr, &allocation)); @@ -269,13 +265,12 @@ TEST_F(D3D12ResourceAllocatorTests, CreateBufferSubAllocated) { ALLOCATOR_DESC newAllocatorDesc = allocatorDesc; newAllocatorDesc.SubAllocationAlgorithm = ALLOCATOR_ALGORITHM_BUDDY_SYSTEM; - ComPtr resourceAllocator; - ASSERT_SUCCEEDED( - ResourceAllocator::CreateResourceAllocator(newAllocatorDesc, &resourceAllocator)); + ComPtr resourceAllocator; + ASSERT_SUCCEEDED(CreateResourceAllocator(newAllocatorDesc, &resourceAllocator, nullptr)); ASSERT_NE(resourceAllocator, nullptr); for (auto& alloc : GenerateBufferAllocations()) { - ComPtr allocation; + ComPtr allocation; EXPECT_EQ(SUCCEEDED(resourceAllocator->CreateResource( allocationDesc, CreateBasicBufferDesc(alloc.size, alloc.alignment), D3D12_RESOURCE_STATE_COMMON, nullptr, &allocation)), @@ -289,13 +284,12 @@ TEST_F(D3D12ResourceAllocatorTests, CreateBufferSubAllocated) { newAllocatorDesc.SubAllocationAlgorithm = ALLOCATOR_ALGORITHM_BUDDY_SYSTEM; newAllocatorDesc.PoolAlgorithm = ALLOCATOR_ALGORITHM_FIXED_POOL; - ComPtr resourceAllocator; - ASSERT_SUCCEEDED( - ResourceAllocator::CreateResourceAllocator(newAllocatorDesc, &resourceAllocator)); + ComPtr resourceAllocator; + ASSERT_SUCCEEDED(CreateResourceAllocator(newAllocatorDesc, &resourceAllocator, nullptr)); ASSERT_NE(resourceAllocator, nullptr); for (auto& alloc : GenerateBufferAllocations()) { - ComPtr allocation; + ComPtr allocation; EXPECT_EQ(SUCCEEDED(resourceAllocator->CreateResource( allocationDesc, CreateBasicBufferDesc(alloc.size, alloc.alignment), D3D12_RESOURCE_STATE_COMMON, nullptr, &allocation)), @@ -309,13 +303,12 @@ TEST_F(D3D12ResourceAllocatorTests, CreateBufferSubAllocated) { newAllocatorDesc.SubAllocationAlgorithm = ALLOCATOR_ALGORITHM_BUDDY_SYSTEM; newAllocatorDesc.PoolAlgorithm = ALLOCATOR_ALGORITHM_SEGMENTED_POOL; - ComPtr resourceAllocator; - ASSERT_SUCCEEDED( - ResourceAllocator::CreateResourceAllocator(newAllocatorDesc, &resourceAllocator)); + ComPtr resourceAllocator; + ASSERT_SUCCEEDED(CreateResourceAllocator(newAllocatorDesc, &resourceAllocator, nullptr)); ASSERT_NE(resourceAllocator, nullptr); for (auto& alloc : GenerateBufferAllocations()) { - ComPtr allocation; + ComPtr allocation; EXPECT_EQ(SUCCEEDED(resourceAllocator->CreateResource( allocationDesc, CreateBasicBufferDesc(alloc.size, alloc.alignment), D3D12_RESOURCE_STATE_COMMON, nullptr, &allocation)), @@ -328,13 +321,12 @@ TEST_F(D3D12ResourceAllocatorTests, CreateBufferSubAllocated) { ALLOCATOR_DESC newAllocatorDesc = allocatorDesc; newAllocatorDesc.SubAllocationAlgorithm = ALLOCATOR_ALGORITHM_SLAB; - ComPtr resourceAllocator; - ASSERT_SUCCEEDED( - ResourceAllocator::CreateResourceAllocator(newAllocatorDesc, &resourceAllocator)); + ComPtr resourceAllocator; + ASSERT_SUCCEEDED(CreateResourceAllocator(newAllocatorDesc, &resourceAllocator, nullptr)); ASSERT_NE(resourceAllocator, nullptr); for (auto& alloc : GenerateBufferAllocations()) { - ComPtr allocation; + ComPtr allocation; EXPECT_EQ(SUCCEEDED(resourceAllocator->CreateResource( allocationDesc, CreateBasicBufferDesc(alloc.size, alloc.alignment), D3D12_RESOURCE_STATE_COMMON, nullptr, &allocation)), @@ -348,13 +340,12 @@ TEST_F(D3D12ResourceAllocatorTests, CreateBufferSubAllocated) { newAllocatorDesc.SubAllocationAlgorithm = ALLOCATOR_ALGORITHM_SLAB; newAllocatorDesc.PoolAlgorithm = ALLOCATOR_ALGORITHM_FIXED_POOL; - ComPtr resourceAllocator; - ASSERT_SUCCEEDED( - ResourceAllocator::CreateResourceAllocator(newAllocatorDesc, &resourceAllocator)); + ComPtr resourceAllocator; + ASSERT_SUCCEEDED(CreateResourceAllocator(newAllocatorDesc, &resourceAllocator, nullptr)); ASSERT_NE(resourceAllocator, nullptr); for (auto& alloc : GenerateBufferAllocations()) { - ComPtr allocation; + ComPtr allocation; EXPECT_EQ(SUCCEEDED(resourceAllocator->CreateResource( allocationDesc, CreateBasicBufferDesc(alloc.size, alloc.alignment), D3D12_RESOURCE_STATE_COMMON, nullptr, &allocation)), @@ -368,13 +359,12 @@ TEST_F(D3D12ResourceAllocatorTests, CreateBufferSubAllocated) { newAllocatorDesc.SubAllocationAlgorithm = ALLOCATOR_ALGORITHM_SLAB; newAllocatorDesc.PoolAlgorithm = ALLOCATOR_ALGORITHM_SEGMENTED_POOL; - ComPtr resourceAllocator; - ASSERT_SUCCEEDED( - ResourceAllocator::CreateResourceAllocator(newAllocatorDesc, &resourceAllocator)); + ComPtr resourceAllocator; + ASSERT_SUCCEEDED(CreateResourceAllocator(newAllocatorDesc, &resourceAllocator, nullptr)); ASSERT_NE(resourceAllocator, nullptr); for (auto& alloc : GenerateBufferAllocations()) { - ComPtr allocation; + ComPtr allocation; EXPECT_EQ(SUCCEEDED(resourceAllocator->CreateResource( allocationDesc, CreateBasicBufferDesc(alloc.size, alloc.alignment), D3D12_RESOURCE_STATE_COMMON, nullptr, &allocation)), @@ -387,13 +377,12 @@ TEST_F(D3D12ResourceAllocatorTests, CreateBufferSubAllocated) { ALLOCATOR_DESC newAllocatorDesc = allocatorDesc; newAllocatorDesc.Flags |= ALLOCATOR_FLAG_ALWAYS_COMMITED; - ComPtr resourceAllocator; - ASSERT_SUCCEEDED( - ResourceAllocator::CreateResourceAllocator(newAllocatorDesc, &resourceAllocator)); + ComPtr resourceAllocator; + ASSERT_SUCCEEDED(CreateResourceAllocator(newAllocatorDesc, &resourceAllocator, nullptr)); ASSERT_NE(resourceAllocator, nullptr); for (auto& alloc : GenerateBufferAllocations()) { - ComPtr allocation; + ComPtr allocation; EXPECT_EQ(SUCCEEDED(resourceAllocator->CreateResource( allocationDesc, CreateBasicBufferDesc(alloc.size, alloc.alignment), D3D12_RESOURCE_STATE_COMMON, nullptr, &allocation)), @@ -406,13 +395,12 @@ TEST_F(D3D12ResourceAllocatorTests, CreateBufferSubAllocated) { ALLOCATOR_DESC newAllocatorDesc = allocatorDesc; newAllocatorDesc.SubAllocationAlgorithm = ALLOCATOR_ALGORITHM_DEDICATED; - ComPtr resourceAllocator; - ASSERT_SUCCEEDED( - ResourceAllocator::CreateResourceAllocator(newAllocatorDesc, &resourceAllocator)); + ComPtr resourceAllocator; + ASSERT_SUCCEEDED(CreateResourceAllocator(newAllocatorDesc, &resourceAllocator, nullptr)); ASSERT_NE(resourceAllocator, nullptr); for (auto& alloc : GenerateBufferAllocations()) { - ComPtr allocation; + ComPtr allocation; EXPECT_EQ(SUCCEEDED(resourceAllocator->CreateResource( allocationDesc, CreateBasicBufferDesc(alloc.size, alloc.alignment), D3D12_RESOURCE_STATE_COMMON, nullptr, &allocation)), @@ -430,12 +418,11 @@ TEST_F(D3D12ResourceAllocatorTests, CreateBufferWithPreferredHeapSize) { newAllocatorDesc.SubAllocationAlgorithm = ALLOCATOR_ALGORITHM_SLAB; newAllocatorDesc.PreferredResourceHeapSize = GPGMM_MB_TO_BYTES(12); - ComPtr resourceAllocator; - ASSERT_SUCCEEDED( - ResourceAllocator::CreateResourceAllocator(newAllocatorDesc, &resourceAllocator)); + ComPtr resourceAllocator; + ASSERT_SUCCEEDED(CreateResourceAllocator(newAllocatorDesc, &resourceAllocator, nullptr)); ASSERT_NE(resourceAllocator, nullptr); - ComPtr allocation; + ComPtr allocation; ASSERT_SUCCEEDED( resourceAllocator->CreateResource({}, CreateBasicBufferDesc(kBufferOf4MBAllocationSize), D3D12_RESOURCE_STATE_COMMON, nullptr, &allocation)); @@ -450,12 +437,11 @@ TEST_F(D3D12ResourceAllocatorTests, CreateBufferWithPreferredHeapSize) { newAllocatorDesc.SubAllocationAlgorithm = ALLOCATOR_ALGORITHM_BUDDY_SYSTEM; newAllocatorDesc.PreferredResourceHeapSize = GPGMM_MB_TO_BYTES(12); - ComPtr resourceAllocator; - ASSERT_SUCCEEDED( - ResourceAllocator::CreateResourceAllocator(newAllocatorDesc, &resourceAllocator)); + ComPtr resourceAllocator; + ASSERT_SUCCEEDED(CreateResourceAllocator(newAllocatorDesc, &resourceAllocator, nullptr)); ASSERT_NE(resourceAllocator, nullptr); - ComPtr allocation; + ComPtr allocation; ASSERT_SUCCEEDED( resourceAllocator->CreateResource({}, CreateBasicBufferDesc(kBufferOf4MBAllocationSize), D3D12_RESOURCE_STATE_COMMON, nullptr, &allocation)); @@ -470,12 +456,11 @@ TEST_F(D3D12ResourceAllocatorTests, CreateBufferWithPreferredHeapSize) { newAllocatorDesc.SubAllocationAlgorithm = ALLOCATOR_ALGORITHM_DEDICATED; newAllocatorDesc.PreferredResourceHeapSize = GPGMM_MB_TO_BYTES(12); - ComPtr resourceAllocator; - ASSERT_SUCCEEDED( - ResourceAllocator::CreateResourceAllocator(newAllocatorDesc, &resourceAllocator)); + ComPtr resourceAllocator; + ASSERT_SUCCEEDED(CreateResourceAllocator(newAllocatorDesc, &resourceAllocator, nullptr)); ASSERT_NE(resourceAllocator, nullptr); - ComPtr allocation; + ComPtr allocation; ASSERT_SUCCEEDED( resourceAllocator->CreateResource({}, CreateBasicBufferDesc(kBufferOf4MBAllocationSize), D3D12_RESOURCE_STATE_COMMON, nullptr, &allocation)); @@ -487,17 +472,17 @@ TEST_F(D3D12ResourceAllocatorTests, CreateBufferWithPreferredHeapSize) { } TEST_F(D3D12ResourceAllocatorTests, CreateBufferManyDeallocateAtEnd) { - ComPtr resourceAllocator; + ComPtr resourceAllocator; ASSERT_SUCCEEDED( - ResourceAllocator::CreateResourceAllocator(CreateBasicAllocatorDesc(), &resourceAllocator)); + CreateResourceAllocator(CreateBasicAllocatorDesc(), &resourceAllocator, nullptr)); ASSERT_NE(resourceAllocator, nullptr); ALLOCATION_DESC allocationDesc = {}; allocationDesc.HeapType = D3D12_HEAP_TYPE_DEFAULT; - std::set> allocs = {}; + std::set> allocs = {}; for (auto& alloc : GenerateBufferAllocations()) { - ComPtr allocation; + ComPtr allocation; EXPECT_EQ(SUCCEEDED(resourceAllocator->CreateResource( allocationDesc, CreateBasicBufferDesc(alloc.size, alloc.alignment), D3D12_RESOURCE_STATE_COMMON, nullptr, &allocation)), @@ -513,9 +498,9 @@ TEST_F(D3D12ResourceAllocatorTests, CreateBufferManyDeallocateAtEnd) { } TEST_F(D3D12ResourceAllocatorTests, CreateBuffer) { - ComPtr resourceAllocator; + ComPtr resourceAllocator; ASSERT_SUCCEEDED( - ResourceAllocator::CreateResourceAllocator(CreateBasicAllocatorDesc(), &resourceAllocator)); + CreateResourceAllocator(CreateBasicAllocatorDesc(), &resourceAllocator, nullptr)); ASSERT_NE(resourceAllocator, nullptr); // Creating a resource without allocation should still succeed. @@ -533,7 +518,7 @@ TEST_F(D3D12ResourceAllocatorTests, CreateBuffer) { ALLOCATION_DESC allocationDesc = {}; allocationDesc.HeapType = D3D12_HEAP_TYPE_DEFAULT; - ComPtr allocation; + ComPtr allocation; ASSERT_SUCCEEDED(resourceAllocator->CreateResource( allocationDesc, CreateBasicBufferDesc(kBufferOf4MBAllocationSize), D3D12_RESOURCE_STATE_COMMON, nullptr, &allocation)); @@ -546,7 +531,7 @@ TEST_F(D3D12ResourceAllocatorTests, CreateBuffer) { ALLOCATION_DESC allocationDesc = {}; allocationDesc.HeapType = D3D12_HEAP_TYPE_UPLOAD; - ComPtr allocation; + ComPtr allocation; ASSERT_SUCCEEDED(resourceAllocator->CreateResource( allocationDesc, CreateBasicBufferDesc(kBufferOf4MBAllocationSize), D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, &allocation)); @@ -605,13 +590,13 @@ TEST_F(D3D12ResourceAllocatorTests, CreateBuffer) { // D3D12_HEAP_FLAG_SHARED is incompatible CPU accessible heaps. allocationDesc.Flags = ALLOCATION_FLAG_ALWAYS_ATTRIBUTE_HEAPS; - ComPtr allocation; + ComPtr allocation; ASSERT_SUCCEEDED(resourceAllocator->CreateResource( allocationDesc, CreateBasicBufferDesc(kBufferOf4MBAllocationSize), D3D12_RESOURCE_STATE_COMMON, nullptr, &allocation)); ASSERT_NE(allocation, nullptr); - ComPtr heap = allocation->GetMemory(); + ComPtr heap = allocation->GetMemory(); ASSERT_NE(heap, nullptr); ComPtr d3dHeap; @@ -625,7 +610,7 @@ TEST_F(D3D12ResourceAllocatorTests, CreateBuffer) { allocationDescWithInvalidHeapFlags.ExtraRequiredHeapFlags = static_cast(0xFF); - ComPtr allocation; + ComPtr allocation; ASSERT_FAILED(resourceAllocator->CreateResource( allocationDescWithInvalidHeapFlags, CreateBasicBufferDesc(kBufferOf4MBAllocationSize), D3D12_RESOURCE_STATE_COMMON, nullptr, &allocation)); @@ -637,7 +622,7 @@ TEST_F(D3D12ResourceAllocatorTests, CreateBuffer) { allocationDesc.HeapType = D3D12_HEAP_TYPE_DEFAULT; allocationDesc.DebugName = L"Buffer"; - ComPtr allocation; + ComPtr allocation; ASSERT_SUCCEEDED(resourceAllocator->CreateResource( allocationDesc, CreateBasicBufferDesc(kBufferOf4MBAllocationSize), D3D12_RESOURCE_STATE_COMMON, nullptr, &allocation)); @@ -647,21 +632,21 @@ TEST_F(D3D12ResourceAllocatorTests, CreateBuffer) { // Creating a buffer without a heap type should be inferred based on the resource state. { - ComPtr allocation; + ComPtr allocation; ASSERT_SUCCEEDED(resourceAllocator->CreateResource( {}, CreateBasicBufferDesc(kBufferOf4MBAllocationSize), D3D12_RESOURCE_STATE_COPY_DEST, nullptr, &allocation)); ASSERT_NE(allocation, nullptr); } { - ComPtr allocation; + ComPtr allocation; ASSERT_SUCCEEDED(resourceAllocator->CreateResource( {}, CreateBasicBufferDesc(kBufferOf4MBAllocationSize), D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, &allocation)); ASSERT_NE(allocation, nullptr); } { - ComPtr allocation; + ComPtr allocation; ASSERT_FAILED(resourceAllocator->CreateResource( {}, CreateBasicBufferDesc(kBufferOf4MBAllocationSize), D3D12_RESOURCE_STATE_UNORDERED_ACCESS, nullptr, &allocation)); @@ -669,12 +654,12 @@ TEST_F(D3D12ResourceAllocatorTests, CreateBuffer) { } TEST_F(D3D12ResourceAllocatorTests, CreateBufferLeaked) { - ComPtr resourceAllocator; + ComPtr resourceAllocator; ASSERT_SUCCEEDED( - ResourceAllocator::CreateResourceAllocator(CreateBasicAllocatorDesc(), &resourceAllocator)); + CreateResourceAllocator(CreateBasicAllocatorDesc(), &resourceAllocator, nullptr)); ASSERT_NE(resourceAllocator, nullptr); - ComPtr allocation; + ComPtr allocation; ASSERT_SUCCEEDED( resourceAllocator->CreateResource({}, CreateBasicBufferDesc(kBufferOf4MBAllocationSize), D3D12_RESOURCE_STATE_COMMON, nullptr, &allocation)); @@ -686,9 +671,9 @@ TEST_F(D3D12ResourceAllocatorTests, CreateBufferLeaked) { TEST_F(D3D12ResourceAllocatorTests, CreateBufferUMA) { GPGMM_SKIP_TEST_IF(!mCaps->IsAdapterUMA()); - ComPtr resourceAllocator; + ComPtr resourceAllocator; ASSERT_SUCCEEDED( - ResourceAllocator::CreateResourceAllocator(CreateBasicAllocatorDesc(), &resourceAllocator)); + CreateResourceAllocator(CreateBasicAllocatorDesc(), &resourceAllocator, nullptr)); ASSERT_NE(resourceAllocator, nullptr); ASSERT_SUCCEEDED( @@ -721,11 +706,11 @@ TEST_F(D3D12ResourceAllocatorTests, CreateBufferDisableCustomHeaps) { ALLOCATOR_DESC allocatorDesc = CreateBasicAllocatorDesc(); allocatorDesc.Flags |= ALLOCATOR_FLAG_DISABLE_CUSTOM_HEAPS; - ComPtr resourceAllocator; - ASSERT_SUCCEEDED(ResourceAllocator::CreateResourceAllocator(allocatorDesc, &resourceAllocator)); + ComPtr resourceAllocator; + ASSERT_SUCCEEDED(CreateResourceAllocator(allocatorDesc, &resourceAllocator, nullptr)); { - ComPtr allocation; + ComPtr allocation; ASSERT_SUCCEEDED(resourceAllocator->CreateResource( {}, CreateBasicBufferDesc(kBufferOf4MBAllocationSize), D3D12_RESOURCE_STATE_COPY_DEST, nullptr, &allocation)); @@ -766,9 +751,9 @@ TEST_F(D3D12ResourceAllocatorTests, CreateBufferDisableCustomHeaps) { } TEST_F(D3D12ResourceAllocatorTests, CreateSmallTexture) { - ComPtr resourceAllocator; + ComPtr resourceAllocator; ASSERT_SUCCEEDED( - ResourceAllocator::CreateResourceAllocator(CreateBasicAllocatorDesc(), &resourceAllocator)); + CreateResourceAllocator(CreateBasicAllocatorDesc(), &resourceAllocator, nullptr)); ASSERT_NE(resourceAllocator, nullptr); // DXGI_FORMAT_R8G8B8A8_UNORM @@ -776,21 +761,21 @@ TEST_F(D3D12ResourceAllocatorTests, CreateSmallTexture) { ALLOCATION_DESC allocationDesc = {}; allocationDesc.HeapType = D3D12_HEAP_TYPE_DEFAULT; - ComPtr allocation; + ComPtr allocation; ASSERT_SUCCEEDED(resourceAllocator->CreateResource( allocationDesc, CreateBasicTextureDesc(DXGI_FORMAT_R8G8B8A8_UNORM, 1, 1), D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, &allocation)); ASSERT_NE(allocation, nullptr); EXPECT_TRUE( - gpgmm::IsAligned(allocation->GetSize(), + gpgmm::IsAligned(allocation->GetInfo().SizeInBytes, static_cast(D3D12_SMALL_RESOURCE_PLACEMENT_ALIGNMENT))); } } TEST_F(D3D12ResourceAllocatorTests, CreateMultisampledTexture) { - ComPtr resourceAllocator; + ComPtr resourceAllocator; ASSERT_SUCCEEDED( - ResourceAllocator::CreateResourceAllocator(CreateBasicAllocatorDesc(), &resourceAllocator)); + CreateResourceAllocator(CreateBasicAllocatorDesc(), &resourceAllocator, nullptr)); ASSERT_NE(resourceAllocator, nullptr); // DXGI_FORMAT_R8G8B8A8_UNORM @@ -798,13 +783,13 @@ TEST_F(D3D12ResourceAllocatorTests, CreateMultisampledTexture) { ALLOCATION_DESC allocationDesc = {}; allocationDesc.HeapType = D3D12_HEAP_TYPE_DEFAULT; - ComPtr allocation; + ComPtr allocation; ASSERT_SUCCEEDED(resourceAllocator->CreateResource( allocationDesc, CreateBasicTextureDesc(DXGI_FORMAT_R8G8B8A8_UNORM, 1, 1, 4), D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, &allocation)); ASSERT_NE(allocation, nullptr); EXPECT_TRUE(gpgmm::IsAligned( - allocation->GetSize(), + allocation->GetInfo().SizeInBytes, static_cast(D3D12_DEFAULT_MSAA_RESOURCE_PLACEMENT_ALIGNMENT))); EXPECT_EQ(resourceAllocator->GetInfo().UsedMemoryCount, 1u); @@ -814,13 +799,13 @@ TEST_F(D3D12ResourceAllocatorTests, CreateMultisampledTexture) { } TEST_F(D3D12ResourceAllocatorTests, CreateBufferImported) { - ComPtr resourceAllocator; + ComPtr resourceAllocator; ASSERT_SUCCEEDED( - ResourceAllocator::CreateResourceAllocator(CreateBasicAllocatorDesc(), &resourceAllocator)); + CreateResourceAllocator(CreateBasicAllocatorDesc(), &resourceAllocator, nullptr)); ASSERT_NE(resourceAllocator, nullptr); // Importing a non-existent buffer should always fail. - ComPtr externalAllocation; + ComPtr externalAllocation; ASSERT_FAILED(resourceAllocator->CreateResource(nullptr, &externalAllocation)); ASSERT_EQ(externalAllocation, nullptr); @@ -833,7 +818,7 @@ TEST_F(D3D12ResourceAllocatorTests, CreateBufferImported) { nullptr, &externalAllocation)); ASSERT_NE(externalAllocation, nullptr); - ComPtr internalAllocation; + ComPtr internalAllocation; ASSERT_SUCCEEDED( resourceAllocator->CreateResource(externalAllocation->GetResource(), &internalAllocation)); ASSERT_NE(internalAllocation, nullptr); @@ -843,16 +828,16 @@ TEST_F(D3D12ResourceAllocatorTests, CreateBufferImported) { } TEST_F(D3D12ResourceAllocatorTests, CreateBufferInvalid) { - ComPtr resourceAllocator; + ComPtr resourceAllocator; ASSERT_SUCCEEDED( - ResourceAllocator::CreateResourceAllocator(CreateBasicAllocatorDesc(), &resourceAllocator)); + CreateResourceAllocator(CreateBasicAllocatorDesc(), &resourceAllocator, nullptr)); ASSERT_NE(resourceAllocator, nullptr); // Garbage buffer descriptor should always fail. D3D12_RESOURCE_DESC badBufferDesc = CreateBasicBufferDesc(kBufferOf4MBAllocationSize); badBufferDesc.Flags = static_cast(0xFF); - ComPtr allocation; + ComPtr allocation; ASSERT_FAILED(resourceAllocator->CreateResource({}, badBufferDesc, D3D12_RESOURCE_STATE_COMMON, nullptr, &allocation)); ASSERT_EQ(allocation, nullptr); @@ -862,22 +847,22 @@ TEST_F(D3D12ResourceAllocatorTests, CreateBufferAlwaysCommitted) { ALLOCATOR_DESC desc = CreateBasicAllocatorDesc(); desc.Flags = ALLOCATOR_FLAG_ALWAYS_COMMITED; - ComPtr resourceAllocator; - ASSERT_SUCCEEDED(ResourceAllocator::CreateResourceAllocator(desc, &resourceAllocator)); + ComPtr resourceAllocator; + ASSERT_SUCCEEDED(CreateResourceAllocator(desc, &resourceAllocator, nullptr)); ASSERT_NE(resourceAllocator, nullptr); ALLOCATION_DESC allocationDesc = {}; allocationDesc.HeapType = D3D12_HEAP_TYPE_DEFAULT; - ComPtr allocation; + ComPtr allocation; ASSERT_SUCCEEDED( resourceAllocator->CreateResource(allocationDesc, CreateBasicBufferDesc(kBufferOf4MBAllocationSize), D3D12_RESOURCE_STATE_COMMON, nullptr, &allocation)); ASSERT_NE(allocation, nullptr); - EXPECT_EQ(allocation->GetSize(), kBufferOf4MBAllocationSize); + EXPECT_EQ(allocation->GetInfo().SizeInBytes, kBufferOf4MBAllocationSize); // Commmitted resources cannot be backed by a D3D12 heap. - ComPtr resourceHeap = allocation->GetMemory(); + ComPtr resourceHeap = allocation->GetMemory(); ASSERT_NE(resourceHeap, nullptr); ComPtr heap; @@ -890,9 +875,9 @@ TEST_F(D3D12ResourceAllocatorTests, CreateBufferAlwaysCommitted) { } TEST_F(D3D12ResourceAllocatorTests, CreateBufferNeverAllocate) { - ComPtr resourceAllocator; + ComPtr resourceAllocator; ASSERT_SUCCEEDED( - ResourceAllocator::CreateResourceAllocator(CreateBasicAllocatorDesc(), &resourceAllocator)); + CreateResourceAllocator(CreateBasicAllocatorDesc(), &resourceAllocator, nullptr)); ASSERT_NE(resourceAllocator, nullptr); // Check we can't reuse memory if CreateResource was never called previously. @@ -900,7 +885,7 @@ TEST_F(D3D12ResourceAllocatorTests, CreateBufferNeverAllocate) { allocationDesc.Flags = ALLOCATION_FLAG_NEVER_ALLOCATE_MEMORY; allocationDesc.HeapType = D3D12_HEAP_TYPE_DEFAULT; - ComPtr allocation; + ComPtr allocation; ASSERT_FAILED(resourceAllocator->CreateResource( allocationDesc, CreateBasicBufferDesc(kBufferOf4MBAllocationSize + 1), D3D12_RESOURCE_STATE_COMMON, nullptr, &allocation)); @@ -909,7 +894,7 @@ TEST_F(D3D12ResourceAllocatorTests, CreateBufferNeverAllocate) { constexpr uint64_t bufferSize = kBufferOf4MBAllocationSize / 8; allocationDesc.Flags = ALLOCATION_FLAG_NONE; - ComPtr allocationA; + ComPtr allocationA; ASSERT_SUCCEEDED( resourceAllocator->CreateResource(allocationDesc, CreateBasicBufferDesc(bufferSize), D3D12_RESOURCE_STATE_COMMON, nullptr, &allocationA)); @@ -920,7 +905,7 @@ TEST_F(D3D12ResourceAllocatorTests, CreateBufferNeverAllocate) { // Re-check that the same resource heap is used once CreateResource gets called. allocationDesc.Flags = ALLOCATION_FLAG_NEVER_ALLOCATE_MEMORY; - ComPtr allocationB; + ComPtr allocationB; ASSERT_SUCCEEDED( resourceAllocator->CreateResource(allocationDesc, CreateBasicBufferDesc(bufferSize), D3D12_RESOURCE_STATE_COMMON, nullptr, &allocationB)); @@ -928,9 +913,9 @@ TEST_F(D3D12ResourceAllocatorTests, CreateBufferNeverAllocate) { } TEST_F(D3D12ResourceAllocatorTests, CreateBufferWithin) { - ComPtr resourceAllocator; + ComPtr resourceAllocator; ASSERT_SUCCEEDED( - ResourceAllocator::CreateResourceAllocator(CreateBasicAllocatorDesc(), &resourceAllocator)); + CreateResourceAllocator(CreateBasicAllocatorDesc(), &resourceAllocator, nullptr)); ASSERT_NE(resourceAllocator, nullptr); ALLOCATION_DESC baseAllocationDesc = {}; @@ -940,83 +925,83 @@ TEST_F(D3D12ResourceAllocatorTests, CreateBufferWithin) { ALLOCATION_DESC smallBufferDesc = baseAllocationDesc; smallBufferDesc.HeapType = D3D12_HEAP_TYPE_UPLOAD; - ComPtr smallBuffer; + ComPtr smallBuffer; ASSERT_SUCCEEDED(resourceAllocator->CreateResource( smallBufferDesc, CreateBasicBufferDesc(4u, 1), D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, &smallBuffer)); - EXPECT_EQ(smallBuffer->GetMethod(), gpgmm::AllocationMethod::kSubAllocatedWithin); - EXPECT_EQ(smallBuffer->GetSize(), 4u); + EXPECT_EQ(smallBuffer->GetInfo().Method, gpgmm::AllocationMethod::kSubAllocatedWithin); + EXPECT_EQ(smallBuffer->GetInfo().SizeInBytes, 4u); EXPECT_EQ(smallBuffer->GetOffsetFromResource(), 0u); - EXPECT_EQ(smallBuffer->GetAlignment(), 4u); // Must re-align. + EXPECT_EQ(smallBuffer->GetInfo().Alignment, 4u); // Must re-align. EXPECT_EQ(resourceAllocator->GetInfo().UsedBlockCount, 1u); EXPECT_EQ(resourceAllocator->GetInfo().UsedMemoryCount, 1u); - EXPECT_EQ(resourceAllocator->GetInfo().UsedBlockUsage, smallBuffer->GetSize()); + EXPECT_EQ(resourceAllocator->GetInfo().UsedBlockUsage, smallBuffer->GetInfo().SizeInBytes); } { ALLOCATION_DESC smallBufferWithinDesc = baseAllocationDesc; smallBufferWithinDesc.HeapType = D3D12_HEAP_TYPE_UPLOAD; - ComPtr smallBuffer; + ComPtr smallBuffer; ASSERT_SUCCEEDED(resourceAllocator->CreateResource( smallBufferWithinDesc, CreateBasicBufferDesc(4u, 16), D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, &smallBuffer)); - EXPECT_EQ(smallBuffer->GetMethod(), gpgmm::AllocationMethod::kSubAllocatedWithin); - EXPECT_EQ(smallBuffer->GetSize(), 16u); + EXPECT_EQ(smallBuffer->GetInfo().Method, gpgmm::AllocationMethod::kSubAllocatedWithin); + EXPECT_EQ(smallBuffer->GetInfo().SizeInBytes, 16u); EXPECT_EQ(smallBuffer->GetOffsetFromResource(), 0u); - EXPECT_EQ(smallBuffer->GetAlignment(), 16u); + EXPECT_EQ(smallBuffer->GetInfo().Alignment, 16u); EXPECT_EQ(resourceAllocator->GetInfo().UsedBlockCount, 1u); EXPECT_EQ(resourceAllocator->GetInfo().UsedMemoryCount, 1u); - EXPECT_EQ(resourceAllocator->GetInfo().UsedBlockUsage, smallBuffer->GetSize()); + EXPECT_EQ(resourceAllocator->GetInfo().UsedBlockUsage, smallBuffer->GetInfo().SizeInBytes); } { ALLOCATION_DESC smallBufferWithinDesc = baseAllocationDesc; smallBufferWithinDesc.HeapType = D3D12_HEAP_TYPE_UPLOAD; - ComPtr smallBuffer; + ComPtr smallBuffer; ASSERT_SUCCEEDED(resourceAllocator->CreateResource( smallBufferWithinDesc, CreateBasicBufferDesc(4u), D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, &smallBuffer)); - EXPECT_EQ(smallBuffer->GetMethod(), gpgmm::AllocationMethod::kSubAllocatedWithin); - EXPECT_EQ(smallBuffer->GetSize(), 256u); + EXPECT_EQ(smallBuffer->GetInfo().Method, gpgmm::AllocationMethod::kSubAllocatedWithin); + EXPECT_EQ(smallBuffer->GetInfo().SizeInBytes, 256u); EXPECT_EQ(smallBuffer->GetOffsetFromResource(), 0u); - EXPECT_EQ(smallBuffer->GetAlignment(), 256u); // Re-align + EXPECT_EQ(smallBuffer->GetInfo().Alignment, 256u); // Re-align EXPECT_EQ(resourceAllocator->GetInfo().UsedBlockCount, 1u); EXPECT_EQ(resourceAllocator->GetInfo().UsedMemoryCount, 1u); - EXPECT_EQ(resourceAllocator->GetInfo().UsedBlockUsage, smallBuffer->GetSize()); + EXPECT_EQ(resourceAllocator->GetInfo().UsedBlockUsage, smallBuffer->GetInfo().SizeInBytes); } { ALLOCATION_DESC smallBufferWithinDesc = baseAllocationDesc; smallBufferWithinDesc.HeapType = D3D12_HEAP_TYPE_READBACK; - ComPtr smallBuffer; + ComPtr smallBuffer; ASSERT_SUCCEEDED(resourceAllocator->CreateResource( smallBufferWithinDesc, CreateBasicBufferDesc(4u), D3D12_RESOURCE_STATE_COPY_DEST, nullptr, &smallBuffer)); - EXPECT_EQ(smallBuffer->GetMethod(), gpgmm::AllocationMethod::kSubAllocatedWithin); - EXPECT_EQ(smallBuffer->GetSize(), 4u); + EXPECT_EQ(smallBuffer->GetInfo().Method, gpgmm::AllocationMethod::kSubAllocatedWithin); + EXPECT_EQ(smallBuffer->GetInfo().SizeInBytes, 4u); EXPECT_EQ(smallBuffer->GetOffsetFromResource(), 0u); - EXPECT_EQ(smallBuffer->GetAlignment(), 4u); + EXPECT_EQ(smallBuffer->GetInfo().Alignment, 4u); } { ALLOCATION_DESC smallBufferWithinDesc = baseAllocationDesc; smallBufferWithinDesc.HeapType = D3D12_HEAP_TYPE_READBACK; - ComPtr smallBuffer; + ComPtr smallBuffer; ASSERT_SUCCEEDED(resourceAllocator->CreateResource( smallBufferWithinDesc, CreateBasicBufferDesc(3u), D3D12_RESOURCE_STATE_COPY_DEST, nullptr, &smallBuffer)); - EXPECT_EQ(smallBuffer->GetMethod(), gpgmm::AllocationMethod::kSubAllocatedWithin); - EXPECT_EQ(smallBuffer->GetSize(), 4u); + EXPECT_EQ(smallBuffer->GetInfo().Method, gpgmm::AllocationMethod::kSubAllocatedWithin); + EXPECT_EQ(smallBuffer->GetInfo().SizeInBytes, 4u); EXPECT_EQ(smallBuffer->GetOffsetFromResource(), 0u); - EXPECT_EQ(smallBuffer->GetAlignment(), 4u); // Re-align + EXPECT_EQ(smallBuffer->GetInfo().Alignment, 4u); // Re-align } // Default heap using a required resource state of another compatible heap type is not allowed. @@ -1024,11 +1009,11 @@ TEST_F(D3D12ResourceAllocatorTests, CreateBufferWithin) { ALLOCATION_DESC invalidSmallBufferWithinDesc = baseAllocationDesc; invalidSmallBufferWithinDesc.HeapType = D3D12_HEAP_TYPE_DEFAULT; - ComPtr smallBuffer; + ComPtr smallBuffer; ASSERT_SUCCEEDED(resourceAllocator->CreateResource( invalidSmallBufferWithinDesc, CreateBasicBufferDesc(3u), D3D12_RESOURCE_STATE_COPY_DEST, nullptr, &smallBuffer)); - EXPECT_NE(smallBuffer->GetMethod(), gpgmm::AllocationMethod::kSubAllocatedWithin); + EXPECT_NE(smallBuffer->GetInfo().Method, gpgmm::AllocationMethod::kSubAllocatedWithin); } // Non-compatible heap type is not allowed reguardless of resource state specified. @@ -1036,11 +1021,11 @@ TEST_F(D3D12ResourceAllocatorTests, CreateBufferWithin) { ALLOCATION_DESC invalidSmallBufferWithinDesc = baseAllocationDesc; invalidSmallBufferWithinDesc.HeapType = D3D12_HEAP_TYPE_DEFAULT; - ComPtr smallBuffer; + ComPtr smallBuffer; ASSERT_SUCCEEDED(resourceAllocator->CreateResource( invalidSmallBufferWithinDesc, CreateBasicBufferDesc(3u), D3D12_RESOURCE_STATE_COMMON, nullptr, &smallBuffer)); - EXPECT_NE(smallBuffer->GetMethod(), gpgmm::AllocationMethod::kSubAllocatedWithin); + EXPECT_NE(smallBuffer->GetInfo().Method, gpgmm::AllocationMethod::kSubAllocatedWithin); } // Custom heaps should use a heap type inferred by the resource state required. @@ -1048,28 +1033,28 @@ TEST_F(D3D12ResourceAllocatorTests, CreateBufferWithin) { ALLOCATION_DESC smallBufferWithinDesc = baseAllocationDesc; smallBufferWithinDesc.HeapType = D3D12_HEAP_TYPE_CUSTOM; - ComPtr smallBuffer; + ComPtr smallBuffer; ASSERT_SUCCEEDED(resourceAllocator->CreateResource( smallBufferWithinDesc, CreateBasicBufferDesc(3u), D3D12_RESOURCE_STATE_COPY_DEST, nullptr, &smallBuffer)); - EXPECT_EQ(smallBuffer->GetMethod(), gpgmm::AllocationMethod::kSubAllocatedWithin); + EXPECT_EQ(smallBuffer->GetInfo().Method, gpgmm::AllocationMethod::kSubAllocatedWithin); } // Unspecified heap type should use the heap type inferred by the resource state // required. { - ComPtr smallBuffer; + ComPtr smallBuffer; ASSERT_SUCCEEDED(resourceAllocator->CreateResource( baseAllocationDesc, CreateBasicBufferDesc(3u), D3D12_RESOURCE_STATE_COPY_DEST, nullptr, &smallBuffer)); - EXPECT_EQ(smallBuffer->GetMethod(), gpgmm::AllocationMethod::kSubAllocatedWithin); + EXPECT_EQ(smallBuffer->GetInfo().Method, gpgmm::AllocationMethod::kSubAllocatedWithin); } { - ComPtr smallBuffer; + ComPtr smallBuffer; ASSERT_SUCCEEDED( resourceAllocator->CreateResource(baseAllocationDesc, CreateBasicBufferDesc(3u), D3D12_RESOURCE_STATE_COMMON, nullptr, &smallBuffer)); - EXPECT_NE(smallBuffer->GetMethod(), gpgmm::AllocationMethod::kSubAllocatedWithin); + EXPECT_NE(smallBuffer->GetInfo().Method, gpgmm::AllocationMethod::kSubAllocatedWithin); } // Resource flags are not allowed. @@ -1077,18 +1062,18 @@ TEST_F(D3D12ResourceAllocatorTests, CreateBufferWithin) { D3D12_RESOURCE_DESC resourceDescWithFlags = CreateBasicBufferDesc(3u); resourceDescWithFlags.Flags = D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE; - ComPtr smallBuffer; + ComPtr smallBuffer; ASSERT_SUCCEEDED(resourceAllocator->CreateResource( baseAllocationDesc, resourceDescWithFlags, D3D12_RESOURCE_STATE_COPY_DEST, nullptr, &smallBuffer)); - EXPECT_NE(smallBuffer->GetMethod(), gpgmm::AllocationMethod::kSubAllocatedWithin); + EXPECT_NE(smallBuffer->GetInfo().Method, gpgmm::AllocationMethod::kSubAllocatedWithin); } } TEST_F(D3D12ResourceAllocatorTests, CreateBufferWithinMany) { - ComPtr resourceAllocator; + ComPtr resourceAllocator; ASSERT_SUCCEEDED( - ResourceAllocator::CreateResourceAllocator(CreateBasicAllocatorDesc(), &resourceAllocator)); + CreateResourceAllocator(CreateBasicAllocatorDesc(), &resourceAllocator, nullptr)); ASSERT_NE(resourceAllocator, nullptr); ALLOCATION_DESC allocationDesc = {}; @@ -1098,35 +1083,35 @@ TEST_F(D3D12ResourceAllocatorTests, CreateBufferWithinMany) { const D3D12_RESOURCE_DESC& smallBufferDesc = CreateBasicBufferDesc(4u, 1); // Create two small buffers that will be byte-aligned. - ComPtr smallBufferA; + ComPtr smallBufferA; allocationDesc.DebugName = GPGMM_GET_VAR_NAME(smallBufferA); ASSERT_SUCCEEDED(resourceAllocator->CreateResource(allocationDesc, smallBufferDesc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, &smallBufferA)); ASSERT_NE(smallBufferA, nullptr); - EXPECT_EQ(smallBufferA->GetMethod(), gpgmm::AllocationMethod::kSubAllocatedWithin); - EXPECT_EQ(smallBufferA->GetSize(), smallBufferDesc.Width); + EXPECT_EQ(smallBufferA->GetInfo().Method, gpgmm::AllocationMethod::kSubAllocatedWithin); + EXPECT_EQ(smallBufferA->GetInfo().SizeInBytes, smallBufferDesc.Width); - ComPtr smallBufferB; + ComPtr smallBufferB; allocationDesc.DebugName = GPGMM_GET_VAR_NAME(smallBufferB); ASSERT_SUCCEEDED(resourceAllocator->CreateResource(allocationDesc, smallBufferDesc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, &smallBufferB)); ASSERT_NE(smallBufferB, nullptr); - EXPECT_EQ(smallBufferB->GetMethod(), gpgmm::AllocationMethod::kSubAllocatedWithin); - EXPECT_EQ(smallBufferB->GetSize(), smallBufferDesc.Width); + EXPECT_EQ(smallBufferB->GetInfo().Method, gpgmm::AllocationMethod::kSubAllocatedWithin); + EXPECT_EQ(smallBufferB->GetInfo().SizeInBytes, smallBufferDesc.Width); - ComPtr smallBufferC; + ComPtr smallBufferC; allocationDesc.DebugName = GPGMM_GET_VAR_NAME(smallBufferC); ASSERT_SUCCEEDED(resourceAllocator->CreateResource(allocationDesc, smallBufferDesc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, &smallBufferC)); ASSERT_NE(smallBufferC, nullptr); - EXPECT_EQ(smallBufferC->GetMethod(), gpgmm::AllocationMethod::kSubAllocatedWithin); - EXPECT_EQ(smallBufferC->GetSize(), smallBufferDesc.Width); + EXPECT_EQ(smallBufferC->GetInfo().Method, gpgmm::AllocationMethod::kSubAllocatedWithin); + EXPECT_EQ(smallBufferC->GetInfo().SizeInBytes, smallBufferDesc.Width); EXPECT_EQ(resourceAllocator->GetInfo().UsedBlockCount, 3u); EXPECT_EQ(resourceAllocator->GetInfo().UsedMemoryCount, 1u); @@ -1154,19 +1139,22 @@ TEST_F(D3D12ResourceAllocatorTests, CreateBufferWithinMany) { ASSERT_FAILED(smallBufferC->Map(/*subresource*/ 1)); // Fill small buffer C with value 0xCC. - std::vector dataCC(static_cast(smallBufferC->GetSize()), 0xCC); + std::vector dataCC(static_cast(smallBufferC->GetInfo().SizeInBytes), + 0xCC); void* mappedBufferC = nullptr; ASSERT_SUCCEEDED(smallBufferC->Map(0, nullptr, &mappedBufferC)); memcpy(mappedBufferC, dataCC.data(), dataCC.size()); // Fill small buffer A with value 0xAA. - std::vector dataAA(static_cast(smallBufferA->GetSize()), 0xAA); + std::vector dataAA(static_cast(smallBufferA->GetInfo().SizeInBytes), + 0xAA); void* mappedBufferA = nullptr; ASSERT_SUCCEEDED(smallBufferA->Map(0, nullptr, &mappedBufferA)); memcpy(mappedBufferA, dataAA.data(), dataAA.size()); // Fill small buffer B with value 0xBB. - std::vector dataBB(static_cast(smallBufferB->GetSize()), 0xBB); + std::vector dataBB(static_cast(smallBufferB->GetInfo().SizeInBytes), + 0xBB); void* mappedBufferB = nullptr; ASSERT_SUCCEEDED(smallBufferB->Map(0, nullptr, &mappedBufferB)); memcpy(mappedBufferB, dataBB.data(), dataBB.size()); @@ -1179,15 +1167,15 @@ TEST_F(D3D12ResourceAllocatorTests, CreateBufferWithinMany) { ASSERT_SUCCEEDED(smallBufferB->GetResource()->Map(0, nullptr, &mappedBuffer)); const uint8_t* mappedByte = static_cast(mappedBuffer); - for (uint32_t i = 0; i < smallBufferA->GetSize(); i++, mappedByte++) { + for (uint32_t i = 0; i < smallBufferA->GetInfo().SizeInBytes; i++, mappedByte++) { EXPECT_EQ(*mappedByte, 0xAA); } - for (uint32_t i = 0; i < smallBufferB->GetSize(); i++, mappedByte++) { + for (uint32_t i = 0; i < smallBufferB->GetInfo().SizeInBytes; i++, mappedByte++) { EXPECT_EQ(*mappedByte, 0xBB); } - for (uint32_t i = 0; i < smallBufferC->GetSize(); i++, mappedByte++) { + for (uint32_t i = 0; i < smallBufferC->GetInfo().SizeInBytes; i++, mappedByte++) { EXPECT_EQ(*mappedByte, 0xCC); } @@ -1201,9 +1189,9 @@ TEST_F(D3D12ResourceAllocatorTests, CreateBufferWithinMany) { } TEST_F(D3D12ResourceAllocatorTests, CreateBufferNeverSubAllocated) { - ComPtr resourceAllocator; + ComPtr resourceAllocator; ASSERT_SUCCEEDED( - ResourceAllocator::CreateResourceAllocator(CreateBasicAllocatorDesc(), &resourceAllocator)); + CreateResourceAllocator(CreateBasicAllocatorDesc(), &resourceAllocator, nullptr)); ASSERT_NE(resourceAllocator, nullptr); constexpr uint64_t bufferSize = kBufferOf4MBAllocationSize / 2; @@ -1212,21 +1200,21 @@ TEST_F(D3D12ResourceAllocatorTests, CreateBufferNeverSubAllocated) { allocationDesc.HeapType = D3D12_HEAP_TYPE_UPLOAD; allocationDesc.Flags = ALLOCATION_FLAG_NEVER_SUBALLOCATE_MEMORY; - ComPtr subAllocation; + ComPtr subAllocation; ASSERT_SUCCEEDED(resourceAllocator->CreateResource( allocationDesc, CreateBasicBufferDesc(bufferSize), D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, &subAllocation)); ASSERT_NE(subAllocation, nullptr); EXPECT_NE(subAllocation->GetResource(), nullptr); - EXPECT_NE(subAllocation->GetMethod(), gpgmm::AllocationMethod::kSubAllocated); + EXPECT_NE(subAllocation->GetInfo().Method, gpgmm::AllocationMethod::kSubAllocated); } TEST_F(D3D12ResourceAllocatorTests, CreateBufferNeverPooled) { ALLOCATOR_DESC allocatorDesc = CreateBasicAllocatorDesc(); allocatorDesc.Flags |= ALLOCATOR_FLAG_ALWAYS_ON_DEMAND; - ComPtr resourceAllocator; - ASSERT_SUCCEEDED(ResourceAllocator::CreateResourceAllocator(allocatorDesc, &resourceAllocator)); + ComPtr resourceAllocator; + ASSERT_SUCCEEDED(CreateResourceAllocator(allocatorDesc, &resourceAllocator, nullptr)); ASSERT_NE(resourceAllocator, nullptr); ALLOCATION_DESC baseAllocationDesc = {}; @@ -1236,7 +1224,7 @@ TEST_F(D3D12ResourceAllocatorTests, CreateBufferNeverPooled) { // Create the first buffer of size A without recyling its memory. { - ComPtr allocation; + ComPtr allocation; ASSERT_SUCCEEDED(resourceAllocator->CreateResource( baseAllocationDesc, CreateBasicBufferDesc(bufferSize * 2), D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, &allocation)); @@ -1249,7 +1237,7 @@ TEST_F(D3D12ResourceAllocatorTests, CreateBufferNeverPooled) { ALLOCATION_DESC allocationDesc = baseAllocationDesc; allocationDesc.Flags = ALLOCATION_FLAG_NEVER_ALLOCATE_MEMORY; - ComPtr allocation; + ComPtr allocation; ASSERT_FAILED(resourceAllocator->CreateResource( allocationDesc, CreateBasicBufferDesc(bufferSize * 2), D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, &allocation)); @@ -1257,7 +1245,7 @@ TEST_F(D3D12ResourceAllocatorTests, CreateBufferNeverPooled) { // Create another buffer of size B which cannot use recycled memory of size A. { - ComPtr allocation; + ComPtr allocation; ASSERT_SUCCEEDED(resourceAllocator->CreateResource( baseAllocationDesc, CreateBasicBufferDesc(bufferSize * 3), D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, &allocation)); @@ -1270,7 +1258,7 @@ TEST_F(D3D12ResourceAllocatorTests, CreateBufferNeverPooled) { ALLOCATION_DESC allocationDesc = baseAllocationDesc; allocationDesc.Flags = ALLOCATION_FLAG_NEVER_ALLOCATE_MEMORY; - ComPtr allocation; + ComPtr allocation; ASSERT_FAILED(resourceAllocator->CreateResource( allocationDesc, CreateBasicBufferDesc(bufferSize * 3), D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, &allocation)); @@ -1282,8 +1270,8 @@ TEST_F(D3D12ResourceAllocatorTests, CreateBufferPooled) { ALLOCATOR_DESC allocatorDesc = CreateBasicAllocatorDesc(); - ComPtr poolAllocator; - ASSERT_SUCCEEDED(ResourceAllocator::CreateResourceAllocator(allocatorDesc, &poolAllocator)); + ComPtr poolAllocator; + ASSERT_SUCCEEDED(CreateResourceAllocator(allocatorDesc, &poolAllocator, nullptr)); ASSERT_NE(poolAllocator, nullptr); // Only standalone allocations can be pool-allocated. @@ -1293,24 +1281,24 @@ TEST_F(D3D12ResourceAllocatorTests, CreateBufferPooled) { // Create buffer of size A with it's own resource heap that will be returned to the pool. { - ComPtr allocation; + ComPtr allocation; ASSERT_SUCCEEDED(poolAllocator->CreateResource( standaloneAllocationDesc, CreateBasicBufferDesc(bufferSize), D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, &allocation)); ASSERT_NE(allocation, nullptr); EXPECT_NE(allocation->GetResource(), nullptr); - EXPECT_EQ(allocation->GetMethod(), gpgmm::AllocationMethod::kStandalone); + EXPECT_EQ(allocation->GetInfo().Method, gpgmm::AllocationMethod::kStandalone); } // Create buffer of size B with it's own resource heap that will be returned to the pool. { - ComPtr allocation; + ComPtr allocation; ASSERT_SUCCEEDED(poolAllocator->CreateResource( standaloneAllocationDesc, CreateBasicBufferDesc(bufferSize / 2), D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, &allocation)); ASSERT_NE(allocation, nullptr); EXPECT_NE(allocation->GetResource(), nullptr); - EXPECT_EQ(allocation->GetMethod(), gpgmm::AllocationMethod::kStandalone); + EXPECT_EQ(allocation->GetInfo().Method, gpgmm::AllocationMethod::kStandalone); } // Create buffer of size A again with it's own resource heap from the pool. @@ -1318,13 +1306,13 @@ TEST_F(D3D12ResourceAllocatorTests, CreateBufferPooled) { ALLOCATION_DESC reusePoolOnlyDesc = standaloneAllocationDesc; reusePoolOnlyDesc.Flags = standaloneAllocationDesc.Flags | ALLOCATION_FLAG_NEVER_ALLOCATE_MEMORY; - ComPtr allocation; + ComPtr allocation; ASSERT_SUCCEEDED( poolAllocator->CreateResource(reusePoolOnlyDesc, CreateBasicBufferDesc(bufferSize), D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, &allocation)); ASSERT_NE(allocation, nullptr); EXPECT_NE(allocation->GetResource(), nullptr); - EXPECT_EQ(allocation->GetMethod(), gpgmm::AllocationMethod::kStandalone); + EXPECT_EQ(allocation->GetInfo().Method, gpgmm::AllocationMethod::kStandalone); } // Create buffer of size B again with it's own resource heap from the pool. @@ -1332,13 +1320,13 @@ TEST_F(D3D12ResourceAllocatorTests, CreateBufferPooled) { ALLOCATION_DESC reusePoolOnlyDesc = standaloneAllocationDesc; reusePoolOnlyDesc.Flags = standaloneAllocationDesc.Flags | ALLOCATION_FLAG_NEVER_ALLOCATE_MEMORY; - ComPtr allocation; + ComPtr allocation; ASSERT_SUCCEEDED( poolAllocator->CreateResource(reusePoolOnlyDesc, CreateBasicBufferDesc(bufferSize / 2), D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, &allocation)); ASSERT_NE(allocation, nullptr); EXPECT_NE(allocation->GetResource(), nullptr); - EXPECT_EQ(allocation->GetMethod(), gpgmm::AllocationMethod::kStandalone); + EXPECT_EQ(allocation->GetInfo().Method, gpgmm::AllocationMethod::kStandalone); } EXPECT_EQ(poolAllocator->GetInfo().FreeMemoryUsage, bufferSize + bufferSize / 2); @@ -1352,7 +1340,7 @@ TEST_F(D3D12ResourceAllocatorTests, CreateBufferPooled) { ALLOCATION_DESC reusePoolOnlyDesc = standaloneAllocationDesc; reusePoolOnlyDesc.Flags = standaloneAllocationDesc.Flags | ALLOCATION_FLAG_NEVER_ALLOCATE_MEMORY; - ComPtr allocation; + ComPtr allocation; ASSERT_FAILED( poolAllocator->CreateResource(reusePoolOnlyDesc, CreateBasicBufferDesc(bufferSize), D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, &allocation)); @@ -1363,7 +1351,7 @@ TEST_F(D3D12ResourceAllocatorTests, CreateBufferPooled) { ALLOCATION_DESC reusePoolOnlyDesc = standaloneAllocationDesc; reusePoolOnlyDesc.Flags = standaloneAllocationDesc.Flags | ALLOCATION_FLAG_NEVER_ALLOCATE_MEMORY; - ComPtr allocation; + ComPtr allocation; ASSERT_FAILED( poolAllocator->CreateResource(reusePoolOnlyDesc, CreateBasicBufferDesc(bufferSize / 2), D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, &allocation)); @@ -1373,16 +1361,16 @@ TEST_F(D3D12ResourceAllocatorTests, CreateBufferPooled) { { ALLOCATOR_DESC desc = CreateBasicAllocatorDesc(); - ComPtr resourceAllocator; - ASSERT_SUCCEEDED(ResourceAllocator::CreateResourceAllocator(desc, &resourceAllocator)); + ComPtr resourceAllocator; + ASSERT_SUCCEEDED(CreateResourceAllocator(desc, &resourceAllocator, nullptr)); ASSERT_NE(resourceAllocator, nullptr); - ComPtr allocation; + ComPtr allocation; ASSERT_SUCCEEDED(resourceAllocator->CreateResource( standaloneAllocationDesc, CreateBasicBufferDesc(1024), D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, &allocation)); ASSERT_NE(allocation, nullptr); - EXPECT_EQ(allocation->GetMethod(), gpgmm::AllocationMethod::kStandalone); + EXPECT_EQ(allocation->GetInfo().Method, gpgmm::AllocationMethod::kStandalone); } EXPECT_EQ(poolAllocator->GetInfo().FreeMemoryUsage, 0u); @@ -1391,21 +1379,21 @@ TEST_F(D3D12ResourceAllocatorTests, CreateBufferPooled) { TEST_F(D3D12ResourceAllocatorTests, CreateBufferGetInfo) { // Calculate info for a single standalone allocation. { - ComPtr resourceAllocator; - ASSERT_SUCCEEDED(ResourceAllocator::CreateResourceAllocator(CreateBasicAllocatorDesc(), - &resourceAllocator)); + ComPtr resourceAllocator; + ASSERT_SUCCEEDED( + CreateResourceAllocator(CreateBasicAllocatorDesc(), &resourceAllocator, nullptr)); ASSERT_NE(resourceAllocator, nullptr); ALLOCATION_DESC standaloneAllocationDesc = {}; standaloneAllocationDesc.Flags = ALLOCATION_FLAG_NEVER_SUBALLOCATE_MEMORY; standaloneAllocationDesc.HeapType = D3D12_HEAP_TYPE_DEFAULT; - ComPtr firstAllocation; + ComPtr firstAllocation; ASSERT_SUCCEEDED(resourceAllocator->CreateResource( standaloneAllocationDesc, CreateBasicBufferDesc(kBufferOf4MBAllocationSize), D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, &firstAllocation)); ASSERT_NE(firstAllocation, nullptr); - EXPECT_EQ(firstAllocation->GetMethod(), gpgmm::AllocationMethod::kStandalone); + EXPECT_EQ(firstAllocation->GetInfo().Method, gpgmm::AllocationMethod::kStandalone); RESOURCE_ALLOCATOR_INFO info = resourceAllocator->GetInfo(); EXPECT_EQ(info.UsedMemoryCount, 1u); @@ -1416,32 +1404,31 @@ TEST_F(D3D12ResourceAllocatorTests, CreateBufferGetInfo) { { ALLOCATOR_DESC allocatorDesc = CreateBasicAllocatorDesc(); - ComPtr resourceAllocator; - ASSERT_SUCCEEDED( - ResourceAllocator::CreateResourceAllocator(allocatorDesc, &resourceAllocator)); + ComPtr resourceAllocator; + ASSERT_SUCCEEDED(CreateResourceAllocator(allocatorDesc, &resourceAllocator, nullptr)); ASSERT_NE(resourceAllocator, nullptr); ALLOCATION_DESC standaloneAllocationDesc = {}; standaloneAllocationDesc.Flags = ALLOCATION_FLAG_NEVER_SUBALLOCATE_MEMORY; standaloneAllocationDesc.HeapType = D3D12_HEAP_TYPE_DEFAULT; - ComPtr firstAllocation; + ComPtr firstAllocation; ASSERT_SUCCEEDED(resourceAllocator->CreateResource( standaloneAllocationDesc, CreateBasicBufferDesc(kBufferOf4MBAllocationSize), D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, &firstAllocation)); ASSERT_NE(firstAllocation, nullptr); - EXPECT_EQ(firstAllocation->GetMethod(), gpgmm::AllocationMethod::kStandalone); + EXPECT_EQ(firstAllocation->GetInfo().Method, gpgmm::AllocationMethod::kStandalone); RESOURCE_ALLOCATOR_INFO info = resourceAllocator->GetInfo(); EXPECT_EQ(info.UsedMemoryCount, 1u); EXPECT_EQ(info.UsedMemoryUsage, kBufferOf4MBAllocationSize); - ComPtr secondAllocation; + ComPtr secondAllocation; ASSERT_SUCCEEDED(resourceAllocator->CreateResource( standaloneAllocationDesc, CreateBasicBufferDesc(kBufferOf4MBAllocationSize), D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, &secondAllocation)); ASSERT_NE(secondAllocation, nullptr); - EXPECT_EQ(secondAllocation->GetMethod(), gpgmm::AllocationMethod::kStandalone); + EXPECT_EQ(secondAllocation->GetInfo().Method, gpgmm::AllocationMethod::kStandalone); info = resourceAllocator->GetInfo(); EXPECT_EQ(info.UsedMemoryCount, 2u); @@ -1450,16 +1437,16 @@ TEST_F(D3D12ResourceAllocatorTests, CreateBufferGetInfo) { // Calculate info for two sub-allocations. { - ComPtr resourceAllocator; - ASSERT_SUCCEEDED(ResourceAllocator::CreateResourceAllocator(CreateBasicAllocatorDesc(), - &resourceAllocator)); + ComPtr resourceAllocator; + ASSERT_SUCCEEDED( + CreateResourceAllocator(CreateBasicAllocatorDesc(), &resourceAllocator, nullptr)); ASSERT_NE(resourceAllocator, nullptr); ALLOCATION_DESC subAllocationDesc = {}; subAllocationDesc.HeapType = D3D12_HEAP_TYPE_DEFAULT; constexpr uint64_t kBufferSize = kBufferOf4MBAllocationSize / 8; - ComPtr firstAllocation; + ComPtr firstAllocation; ASSERT_SUCCEEDED(resourceAllocator->CreateResource( subAllocationDesc, CreateBasicBufferDesc(kBufferSize), D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, &firstAllocation)); @@ -1468,7 +1455,8 @@ TEST_F(D3D12ResourceAllocatorTests, CreateBufferGetInfo) { // Depending on the device, sub-allocation could fail. Since this test relies on a // sub-allocator's info counts, it must be skipped. // TODO: Consider testing counts by allocator type. - GPGMM_SKIP_TEST_IF(firstAllocation->GetMethod() != gpgmm::AllocationMethod::kSubAllocated); + GPGMM_SKIP_TEST_IF(firstAllocation->GetInfo().Method != + gpgmm::AllocationMethod::kSubAllocated); RESOURCE_ALLOCATOR_INFO info = resourceAllocator->GetInfo(); EXPECT_EQ(info.UsedMemoryCount, 1u); @@ -1476,12 +1464,12 @@ TEST_F(D3D12ResourceAllocatorTests, CreateBufferGetInfo) { EXPECT_EQ(info.UsedBlockCount, 1u); EXPECT_GE(info.UsedBlockUsage, kBufferSize); - ComPtr secondAllocation; + ComPtr secondAllocation; ASSERT_SUCCEEDED(resourceAllocator->CreateResource( subAllocationDesc, CreateBasicBufferDesc(kBufferSize), D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, &secondAllocation)); ASSERT_NE(secondAllocation, nullptr); - EXPECT_EQ(secondAllocation->GetMethod(), gpgmm::AllocationMethod::kSubAllocated); + EXPECT_EQ(secondAllocation->GetInfo().Method, gpgmm::AllocationMethod::kSubAllocated); info = resourceAllocator->GetInfo(); EXPECT_GE(info.UsedMemoryCount, 1u); @@ -1492,9 +1480,9 @@ TEST_F(D3D12ResourceAllocatorTests, CreateBufferGetInfo) { // Calculate info for two sub-allocations within the same resource. { - ComPtr resourceAllocator; - ASSERT_SUCCEEDED(ResourceAllocator::CreateResourceAllocator(CreateBasicAllocatorDesc(), - &resourceAllocator)); + ComPtr resourceAllocator; + ASSERT_SUCCEEDED( + CreateResourceAllocator(CreateBasicAllocatorDesc(), &resourceAllocator, nullptr)); ASSERT_NE(resourceAllocator, nullptr); ALLOCATION_DESC allocationWithinDesc = {}; @@ -1503,12 +1491,12 @@ TEST_F(D3D12ResourceAllocatorTests, CreateBufferGetInfo) { constexpr uint32_t kBufferSize = 4u; // Must less than 64KB - ComPtr firstAllocation; + ComPtr firstAllocation; ASSERT_SUCCEEDED(resourceAllocator->CreateResource( allocationWithinDesc, CreateBasicBufferDesc(kBufferSize, 1), D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, &firstAllocation)); ASSERT_NE(firstAllocation, nullptr); - EXPECT_EQ(firstAllocation->GetMethod(), gpgmm::AllocationMethod::kSubAllocatedWithin); + EXPECT_EQ(firstAllocation->GetInfo().Method, gpgmm::AllocationMethod::kSubAllocatedWithin); RESOURCE_ALLOCATOR_INFO info = resourceAllocator->GetInfo(); EXPECT_EQ(info.UsedMemoryCount, 1u); @@ -1516,12 +1504,12 @@ TEST_F(D3D12ResourceAllocatorTests, CreateBufferGetInfo) { EXPECT_EQ(info.UsedBlockCount, 1u); EXPECT_EQ(info.UsedBlockUsage, kBufferSize); - ComPtr secondAllocation; + ComPtr secondAllocation; ASSERT_SUCCEEDED(resourceAllocator->CreateResource( allocationWithinDesc, CreateBasicBufferDesc(kBufferSize, 1), D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, &secondAllocation)); ASSERT_NE(secondAllocation, nullptr); - EXPECT_EQ(secondAllocation->GetMethod(), gpgmm::AllocationMethod::kSubAllocatedWithin); + EXPECT_EQ(secondAllocation->GetInfo().Method, gpgmm::AllocationMethod::kSubAllocatedWithin); info = resourceAllocator->GetInfo(); EXPECT_EQ(info.UsedMemoryCount, 1u); @@ -1532,10 +1520,10 @@ TEST_F(D3D12ResourceAllocatorTests, CreateBufferGetInfo) { } TEST_F(D3D12ResourceAllocatorTests, CreateTexturePooled) { - ComPtr poolAllocator; + ComPtr poolAllocator; { ALLOCATOR_DESC allocatorDesc = CreateBasicAllocatorDesc(); - ASSERT_SUCCEEDED(ResourceAllocator::CreateResourceAllocator(allocatorDesc, &poolAllocator)); + ASSERT_SUCCEEDED(CreateResourceAllocator(allocatorDesc, &poolAllocator, nullptr)); ASSERT_NE(poolAllocator, nullptr); } @@ -1547,12 +1535,12 @@ TEST_F(D3D12ResourceAllocatorTests, CreateTexturePooled) { // Create a small texture of size A with it's own resource heap that will be returned to the // pool. { - ComPtr firstAllocation; + ComPtr firstAllocation; ASSERT_SUCCEEDED(poolAllocator->CreateResource( standaloneAllocationDesc, CreateBasicTextureDesc(DXGI_FORMAT_R8G8B8A8_UNORM, 1, 1), D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, &firstAllocation)); ASSERT_NE(firstAllocation, nullptr); - EXPECT_EQ(firstAllocation->GetMethod(), gpgmm::AllocationMethod::kStandalone); + EXPECT_EQ(firstAllocation->GetInfo().Method, gpgmm::AllocationMethod::kStandalone); } ALLOCATION_DESC reusePoolOnlyDesc = standaloneAllocationDesc; @@ -1561,12 +1549,12 @@ TEST_F(D3D12ResourceAllocatorTests, CreateTexturePooled) { // Check the first small texture of size A was pool-allocated by creating it again. { - ComPtr secondAllocation; + ComPtr secondAllocation; ASSERT_SUCCEEDED(poolAllocator->CreateResource( reusePoolOnlyDesc, CreateBasicTextureDesc(DXGI_FORMAT_R8G8B8A8_UNORM, 1, 1), D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, &secondAllocation)); ASSERT_NE(secondAllocation, nullptr); - EXPECT_EQ(secondAllocation->GetMethod(), gpgmm::AllocationMethod::kStandalone); + EXPECT_EQ(secondAllocation->GetInfo().Method, gpgmm::AllocationMethod::kStandalone); } } @@ -1585,12 +1573,11 @@ TEST_F(D3D12ResourceAllocatorTests, CreateBufferWithLimitedFragmentation) { // By default, buffer should be sub-allocated. { - ComPtr resourceAllocator; - ASSERT_SUCCEEDED( - ResourceAllocator::CreateResourceAllocator(allocatorDesc, &resourceAllocator)); + ComPtr resourceAllocator; + ASSERT_SUCCEEDED(CreateResourceAllocator(allocatorDesc, &resourceAllocator, nullptr)); ASSERT_NE(resourceAllocator, nullptr); - ComPtr allocation; + ComPtr allocation; ASSERT_SUCCEEDED( resourceAllocator->CreateResource(baseAllocationDesc, CreateBasicBufferDesc(1), D3D12_RESOURCE_STATE_COMMON, nullptr, &allocation)); @@ -1600,15 +1587,14 @@ TEST_F(D3D12ResourceAllocatorTests, CreateBufferWithLimitedFragmentation) { // Force standalone buffer creation. { - ComPtr resourceAllocator; - ASSERT_SUCCEEDED( - ResourceAllocator::CreateResourceAllocator(allocatorDesc, &resourceAllocator)); + ComPtr resourceAllocator; + ASSERT_SUCCEEDED(CreateResourceAllocator(allocatorDesc, &resourceAllocator, nullptr)); ASSERT_NE(resourceAllocator, nullptr); ALLOCATION_DESC standaloneAllocationDesc = baseAllocationDesc; standaloneAllocationDesc.Flags |= ALLOCATION_FLAG_NEVER_SUBALLOCATE_MEMORY; - ComPtr allocation; + ComPtr allocation; ASSERT_SUCCEEDED( resourceAllocator->CreateResource(standaloneAllocationDesc, CreateBasicBufferDesc(1), D3D12_RESOURCE_STATE_COMMON, nullptr, &allocation)); @@ -1620,12 +1606,11 @@ TEST_F(D3D12ResourceAllocatorTests, CreateBufferWithLimitedFragmentation) { { allocatorDesc.Flags |= ALLOCATOR_FLAG_ALWAYS_COMMITED; - ComPtr commitedAllocator; - ASSERT_SUCCEEDED( - ResourceAllocator::CreateResourceAllocator(allocatorDesc, &commitedAllocator)); + ComPtr commitedAllocator; + ASSERT_SUCCEEDED(CreateResourceAllocator(allocatorDesc, &commitedAllocator, nullptr)); ASSERT_NE(commitedAllocator, nullptr); - ComPtr allocation; + ComPtr allocation; ASSERT_SUCCEEDED( commitedAllocator->CreateResource(baseAllocationDesc, CreateBasicBufferDesc(1), D3D12_RESOURCE_STATE_COMMON, nullptr, &allocation)); @@ -1641,8 +1626,8 @@ TEST_F(D3D12ResourceAllocatorTests, CreateBufferManyPrefetch) { ALLOCATOR_DESC allocatorDesc = CreateBasicAllocatorDesc(); allocatorDesc.Flags ^= ALLOCATOR_FLAG_DISABLE_MEMORY_PREFETCH; - ComPtr resourceAllocator; - ASSERT_SUCCEEDED(ResourceAllocator::CreateResourceAllocator(allocatorDesc, &resourceAllocator)); + ComPtr resourceAllocator; + ASSERT_SUCCEEDED(CreateResourceAllocator(allocatorDesc, &resourceAllocator, nullptr)); ASSERT_NE(resourceAllocator, nullptr); constexpr uint64_t kNumOfBuffers = 1000u; @@ -1653,9 +1638,9 @@ TEST_F(D3D12ResourceAllocatorTests, CreateBufferManyPrefetch) { constexpr uint32_t kMinBufferSize = GPGMM_KB_TO_BYTES(64); - std::set> allocs = {}; + std::set> allocs = {}; for (uint64_t i = 0; i < kNumOfBuffers; i++) { - ComPtr allocation; + ComPtr allocation; ASSERT_SUCCEEDED( resourceAllocator->CreateResource(allocationDesc, CreateBasicBufferDesc(kMinBufferSize), D3D12_RESOURCE_STATE_COMMON, nullptr, &allocation)); @@ -1668,9 +1653,9 @@ TEST_F(D3D12ResourceAllocatorTests, CreateBufferManyPrefetch) { // Creates a bunch of buffers concurrently. TEST_F(D3D12ResourceAllocatorTests, CreateBufferManyThreaded) { - ComPtr resourceAllocator; + ComPtr resourceAllocator; ASSERT_SUCCEEDED( - ResourceAllocator::CreateResourceAllocator(CreateBasicAllocatorDesc(), &resourceAllocator)); + CreateResourceAllocator(CreateBasicAllocatorDesc(), &resourceAllocator, nullptr)); ASSERT_NE(resourceAllocator, nullptr); ALLOCATION_DESC allocationDesc = {}; @@ -1680,7 +1665,7 @@ TEST_F(D3D12ResourceAllocatorTests, CreateBufferManyThreaded) { std::vector threads(kThreadCount); for (size_t threadIdx = 0; threadIdx < threads.size(); threadIdx++) { threads[threadIdx] = std::thread([&]() { - ComPtr allocation; + ComPtr allocation; ASSERT_SUCCEEDED(resourceAllocator->CreateResource( allocationDesc, CreateBasicBufferDesc(kBufferOf4MBAllocationSize), D3D12_RESOURCE_STATE_COMMON, nullptr, &allocation)); @@ -1697,9 +1682,9 @@ TEST_F(D3D12ResourceAllocatorTests, CreateBufferManyThreaded) { // Creates a bunch of buffers concurrently. TEST_F(D3D12ResourceAllocatorTests, CreateBufferWithinManyThreaded) { - ComPtr resourceAllocator; + ComPtr resourceAllocator; ASSERT_SUCCEEDED( - ResourceAllocator::CreateResourceAllocator(CreateBasicAllocatorDesc(), &resourceAllocator)); + CreateResourceAllocator(CreateBasicAllocatorDesc(), &resourceAllocator, nullptr)); ASSERT_NE(resourceAllocator, nullptr); ALLOCATION_DESC allocationDesc = {}; @@ -1712,12 +1697,12 @@ TEST_F(D3D12ResourceAllocatorTests, CreateBufferWithinManyThreaded) { std::vector threads(kThreadCount); for (size_t threadIdx = 0; threadIdx < threads.size(); threadIdx++) { threads[threadIdx] = std::thread([&]() { - ComPtr allocation; + ComPtr allocation; ASSERT_SUCCEEDED(resourceAllocator->CreateResource( allocationDesc, CreateBasicBufferDesc(kSmallBufferSize), D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, &allocation)); ASSERT_NE(allocation, nullptr); - EXPECT_EQ(allocation->GetMethod(), gpgmm::AllocationMethod::kSubAllocatedWithin); + EXPECT_EQ(allocation->GetInfo().Method, gpgmm::AllocationMethod::kSubAllocatedWithin); }); } @@ -1733,9 +1718,9 @@ TEST_F(D3D12ResourceAllocatorTests, CreateBufferCacheSize) { // skip the test. GPGMM_SKIP_TEST_IF(IsSizeCacheEnabled()); - ComPtr resourceAllocator; + ComPtr resourceAllocator; ASSERT_SUCCEEDED( - ResourceAllocator::CreateResourceAllocator(CreateBasicAllocatorDesc(), &resourceAllocator)); + CreateResourceAllocator(CreateBasicAllocatorDesc(), &resourceAllocator, nullptr)); ASSERT_NE(resourceAllocator, nullptr); // First request is always a cache miss. @@ -1744,7 +1729,7 @@ TEST_F(D3D12ResourceAllocatorTests, CreateBufferCacheSize) { baseAllocationDesc.Flags |= ALLOCATION_FLAG_ALWAYS_CACHE_SIZE; { - ComPtr allocation; + ComPtr allocation; ALLOCATION_DESC smallResourceAllocDesc = baseAllocationDesc; smallResourceAllocDesc.HeapType = D3D12_HEAP_TYPE_UPLOAD; @@ -1757,11 +1742,11 @@ TEST_F(D3D12ResourceAllocatorTests, CreateBufferCacheSize) { D3D12_SMALL_RESOURCE_PLACEMENT_ALIGNMENT)), D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, &allocation)); ASSERT_NE(allocation, nullptr); - EXPECT_EQ(allocation->GetSize(), + EXPECT_EQ(allocation->GetInfo().SizeInBytes, static_cast(D3D12_SMALL_RESOURCE_PLACEMENT_ALIGNMENT)); } { - ComPtr allocation; + ComPtr allocation; EXPECT_SIZE_CACHE_MISS( resourceAllocator, resourceAllocator->CreateResource( @@ -1769,11 +1754,11 @@ TEST_F(D3D12ResourceAllocatorTests, CreateBufferCacheSize) { CreateBasicBufferDesc(D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT), D3D12_RESOURCE_STATE_COMMON, nullptr, &allocation)); ASSERT_NE(allocation, nullptr); - EXPECT_EQ(allocation->GetSize(), + EXPECT_EQ(allocation->GetInfo().SizeInBytes, static_cast(D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT)); } { - ComPtr allocation; + ComPtr allocation; EXPECT_SIZE_CACHE_MISS( resourceAllocator, resourceAllocator->CreateResource( @@ -1781,13 +1766,13 @@ TEST_F(D3D12ResourceAllocatorTests, CreateBufferCacheSize) { CreateBasicBufferDesc(D3D12_DEFAULT_MSAA_RESOURCE_PLACEMENT_ALIGNMENT), D3D12_RESOURCE_STATE_COMMON, nullptr, &allocation)); ASSERT_NE(allocation, nullptr); - EXPECT_EQ(allocation->GetSize(), + EXPECT_EQ(allocation->GetInfo().SizeInBytes, static_cast(D3D12_DEFAULT_MSAA_RESOURCE_PLACEMENT_ALIGNMENT)); } // Second request is always a cache hit. { - ComPtr allocation; + ComPtr allocation; ALLOCATION_DESC smallResourceAllocDesc = baseAllocationDesc; smallResourceAllocDesc.HeapType = D3D12_HEAP_TYPE_UPLOAD; @@ -1799,22 +1784,22 @@ TEST_F(D3D12ResourceAllocatorTests, CreateBufferCacheSize) { CreateBasicBufferDesc(D3D12_SMALL_RESOURCE_PLACEMENT_ALIGNMENT), D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, &allocation)); ASSERT_NE(allocation, nullptr); - EXPECT_EQ(allocation->GetSize(), + EXPECT_EQ(allocation->GetInfo().SizeInBytes, static_cast(D3D12_SMALL_RESOURCE_PLACEMENT_ALIGNMENT)); } { - ComPtr allocation; + ComPtr allocation; EXPECT_SIZE_CACHE_HIT(resourceAllocator, resourceAllocator->CreateResource( baseAllocationDesc, CreateBasicBufferDesc(D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT), D3D12_RESOURCE_STATE_COMMON, nullptr, &allocation)); ASSERT_NE(allocation, nullptr); - EXPECT_EQ(allocation->GetSize(), + EXPECT_EQ(allocation->GetInfo().SizeInBytes, static_cast(D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT)); } { - ComPtr allocation; + ComPtr allocation; EXPECT_SIZE_CACHE_HIT( resourceAllocator, resourceAllocator->CreateResource( @@ -1822,16 +1807,16 @@ TEST_F(D3D12ResourceAllocatorTests, CreateBufferCacheSize) { CreateBasicBufferDesc(D3D12_DEFAULT_MSAA_RESOURCE_PLACEMENT_ALIGNMENT), D3D12_RESOURCE_STATE_COMMON, nullptr, &allocation)); ASSERT_NE(allocation, nullptr); - EXPECT_EQ(allocation->GetSize(), + EXPECT_EQ(allocation->GetInfo().SizeInBytes, static_cast(D3D12_DEFAULT_MSAA_RESOURCE_PLACEMENT_ALIGNMENT)); } } // Verify two buffers, with and without padding, allocate the correct size. TEST_F(D3D12ResourceAllocatorTests, CreateBufferWithPadding) { - ComPtr resourceAllocator; + ComPtr resourceAllocator; ASSERT_SUCCEEDED( - ResourceAllocator::CreateResourceAllocator(CreateBasicAllocatorDesc(), &resourceAllocator)); + CreateResourceAllocator(CreateBasicAllocatorDesc(), &resourceAllocator, nullptr)); ASSERT_NE(resourceAllocator, nullptr); constexpr uint64_t kBufferSize = GPGMM_MB_TO_BYTES(1); @@ -1839,56 +1824,58 @@ TEST_F(D3D12ResourceAllocatorTests, CreateBufferWithPadding) { ALLOCATION_DESC allocationDesc = {}; allocationDesc.HeapType = D3D12_HEAP_TYPE_DEFAULT; - ComPtr allocationWithoutPadding; + ComPtr allocationWithoutPadding; ASSERT_SUCCEEDED(resourceAllocator->CreateResource( allocationDesc, CreateBasicBufferDesc(kBufferSize), D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, &allocationWithoutPadding)); allocationDesc.RequireResourceHeapPadding = 63; - ComPtr allocationWithPadding; + ComPtr allocationWithPadding; ASSERT_SUCCEEDED(resourceAllocator->CreateResource( allocationDesc, CreateBasicBufferDesc(kBufferSize), D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, &allocationWithPadding)); - EXPECT_GE(allocationWithPadding->GetSize() - allocationWithoutPadding->GetSize(), + EXPECT_GE(allocationWithPadding->GetInfo().SizeInBytes - + allocationWithoutPadding->GetInfo().SizeInBytes, allocationDesc.RequireResourceHeapPadding); // Padded resources are only supported for standalone allocations. - EXPECT_EQ(allocationWithPadding->GetMethod(), gpgmm::AllocationMethod::kStandalone); + EXPECT_EQ(allocationWithPadding->GetInfo().Method, gpgmm::AllocationMethod::kStandalone); } // Verify two textures, with and without padding, allocate the correct size. TEST_F(D3D12ResourceAllocatorTests, CreateTextureWithPadding) { - ComPtr resourceAllocator; + ComPtr resourceAllocator; ASSERT_SUCCEEDED( - ResourceAllocator::CreateResourceAllocator(CreateBasicAllocatorDesc(), &resourceAllocator)); + CreateResourceAllocator(CreateBasicAllocatorDesc(), &resourceAllocator, nullptr)); ASSERT_NE(resourceAllocator, nullptr); ALLOCATION_DESC allocationDesc = {}; allocationDesc.HeapType = D3D12_HEAP_TYPE_DEFAULT; - ComPtr allocationWithoutPadding; + ComPtr allocationWithoutPadding; ASSERT_SUCCEEDED(resourceAllocator->CreateResource( allocationDesc, CreateBasicTextureDesc(DXGI_FORMAT_R8G8B8A8_UNORM, 1, 1), D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, &allocationWithoutPadding)); allocationDesc.RequireResourceHeapPadding = 63; - ComPtr allocationWithPadding; + ComPtr allocationWithPadding; ASSERT_SUCCEEDED(resourceAllocator->CreateResource( allocationDesc, CreateBasicTextureDesc(DXGI_FORMAT_R8G8B8A8_UNORM, 1, 1), D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, &allocationWithPadding)); - EXPECT_GE(allocationWithPadding->GetSize() - allocationWithoutPadding->GetSize(), + EXPECT_GE(allocationWithPadding->GetInfo().SizeInBytes - + allocationWithoutPadding->GetInfo().SizeInBytes, allocationDesc.RequireResourceHeapPadding); // Padded resources are only supported for standalone allocations. - EXPECT_EQ(allocationWithPadding->GetMethod(), gpgmm::AllocationMethod::kStandalone); + EXPECT_EQ(allocationWithPadding->GetInfo().Method, gpgmm::AllocationMethod::kStandalone); } TEST_F(D3D12ResourceAllocatorTests, AllocatorFeatures) { - ComPtr resourceAllocator; + ComPtr resourceAllocator; ASSERT_SUCCEEDED( - ResourceAllocator::CreateResourceAllocator(CreateBasicAllocatorDesc(), &resourceAllocator)); + CreateResourceAllocator(CreateBasicAllocatorDesc(), &resourceAllocator, nullptr)); ASSERT_NE(resourceAllocator, nullptr); // Request information with invalid data size. diff --git a/src/tests/unittests/BuddyMemoryAllocatorTests.cpp b/src/tests/unittests/BuddyMemoryAllocatorTests.cpp index 844fb4f53..9aa8a9397 100644 --- a/src/tests/unittests/BuddyMemoryAllocatorTests.cpp +++ b/src/tests/unittests/BuddyMemoryAllocatorTests.cpp @@ -463,7 +463,7 @@ TEST_F(BuddyMemoryAllocatorTests, ReuseFreedHeaps) { BuddyMemoryAllocator allocator(kMaxBlockSize, kDefaultMemorySize, kDefaultMemoryAlignment, std::move(poolAllocator)); - std::set heaps = {}; + std::set heaps = {}; std::vector> allocations = {}; constexpr uint32_t kNumOfAllocations = 100; @@ -523,7 +523,7 @@ TEST_F(BuddyMemoryAllocatorTests, DestroyHeaps) { BuddyMemoryAllocator allocator(kMaxBlockSize, kDefaultMemorySize, kDefaultMemoryAlignment, std::move(poolAllocator)); - std::set heaps = {}; + std::set heaps = {}; std::vector> allocations = {}; // Count by heap (vs number of allocations) to ensure there are exactly |kNumOfHeaps| worth of diff --git a/src/tests/unittests/SlabMemoryAllocatorTests.cpp b/src/tests/unittests/SlabMemoryAllocatorTests.cpp index 50bd7d00d..afb620dff 100644 --- a/src/tests/unittests/SlabMemoryAllocatorTests.cpp +++ b/src/tests/unittests/SlabMemoryAllocatorTests.cpp @@ -349,7 +349,7 @@ TEST_F(SlabMemoryAllocatorTests, ReuseSlabs) { kDefaultSlabFragmentationLimit, kNoSlabPrefetchAllowed, kDisableSlabGrowth, poolAllocator.get()); - std::set slabMemory = {}; + std::set slabMemory = {}; std::vector> allocations = {}; // Count by slabs (vs number of allocations) to ensure there are exactly |kNumOfSlabs| worth of