Improved the logic of small alignment heuristics
diff --git a/src/D3D12MemAlloc.cpp b/src/D3D12MemAlloc.cpp index 75931fe..0a58d30 100644 --- a/src/D3D12MemAlloc.cpp +++ b/src/D3D12MemAlloc.cpp
@@ -785,6 +785,18 @@ (resDesc.Flags & (D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET | D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL)) != 0; return isRenderTargetOrDepthStencil ? ResourceClass::RT_DS_Texture : ResourceClass::Non_RT_DS_Texture; } + +static bool ResourceDimensionIsTexture(D3D12_RESOURCE_DIMENSION dimension) +{ + return dimension == D3D12_RESOURCE_DIMENSION_TEXTURE1D || + dimension == D3D12_RESOURCE_DIMENSION_TEXTURE2D || + dimension == D3D12_RESOURCE_DIMENSION_TEXTURE3D; +} + +static bool ResourceFlagsContainRenderTargetOrDepthStencil(D3D12_RESOURCE_FLAGS flags) +{ + return (flags & (D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET | D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL)) != 0; +} struct SmallAlignmentTileShape { @@ -795,9 +807,30 @@ static bool GetSmallAlignmentTileShape( D3D12_RESOURCE_DIMENSION dimension, + bool isMsaa, UINT bitsPerUnit, SmallAlignmentTileShape& outTileShape) { + if (isMsaa) + { + if (dimension != D3D12_RESOURCE_DIMENSION_TEXTURE2D) + return false; + + switch (bitsPerUnit) + { + case 8: outTileShape = { 256, 256, 1 }; return true; + case 16: outTileShape = { 256, 128, 1 }; return true; + case 32: outTileShape = { 128, 128, 1 }; return true; + case 64: outTileShape = { 128, 64, 1 }; return true; + case 128: outTileShape = { 64, 64, 1 }; return true; + case 256: outTileShape = { 64, 32, 1 }; return true; + case 512: outTileShape = { 32, 32, 1 }; return true; + case 1024: outTileShape = { 32, 16, 1 }; return true; + case 2048: outTileShape = { 16, 16, 1 }; return true; + default: return false; + } + } + switch (dimension) { case D3D12_RESOURCE_DIMENSION_TEXTURE1D: @@ -839,16 +872,29 @@ } } -// This algorithm is overly conservative and applies only to the legacy non-MSAA small-alignment path. +// This algorithm is overly conservative and applies only to the legacy small-alignment heuristic path. template<typename D3D12_RESOURCE_DESC_T> static bool CanUseSmallAlignment(const D3D12_RESOURCE_DESC_T& resourceDesc) { - if (resourceDesc.SampleDesc.Count > 1 || + if (resourceDesc.SampleDesc.Count == 0 || + resourceDesc.Layout != D3D12_TEXTURE_LAYOUT_UNKNOWN || resourceDesc.Width > UINT_MAX) { return false; } + const bool isMsaa = resourceDesc.SampleDesc.Count > 1; + const bool isRtDs = ResourceFlagsContainRenderTargetOrDepthStencil(resourceDesc.Flags); + if (isMsaa && !isRtDs) + { + return false; + } + if (!isMsaa && + isRtDs) + { + return false; + } + UINT sizeX = (UINT)resourceDesc.Width; UINT sizeY = 1; UINT sizeZ = 1; @@ -859,6 +905,8 @@ switch (resourceDesc.Dimension) { case D3D12_RESOURCE_DIMENSION_TEXTURE1D: + if (isMsaa) + return false; // For 1D arrays, small-alignment eligibility depends on the mip0 slice size, not array size. if (IsFormatCompressed(resourceDesc.Format)) return false; @@ -867,6 +915,8 @@ case D3D12_RESOURCE_DIMENSION_TEXTURE2D: // For 2D arrays, small-alignment eligibility depends on mip0 width/height, not array size. sizeY = resourceDesc.Height; + if (isMsaa && IsFormatCompressed(resourceDesc.Format)) + return false; if (IsFormatCompressed(resourceDesc.Format)) { sizeX = DivideRoundingUp(sizeX, 4u); @@ -876,6 +926,8 @@ break; case D3D12_RESOURCE_DIMENSION_TEXTURE3D: + if (isMsaa) + return false; sizeY = resourceDesc.Height; sizeZ = resourceDesc.DepthOrArraySize; if (IsFormatCompressed(resourceDesc.Format)) @@ -890,15 +942,18 @@ return false; } + bitsPerUnit *= resourceDesc.SampleDesc.Count; + SmallAlignmentTileShape tileShape = {}; - if (!GetSmallAlignmentTileShape(resourceDesc.Dimension, bitsPerUnit, tileShape)) + if (!GetSmallAlignmentTileShape(resourceDesc.Dimension, isMsaa, bitsPerUnit, tileShape)) return false; const UINT64 tileCount = UINT64(DivideRoundingUp(sizeX, tileShape.Width)) * UINT64(DivideRoundingUp(sizeY, tileShape.Height)) * UINT64(DivideRoundingUp(sizeZ, tileShape.Depth)); - return tileCount <= 16; + // Non-MSAA compares 64 KB against 4 KB tiles => 16 pages. MSAA compares 4 MB against 64 KB tiles => 64 pages. + return tileCount <= (isMsaa ? 64 : 16); } static bool ValidateAllocateMemoryParameters( @@ -7958,34 +8013,33 @@ HRESULT hr = S_OK; #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) + bool trySmallAlignment = false; + + if (inOutResourceDesc.Alignment == 0 + && (inOutResourceDesc.Flags & D3D12_RESOURCE_FLAG_USE_TIGHT_ALIGNMENT_COPY) == 0 + && inOutResourceDesc.Layout == D3D12_TEXTURE_LAYOUT_UNKNOWN + && ResourceDimensionIsTexture(inOutResourceDesc.Dimension)) { - // 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) + // MSAA texture. + if(inOutResourceDesc.SampleDesc.Count > 1) { - return S_OK; + trySmallAlignment = IsMsaa64KBAlignedTextureSupported() + && ResourceFlagsContainRenderTargetOrDepthStencil(inOutResourceDesc.Flags) + && inOutResourceDesc.Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE2D; } - inOutResourceDesc.Alignment = 0; // Restore original + // Non-MSAA texture. + else + { + trySmallAlignment = !ResourceFlagsContainRenderTargetOrDepthStencil(inOutResourceDesc.Flags); + } } - 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 || - inOutResourceDesc.Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE3D) && - (inOutResourceDesc.Flags & (D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET | D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL)) == 0 && - inOutResourceDesc.SampleDesc.Count == 1 #if D3D12MA_USE_SMALL_RESOURCE_PLACEMENT_ALIGNMENT == 1 - && CanUseSmallAlignment(inOutResourceDesc) + if(trySmallAlignment) + trySmallAlignment = CanUseSmallAlignment(inOutResourceDesc); #endif - ) + + if (trySmallAlignment) { /* The algorithm here is based on Microsoft sample: "Small Resources Sample"