CounterQueueEvent.cs
1 using OpenTK.Graphics.OpenGL; 2 using Ryujinx.Graphics.GAL; 3 using System; 4 using System.Threading; 5 6 namespace Ryujinx.Graphics.OpenGL.Queries 7 { 8 class CounterQueueEvent : ICounterEvent 9 { 10 public event EventHandler<ulong> OnResult; 11 12 public QueryTarget Type { get; } 13 public bool ClearCounter { get; private set; } 14 public int Query => _counter.Query; 15 16 public bool Disposed { get; private set; } 17 public bool Invalid { get; set; } 18 19 public ulong DrawIndex { get; } 20 21 private readonly CounterQueue _queue; 22 private readonly BufferedQuery _counter; 23 24 private bool _hostAccessReserved = false; 25 private int _refCount = 1; // Starts with a reference from the counter queue. 26 27 private readonly object _lock = new(); 28 private ulong _result = ulong.MaxValue; 29 private double _divisor = 1f; 30 31 public CounterQueueEvent(CounterQueue queue, QueryTarget type, ulong drawIndex) 32 { 33 _queue = queue; 34 35 _counter = queue.GetQueryObject(); 36 Type = type; 37 38 DrawIndex = drawIndex; 39 40 _counter.Begin(); 41 } 42 43 internal void Clear() 44 { 45 _counter.Reset(); 46 ClearCounter = true; 47 } 48 49 internal void Complete(bool withResult, double divisor) 50 { 51 _counter.End(withResult); 52 53 _divisor = divisor; 54 } 55 56 internal bool TryConsume(ref ulong result, bool block, AutoResetEvent wakeSignal = null) 57 { 58 lock (_lock) 59 { 60 if (Disposed) 61 { 62 return true; 63 } 64 65 if (ClearCounter || Type == QueryTarget.Timestamp) 66 { 67 result = 0; 68 } 69 70 long queryResult; 71 72 if (block) 73 { 74 queryResult = _counter.AwaitResult(wakeSignal); 75 } 76 else 77 { 78 if (!_counter.TryGetResult(out queryResult)) 79 { 80 return false; 81 } 82 } 83 84 result += _divisor == 1 ? (ulong)queryResult : (ulong)Math.Ceiling(queryResult / _divisor); 85 86 _result = result; 87 88 OnResult?.Invoke(this, result); 89 90 Dispose(); // Return the our resources to the pool. 91 92 return true; 93 } 94 } 95 96 public void Flush() 97 { 98 if (Disposed) 99 { 100 return; 101 } 102 103 // Tell the queue to process all events up to this one. 104 _queue.FlushTo(this); 105 } 106 107 public void DecrementRefCount() 108 { 109 if (Interlocked.Decrement(ref _refCount) == 0) 110 { 111 DisposeInternal(); 112 } 113 } 114 115 public bool ReserveForHostAccess() 116 { 117 if (_hostAccessReserved) 118 { 119 return true; 120 } 121 122 if (IsValueAvailable()) 123 { 124 return false; 125 } 126 127 if (Interlocked.Increment(ref _refCount) == 1) 128 { 129 Interlocked.Decrement(ref _refCount); 130 131 return false; 132 } 133 134 _hostAccessReserved = true; 135 136 return true; 137 } 138 139 public void ReleaseHostAccess() 140 { 141 _hostAccessReserved = false; 142 143 DecrementRefCount(); 144 } 145 146 private void DisposeInternal() 147 { 148 _queue.ReturnQueryObject(_counter); 149 } 150 151 private bool IsValueAvailable() 152 { 153 return _result != ulong.MaxValue || _counter.TryGetResult(out _); 154 } 155 156 public void Dispose() 157 { 158 Disposed = true; 159 160 DecrementRefCount(); 161 } 162 } 163 }