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
This commit is contained in:
riperiperi 2024-07-03 10:27:03 +01:00 committed by Isaac Marovitz
parent 0c1acb5107
commit 7b9b23e500
5 changed files with 351 additions and 115 deletions

View file

@ -318,11 +318,14 @@ namespace Ryujinx.Graphics.Metal
0f, 0f,
1f); 1f);
Span<uint> componentMasks = stackalloc uint[index + 1];
componentMasks[index] = componentMask;
_pipeline.SetProgram(_programsColorClear[index]); _pipeline.SetProgram(_programsColorClear[index]);
_pipeline.SetBlendState(index, new BlendDescriptor()); _pipeline.SetBlendState(index, new BlendDescriptor());
_pipeline.SetFaceCulling(false, Face.Front); _pipeline.SetFaceCulling(false, Face.Front);
_pipeline.SetDepthTest(new DepthTestDescriptor(false, false, CompareOp.Always)); _pipeline.SetDepthTest(new DepthTestDescriptor(false, false, CompareOp.Always));
_pipeline.SetRenderTargetColorMasks([componentMask]); _pipeline.SetRenderTargetColorMasks(componentMasks);
_pipeline.SetViewports(viewports); _pipeline.SetViewports(viewports);
_pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip); _pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip);
_pipeline.Draw(4, 1, 0, 0); _pipeline.Draw(4, 1, 0, 0);

View file

