Skip to content

Commit

Permalink
Fix a bunch of issues with texture copy and flush (#32)
Browse files Browse the repository at this point in the history
* Fix a bunch of issues with texture copy and flush

* TextureCopy helper class, fix clear bug
  • Loading branch information
riperiperi authored and GreemDev committed Dec 24, 2024
1 parent 58527e0 commit 82b5f8e
Show file tree
Hide file tree
Showing 5 changed files with 337 additions and 101 deletions.
5 changes: 4 additions & 1 deletion src/Ryujinx.Graphics.Metal/HelperShader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -318,11 +318,14 @@ public unsafe void ClearColor(
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);
Expand Down
2 changes: 1 addition & 1 deletion src/Ryujinx.Graphics.Metal/SyncManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ public void Wait(ulong id)
return;
}

bool signaled = result.Signalled || result.Waitable.WaitForFences(false);
bool signaled = result.Signalled || result.Waitable.WaitForFences(true);

if (!signaled)
{
Expand Down
227 changes: 128 additions & 99 deletions src/Ryujinx.Graphics.Metal/Texture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,10 @@ public Texture(MTLDevice device, MetalRenderer renderer, Pipeline pipeline, Text
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)
Expand Down Expand Up @@ -95,114 +98,141 @@ private MTLTextureSwizzleChannels GetSwizzle(TextureCreateInfo info, MTLPixelFor

public void CopyTo(ITexture destination, int firstLayer, int firstLevel)
{
if (!_renderer.CommandBufferPool.OwnedByCurrentThread)
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())
{
Logger.Warning?.PrintMsg(LogClass.Gpu, "Metal doesn't currently support scaled blit on background thread.");
//int layers = Math.Min(Info.GetLayers(), dst.Info.GetLayers() - firstLayer);

return;
//_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);

var blitCommandEncoder = _pipeline.GetOrCreateBlitEncoder();
//_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);

if (destination is Texture destinationTexture)
// TODO: depth copy?
//_gd.HelperShader.CopyColor(_gd, cbs, src, dst, 0, firstLayer, 0, FirstLevel, layers, levels);
}
else
{
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);
}
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)
{
var blitCommandEncoder = _pipeline.GetOrCreateBlitEncoder();
CommandBufferScoped cbs = _pipeline.Cbs;

TextureBase src = this;
TextureBase dst = (TextureBase)destination;

var srcImage = GetHandle();
var dstImage = dst.GetHandle();

if (destination is Texture destinationTexture)
if (!dst.Info.Target.IsMultisample() && Info.Target.IsMultisample())
{
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);
}
//_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)
{
Logger.Warning?.PrintMsg(LogClass.Gpu, "Metal doesn't currently support scaled blit on background thread.");

return;
}

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)
Expand All @@ -217,6 +247,13 @@ private int GetBufferDataLength(int 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)
{
// TODO: D32S8 conversion
Expand Down Expand Up @@ -422,37 +459,29 @@ public void SetData(IMemoryOwner<byte> data)
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);
ulong bytesPerImage = 0;
if (_mtlTexture.TextureType == MTLTextureType.Type3D)
{
bytesPerImage = bytesPerRow * (ulong)Info.Height;
}
using var bufferHolder = _renderer.BufferManager.Create(bufferDataLength);

var dataSpan = data.Memory.Span;
// TODO: loadInline logic

var buffer = _renderer.BufferManager.Create(dataSpan.Length);
buffer.SetDataUnchecked(0, dataSpan);
var mtlBuffer = buffer.GetBuffer(false).Get(_pipeline.Cbs).Value;
var cbs = _pipeline.Cbs;

blitCommandEncoder.CopyFromBuffer(
mtlBuffer,
0,
bytesPerRow,
bytesPerImage,
new MTLSize { width = _mtlTexture.Width, height = _mtlTexture.Height, depth = _mtlTexture.Depth },
_mtlTexture,
(ulong)layer,
(ulong)level,
new MTLOrigin()
);
CopyDataToBuffer(bufferHolder.GetDataStorage(0, bufferDataLength), data);

// Cleanup
buffer.Dispose();
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)
{
SetData(data.Memory.Span, layer, level, 1, 1, singleSlice: true);

data.Dispose();
}

public void SetData(IMemoryOwner<byte> data, int layer, int level, Rectangle<int> region)
Expand Down
3 changes: 3 additions & 0 deletions src/Ryujinx.Graphics.Metal/TextureBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@ abstract class TextureBase : IDisposable
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)
{
Expand Down
Loading

0 comments on commit 82b5f8e

Please sign in to comment.