Jelajahi Sumber

Implement fast DMA texture to texture copy (#7299)

* Implement fast DMA texture to texture copy

* PR feedback
gdkchan 1 tahun lalu
induk
melakukan
cd74ae1bbd

+ 46 - 2
src/Ryujinx.Graphics.Gpu/Engine/Dma/DmaClass.cs

@@ -276,8 +276,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma
                     dstBaseOffset += dstStride * (yCount - 1);
                 }
 
-                ReadOnlySpan<byte> srcSpan = memoryManager.GetSpan(srcGpuVa + (ulong)srcBaseOffset, srcSize, true);
-
                 // If remapping is disabled, we always copy the components directly, in order.
                 // If it's enabled, but the mapping is just XYZW, we also copy them in order.
                 bool isIdentityRemap = !remap ||
@@ -289,6 +287,52 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma
                 bool completeSource = IsTextureCopyComplete(src, srcLinear, srcBpp, srcStride, xCount, yCount);
                 bool completeDest = IsTextureCopyComplete(dst, dstLinear, dstBpp, dstStride, xCount, yCount);
 
+                // Check if the source texture exists on the GPU, if it does, do a GPU side copy.
+                // Otherwise, we would need to flush the source texture which is costly.
+                // We don't expect the source to be linear in such cases, as linear source usually indicates buffer or CPU written data.
+
+                if (completeSource && completeDest && !srcLinear && isIdentityRemap)
+                {
+                    var source = memoryManager.Physical.TextureCache.FindTexture(
+                        memoryManager,
+                        srcGpuVa,
+                        srcBpp,
+                        srcStride,
+                        src.Height,
+                        xCount,
+                        yCount,
+                        srcLinear,
+                        src.MemoryLayout.UnpackGobBlocksInY(),
+                        src.MemoryLayout.UnpackGobBlocksInZ());
+
+                    if (source != null && source.Height == yCount)
+                    {
+                        source.SynchronizeMemory();
+
+                        var target = memoryManager.Physical.TextureCache.FindOrCreateTexture(
+                            memoryManager,
+                            source.Info.FormatInfo,
+                            dstGpuVa,
+                            xCount,
+                            yCount,
+                            dstStride,
+                            dstLinear,
+                            dst.MemoryLayout.UnpackGobBlocksInY(),
+                            dst.MemoryLayout.UnpackGobBlocksInZ());
+
+                        if (source.ScaleFactor != target.ScaleFactor)
+                        {
+                            target.PropagateScale(source);
+                        }
+
+                        source.HostTexture.CopyTo(target.HostTexture, 0, 0);
+                        target.SignalModified();
+                        return;
+                    }
+                }
+
+                ReadOnlySpan<byte> srcSpan = memoryManager.GetSpan(srcGpuVa + (ulong)srcBaseOffset, srcSize, true);
+
                 // Try to set the texture data directly,
                 // but only if we are doing a complete copy,
                 // and not for block linear to linear copies, since those are typically accessed from the CPU.

+ 47 - 0
src/Ryujinx.Graphics.Gpu/Image/TextureCache.cs

@@ -347,6 +347,53 @@ namespace Ryujinx.Graphics.Gpu.Image
             return texture;
         }
 
+        /// <summary>
+        /// Tries to find an existing texture, or create a new one if not found.
+        /// </summary>
+        /// <param name="memoryManager">GPU memory manager where the texture is mapped</param>
+        /// <param name="formatInfo">Format of the texture</param>
+        /// <param name="gpuAddress">GPU virtual address of the texture</param>
+        /// <param name="xCount">Texture width in bytes</param>
+        /// <param name="yCount">Texture height</param>
+        /// <param name="stride">Texture stride if linear, otherwise ignored</param>
+        /// <param name="isLinear">Indicates if the texture is linear or block linear</param>
+        /// <param name="gobBlocksInY">GOB blocks in Y for block linear textures</param>
+        /// <param name="gobBlocksInZ">GOB blocks in Z for 3D block linear textures</param>
+        /// <returns>The texture</returns>
+        public Texture FindOrCreateTexture(
+            MemoryManager memoryManager,
+            FormatInfo formatInfo,
+            ulong gpuAddress,
+            int xCount,
+            int yCount,
+            int stride,
+            bool isLinear,
+            int gobBlocksInY,
+            int gobBlocksInZ)
+        {
+            TextureInfo info = new(
+                gpuAddress,
+                xCount / formatInfo.BytesPerPixel,
+                yCount,
+                1,
+                1,
+                1,
+                1,
+                stride,
+                isLinear,
+                gobBlocksInY,
+                gobBlocksInZ,
+                1,
+                Target.Texture2D,
+                formatInfo);
+
+            Texture texture = FindOrCreateTexture(memoryManager, TextureSearchFlags.ForCopy, info, 0, sizeHint: new Size(xCount, yCount, 1));
+
+            texture?.SynchronizeMemory();
+
+            return texture;
+        }
+
         /// <summary>
         /// Tries to find an existing texture, or create a new one if not found.
         /// </summary>