/ src / Ryujinx.Graphics.OpenGL / Queries / CounterQueueEvent.cs
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  }