PersistentFlushBuffer + BackgroundResources

This commit is contained in:
Isaac Marovitz 2024-06-21 00:15:14 +01:00 committed by Isaac Marovitz
parent a638060dee
commit 30b50a99e4
3 changed files with 199 additions and 18 deletions

View file

@ -0,0 +1,110 @@
using SharpMetal.Metal;
using System;
using System.Collections.Generic;
using System.Runtime.Versioning;
using System.Threading;
namespace Ryujinx.Graphics.Metal
{
[SupportedOSPlatform("macos")]
class BackgroundResource : IDisposable
{
private readonly MetalRenderer _renderer;
private readonly Pipeline _pipeline;
private CommandBufferPool _pool;
private PersistentFlushBuffer _flushBuffer;
public BackgroundResource(MetalRenderer renderer, Pipeline pipeline)
{
_renderer = renderer;
_pipeline = pipeline;
}
public CommandBufferPool GetPool()
{
if (_pool == null)
{
MTLCommandQueue queue = _renderer.BackgroundQueue;
_pool = new CommandBufferPool(queue.Device, queue);
}
return _pool;
}
public PersistentFlushBuffer GetFlushBuffer()
{
_flushBuffer ??= new PersistentFlushBuffer(_renderer, _pipeline);
return _flushBuffer;
}
public void Dispose()
{
_pool?.Dispose();
_flushBuffer?.Dispose();
}
}
[SupportedOSPlatform("macos")]
class BackgroundResources : IDisposable
{
private readonly MetalRenderer _renderer;
private readonly Pipeline _pipeline;
private readonly Dictionary<Thread, BackgroundResource> _resources;
public BackgroundResources(MetalRenderer renderer, Pipeline pipeline)
{
_renderer = renderer;
_pipeline = pipeline;
_resources = new Dictionary<Thread, BackgroundResource>();
}
private void Cleanup()
{
lock (_resources)
{
foreach (KeyValuePair<Thread, BackgroundResource> tuple in _resources)
{
if (!tuple.Key.IsAlive)
{
tuple.Value.Dispose();
_resources.Remove(tuple.Key);
}
}
}
}
public BackgroundResource Get()
{
Thread thread = Thread.CurrentThread;
lock (_resources)
{
if (!_resources.TryGetValue(thread, out BackgroundResource resource))
{
Cleanup();
resource = new BackgroundResource(_renderer, _pipeline);
_resources[thread] = resource;
}
return resource;
}
}
public void Dispose()
{
lock (_resources)
{
foreach (var resource in _resources.Values)
{
resource.Dispose();
}
}
}
}
}

View file

