AutoFlushCounter.cs 5.2 KB

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