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);