From 2eb09d9939f11befdb05a0c58495a413cf4d5462 Mon Sep 17 00:00:00 2001 From: MarcelMediaDev Date: Sun, 26 Jul 2026 00:55:28 +0100 Subject: [PATCH] fix(gpu): decode Gen5 R16 and RG32 render-target formats Co-authored-by: Cursor --- .../Gpu/Metal/MetalGuestFormats.cs | 19 ++++++++++++---- .../VideoOut/VulkanVideoPresenter.cs | 20 +++++++++++++---- .../VulkanPresentEncodeFormatTests.cs | 22 +++++++++++++++++++ 3 files changed, 53 insertions(+), 8 deletions(-) diff --git a/src/SharpEmu.Libs/Gpu/Metal/MetalGuestFormats.cs b/src/SharpEmu.Libs/Gpu/Metal/MetalGuestFormats.cs index 2715eac0..76d0bb55 100644 --- a/src/SharpEmu.Libs/Gpu/Metal/MetalGuestFormats.cs +++ b/src/SharpEmu.Libs/Gpu/Metal/MetalGuestFormats.cs @@ -218,6 +218,13 @@ internal static class MetalGuestFormats { var format = (dataFormat, numberType) switch { + // Early G-buffer / scene targets (R16 + RG32). Keep in sync with + // VulkanVideoPresenter.TryDecodeRenderTargetFormat. + (2, 0) => MtlPixelFormat.R16Unorm, + (2, 1) => MtlPixelFormat.R16Snorm, + (2, 4) => MtlPixelFormat.R16Uint, + (2, 5) => MtlPixelFormat.R16Sint, + (2, 7) => MtlPixelFormat.R16Float, (4, 4) => MtlPixelFormat.R32Uint, (4, 5) => MtlPixelFormat.R32Sint, (4, 7) => MtlPixelFormat.R32Float, @@ -230,6 +237,8 @@ internal static class MetalGuestFormats (10, 5) => MtlPixelFormat.Rgba8Sint, (10, 9) => MtlPixelFormat.Rgba8UnormSrgb, (10, _) => MtlPixelFormat.Rgba8Unorm, + (11, 4) => MtlPixelFormat.Rg32Uint, + (11, 5) => MtlPixelFormat.Rg32Sint, (11, 7) => MtlPixelFormat.Rg32Float, (12, 4) => MtlPixelFormat.Rgba16Uint, (12, 5) => MtlPixelFormat.Rgba16Sint, @@ -258,10 +267,12 @@ internal static class MetalGuestFormats var outputKind = format switch { - MtlPixelFormat.R8Uint or MtlPixelFormat.R32Uint or MtlPixelFormat.Rg16Uint or - MtlPixelFormat.Rgba8Uint or MtlPixelFormat.Rgba16Uint => Gen5PixelOutputKind.Uint, - MtlPixelFormat.R32Sint or MtlPixelFormat.Rg16Sint or MtlPixelFormat.Rgba8Sint or - MtlPixelFormat.Rgba16Sint => Gen5PixelOutputKind.Sint, + MtlPixelFormat.R8Uint or MtlPixelFormat.R16Uint or MtlPixelFormat.R32Uint or + MtlPixelFormat.Rg16Uint or MtlPixelFormat.Rg32Uint or MtlPixelFormat.Rgba8Uint or + MtlPixelFormat.Rgba16Uint => Gen5PixelOutputKind.Uint, + MtlPixelFormat.R16Sint or MtlPixelFormat.R32Sint or MtlPixelFormat.Rg16Sint or + MtlPixelFormat.Rg32Sint or MtlPixelFormat.Rgba8Sint or MtlPixelFormat.Rgba16Sint => + Gen5PixelOutputKind.Sint, _ => Gen5PixelOutputKind.Float, }; result = new MetalRenderTargetFormat(format, outputKind); diff --git a/src/SharpEmu.Libs/VideoOut/VulkanVideoPresenter.cs b/src/SharpEmu.Libs/VideoOut/VulkanVideoPresenter.cs index baefabc5..868d3135 100644 --- a/src/SharpEmu.Libs/VideoOut/VulkanVideoPresenter.cs +++ b/src/SharpEmu.Libs/VideoOut/VulkanVideoPresenter.cs @@ -2025,6 +2025,15 @@ internal static unsafe class VulkanVideoPresenter { var format = (dataFormat, numberType) switch { + // Early G-buffer / scene targets (R16 + RG32). GTA V Enhanced hits + // these as color targets; texture decode already knew them. + (2, 0) => Format.R16Unorm, + (2, 1) => Format.R16SNorm, + (2, 2) => Format.R16Uscaled, + (2, 3) => Format.R16Sscaled, + (2, 4) => Format.R16Uint, + (2, 5) => Format.R16Sint, + (2, 7) => Format.R16Sfloat, (4, 4) => Format.R32Uint, (4, 5) => Format.R32Sint, (4, 7) => Format.R32Sfloat, @@ -2037,6 +2046,8 @@ internal static unsafe class VulkanVideoPresenter (10, 5) => Format.R8G8B8A8Sint, (10, 9) => Format.R8G8B8A8Srgb, (10, _) => Format.R8G8B8A8Unorm, + (11, 4) => Format.R32G32Uint, + (11, 5) => Format.R32G32Sint, (11, 7) => Format.R32G32Sfloat, (12, 4) => Format.R16G16B16A16Uint, (12, 5) => Format.R16G16B16A16Sint, @@ -2065,10 +2076,11 @@ internal static unsafe class VulkanVideoPresenter var outputKind = format switch { - Format.R8Uint or Format.R32Uint or Format.R16G16Uint or - Format.R8G8B8A8Uint or Format.R16G16B16A16Uint => Gen5PixelOutputKind.Uint, - Format.R32Sint or Format.R16G16Sint or Format.R8G8B8A8Sint or - Format.R16G16B16A16Sint => Gen5PixelOutputKind.Sint, + Format.R8Uint or Format.R16Uint or Format.R32Uint or Format.R16G16Uint or + Format.R32G32Uint or Format.R8G8B8A8Uint or Format.R16G16B16A16Uint => + Gen5PixelOutputKind.Uint, + Format.R16Sint or Format.R32Sint or Format.R16G16Sint or Format.R32G32Sint or + Format.R8G8B8A8Sint or Format.R16G16B16A16Sint => Gen5PixelOutputKind.Sint, _ => Gen5PixelOutputKind.Float, }; result = new VulkanRenderTargetFormat(format, outputKind); diff --git a/tests/SharpEmu.Libs.Tests/VideoOut/VulkanPresentEncodeFormatTests.cs b/tests/SharpEmu.Libs.Tests/VideoOut/VulkanPresentEncodeFormatTests.cs index 9d4f7f18..2b8bdaec 100644 --- a/tests/SharpEmu.Libs.Tests/VideoOut/VulkanPresentEncodeFormatTests.cs +++ b/tests/SharpEmu.Libs.Tests/VideoOut/VulkanPresentEncodeFormatTests.cs @@ -56,4 +56,26 @@ public sealed class VulkanPresentEncodeFormatTests { Assert.False(VulkanVideoPresenter.IsLinearFloatPresentSource(sourceFormat)); } + + // GTA V Enhanced early G-buffer color targets observed as COMPAT failures + // when missing from TryDecodeRenderTargetFormat (format=2 / format=11). + [Theory] + [InlineData(2u, 0u, Format.R16Unorm)] + [InlineData(2u, 4u, Format.R16Uint)] + [InlineData(2u, 7u, Format.R16Sfloat)] + [InlineData(11u, 4u, Format.R32G32Uint)] + [InlineData(11u, 5u, Format.R32G32Sint)] + [InlineData(11u, 7u, Format.R32G32Sfloat)] + public void TryDecodeRenderTargetFormat_DecodesGen5R16AndRg32( + uint dataFormat, + uint numberType, + Format expected) + { + Assert.True( + VulkanVideoPresenter.TryDecodeRenderTargetFormat( + dataFormat, + numberType, + out var decoded)); + Assert.Equal(expected, decoded.Format); + } }