diff --git a/src/gpgmm/common/DedicatedMemoryAllocator.cpp b/src/gpgmm/common/DedicatedMemoryAllocator.cpp index f5b5241b6..4f6aef3c8 100644 --- a/src/gpgmm/common/DedicatedMemoryAllocator.cpp +++ b/src/gpgmm/common/DedicatedMemoryAllocator.cpp @@ -35,11 +35,11 @@ namespace gpgmm { GPGMM_TRY_ASSIGN(GetNextInChain()->TryAllocateMemory(request), allocation); mInfo.UsedBlockCount++; - mInfo.UsedBlockUsage += request.SizeInBytes; + mInfo.UsedBlockUsage += allocation->GetSize(); return std::make_unique( this, allocation->GetMemory(), /*offset*/ 0, allocation->GetMethod(), - new MemoryBlock{0, request.SizeInBytes}, request.SizeInBytes); + new MemoryBlock{0, allocation->GetSize()}, request.SizeInBytes); } void DedicatedMemoryAllocator::DeallocateMemory(std::unique_ptr allocation) { diff --git a/src/gpgmm/common/DedicatedMemoryAllocator.h b/src/gpgmm/common/DedicatedMemoryAllocator.h index 3c89f3f1e..81041533c 100644 --- a/src/gpgmm/common/DedicatedMemoryAllocator.h +++ b/src/gpgmm/common/DedicatedMemoryAllocator.h @@ -17,11 +17,12 @@ #include "gpgmm/common/MemoryAllocator.h" -#include - namespace gpgmm { - // DedicatedMemoryAllocator sub-allocates memory with exactly one block. + // DedicatedMemoryAllocator allocates from device memory with exactly one block. + // DedicatedMemoryAllocator is useful in situations where whole memory objects could be reused + // without the need for sub-allocation. DedicatedMemoryAllocator also allows + // memory to be tracked. class DedicatedMemoryAllocator final : public MemoryAllocator { public: DedicatedMemoryAllocator(std::unique_ptr memoryAllocator); @@ -33,6 +34,8 @@ namespace gpgmm { uint64_t GetMemoryAlignment() const override; MemoryAllocatorInfo GetInfo() const override; + + private: const char* GetTypename() const override; }; diff --git a/src/gpgmm/d3d12/ResourceAllocatorD3D12.cpp b/src/gpgmm/d3d12/ResourceAllocatorD3D12.cpp index ff2941f47..e0038e980 100644 --- a/src/gpgmm/d3d12/ResourceAllocatorD3D12.cpp +++ b/src/gpgmm/d3d12/ResourceAllocatorD3D12.cpp @@ -597,6 +597,9 @@ namespace gpgmm::d3d12 { /*slabGrowthFactor*/ descriptor.MemoryGrowthFactor, /*memoryAllocator*/ std::move(pooledOrNonPooledAllocator)); } + case ALLOCATOR_ALGORITHM_DEDICATED: + return std::make_unique( + /*memoryAllocator*/ std::move(pooledOrNonPooledAllocator)); default: { UNREACHABLE(); return {}; @@ -677,6 +680,9 @@ namespace gpgmm::d3d12 { /*slabMemoryGrowth*/ 1, /*memoryAllocator*/ std::move(pooledOrNonPooledAllocator)); } + case ALLOCATOR_ALGORITHM_DEDICATED: + return std::make_unique( + /*memoryAllocator*/ std::move(pooledOrNonPooledAllocator)); default: UNREACHABLE(); return {}; diff --git a/src/gpgmm/d3d12/ResourceAllocatorD3D12.h b/src/gpgmm/d3d12/ResourceAllocatorD3D12.h index 048e75516..0c9d4dfda 100644 --- a/src/gpgmm/d3d12/ResourceAllocatorD3D12.h +++ b/src/gpgmm/d3d12/ResourceAllocatorD3D12.h @@ -69,11 +69,14 @@ namespace gpgmm::d3d12 { */ ALLOCATOR_FLAG_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). + /** \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 or debugging OOM failures. + minimal possible GPU memory footprint, avoiding out-of-memory, or debugging possible + corruption of heaps. */ ALLOCATOR_FLAG_ALWAYS_ON_DEMAND = 0x8, @@ -99,7 +102,20 @@ namespace gpgmm::d3d12 { */ 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. @@ -140,6 +156,20 @@ namespace gpgmm::d3d12 { 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 diff --git a/src/include/min/gpgmm_d3d12.h b/src/include/min/gpgmm_d3d12.h index 1358d45bb..160252828 100644 --- a/src/include/min/gpgmm_d3d12.h +++ b/src/include/min/gpgmm_d3d12.h @@ -237,6 +237,7 @@ namespace gpgmm::d3d12 { 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) diff --git a/src/tests/end2end/D3D12ResourceAllocatorTests.cpp b/src/tests/end2end/D3D12ResourceAllocatorTests.cpp index 0404b7f7f..f33be21de 100644 --- a/src/tests/end2end/D3D12ResourceAllocatorTests.cpp +++ b/src/tests/end2end/D3D12ResourceAllocatorTests.cpp @@ -145,7 +145,7 @@ TEST_F(D3D12ResourceAllocatorTests, CreateBufferOversized) { ResourceAllocator::CreateAllocator(CreateBasicAllocatorDesc(), &resourceAllocator)); ASSERT_NE(resourceAllocator, nullptr); - constexpr uint64_t kOversizedBuffer = 32ll * 1024ll * 1024ll * 1024ll; // 32GB + constexpr uint64_t kOversizedBuffer = GPGMM_GB_TO_BYTES(32); ComPtr allocation; ASSERT_FAILED(resourceAllocator->CreateResource({}, CreateBasicBufferDesc(kOversizedBuffer + 1), D3D12_RESOURCE_STATE_COMMON, nullptr, @@ -232,6 +232,25 @@ TEST_F(D3D12ResourceAllocatorTests, CreateBufferSubAllocated) { ALLOCATION_DESC allocationDesc = {}; allocationDesc.HeapType = D3D12_HEAP_TYPE_DEFAULT; + for (auto& alloc : GenerateBufferAllocations()) { + ComPtr allocation; + EXPECT_EQ(SUCCEEDED(resourceAllocator->CreateResource( + allocationDesc, CreateBasicBufferDesc(alloc.size, alloc.alignment), + D3D12_RESOURCE_STATE_COMMON, nullptr, &allocation)), + alloc.succeeds); + } + } + { + ALLOCATOR_DESC newAllocatorDesc = allocatorDesc; + newAllocatorDesc.SubAllocationAlgorithm = ALLOCATOR_ALGORITHM_DEDICATED; + + ComPtr resourceAllocator; + ASSERT_SUCCEEDED(ResourceAllocator::CreateAllocator(newAllocatorDesc, &resourceAllocator)); + ASSERT_NE(resourceAllocator, nullptr); + + ALLOCATION_DESC allocationDesc = {}; + allocationDesc.HeapType = D3D12_HEAP_TYPE_DEFAULT; + for (auto& alloc : GenerateBufferAllocations()) { ComPtr allocation; EXPECT_EQ(SUCCEEDED(resourceAllocator->CreateResource(