Skip to content

Commit

Permalink
Preload command speedup, Texture/buffer data flush, blit shader fix (#30
Browse files Browse the repository at this point in the history
)

* Move encoder state to be tied to command buffer, so preload and background cbs have their own encoder state

* Texture buffer/data flush, blit shader fix
  • Loading branch information
riperiperi authored and GreemDev committed Dec 24, 2024
1 parent 7d5b4c5 commit 879c93c
Show file tree
Hide file tree
Showing 13 changed files with 411 additions and 201 deletions.
1 change: 1 addition & 0 deletions src/Ryujinx.Graphics.Metal/BackgroundResources.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ public CommandBufferPool GetPool()
{
MTLCommandQueue queue = _renderer.BackgroundQueue;
_pool = new CommandBufferPool(queue);
_pool.Initialize(null); // TODO: Proper encoder factory for background render/compute
}

return _pool;
Expand Down
12 changes: 5 additions & 7 deletions src/Ryujinx.Graphics.Metal/BufferHolder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ public unsafe Span<byte> GetDataStorage(int offset, int size)
throw new InvalidOperationException("The buffer is not mapped.");
}

public unsafe void SetData(int offset, ReadOnlySpan<byte> data, CommandBufferScoped? cbs = null, Action endRenderPass = null, bool allowCbsWait = true)
public unsafe void SetData(int offset, ReadOnlySpan<byte> data, CommandBufferScoped? cbs = null, bool allowCbsWait = true)
{
int dataSize = Math.Min(data.Length, Size - offset);
if (dataSize == 0)
Expand Down Expand Up @@ -199,12 +199,11 @@ public unsafe void SetData(int offset, ReadOnlySpan<byte> data, CommandBufferSco
// This avoids ending and beginning render passes on each buffer data upload.

cbs = _pipeline.PreloadCbs;
endRenderPass = null;
}

if (allowCbsWait)
{
_renderer.BufferManager.StagingBuffer.PushData(_renderer.CommandBufferPool, cbs, endRenderPass, this, offset, data);
_renderer.BufferManager.StagingBuffer.PushData(_renderer.CommandBufferPool, cbs, this, offset, data);
}
else
{
Expand All @@ -214,7 +213,7 @@ public unsafe void SetData(int offset, ReadOnlySpan<byte> data, CommandBufferSco
cbs = _renderer.CommandBufferPool.Rent();
}

if (!_renderer.BufferManager.StagingBuffer.TryPushData(cbs.Value, endRenderPass, this, offset, data))
if (!_renderer.BufferManager.StagingBuffer.TryPushData(cbs.Value, this, offset, data))
{
// Need to do a slow upload.
BufferHolder srcHolder = _renderer.BufferManager.Create(dataSize);
Expand All @@ -223,7 +222,7 @@ public unsafe void SetData(int offset, ReadOnlySpan<byte> data, CommandBufferSco
var srcBuffer = srcHolder.GetBuffer();
var dstBuffer = this.GetBuffer(true);

Copy(_pipeline, cbs.Value, srcBuffer, dstBuffer, 0, offset, dataSize);
Copy(cbs.Value, srcBuffer, dstBuffer, 0, offset, dataSize);

srcHolder.Dispose();
}
Expand Down Expand Up @@ -255,7 +254,6 @@ public void SetDataUnchecked<T>(int offset, ReadOnlySpan<T> data) where T : unma
}

public static void Copy(
Pipeline pipeline,
CommandBufferScoped cbs,
Auto<DisposableBuffer> src,
Auto<DisposableBuffer> dst,
Expand All @@ -267,7 +265,7 @@ public static void Copy(
var srcBuffer = registerSrcUsage ? src.Get(cbs, srcOffset, size).Value : src.GetUnsafe().Value;
var dstbuffer = dst.Get(cbs, dstOffset, size, true).Value;

pipeline.GetOrCreateBlitEncoder().CopyFromBuffer(
cbs.Encoders.EnsureBlitEncoder().CopyFromBuffer(
srcBuffer,
(ulong)srcOffset,
dstbuffer,
Expand Down
6 changes: 3 additions & 3 deletions src/Ryujinx.Graphics.Metal/BufferManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -176,14 +176,14 @@ public PinnedSpan<byte> GetData(BufferHandle handle, int offset, int size)

public void SetData<T>(BufferHandle handle, int offset, ReadOnlySpan<T> data) where T : unmanaged
{
SetData(handle, offset, MemoryMarshal.Cast<T, byte>(data), null, null);
SetData(handle, offset, MemoryMarshal.Cast<T, byte>(data), null);
}

public void SetData(BufferHandle handle, int offset, ReadOnlySpan<byte> data, CommandBufferScoped? cbs, Action endRenderPass)
public void SetData(BufferHandle handle, int offset, ReadOnlySpan<byte> data, CommandBufferScoped? cbs)
{
if (TryGetBuffer(handle, out var holder))
{
holder.SetData(offset, data, cbs, endRenderPass);
holder.SetData(offset, data, cbs);
}
}

Expand Down
170 changes: 170 additions & 0 deletions src/Ryujinx.Graphics.Metal/CommandBufferEncoder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
using Ryujinx.Graphics.Metal;
using SharpMetal.Metal;
using System;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;

interface IEncoderFactory
{
MTLRenderCommandEncoder CreateRenderCommandEncoder();
MTLComputeCommandEncoder CreateComputeCommandEncoder();
}

/// <summary>
/// Tracks active encoder object for a command buffer.
/// </summary>
[SupportedOSPlatform("macos")]
class CommandBufferEncoder
{
public EncoderType CurrentEncoderType { get; private set; } = EncoderType.None;

public MTLBlitCommandEncoder BlitEncoder => new MTLBlitCommandEncoder(CurrentEncoder.Value);

public MTLComputeCommandEncoder ComputeEncoder => new MTLComputeCommandEncoder(CurrentEncoder.Value);

public MTLRenderCommandEncoder RenderEncoder => new MTLRenderCommandEncoder(CurrentEncoder.Value);

internal MTLCommandEncoder? CurrentEncoder { get; private set; }

private MTLCommandBuffer _commandBuffer;
private IEncoderFactory _encoderFactory;

public void Initialize(MTLCommandBuffer commandBuffer, IEncoderFactory encoderFactory)
{
_commandBuffer = commandBuffer;
_encoderFactory = encoderFactory;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public MTLRenderCommandEncoder EnsureRenderEncoder()
{
if (CurrentEncoderType != EncoderType.Render)
{
return BeginRenderPass();
}

return RenderEncoder;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public MTLBlitCommandEncoder EnsureBlitEncoder()
{
if (CurrentEncoderType != EncoderType.Blit)
{
return BeginBlitPass();
}

return BlitEncoder;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public MTLComputeCommandEncoder EnsureComputeEncoder()
{
if (CurrentEncoderType != EncoderType.Compute)
{
return BeginComputePass();
}

return ComputeEncoder;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryGetRenderEncoder(out MTLRenderCommandEncoder encoder)
{
if (CurrentEncoderType != EncoderType.Render)
{
encoder = default;
return false;
}

encoder = RenderEncoder;
return true;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryGetBlitEncoder(out MTLBlitCommandEncoder encoder)
{
if (CurrentEncoderType != EncoderType.Blit)
{
encoder = default;
return false;
}

encoder = BlitEncoder;
return true;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryGetComputeEncoder(out MTLComputeCommandEncoder encoder)
{
if (CurrentEncoderType != EncoderType.Compute)
{
encoder = default;
return false;
}

encoder = ComputeEncoder;
return true;
}

public void EndCurrentPass()
{
if (CurrentEncoder != null)
{
switch (CurrentEncoderType)
{
case EncoderType.Blit:
BlitEncoder.EndEncoding();
CurrentEncoder = null;
break;
case EncoderType.Compute:
ComputeEncoder.EndEncoding();
CurrentEncoder = null;
break;
case EncoderType.Render:
RenderEncoder.EndEncoding();
CurrentEncoder = null;
break;
default:
throw new InvalidOperationException();
}

CurrentEncoderType = EncoderType.None;
}
}

private MTLRenderCommandEncoder BeginRenderPass()
{
EndCurrentPass();

var renderCommandEncoder = _encoderFactory.CreateRenderCommandEncoder();

CurrentEncoder = renderCommandEncoder;
CurrentEncoderType = EncoderType.Render;

return renderCommandEncoder;
}

private MTLBlitCommandEncoder BeginBlitPass()
{
EndCurrentPass();

var descriptor = new MTLBlitPassDescriptor();
var blitCommandEncoder = _commandBuffer.BlitCommandEncoder(descriptor);

CurrentEncoder = blitCommandEncoder;
CurrentEncoderType = EncoderType.Blit;
return blitCommandEncoder;
}

private MTLComputeCommandEncoder BeginComputePass()
{
EndCurrentPass();

var computeCommandEncoder = _encoderFactory.CreateComputeCommandEncoder();

CurrentEncoder = computeCommandEncoder;
CurrentEncoderType = EncoderType.Compute;
return computeCommandEncoder;
}
}
30 changes: 25 additions & 5 deletions src/Ryujinx.Graphics.Metal/CommandBufferPool.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.Versioning;
using System.Threading;

namespace Ryujinx.Graphics.Metal
{
Expand All @@ -14,6 +15,10 @@ class CommandBufferPool : IDisposable
private readonly int _totalCommandBuffers;
private readonly int _totalCommandBuffersMask;
private readonly MTLCommandQueue _queue;
private readonly Thread _owner;
private IEncoderFactory _defaultEncoderFactory;

public bool OwnedByCurrentThread => _owner == Thread.CurrentThread;

[SupportedOSPlatform("macos")]
private struct ReservedCommandBuffer
Expand All @@ -22,22 +27,28 @@ private struct ReservedCommandBuffer
public bool InConsumption;
public int SubmissionCount;
public MTLCommandBuffer CommandBuffer;
public CommandBufferEncoder Encoders;
public FenceHolder Fence;

public List<IAuto> Dependants;
public List<MultiFenceHolder> Waitables;

public void Reinitialize(MTLCommandQueue queue)
public void Reinitialize(MTLCommandQueue queue, IEncoderFactory stateManager)
{
CommandBuffer = queue.CommandBuffer();

Encoders.Initialize(CommandBuffer, stateManager);
}

public void Initialize(MTLCommandQueue queue)
public void Initialize(MTLCommandQueue queue, IEncoderFactory stateManager)
{
CommandBuffer = queue.CommandBuffer();

Dependants = new List<IAuto>();
Waitables = new List<MultiFenceHolder>();
Encoders = new CommandBufferEncoder();

Encoders.Initialize(CommandBuffer, stateManager);
}
}

Expand All @@ -51,6 +62,7 @@ public void Initialize(MTLCommandQueue queue)
public CommandBufferPool(MTLCommandQueue queue)
{
_queue = queue;
_owner = Thread.CurrentThread;

_totalCommandBuffers = MaxCommandBuffers;
_totalCommandBuffersMask = _totalCommandBuffers - 1;
Expand All @@ -60,10 +72,15 @@ public CommandBufferPool(MTLCommandQueue queue)
_queuedIndexes = new int[_totalCommandBuffers];
_queuedIndexesPtr = 0;
_queuedCount = 0;
}

public void Initialize(IEncoderFactory encoderFactory)
{
_defaultEncoderFactory = encoderFactory;

for (int i = 0; i < _totalCommandBuffers; i++)
{
_commandBuffers[i].Initialize(_queue);
_commandBuffers[i].Initialize(_queue, _defaultEncoderFactory);
WaitAndDecrementRef(i);
}
}
Expand Down Expand Up @@ -194,7 +211,7 @@ public CommandBufferScoped Rent()

_inUseCount++;

return new CommandBufferScoped(this, entry.CommandBuffer, cursor);
return new CommandBufferScoped(this, entry.CommandBuffer, entry.Encoders, cursor);
}

cursor = (cursor + 1) & _totalCommandBuffersMask;
Expand All @@ -206,6 +223,9 @@ public CommandBufferScoped Rent()

public void Return(CommandBufferScoped cbs)
{
// Ensure the encoder is committed.
cbs.Encoders.EndCurrentPass();

lock (_commandBuffers)
{
int cbIndex = cbs.CommandBufferIndex;
Expand All @@ -223,7 +243,7 @@ public void Return(CommandBufferScoped cbs)
commandBuffer.Commit();

// Replace entry with new MTLCommandBuffer
entry.Reinitialize(_queue);
entry.Reinitialize(_queue, _defaultEncoderFactory);

int ptr = (_queuedIndexesPtr + _queuedCount) % _totalCommandBuffers;
_queuedIndexes[ptr] = cbIndex;
Expand Down
4 changes: 3 additions & 1 deletion src/Ryujinx.Graphics.Metal/CommandBufferScoped.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,14 @@ namespace Ryujinx.Graphics.Metal
{
private readonly CommandBufferPool _pool;
public MTLCommandBuffer CommandBuffer { get; }
public CommandBufferEncoder Encoders { get; }
public int CommandBufferIndex { get; }

public CommandBufferScoped(CommandBufferPool pool, MTLCommandBuffer commandBuffer, int commandBufferIndex)
public CommandBufferScoped(CommandBufferPool pool, MTLCommandBuffer commandBuffer, CommandBufferEncoder encoders, int commandBufferIndex)
{
_pool = pool;
CommandBuffer = commandBuffer;
Encoders = encoders;
CommandBufferIndex = commandBufferIndex;
}

Expand Down
Loading

0 comments on commit 879c93c

Please sign in to comment.