From 51a02e910d42d07e482c179072fa365bde5b7168 Mon Sep 17 00:00:00 2001 From: Bryan Bernhart Date: Wed, 31 Aug 2022 15:29:07 -0700 Subject: [PATCH] Support minimal viable GPGMM implementation (MVI). Allows users to use GPGMM by only building src/include/*_min.h and src/include/*_min.cpp files. These "min" sources expose the full GPGMM interface but only have a bare bones functional implementation. See src/samples/D3D12Sample for how to use. --- src/include/min/gpgmm.cpp | 104 ++++++++++ src/include/min/gpgmm.h | 107 ++++++++++ src/include/min/gpgmm_d3d12.cpp | 351 ++++++++++++++++++++++++++++++++ src/include/min/gpgmm_d3d12.h | 320 +++++++++++++++++++++++++++++ src/samples/BUILD.gn | 25 ++- src/samples/D3D12Sample.cpp | 4 +- 6 files changed, 908 insertions(+), 3 deletions(-) create mode 100644 src/include/min/gpgmm.cpp create mode 100644 src/include/min/gpgmm.h create mode 100644 src/include/min/gpgmm_d3d12.cpp create mode 100644 src/include/min/gpgmm_d3d12.h diff --git a/src/include/min/gpgmm.cpp b/src/include/min/gpgmm.cpp new file mode 100644 index 000000000..65c7554ce --- /dev/null +++ b/src/include/min/gpgmm.cpp @@ -0,0 +1,104 @@ +// Copyright 2021 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. + +#include "gpgmm.h" + +namespace gpgmm { + + // MemoryBase + + MemoryBase::MemoryBase(uint64_t size, uint64_t alignment) : mSize(size), mAlignment(alignment) { + } + + MemoryBase::~MemoryBase() = default; + + uint64_t MemoryBase::GetSize() const { + return mSize; + } + + uint64_t MemoryBase::GetAlignment() const { + return mAlignment; + } + + // MemoryAllocator + + uint64_t MemoryAllocator::ReleaseMemory(uint64_t bytesToRelease) { + return 0; + } + + MemoryAllocatorInfo MemoryAllocator::GetInfo() const { + return {}; + } + + // MemoryAllocation + + MemoryAllocation::MemoryAllocation(MemoryAllocator* allocator, + MemoryBase* memory, + uint64_t requestSize) + : mAllocator(allocator), mMemory(memory), mRequestSize(requestSize) { + } + + 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 { + return mMemory; + } + + uint8_t* MemoryAllocation::GetMappedPointer() const { + return nullptr; + } + + MemoryAllocator* MemoryAllocation::GetAllocator() const { + return mAllocator; + } + uint64_t MemoryAllocation::GetSize() const { + return mMemory->GetSize(); + } + + uint64_t MemoryAllocation::GetRequestSize() const { + return mRequestSize; + } + + 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/include/min/gpgmm.h b/src/include/min/gpgmm.h new file mode 100644 index 000000000..df7f6f613 --- /dev/null +++ b/src/include/min/gpgmm.h @@ -0,0 +1,107 @@ +// Copyright 2021 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 +#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 AllocationMethod { + kUndefined = 0, + kStandalone = 1, + kSubAllocated = 2, + kSubAllocatedWithin = 3, + }; + + class MemoryBlock; + + 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; + uint64_t mRequestSize; + }; + +} // namespace gpgmm + +#endif // INCLUDE_MIN_GPGMM_H_ diff --git a/src/include/min/gpgmm_d3d12.cpp b/src/include/min/gpgmm_d3d12.cpp new file mode 100644 index 000000000..ff0c41ba1 --- /dev/null +++ b/src/include/min/gpgmm_d3d12.cpp @@ -0,0 +1,351 @@ +// Copyright 2021 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. + +#include "gpgmm_d3d12.h" + +#include + +#define ReturnIfFailed(expr) \ + { \ + HRESULT hr = expr; \ + if (FAILED(hr)) { \ + return hr; \ + } \ + } \ + for (;;) \ + break + +namespace gpgmm::d3d12 { + + static constexpr uint64_t kInvalidOffset = std::numeric_limits::max(); + + // IUnknownImpl + + IUnknownImpl::IUnknownImpl() : mRefCount(1) { + } + + IUnknownImpl::~IUnknownImpl() = default; + + HRESULT STDMETHODCALLTYPE IUnknownImpl::QueryInterface(REFIID riid, void** ppvObject) { + // Always set out parameter to nullptr, validating it first. + if (ppvObject == nullptr) { + return E_INVALIDARG; + } + + *ppvObject = nullptr; + + if (riid == IID_IUnknown) { + // Increment reference and return pointer. + *ppvObject = this; + AddRef(); + return S_OK; + } + return E_NOINTERFACE; + } + + ULONG STDMETHODCALLTYPE IUnknownImpl::AddRef() { + return ++mRefCount; + } + + ULONG STDMETHODCALLTYPE IUnknownImpl::Release() { + const ULONG refCount = --mRefCount; + if (refCount == 0) { + DeleteThis(); + } + return refCount; + } + + void IUnknownImpl::DeleteThis() { + delete this; + } + + // Heap + + // static + HRESULT Heap::CreateHeap(const HEAP_DESC& descriptor, + ResidencyManager* const pResidencyManager, + CreateHeapFn&& createHeapFn, + Heap** ppHeapOut) { + Microsoft::WRL::ComPtr pageable; + ReturnIfFailed(createHeapFn(&pageable)); + + if (ppHeapOut != nullptr) { + *ppHeapOut = new Heap(pageable, descriptor.SizeInBytes, descriptor.Alignment); + } + + return S_OK; + } + + HRESULT STDMETHODCALLTYPE Heap::QueryInterface(REFIID riid, void** ppvObject) { + return mPageable->QueryInterface(riid, ppvObject); + } + + bool Heap::IsResident() const { + return true; + } + + HEAP_INFO Heap::GetInfo() const { + return {IsResident()}; + } + + Heap::Heap(Microsoft::WRL::ComPtr pageable, uint64_t size, uint64_t alignment) + : MemoryBase(size, alignment), mPageable(std::move(pageable)) { + } + + // ResidencyList + + ResidencyList::ResidencyList() = default; + + HRESULT ResidencyList::Add(Heap* pHeap) { + return S_OK; + } + + HRESULT ResidencyList::Reset() { + return S_OK; + } + + // ResidencyManager + + // static + HRESULT ResidencyManager::CreateResidencyManager(const RESIDENCY_DESC& descriptor, + ResidencyManager** ppResidencyManagerOut) { + if (ppResidencyManagerOut != nullptr) { + *ppResidencyManagerOut = new ResidencyManager(descriptor); + } + + return S_OK; + } + + ResidencyManager::~ResidencyManager() = default; + + HRESULT ResidencyManager::LockHeap(Heap* pHeap) { + return S_OK; + } + + HRESULT ResidencyManager::UnlockHeap(Heap* pHeap) { + return S_OK; + } + + HRESULT ResidencyManager::ExecuteCommandLists(ID3D12CommandQueue* pQueue, + ID3D12CommandList* const* ppCommandLists, + ResidencyList* const* ppResidencyLists, + uint32_t count) { + pQueue->ExecuteCommandLists(count, ppCommandLists); + return S_OK; + } + + HRESULT ResidencyManager::SetVideoMemoryReservation( + const DXGI_MEMORY_SEGMENT_GROUP& memorySegmentGroup, + uint64_t availableForReservation, + uint64_t* pCurrentReservationOut) { + return S_OK; + } + + HRESULT ResidencyManager::QueryVideoMemoryInfo( + const DXGI_MEMORY_SEGMENT_GROUP& memorySegmentGroup, + DXGI_QUERY_VIDEO_MEMORY_INFO* pVideoMemoryInfoOut) { + return S_OK; + } + + RESIDENCY_INFO ResidencyManager::GetInfo() const { + return {0, 0}; + } + + ResidencyManager::ResidencyManager(const RESIDENCY_DESC& descriptor) + : mDevice(std::move(descriptor.Device)), mAdapter(std::move(descriptor.Adapter)) { + } + + // ResourceAllocation + + void ResourceAllocation::DeleteThis() { + GetAllocator()->DeallocateMemory(std::unique_ptr(this)); + } + + HRESULT ResourceAllocation::Map(uint32_t subresource, + const D3D12_RANGE* readRange, + void** dataOut) { + return mResource->Map(subresource, readRange, dataOut); + } + + void ResourceAllocation::Unmap(uint32_t subresource, const D3D12_RANGE* writtenRange) { + return mResource->Unmap(subresource, writtenRange); + } + + ID3D12Resource* ResourceAllocation::GetResource() const { + return mResource.Get(); + } + + bool ResourceAllocation::IsResident() const { + return true; + } + + D3D12_GPU_VIRTUAL_ADDRESS ResourceAllocation::GetGPUVirtualAddress() const { + return mResource->GetGPUVirtualAddress(); + } + + uint64_t ResourceAllocation::GetOffsetFromResource() const { + return 0; + } + + RESOURCE_ALLOCATION_INFO ResourceAllocation::GetInfo() const { + return {GetSize(), GetAlignment()}; + } + + Heap* ResourceAllocation::GetMemory() const { + return static_cast(MemoryAllocation::GetMemory()); + } + + ResourceAllocation::ResourceAllocation(const RESOURCE_ALLOCATION_DESC& desc, + MemoryAllocator* allocator, + Heap* resourceHeap, + Microsoft::WRL::ComPtr resource) + : MemoryAllocation(allocator, resourceHeap, desc.SizeInBytes), + mResource(std::move(resource)) { + } + + // ResourceAllocator + + // static + HRESULT ResourceAllocator::CreateAllocator(const ALLOCATOR_DESC& allocatorDescriptor, + ResourceAllocator** ppResourceAllocatorOut, + ResidencyManager** ppResidencyManagerOut) { + if (allocatorDescriptor.Device == nullptr || allocatorDescriptor.Adapter == nullptr) { + return E_INVALIDARG; + } + + Microsoft::WRL::ComPtr residencyManager; + if (ppResidencyManagerOut != nullptr) { + RESIDENCY_DESC residencyDesc = {}; + residencyDesc.Device = allocatorDescriptor.Device; + + D3D12_FEATURE_DATA_ARCHITECTURE arch = {}; + ReturnIfFailed(residencyDesc.Device->CheckFeatureSupport(D3D12_FEATURE_ARCHITECTURE, + &arch, sizeof(arch))); + residencyDesc.IsUMA = arch.UMA; + + ReturnIfFailed(allocatorDescriptor.Adapter.As(&residencyDesc.Adapter)); + + ReturnIfFailed( + ResidencyManager::CreateResidencyManager(residencyDesc, &residencyManager)); + } + + Microsoft::WRL::ComPtr resourceAllocator; + ReturnIfFailed( + CreateAllocator(allocatorDescriptor, residencyManager.Get(), &resourceAllocator)); + + if (ppResourceAllocatorOut != nullptr) { + *ppResourceAllocatorOut = resourceAllocator.Detach(); + } + + if (ppResidencyManagerOut != nullptr) { + *ppResidencyManagerOut = residencyManager.Detach(); + } + + return S_OK; + } + + // static + HRESULT ResourceAllocator::CreateAllocator(const ALLOCATOR_DESC& allocatorDescriptor, + ResidencyManager* pResidencyManager, + ResourceAllocator** ppResourceAllocatorOut) { + if (ppResourceAllocatorOut != nullptr) { + *ppResourceAllocatorOut = new ResourceAllocator(allocatorDescriptor, pResidencyManager); + } + + return S_OK; + } + + HRESULT ResourceAllocator::CreateResource(const ALLOCATION_DESC& allocationDescriptor, + const D3D12_RESOURCE_DESC& resourceDescriptor, + D3D12_RESOURCE_STATES initialResourceState, + const D3D12_CLEAR_VALUE* pClearValue, + ResourceAllocation** ppResourceAllocationOut) { + Heap* resourceHeap = nullptr; + Microsoft::WRL::ComPtr committedResource; + + const D3D12_RESOURCE_ALLOCATION_INFO resourceInfo = + mDevice->GetResourceAllocationInfo(0, 1, &resourceDescriptor); + + HEAP_DESC resourceHeapDesc = {}; + resourceHeapDesc.SizeInBytes = resourceInfo.SizeInBytes; + resourceHeapDesc.Alignment = resourceInfo.Alignment; + resourceHeapDesc.HeapType = allocationDescriptor.HeapType; + + ReturnIfFailed(Heap::CreateHeap( + resourceHeapDesc, mResidencyManager.Get(), + [&](ID3D12Pageable** ppPageableOut) -> HRESULT { + D3D12_HEAP_PROPERTIES heapProperties = {}; + heapProperties.Type = allocationDescriptor.HeapType; + + ReturnIfFailed(mDevice->CreateCommittedResource( + &heapProperties, D3D12_HEAP_FLAG_NONE, &resourceDescriptor, + initialResourceState, pClearValue, IID_PPV_ARGS(&committedResource))); + + Microsoft::WRL::ComPtr pageable; + ReturnIfFailed(committedResource.As(&pageable)); + *ppPageableOut = pageable.Detach(); + return S_OK; + }, + &resourceHeap)); + + const uint64_t allocationSize = resourceHeap->GetSize(); + mInfo.UsedMemoryUsage += allocationSize; + mInfo.UsedMemoryCount++; + mInfo.UsedBlockUsage += allocationSize; + + RESOURCE_ALLOCATION_DESC allocationDesc = {}; + allocationDesc.HeapOffset = kInvalidOffset; + allocationDesc.SizeInBytes = resourceInfo.SizeInBytes; + allocationDesc.Method = AllocationMethod::kStandalone; + + *ppResourceAllocationOut = new ResourceAllocation(allocationDesc, this, resourceHeap, + std::move(committedResource)); + + return S_OK; + } + + HRESULT ResourceAllocator::CreateResource( + Microsoft::WRL::ComPtr committedResource, + ResourceAllocation** ppResourceAllocationOut) { + return E_NOTIMPL; + } + + uint64_t ResourceAllocator::ReleaseMemory(uint64_t bytesToRelease) { + return 0; + } + + RESOURCE_ALLOCATOR_INFO ResourceAllocator::GetInfo() const { + return mInfo; + } + + HRESULT ResourceAllocator::CheckFeatureSupport(FEATURE feature, + void* pFeatureSupportData, + uint32_t featureSupportDataSize) const { + return E_INVALIDARG; // Unsupported + } + + ResourceAllocator::ResourceAllocator(const ALLOCATOR_DESC& descriptor, + Microsoft::WRL::ComPtr residencyManager) + : mDevice(std::move(descriptor.Device)), mResidencyManager(std::move(residencyManager)) { + } + + void ResourceAllocator::DeallocateMemory(std::unique_ptr allocation) { + const uint64_t allocationSize = allocation->GetSize(); + mInfo.UsedMemoryUsage -= allocationSize; + mInfo.UsedMemoryCount--; + mInfo.UsedBlockUsage -= allocationSize; + delete allocation->GetMemory(); + } + +} // namespace gpgmm::d3d12 diff --git a/src/include/min/gpgmm_d3d12.h b/src/include/min/gpgmm_d3d12.h new file mode 100644 index 000000000..a5e6dc734 --- /dev/null +++ b/src/include/min/gpgmm_d3d12.h @@ -0,0 +1,320 @@ +// Copyright 2021 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, allowing for incremental enabling during +// development. +// +// GPGMM MVI specifically, +// * Is not thread-safe. +// * Is functionally-equivelent to calling ID3D12Device::CreateCommittedResource. +// * Does not perform residency management. +// +// 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" + +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: + uint64_t mRefCount; + }; + + struct HEAP_INFO { + bool IsResident; + }; + + struct HEAP_DESC { + uint64_t SizeInBytes; + uint64_t Alignment; + D3D12_HEAP_TYPE HeapType; + bool AlwaysInBudget; + bool IsExternal; + DXGI_MEMORY_SEGMENT_GROUP MemorySegmentGroup; + std::string 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; + bool IsResident() const; + HEAP_INFO GetInfo() const; + + private: + Heap(Microsoft::WRL::ComPtr pageable, uint64_t size, uint64_t alignment); + + 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; + std::string TraceFile; + }; + + struct RESIDENCY_DESC { + Microsoft::WRL::ComPtr Device; + Microsoft::WRL::ComPtr Adapter; + bool IsUMA; + D3D12_MESSAGE_SEVERITY MinLogLevel; + EVENT_RECORD_OPTIONS RecordOptions; + float VideoMemoryBudget; + float MinPctOfBudgetToReserve; + uint64_t Budget; + uint64_t EvictSizeInBytes; + uint64_t InitialFenceValue; + bool UpdateBudgetByPolling; + }; + + struct RESIDENCY_INFO { + uint64_t ResidentMemoryUsage; + uint64_t ResidentMemoryCount; + }; + + 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; + std::string 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* readRange = nullptr, + void** dataOut = nullptr); + void Unmap(uint32_t subresource = 0, const D3D12_RANGE* writtenRange = nullptr); + ID3D12Resource* GetResource() const; + bool IsResident() 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, + }; + + 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, + }; + + 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, + }; + + DEFINE_ENUM_FLAG_OPERATORS(ALLOCATION_FLAGS) + + struct ALLOCATION_DESC { + ALLOCATION_FLAGS Flags; + D3D12_HEAP_TYPE HeapType; + D3D12_HEAP_FLAGS ExtraRequiredHeapFlags; + uint64_t RequireResourceHeapPadding; + std::string DebugName; + }; + + enum FEATURE { + FEATURE_RESOURCE_SUBALLOCATION_SUPPORT, + }; + + using RESOURCE_ALLOCATOR_INFO = MemoryAllocatorInfo; + + class ResourceAllocator final : public MemoryAllocator, public IUnknownImpl { + public: + static HRESULT CreateAllocator(const ALLOCATOR_DESC& allocatorDescriptor, + ResourceAllocator** ppResourceAllocatorOut, + ResidencyManager** ppResidencyManagerOut = nullptr); + + static HRESULT CreateAllocator(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(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/samples/BUILD.gn b/src/samples/BUILD.gn index 78e5e9842..1bf2c8825 100644 --- a/src/samples/BUILD.gn +++ b/src/samples/BUILD.gn @@ -19,7 +19,10 @@ import("${gpgmm_root_dir}/build_overrides/gpgmm_features.gni") group("samples") { deps = [] if (gpgmm_enable_d3d12) { - deps = [ ":D3D12Sample" ] + deps = [ + ":D3D12Sample", + ":D3D12SampleMin", + ] } } @@ -31,3 +34,23 @@ executable("D3D12Sample") { "dxgi.lib", ] } + +source_set("min_gpgmm") { + 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", + ] +} + +executable("D3D12SampleMin") { + deps = [ ":min_gpgmm" ] + defines = [ "GPGMM_D3D12_HEADERS_ALREADY_INCLUDED" ] + sources = [ "D3D12Sample.cpp" ] + include_dirs = [ "${gpgmm_root_dir}/src/include/min" ] + libs = [ + "d3d12.lib", + "dxgi.lib", + ] +} diff --git a/src/samples/D3D12Sample.cpp b/src/samples/D3D12Sample.cpp index 60bd59ddc..07672df7f 100644 --- a/src/samples/D3D12Sample.cpp +++ b/src/samples/D3D12Sample.cpp @@ -12,12 +12,12 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include - #include #include #include +#include + HRESULT Init() { Microsoft::WRL::ComPtr adapter3; Microsoft::WRL::ComPtr device;