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 }