diff options
Diffstat (limited to 'Ryujinx.Graphics.Gpu/Engine/MethodCopyBuffer.cs')
| -rw-r--r-- | Ryujinx.Graphics.Gpu/Engine/MethodCopyBuffer.cs | 139 |
1 files changed, 107 insertions, 32 deletions
diff --git a/Ryujinx.Graphics.Gpu/Engine/MethodCopyBuffer.cs b/Ryujinx.Graphics.Gpu/Engine/MethodCopyBuffer.cs index 277054ce..67047a27 100644 --- a/Ryujinx.Graphics.Gpu/Engine/MethodCopyBuffer.cs +++ b/Ryujinx.Graphics.Gpu/Engine/MethodCopyBuffer.cs @@ -1,3 +1,4 @@ +using Ryujinx.Common; using Ryujinx.Graphics.Gpu.State; using Ryujinx.Graphics.Texture; using System; @@ -7,6 +8,37 @@ namespace Ryujinx.Graphics.Gpu.Engine { partial class Methods { + private const int StrideAlignment = 32; + private const int GobAlignment = 64; + + /// <summary> + /// Determine if a buffer-to-texture region covers the entirety of a texture. + /// </summary> + /// <param name="cbp">Copy command parameters</param> + /// <param name="tex">Texture to compare</param> + /// <param name="linear">True if the texture is linear, false if block linear</param> + /// <param name="bpp">Texture bytes per pixel</param> + /// <param name="stride">Texture stride</param> + /// <returns></returns> + private bool IsTextureCopyComplete(CopyBufferParams cbp, CopyBufferTexture tex, bool linear, int bpp, int stride) + { + if (linear) + { + int alignWidth = StrideAlignment / bpp; + return tex.RegionX == 0 && + tex.RegionY == 0 && + stride / bpp == BitUtils.AlignUp(cbp.XCount, alignWidth); + } + else + { + int alignWidth = GobAlignment / bpp; + return tex.RegionX == 0 && + tex.RegionY == 0 && + tex.Width == BitUtils.AlignUp(cbp.XCount, alignWidth) && + tex.Height == cbp.YCount; + } + } + /// <summary> /// Performs a buffer to buffer, or buffer to texture copy. /// </summary> @@ -62,54 +94,97 @@ namespace Ryujinx.Graphics.Gpu.Engine (int srcBaseOffset, int srcSize) = srcCalculator.GetRectangleRange(src.RegionX, src.RegionY, cbp.XCount, cbp.YCount); (int dstBaseOffset, int dstSize) = dstCalculator.GetRectangleRange(dst.RegionX, dst.RegionY, cbp.XCount, cbp.YCount); - ReadOnlySpan<byte> srcSpan = _context.PhysicalMemory.GetSpan(srcBaseAddress + (ulong)srcBaseOffset, srcSize); + ReadOnlySpan<byte> srcSpan = _context.PhysicalMemory.GetSpan(srcBaseAddress + (ulong)srcBaseOffset, srcSize, true); Span<byte> dstSpan = _context.PhysicalMemory.GetSpan(dstBaseAddress + (ulong)dstBaseOffset, dstSize).ToArray(); - bool completeSource = src.RegionX == 0 && src.RegionY == 0 && src.Width == cbp.XCount && src.Height == cbp.YCount; - bool completeDest = dst.RegionX == 0 && dst.RegionY == 0 && dst.Width == cbp.XCount && dst.Height == cbp.YCount; + bool completeSource = IsTextureCopyComplete(cbp, src, srcLinear, srcBpp, cbp.SrcStride); + bool completeDest = IsTextureCopyComplete(cbp, dst, dstLinear, dstBpp, cbp.DstStride); - if (completeSource && completeDest && srcCalculator.LayoutMatches(dstCalculator)) + if (completeSource && completeDest) { - srcSpan.CopyTo(dstSpan); // No layout conversion has to be performed, just copy the data entirely. + Image.Texture target = TextureManager.FindTexture(dst, cbp, swizzle, dstLinear); + if (target != null) + { + ReadOnlySpan<byte> data; + if (srcLinear) + { + data = LayoutConverter.ConvertLinearStridedToLinear( + target.Info.Width, + target.Info.Height, + 1, + 1, + cbp.SrcStride, + target.Info.FormatInfo.BytesPerPixel, + srcSpan); + } + else + { + data = LayoutConverter.ConvertBlockLinearToLinear( + src.Width, + src.Height, + 1, + target.Info.Levels, + 1, + 1, + 1, + srcBpp, + src.MemoryLayout.UnpackGobBlocksInY(), + src.MemoryLayout.UnpackGobBlocksInZ(), + src.MemoryLayout.UnpackGobBlocksInX(), + new SizeInfo((int)target.Size), + srcSpan); + } + + 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. + + _context.PhysicalMemory.Write(dstBaseAddress + (ulong)dstBaseOffset, dstSpan); + + return; + } } - else + + unsafe bool Convert<T>(Span<byte> dstSpan, ReadOnlySpan<byte> srcSpan) where T : unmanaged { - unsafe bool Convert<T>(Span<byte> dstSpan, ReadOnlySpan<byte> srcSpan) where T : unmanaged + fixed (byte* dstPtr = dstSpan, srcPtr = srcSpan) { - 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 y = 0; y < cbp.YCount; y++) { - byte* dstBase = dstPtr - dstBaseOffset; // Layout offset is relative to the base, so we need to subtract the span's offset. - byte* srcBase = srcPtr - srcBaseOffset; + srcCalculator.SetY(src.RegionY + y); + dstCalculator.SetY(dst.RegionY + y); - for (int y = 0; y < cbp.YCount; y++) + for (int x = 0; x < cbp.XCount; x++) { - srcCalculator.SetY(src.RegionY + y); - dstCalculator.SetY(dst.RegionY + y); + int srcOffset = srcCalculator.GetOffset(src.RegionX + x); + int dstOffset = dstCalculator.GetOffset(dst.RegionX + x); - for (int x = 0; x < cbp.XCount; x++) - { - int srcOffset = srcCalculator.GetOffset(src.RegionX + x); - int dstOffset = dstCalculator.GetOffset(dst.RegionX + x); - - *(T*)(dstBase + dstOffset) = *(T*)(srcBase + srcOffset); - } + *(T*)(dstBase + dstOffset) = *(T*)(srcBase + srcOffset); } } - return true; } - - bool _ = srcBpp switch - { - 1 => Convert<byte>(dstSpan, srcSpan), - 2 => Convert<ushort>(dstSpan, srcSpan), - 4 => Convert<uint>(dstSpan, srcSpan), - 8 => Convert<ulong>(dstSpan, srcSpan), - 12 => Convert<Bpp12Pixel>(dstSpan, srcSpan), - 16 => Convert<Vector128<byte>>(dstSpan, srcSpan), - _ => throw new NotSupportedException($"Unable to copy ${srcBpp} bpp pixel format.") - }; + return true; } + bool _ = srcBpp switch + { + 1 => Convert<byte>(dstSpan, srcSpan), + 2 => Convert<ushort>(dstSpan, srcSpan), + 4 => Convert<uint>(dstSpan, srcSpan), + 8 => Convert<ulong>(dstSpan, srcSpan), + 12 => Convert<Bpp12Pixel>(dstSpan, srcSpan), + 16 => Convert<Vector128<byte>>(dstSpan, srcSpan), + _ => throw new NotSupportedException($"Unable to copy ${srcBpp} bpp pixel format.") + }; + _context.PhysicalMemory.Write(dstBaseAddress + (ulong)dstBaseOffset, dstSpan); } else |
