diff --git a/src/gpgmm/d3d12/ResourceAllocatorD3D12.cpp b/src/gpgmm/d3d12/ResourceAllocatorD3D12.cpp index 34f363fef..d1b21f6d8 100644 --- a/src/gpgmm/d3d12/ResourceAllocatorD3D12.cpp +++ b/src/gpgmm/d3d12/ResourceAllocatorD3D12.cpp @@ -415,6 +415,11 @@ namespace gpgmm::d3d12 { ? std::min(allocatorDescriptor.MaxResourceHeapSize, caps->GetMaxResourceHeapSize()) : caps->GetMaxResourceHeapSize(); + newDescriptor.PreferredResourceHeapSize = + (allocatorDescriptor.PreferredResourceHeapSize == 0) + ? kNoRequiredAlignment + : allocatorDescriptor.PreferredResourceHeapSize; + newDescriptor.MemoryFragmentationLimit = (allocatorDescriptor.MemoryFragmentationLimit > 0) ? allocatorDescriptor.MemoryFragmentationLimit : kDefaultFragmentationLimit; @@ -611,16 +616,19 @@ namespace gpgmm::d3d12 { const uint64_t maxResourceHeapSize = mCaps->GetMaxResourceHeapSize(); switch (algorithm) { case ALLOCATOR_ALGORITHM_BUDDY_SYSTEM: { + // System and memory size must be aligned at creation-time. return std::make_unique( /*systemSize*/ PrevPowerOfTwo(maxResourceHeapSize), - /*memorySize*/ std::max(memoryAlignment, memorySize), + /*memorySize*/ NextPowerOfTwo(memorySize), /*memoryAlignment*/ memoryAlignment, /*memoryAllocator*/ std::move(underlyingAllocator)); } case ALLOCATOR_ALGORITHM_SLAB: { + // Min slab size is always equal to the memory size because the + // slab allocator aligns the slab size at allocate-time. return std::make_unique( /*maxSlabSize*/ PrevPowerOfTwo(maxResourceHeapSize), - /*minSlabSize*/ std::max(memoryAlignment, memorySize), + /*minSlabSize*/ memorySize, /*slabAlignment*/ memoryAlignment, /*slabFragmentationLimit*/ memoryFragmentationLimit, /*allowSlabPrefetch*/ isPrefetchAllowed, @@ -647,15 +655,16 @@ namespace gpgmm::d3d12 { std::make_unique(mResidencyManager.Get(), mDevice.Get(), heapProperties, heapFlags, mIsAlwaysInBudget); + const uint64_t heapSize = + std::max(heapAlignment, AlignTo(descriptor.PreferredResourceHeapSize, heapAlignment)); + std::unique_ptr pooledOrNonPooledAllocator = CreatePoolAllocator( - descriptor.PoolAlgorithm, /*memorySize*/ descriptor.PreferredResourceHeapSize, - heapAlignment, (descriptor.Flags & ALLOCATOR_FLAG_ALWAYS_ON_DEMAND), - std::move(resourceHeapAllocator)); + descriptor.PoolAlgorithm, heapSize, heapAlignment, + (descriptor.Flags & ALLOCATOR_FLAG_ALWAYS_ON_DEMAND), std::move(resourceHeapAllocator)); return CreateSubAllocator( - descriptor.SubAllocationAlgorithm, - /*memorySize*/ std::max(heapAlignment, descriptor.PreferredResourceHeapSize), - heapAlignment, descriptor.MemoryFragmentationLimit, descriptor.MemoryGrowthFactor, + descriptor.SubAllocationAlgorithm, heapSize, heapAlignment, + descriptor.MemoryFragmentationLimit, descriptor.MemoryGrowthFactor, /*allowSlabPrefetch*/ !(descriptor.Flags & ALLOCATOR_FLAG_DISABLE_MEMORY_PREFETCH), std::move(pooledOrNonPooledAllocator)); } @@ -677,14 +686,15 @@ namespace gpgmm::d3d12 { (descriptor.Flags & ALLOCATOR_FLAG_ALWAYS_ON_DEMAND), std::move(smallBufferOnlyAllocator)); + const uint64_t heapSize = + std::max(heapAlignment, AlignTo(descriptor.PreferredResourceHeapSize, heapAlignment)); + // Any amount of fragmentation must be allowed for small buffers since the allocation can // be smaller then the resource heap alignment. - return CreateSubAllocator( - descriptor.SubAllocationAlgorithm, - /*memorySize*/ std::max(heapAlignment, descriptor.PreferredResourceHeapSize), - heapAlignment, - /*memoryFragmentationLimit*/ 1, descriptor.MemoryGrowthFactor, - /*allowSlabPrefetch*/ false, std::move(pooledOrNonPooledAllocator)); + return CreateSubAllocator(descriptor.SubAllocationAlgorithm, heapSize, heapAlignment, + /*memoryFragmentationLimit*/ 1, descriptor.MemoryGrowthFactor, + /*allowSlabPrefetch*/ false, + std::move(pooledOrNonPooledAllocator)); } ResourceAllocator::~ResourceAllocator() { diff --git a/src/tests/end2end/D3D12ResourceAllocatorTests.cpp b/src/tests/end2end/D3D12ResourceAllocatorTests.cpp index f33be21de..8c2aa6e27 100644 --- a/src/tests/end2end/D3D12ResourceAllocatorTests.cpp +++ b/src/tests/end2end/D3D12ResourceAllocatorTests.cpp @@ -261,6 +261,68 @@ TEST_F(D3D12ResourceAllocatorTests, CreateBufferSubAllocated) { } } +TEST_F(D3D12ResourceAllocatorTests, CreateBufferWithPreferredHeapSize) { + ALLOCATOR_DESC allocatorDesc = CreateBasicAllocatorDesc(); + + // ALLOCATOR_ALGORITHM_SLAB + { + ALLOCATOR_DESC newAllocatorDesc = allocatorDesc; + newAllocatorDesc.SubAllocationAlgorithm = ALLOCATOR_ALGORITHM_SLAB; + newAllocatorDesc.PreferredResourceHeapSize = GPGMM_MB_TO_BYTES(12); + + ComPtr resourceAllocator; + ASSERT_SUCCEEDED(ResourceAllocator::CreateAllocator(newAllocatorDesc, &resourceAllocator)); + ASSERT_NE(resourceAllocator, nullptr); + + ComPtr allocation; + ASSERT_SUCCEEDED( + resourceAllocator->CreateResource({}, CreateBasicBufferDesc(kDefaultBufferSize), + D3D12_RESOURCE_STATE_COMMON, nullptr, &allocation)); + + // Slab allocator requires heaps to be in aligned in powers-of-two sizes. + EXPECT_EQ(allocation->GetMemory()->GetSize(), GPGMM_MB_TO_BYTES(16)); + } + + // ALLOCATOR_ALGORITHM_BUDDY_SYSTEM + { + ALLOCATOR_DESC newAllocatorDesc = allocatorDesc; + newAllocatorDesc.SubAllocationAlgorithm = ALLOCATOR_ALGORITHM_BUDDY_SYSTEM; + newAllocatorDesc.PreferredResourceHeapSize = GPGMM_MB_TO_BYTES(12); + + ComPtr resourceAllocator; + ASSERT_SUCCEEDED(ResourceAllocator::CreateAllocator(newAllocatorDesc, &resourceAllocator)); + ASSERT_NE(resourceAllocator, nullptr); + + ComPtr allocation; + ASSERT_SUCCEEDED( + resourceAllocator->CreateResource({}, CreateBasicBufferDesc(kDefaultBufferSize), + D3D12_RESOURCE_STATE_COMMON, nullptr, &allocation)); + + // Buddy allocator requires heaps to be in aligned in powers-of-two sizes. + EXPECT_EQ(allocation->GetMemory()->GetSize(), GPGMM_MB_TO_BYTES(16)); + } + + // ALLOCATOR_ALGORITHM_DEDICATED + { + ALLOCATOR_DESC newAllocatorDesc = allocatorDesc; + newAllocatorDesc.SubAllocationAlgorithm = ALLOCATOR_ALGORITHM_DEDICATED; + newAllocatorDesc.PreferredResourceHeapSize = GPGMM_MB_TO_BYTES(12); + + ComPtr resourceAllocator; + ASSERT_SUCCEEDED(ResourceAllocator::CreateAllocator(newAllocatorDesc, &resourceAllocator)); + ASSERT_NE(resourceAllocator, nullptr); + + ComPtr allocation; + ASSERT_SUCCEEDED( + resourceAllocator->CreateResource({}, CreateBasicBufferDesc(kDefaultBufferSize), + D3D12_RESOURCE_STATE_COMMON, nullptr, &allocation)); + + // Dedicated allocator ignores the preferred resource heap size and allocates exactly what + // is needed. + EXPECT_EQ(allocation->GetMemory()->GetSize(), kDefaultBufferSize); + } +} + TEST_F(D3D12ResourceAllocatorTests, CreateBufferManyDeallocateAtEnd) { ComPtr resourceAllocator; ASSERT_SUCCEEDED(