From 841dd56f4ce850693aee5980cd750791624e47be Mon Sep 17 00:00:00 2001
From: gdkchan <gab.dark.100@gmail.com>
Date: Tue, 31 Oct 2023 19:00:39 -0300
Subject: [PATCH] Implement copy dependency for depth and color textures
 (#4365)

* Implement copy dependency for depth and color textures

* Revert changes added because R32 <-> D32 copies were illegal

* Restore depth alias matches
---
 .../Image/TextureCache.cs                     |  6 +-
 .../Image/TextureCompatibility.cs             | 61 ++++++++++++++-----
 .../Image/TextureCopy.cs                      |  4 +-
 .../Image/TextureView.cs                      | 29 +++++++++
 src/Ryujinx.Graphics.Vulkan/TextureView.cs    | 11 ++++
 5 files changed, 90 insertions(+), 21 deletions(-)

diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureCache.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureCache.cs
index 432b108535..6b92c0aaf6 100644
--- a/src/Ryujinx.Graphics.Gpu/Image/TextureCache.cs
+++ b/src/Ryujinx.Graphics.Gpu/Image/TextureCache.cs
@@ -733,9 +733,7 @@ namespace Ryujinx.Graphics.Gpu.Image
                 {
                     if (overlap.IsView)
                     {
-                        overlapCompatibility = overlapCompatibility == TextureViewCompatibility.FormatAlias ?
-                            TextureViewCompatibility.Incompatible :
-                            TextureViewCompatibility.CopyOnly;
+                        overlapCompatibility = TextureViewCompatibility.CopyOnly;
                     }
                     else
                     {
@@ -813,7 +811,7 @@ namespace Ryujinx.Graphics.Gpu.Image
                     Texture overlap = _textureOverlaps[index];
                     OverlapInfo oInfo = _overlapInfo[index];
 
-                    if (oInfo.Compatibility <= TextureViewCompatibility.LayoutIncompatible || oInfo.Compatibility == TextureViewCompatibility.FormatAlias)
+                    if (oInfo.Compatibility <= TextureViewCompatibility.LayoutIncompatible)
                     {
                         if (!overlap.IsView && texture.DataOverlaps(overlap, oInfo.Compatibility))
                         {
diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs
index 3a0efcddaa..5af0471c02 100644
--- a/src/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs
+++ b/src/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs
@@ -226,7 +226,7 @@ namespace Ryujinx.Graphics.Gpu.Image
         {
             // D32F and R32F texture have the same representation internally,
             // however the R32F format is used to sample from depth textures.
-            if (lhs.FormatInfo.Format == Format.D32Float && rhs.FormatInfo.Format == Format.R32Float && (forSampler || depthAlias))
+            if (IsValidDepthAsColorAlias(lhs.FormatInfo.Format, rhs.FormatInfo.Format) && (forSampler || depthAlias))
             {
                 return TextureMatchQuality.FormatAlias;
             }
@@ -239,14 +239,8 @@ namespace Ryujinx.Graphics.Gpu.Image
                 {
                     return TextureMatchQuality.FormatAlias;
                 }
-
-                if (lhs.FormatInfo.Format == Format.D16Unorm && rhs.FormatInfo.Format == Format.R16Unorm)
-                {
-                    return TextureMatchQuality.FormatAlias;
-                }
-
-                if ((lhs.FormatInfo.Format == Format.D24UnormS8Uint ||
-                     lhs.FormatInfo.Format == Format.S8UintD24Unorm) && rhs.FormatInfo.Format == Format.B8G8R8A8Unorm)
+                else if ((lhs.FormatInfo.Format == Format.D24UnormS8Uint ||
+                          lhs.FormatInfo.Format == Format.S8UintD24Unorm) && rhs.FormatInfo.Format == Format.B8G8R8A8Unorm)
                 {
                     return TextureMatchQuality.FormatAlias;
                 }
@@ -632,12 +626,27 @@ namespace Ryujinx.Graphics.Gpu.Image
 
             if (lhsFormat.Format.IsDepthOrStencil() || rhsFormat.Format.IsDepthOrStencil())
             {
-                return FormatMatches(lhs, rhs, flags.HasFlag(TextureSearchFlags.ForSampler), flags.HasFlag(TextureSearchFlags.DepthAlias)) switch
+                bool forSampler = flags.HasFlag(TextureSearchFlags.ForSampler);
+                bool depthAlias = flags.HasFlag(TextureSearchFlags.DepthAlias);
+
+                TextureMatchQuality matchQuality = FormatMatches(lhs, rhs, forSampler, depthAlias);
+
+                if (matchQuality == TextureMatchQuality.Perfect)
                 {
-                    TextureMatchQuality.Perfect => TextureViewCompatibility.Full,
-                    TextureMatchQuality.FormatAlias => TextureViewCompatibility.FormatAlias,
-                    _ => TextureViewCompatibility.Incompatible,
-                };
+                    return TextureViewCompatibility.Full;
+                }
+                else if (matchQuality == TextureMatchQuality.FormatAlias)
+                {
+                    return TextureViewCompatibility.FormatAlias;
+                }
+                else if (IsValidColorAsDepthAlias(lhsFormat.Format, rhsFormat.Format) || IsValidDepthAsColorAlias(lhsFormat.Format, rhsFormat.Format))
+                {
+                    return TextureViewCompatibility.CopyOnly;
+                }
+                else
+                {
+                    return TextureViewCompatibility.Incompatible;
+                }
             }
 
             if (IsFormatHostIncompatible(lhs, caps) || IsFormatHostIncompatible(rhs, caps))
@@ -666,6 +675,30 @@ namespace Ryujinx.Graphics.Gpu.Image
             return TextureViewCompatibility.Incompatible;
         }
 
+        /// <summary>
+        /// Checks if it's valid to alias a color format as a depth format.
+        /// </summary>
+        /// <param name="lhsFormat">Source format to be checked</param>
+        /// <param name="rhsFormat">Target format to be checked</param>
+        /// <returns>True if it's valid to alias the formats</returns>
+        private static bool IsValidColorAsDepthAlias(Format lhsFormat, Format rhsFormat)
+        {
+            return (lhsFormat == Format.R32Float && rhsFormat == Format.D32Float) ||
+                   (lhsFormat == Format.R16Unorm && rhsFormat == Format.D16Unorm);
+        }
+
+        /// <summary>
+        /// Checks if it's valid to alias a depth format as a color format.
+        /// </summary>
+        /// <param name="lhsFormat">Source format to be checked</param>
+        /// <param name="rhsFormat">Target format to be checked</param>
+        /// <returns>True if it's valid to alias the formats</returns>
+        private static bool IsValidDepthAsColorAlias(Format lhsFormat, Format rhsFormat)
+        {
+            return (lhsFormat == Format.D32Float && rhsFormat == Format.R32Float) ||
+                   (lhsFormat == Format.D16Unorm && rhsFormat == Format.R16Unorm);
+        }
+
         /// <summary>
         /// Checks if aliasing of two formats that would normally be considered incompatible be allowed,
         /// using copy dependencies.
diff --git a/src/Ryujinx.Graphics.OpenGL/Image/TextureCopy.cs b/src/Ryujinx.Graphics.OpenGL/Image/TextureCopy.cs
index e33940cb13..128f481f67 100644
--- a/src/Ryujinx.Graphics.OpenGL/Image/TextureCopy.cs
+++ b/src/Ryujinx.Graphics.OpenGL/Image/TextureCopy.cs
@@ -367,7 +367,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
             return to;
         }
 
-        private TextureView PboCopy(TextureView from, TextureView to, int srcLayer, int dstLayer, int srcLevel, int dstLevel, int width, int height)
+        public void PboCopy(TextureView from, TextureView to, int srcLayer, int dstLayer, int srcLevel, int dstLevel, int width, int height)
         {
             int dstWidth = width;
             int dstHeight = height;
@@ -445,8 +445,6 @@ namespace Ryujinx.Graphics.OpenGL.Image
             }
 
             GL.BindBuffer(BufferTarget.PixelUnpackBuffer, 0);
-
-            return to;
         }
 
         private void EnsurePbo(TextureView view)
diff --git a/src/Ryujinx.Graphics.OpenGL/Image/TextureView.cs b/src/Ryujinx.Graphics.OpenGL/Image/TextureView.cs
index 0f5fe46a5f..7f1b1c3824 100644
--- a/src/Ryujinx.Graphics.OpenGL/Image/TextureView.cs
+++ b/src/Ryujinx.Graphics.OpenGL/Image/TextureView.cs
@@ -140,6 +140,28 @@ namespace Ryujinx.Graphics.OpenGL.Image
                 int levels = Math.Min(Info.Levels, destinationView.Info.Levels - firstLevel);
                 _renderer.TextureCopyIncompatible.CopyIncompatibleFormats(this, destinationView, 0, firstLayer, 0, firstLevel, layers, levels);
             }
+            else if (destinationView.Format.IsDepthOrStencil() != Format.IsDepthOrStencil())
+            {
+                int layers = Math.Min(Info.GetLayers(), destinationView.Info.GetLayers() - firstLayer);
+                int levels = Math.Min(Info.Levels, destinationView.Info.Levels - firstLevel);
+
+                for (int level = 0; level < levels; level++)
+                {
+                    int srcWidth = Math.Max(1, Width >> level);
+                    int srcHeight = Math.Max(1, Height >> level);
+
+                    int dstWidth = Math.Max(1, destinationView.Width >> (firstLevel + level));
+                    int dstHeight = Math.Max(1, destinationView.Height >> (firstLevel + level));
+
+                    int minWidth = Math.Min(srcWidth, dstWidth);
+                    int minHeight = Math.Min(srcHeight, dstHeight);
+
+                    for (int layer = 0; layer < layers; layer++)
+                    {
+                        _renderer.TextureCopy.PboCopy(this, destinationView, 0, firstLayer + layer, 0, firstLevel + level, minWidth, minHeight);
+                    }
+                }
+            }
             else
             {
                 _renderer.TextureCopy.CopyUnscaled(this, destinationView, 0, firstLayer, 0, firstLevel);
@@ -169,6 +191,13 @@ namespace Ryujinx.Graphics.OpenGL.Image
             {
                 _renderer.TextureCopyIncompatible.CopyIncompatibleFormats(this, destinationView, srcLayer, dstLayer, srcLevel, dstLevel, 1, 1);
             }
+            else if (destinationView.Format.IsDepthOrStencil() != Format.IsDepthOrStencil())
+            {
+                int minWidth = Math.Min(Width, destinationView.Width);
+                int minHeight = Math.Min(Height, destinationView.Height);
+
+                _renderer.TextureCopy.PboCopy(this, destinationView, srcLayer, dstLayer, srcLevel, dstLevel, minWidth, minHeight);
+            }
             else
             {
                 _renderer.TextureCopy.CopyUnscaled(this, destinationView, srcLayer, dstLayer, srcLevel, dstLevel, 1, 1);
diff --git a/src/Ryujinx.Graphics.Vulkan/TextureView.cs b/src/Ryujinx.Graphics.Vulkan/TextureView.cs
index 09128f0075..05dbd15cef 100644
--- a/src/Ryujinx.Graphics.Vulkan/TextureView.cs
+++ b/src/Ryujinx.Graphics.Vulkan/TextureView.cs
@@ -211,6 +211,13 @@ namespace Ryujinx.Graphics.Vulkan
                 int levels = Math.Min(Info.Levels, dst.Info.Levels - firstLevel);
                 _gd.HelperShader.CopyIncompatibleFormats(_gd, cbs, src, dst, 0, firstLayer, 0, firstLevel, layers, levels);
             }
+            else if (src.Info.Format.IsDepthOrStencil() != dst.Info.Format.IsDepthOrStencil())
+            {
+                int layers = Math.Min(Info.GetLayers(), dst.Info.GetLayers() - firstLayer);
+                int levels = Math.Min(Info.Levels, dst.Info.Levels - firstLevel);
+
+                _gd.HelperShader.CopyColor(_gd, cbs, src, dst, 0, firstLayer, 0, FirstLevel, layers, levels);
+            }
             else
             {
                 TextureCopy.Copy(
@@ -260,6 +267,10 @@ namespace Ryujinx.Graphics.Vulkan
             {
                 _gd.HelperShader.CopyIncompatibleFormats(_gd, cbs, src, dst, srcLayer, dstLayer, srcLevel, dstLevel, 1, 1);
             }
+            else if (src.Info.Format.IsDepthOrStencil() != dst.Info.Format.IsDepthOrStencil())
+            {
+                _gd.HelperShader.CopyColor(_gd, cbs, src, dst, srcLayer, dstLayer, srcLevel, dstLevel, 1, 1);
+            }
             else
             {
                 TextureCopy.Copy(