aboutsummaryrefslogtreecommitdiff
path: root/src/Ryujinx.Graphics.Vulkan/AutoFlushCounter.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/Ryujinx.Graphics.Vulkan/AutoFlushCounter.cs')
-rw-r--r--src/Ryujinx.Graphics.Vulkan/AutoFlushCounter.cs179
1 files changed, 179 insertions, 0 deletions
diff --git a/src/Ryujinx.Graphics.Vulkan/AutoFlushCounter.cs b/src/Ryujinx.Graphics.Vulkan/AutoFlushCounter.cs
new file mode 100644
index 00000000..4e2a9d6b
--- /dev/null
+++ b/src/Ryujinx.Graphics.Vulkan/AutoFlushCounter.cs
@@ -0,0 +1,179 @@
+using Ryujinx.Common.Logging;
+using System;
+using System.Diagnostics;
+using System.Linq;
+
+namespace Ryujinx.Graphics.Vulkan
+{
+ internal class AutoFlushCounter
+ {
+ // How often to flush on framebuffer change.
+ 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;
+ private int _consecutiveQueries;
+ private int _queryCount;
+
+ private int[] _queryCountHistory = new int[3];
+ 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();
+ _lastDrawCount = drawCount;
+
+ _hasPendingQuery = false;
+ _consecutiveQueries = 0;
+ }
+
+ public bool RegisterPendingQuery()
+ {
+ _hasPendingQuery = true;
+ _consecutiveQueries++;
+ _remainingQueries--;
+
+ _queryCountHistory[_queryCountHistoryIndex]++;
+
+ // Interrupt render passes to flush queries, so that early results arrive sooner.
+ if (++_queryCount == InitialQueryCountForFlush)
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ public int GetRemainingQueries()
+ {
+ if (_remainingQueries <= 0)
+ {
+ _remainingQueries = 16;
+ }
+
+ if (_queryCount < InitialQueryCountForFlush)
+ {
+ return Math.Min(InitialQueryCountForFlush - _queryCount, _remainingQueries);
+ }
+
+ return _remainingQueries;
+ }
+
+ public bool ShouldFlushQuery()
+ {
+ 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;
+
+ // Flush when there's an attachment change out of a large block of queries.
+ if (_consecutiveQueries > MinConsecutiveQueryForFlush)
+ {
+ return true;
+ }
+
+ _consecutiveQueries = 0;
+
+ long draws = (long)(drawCount - _lastDrawCount);
+
+ if (draws < MinDrawCountForFlush)
+ {
+ if (draws == 0)
+ {
+ _lastFlush = Stopwatch.GetTimestamp();
+ }
+
+ return false;
+ }
+
+ long flushTimeout = FramebufferFlushTimer;
+
+ long now = Stopwatch.GetTimestamp();
+
+ return now > _lastFlush + flushTimeout;
+ }
+
+ 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})");
+ }
+ }
+ }
+}