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