diff --git a/Ryujinx.Graphics.Vulkan/AutoFlushCounter.cs b/Ryujinx.Graphics.Vulkan/AutoFlushCounter.cs index 953316a6c..4e2a9d6bc 100644 --- a/Ryujinx.Graphics.Vulkan/AutoFlushCounter.cs +++ b/Ryujinx.Graphics.Vulkan/AutoFlushCounter.cs @@ -1,4 +1,5 @@ -using System; +using Ryujinx.Common.Logging; +using System; using System.Diagnostics; using System.Linq; @@ -7,12 +8,26 @@ namespace Ryujinx.Graphics.Vulkan internal class AutoFlushCounter { // How often to flush on framebuffer change. - private readonly static long FramebufferFlushTimer = Stopwatch.Frequency / 1000; + private readonly static long FramebufferFlushTimer = Stopwatch.Frequency / 1000; // (1ms) + + // How often to flush on draw when fast flush mode is enabled. + private readonly static long DrawFlushTimer = Stopwatch.Frequency / 666; // (1.5ms) + + // Average wait time that triggers fast flush mode to be entered. + private readonly static long FastFlushEnterThreshold = Stopwatch.Frequency / 666; // (1.5ms) + + // Average wait time that triggers fast flush mode to be exited. + private readonly static long FastFlushExitThreshold = Stopwatch.Frequency / 10000; // (0.1ms) + + // Number of frames to average waiting times over. + private const int SyncWaitAverageCount = 20; private const int MinDrawCountForFlush = 10; private const int MinConsecutiveQueryForFlush = 10; private const int InitialQueryCountForFlush = 32; + private readonly VulkanRenderer _gd; + private long _lastFlush; private ulong _lastDrawCount; private bool _hasPendingQuery; @@ -23,6 +38,16 @@ namespace Ryujinx.Graphics.Vulkan private int _queryCountHistoryIndex; private int _remainingQueries; + private long[] _syncWaitHistory = new long[SyncWaitAverageCount]; + private int _syncWaitHistoryIndex; + + private bool _fastFlushMode; + + public AutoFlushCounter(VulkanRenderer gd) + { + _gd = gd; + } + public void RegisterFlush(ulong drawCount) { _lastFlush = Stopwatch.GetTimestamp(); @@ -69,6 +94,32 @@ namespace Ryujinx.Graphics.Vulkan return _hasPendingQuery; } + public bool ShouldFlushDraw(ulong drawCount) + { + if (_fastFlushMode) + { + long draws = (long)(drawCount - _lastDrawCount); + + if (draws < MinDrawCountForFlush) + { + if (draws == 0) + { + _lastFlush = Stopwatch.GetTimestamp(); + } + + return false; + } + + long flushTimeout = DrawFlushTimer; + + long now = Stopwatch.GetTimestamp(); + + return now > _lastFlush + flushTimeout; + } + + return false; + } + public bool ShouldFlushAttachmentChange(ulong drawCount) { _queryCount = 0; @@ -102,11 +153,27 @@ namespace Ryujinx.Graphics.Vulkan public void Present() { + // Query flush prediction. + _queryCountHistoryIndex = (_queryCountHistoryIndex + 1) % 3; _remainingQueries = _queryCountHistory.Max() + 10; _queryCountHistory[_queryCountHistoryIndex] = 0; + + // Fast flush mode toggle. + + _syncWaitHistory[_syncWaitHistoryIndex] = _gd.SyncManager.GetAndResetWaitTicks(); + + _syncWaitHistoryIndex = (_syncWaitHistoryIndex + 1) % SyncWaitAverageCount; + + long averageWait = (long)_syncWaitHistory.Average(); + + if (_fastFlushMode ? averageWait < FastFlushExitThreshold : averageWait > FastFlushEnterThreshold) + { + _fastFlushMode = !_fastFlushMode; + Logger.Debug?.PrintMsg(LogClass.Gpu, $"Switched fast flush mode: ({_fastFlushMode})"); + } } } } diff --git a/Ryujinx.Graphics.Vulkan/PipelineBase.cs b/Ryujinx.Graphics.Vulkan/PipelineBase.cs index 2bec52930..c54d7980c 100644 --- a/Ryujinx.Graphics.Vulkan/PipelineBase.cs +++ b/Ryujinx.Graphics.Vulkan/PipelineBase.cs @@ -86,7 +86,7 @@ namespace Ryujinx.Graphics.Vulkan Gd = gd; Device = device; - AutoFlush = new AutoFlushCounter(); + AutoFlush = new AutoFlushCounter(gd); var pipelineCacheCreateInfo = new PipelineCacheCreateInfo() { @@ -1562,6 +1562,11 @@ namespace Ryujinx.Graphics.Vulkan private void RecreatePipelineIfNeeded(PipelineBindPoint pbp) { + if (AutoFlush.ShouldFlushDraw(DrawCount)) + { + Gd.FlushAllCommands(); + } + DynamicState.ReplayIfDirty(Gd.Api, CommandBuffer); // Commit changes to the support buffer before drawing. diff --git a/Ryujinx.Graphics.Vulkan/SyncManager.cs b/Ryujinx.Graphics.Vulkan/SyncManager.cs index c046dc3c7..432d224f0 100644 --- a/Ryujinx.Graphics.Vulkan/SyncManager.cs +++ b/Ryujinx.Graphics.Vulkan/SyncManager.cs @@ -1,6 +1,7 @@ using Ryujinx.Common.Logging; using Silk.NET.Vulkan; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; namespace Ryujinx.Graphics.Vulkan @@ -26,6 +27,7 @@ namespace Ryujinx.Graphics.Vulkan private readonly Device _device; private List _handles; private ulong FlushId; + private long WaitTicks; public SyncManager(VulkanRenderer gd, Device device) { @@ -130,6 +132,8 @@ namespace Ryujinx.Graphics.Vulkan return; } + long beforeTicks = Stopwatch.GetTimestamp(); + if (result.NeedsFlush(FlushId)) { _gd.InterruptAction(() => @@ -142,12 +146,14 @@ namespace Ryujinx.Graphics.Vulkan } bool signaled = result.Signalled || result.Waitable.WaitForFences(_gd.Api, _device, 1000000000); + if (!signaled) { Logger.Error?.PrintMsg(LogClass.Gpu, $"VK Sync Object {result.ID} failed to signal within 1000ms. Continuing..."); } else { + WaitTicks += Stopwatch.GetTimestamp() - beforeTicks; result.Signalled = true; } } @@ -188,5 +194,13 @@ namespace Ryujinx.Graphics.Vulkan } } } + + public long GetAndResetWaitTicks() + { + long result = WaitTicks; + WaitTicks = 0; + + return result; + } } } diff --git a/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs b/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs index 193cdce31..92b453fb1 100644 --- a/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs +++ b/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs @@ -49,6 +49,7 @@ namespace Ryujinx.Graphics.Vulkan internal PipelineLayoutCache PipelineLayoutCache { get; private set; } internal BackgroundResources BackgroundResources { get; private set; } internal Action InterruptAction { get; private set; } + internal SyncManager SyncManager { get; private set; } internal BufferManager BufferManager { get; private set; } @@ -58,7 +59,6 @@ namespace Ryujinx.Graphics.Vulkan private VulkanDebugMessenger _debugMessenger; private Counters _counters; - private SyncManager _syncManager; private PipelineFull _pipeline; @@ -327,7 +327,7 @@ namespace Ryujinx.Graphics.Vulkan BufferManager = new BufferManager(this, _device); - _syncManager = new SyncManager(this, _device); + SyncManager = new SyncManager(this, _device); _pipeline = new PipelineFull(this, _device); _pipeline.Initialize(); @@ -436,7 +436,7 @@ namespace Ryujinx.Graphics.Vulkan internal void RegisterFlush() { - _syncManager.RegisterFlush(); + SyncManager.RegisterFlush(); } public PinnedSpan GetBufferData(BufferHandle buffer, int offset, int size) @@ -696,7 +696,7 @@ namespace Ryujinx.Graphics.Vulkan public void PreFrame() { - _syncManager.Cleanup(); + SyncManager.Cleanup(); } public ICounterEvent ReportCounter(CounterType type, EventHandler resultHandler, bool hostReserved) @@ -736,7 +736,7 @@ namespace Ryujinx.Graphics.Vulkan public void CreateSync(ulong id, bool strict) { - _syncManager.Create(id, strict); + SyncManager.Create(id, strict); } public IProgram LoadProgramBinary(byte[] programBinary, bool isFragment, ShaderInfo info) @@ -746,12 +746,12 @@ namespace Ryujinx.Graphics.Vulkan public void WaitSync(ulong id) { - _syncManager.Wait(id); + SyncManager.Wait(id); } public ulong GetCurrentSync() { - return _syncManager.GetCurrent(); + return SyncManager.GetCurrent(); } public void SetInterruptAction(Action interruptAction)