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,
1f);
Span<uint> 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);

View file

@ -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)
{

View file

@ -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<byte> storage, ReadOnlySpan<byte> input)
{
// TODO: D32S8 conversion
input.CopyTo(storage);
}
private ReadOnlySpan<byte> GetDataFromBuffer(ReadOnlySpan<byte> storage, int size, Span<byte> output)
{
// TODO: D32S8 conversion
@ -422,37 +459,29 @@ namespace Ryujinx.Graphics.Metal
buffer.Dispose();
}
private void SetData(ReadOnlySpan<byte> 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<byte> 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<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 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)
{

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