CounterQueue.cs 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. using Ryujinx.Graphics.GAL;
  2. using Silk.NET.Vulkan;
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Threading;
  6. namespace Ryujinx.Graphics.Vulkan.Queries
  7. {
  8. class CounterQueue : IDisposable
  9. {
  10. private const int QueryPoolInitialSize = 100;
  11. private readonly VulkanRenderer _gd;
  12. private readonly Device _device;
  13. private readonly PipelineFull _pipeline;
  14. public CounterType Type { get; }
  15. public bool Disposed { get; private set; }
  16. private Queue<CounterQueueEvent> _events = new Queue<CounterQueueEvent>();
  17. private CounterQueueEvent _current;
  18. private ulong _accumulatedCounter;
  19. private int _waiterCount;
  20. private object _lock = new object();
  21. private Queue<BufferedQuery> _queryPool;
  22. private AutoResetEvent _queuedEvent = new AutoResetEvent(false);
  23. private AutoResetEvent _wakeSignal = new AutoResetEvent(false);
  24. private AutoResetEvent _eventConsumed = new AutoResetEvent(false);
  25. private Thread _consumerThread;
  26. internal CounterQueue(VulkanRenderer gd, Device device, PipelineFull pipeline, CounterType type)
  27. {
  28. _gd = gd;
  29. _device = device;
  30. _pipeline = pipeline;
  31. Type = type;
  32. _queryPool = new Queue<BufferedQuery>(QueryPoolInitialSize);
  33. for (int i = 0; i < QueryPoolInitialSize; i++)
  34. {
  35. // AMD Polaris GPUs on Windows seem to have issues reporting 64-bit query results.
  36. _queryPool.Enqueue(new BufferedQuery(_gd, _device, _pipeline, type, gd.IsAmdWindows));
  37. }
  38. _current = new CounterQueueEvent(this, type, 0);
  39. _consumerThread = new Thread(EventConsumer);
  40. _consumerThread.Start();
  41. }
  42. private void EventConsumer()
  43. {
  44. while (!Disposed)
  45. {
  46. CounterQueueEvent evt = null;
  47. lock (_lock)
  48. {
  49. if (_events.Count > 0)
  50. {
  51. evt = _events.Dequeue();
  52. }
  53. }
  54. if (evt == null)
  55. {
  56. _queuedEvent.WaitOne(); // No more events to go through, wait for more.
  57. }
  58. else
  59. {
  60. // Spin-wait rather than sleeping if there are any waiters, by passing null instead of the wake signal.
  61. evt.TryConsume(ref _accumulatedCounter, true, _waiterCount == 0 ? _wakeSignal : null);
  62. }
  63. if (_waiterCount > 0)
  64. {
  65. _eventConsumed.Set();
  66. }
  67. }
  68. }
  69. internal BufferedQuery GetQueryObject()
  70. {
  71. // Creating/disposing query objects on a context we're sharing with will cause issues.
  72. // So instead, make a lot of query objects on the main thread and reuse them.
  73. lock (_lock)
  74. {
  75. if (_queryPool.Count > 0)
  76. {
  77. BufferedQuery result = _queryPool.Dequeue();
  78. return result;
  79. }
  80. else
  81. {
  82. return new BufferedQuery(_gd, _device, _pipeline, Type, _gd.IsAmdWindows);
  83. }
  84. }
  85. }
  86. internal void ReturnQueryObject(BufferedQuery query)
  87. {
  88. lock (_lock)
  89. {
  90. _pipeline.ResetQuery(query);
  91. _queryPool.Enqueue(query);
  92. }
  93. }
  94. public CounterQueueEvent QueueReport(EventHandler<ulong> resultHandler, ulong lastDrawIndex, bool hostReserved)
  95. {
  96. CounterQueueEvent result;
  97. ulong draws = lastDrawIndex - _current.DrawIndex;
  98. lock (_lock)
  99. {
  100. // A query's result only matters if more than one draw was performed during it.
  101. // Otherwise, dummy it out and return 0 immediately.
  102. if (hostReserved)
  103. {
  104. // This counter event is guaranteed to be available for host conditional rendering.
  105. _current.ReserveForHostAccess();
  106. }
  107. _current.Complete(draws > 0 && Type != CounterType.TransformFeedbackPrimitivesWritten);
  108. _events.Enqueue(_current);
  109. _current.OnResult += resultHandler;
  110. result = _current;
  111. _current = new CounterQueueEvent(this, Type, lastDrawIndex);
  112. }
  113. _queuedEvent.Set();
  114. return result;
  115. }
  116. public void QueueReset(ulong lastDrawIndex)
  117. {
  118. ulong draws = lastDrawIndex - _current.DrawIndex;
  119. lock (_lock)
  120. {
  121. _current.Clear(draws != 0);
  122. }
  123. }
  124. public void Flush(bool blocking)
  125. {
  126. if (!blocking)
  127. {
  128. // Just wake the consumer thread - it will update the queries.
  129. _wakeSignal.Set();
  130. return;
  131. }
  132. lock (_lock)
  133. {
  134. // Tell the queue to process all events.
  135. while (_events.Count > 0)
  136. {
  137. CounterQueueEvent flush = _events.Peek();
  138. if (!flush.TryConsume(ref _accumulatedCounter, true))
  139. {
  140. return; // If not blocking, then return when we encounter an event that is not ready yet.
  141. }
  142. _events.Dequeue();
  143. }
  144. }
  145. }
  146. public void FlushTo(CounterQueueEvent evt)
  147. {
  148. // Flush the counter queue on the main thread.
  149. Interlocked.Increment(ref _waiterCount);
  150. _wakeSignal.Set();
  151. while (!evt.Disposed)
  152. {
  153. _eventConsumed.WaitOne(1);
  154. }
  155. Interlocked.Decrement(ref _waiterCount);
  156. }
  157. public void Dispose()
  158. {
  159. lock (_lock)
  160. {
  161. while (_events.Count > 0)
  162. {
  163. CounterQueueEvent evt = _events.Dequeue();
  164. evt.Dispose();
  165. }
  166. Disposed = true;
  167. }
  168. _queuedEvent.Set();
  169. _consumerThread.Join();
  170. _current?.Dispose();
  171. foreach (BufferedQuery query in _queryPool)
  172. {
  173. query.Dispose();
  174. }
  175. _queuedEvent.Dispose();
  176. _wakeSignal.Dispose();
  177. _eventConsumed.Dispose();
  178. }
  179. }
  180. }