diff --git a/Ryujinx.Graphics.Gpu/Engine/Dma/DmaClass.cs b/Ryujinx.Graphics.Gpu/Engine/Dma/DmaClass.cs index df7e55a11..b1f766b83 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Dma/DmaClass.cs +++ b/Ryujinx.Graphics.Gpu/Engine/Dma/DmaClass.cs @@ -208,7 +208,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma } ReadOnlySpan srcSpan = memoryManager.GetSpan(srcGpuVa + (ulong)srcBaseOffset, srcSize, true); - Span 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); @@ -262,43 +261,63 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma target.SynchronizeMemory(); target.SetData(data); target.SignalModified(); - return; } else if (srcCalculator.LayoutMatches(dstCalculator)) { - srcSpan.CopyTo(dstSpan); // No layout conversion has to be performed, just copy the data entirely. - - memoryManager.Write(dstGpuVa + (ulong)dstBaseOffset, dstSpan); - + // No layout conversion has to be performed, just copy the data entirely. + memoryManager.Write(dstGpuVa + (ulong)dstBaseOffset, srcSpan); return; } } unsafe bool Convert(Span dstSpan, ReadOnlySpan srcSpan) where T : unmanaged { - fixed (byte* dstPtr = dstSpan, srcPtr = srcSpan) + if (srcLinear && dstLinear && srcBpp == dstBpp) { - byte* dstBase = dstPtr - dstBaseOffset; // Layout offset is relative to the base, so we need to subtract the span's offset. - byte* srcBase = srcPtr - srcBaseOffset; - + // Optimized path for purely linear copies - we don't need to calculate every single byte offset, + // and we can make use of Span.CopyTo which is very very fast (even compared to pointers) for (int y = 0; y < yCount; y++) { srcCalculator.SetY(srcRegionY + y); dstCalculator.SetY(dstRegionY + y); + int srcOffset = srcCalculator.GetOffset(srcRegionX); + int dstOffset = dstCalculator.GetOffset(dstRegionX); + srcSpan.Slice(srcOffset - srcBaseOffset, xCount * srcBpp) + .CopyTo(dstSpan.Slice(dstOffset - dstBaseOffset, xCount * dstBpp)); + } + } + else + { + fixed (byte* dstPtr = dstSpan, srcPtr = srcSpan) + { + byte* dstBase = dstPtr - dstBaseOffset; // Layout offset is relative to the base, so we need to subtract the span's offset. + byte* srcBase = srcPtr - srcBaseOffset; - for (int x = 0; x < xCount; x++) + for (int y = 0; y < yCount; y++) { - int srcOffset = srcCalculator.GetOffset(srcRegionX + x); - int dstOffset = dstCalculator.GetOffset(dstRegionX + x); + srcCalculator.SetY(srcRegionY + y); + dstCalculator.SetY(dstRegionY + y); - *(T*)(dstBase + dstOffset) = *(T*)(srcBase + srcOffset); + for (int x = 0; x < xCount; x++) + { + int srcOffset = srcCalculator.GetOffset(srcRegionX + x); + int dstOffset = dstCalculator.GetOffset(dstRegionX + x); + + *(T*)(dstBase + dstOffset) = *(T*)(srcBase + srcOffset); + } } } } + return true; } + // OPT: This allocates a (potentially) huge temporary array and then copies an existing + // region of memory into it, data that might get overwritten entirely anyways. Ideally this should + // all be rewritten to use pooled arrays, but that gets complicated with packed data and strides + Span dstSpan = memoryManager.GetSpan(dstGpuVa + (ulong)dstBaseOffset, dstSize).ToArray(); + bool _ = srcBpp switch { 1 => Convert(dstSpan, srcSpan),