@ -17,20 +17,21 @@ namespace Ryujinx.Graphics.Metal
private readonly Func<CAMetalLayer> _getMetalLayer; private readonly Func<CAMetalLayer> _getMetalLayer;
private Pipeline _pipeline; private Pipeline _pipeline;
private HelperShader _helperShader;
private BufferManager _bufferManager;
private Window _window; private Window _window;
private CommandBufferPool _commandBufferPool;
public event EventHandler<ScreenCaptureImageInfo> ScreenCaptured; public event EventHandler<ScreenCaptureImageInfo> ScreenCaptured;
public bool PreferThreading => true; public bool PreferThreading => true;
public IPipeline Pipeline => _pipeline; public IPipeline Pipeline => _pipeline;
public IWindow Window => _window; public IWindow Window => _window;
public HelperShader HelperShader => _helperShader;
public BufferManager BufferManager => _bufferManager; internal MTLCommandQueue BackgroundQueue { get; private set; }
public CommandBufferPool CommandBufferPool => _commandBufferPool; internal HelperShader HelperShader { get; private set; }
public Action<Action> InterruptAction { get; private set; } internal BufferManager BufferManager { get; private set; }
public SyncManager SyncManager { get; private set; } internal CommandBufferPool CommandBufferPool { get; private set; }
internal BackgroundResources BackgroundResources { get; private set; }
internal Action<Action> InterruptAction { get; private set; }
internal SyncManager SyncManager { get; private set; }
public MetalRenderer(Func<CAMetalLayer> metalLayer) public MetalRenderer(Func<CAMetalLayer> metalLayer)
{ {
@ -42,6 +43,8 @@ namespace Ryujinx.Graphics.Metal
} }
_queue = _device.NewCommandQueue(CommandBufferPool.MaxCommandBuffers); _queue = _device.NewCommandQueue(CommandBufferPool.MaxCommandBuffers);
BackgroundQueue = _device.NewCommandQueue(CommandBufferPool.MaxCommandBuffers);
_getMetalLayer = metalLayer; _getMetalLayer = metalLayer;
} }
@ -51,14 +54,15 @@ namespace Ryujinx.Graphics.Metal
layer.Device = _device; layer.Device = _device;
layer.FramebufferOnly = false; layer.FramebufferOnly = false;
_commandBufferPool = new CommandBufferPool(_device, _queue); CommandBufferPool = new CommandBufferPool(_device, _queue);
_window = new Window(this, layer); _window = new Window(this, layer);
_pipeline = new Pipeline(_device, this, _queue); _pipeline = new Pipeline(_device, this, _queue);
_bufferManager = new BufferManager(_device, this, _pipeline); BufferManager = new BufferManager(_device, this, _pipeline);
_pipeline.InitEncoderStateManager(_bufferManager); _pipeline.InitEncoderStateManager(BufferManager);
_helperShader = new HelperShader(_device, this, _pipeline); BackgroundResources = new BackgroundResources(this, _pipeline);
HelperShader = new HelperShader(_device, this, _pipeline);
SyncManager = new SyncManager(this); SyncManager = new SyncManager(this);
} }
@ -69,12 +73,12 @@ namespace Ryujinx.Graphics.Metal
public BufferHandle CreateBuffer(int size, BufferAccess access) public BufferHandle CreateBuffer(int size, BufferAccess access)
{ {
return _bufferManager.CreateWithHandle(size); return BufferManager.CreateWithHandle(size);
} }
public BufferHandle CreateBuffer(IntPtr pointer, int size) public BufferHandle CreateBuffer(IntPtr pointer, int size)
{ {
return _bufferManager.Create(pointer, size); return BufferManager.Create(pointer, size);
} }
public BufferHandle CreateBufferSparse(ReadOnlySpan<BufferRange> storageBuffers) public BufferHandle CreateBufferSparse(ReadOnlySpan<BufferRange> storageBuffers)
@ -125,12 +129,12 @@ namespace Ryujinx.Graphics.Metal
public void DeleteBuffer(BufferHandle buffer) public void DeleteBuffer(BufferHandle buffer)
{ {
_bufferManager.Delete(buffer); BufferManager.Delete(buffer);
} }
public PinnedSpan<byte> GetBufferData(BufferHandle buffer, int offset, int size) public PinnedSpan<byte> GetBufferData(BufferHandle buffer, int offset, int size)
{ {
return _bufferManager.GetData(buffer, offset, size); return BufferManager.GetData(buffer, offset, size);
} }
public Capabilities GetCapabilities() public Capabilities GetCapabilities()
@ -218,7 +222,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.CurrentCommandBuffer, _pipeline.EndRenderPassDelegate); BufferManager.SetData(buffer, offset, data, _pipeline.CurrentCommandBuffer, _pipeline.EndRenderPassDelegate);
} }
public void UpdateCounters() public void UpdateCounters()
@ -259,7 +263,7 @@ namespace Ryujinx.Graphics.Metal
SyncManager.RegisterFlush(); SyncManager.RegisterFlush();
// Periodically free unused regions of the staging buffer to avoid doing it all at once. // Periodically free unused regions of the staging buffer to avoid doing it all at once.
_bufferManager.StagingBuffer.FreeCompleted(); BufferManager.StagingBuffer.FreeCompleted();
} }
public void SetInterruptAction(Action<Action> interruptAction) public void SetInterruptAction(Action<Action> interruptAction)
@ -274,6 +278,7 @@ namespace Ryujinx.Graphics.Metal
public void Dispose() public void Dispose()
{ {
BackgroundResources.Dispose();
_pipeline.Dispose(); _pipeline.Dispose();
_window.Dispose(); _window.Dispose();
} }

View file

@ -0,0 +1,66 @@
using System;
using System.Runtime.Versioning;
namespace Ryujinx.Graphics.Metal
{
[SupportedOSPlatform("macos")]
internal class PersistentFlushBuffer : IDisposable
{
private readonly MetalRenderer _renderer;
private readonly Pipeline _pipeline;
private BufferHolder _flushStorage;
public PersistentFlushBuffer(MetalRenderer renderer, Pipeline pipeline)
{
_renderer = renderer;
_pipeline = pipeline;
}
private BufferHolder ResizeIfNeeded(int size)
{
var flushStorage = _flushStorage;
if (flushStorage == null || size > _flushStorage.Size)
{
flushStorage?.Dispose();
flushStorage = _renderer.BufferManager.Create(size);
_flushStorage = flushStorage;
}
return flushStorage;
}
public Span<byte> GetBufferData(CommandBufferPool cbp, BufferHolder buffer, int offset, int size)
{
var flushStorage = ResizeIfNeeded(size);
Auto<DisposableBuffer> srcBuffer;
using (var cbs = cbp.Rent())
{
srcBuffer = buffer.GetBuffer();
var dstBuffer = flushStorage.GetBuffer();
if (srcBuffer.TryIncrementReferenceCount())
{
BufferHolder.Copy(_pipeline, cbs, srcBuffer, dstBuffer, offset, 0, size, registerSrcUsage: false);
}
else
{
// Source buffer is no longer alive, don't copy anything to flush storage.
srcBuffer = null;
}
}
flushStorage.WaitForFences();
srcBuffer?.DecrementReferenceCount();
return flushStorage.GetDataStorage(0, size);
}
public void Dispose()
{
_flushStorage.Dispose();
}
}
}