@ -149,7 +149,7 @@ namespace Ryujinx.Graphics.Metal
return; return;
} }
bool signaled = result.Signalled || result.Waitable.WaitForFences(false); bool signaled = result.Signalled || result.Waitable.WaitForFences(true);
if (!signaled) if (!signaled)
{ {

View file

@ -57,7 +57,10 @@ namespace Ryujinx.Graphics.Metal
var swizzle = GetSwizzle(info, pixelFormat); var swizzle = GetSwizzle(info, pixelFormat);
_mtlTexture = sourceTexture.NewTextureView(pixelFormat, textureType, levels, slices, swizzle); _mtlTexture = sourceTexture.NewTextureView(pixelFormat, textureType, levels, slices, swizzle);
MtlFormat = pixelFormat; MtlFormat = pixelFormat;
FirstLayer = firstLayer;
FirstLevel = firstLevel;
} }
private MTLTextureSwizzleChannels GetSwizzle(TextureCreateInfo info, MTLPixelFormat pixelFormat) private MTLTextureSwizzleChannels GetSwizzle(TextureCreateInfo info, MTLPixelFormat pixelFormat)
@ -94,6 +97,109 @@ namespace Ryujinx.Graphics.Metal
} }
public void CopyTo(ITexture destination, int firstLayer, int firstLevel) 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) if (!_renderer.CommandBufferPool.OwnedByCurrentThread)
{ {
@ -102,107 +208,31 @@ namespace Ryujinx.Graphics.Metal
return; 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; var dst = (Texture)destination;
bool isDepthOrStencil = dst.Info.Format.IsDepthOrStencil(); bool isDepthOrStencil = dst.Info.Format.IsDepthOrStencil();
if (dst.Info.IsCompressed) {
Console.WriteLine("shit");
}
_pipeline.Blit(this, destination, srcRegion, dstRegion, isDepthOrStencil, linearFilter); _pipeline.Blit(this, destination, srcRegion, dstRegion, isDepthOrStencil, linearFilter);
} }
public void CopyTo(BufferRange range, int layer, int level, int stride) public void CopyTo(BufferRange range, int layer, int level, int stride)
{ {
var blitCommandEncoder = _pipeline.GetOrCreateBlitEncoder();
var cbs = _pipeline.Cbs; var cbs = _pipeline.Cbs;
int outSize = Info.GetMipSize(level); int outSize = Info.GetMipSize(level);
int hostSize = GetBufferDataLength(outSize);
ulong bytesPerRow = (ulong)Info.GetMipStride(level); int offset = range.Offset;
ulong bytesPerImage = 0;
if (_mtlTexture.TextureType == MTLTextureType.Type3D)
{
bytesPerImage = bytesPerRow * (ulong)Info.Height;
}
var autoBuffer = _renderer.BufferManager.GetBuffer(range.Handle, true); var autoBuffer = _renderer.BufferManager.GetBuffer(range.Handle, true);
var mtlBuffer = autoBuffer.Get(cbs, range.Offset, outSize).Value; var mtlBuffer = autoBuffer.Get(cbs, range.Offset, outSize).Value;
blitCommandEncoder.CopyFromTexture( // TODO: D32S8 conversion via temp copy holder
_mtlTexture,
(ulong)layer, CopyFromOrToBuffer(cbs, mtlBuffer, _mtlTexture, hostSize, true, layer, level, 1, 1, singleSlice: true, offset: offset, stride: stride);
(ulong)level,
new MTLOrigin(),
new MTLSize { width = _mtlTexture.Width, height = _mtlTexture.Height, depth = _mtlTexture.Depth },
mtlBuffer,
(ulong)range.Offset,
bytesPerRow,
bytesPerImage);
} }
public ITexture CreateView(TextureCreateInfo info, int firstLayer, int firstLevel) public ITexture CreateView(TextureCreateInfo info, int firstLayer, int firstLevel)
@ -217,6 +247,13 @@ namespace Ryujinx.Graphics.Metal
return size; return size;
} }
private void CopyDataToBuffer(Span<byte> storage, ReadOnlySpan<byte> input)
{
// TODO: D32S8 conversion
input.CopyTo(storage);
}
private ReadOnlySpan<byte> GetDataFromBuffer(ReadOnlySpan<byte> storage, int size, Span<byte> output) private ReadOnlySpan<byte> GetDataFromBuffer(ReadOnlySpan<byte> storage, int size, Span<byte> output)
{ {
// TODO: D32S8 conversion // TODO: D32S8 conversion
@ -422,37 +459,29 @@ namespace Ryujinx.Graphics.Metal
buffer.Dispose(); buffer.Dispose();
} }
public void SetData(IMemoryOwner<byte> data, int layer, int level) private void SetData(ReadOnlySpan<byte> data, int layer, int level, int layers, int levels, bool singleSlice)
{ {
var blitCommandEncoder = _pipeline.GetOrCreateBlitEncoder(); int bufferDataLength = GetBufferDataLength(data.Length);
ulong bytesPerRow = (ulong)Info.GetMipStride(level); using var bufferHolder = _renderer.BufferManager.Create(bufferDataLength);
ulong bytesPerImage = 0;
if (_mtlTexture.TextureType == MTLTextureType.Type3D) // TODO: loadInline logic
{
bytesPerImage = bytesPerRow * (ulong)Info.Height; 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);
} }
var dataSpan = data.Memory.Span; public void SetData(IMemoryOwner<byte> data, int layer, int level)
{
SetData(data.Memory.Span, layer, level, 1, 1, singleSlice: true);
var buffer = _renderer.BufferManager.Create(dataSpan.Length); data.Dispose();
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();
} }
public void SetData(IMemoryOwner<byte> data, int layer, int level, Rectangle<int> region) public void SetData(IMemoryOwner<byte> data, int layer, int level, Rectangle<int> region)

View file

@ -21,7 +21,10 @@ namespace Ryujinx.Graphics.Metal
public int Width => Info.Width; public int Width => Info.Width;
public int Height => Info.Height; public int Height => Info.Height;
public int Depth => Info.Depth; public int Depth => Info.Depth;
public MTLPixelFormat MtlFormat { get; protected set; } 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) public TextureBase(MTLDevice device, MetalRenderer renderer, Pipeline pipeline, TextureCreateInfo info)
{ {

View file

@ -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);
}
}
}
}
}