From 70d65d3d8e77e66226ebab7f23d9b6e8c271319f Mon Sep 17 00:00:00 2001
From: gdkchan <gab.dark.100@gmail.com>
Date: Sun, 19 Nov 2023 15:27:34 -0300
Subject: Enable copy dependency between RGBA8 and RGBA32 formats (#5943)

* Enable copy dependency between RGBA8 and RGBA32 formats

* Take packed flag into account for texture formats

* Account for widths not being a multiple of each other

* Don't try to alias depth textures as color, fix log condition

* PR feedback
---
 src/Ryujinx.Graphics.Gpu/Image/FormatTable.cs      | 30 ++++++++++++++++++++--
 .../Image/TextureCompatibility.cs                  | 20 +++++++++++++--
 src/Ryujinx.Graphics.Gpu/Image/TexturePool.cs      |  2 +-
 3 files changed, 47 insertions(+), 5 deletions(-)

(limited to 'src')

diff --git a/src/Ryujinx.Graphics.Gpu/Image/FormatTable.cs b/src/Ryujinx.Graphics.Gpu/Image/FormatTable.cs
index 1b517e63..0af0725a 100644
--- a/src/Ryujinx.Graphics.Gpu/Image/FormatTable.cs
+++ b/src/Ryujinx.Graphics.Gpu/Image/FormatTable.cs
@@ -651,9 +651,35 @@ namespace Ryujinx.Graphics.Gpu.Image
         /// <returns>True if the format is valid, false otherwise</returns>
         public static bool TryGetTextureFormat(uint encoded, bool isSrgb, out FormatInfo format)
         {
-            encoded |= (isSrgb ? 1u << 19 : 0u);
+            bool isPacked = (encoded & 0x80000000u) != 0;
+            if (isPacked)
+            {
+                encoded &= ~0x80000000u;
+            }
 
-            return _textureFormats.TryGetValue((TextureFormat)encoded, out format);
+            encoded |= isSrgb ? 1u << 19 : 0u;
+
+            bool found = _textureFormats.TryGetValue((TextureFormat)encoded, out format);
+
+            if (found && isPacked && !format.Format.IsDepthOrStencil())
+            {
+                // If the packed flag is set, then the components of the pixel are tightly packed into the
+                // GPU registers on the shader.
+                // We can get the same behaviour by aliasing the texture as a format with the same amount of
+                // bytes per pixel, but only a single or the lowest possible number of components.
+
+                format = format.BytesPerPixel switch
+                {
+                    1 => new FormatInfo(Format.R8Unorm, 1, 1, 1, 1),
+                    2 => new FormatInfo(Format.R16Unorm, 1, 1, 2, 1),
+                    4 => new FormatInfo(Format.R32Float, 1, 1, 4, 1),
+                    8 => new FormatInfo(Format.R32G32Float, 1, 1, 8, 2),
+                    16 => new FormatInfo(Format.R32G32B32A32Float, 1, 1, 16, 4),
+                    _ => format,
+                };
+            }
+
+            return found;
         }
 
         /// <summary>
diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs
index 5af0471c..eef38948 100644
--- a/src/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs
+++ b/src/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs
@@ -2,6 +2,8 @@ using Ryujinx.Common;
 using Ryujinx.Graphics.GAL;
 using Ryujinx.Graphics.Texture;
 using System;
+using System.Diagnostics;
+using System.Numerics;
 
 namespace Ryujinx.Graphics.Gpu.Image
 {
@@ -339,7 +341,20 @@ namespace Ryujinx.Graphics.Gpu.Image
 
             if (lhs.FormatInfo.BytesPerPixel != rhs.FormatInfo.BytesPerPixel && IsIncompatibleFormatAliasingAllowed(lhs.FormatInfo, rhs.FormatInfo))
             {
-                alignedWidthMatches = lhsSize.Width * lhs.FormatInfo.BytesPerPixel == rhsSize.Width * rhs.FormatInfo.BytesPerPixel;
+                // If the formats are incompatible, but the texture strides match,
+                // we might allow them to be copy compatible depending on the format.
+                // The strides are aligned because the format with higher bytes per pixel
+                // might need a bit of padding at the end due to one width not being a multiple of the other.
+
+                Debug.Assert((1 << BitOperations.Log2((uint)lhs.FormatInfo.BytesPerPixel)) == lhs.FormatInfo.BytesPerPixel);
+                Debug.Assert((1 << BitOperations.Log2((uint)rhs.FormatInfo.BytesPerPixel)) == rhs.FormatInfo.BytesPerPixel);
+
+                int alignment = Math.Max(lhs.FormatInfo.BytesPerPixel, rhs.FormatInfo.BytesPerPixel);
+
+                int lhsStride = BitUtils.AlignUp(lhsSize.Width * lhs.FormatInfo.BytesPerPixel, alignment);
+                int rhsStride = BitUtils.AlignUp(rhsSize.Width * rhs.FormatInfo.BytesPerPixel, alignment);
+
+                alignedWidthMatches = lhsStride == rhsStride;
             }
 
             TextureViewCompatibility result = TextureViewCompatibility.Full;
@@ -718,7 +733,8 @@ namespace Ryujinx.Graphics.Gpu.Image
                 (lhsFormat, rhsFormat) = (rhsFormat, lhsFormat);
             }
 
-            return lhsFormat.Format == Format.R8Unorm && rhsFormat.Format == Format.R8G8B8A8Unorm;
+            return (lhsFormat.Format == Format.R8G8B8A8Unorm && rhsFormat.Format == Format.R32G32B32A32Float) ||
+                   (lhsFormat.Format == Format.R8Unorm && rhsFormat.Format == Format.R8G8B8A8Unorm);
         }
 
         /// <summary>
diff --git a/src/Ryujinx.Graphics.Gpu/Image/TexturePool.cs b/src/Ryujinx.Graphics.Gpu/Image/TexturePool.cs
index 0fdb6cd6..a4035577 100644
--- a/src/Ryujinx.Graphics.Gpu/Image/TexturePool.cs
+++ b/src/Ryujinx.Graphics.Gpu/Image/TexturePool.cs
@@ -430,7 +430,7 @@ namespace Ryujinx.Graphics.Gpu.Image
 
             if (!FormatTable.TryGetTextureFormat(format, srgb, out FormatInfo formatInfo))
             {
-                if (gpuVa != 0 && (int)format > 0)
+                if (gpuVa != 0 && format != 0)
                 {
                     Logger.Error?.Print(LogClass.Gpu, $"Invalid texture format 0x{format:X} (sRGB: {srgb}).");
                 }
-- 
cgit v1.2.3-70-g09d2