fix(gpu): decode Gen5 R16 and RG32 render-target formats

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
MarcelMediaDev
2026-07-26 00:55:28 +01:00
parent 927a2349bf
commit 2eb09d9939
3 changed files with 53 additions and 8 deletions
@@ -218,6 +218,13 @@ internal static class MetalGuestFormats
{ {
var format = (dataFormat, numberType) switch 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, 4) => MtlPixelFormat.R32Uint,
(4, 5) => MtlPixelFormat.R32Sint, (4, 5) => MtlPixelFormat.R32Sint,
(4, 7) => MtlPixelFormat.R32Float, (4, 7) => MtlPixelFormat.R32Float,
@@ -230,6 +237,8 @@ internal static class MetalGuestFormats
(10, 5) => MtlPixelFormat.Rgba8Sint, (10, 5) => MtlPixelFormat.Rgba8Sint,
(10, 9) => MtlPixelFormat.Rgba8UnormSrgb, (10, 9) => MtlPixelFormat.Rgba8UnormSrgb,
(10, _) => MtlPixelFormat.Rgba8Unorm, (10, _) => MtlPixelFormat.Rgba8Unorm,
(11, 4) => MtlPixelFormat.Rg32Uint,
(11, 5) => MtlPixelFormat.Rg32Sint,
(11, 7) => MtlPixelFormat.Rg32Float, (11, 7) => MtlPixelFormat.Rg32Float,
(12, 4) => MtlPixelFormat.Rgba16Uint, (12, 4) => MtlPixelFormat.Rgba16Uint,
(12, 5) => MtlPixelFormat.Rgba16Sint, (12, 5) => MtlPixelFormat.Rgba16Sint,
@@ -258,10 +267,12 @@ internal static class MetalGuestFormats
var outputKind = format switch var outputKind = format switch
{ {
MtlPixelFormat.R8Uint or MtlPixelFormat.R32Uint or MtlPixelFormat.Rg16Uint or MtlPixelFormat.R8Uint or MtlPixelFormat.R16Uint or MtlPixelFormat.R32Uint or
MtlPixelFormat.Rgba8Uint or MtlPixelFormat.Rgba16Uint => Gen5PixelOutputKind.Uint, MtlPixelFormat.Rg16Uint or MtlPixelFormat.Rg32Uint or MtlPixelFormat.Rgba8Uint or
MtlPixelFormat.R32Sint or MtlPixelFormat.Rg16Sint or MtlPixelFormat.Rgba8Sint or MtlPixelFormat.Rgba16Uint => Gen5PixelOutputKind.Uint,
MtlPixelFormat.Rgba16Sint => Gen5PixelOutputKind.Sint, MtlPixelFormat.R16Sint or MtlPixelFormat.R32Sint or MtlPixelFormat.Rg16Sint or
MtlPixelFormat.Rg32Sint or MtlPixelFormat.Rgba8Sint or MtlPixelFormat.Rgba16Sint =>
Gen5PixelOutputKind.Sint,
_ => Gen5PixelOutputKind.Float, _ => Gen5PixelOutputKind.Float,
}; };
result = new MetalRenderTargetFormat(format, outputKind); result = new MetalRenderTargetFormat(format, outputKind);
@@ -2025,6 +2025,15 @@ internal static unsafe class VulkanVideoPresenter
{ {
var format = (dataFormat, numberType) switch 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, 4) => Format.R32Uint,
(4, 5) => Format.R32Sint, (4, 5) => Format.R32Sint,
(4, 7) => Format.R32Sfloat, (4, 7) => Format.R32Sfloat,
@@ -2037,6 +2046,8 @@ internal static unsafe class VulkanVideoPresenter
(10, 5) => Format.R8G8B8A8Sint, (10, 5) => Format.R8G8B8A8Sint,
(10, 9) => Format.R8G8B8A8Srgb, (10, 9) => Format.R8G8B8A8Srgb,
(10, _) => Format.R8G8B8A8Unorm, (10, _) => Format.R8G8B8A8Unorm,
(11, 4) => Format.R32G32Uint,
(11, 5) => Format.R32G32Sint,
(11, 7) => Format.R32G32Sfloat, (11, 7) => Format.R32G32Sfloat,
(12, 4) => Format.R16G16B16A16Uint, (12, 4) => Format.R16G16B16A16Uint,
(12, 5) => Format.R16G16B16A16Sint, (12, 5) => Format.R16G16B16A16Sint,
@@ -2065,10 +2076,11 @@ internal static unsafe class VulkanVideoPresenter
var outputKind = format switch var outputKind = format switch
{ {
Format.R8Uint or Format.R32Uint or Format.R16G16Uint or Format.R8Uint or Format.R16Uint or Format.R32Uint or Format.R16G16Uint or
Format.R8G8B8A8Uint or Format.R16G16B16A16Uint => Gen5PixelOutputKind.Uint, Format.R32G32Uint or Format.R8G8B8A8Uint or Format.R16G16B16A16Uint =>
Format.R32Sint or Format.R16G16Sint or Format.R8G8B8A8Sint or Gen5PixelOutputKind.Uint,
Format.R16G16B16A16Sint => Gen5PixelOutputKind.Sint, Format.R16Sint or Format.R32Sint or Format.R16G16Sint or Format.R32G32Sint or
Format.R8G8B8A8Sint or Format.R16G16B16A16Sint => Gen5PixelOutputKind.Sint,
_ => Gen5PixelOutputKind.Float, _ => Gen5PixelOutputKind.Float,
}; };
result = new VulkanRenderTargetFormat(format, outputKind); result = new VulkanRenderTargetFormat(format, outputKind);
@@ -56,4 +56,26 @@ public sealed class VulkanPresentEncodeFormatTests
{ {
Assert.False(VulkanVideoPresenter.IsLinearFloatPresentSource(sourceFormat)); 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);
}
} }