From ef10b4a1d8fef747a57e9695de22d78c2b403c63 Mon Sep 17 00:00:00 2001 From: ParantezTech <12572227+par274@users.noreply.github.com> Date: Thu, 8 Oct 2026 17:32:00 +0300 Subject: [PATCH] [GPU] Track DCC metadata coverage for color targets and volume textures Co-authored-by: Sekai <140012992+SekaixX@users.noreply.github.com> --- .../ImageRequestBuilders.ColorTarget.cs | 42 ++++++++++++++----- .../Images/ImageRequestBuilders.Texture.cs | 11 +++-- .../Gpu/Images/TextureDescriptorWords.cs | 2 + src/SharpEmu.Libs/Gpu/Images/TileGeometry.cs | 33 +++++++++++++++ 4 files changed, 73 insertions(+), 15 deletions(-) diff --git a/src/SharpEmu.Libs/Gpu/Images/ImageRequestBuilders.ColorTarget.cs b/src/SharpEmu.Libs/Gpu/Images/ImageRequestBuilders.ColorTarget.cs index 334bd23..4834d58 100644 --- a/src/SharpEmu.Libs/Gpu/Images/ImageRequestBuilders.ColorTarget.cs +++ b/src/SharpEmu.Libs/Gpu/Images/ImageRequestBuilders.ColorTarget.cs @@ -43,24 +43,38 @@ public static partial class ImageRequestBuilders public static uint SampleCount(uint encodedLog2) => encodedLog2 <= 3 ? 1u << (int)encodedLog2 : 0; + private static bool DccAlphaOnMsb(in ColorTargetWords words) + { + switch (words.Layout) + { + case ChannelLayout.Bits10_10_10_2: + case ChannelLayout.Bits10_10_10_2Float: + case ChannelLayout.Bits5_5_5_1: + return true; + case ChannelLayout.Bits2_10_10_10: + case ChannelLayout.Bits1_5_5_5: + return false; + } + + var components = GuestPixelFormats.ResolveRenderTargetEncoding(words.Layout, words.NumberType).Components; + if (components == 1) + { + return words.Order != ChannelOrder.Standard; + } + + return components == 3 || words.Order is ChannelOrder.Standard or ChannelOrder.Alternate; + } + // DCC clears use the target's packed clear word; the fixed-clear set is a format allow-list. private static (bool Supported, bool FixedSupported, ClearColorValue Value) DccClearInfo(Format format, bool hasDcc, uint packedClear) { ClearColorValue value = default; var supported = hasDcc && PackedClearValue.TryDecodeColor(format, packedClear, out value); - var fixedSupported = hasDcc && SupportsDccFixedClear(format); + var fixedSupported = hasDcc && PackedClearValue.SupportsDccFixedColor(format); return (supported, fixedSupported, supported ? value : default); } - public static bool SupportsDccFixedClear(Format format) => format switch - { - Format.R8Unorm or Format.R8G8Unorm or Format.R8G8B8A8Unorm or Format.R8G8B8A8Srgb or Format.B8G8R8A8Unorm or Format.B8G8R8A8Srgb or - Format.A2B10G10R10UnormPack32 or Format.A2R10G10B10UnormPack32 or Format.R5G6B5UnormPack16 or Format.A1R5G5B5UnormPack16 or - Format.R4G4B4A4UnormPack16 or Format.R16Unorm or Format.R16G16Unorm or Format.R16G16B16A16Unorm or Format.R16Sfloat or - Format.R16G16Sfloat or Format.R16G16B16A16Sfloat or Format.R32Sfloat or Format.R32G32Sfloat or Format.R32G32B32A32Sfloat or - Format.B10G11R11UfloatPack32 => true, - _ => false, - }; + public static bool SupportsDccFixedClear(Format format) => PackedClearValue.SupportsDccFixedColor(format); // Builds the request for a bound color target. Null when the slot carries no target. public static ColorTargetResolution? ColorTarget(in ColorTargetWords words, uint targetMask, uint drawLayerOffset, bool ignoreTargetMask) @@ -324,8 +338,14 @@ public static partial class ImageRequestBuilders if (hasDcc) { // DCC lives in its own allocation; the address lets the cache match fills seen before the target. + _ = TileGeometry.TryGetDccSize( + width, height, volume ? depth : view.ImageLayers, bytesPerElement, levels, tileMode, + out var metadataSize, words.FragmentsLog2); description.Metadata.Kind = MetadataKind.Dcc; - description.Metadata.Range = new GuestSpan(words.DccAddress, 0); + description.Metadata.Range = new GuestSpan(words.DccAddress, metadataSize.Size); + description.Metadata.DccClearWord = words.ClearWord0; + description.Metadata.DccClearRegisterValid = true; + description.Metadata.DccAlphaMsb = DccAlphaOnMsb(words); } for (var level = 0; level < levels; level++) diff --git a/src/SharpEmu.Libs/Gpu/Images/ImageRequestBuilders.Texture.cs b/src/SharpEmu.Libs/Gpu/Images/ImageRequestBuilders.Texture.cs index 2b79922..1508dd0 100644 --- a/src/SharpEmu.Libs/Gpu/Images/ImageRequestBuilders.Texture.cs +++ b/src/SharpEmu.Libs/Gpu/Images/ImageRequestBuilders.Texture.cs @@ -300,12 +300,15 @@ public static partial class ImageRequestBuilders description.BytesPerBlock = blockBytes != 0 ? blockBytes : GuestPixelFormats.BytesPerElement(format); description.Samples = samples; description.TileMode = tile; - var metadataAddress = descriptor.MetadataAddress << 8; - if (!storage && type == GuestImageType.Color2D && samples == 1 && levels == 1 && tile == GuestTileMode.RenderTarget && descriptor.MetadataCompress && - metadataAddress != 0 && metadataAddress < TrackerLayout.SpaceBytes) + if (!shape.R128 && words.Length >= 8 && descriptor.MetadataCompress && + tile != GuestTileMode.Depth && !description.IsDepth) { + _ = TileGeometry.TryGetDccSize( + width, height, volume ? depth : imageLayers, description.BytesPerBlock, levels, tile, + out var metadataSize, (uint)System.Numerics.BitOperations.TrailingZeroCount(samples)); description.Metadata.Kind = MetadataKind.Dcc; - description.Metadata.Range = new GuestSpan(metadataAddress, 0); + description.Metadata.Range = new GuestSpan(descriptor.MetadataAddress << 8, metadataSize.Size); + description.Metadata.DccAlphaMsb = descriptor.DccAlphaMsb; } if (samples > 1) diff --git a/src/SharpEmu.Libs/Gpu/Images/TextureDescriptorWords.cs b/src/SharpEmu.Libs/Gpu/Images/TextureDescriptorWords.cs index f6e3bf7..e3c6b72 100644 --- a/src/SharpEmu.Libs/Gpu/Images/TextureDescriptorWords.cs +++ b/src/SharpEmu.Libs/Gpu/Images/TextureDescriptorWords.cs @@ -78,6 +78,8 @@ public readonly struct TextureDescriptorWords public bool MetadataCompress => ((Fields[6] >> 21) & 0x1) == 1; + public bool DccAlphaMsb => ((Fields[6] >> 22) & 0x1) == 1; + public ulong MetadataAddress => ((Fields[6] >> 24) & 0xFF) | ((ulong)Fields[7] << 8); } diff --git a/src/SharpEmu.Libs/Gpu/Images/TileGeometry.cs b/src/SharpEmu.Libs/Gpu/Images/TileGeometry.cs index 989e3a8..2de8d20 100644 --- a/src/SharpEmu.Libs/Gpu/Images/TileGeometry.cs +++ b/src/SharpEmu.Libs/Gpu/Images/TileGeometry.cs @@ -608,6 +608,39 @@ public static partial class TileGeometry return true; } + public static bool TryGetDccSize( + uint width, + uint height, + uint slices, + uint bytesPerElement, + uint levels, + GuestTileMode tile, + out TileSizeAndAlignment totalSize, + uint fragmentsLog2 = 0) + { + totalSize = default; + if (width == 0 || height == 0 || slices == 0 || levels != 1 || fragmentsLog2 != 0 || + !BitOperations.IsPow2(bytesPerElement) || bytesPerElement > 16 || + tile is not (GuestTileMode.RenderTarget or GuestTileMode.Depth)) + { + return false; + } + + var coverageBits = 20 - BitOperations.TrailingZeroCount(bytesPerElement); + var blockWidth = 1u << ((coverageBits + 1) / 2); + var blockHeight = 1u << (coverageBits / 2); + var blocksX = ((ulong)width + blockWidth - 1) / blockWidth; + var blocksY = ((ulong)height + blockHeight - 1) / blockHeight; + var blocks = blocksX * blocksY; + if (blocks == 0 || blocks > uint.MaxValue / 4096u / slices) + { + return false; + } + + totalSize = new TileSizeAndAlignment((uint)(blocks * slices * 4096u), 4096); + return true; + } + public static bool TryGetRenderTargetMipLayout(uint width, uint height, uint pitch, uint bytesPerElement, uint levels, out TileSizeAndAlignment totalSize, TileLevelSpan[]? levelSpans, TilePaddedSize[]? paddedSizes) { totalSize = default;