Added support for D3D12_FEATURE_DATA_D3D12_OPTIONS4::MSAA64KBAlignedTextureSupported
diff --git a/CHANGELOG.md b/CHANGELOG.md index aaa76a0..d5912f5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md
@@ -1,7 +1,7 @@ # 3.2.0 (2026-??-??) - Added `POOL_FLAG_DONT_USE_TIGHT_ALIGNMENT` (#91). -- Further improvements in the algorithm deciding when to use small alignment or tight alignment. +- Further improvements in the algorithm deciding when to use small alignment or tight alignment, including added support for `D3D12_FEATURE_DATA_D3D12_OPTIONS4::MSAA64KBAlignedTextureSupported`. # 3.1.0 (2026-02-23)
diff --git a/src/D3D12MemAlloc.cpp b/src/D3D12MemAlloc.cpp index b5a56d0..29d8d7a 100644 --- a/src/D3D12MemAlloc.cpp +++ b/src/D3D12MemAlloc.cpp
@@ -163,6 +163,14 @@ #endif #endif +#ifndef D3D12MA_OPTIONS4_SUPPORTED + #ifdef __ID3D12Device5_INTERFACE_DEFINED__ + #define D3D12MA_OPTIONS4_SUPPORTED 1 + #else + #define D3D12MA_OPTIONS4_SUPPORTED 0 + #endif +#endif + #ifndef D3D12MA_DEBUG_LOG #define D3D12MA_DEBUG_LOG(format, ...) /* @@ -778,7 +786,7 @@ return isRenderTargetOrDepthStencil ? ResourceClass::RT_DS_Texture : ResourceClass::Non_RT_DS_Texture; } -// This algorithm is overly conservative. +// This algorithm is overly conservative and applies only to the legacy non-MSAA small-alignment path. template<typename D3D12_RESOURCE_DESC_T> static bool CanUseSmallAlignment(const D3D12_RESOURCE_DESC_T& resourceDesc) { @@ -5998,6 +6006,7 @@ 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 IsMsaa64KBAlignedTextureSupported() const { return m_MSAA64KBAlignedTextureSupported != FALSE; } bool IsTightAlignmentSupported() const { return m_TightAlignmentSupported != FALSE; } bool IsTightAlignmentEnabled() const { return IsTightAlignmentSupported() && m_UseTightAlignment; } bool UseMutex() const { return m_UseMutex; } @@ -6115,6 +6124,7 @@ DXGI_ADAPTER_DESC m_AdapterDesc; D3D12_FEATURE_DATA_D3D12_OPTIONS m_D3D12Options; BOOL m_GPUUploadHeapSupported = FALSE; + BOOL m_MSAA64KBAlignedTextureSupported = FALSE; BOOL m_TightAlignmentSupported = FALSE; D3D12_FEATURE_DATA_ARCHITECTURE m_D3D12Architecture; AllocationObjectAllocator m_AllocationObjectAllocator; @@ -6291,6 +6301,17 @@ } #endif // #if D3D12MA_OPTIONS16_SUPPORTED +#if D3D12MA_OPTIONS4_SUPPORTED + { + D3D12_FEATURE_DATA_D3D12_OPTIONS4 options4 = {}; + hr = m_Device->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS4, &options4, sizeof(options4)); + if (SUCCEEDED(hr)) + { + m_MSAA64KBAlignedTextureSupported = options4.MSAA64KBAlignedTextureSupported; + } + } +#endif // #if D3D12MA_OPTIONS4_SUPPORTED + #if D3D12MA_TIGHT_ALIGNMENT_SUPPORTED { D3D12_FEATURE_DATA_TIGHT_ALIGNMENT tightAlignment = {}; @@ -7864,6 +7885,23 @@ #if D3D12MA_USE_SMALL_RESOURCE_PLACEMENT_ALIGNMENT if (inOutResourceDesc.Alignment == 0 && + IsMsaa64KBAlignedTextureSupported() && + (inOutResourceDesc.Flags & D3D12_RESOURCE_FLAG_USE_TIGHT_ALIGNMENT_COPY) == 0 && + inOutResourceDesc.Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE2D && + inOutResourceDesc.SampleDesc.Count > 1) + { + // This capability-driven MSAA path is separate from the legacy CanUseSmallAlignment heuristic. + inOutResourceDesc.Alignment = D3D12_SMALL_MSAA_RESOURCE_PLACEMENT_ALIGNMENT; + hr = GetResourceAllocationInfoMiddle( + inOutResourceDesc, NumCastableFormats, pCastableFormats, outAllocInfo); + if (SUCCEEDED(hr) && outAllocInfo.Alignment == D3D12_SMALL_MSAA_RESOURCE_PLACEMENT_ALIGNMENT) + { + return S_OK; + } + inOutResourceDesc.Alignment = 0; // Restore original + } + + if (inOutResourceDesc.Alignment == 0 && (inOutResourceDesc.Flags & D3D12_RESOURCE_FLAG_USE_TIGHT_ALIGNMENT_COPY) == 0 && (inOutResourceDesc.Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE1D || inOutResourceDesc.Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE2D ||
diff --git a/src/Tests.cpp b/src/Tests.cpp index 42e402e..b852075 100644 --- a/src/Tests.cpp +++ b/src/Tests.cpp
@@ -165,6 +165,22 @@ outResourceDesc.Flags = D3D12_RESOURCE_FLAG_NONE; } +static void FillResourceDescForSmallMsaaRenderTarget(D3D12_RESOURCE_DESC& outResourceDesc) +{ + outResourceDesc = {}; + outResourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D; + outResourceDesc.Alignment = 0; + outResourceDesc.Width = 64; + outResourceDesc.Height = 64; + outResourceDesc.DepthOrArraySize = 1; + outResourceDesc.MipLevels = 1; + outResourceDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + outResourceDesc.SampleDesc.Count = 2; + outResourceDesc.SampleDesc.Quality = 0; + outResourceDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN; + outResourceDesc.Flags = D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET; +} + static void FillData(void* outPtr, const UINT64 sizeInBytes, UINT seed) { UINT* outValues = (UINT*)outPtr; @@ -361,6 +377,30 @@ ctx.allocator->FreeStatsString(s); } +static bool QueryMsaa64KBAlignedTextureSupported(const TestContext& ctx) +{ + D3D12_FEATURE_DATA_D3D12_OPTIONS4 options4 = {}; + CHECK_HR(ctx.device->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS4, &options4, sizeof(options4))); + return options4.MSAA64KBAlignedTextureSupported != FALSE; +} + +static UINT64 ExpectedMsaaTexturePlacementAlignment(bool msaa64KBAlignedTextureSupported) +{ + return msaa64KBAlignedTextureSupported ? + D3D12_SMALL_MSAA_RESOURCE_PLACEMENT_ALIGNMENT : + D3D12_DEFAULT_MSAA_RESOURCE_PLACEMENT_ALIGNMENT; +} + +static void ValidatePlacedSmallMsaaTextureAllocation(const D3D12MA::Allocation* alloc, UINT64 expectedAlignment) +{ + CHECK_BOOL(alloc != nullptr); + CHECK_BOOL(alloc->GetResource() != nullptr); + CHECK_BOOL(alloc->GetHeap() != nullptr); + CHECK_BOOL(alloc->GetAlignment() == expectedAlignment); + CHECK_BOOL(alloc->GetOffset() % expectedAlignment == 0); + CHECK_BOOL(alloc->GetHeap()->GetDesc().Alignment == D3D12_DEFAULT_MSAA_RESOURCE_PLACEMENT_ALIGNMENT); +} + static void TestDebugMargin(const TestContext& ctx) { @@ -1780,6 +1820,57 @@ CHECK_HR(ctx.allocator->CreateResource(&allocDesc, &resDesc, D3D12_RESOURCE_STATE_RENDER_TARGET, nullptr, &alloc, IID_NULL, nullptr)); // Committed allocation should not have explicit heap CHECK_BOOL(alloc->GetHeap() == nullptr); + CHECK_BOOL(alloc->GetOffset() == 0); +} + +static void TestMsaa64KBAlignedTextureSupported_DefaultPool(const TestContext& ctx) +{ + wprintf(L"Test MSAA 64 KB alignment in default pool\n"); + + CHECK_BOOL((ctx.allocatorFlags & D3D12MA::ALLOCATOR_FLAG_ALWAYS_COMMITTED) == 0); + CHECK_BOOL((ctx.allocatorFlags & D3D12MA::ALLOCATOR_FLAG_MSAA_TEXTURES_ALWAYS_COMMITTED) == 0); + + const UINT64 expectedAlignment = + ExpectedMsaaTexturePlacementAlignment(QueryMsaa64KBAlignedTextureSupported(ctx)); + + D3D12MA::CALLOCATION_DESC allocDesc = D3D12MA::CALLOCATION_DESC{ D3D12_HEAP_TYPE_DEFAULT }; + + D3D12_RESOURCE_DESC resDesc = {}; + FillResourceDescForSmallMsaaRenderTarget(resDesc); + + ComPtr<D3D12MA::Allocation> alloc; + ComPtr<ID3D12Resource> resource; + CHECK_HR(ctx.allocator->CreateResource(&allocDesc, &resDesc, D3D12_RESOURCE_STATE_RENDER_TARGET, + nullptr, &alloc, IID_PPV_ARGS(&resource))); + + ValidatePlacedSmallMsaaTextureAllocation(alloc.Get(), expectedAlignment); +} + +static void TestMsaa64KBAlignedTextureSupported_CustomPool(const TestContext& ctx) +{ + wprintf(L"Test MSAA 64 KB alignment in custom pool\n"); + + const UINT64 expectedAlignment = + ExpectedMsaaTexturePlacementAlignment(QueryMsaa64KBAlignedTextureSupported(ctx)); + + D3D12MA::CPOOL_DESC poolDesc = D3D12MA::CPOOL_DESC{ + D3D12_HEAP_TYPE_DEFAULT, + D3D12_HEAP_FLAG_ALLOW_ONLY_RT_DS_TEXTURES, + D3D12MA::POOL_FLAG_NONE }; + ComPtr<D3D12MA::Pool> pool; + CHECK_HR(ctx.allocator->CreatePool(&poolDesc, &pool)); + + D3D12MA::CALLOCATION_DESC allocDesc = D3D12MA::CALLOCATION_DESC{ pool.Get() }; + + D3D12_RESOURCE_DESC resDesc = {}; + FillResourceDescForSmallMsaaRenderTarget(resDesc); + + ComPtr<D3D12MA::Allocation> alloc; + ComPtr<ID3D12Resource> resource; + CHECK_HR(ctx.allocator->CreateResource(&allocDesc, &resDesc, D3D12_RESOURCE_STATE_RENDER_TARGET, + nullptr, &alloc, IID_PPV_ARGS(&resource))); + + ValidatePlacedSmallMsaaTextureAllocation(alloc.Get(), expectedAlignment); } static void TestMapping(const TestContext& ctx) @@ -4697,6 +4788,8 @@ TestStandardCustomCommittedPlaced(ctx); TestAliasingMemory(ctx); TestAliasingImplicitCommitted(ctx); + TestMsaa64KBAlignedTextureSupported_DefaultPool(ctx); + TestMsaa64KBAlignedTextureSupported_CustomPool(ctx); TestPoolMsaaTextureAsCommitted(ctx); TestMapping(ctx); TestStats(ctx);