PersistentFlushBuffer + BackgroundResources
This commit is contained in:
parent
a638060dee
commit
30b50a99e4
3 changed files with 199 additions and 18 deletions
110
src/Ryujinx.Graphics.Metal/BackgroundResources.cs
Normal file
110
src/Ryujinx.Graphics.Metal/BackgroundResources.cs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
66
src/Ryujinx.Graphics.Metal/PersistentFlushBuffer.cs
Normal file
66
src/Ryujinx.Graphics.Metal/PersistentFlushBuffer.cs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue