From 7b9b23e5006386967b2d7cdc6855c51b3ced9d3a Mon Sep 17 00:00:00 2001 From: riperiperi Date: Wed, 3 Jul 2024 10:27:03 +0100 Subject: [PATCH] Fix a bunch of issues with texture copy and flush (#32) * Fix a bunch of issues with texture copy and flush * TextureCopy helper class, fix clear bug --- src/Ryujinx.Graphics.Metal/HelperShader.cs | 5 +- src/Ryujinx.Graphics.Metal/SyncManager.cs | 2 +- src/Ryujinx.Graphics.Metal/Texture.cs | 255 ++++++++++++--------- src/Ryujinx.Graphics.Metal/TextureBase.cs | 3 + src/Ryujinx.Graphics.Metal/TextureCopy.cs | 201 ++++++++++++++++ 5 files changed, 351 insertions(+), 115 deletions(-) create mode 100644 src/Ryujinx.Graphics.Metal/TextureCopy.cs diff --git a/src/Ryujinx.Graphics.Metal/HelperShader.cs b/src/Ryujinx.Graphics.Metal/HelperShader.cs index f2d1aabad..02c44365d 100644 --- a/src/Ryujinx.Graphics.Metal/HelperShader.cs +++ b/src/Ryujinx.Graphics.Metal/HelperShader.cs @@ -318,11 +318,14 @@ namespace Ryujinx.Graphics.Metal 0f, 1f); + Span componentMasks = stackalloc uint[index + 1]; + componentMasks[index] = componentMask; + _pipeline.SetProgram(_programsColorClear[index]); _pipeline.SetBlendState(index, new BlendDescriptor()); _pipeline.SetFaceCulling(false, Face.Front); _pipeline.SetDepthTest(new DepthTestDescriptor(false, false, CompareOp.Always)); - _pipeline.SetRenderTargetColorMasks([componentMask]); + _pipeline.SetRenderTargetColorMasks(componentMasks); _pipeline.SetViewports(viewports); _pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip); _pipeline.Draw(4, 1, 0, 0); diff --git a/src/Ryujinx.Graphics.Metal/SyncManager.cs b/src/Ryujinx.Graphics.Metal/SyncManager.cs index 1d37bacc4..ca49fe263 100644 --- a/src/Ryujinx.Graphics.Metal/SyncManager.cs +++ b/src/Ryujinx.Graphics.Metal/SyncManager.cs @@ -149,7 +149,7 @@ namespace Ryujinx.Graphics.Metal return; } - bool signaled = result.Signalled || result.Waitable.WaitForFences(false); + bool signaled = result.Signalled || result.Waitable.WaitForFences(true); if (!signaled) { diff --git a/src/Ryujinx.Graphics.Metal/Texture.cs b/src/Ryujinx.Graphics.Metal/Texture.cs index c26890d36..37813960e 100644 --- a/src/Ryujinx.Graphics.Metal/Texture.cs +++ b/src/Ryujinx.Graphics.Metal/Texture.cs @@ -57,7 +57,10 @@ namespace Ryujinx.Graphics.Metal var swizzle = GetSwizzle(info, pixelFormat); _mtlTexture = sourceTexture.NewTextureView(pixelFormat, textureType, levels, slices, swizzle); + MtlFormat = pixelFormat; + FirstLayer = firstLayer; + FirstLevel = firstLevel; } private MTLTextureSwizzleChannels GetSwizzle(TextureCreateInfo info, MTLPixelFormat pixelFormat) @@ -94,6 +97,109 @@ namespace Ryujinx.Graphics.Metal } public void CopyTo(ITexture destination, int firstLayer, int firstLevel) + { + CommandBufferScoped cbs = _pipeline.Cbs; + + TextureBase src = this; + TextureBase dst = (TextureBase)destination; + + var srcImage = GetHandle(); + var dstImage = dst.GetHandle(); + + if (!dst.Info.Target.IsMultisample() && Info.Target.IsMultisample()) + { + //int layers = Math.Min(Info.GetLayers(), dst.Info.GetLayers() - firstLayer); + + //_gd.HelperShader.CopyMSToNonMS(_gd, cbs, src, dst, 0, firstLayer, layers); + } + else if (dst.Info.Target.IsMultisample() && !Info.Target.IsMultisample()) + { + //int layers = Math.Min(Info.GetLayers(), dst.Info.GetLayers() - firstLayer); + + //_gd.HelperShader.CopyNonMSToMS(_gd, cbs, src, dst, 0, firstLayer, layers); + } + else if (dst.Info.BytesPerPixel != Info.BytesPerPixel) + { + //int layers = Math.Min(Info.GetLayers(), dst.Info.GetLayers() - firstLayer); + //int levels = Math.Min(Info.Levels, dst.Info.Levels - firstLevel); + + //_gd.HelperShader.CopyIncompatibleFormats(_gd, cbs, src, dst, 0, firstLayer, 0, firstLevel, layers, levels); + } + else if (src.Info.Format.IsDepthOrStencil() != dst.Info.Format.IsDepthOrStencil()) + { + int layers = Math.Min(Info.GetLayers(), dst.Info.GetLayers() - firstLayer); + int levels = Math.Min(Info.Levels, dst.Info.Levels - firstLevel); + + // TODO: depth copy? + //_gd.HelperShader.CopyColor(_gd, cbs, src, dst, 0, firstLayer, 0, FirstLevel, layers, levels); + } + else + { + TextureCopy.Copy( + cbs, + srcImage, + dstImage, + src.Info, + dst.Info, + 0,//src.FirstLayer, + 0,//dst.FirstLayer, + 0,//src.FirstLevel, + 0,//dst.FirstLevel, + 0, + firstLayer, + 0, + firstLevel); + } + } + + public void CopyTo(ITexture destination, int srcLayer, int dstLayer, int srcLevel, int dstLevel) + { + CommandBufferScoped cbs = _pipeline.Cbs; + + TextureBase src = this; + TextureBase dst = (TextureBase)destination; + + var srcImage = GetHandle(); + var dstImage = dst.GetHandle(); + + if (!dst.Info.Target.IsMultisample() && Info.Target.IsMultisample()) + { + //_gd.HelperShader.CopyMSToNonMS(_gd, cbs, src, dst, srcLayer, dstLayer, 1); + } + else if (dst.Info.Target.IsMultisample() && !Info.Target.IsMultisample()) + { + //_gd.HelperShader.CopyNonMSToMS(_gd, cbs, src, dst, srcLayer, dstLayer, 1); + } + else if (dst.Info.BytesPerPixel != Info.BytesPerPixel) + { + //_gd.HelperShader.CopyIncompatibleFormats(_gd, cbs, src, dst, srcLayer, dstLayer, srcLevel, dstLevel, 1, 1); + } + else if (src.Info.Format.IsDepthOrStencil() != dst.Info.Format.IsDepthOrStencil()) + { + //_gd.HelperShader.CopyColor(_gd, cbs, src, dst, srcLayer, dstLayer, srcLevel, dstLevel, 1, 1); + } + else + { + TextureCopy.Copy( + cbs, + srcImage, + dstImage, + src.Info, + dst.Info, + 0, //src.FirstLayer, + 0, //dst.FirstLayer, + 0, //src.FirstLevel, + 0, //dst.FirstLevel, + srcLayer, + dstLayer, + srcLevel, + dstLevel, + 1, + 1); + } + } + + public void CopyTo(ITexture destination, Extents2D srcRegion, Extents2D dstRegion, bool linearFilter) { if (!_renderer.CommandBufferPool.OwnedByCurrentThread) { @@ -102,107 +208,31 @@ namespace Ryujinx.Graphics.Metal return; } - var blitCommandEncoder = _pipeline.GetOrCreateBlitEncoder(); - - if (destination is Texture destinationTexture) - { - if (destinationTexture.Info.Target == Target.Texture3D) - { - blitCommandEncoder.CopyFromTexture( - _mtlTexture, - 0, - (ulong)firstLevel, - new MTLOrigin { x = 0, y = 0, z = (ulong)firstLayer }, - new MTLSize { width = (ulong)Math.Min(Info.Width, destinationTexture.Info.Width), height = (ulong)Math.Min(Info.Height, destinationTexture.Info.Height), depth = 1 }, - destinationTexture._mtlTexture, - 0, - (ulong)firstLevel, - new MTLOrigin { x = 0, y = 0, z = (ulong)firstLayer }); - } - else - { - blitCommandEncoder.CopyFromTexture( - _mtlTexture, - (ulong)firstLayer, - (ulong)firstLevel, - destinationTexture._mtlTexture, - (ulong)firstLayer, - (ulong)firstLevel, - _mtlTexture.ArrayLength, - _mtlTexture.MipmapLevelCount); - } - } - } - - public void CopyTo(ITexture destination, int srcLayer, int dstLayer, int srcLevel, int dstLevel) - { - var blitCommandEncoder = _pipeline.GetOrCreateBlitEncoder(); - - if (destination is Texture destinationTexture) - { - if (destinationTexture.Info.Target == Target.Texture3D) - { - blitCommandEncoder.CopyFromTexture( - _mtlTexture, - 0, - (ulong)srcLevel, - new MTLOrigin { x = 0, y = 0, z = (ulong)srcLayer }, - new MTLSize { width = (ulong)Math.Min(Info.Width, destinationTexture.Info.Width), height = (ulong)Math.Min(Info.Height, destinationTexture.Info.Height), depth = 1 }, - destinationTexture._mtlTexture, - 0, - (ulong)dstLevel, - new MTLOrigin { x = 0, y = 0, z = (ulong)dstLayer }); - } - else - { - blitCommandEncoder.CopyFromTexture( - _mtlTexture, - (ulong)srcLayer, - (ulong)srcLevel, - destinationTexture._mtlTexture, - (ulong)dstLayer, - (ulong)dstLevel, - _mtlTexture.ArrayLength, - _mtlTexture.MipmapLevelCount); - } - } - } - - public void CopyTo(ITexture destination, Extents2D srcRegion, Extents2D dstRegion, bool linearFilter) - { var dst = (Texture)destination; bool isDepthOrStencil = dst.Info.Format.IsDepthOrStencil(); + if (dst.Info.IsCompressed) { + Console.WriteLine("shit"); + } + _pipeline.Blit(this, destination, srcRegion, dstRegion, isDepthOrStencil, linearFilter); } public void CopyTo(BufferRange range, int layer, int level, int stride) { - var blitCommandEncoder = _pipeline.GetOrCreateBlitEncoder(); var cbs = _pipeline.Cbs; int outSize = Info.GetMipSize(level); + int hostSize = GetBufferDataLength(outSize); - ulong bytesPerRow = (ulong)Info.GetMipStride(level); - ulong bytesPerImage = 0; - if (_mtlTexture.TextureType == MTLTextureType.Type3D) - { - bytesPerImage = bytesPerRow * (ulong)Info.Height; - } + int offset = range.Offset; var autoBuffer = _renderer.BufferManager.GetBuffer(range.Handle, true); var mtlBuffer = autoBuffer.Get(cbs, range.Offset, outSize).Value; - blitCommandEncoder.CopyFromTexture( - _mtlTexture, - (ulong)layer, - (ulong)level, - new MTLOrigin(), - new MTLSize { width = _mtlTexture.Width, height = _mtlTexture.Height, depth = _mtlTexture.Depth }, - mtlBuffer, - (ulong)range.Offset, - bytesPerRow, - bytesPerImage); + // TODO: D32S8 conversion via temp copy holder + + CopyFromOrToBuffer(cbs, mtlBuffer, _mtlTexture, hostSize, true, layer, level, 1, 1, singleSlice: true, offset: offset, stride: stride); } public ITexture CreateView(TextureCreateInfo info, int firstLayer, int firstLevel) @@ -217,6 +247,13 @@ namespace Ryujinx.Graphics.Metal return size; } + private void CopyDataToBuffer(Span storage, ReadOnlySpan input) + { + // TODO: D32S8 conversion + + input.CopyTo(storage); + } + private ReadOnlySpan GetDataFromBuffer(ReadOnlySpan storage, int size, Span output) { // TODO: D32S8 conversion @@ -422,37 +459,29 @@ namespace Ryujinx.Graphics.Metal buffer.Dispose(); } + private void SetData(ReadOnlySpan data, int layer, int level, int layers, int levels, bool singleSlice) + { + int bufferDataLength = GetBufferDataLength(data.Length); + + using var bufferHolder = _renderer.BufferManager.Create(bufferDataLength); + + // TODO: loadInline logic + + var cbs = _pipeline.Cbs; + + CopyDataToBuffer(bufferHolder.GetDataStorage(0, bufferDataLength), data); + + var buffer = bufferHolder.GetBuffer().Get(cbs).Value; + var image = GetHandle(); + + CopyFromOrToBuffer(cbs, buffer, image, bufferDataLength, false, layer, level, layers, levels, singleSlice); + } + public void SetData(IMemoryOwner data, int layer, int level) { - var blitCommandEncoder = _pipeline.GetOrCreateBlitEncoder(); + SetData(data.Memory.Span, layer, level, 1, 1, singleSlice: true); - ulong bytesPerRow = (ulong)Info.GetMipStride(level); - ulong bytesPerImage = 0; - if (_mtlTexture.TextureType == MTLTextureType.Type3D) - { - bytesPerImage = bytesPerRow * (ulong)Info.Height; - } - - var dataSpan = data.Memory.Span; - - var buffer = _renderer.BufferManager.Create(dataSpan.Length); - buffer.SetDataUnchecked(0, dataSpan); - var mtlBuffer = buffer.GetBuffer(false).Get(_pipeline.Cbs).Value; - - blitCommandEncoder.CopyFromBuffer( - mtlBuffer, - 0, - bytesPerRow, - bytesPerImage, - new MTLSize { width = _mtlTexture.Width, height = _mtlTexture.Height, depth = _mtlTexture.Depth }, - _mtlTexture, - (ulong)layer, - (ulong)level, - new MTLOrigin() - ); - - // Cleanup - buffer.Dispose(); + data.Dispose(); } public void SetData(IMemoryOwner data, int layer, int level, Rectangle region) diff --git a/src/Ryujinx.Graphics.Metal/TextureBase.cs b/src/Ryujinx.Graphics.Metal/TextureBase.cs index fcd07a66a..80d9751a8 100644 --- a/src/Ryujinx.Graphics.Metal/TextureBase.cs +++ b/src/Ryujinx.Graphics.Metal/TextureBase.cs @@ -21,7 +21,10 @@ namespace Ryujinx.Graphics.Metal public int Width => Info.Width; public int Height => Info.Height; public int Depth => Info.Depth; + public MTLPixelFormat MtlFormat { get; protected set; } + public int FirstLayer { get; protected set; } + public int FirstLevel { get; protected set; } public TextureBase(MTLDevice device, MetalRenderer renderer, Pipeline pipeline, TextureCreateInfo info) { diff --git a/src/Ryujinx.Graphics.Metal/TextureCopy.cs b/src/Ryujinx.Graphics.Metal/TextureCopy.cs new file mode 100644 index 000000000..ac22734df --- /dev/null +++ b/src/Ryujinx.Graphics.Metal/TextureCopy.cs @@ -0,0 +1,201 @@ +using Ryujinx.Common; +using Ryujinx.Common.Logging; +using Ryujinx.Graphics.GAL; +using SharpMetal.Metal; +using System; +using System.Numerics; + +namespace Ryujinx.Graphics.Metal +{ + static class TextureCopy + { + public static void Copy( + CommandBufferScoped cbs, + MTLTexture srcImage, + MTLTexture dstImage, + TextureCreateInfo srcInfo, + TextureCreateInfo dstInfo, + int srcViewLayer, + int dstViewLayer, + int srcViewLevel, + int dstViewLevel, + int srcLayer, + int dstLayer, + int srcLevel, + int dstLevel) + { + int srcDepth = srcInfo.GetDepthOrLayers(); + int srcLevels = srcInfo.Levels; + + int dstDepth = dstInfo.GetDepthOrLayers(); + int dstLevels = dstInfo.Levels; + + if (dstInfo.Target == Target.Texture3D) + { + dstDepth = Math.Max(1, dstDepth >> dstLevel); + } + + int depth = Math.Min(srcDepth, dstDepth); + int levels = Math.Min(srcLevels, dstLevels); + + Copy( + cbs, + srcImage, + dstImage, + srcInfo, + dstInfo, + srcViewLayer, + dstViewLayer, + srcViewLevel, + dstViewLevel, + srcLayer, + dstLayer, + srcLevel, + dstLevel, + depth, + levels); + } + + public static void Copy( + CommandBufferScoped cbs, + MTLTexture srcImage, + MTLTexture dstImage, + TextureCreateInfo srcInfo, + TextureCreateInfo dstInfo, + int srcViewLayer, + int dstViewLayer, + int srcViewLevel, + int dstViewLevel, + int srcDepthOrLayer, + int dstDepthOrLayer, + int srcLevel, + int dstLevel, + int depthOrLayers, + int levels) + { + MTLBlitCommandEncoder blitCommandEncoder = cbs.Encoders.EnsureBlitEncoder(); + + int srcZ; + int srcLayer; + int srcDepth; + int srcLayers; + + if (srcInfo.Target == Target.Texture3D) + { + srcZ = srcDepthOrLayer; + srcLayer = 0; + srcDepth = depthOrLayers; + srcLayers = 1; + } + else + { + srcZ = 0; + srcLayer = srcDepthOrLayer; + srcDepth = 1; + srcLayers = depthOrLayers; + } + + int dstZ; + int dstLayer; + int dstLayers; + + if (dstInfo.Target == Target.Texture3D) + { + dstZ = dstDepthOrLayer; + dstLayer = 0; + dstLayers = 1; + } + else + { + dstZ = 0; + dstLayer = dstDepthOrLayer; + dstLayers = depthOrLayers; + } + + int srcWidth = srcInfo.Width; + int srcHeight = srcInfo.Height; + + int dstWidth = dstInfo.Width; + int dstHeight = dstInfo.Height; + + srcWidth = Math.Max(1, srcWidth >> srcLevel); + srcHeight = Math.Max(1, srcHeight >> srcLevel); + + dstWidth = Math.Max(1, dstWidth >> dstLevel); + dstHeight = Math.Max(1, dstHeight >> dstLevel); + + int blockWidth = 1; + int blockHeight = 1; + bool sizeInBlocks = false; + + // When copying from a compressed to a non-compressed format, + // the non-compressed texture will have the size of the texture + // in blocks (not in texels), so we must adjust that size to + // match the size in texels of the compressed texture. + if (!srcInfo.IsCompressed && dstInfo.IsCompressed) + { + srcWidth *= dstInfo.BlockWidth; + srcHeight *= dstInfo.BlockHeight; + blockWidth = dstInfo.BlockWidth; + blockHeight = dstInfo.BlockHeight; + + sizeInBlocks = true; + } + else if (srcInfo.IsCompressed && !dstInfo.IsCompressed) + { + dstWidth *= srcInfo.BlockWidth; + dstHeight *= srcInfo.BlockHeight; + blockWidth = srcInfo.BlockWidth; + blockHeight = srcInfo.BlockHeight; + } + + int width = Math.Min(srcWidth, dstWidth); + int height = Math.Min(srcHeight, dstHeight); + + for (int level = 0; level < levels; level++) + { + // Stop copy if we are already out of the levels range. + if (level >= srcInfo.Levels || dstLevel + level >= dstInfo.Levels) + { + break; + } + + int copyWidth = sizeInBlocks ? BitUtils.DivRoundUp(width, blockWidth) : width; + int copyHeight = sizeInBlocks ? BitUtils.DivRoundUp(height, blockHeight) : height; + + int layers = Math.Max(dstLayers - dstLayer, srcLayers); + + for (int layer = 0; layer < layers; layer++) + { + if (srcInfo.Samples > 1 && srcInfo.Samples != dstInfo.Samples) + { + // TODO + + Logger.Warning?.PrintMsg(LogClass.Gpu, "Unsupported mismatching sample count copy"); + } + else + { + blitCommandEncoder.CopyFromTexture( + srcImage, + (ulong)(srcViewLevel + srcLevel + level), + (ulong)(srcViewLayer + srcLayer + layer), + new MTLOrigin { z = (ulong)srcZ }, + new MTLSize { width = (ulong)copyWidth, height = (ulong)copyHeight, depth = (ulong)srcDepth }, + dstImage, + (ulong)(dstViewLevel + dstLevel + level), + (ulong)(dstViewLayer + dstLayer + layer), + new MTLOrigin { z = (ulong)dstZ }); + } + } + + width = Math.Max(1, width >> 1); + height = Math.Max(1, height >> 1); + + if (srcInfo.Target == Target.Texture3D) + { + srcDepth = Math.Max(1, srcDepth >> 1); + } + } + } + } +}