Preload command speedup, Texture/buffer data flush, blit shader fix (#30)
* 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
This commit is contained in:
parent
80f9a5d0da
commit
2511bf1e4c
13 changed files with 414 additions and 204 deletions
|
@ -27,6 +27,7 @@ namespace Ryujinx.Graphics.Metal
|
||||||
{
|
{
|
||||||
MTLCommandQueue queue = _renderer.BackgroundQueue;
|
MTLCommandQueue queue = _renderer.BackgroundQueue;
|
||||||
_pool = new CommandBufferPool(queue);
|
_pool = new CommandBufferPool(queue);
|
||||||
|
_pool.Initialize(null); // TODO: Proper encoder factory for background render/compute
|
||||||
}
|
}
|
||||||
|
|
||||||
return _pool;
|
return _pool;
|
||||||
|
|
|
@ -162,7 +162,7 @@ namespace Ryujinx.Graphics.Metal
|
||||||
throw new InvalidOperationException("The buffer is not mapped.");
|
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);
|
int dataSize = Math.Min(data.Length, Size - offset);
|
||||||
if (dataSize == 0)
|
if (dataSize == 0)
|
||||||
|
@ -199,12 +199,11 @@ namespace Ryujinx.Graphics.Metal
|
||||||
// This avoids ending and beginning render passes on each buffer data upload.
|
// This avoids ending and beginning render passes on each buffer data upload.
|
||||||
|
|
||||||
cbs = _pipeline.PreloadCbs;
|
cbs = _pipeline.PreloadCbs;
|
||||||
endRenderPass = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (allowCbsWait)
|
if (allowCbsWait)
|
||||||
{
|
{
|
||||||
_renderer.BufferManager.StagingBuffer.PushData(_renderer.CommandBufferPool, cbs, endRenderPass, this, offset, data);
|
_renderer.BufferManager.StagingBuffer.PushData(_renderer.CommandBufferPool, cbs, this, offset, data);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -214,7 +213,7 @@ namespace Ryujinx.Graphics.Metal
|
||||||
cbs = _renderer.CommandBufferPool.Rent();
|
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.
|
// Need to do a slow upload.
|
||||||
BufferHolder srcHolder = _renderer.BufferManager.Create(dataSize);
|
BufferHolder srcHolder = _renderer.BufferManager.Create(dataSize);
|
||||||
|
@ -223,7 +222,7 @@ namespace Ryujinx.Graphics.Metal
|
||||||
var srcBuffer = srcHolder.GetBuffer();
|
var srcBuffer = srcHolder.GetBuffer();
|
||||||
var dstBuffer = this.GetBuffer(true);
|
var dstBuffer = this.GetBuffer(true);
|
||||||
|
|
||||||
Copy(_pipeline, cbs.Value, srcBuffer, dstBuffer, 0, offset, dataSize);
|
Copy(cbs.Value, srcBuffer, dstBuffer, 0, offset, dataSize);
|
||||||
|
|
||||||
srcHolder.Dispose();
|
srcHolder.Dispose();
|
||||||
}
|
}
|
||||||
|
@ -255,7 +254,6 @@ namespace Ryujinx.Graphics.Metal
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Copy(
|
public static void Copy(
|
||||||
Pipeline pipeline,
|
|
||||||
CommandBufferScoped cbs,
|
CommandBufferScoped cbs,
|
||||||
Auto<DisposableBuffer> src,
|
Auto<DisposableBuffer> src,
|
||||||
Auto<DisposableBuffer> dst,
|
Auto<DisposableBuffer> dst,
|
||||||
|
@ -267,7 +265,7 @@ namespace Ryujinx.Graphics.Metal
|
||||||
var srcBuffer = registerSrcUsage ? src.Get(cbs, srcOffset, size).Value : src.GetUnsafe().Value;
|
var srcBuffer = registerSrcUsage ? src.Get(cbs, srcOffset, size).Value : src.GetUnsafe().Value;
|
||||||
var dstbuffer = dst.Get(cbs, dstOffset, size, true).Value;
|
var dstbuffer = dst.Get(cbs, dstOffset, size, true).Value;
|
||||||
|
|
||||||
pipeline.GetOrCreateBlitEncoder().CopyFromBuffer(
|
cbs.Encoders.EnsureBlitEncoder().CopyFromBuffer(
|
||||||
srcBuffer,
|
srcBuffer,
|
||||||
(ulong)srcOffset,
|
(ulong)srcOffset,
|
||||||
dstbuffer,
|
dstbuffer,
|
||||||
|
|
|
@ -176,14 +176,14 @@ namespace Ryujinx.Graphics.Metal
|
||||||
|
|
||||||
public void SetData<T>(BufferHandle handle, int offset, ReadOnlySpan<T> data) where T : unmanaged
|
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))
|
if (TryGetBuffer(handle, out var holder))
|
||||||
{
|
{
|
||||||
holder.SetData(offset, data, cbs, endRenderPass);
|
holder.SetData(offset, data, cbs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
170
src/Ryujinx.Graphics.Metal/CommandBufferEncoder.cs
Normal file
170
src/Ryujinx.Graphics.Metal/CommandBufferEncoder.cs
Normal file
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,6 +3,7 @@ using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Runtime.Versioning;
|
using System.Runtime.Versioning;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Metal
|
namespace Ryujinx.Graphics.Metal
|
||||||
{
|
{
|
||||||
|
@ -14,6 +15,10 @@ namespace Ryujinx.Graphics.Metal
|
||||||
private readonly int _totalCommandBuffers;
|
private readonly int _totalCommandBuffers;
|
||||||
private readonly int _totalCommandBuffersMask;
|
private readonly int _totalCommandBuffersMask;
|
||||||
private readonly MTLCommandQueue _queue;
|
private readonly MTLCommandQueue _queue;
|
||||||
|
private readonly Thread _owner;
|
||||||
|
private IEncoderFactory _defaultEncoderFactory;
|
||||||
|
|
||||||
|
public bool OwnedByCurrentThread => _owner == Thread.CurrentThread;
|
||||||
|
|
||||||
[SupportedOSPlatform("macos")]
|
[SupportedOSPlatform("macos")]
|
||||||
private struct ReservedCommandBuffer
|
private struct ReservedCommandBuffer
|
||||||
|
@ -22,22 +27,28 @@ namespace Ryujinx.Graphics.Metal
|
||||||
public bool InConsumption;
|
public bool InConsumption;
|
||||||
public int SubmissionCount;
|
public int SubmissionCount;
|
||||||
public MTLCommandBuffer CommandBuffer;
|
public MTLCommandBuffer CommandBuffer;
|
||||||
|
public CommandBufferEncoder Encoders;
|
||||||
public FenceHolder Fence;
|
public FenceHolder Fence;
|
||||||
|
|
||||||
public List<IAuto> Dependants;
|
public List<IAuto> Dependants;
|
||||||
public List<MultiFenceHolder> Waitables;
|
public List<MultiFenceHolder> Waitables;
|
||||||
|
|
||||||
public void Reinitialize(MTLCommandQueue queue)
|
public void Reinitialize(MTLCommandQueue queue, IEncoderFactory stateManager)
|
||||||
{
|
{
|
||||||
CommandBuffer = queue.CommandBuffer();
|
CommandBuffer = queue.CommandBuffer();
|
||||||
|
|
||||||
|
Encoders.Initialize(CommandBuffer, stateManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Initialize(MTLCommandQueue queue)
|
public void Initialize(MTLCommandQueue queue, IEncoderFactory stateManager)
|
||||||
{
|
{
|
||||||
CommandBuffer = queue.CommandBuffer();
|
CommandBuffer = queue.CommandBuffer();
|
||||||
|
|
||||||
Dependants = new List<IAuto>();
|
Dependants = new List<IAuto>();
|
||||||
Waitables = new List<MultiFenceHolder>();
|
Waitables = new List<MultiFenceHolder>();
|
||||||
|
Encoders = new CommandBufferEncoder();
|
||||||
|
|
||||||
|
Encoders.Initialize(CommandBuffer, stateManager);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,6 +62,7 @@ namespace Ryujinx.Graphics.Metal
|
||||||
public CommandBufferPool(MTLCommandQueue queue)
|
public CommandBufferPool(MTLCommandQueue queue)
|
||||||
{
|
{
|
||||||
_queue = queue;
|
_queue = queue;
|
||||||
|
_owner = Thread.CurrentThread;
|
||||||
|
|
||||||
_totalCommandBuffers = MaxCommandBuffers;
|
_totalCommandBuffers = MaxCommandBuffers;
|
||||||
_totalCommandBuffersMask = _totalCommandBuffers - 1;
|
_totalCommandBuffersMask = _totalCommandBuffers - 1;
|
||||||
|
@ -60,10 +72,15 @@ namespace Ryujinx.Graphics.Metal
|
||||||
_queuedIndexes = new int[_totalCommandBuffers];
|
_queuedIndexes = new int[_totalCommandBuffers];
|
||||||
_queuedIndexesPtr = 0;
|
_queuedIndexesPtr = 0;
|
||||||
_queuedCount = 0;
|
_queuedCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Initialize(IEncoderFactory encoderFactory)
|
||||||
|
{
|
||||||
|
_defaultEncoderFactory = encoderFactory;
|
||||||
|
|
||||||
for (int i = 0; i < _totalCommandBuffers; i++)
|
for (int i = 0; i < _totalCommandBuffers; i++)
|
||||||
{
|
{
|
||||||
_commandBuffers[i].Initialize(_queue);
|
_commandBuffers[i].Initialize(_queue, _defaultEncoderFactory);
|
||||||
WaitAndDecrementRef(i);
|
WaitAndDecrementRef(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -194,7 +211,7 @@ namespace Ryujinx.Graphics.Metal
|
||||||
|
|
||||||
_inUseCount++;
|
_inUseCount++;
|
||||||
|
|
||||||
return new CommandBufferScoped(this, entry.CommandBuffer, cursor);
|
return new CommandBufferScoped(this, entry.CommandBuffer, entry.Encoders, cursor);
|
||||||
}
|
}
|
||||||
|
|
||||||
cursor = (cursor + 1) & _totalCommandBuffersMask;
|
cursor = (cursor + 1) & _totalCommandBuffersMask;
|
||||||
|
@ -206,6 +223,9 @@ namespace Ryujinx.Graphics.Metal
|
||||||
|
|
||||||
public void Return(CommandBufferScoped cbs)
|
public void Return(CommandBufferScoped cbs)
|
||||||
{
|
{
|
||||||
|
// Ensure the encoder is committed.
|
||||||
|
cbs.Encoders.EndCurrentPass();
|
||||||
|
|
||||||
lock (_commandBuffers)
|
lock (_commandBuffers)
|
||||||
{
|
{
|
||||||
int cbIndex = cbs.CommandBufferIndex;
|
int cbIndex = cbs.CommandBufferIndex;
|
||||||
|
@ -223,7 +243,7 @@ namespace Ryujinx.Graphics.Metal
|
||||||
commandBuffer.Commit();
|
commandBuffer.Commit();
|
||||||
|
|
||||||
// Replace entry with new MTLCommandBuffer
|
// Replace entry with new MTLCommandBuffer
|
||||||
entry.Reinitialize(_queue);
|
entry.Reinitialize(_queue, _defaultEncoderFactory);
|
||||||
|
|
||||||
int ptr = (_queuedIndexesPtr + _queuedCount) % _totalCommandBuffers;
|
int ptr = (_queuedIndexesPtr + _queuedCount) % _totalCommandBuffers;
|
||||||
_queuedIndexes[ptr] = cbIndex;
|
_queuedIndexes[ptr] = cbIndex;
|
||||||
|
|
|
@ -9,12 +9,14 @@ namespace Ryujinx.Graphics.Metal
|
||||||
{
|
{
|
||||||
private readonly CommandBufferPool _pool;
|
private readonly CommandBufferPool _pool;
|
||||||
public MTLCommandBuffer CommandBuffer { get; }
|
public MTLCommandBuffer CommandBuffer { get; }
|
||||||
|
public CommandBufferEncoder Encoders { get; }
|
||||||
public int CommandBufferIndex { 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;
|
_pool = pool;
|
||||||
CommandBuffer = commandBuffer;
|
CommandBuffer = commandBuffer;
|
||||||
|
Encoders = encoders;
|
||||||
CommandBufferIndex = commandBufferIndex;
|
CommandBufferIndex = commandBufferIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -581,9 +581,8 @@ namespace Ryujinx.Graphics.Metal
|
||||||
_currentState.DepthClipMode = clamp ? MTLDepthClipMode.Clamp : MTLDepthClipMode.Clip;
|
_currentState.DepthClipMode = clamp ? MTLDepthClipMode.Clamp : MTLDepthClipMode.Clip;
|
||||||
|
|
||||||
// Inline update
|
// Inline update
|
||||||
if (_pipeline.CurrentEncoderType == EncoderType.Render && _pipeline.CurrentEncoder != null)
|
if (_pipeline.Encoders.TryGetRenderEncoder(out MTLRenderCommandEncoder renderCommandEncoder))
|
||||||
{
|
{
|
||||||
var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value);
|
|
||||||
SetDepthClamp(renderCommandEncoder);
|
SetDepthClamp(renderCommandEncoder);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -600,9 +599,8 @@ namespace Ryujinx.Graphics.Metal
|
||||||
_currentState.Clamp = clamp;
|
_currentState.Clamp = clamp;
|
||||||
|
|
||||||
// Inline update
|
// Inline update
|
||||||
if (_pipeline.CurrentEncoderType == EncoderType.Render && _pipeline.CurrentEncoder != null)
|
if (_pipeline.Encoders.TryGetRenderEncoder(out MTLRenderCommandEncoder renderCommandEncoder))
|
||||||
{
|
{
|
||||||
var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value);
|
|
||||||
SetDepthBias(renderCommandEncoder);
|
SetDepthBias(renderCommandEncoder);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -632,9 +630,8 @@ namespace Ryujinx.Graphics.Metal
|
||||||
}
|
}
|
||||||
|
|
||||||
// Inline update
|
// Inline update
|
||||||
if (_pipeline.CurrentEncoderType == EncoderType.Render && _pipeline.CurrentEncoder != null)
|
if (_pipeline.Encoders.TryGetRenderEncoder(out MTLRenderCommandEncoder renderCommandEncoder))
|
||||||
{
|
{
|
||||||
var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value);
|
|
||||||
SetScissors(renderCommandEncoder);
|
SetScissors(renderCommandEncoder);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -669,9 +666,8 @@ namespace Ryujinx.Graphics.Metal
|
||||||
}
|
}
|
||||||
|
|
||||||
// Inline update
|
// Inline update
|
||||||
if (_pipeline.CurrentEncoderType == EncoderType.Render && _pipeline.CurrentEncoder != null)
|
if (_pipeline.Encoders.TryGetRenderEncoder(out MTLRenderCommandEncoder renderCommandEncoder))
|
||||||
{
|
{
|
||||||
var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value);
|
|
||||||
SetViewports(renderCommandEncoder);
|
SetViewports(renderCommandEncoder);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -688,9 +684,8 @@ namespace Ryujinx.Graphics.Metal
|
||||||
UpdatePipelineVertexState(_currentState.VertexBuffers, _currentState.VertexAttribs);
|
UpdatePipelineVertexState(_currentState.VertexBuffers, _currentState.VertexAttribs);
|
||||||
|
|
||||||
// Inline update
|
// Inline update
|
||||||
if (_pipeline.CurrentEncoderType == EncoderType.Render && _pipeline.CurrentEncoder != null)
|
if (_pipeline.Encoders.TryGetRenderEncoder(out MTLRenderCommandEncoder renderCommandEncoder))
|
||||||
{
|
{
|
||||||
var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value);
|
|
||||||
SetVertexBuffers(renderCommandEncoder, _currentState.VertexBuffers);
|
SetVertexBuffers(renderCommandEncoder, _currentState.VertexBuffers);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -755,9 +750,8 @@ namespace Ryujinx.Graphics.Metal
|
||||||
_currentState.CullBoth = face == Face.FrontAndBack;
|
_currentState.CullBoth = face == Face.FrontAndBack;
|
||||||
|
|
||||||
// Inline update
|
// Inline update
|
||||||
if (_pipeline.CurrentEncoderType == EncoderType.Render && _pipeline.CurrentEncoder != null)
|
if (_pipeline.Encoders.TryGetRenderEncoder(out MTLRenderCommandEncoder renderCommandEncoder))
|
||||||
{
|
{
|
||||||
var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value);
|
|
||||||
SetCullMode(renderCommandEncoder);
|
SetCullMode(renderCommandEncoder);
|
||||||
SetScissors(renderCommandEncoder);
|
SetScissors(renderCommandEncoder);
|
||||||
return;
|
return;
|
||||||
|
@ -778,9 +772,8 @@ namespace Ryujinx.Graphics.Metal
|
||||||
_currentState.Winding = frontFace.Convert();
|
_currentState.Winding = frontFace.Convert();
|
||||||
|
|
||||||
// Inline update
|
// Inline update
|
||||||
if (_pipeline.CurrentEncoderType == EncoderType.Render && _pipeline.CurrentEncoder != null)
|
if (_pipeline.Encoders.TryGetRenderEncoder(out MTLRenderCommandEncoder renderCommandEncoder))
|
||||||
{
|
{
|
||||||
var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value);
|
|
||||||
SetFrontFace(renderCommandEncoder);
|
SetFrontFace(renderCommandEncoder);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -795,9 +788,8 @@ namespace Ryujinx.Graphics.Metal
|
||||||
_currentState.BackRefValue = backRef;
|
_currentState.BackRefValue = backRef;
|
||||||
|
|
||||||
// Inline update
|
// Inline update
|
||||||
if (_pipeline.CurrentEncoderType == EncoderType.Render && _pipeline.CurrentEncoder != null)
|
if (_pipeline.Encoders.TryGetRenderEncoder(out MTLRenderCommandEncoder renderCommandEncoder))
|
||||||
{
|
{
|
||||||
var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value);
|
|
||||||
SetStencilRefValue(renderCommandEncoder);
|
SetStencilRefValue(renderCommandEncoder);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -277,7 +277,7 @@ namespace Ryujinx.Graphics.Metal
|
||||||
DirtyFlags clearFlags = DirtyFlags.All & (~DirtyFlags.Scissors);
|
DirtyFlags clearFlags = DirtyFlags.All & (~DirtyFlags.Scissors);
|
||||||
|
|
||||||
// Save current state
|
// Save current state
|
||||||
EncoderState originalState = _pipeline.SwapState(_helperShaderState, clearFlags);
|
EncoderState originalState = _pipeline.SwapState(_helperShaderState, clearFlags, false);
|
||||||
|
|
||||||
// Inherit some state without fully recreating render pipeline.
|
// Inherit some state without fully recreating render pipeline.
|
||||||
RenderTargetCopy save = _helperShaderState.InheritForClear(originalState, false, index);
|
RenderTargetCopy save = _helperShaderState.InheritForClear(originalState, false, index);
|
||||||
|
@ -312,7 +312,7 @@ namespace Ryujinx.Graphics.Metal
|
||||||
_pipeline.Draw(4, 1, 0, 0);
|
_pipeline.Draw(4, 1, 0, 0);
|
||||||
|
|
||||||
// Restore previous state
|
// Restore previous state
|
||||||
_pipeline.SwapState(null, clearFlags);
|
_pipeline.SwapState(null, clearFlags, false);
|
||||||
|
|
||||||
_helperShaderState.Restore(save);
|
_helperShaderState.Restore(save);
|
||||||
}
|
}
|
||||||
|
@ -330,7 +330,7 @@ namespace Ryujinx.Graphics.Metal
|
||||||
var helperScissors = _helperShaderState.Scissors;
|
var helperScissors = _helperShaderState.Scissors;
|
||||||
|
|
||||||
// Save current state
|
// Save current state
|
||||||
EncoderState originalState = _pipeline.SwapState(_helperShaderState, clearFlags);
|
EncoderState originalState = _pipeline.SwapState(_helperShaderState, clearFlags, false);
|
||||||
|
|
||||||
// Inherit some state without fully recreating render pipeline.
|
// Inherit some state without fully recreating render pipeline.
|
||||||
RenderTargetCopy save = _helperShaderState.InheritForClear(originalState, true);
|
RenderTargetCopy save = _helperShaderState.InheritForClear(originalState, true);
|
||||||
|
@ -365,7 +365,7 @@ namespace Ryujinx.Graphics.Metal
|
||||||
_pipeline.SetStencilTest(CreateStencilTestDescriptor(false));
|
_pipeline.SetStencilTest(CreateStencilTestDescriptor(false));
|
||||||
|
|
||||||
// Restore previous state
|
// Restore previous state
|
||||||
_pipeline.SwapState(null, clearFlags);
|
_pipeline.SwapState(null, clearFlags, false);
|
||||||
|
|
||||||
_helperShaderState.Restore(save);
|
_helperShaderState.Restore(save);
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,7 +67,10 @@ namespace Ryujinx.Graphics.Metal
|
||||||
|
|
||||||
public void BackgroundContextAction(Action action, bool alwaysBackground = false)
|
public void BackgroundContextAction(Action action, bool alwaysBackground = false)
|
||||||
{
|
{
|
||||||
Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!");
|
// GetData methods should be thread safe, so we can call this directly.
|
||||||
|
// Texture copy (scaled) may also happen in here, so that should also be thread safe.
|
||||||
|
|
||||||
|
action();
|
||||||
}
|
}
|
||||||
|
|
||||||
public BufferHandle CreateBuffer(int size, BufferAccess access)
|
public BufferHandle CreateBuffer(int size, BufferAccess access)
|
||||||
|
@ -221,7 +224,7 @@ namespace Ryujinx.Graphics.Metal
|
||||||
|
|
||||||
public void SetBufferData(BufferHandle buffer, int offset, ReadOnlySpan<byte> data)
|
public void SetBufferData(BufferHandle buffer, int offset, ReadOnlySpan<byte> data)
|
||||||
{
|
{
|
||||||
BufferManager.SetData(buffer, offset, data, _pipeline.Cbs, _pipeline.EndRenderPassDelegate);
|
BufferManager.SetData(buffer, offset, data, _pipeline.Cbs);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UpdateCounters()
|
public void UpdateCounters()
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
using Ryujinx.Graphics.GAL;
|
||||||
using System;
|
using System;
|
||||||
using System.Runtime.Versioning;
|
using System.Runtime.Versioning;
|
||||||
|
|
||||||
|
@ -44,7 +45,7 @@ namespace Ryujinx.Graphics.Metal
|
||||||
|
|
||||||
if (srcBuffer.TryIncrementReferenceCount())
|
if (srcBuffer.TryIncrementReferenceCount())
|
||||||
{
|
{
|
||||||
BufferHolder.Copy(_pipeline, cbs, srcBuffer, dstBuffer, offset, 0, size, registerSrcUsage: false);
|
BufferHolder.Copy(cbs, srcBuffer, dstBuffer, offset, 0, size, registerSrcUsage: false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -58,6 +59,40 @@ namespace Ryujinx.Graphics.Metal
|
||||||
return flushStorage.GetDataStorage(0, size);
|
return flushStorage.GetDataStorage(0, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Span<byte> GetTextureData(CommandBufferPool cbp, Texture view, int size)
|
||||||
|
{
|
||||||
|
TextureCreateInfo info = view.Info;
|
||||||
|
|
||||||
|
var flushStorage = ResizeIfNeeded(size);
|
||||||
|
|
||||||
|
using (var cbs = cbp.Rent())
|
||||||
|
{
|
||||||
|
var buffer = flushStorage.GetBuffer().Get(cbs).Value;
|
||||||
|
var image = view.GetHandle();
|
||||||
|
|
||||||
|
view.CopyFromOrToBuffer(cbs, buffer, image, size, true, 0, 0, info.GetLayers(), info.Levels, singleSlice: false);
|
||||||
|
}
|
||||||
|
|
||||||
|
flushStorage.WaitForFences();
|
||||||
|
return flushStorage.GetDataStorage(0, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Span<byte> GetTextureData(CommandBufferPool cbp, Texture view, int size, int layer, int level)
|
||||||
|
{
|
||||||
|
var flushStorage = ResizeIfNeeded(size);
|
||||||
|
|
||||||
|
using (var cbs = cbp.Rent())
|
||||||
|
{
|
||||||
|
var buffer = flushStorage.GetBuffer().Get(cbs).Value;
|
||||||
|
var image = view.GetHandle();
|
||||||
|
|
||||||
|
view.CopyFromOrToBuffer(cbs, buffer, image, size, true, layer, level, 1, 1, singleSlice: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
flushStorage.WaitForFences();
|
||||||
|
return flushStorage.GetDataStorage(0, size);
|
||||||
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
_flushStorage.Dispose();
|
_flushStorage.Dispose();
|
||||||
|
|
|
@ -18,7 +18,7 @@ namespace Ryujinx.Graphics.Metal
|
||||||
}
|
}
|
||||||
|
|
||||||
[SupportedOSPlatform("macos")]
|
[SupportedOSPlatform("macos")]
|
||||||
class Pipeline : IPipeline, IDisposable
|
class Pipeline : IPipeline, IEncoderFactory, IDisposable
|
||||||
{
|
{
|
||||||
private const ulong MinByteWeightForFlush = 256 * 1024 * 1024; // MiB
|
private const ulong MinByteWeightForFlush = 256 * 1024 * 1024; // MiB
|
||||||
|
|
||||||
|
@ -27,7 +27,6 @@ namespace Ryujinx.Graphics.Metal
|
||||||
private EncoderStateManager _encoderStateManager;
|
private EncoderStateManager _encoderStateManager;
|
||||||
private ulong _byteWeight;
|
private ulong _byteWeight;
|
||||||
|
|
||||||
public readonly Action EndRenderPassDelegate;
|
|
||||||
public MTLCommandBuffer CommandBuffer;
|
public MTLCommandBuffer CommandBuffer;
|
||||||
|
|
||||||
public IndexBufferPattern QuadsToTrisPattern;
|
public IndexBufferPattern QuadsToTrisPattern;
|
||||||
|
@ -35,8 +34,8 @@ namespace Ryujinx.Graphics.Metal
|
||||||
|
|
||||||
internal CommandBufferScoped? PreloadCbs { get; private set; }
|
internal CommandBufferScoped? PreloadCbs { get; private set; }
|
||||||
internal CommandBufferScoped Cbs { get; private set; }
|
internal CommandBufferScoped Cbs { get; private set; }
|
||||||
internal MTLCommandEncoder? CurrentEncoder { get; private set; }
|
internal CommandBufferEncoder Encoders => Cbs.Encoders;
|
||||||
internal EncoderType CurrentEncoderType { get; private set; } = EncoderType.None;
|
internal EncoderType CurrentEncoderType => Encoders.CurrentEncoderType;
|
||||||
internal bool RenderPassActive { get; private set; }
|
internal bool RenderPassActive { get; private set; }
|
||||||
|
|
||||||
public Pipeline(MTLDevice device, MetalRenderer renderer)
|
public Pipeline(MTLDevice device, MetalRenderer renderer)
|
||||||
|
@ -44,7 +43,7 @@ namespace Ryujinx.Graphics.Metal
|
||||||
_device = device;
|
_device = device;
|
||||||
_renderer = renderer;
|
_renderer = renderer;
|
||||||
|
|
||||||
EndRenderPassDelegate = EndCurrentPass;
|
renderer.CommandBufferPool.Initialize(this);
|
||||||
|
|
||||||
CommandBuffer = (Cbs = _renderer.CommandBufferPool.Rent()).CommandBuffer;
|
CommandBuffer = (Cbs = _renderer.CommandBufferPool.Rent()).CommandBuffer;
|
||||||
}
|
}
|
||||||
|
@ -57,8 +56,13 @@ namespace Ryujinx.Graphics.Metal
|
||||||
TriFanToTrisPattern = new IndexBufferPattern(_renderer, 3, 3, 2, [int.MinValue, -1, 0], 1, true);
|
TriFanToTrisPattern = new IndexBufferPattern(_renderer, 3, 3, 2, [int.MinValue, -1, 0], 1, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public EncoderState SwapState(EncoderState state, DirtyFlags flags = DirtyFlags.All)
|
public EncoderState SwapState(EncoderState state, DirtyFlags flags = DirtyFlags.All, bool endRenderPass = true)
|
||||||
{
|
{
|
||||||
|
if (endRenderPass && CurrentEncoderType == EncoderType.Render)
|
||||||
|
{
|
||||||
|
EndCurrentPass();
|
||||||
|
}
|
||||||
|
|
||||||
return _encoderStateManager.SwapState(state, flags);
|
return _encoderStateManager.SwapState(state, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,15 +83,7 @@ namespace Ryujinx.Graphics.Metal
|
||||||
|
|
||||||
public MTLRenderCommandEncoder GetOrCreateRenderEncoder(bool forDraw = false)
|
public MTLRenderCommandEncoder GetOrCreateRenderEncoder(bool forDraw = false)
|
||||||
{
|
{
|
||||||
MTLRenderCommandEncoder renderCommandEncoder;
|
MTLRenderCommandEncoder renderCommandEncoder = Cbs.Encoders.EnsureRenderEncoder();
|
||||||
if (CurrentEncoder == null || CurrentEncoderType != EncoderType.Render)
|
|
||||||
{
|
|
||||||
renderCommandEncoder = BeginRenderPass();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
renderCommandEncoder = new MTLRenderCommandEncoder(CurrentEncoder.Value);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (forDraw)
|
if (forDraw)
|
||||||
{
|
{
|
||||||
|
@ -99,28 +95,12 @@ namespace Ryujinx.Graphics.Metal
|
||||||
|
|
||||||
public MTLBlitCommandEncoder GetOrCreateBlitEncoder()
|
public MTLBlitCommandEncoder GetOrCreateBlitEncoder()
|
||||||
{
|
{
|
||||||
if (CurrentEncoder != null)
|
return Cbs.Encoders.EnsureBlitEncoder();
|
||||||
{
|
|
||||||
if (CurrentEncoderType == EncoderType.Blit)
|
|
||||||
{
|
|
||||||
return new MTLBlitCommandEncoder(CurrentEncoder.Value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return BeginBlitPass();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public MTLComputeCommandEncoder GetOrCreateComputeEncoder(bool forDispatch = false)
|
public MTLComputeCommandEncoder GetOrCreateComputeEncoder(bool forDispatch = false)
|
||||||
{
|
{
|
||||||
MTLComputeCommandEncoder computeCommandEncoder;
|
MTLComputeCommandEncoder computeCommandEncoder = Cbs.Encoders.EnsureComputeEncoder();
|
||||||
if (CurrentEncoder == null || CurrentEncoderType != EncoderType.Compute)
|
|
||||||
{
|
|
||||||
computeCommandEncoder = BeginComputePass();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
computeCommandEncoder = new MTLComputeCommandEncoder(CurrentEncoder.Value);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (forDispatch)
|
if (forDispatch)
|
||||||
{
|
{
|
||||||
|
@ -132,65 +112,17 @@ namespace Ryujinx.Graphics.Metal
|
||||||
|
|
||||||
public void EndCurrentPass()
|
public void EndCurrentPass()
|
||||||
{
|
{
|
||||||
if (CurrentEncoder != null)
|
Cbs.Encoders.EndCurrentPass();
|
||||||
{
|
|
||||||
switch (CurrentEncoderType)
|
|
||||||
{
|
|
||||||
case EncoderType.Blit:
|
|
||||||
new MTLBlitCommandEncoder(CurrentEncoder.Value).EndEncoding();
|
|
||||||
CurrentEncoder = null;
|
|
||||||
break;
|
|
||||||
case EncoderType.Compute:
|
|
||||||
new MTLComputeCommandEncoder(CurrentEncoder.Value).EndEncoding();
|
|
||||||
CurrentEncoder = null;
|
|
||||||
break;
|
|
||||||
case EncoderType.Render:
|
|
||||||
new MTLRenderCommandEncoder(CurrentEncoder.Value).EndEncoding();
|
|
||||||
CurrentEncoder = null;
|
|
||||||
RenderPassActive = false;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new ArgumentOutOfRangeException();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CurrentEncoderType = EncoderType.None;
|
public MTLRenderCommandEncoder CreateRenderCommandEncoder()
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private MTLRenderCommandEncoder BeginRenderPass()
|
|
||||||
{
|
{
|
||||||
EndCurrentPass();
|
return _encoderStateManager.CreateRenderCommandEncoder();
|
||||||
|
|
||||||
var renderCommandEncoder = _encoderStateManager.CreateRenderCommandEncoder();
|
|
||||||
|
|
||||||
CurrentEncoder = renderCommandEncoder;
|
|
||||||
CurrentEncoderType = EncoderType.Render;
|
|
||||||
RenderPassActive = true;
|
|
||||||
|
|
||||||
return renderCommandEncoder;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private MTLBlitCommandEncoder BeginBlitPass()
|
public MTLComputeCommandEncoder CreateComputeCommandEncoder()
|
||||||
{
|
{
|
||||||
EndCurrentPass();
|
return _encoderStateManager.CreateComputeCommandEncoder();
|
||||||
|
|
||||||
var descriptor = new MTLBlitPassDescriptor();
|
|
||||||
var blitCommandEncoder = Cbs.CommandBuffer.BlitCommandEncoder(descriptor);
|
|
||||||
|
|
||||||
CurrentEncoder = blitCommandEncoder;
|
|
||||||
CurrentEncoderType = EncoderType.Blit;
|
|
||||||
return blitCommandEncoder;
|
|
||||||
}
|
|
||||||
|
|
||||||
private MTLComputeCommandEncoder BeginComputePass()
|
|
||||||
{
|
|
||||||
EndCurrentPass();
|
|
||||||
|
|
||||||
var computeCommandEncoder = _encoderStateManager.CreateComputeCommandEncoder();
|
|
||||||
|
|
||||||
CurrentEncoder = computeCommandEncoder;
|
|
||||||
CurrentEncoderType = EncoderType.Compute;
|
|
||||||
return computeCommandEncoder;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Present(CAMetalDrawable drawable, Texture src, Extents2D srcRegion, Extents2D dstRegion, bool isLinear)
|
public void Present(CAMetalDrawable drawable, Texture src, Extents2D srcRegion, Extents2D dstRegion, bool isLinear)
|
||||||
|
@ -279,19 +211,15 @@ namespace Ryujinx.Graphics.Metal
|
||||||
{
|
{
|
||||||
case EncoderType.Render:
|
case EncoderType.Render:
|
||||||
{
|
{
|
||||||
var renderCommandEncoder = GetOrCreateRenderEncoder();
|
|
||||||
|
|
||||||
var scope = MTLBarrierScope.Buffers | MTLBarrierScope.Textures | MTLBarrierScope.RenderTargets;
|
var scope = MTLBarrierScope.Buffers | MTLBarrierScope.Textures | MTLBarrierScope.RenderTargets;
|
||||||
MTLRenderStages stages = MTLRenderStages.RenderStageVertex | MTLRenderStages.RenderStageFragment;
|
MTLRenderStages stages = MTLRenderStages.RenderStageVertex | MTLRenderStages.RenderStageFragment;
|
||||||
renderCommandEncoder.MemoryBarrier(scope, stages, stages);
|
Encoders.RenderEncoder.MemoryBarrier(scope, stages, stages);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case EncoderType.Compute:
|
case EncoderType.Compute:
|
||||||
{
|
{
|
||||||
var computeCommandEncoder = GetOrCreateComputeEncoder();
|
|
||||||
|
|
||||||
var scope = MTLBarrierScope.Buffers | MTLBarrierScope.Textures | MTLBarrierScope.RenderTargets;;
|
var scope = MTLBarrierScope.Buffers | MTLBarrierScope.Textures | MTLBarrierScope.RenderTargets;;
|
||||||
computeCommandEncoder.MemoryBarrier(scope);
|
Encoders.ComputeEncoder.MemoryBarrier(scope);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -353,7 +281,7 @@ namespace Ryujinx.Graphics.Metal
|
||||||
var srcBuffer = _renderer.BufferManager.GetBuffer(src, srcOffset, size, false);
|
var srcBuffer = _renderer.BufferManager.GetBuffer(src, srcOffset, size, false);
|
||||||
var dstBuffer = _renderer.BufferManager.GetBuffer(dst, dstOffset, size, true);
|
var dstBuffer = _renderer.BufferManager.GetBuffer(dst, dstOffset, size, true);
|
||||||
|
|
||||||
BufferHolder.Copy(this, Cbs, srcBuffer, dstBuffer, srcOffset, dstOffset, size);
|
BufferHolder.Copy(Cbs, srcBuffer, dstBuffer, srcOffset, dstOffset, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DispatchCompute(int groupsX, int groupsY, int groupsZ)
|
public void DispatchCompute(int groupsX, int groupsY, int groupsZ)
|
||||||
|
@ -709,9 +637,7 @@ namespace Ryujinx.Graphics.Metal
|
||||||
{
|
{
|
||||||
if (CurrentEncoderType == EncoderType.Render)
|
if (CurrentEncoderType == EncoderType.Render)
|
||||||
{
|
{
|
||||||
var renderCommandEncoder = GetOrCreateRenderEncoder();
|
Encoders.RenderEncoder.MemoryBarrier(MTLBarrierScope.Textures, MTLRenderStages.RenderStageFragment, MTLRenderStages.RenderStageFragment);
|
||||||
|
|
||||||
renderCommandEncoder.MemoryBarrier(MTLBarrierScope.Textures, MTLRenderStages.RenderStageFragment, MTLRenderStages.RenderStageFragment);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -63,15 +63,13 @@ namespace Ryujinx.Graphics.Metal
|
||||||
_resourceAlignment = Constants.MinResourceAlignment;
|
_resourceAlignment = Constants.MinResourceAlignment;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void PushData(CommandBufferPool cbp, CommandBufferScoped? cbs, Action endRenderPass, BufferHolder dst, int dstOffset, ReadOnlySpan<byte> data)
|
public void PushData(CommandBufferPool cbp, CommandBufferScoped? cbs, BufferHolder dst, int dstOffset, ReadOnlySpan<byte> data)
|
||||||
{
|
{
|
||||||
bool isRender = cbs != null;
|
bool isRender = cbs != null;
|
||||||
CommandBufferScoped scoped = cbs ?? cbp.Rent();
|
CommandBufferScoped scoped = cbs ?? cbp.Rent();
|
||||||
|
|
||||||
// Must push all data to the buffer. If it can't fit, split it up.
|
// Must push all data to the buffer. If it can't fit, split it up.
|
||||||
|
|
||||||
endRenderPass?.Invoke();
|
|
||||||
|
|
||||||
while (data.Length > 0)
|
while (data.Length > 0)
|
||||||
{
|
{
|
||||||
if (_freeSize < data.Length)
|
if (_freeSize < data.Length)
|
||||||
|
@ -122,14 +120,14 @@ namespace Ryujinx.Graphics.Metal
|
||||||
_buffer.SetDataUnchecked(offset, data[..capacity]);
|
_buffer.SetDataUnchecked(offset, data[..capacity]);
|
||||||
_buffer.SetDataUnchecked(0, data[capacity..]);
|
_buffer.SetDataUnchecked(0, data[capacity..]);
|
||||||
|
|
||||||
BufferHolder.Copy(_pipeline, cbs, srcBuffer, dstBuffer, offset, dstOffset, capacity);
|
BufferHolder.Copy(cbs, srcBuffer, dstBuffer, offset, dstOffset, capacity);
|
||||||
BufferHolder.Copy(_pipeline, cbs, srcBuffer, dstBuffer, 0, dstOffset + capacity, data.Length - capacity);
|
BufferHolder.Copy(cbs, srcBuffer, dstBuffer, 0, dstOffset + capacity, data.Length - capacity);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_buffer.SetDataUnchecked(offset, data);
|
_buffer.SetDataUnchecked(offset, data);
|
||||||
|
|
||||||
BufferHolder.Copy(_pipeline, cbs, srcBuffer, dstBuffer, offset, dstOffset, data.Length);
|
BufferHolder.Copy(cbs, srcBuffer, dstBuffer, offset, dstOffset, data.Length);
|
||||||
}
|
}
|
||||||
|
|
||||||
_freeOffset = (offset + data.Length) & (BufferSize - 1);
|
_freeOffset = (offset + data.Length) & (BufferSize - 1);
|
||||||
|
@ -139,7 +137,7 @@ namespace Ryujinx.Graphics.Metal
|
||||||
_pendingCopies.Enqueue(new PendingCopy(cbs.GetFence(), data.Length));
|
_pendingCopies.Enqueue(new PendingCopy(cbs.GetFence(), data.Length));
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TryPushData(CommandBufferScoped cbs, Action endRenderPass, BufferHolder dst, int dstOffset, ReadOnlySpan<byte> data)
|
public bool TryPushData(CommandBufferScoped cbs, BufferHolder dst, int dstOffset, ReadOnlySpan<byte> data)
|
||||||
{
|
{
|
||||||
if (data.Length > BufferSize)
|
if (data.Length > BufferSize)
|
||||||
{
|
{
|
||||||
|
@ -156,8 +154,6 @@ namespace Ryujinx.Graphics.Metal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
endRenderPass?.Invoke();
|
|
||||||
|
|
||||||
PushDataImpl(cbs, dst, dstOffset, data);
|
PushDataImpl(cbs, dst, dstOffset, data);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Graphics.GAL;
|
||||||
using SharpMetal.Foundation;
|
using SharpMetal.Foundation;
|
||||||
using SharpMetal.Metal;
|
using SharpMetal.Metal;
|
||||||
|
@ -94,6 +95,13 @@ namespace Ryujinx.Graphics.Metal
|
||||||
|
|
||||||
public void CopyTo(ITexture destination, int firstLayer, int firstLevel)
|
public void CopyTo(ITexture destination, int firstLayer, int firstLevel)
|
||||||
{
|
{
|
||||||
|
if (!_renderer.CommandBufferPool.OwnedByCurrentThread)
|
||||||
|
{
|
||||||
|
Logger.Warning?.PrintMsg(LogClass.Gpu, "Metal doesn't currently support scaled blit on background thread.");
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var blitCommandEncoder = _pipeline.GetOrCreateBlitEncoder();
|
var blitCommandEncoder = _pipeline.GetOrCreateBlitEncoder();
|
||||||
|
|
||||||
if (destination is Texture destinationTexture)
|
if (destination is Texture destinationTexture)
|
||||||
|
@ -202,48 +210,92 @@ namespace Ryujinx.Graphics.Metal
|
||||||
return new Texture(_device, _renderer, _pipeline, info, _mtlTexture, firstLayer, firstLevel);
|
return new Texture(_device, _renderer, _pipeline, info, _mtlTexture, firstLayer, firstLevel);
|
||||||
}
|
}
|
||||||
|
|
||||||
public PinnedSpan<byte> GetData()
|
private int GetBufferDataLength(int size)
|
||||||
{
|
{
|
||||||
var blitCommandEncoder = _pipeline.GetOrCreateBlitEncoder();
|
// TODO: D32S8 conversion
|
||||||
|
|
||||||
ulong length = 0;
|
return size;
|
||||||
|
|
||||||
for (int level = 0; level < Info.Levels; level++)
|
|
||||||
{
|
|
||||||
length += (ulong)Info.GetMipSize(level);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe
|
private ReadOnlySpan<byte> GetDataFromBuffer(ReadOnlySpan<byte> storage, int size, Span<byte> output)
|
||||||
{
|
{
|
||||||
var mtlBuffer = _device.NewBuffer(length, MTLResourceOptions.ResourceStorageModeShared);
|
// TODO: D32S8 conversion
|
||||||
|
|
||||||
|
return storage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CopyFromOrToBuffer(
|
||||||
|
CommandBufferScoped cbs,
|
||||||
|
MTLBuffer buffer,
|
||||||
|
MTLTexture image,
|
||||||
|
int size,
|
||||||
|
bool to,
|
||||||
|
int dstLayer,
|
||||||
|
int dstLevel,
|
||||||
|
int dstLayers,
|
||||||
|
int dstLevels,
|
||||||
|
bool singleSlice,
|
||||||
|
int offset = 0,
|
||||||
|
int stride = 0)
|
||||||
|
{
|
||||||
|
MTLBlitCommandEncoder blitCommandEncoder = cbs.Encoders.EnsureBlitEncoder();
|
||||||
|
|
||||||
int width = Info.Width;
|
|
||||||
int height = Info.Height;
|
|
||||||
int depth = Info.Depth;
|
|
||||||
int levels = Info.GetLevelsClamped();
|
|
||||||
int layers = Info.GetLayers();
|
|
||||||
bool is3D = Info.Target == Target.Texture3D;
|
bool is3D = Info.Target == Target.Texture3D;
|
||||||
|
int width = Math.Max(1, Info.Width >> dstLevel);
|
||||||
|
int height = Math.Max(1, Info.Height >> dstLevel);
|
||||||
|
int depth = is3D && !singleSlice ? Math.Max(1, Info.Depth >> dstLevel) : 1;
|
||||||
|
int layers = dstLayers;
|
||||||
|
int levels = dstLevels;
|
||||||
|
|
||||||
int offset = 0;
|
for (int oLevel = 0; oLevel < levels; oLevel++)
|
||||||
|
|
||||||
for (int level = 0; level < levels; level++)
|
|
||||||
{
|
{
|
||||||
|
int level = oLevel + dstLevel;
|
||||||
int mipSize = Info.GetMipSize2D(level);
|
int mipSize = Info.GetMipSize2D(level);
|
||||||
int endOffset = offset + mipSize;
|
|
||||||
|
|
||||||
for (int layer = 0; layer < layers; layer++)
|
int mipSizeLevel = GetBufferDataLength(is3D && !singleSlice
|
||||||
|
? Info.GetMipSize(level)
|
||||||
|
: mipSize * dstLayers);
|
||||||
|
|
||||||
|
int endOffset = offset + mipSizeLevel;
|
||||||
|
|
||||||
|
if ((uint)endOffset > (uint)size)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int oLayer = 0; oLayer < layers; oLayer++)
|
||||||
|
{
|
||||||
|
int layer = !is3D ? dstLayer + oLayer : 0;
|
||||||
|
int z = is3D ? dstLayer + oLayer : 0;
|
||||||
|
|
||||||
|
if (to)
|
||||||
{
|
{
|
||||||
blitCommandEncoder.CopyFromTexture(
|
blitCommandEncoder.CopyFromTexture(
|
||||||
_mtlTexture,
|
image,
|
||||||
(ulong)layer,
|
(ulong)layer,
|
||||||
(ulong)level,
|
(ulong)level,
|
||||||
new MTLOrigin(),
|
new MTLOrigin { z = (ulong)z },
|
||||||
new MTLSize { width = (ulong)width, height = (ulong)height, depth = is3D ? (ulong)depth : 1 },
|
new MTLSize { width = (ulong)width, height = (ulong)height, depth = 1 },
|
||||||
mtlBuffer,
|
buffer,
|
||||||
(ulong)offset,
|
(ulong)offset,
|
||||||
(ulong)Info.GetMipStride(level),
|
(ulong)Info.GetMipStride(level),
|
||||||
(ulong)mipSize
|
(ulong)mipSize
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
blitCommandEncoder.CopyFromBuffer(
|
||||||
|
buffer,
|
||||||
|
(ulong)offset,
|
||||||
|
(ulong)Info.GetMipStride(level),
|
||||||
|
(ulong)mipSize,
|
||||||
|
new MTLSize { width = (ulong)width, height = (ulong)height, depth = 1 },
|
||||||
|
image,
|
||||||
|
(ulong)(layer + oLayer),
|
||||||
|
(ulong)level,
|
||||||
|
new MTLOrigin { z = (ulong)z }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
offset += mipSize;
|
offset += mipSize;
|
||||||
}
|
}
|
||||||
|
@ -251,49 +303,64 @@ namespace Ryujinx.Graphics.Metal
|
||||||
width = Math.Max(1, width >> 1);
|
width = Math.Max(1, width >> 1);
|
||||||
height = Math.Max(1, height >> 1);
|
height = Math.Max(1, height >> 1);
|
||||||
|
|
||||||
if (is3D)
|
if (Info.Target == Target.Texture3D)
|
||||||
{
|
{
|
||||||
depth = Math.Max(1, depth >> 1);
|
depth = Math.Max(1, depth >> 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: wait
|
|
||||||
|
|
||||||
return new PinnedSpan<byte>(mtlBuffer.Contents.ToPointer(), (int)length, () => mtlBuffer.Dispose());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ReadOnlySpan<byte> GetData(CommandBufferPool cbp, PersistentFlushBuffer flushBuffer)
|
||||||
|
{
|
||||||
|
int size = 0;
|
||||||
|
|
||||||
|
for (int level = 0; level < Info.Levels; level++)
|
||||||
|
{
|
||||||
|
size += Info.GetMipSize(level);
|
||||||
|
}
|
||||||
|
|
||||||
|
size = GetBufferDataLength(size);
|
||||||
|
|
||||||
|
Span<byte> result = flushBuffer.GetTextureData(cbp, this, size);
|
||||||
|
|
||||||
|
return GetDataFromBuffer(result, size, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ReadOnlySpan<byte> GetData(CommandBufferPool cbp, PersistentFlushBuffer flushBuffer, int layer, int level)
|
||||||
|
{
|
||||||
|
int size = GetBufferDataLength(Info.GetMipSize(level));
|
||||||
|
|
||||||
|
Span<byte> result = flushBuffer.GetTextureData(cbp, this, size, layer, level);
|
||||||
|
|
||||||
|
return GetDataFromBuffer(result, size, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PinnedSpan<byte> GetData()
|
||||||
|
{
|
||||||
|
BackgroundResource resources = _renderer.BackgroundResources.Get();
|
||||||
|
|
||||||
|
if (_renderer.CommandBufferPool.OwnedByCurrentThread)
|
||||||
|
{
|
||||||
|
_renderer.FlushAllCommands();
|
||||||
|
|
||||||
|
return PinnedSpan<byte>.UnsafeFromSpan(GetData(_renderer.CommandBufferPool, resources.GetFlushBuffer()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return PinnedSpan<byte>.UnsafeFromSpan(GetData(resources.GetPool(), resources.GetFlushBuffer()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public PinnedSpan<byte> GetData(int layer, int level)
|
public PinnedSpan<byte> GetData(int layer, int level)
|
||||||
{
|
{
|
||||||
var blitCommandEncoder = _pipeline.GetOrCreateBlitEncoder();
|
BackgroundResource resources = _renderer.BackgroundResources.Get();
|
||||||
|
|
||||||
ulong bytesPerRow = (ulong)Info.GetMipStride(level);
|
if (_renderer.CommandBufferPool.OwnedByCurrentThread)
|
||||||
ulong length = bytesPerRow * (ulong)Info.Height;
|
|
||||||
ulong bytesPerImage = 0;
|
|
||||||
if (_mtlTexture.TextureType == MTLTextureType.Type3D)
|
|
||||||
{
|
{
|
||||||
bytesPerImage = length;
|
_renderer.FlushAllCommands();
|
||||||
|
|
||||||
|
return PinnedSpan<byte>.UnsafeFromSpan(GetData(_renderer.CommandBufferPool, resources.GetFlushBuffer(), layer, level));
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe
|
return PinnedSpan<byte>.UnsafeFromSpan(GetData(resources.GetPool(), resources.GetFlushBuffer(), layer, level));
|
||||||
{
|
|
||||||
|
|
||||||
var mtlBuffer = _device.NewBuffer(length, MTLResourceOptions.ResourceStorageModeShared);
|
|
||||||
|
|
||||||
blitCommandEncoder.CopyFromTexture(
|
|
||||||
_mtlTexture,
|
|
||||||
(ulong)layer,
|
|
||||||
(ulong)level,
|
|
||||||
new MTLOrigin(),
|
|
||||||
new MTLSize { width = _mtlTexture.Width, height = _mtlTexture.Height, depth = _mtlTexture.Depth },
|
|
||||||
mtlBuffer,
|
|
||||||
0,
|
|
||||||
bytesPerRow,
|
|
||||||
bytesPerImage
|
|
||||||
);
|
|
||||||
|
|
||||||
return new PinnedSpan<byte>(mtlBuffer.Contents.ToPointer(), (int)length, () => mtlBuffer.Dispose());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetData(IMemoryOwner<byte> data)
|
public void SetData(IMemoryOwner<byte> data)
|
||||||
|
|
Loading…
Reference in a new issue