PreloadCbs + FlushCommandsIfWeightExceeding

This commit is contained in:
Isaac Marovitz 2024-06-21 00:54:04 +01:00 committed by Isaac Marovitz
parent b1928461bb
commit 03161d8048
2 changed files with 53 additions and 0 deletions

View file

@ -190,6 +190,18 @@ namespace Ryujinx.Graphics.Metal
} }
} }
if (cbs != null &&
_pipeline.RenderPassActive &&
!(_buffer.HasCommandBufferDependency(cbs.Value) &&
_waitable.IsBufferRangeInUse(cbs.Value.CommandBufferIndex, offset, dataSize)))
{
// If the buffer hasn't been used on the command buffer yet, try to preload the data.
// This avoids ending and beginning render passes on each buffer data upload.
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, endRenderPass, this, offset, data);
@ -331,6 +343,8 @@ namespace Ryujinx.Graphics.Metal
public void Dispose() public void Dispose()
{ {
_pipeline.FlushCommandsIfWeightExceeding(_buffer, (ulong)Size);
_buffer.Dispose(); _buffer.Dispose();
_cachedConvertedBuffers.Dispose(); _cachedConvertedBuffers.Dispose();

View file

@ -20,16 +20,21 @@ namespace Ryujinx.Graphics.Metal
[SupportedOSPlatform("macos")] [SupportedOSPlatform("macos")]
class Pipeline : IPipeline, IDisposable class Pipeline : IPipeline, IDisposable
{ {
private const ulong MinByteWeightForFlush = 256 * 1024 * 1024; // MiB
private readonly MTLDevice _device; private readonly MTLDevice _device;
private readonly MetalRenderer _renderer; private readonly MetalRenderer _renderer;
private EncoderStateManager _encoderStateManager; private EncoderStateManager _encoderStateManager;
private ulong _byteWeight;
public readonly Action EndRenderPassDelegate; public readonly Action EndRenderPassDelegate;
public MTLCommandBuffer CommandBuffer; public MTLCommandBuffer CommandBuffer;
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 MTLCommandEncoder? CurrentEncoder { get; private set; }
internal EncoderType CurrentEncoderType { get; private set; } = EncoderType.None; internal EncoderType CurrentEncoderType { get; private set; } = EncoderType.None;
internal bool RenderPassActive { get; private set; }
public Pipeline(MTLDevice device, MetalRenderer renderer) public Pipeline(MTLDevice device, MetalRenderer renderer)
{ {
@ -133,6 +138,7 @@ namespace Ryujinx.Graphics.Metal
case EncoderType.Render: case EncoderType.Render:
new MTLRenderCommandEncoder(CurrentEncoder.Value).EndEncoding(); new MTLRenderCommandEncoder(CurrentEncoder.Value).EndEncoding();
CurrentEncoder = null; CurrentEncoder = null;
RenderPassActive = false;
break; break;
default: default:
throw new ArgumentOutOfRangeException(); throw new ArgumentOutOfRangeException();
@ -150,6 +156,7 @@ namespace Ryujinx.Graphics.Metal
CurrentEncoder = renderCommandEncoder; CurrentEncoder = renderCommandEncoder;
CurrentEncoderType = EncoderType.Render; CurrentEncoderType = EncoderType.Render;
RenderPassActive = true;
return renderCommandEncoder; return renderCommandEncoder;
} }
@ -198,12 +205,44 @@ namespace Ryujinx.Graphics.Metal
dst.Dispose(); dst.Dispose();
} }
public void FlushCommandsIfWeightExceeding(IAuto disposedResource, ulong byteWeight)
{
bool usedByCurrentCb = disposedResource.HasCommandBufferDependency(Cbs);
if (PreloadCbs != null && !usedByCurrentCb)
{
usedByCurrentCb = disposedResource.HasCommandBufferDependency(PreloadCbs.Value);
}
if (usedByCurrentCb)
{
// Since we can only free memory after the command buffer that uses a given resource was executed,
// keeping the command buffer might cause a high amount of memory to be in use.
// To prevent that, we force submit command buffers if the memory usage by resources
// in use by the current command buffer is above a given limit, and those resources were disposed.
_byteWeight += byteWeight;
if (_byteWeight >= MinByteWeightForFlush)
{
FlushCommandsImpl();
}
}
}
public void FlushCommandsImpl() public void FlushCommandsImpl()
{ {
SaveState(); SaveState();
EndCurrentPass(); EndCurrentPass();
_byteWeight = 0;
if (PreloadCbs != null)
{
PreloadCbs.Value.Dispose();
PreloadCbs = null;
}
CommandBuffer = (Cbs = _renderer.CommandBufferPool.ReturnAndRent(Cbs)).CommandBuffer; CommandBuffer = (Cbs = _renderer.CommandBufferPool.ReturnAndRent(Cbs)).CommandBuffer;
_renderer.RegisterFlush(); _renderer.RegisterFlush();