From d9b63353b019e1f1775370154f4f045ff14184ce Mon Sep 17 00:00:00 2001
From: gdkchan <gab.dark.100@gmail.com>
Date: Mon, 17 Apr 2023 05:13:53 -0300
Subject: Support copy between multisample and non-multisample depth textures
 (#4676)

* Support copy between multisample and non-multisample depth textures

* PR feedback
---
 Ryujinx.Graphics.OpenGL/Image/TextureView.cs | 78 ++++++++++++++++++++++++++--
 1 file changed, 74 insertions(+), 4 deletions(-)

(limited to 'Ryujinx.Graphics.OpenGL/Image/TextureView.cs')

diff --git a/Ryujinx.Graphics.OpenGL/Image/TextureView.cs b/Ryujinx.Graphics.OpenGL/Image/TextureView.cs
index ddc5f9a3..804b3b03 100644
--- a/Ryujinx.Graphics.OpenGL/Image/TextureView.cs
+++ b/Ryujinx.Graphics.OpenGL/Image/TextureView.cs
@@ -117,12 +117,20 @@ namespace Ryujinx.Graphics.OpenGL.Image
         {
             TextureView destinationView = (TextureView)destination;
 
-            if (!destinationView.Target.IsMultisample() && Target.IsMultisample())
+            bool srcIsMultisample = Target.IsMultisample();
+            bool dstIsMultisample = destinationView.Target.IsMultisample();
+
+            if (dstIsMultisample != srcIsMultisample && Info.Format.IsDepthOrStencil())
+            {
+                int layers = Math.Min(Info.GetLayers(), destinationView.Info.GetLayers() - firstLayer);
+                CopyWithBlitForDepthMS(destinationView, 0, firstLayer, layers);
+            }
+            else if (!dstIsMultisample && srcIsMultisample)
             {
                 int layers = Math.Min(Info.GetLayers(), destinationView.Info.GetLayers() - firstLayer);
                 _renderer.TextureCopyMS.CopyMSToNonMS(this, destinationView, 0, firstLayer, layers);
             }
-            else if (destinationView.Target.IsMultisample() && !Target.IsMultisample())
+            else if (dstIsMultisample && !srcIsMultisample)
             {
                 int layers = Math.Min(Info.GetLayers(), destinationView.Info.GetLayers() - firstLayer);
                 _renderer.TextureCopyMS.CopyNonMSToMS(this, destinationView, 0, firstLayer, layers);
@@ -143,11 +151,18 @@ namespace Ryujinx.Graphics.OpenGL.Image
         {
             TextureView destinationView = (TextureView)destination;
 
-            if (!destinationView.Target.IsMultisample() && Target.IsMultisample())
+            bool srcIsMultisample = Target.IsMultisample();
+            bool dstIsMultisample = destinationView.Target.IsMultisample();
+
+            if (dstIsMultisample != srcIsMultisample && Info.Format.IsDepthOrStencil())
+            {
+                CopyWithBlitForDepthMS(destinationView, srcLayer, dstLayer, 1);
+            }
+            else if (!dstIsMultisample && srcIsMultisample)
             {
                 _renderer.TextureCopyMS.CopyMSToNonMS(this, destinationView, srcLayer, dstLayer, 1);
             }
-            else if (destinationView.Target.IsMultisample() && !Target.IsMultisample())
+            else if (dstIsMultisample && !srcIsMultisample)
             {
                 _renderer.TextureCopyMS.CopyNonMSToMS(this, destinationView, srcLayer, dstLayer, 1);
             }
@@ -161,6 +176,61 @@ namespace Ryujinx.Graphics.OpenGL.Image
             }
         }
 
+        private void CopyWithBlitForDepthMS(TextureView destinationView, int srcLayer, int dstLayer, int layers)
+        {
+            // This is currently used for multisample <-> non-multisample copies.
+            // We can't do that with compute because it's not possible to write depth textures on compute.
+            // It can be done with draws, but we don't have support for saving and restoring the OpenGL state
+            // for a draw with different state right now.
+            // This approach uses blit, which causes a resolution loss since some samples will be lost
+            // in the process.
+
+            Extents2D srcRegion = new Extents2D(0, 0, Width, Height);
+            Extents2D dstRegion = new Extents2D(0, 0, destinationView.Width, destinationView.Height);
+
+            if (destinationView.Target.IsMultisample())
+            {
+                TextureView intermmediate = _renderer.TextureCopy.IntermediatePool.GetOrCreateWithAtLeast(
+                    Info.Target,
+                    Info.BlockWidth,
+                    Info.BlockHeight,
+                    Info.BytesPerPixel,
+                    Format,
+                    destinationView.Width,
+                    destinationView.Height,
+                    Info.Depth,
+                    1,
+                    1);
+
+                _renderer.TextureCopy.Copy(this, intermmediate, srcRegion, dstRegion, false);
+                _renderer.TextureCopy.Copy(intermmediate, destinationView, dstRegion, dstRegion, false, srcLayer, dstLayer, 0, 0, layers, 1);
+            }
+            else
+            {
+                Target target = Target switch
+                {
+                    Target.Texture2DMultisample => Target.Texture2D,
+                    Target.Texture2DMultisampleArray => Target.Texture2DArray,
+                    _ => Target
+                };
+
+                TextureView intermmediate = _renderer.TextureCopy.IntermediatePool.GetOrCreateWithAtLeast(
+                    target,
+                    Info.BlockWidth,
+                    Info.BlockHeight,
+                    Info.BytesPerPixel,
+                    Format,
+                    Width,
+                    Height,
+                    Info.Depth,
+                    1,
+                    1);
+
+                _renderer.TextureCopy.Copy(this, intermmediate, srcRegion, srcRegion, false);
+                _renderer.TextureCopy.Copy(intermmediate, destinationView, srcRegion, dstRegion, false, srcLayer, dstLayer, 0, 0, layers, 1);
+            }
+        }
+
         public void CopyTo(ITexture destination, Extents2D srcRegion, Extents2D dstRegion, bool linearFilter)
         {
             _renderer.TextureCopy.Copy(this, (TextureView)destination, srcRegion, dstRegion, linearFilter);
-- 
cgit v1.2.3-70-g09d2