Added support for resource tight alignment Added macro D3D12MA_TIGHT_ALIGNMENT_SUPPORTED, flag ALLOCATOR_FLAG_DONT_USE_TIGHT_ALIGNMENT, function Allocator::IsTightAlignmentSupported.
diff --git a/include/D3D12MemAlloc.h b/include/D3D12MemAlloc.h index 2dc31c7..737e6ae 100644 --- a/include/D3D12MemAlloc.h +++ b/include/D3D12MemAlloc.h
@@ -1053,6 +1053,8 @@ It can also be disabled for a single allocation by using #ALLOCATION_FLAG_STRATEGY_MIN_TIME. */ ALLOCATOR_FLAG_DONT_PREFER_SMALL_BUFFERS_COMMITTED = 0x10, + /** TODO document... */ + ALLOCATOR_FLAG_DONT_USE_TIGHT_ALIGNMENT = 0x20, }; /// \brief Parameters of created Allocator object. To be used with CreateAllocator(). @@ -1127,6 +1129,15 @@ `#define D3D12MA_OPTIONS16_SUPPORTED 1` is needed for the compilation of this library. Otherwise the flag is always false. */ BOOL IsGPUUploadHeapSupported() const; + /** \brief Returns true if resource tight alignment is supported on the current system. + When supported, it is automatically used by the library, unless + #ALLOCATOR_FLAG_DONT_USE_TIGHT_ALIGNMENT flag was specified on allocator creation. + + This flag is fetched from `D3D12_FEATURE_DATA_TIGHT_ALIGNMENT::SupportTier`. + + `#define D3D12MA_TIGHT_ALIGNMENT_SUPPORTED 1` is needed for the compilation of this library. Otherwise the flag is always false. + */ + BOOL IsTightAlignmentSupported() const; /** \brief Returns total amount of memory of specific segment group, in bytes. \param memorySegmentGroup use `DXGI_MEMORY_SEGMENT_GROUP_LOCAL` or DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL`.
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 23e0941..6134ea3 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt
@@ -198,3 +198,11 @@ target_compile_definitions(D3D12Sample PRIVATE D3D12MA_OPTIONS16_SUPPORTED=1) endif() endif() + +option(D3D12MA_TIGHT_ALIGNMENT_SUPPORTED "Set if using Agility SDK 1.716.0-preview or newer that defines D3D12_FEATURE_DATA_TIGHT_ALIGNMENT." OFF) +if(D3D12MA_TIGHT_ALIGNMENT_SUPPORTED) + target_compile_definitions(D3D12MemoryAllocator PRIVATE D3D12MA_TIGHT_ALIGNMENT_SUPPORTED=1) + if(${D3D12MA_BUILD_SAMPLE} AND ${WIN32}) + target_compile_definitions(D3D12Sample PRIVATE D3D12MA_TIGHT_ALIGNMENT_SUPPORTED=1) + endif() +endif()
diff --git a/src/D3D12MemAlloc.cpp b/src/D3D12MemAlloc.cpp index 6962f0b..7eb79f0 100644 --- a/src/D3D12MemAlloc.cpp +++ b/src/D3D12MemAlloc.cpp
@@ -5911,6 +5911,8 @@ BOOL IsCacheCoherentUMA() const { return m_D3D12Architecture.CacheCoherentUMA; } bool SupportsResourceHeapTier2() const { return m_D3D12Options.ResourceHeapTier >= D3D12_RESOURCE_HEAP_TIER_2; } bool IsGPUUploadHeapSupported() const { return m_GPUUploadHeapSupported != FALSE; } + bool IsTightAlignmentSupported() const { return m_TightAlignmentSupported != FALSE; } + bool IsTightAlignmentEnabled() const { return IsTightAlignmentSupported() && m_UseTightAlignment; } bool UseMutex() const { return m_UseMutex; } AllocationObjectAllocator& GetAllocationObjectAllocator() { return m_AllocationObjectAllocator; } UINT GetCurrentFrameIndex() const { return m_CurrentFrameIndex.load(); } @@ -5998,6 +6000,7 @@ const bool m_AlwaysCommitted; const bool m_MsaaAlwaysCommitted; const bool m_PreferSmallBuffersCommitted; + const bool m_UseTightAlignment; bool m_DefaultPoolsNotZeroed = false; ID3D12Device* m_Device; // AddRef #ifdef __ID3D12Device1_INTERFACE_DEFINED__ @@ -6022,6 +6025,7 @@ DXGI_ADAPTER_DESC m_AdapterDesc; D3D12_FEATURE_DATA_D3D12_OPTIONS m_D3D12Options; BOOL m_GPUUploadHeapSupported = FALSE; + BOOL m_TightAlignmentSupported = FALSE; D3D12_FEATURE_DATA_ARCHITECTURE m_D3D12Architecture; AllocationObjectAllocator m_AllocationObjectAllocator; @@ -6089,7 +6093,8 @@ : m_UseMutex((desc.Flags & ALLOCATOR_FLAG_SINGLETHREADED) == 0), m_AlwaysCommitted((desc.Flags & ALLOCATOR_FLAG_ALWAYS_COMMITTED) != 0), m_MsaaAlwaysCommitted((desc.Flags & ALLOCATOR_FLAG_MSAA_TEXTURES_ALWAYS_COMMITTED) != 0), - m_PreferSmallBuffersCommitted((desc.Flags & ALLOCATOR_FLAG_DONT_PREFER_SMALL_BUFFERS_COMMITTED) == 0), + m_PreferSmallBuffersCommitted((desc.Flags& ALLOCATOR_FLAG_DONT_PREFER_SMALL_BUFFERS_COMMITTED) == 0), + m_UseTightAlignment((desc.Flags & ALLOCATOR_FLAG_DONT_USE_TIGHT_ALIGNMENT) == 0), m_Device(desc.pDevice), m_Adapter(desc.pAdapter), m_PreferredBlockSize(desc.PreferredBlockSize != 0 ? desc.PreferredBlockSize : D3D12MA_DEFAULT_BLOCK_SIZE), @@ -6177,6 +6182,20 @@ } #endif + // You must define macro `#define D3D12MA_TIGHT_ALIGNMENT_SUPPORTED 1` to enable resource tight alignment! + // Unfortunately there is no way to programmatically check if the included <d3d12.h> defines D3D12_FEATURE_DATA_TIGHT_ALIGNMENT or not. + // Main interfaces have respective macros like __ID3D12Device4_INTERFACE_DEFINED__, but structures like this do not. +#if D3D12MA_TIGHT_ALIGNMENT_SUPPORTED + { + D3D12_FEATURE_DATA_TIGHT_ALIGNMENT tightAlignment = {}; + hr = m_Device->CheckFeatureSupport(D3D12_FEATURE_D3D12_TIGHT_ALIGNMENT, &tightAlignment, sizeof(tightAlignment)); + if (SUCCEEDED(hr)) + { + m_TightAlignmentSupported = tightAlignment.SupportTier >= D3D12_TIGHT_ALIGNMENT_TIER_1; + } + } +#endif + hr = m_Device->CheckFeatureSupport(D3D12_FEATURE_ARCHITECTURE, &m_D3D12Architecture, sizeof(m_D3D12Architecture)); if (FAILED(hr)) { @@ -6861,6 +6880,9 @@ json.WriteString(L"GPUUploadHeapSupported"); json.WriteBool(m_GPUUploadHeapSupported != FALSE); + + json.WriteString(L"TightAlignmentSupported"); + json.WriteBool(m_TightAlignmentSupported != FALSE); } json.EndObject(); } @@ -7623,8 +7645,19 @@ D3D12_RESOURCE_ALLOCATION_INFO AllocatorPimpl::GetResourceAllocationInfo(D3D12_RESOURCE_DESC_T& inOutResourceDesc) const { #ifdef __ID3D12Device1_INTERFACE_DEFINED__ - /* Optional optimization: Microsoft documentation says: - https://docs.microsoft.com/en-us/windows/win32/api/d3d12/nf-d3d12-id3d12device-getresourceallocationinfo + +#if D3D12MA_TIGHT_ALIGNMENT_SUPPORTED + if (IsTightAlignmentEnabled() && + // Don't allow USE_TIGHT_ALIGNMENT together with ALLOW_CROSS_ADAPTER as there is a D3D Debug Layer error: + // D3D12 ERROR: ID3D12Device::GetResourceAllocationInfo: D3D12_RESOURCE_DESC::Flag D3D12_RESOURCE_FLAG_USE_TIGHT_ALIGNMENT will be ignored since D3D12_RESOURCE_FLAG_ALLOW_CROSS_ADAPTER is set. [ STATE_CREATION ERROR #599: CREATERESOURCE_INVALIDMISCFLAGS] + (inOutResourceDesc.Flags & D3D12_RESOURCE_FLAG_ALLOW_CROSS_ADAPTER) == 0) + { + inOutResourceDesc.Flags |= D3D12_RESOURCE_FLAG_USE_TIGHT_ALIGNMENT; + } +#endif // #if D3D12MA_TIGHT_ALIGNMENT_SUPPORTED + + /* Optional optimization: Microsoft documentation of the ID3D12Device:: + GetResourceAllocationInfo function says: Your application can forgo using GetResourceAllocationInfo for buffer resources (D3D12_RESOURCE_DIMENSION_BUFFER). Buffers have the same size on all adapters, @@ -7632,12 +7665,14 @@ D3D12_RESOURCE_DESC::Width. */ if (inOutResourceDesc.Alignment == 0 && - inOutResourceDesc.Dimension == D3D12_RESOURCE_DIMENSION_BUFFER) + inOutResourceDesc.Dimension == D3D12_RESOURCE_DIMENSION_BUFFER && + !IsTightAlignmentEnabled()) { return { AlignUp<UINT64>(inOutResourceDesc.Width, D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT), // SizeInBytes D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT }; // Alignment } + #endif // #ifdef __ID3D12Device1_INTERFACE_DEFINED__ #if D3D12MA_USE_SMALL_RESOURCE_PLACEMENT_ALIGNMENT @@ -9552,6 +9587,11 @@ return m_Pimpl->IsGPUUploadHeapSupported(); } +BOOL Allocator::IsTightAlignmentSupported() const +{ + return m_Pimpl->IsTightAlignmentSupported(); +} + UINT64 Allocator::GetMemoryCapacity(UINT memorySegmentGroup) const { return m_Pimpl->GetMemoryCapacity(memorySegmentGroup);
diff --git a/src/D3D12Sample.cpp b/src/D3D12Sample.cpp index e39e3fe..ea88ee9 100644 --- a/src/D3D12Sample.cpp +++ b/src/D3D12Sample.cpp
@@ -438,6 +438,11 @@ UINT* pNumRows = reinterpret_cast<UINT*>(pRowSizesInBytes + NumSubresources); D3D12_RESOURCE_DESC Desc = pDestinationResource->GetDesc(); + + // Needed because of the D3D Debug Layer error: + // D3D12 ERROR: ID3D12Device::GetCopyableFootprints: D3D12_RESOURCE_DESC::Alignment is invalid. The value is 16. When D3D12_RESOURCE_DESC::Flag bit for D3D12_RESOURCE_FLAG_USE_TIGHT_ALIGNMENT is set, Alignment must be 0. [ STATE_CREATION ERROR #721: CREATERESOURCE_INVALIDALIGNMENT] + Desc.Alignment = 0; + ID3D12Device* pDevice; pDestinationResource->GetDevice(__uuidof(*pDevice), reinterpret_cast<void**>(&pDevice)); pDevice->GetCopyableFootprints(&Desc, FirstSubresource, NumSubresources, IntermediateOffset, pLayouts, pNumRows, pRowSizesInBytes, &RequiredSize); @@ -1052,8 +1057,7 @@ CHECK_HR( g_Allocator->CreateResource( &vertexBufferAllocDesc, &vertexBufferResourceDesc, // resource description for a buffer - D3D12_RESOURCE_STATE_COPY_DEST, // we will start this heap in the copy destination state since we will copy data - // from the upload heap to this heap + D3D12_RESOURCE_STATE_COMMON, nullptr, // optimized clear value must be null for this type of resource. used for render targets and depth/stencil buffers &g_VertexBufferAllocation, IID_PPV_ARGS(&vertexBufferPtr)) ); @@ -1165,7 +1169,7 @@ CHECK_HR( g_Allocator->CreateResource( &indexBufferAllocDesc, &indexBufferResourceDesc, // resource description for a buffer - D3D12_RESOURCE_STATE_COPY_DEST, // start in the copy destination state + D3D12_RESOURCE_STATE_COMMON, nullptr, // optimized clear value must be null for this type of resource &g_IndexBufferAllocation, IID_PPV_ARGS(&g_IndexBuffer)) );
diff --git a/src/Tests.cpp b/src/Tests.cpp index bd065eb..40388f9 100644 --- a/src/Tests.cpp +++ b/src/Tests.cpp
@@ -51,6 +51,17 @@ static constexpr CONFIG_TYPE ConfigType = CONFIG_TYPE_AVERAGE; static const char* FREE_ORDER_NAMES[] = { "FORWARD", "BACKWARD", "RANDOM", }; +// Indexes match enum D3D12_HEAP_TYPE. +static const WCHAR* const HEAP_TYPE_NAMES[] = +{ + L"", + L"DEFAULT", + L"UPLOAD", + L"READBACK", + L"CUSTOM", + L"GPU_UPLOAD", +}; + static void CurrentTimeToStr(std::string& out) { time_t rawTime; time(&rawTime); @@ -2990,6 +3001,55 @@ #endif } +static void TestTightAlignment(const TestContext& ctx) +{ +#if D3D12MA_TIGHT_ALIGNMENT_SUPPORTED + using namespace D3D12MA; + + wprintf(L"Test resource tight alignment\n"); + + if(!ctx.allocator->IsTightAlignmentSupported()) + { + wprintf(L" Skipped due to tight alignment not supported.\n"); + return; + } + + // Use a custom heap to make sure our small buffers are not created as committed. + POOL_DESC poolDesc = {}; + poolDesc.BlockSize = 1024 * 1024; + poolDesc.MinBlockCount = poolDesc.MaxBlockCount = 1; + + D3D12_RESOURCE_DESC resDesc; + FillResourceDescForBuffer(resDesc, 16); + + const D3D12_HEAP_TYPE heapTypes[] = { D3D12_HEAP_TYPE_DEFAULT, D3D12_HEAP_TYPE_UPLOAD }; + for (auto heapType : heapTypes) + { + poolDesc.HeapProperties.Type = heapType; + ComPtr<Pool> pool; + CHECK_HR(ctx.allocator->CreatePool(&poolDesc, &pool)); + + ALLOCATION_DESC allocDesc = {}; + allocDesc.CustomPool = pool.Get(); + + ComPtr<Allocation> allocs[2] = {}; + + for (size_t i = 0; i < _countof(allocs); ++i) + { + CHECK_HR(ctx.allocator->CreateResource(&allocDesc, &resDesc, + D3D12_RESOURCE_STATE_COMMON, NULL, &allocs[i], IID_NULL, NULL)); + CHECK_BOOL(allocs[i] && allocs[i]->GetResource()); + } + + // Print the offset of the 2nd buffer. + wprintf(L" In D3D12_HEAP_TYPE_%s, a %llu B buffer was aligned to %llu B.\n", + HEAP_TYPE_NAMES[(size_t)heapType], + resDesc.Width, + allocs[1]->GetOffset()); + } +#endif +} + static void TestVirtualBlocks(const TestContext& ctx) { wprintf(L"Test virtual blocks\n"); @@ -4257,6 +4317,7 @@ #endif TestGPUUploadHeap(ctx); + TestTightAlignment(ctx); FILE* file; fopen_s(&file, "Results.csv", "w");