From ac4ec1a0151fd958d7ec58146169763b446836fe Mon Sep 17 00:00:00 2001
From: gdkchan <gab.dark.100@gmail.com>
Date: Sat, 11 Sep 2021 17:54:18 -0300
Subject: [PATCH] Account for negative strides on DMA copy (#2623)

* Account for negative strides on DMA copy

* Should account for non-zero Y
---
 Ryujinx.Graphics.Gpu/Engine/Dma/DmaClass.cs   | 25 ++++++++++++++++---
 .../InlineToMemory/InlineToMemoryClass.cs     |  4 +--
 Ryujinx.Graphics.Gpu/Image/Texture.cs         |  2 +-
 Ryujinx.Graphics.Texture/OffsetCalculator.cs  |  7 +++---
 4 files changed, 27 insertions(+), 11 deletions(-)

diff --git a/Ryujinx.Graphics.Gpu/Engine/Dma/DmaClass.cs b/Ryujinx.Graphics.Gpu/Engine/Dma/DmaClass.cs
index 3078cc8a..8e4ac5e2 100644
--- a/Ryujinx.Graphics.Gpu/Engine/Dma/DmaClass.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/Dma/DmaClass.cs
@@ -76,6 +76,13 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma
         {
             if (linear)
             {
+                // If the stride is negative, the texture has to be flipped, so
+                // the fast copy is not trivial, use the slow path.
+                if (stride <= 0)
+                {
+                    return false;
+                }
+
                 int alignWidth = Constants.StrideAlignment / bpp;
                 return tex.RegionX == 0 &&
                        tex.RegionY == 0 &&
@@ -155,8 +162,18 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma
                 (int srcBaseOffset, int srcSize) = srcCalculator.GetRectangleRange(src.RegionX, src.RegionY, xCount, yCount);
                 (int dstBaseOffset, int dstSize) = dstCalculator.GetRectangleRange(dst.RegionX, dst.RegionY, xCount, yCount);
 
-                ReadOnlySpan<byte> srcSpan = memoryManager.GetSpan(srcGpuVa + (uint)srcBaseOffset, srcSize, true);
-                Span<byte> dstSpan = memoryManager.GetSpan(dstGpuVa + (uint)dstBaseOffset, dstSize).ToArray();
+                if (srcLinear && srcStride < 0)
+                {
+                    srcBaseOffset += srcStride * (yCount - 1);
+                }
+
+                if (dstLinear && dstStride < 0)
+                {
+                    dstBaseOffset += dstStride * (yCount - 1);
+                }
+
+                ReadOnlySpan<byte> srcSpan = memoryManager.GetSpan(srcGpuVa + (ulong)srcBaseOffset, srcSize, true);
+                Span<byte> dstSpan = memoryManager.GetSpan(dstGpuVa + (ulong)dstBaseOffset, dstSize).ToArray();
 
                 bool completeSource = IsTextureCopyComplete(src, srcLinear, srcBpp, srcStride, xCount, yCount);
                 bool completeDest = IsTextureCopyComplete(dst, dstLinear, dstBpp, dstStride, xCount, yCount);
@@ -214,7 +231,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma
                     {
                         srcSpan.CopyTo(dstSpan); // No layout conversion has to be performed, just copy the data entirely.
 
-                        memoryManager.Write(dstGpuVa + (uint)dstBaseOffset, dstSpan);
+                        memoryManager.Write(dstGpuVa + (ulong)dstBaseOffset, dstSpan);
 
                         return;
                     }
@@ -255,7 +272,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma
                     _ => throw new NotSupportedException($"Unable to copy ${srcBpp} bpp pixel format.")
                 };
 
-                memoryManager.Write(dstGpuVa + (uint)dstBaseOffset, dstSpan);
+                memoryManager.Write(dstGpuVa + (ulong)dstBaseOffset, dstSpan);
             }
             else
             {
diff --git a/Ryujinx.Graphics.Gpu/Engine/InlineToMemory/InlineToMemoryClass.cs b/Ryujinx.Graphics.Gpu/Engine/InlineToMemory/InlineToMemoryClass.cs
index 81c5ad77..75b8e220 100644
--- a/Ryujinx.Graphics.Gpu/Engine/InlineToMemory/InlineToMemoryClass.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/InlineToMemory/InlineToMemoryClass.cs
@@ -110,10 +110,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.InlineToMemory
 
             ulong dstGpuVa = ((ulong)state.OffsetOutUpperValue << 32) | state.OffsetOut;
 
-            ulong dstBaseAddress = _channel.MemoryManager.Translate(dstGpuVa);
-
             // Trigger read tracking, to flush any managed resources in the destination region.
-            _channel.MemoryManager.Physical.GetSpan(dstBaseAddress, _size, true);
+            _channel.MemoryManager.GetSpan(dstGpuVa, _size, true);
 
             _dstGpuVa = dstGpuVa;
             _dstX = state.SetDstOriginBytesXV;
diff --git a/Ryujinx.Graphics.Gpu/Image/Texture.cs b/Ryujinx.Graphics.Gpu/Image/Texture.cs
index 3a66960d..7cc8faf6 100644
--- a/Ryujinx.Graphics.Gpu/Image/Texture.cs
+++ b/Ryujinx.Graphics.Gpu/Image/Texture.cs
@@ -892,7 +892,7 @@ namespace Ryujinx.Graphics.Gpu.Image
                 {
                     _physicalMemory.Write(Range, GetTextureDataFromGpu(Span<byte>.Empty, true, texture));
                 }
-                else 
+                else
                 {
                     _physicalMemory.WriteUntracked(Range, GetTextureDataFromGpu(Span<byte>.Empty, false, texture));
                 }
diff --git a/Ryujinx.Graphics.Texture/OffsetCalculator.cs b/Ryujinx.Graphics.Texture/OffsetCalculator.cs
index dd4b6e7f..d7472e2f 100644
--- a/Ryujinx.Graphics.Texture/OffsetCalculator.cs
+++ b/Ryujinx.Graphics.Texture/OffsetCalculator.cs
@@ -1,4 +1,5 @@
 using Ryujinx.Common;
+using System;
 using System.Runtime.CompilerServices;
 using static Ryujinx.Graphics.Texture.BlockLinearConstants;
 
@@ -111,9 +112,9 @@ namespace Ryujinx.Graphics.Texture
         {
             if (_isLinear)
             {
-                int start = y * _stride + x * _bytesPerPixel;
-                int end = (y + height - 1) * _stride + (x + width) * _bytesPerPixel;
-                return (start, end - start);
+                int start = y * Math.Abs(_stride) + x * _bytesPerPixel;
+                int end = (y + height - 1) * Math.Abs(_stride) + (x + width) * _bytesPerPixel;
+                return (y * _stride + x * _bytesPerPixel, end - start);
             }
             else
             {