/ src / Ryujinx.Graphics.Vulkan / AutoFlushCounter.cs
AutoFlushCounter.cs
  1  using Ryujinx.Common.Logging;
  2  using System;
  3  using System.Diagnostics;
  4  using System.Linq;
  5  
  6  namespace Ryujinx.Graphics.Vulkan
  7  {
  8      internal class AutoFlushCounter
  9      {
 10          // How often to flush on framebuffer change.
 11          private readonly static long _framebufferFlushTimer = Stopwatch.Frequency / 1000; // (1ms)
 12  
 13          // How often to flush on draw when fast flush mode is enabled.
 14          private readonly static long _drawFlushTimer = Stopwatch.Frequency / 666; // (1.5ms)
 15  
 16          // Average wait time that triggers fast flush mode to be entered.
 17          private readonly static long _fastFlushEnterThreshold = Stopwatch.Frequency / 666; // (1.5ms)
 18  
 19          // Average wait time that triggers fast flush mode to be exited.
 20          private readonly static long _fastFlushExitThreshold = Stopwatch.Frequency / 10000; // (0.1ms)
 21  
 22          // Number of frames to average waiting times over.
 23          private const int SyncWaitAverageCount = 20;
 24  
 25          private const int MinDrawCountForFlush = 10;
 26          private const int MinConsecutiveQueryForFlush = 10;
 27          private const int InitialQueryCountForFlush = 32;
 28  
 29          private readonly VulkanRenderer _gd;
 30  
 31          private long _lastFlush;
 32          private ulong _lastDrawCount;
 33          private bool _hasPendingQuery;
 34          private int _consecutiveQueries;
 35          private int _queryCount;
 36  
 37          private readonly int[] _queryCountHistory = new int[3];
 38          private int _queryCountHistoryIndex;
 39          private int _remainingQueries;
 40  
 41          private readonly long[] _syncWaitHistory = new long[SyncWaitAverageCount];
 42          private int _syncWaitHistoryIndex;
 43  
 44          private bool _fastFlushMode;
 45  
 46          public AutoFlushCounter(VulkanRenderer gd)
 47          {
 48              _gd = gd;
 49          }
 50  
 51          public void RegisterFlush(ulong drawCount)
 52          {
 53              _lastFlush = Stopwatch.GetTimestamp();
 54              _lastDrawCount = drawCount;
 55  
 56              _hasPendingQuery = false;
 57              _consecutiveQueries = 0;
 58          }
 59  
 60          public bool RegisterPendingQuery()
 61          {
 62              _hasPendingQuery = true;
 63              _consecutiveQueries++;
 64              _remainingQueries--;
 65  
 66              _queryCountHistory[_queryCountHistoryIndex]++;
 67  
 68              // Interrupt render passes to flush queries, so that early results arrive sooner.
 69              if (++_queryCount == InitialQueryCountForFlush)
 70              {
 71                  return true;
 72              }
 73  
 74              return false;
 75          }
 76  
 77          public int GetRemainingQueries()
 78          {
 79              if (_remainingQueries <= 0)
 80              {
 81                  _remainingQueries = 16;
 82              }
 83  
 84              if (_queryCount < InitialQueryCountForFlush)
 85              {
 86                  return Math.Min(InitialQueryCountForFlush - _queryCount, _remainingQueries);
 87              }
 88  
 89              return _remainingQueries;
 90          }
 91  
 92          public bool ShouldFlushQuery()
 93          {
 94              return _hasPendingQuery;
 95          }
 96  
 97          public bool ShouldFlushDraw(ulong drawCount)
 98          {
 99              if (_fastFlushMode)
100              {
101                  long draws = (long)(drawCount - _lastDrawCount);
102  
103                  if (draws < MinDrawCountForFlush)
104                  {
105                      if (draws == 0)
106                      {
107                          _lastFlush = Stopwatch.GetTimestamp();
108                      }
109  
110                      return false;
111                  }
112  
113                  long flushTimeout = _drawFlushTimer;
114  
115                  long now = Stopwatch.GetTimestamp();
116  
117                  return now > _lastFlush + flushTimeout;
118              }
119  
120              return false;
121          }
122  
123          public bool ShouldFlushAttachmentChange(ulong drawCount)
124          {
125              _queryCount = 0;
126  
127              // Flush when there's an attachment change out of a large block of queries.
128              if (_consecutiveQueries > MinConsecutiveQueryForFlush)
129              {
130                  return true;
131              }
132  
133              _consecutiveQueries = 0;
134  
135              long draws = (long)(drawCount - _lastDrawCount);
136  
137              if (draws < MinDrawCountForFlush)
138              {
139                  if (draws == 0)
140                  {
141                      _lastFlush = Stopwatch.GetTimestamp();
142                  }
143  
144                  return false;
145              }
146  
147              long flushTimeout = _framebufferFlushTimer;
148  
149              long now = Stopwatch.GetTimestamp();
150  
151              return now > _lastFlush + flushTimeout;
152          }
153  
154          public void Present()
155          {
156              // Query flush prediction.
157  
158              _queryCountHistoryIndex = (_queryCountHistoryIndex + 1) % 3;
159  
160              _remainingQueries = _queryCountHistory.Max() + 10;
161  
162              _queryCountHistory[_queryCountHistoryIndex] = 0;
163  
164              // Fast flush mode toggle.
165  
166              _syncWaitHistory[_syncWaitHistoryIndex] = _gd.SyncManager.GetAndResetWaitTicks();
167  
168              _syncWaitHistoryIndex = (_syncWaitHistoryIndex + 1) % SyncWaitAverageCount;
169  
170              long averageWait = (long)_syncWaitHistory.Average();
171  
172              if (_fastFlushMode ? averageWait < _fastFlushExitThreshold : averageWait > _fastFlushEnterThreshold)
173              {
174                  _fastFlushMode = !_fastFlushMode;
175                  Logger.Debug?.PrintMsg(LogClass.Gpu, $"Switched fast flush mode: ({_fastFlushMode})");
176              }
177          }
178      }
179  }