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