/ src / Ryujinx.Graphics.OpenGL / Queries / BufferedQuery.cs
BufferedQuery.cs
  1  using OpenTK.Graphics.OpenGL;
  2  using Ryujinx.Common.Logging;
  3  using System;
  4  using System.Runtime.InteropServices;
  5  using System.Threading;
  6  
  7  namespace Ryujinx.Graphics.OpenGL.Queries
  8  {
  9      class BufferedQuery : IDisposable
 10      {
 11          private const int MaxQueryRetries = 5000;
 12          private const long DefaultValue = -1;
 13          private const ulong HighMask = 0xFFFFFFFF00000000;
 14  
 15          public int Query { get; }
 16  
 17          private readonly int _buffer;
 18          private readonly IntPtr _bufferMap;
 19          private readonly QueryTarget _type;
 20  
 21          public BufferedQuery(QueryTarget type)
 22          {
 23              _buffer = GL.GenBuffer();
 24              Query = GL.GenQuery();
 25              _type = type;
 26  
 27              GL.BindBuffer(BufferTarget.QueryBuffer, _buffer);
 28  
 29              unsafe
 30              {
 31                  long defaultValue = DefaultValue;
 32                  GL.BufferStorage(BufferTarget.QueryBuffer, sizeof(long), (IntPtr)(&defaultValue), BufferStorageFlags.MapReadBit | BufferStorageFlags.MapWriteBit | BufferStorageFlags.MapPersistentBit);
 33              }
 34              _bufferMap = GL.MapBufferRange(BufferTarget.QueryBuffer, IntPtr.Zero, sizeof(long), BufferAccessMask.MapReadBit | BufferAccessMask.MapWriteBit | BufferAccessMask.MapPersistentBit);
 35          }
 36  
 37          public void Reset()
 38          {
 39              GL.EndQuery(_type);
 40              GL.BeginQuery(_type, Query);
 41          }
 42  
 43          public void Begin()
 44          {
 45              GL.BeginQuery(_type, Query);
 46          }
 47  
 48          public unsafe void End(bool withResult)
 49          {
 50              GL.EndQuery(_type);
 51  
 52              if (withResult)
 53              {
 54                  GL.BindBuffer(BufferTarget.QueryBuffer, _buffer);
 55  
 56                  Marshal.WriteInt64(_bufferMap, -1L);
 57                  GL.GetQueryObject(Query, GetQueryObjectParam.QueryResult, (long*)0);
 58                  GL.MemoryBarrier(MemoryBarrierFlags.QueryBufferBarrierBit | MemoryBarrierFlags.ClientMappedBufferBarrierBit);
 59              }
 60              else
 61              {
 62                  // Dummy result, just return 0.
 63                  Marshal.WriteInt64(_bufferMap, 0L);
 64              }
 65          }
 66  
 67          private static bool WaitingForValue(long data)
 68          {
 69              return data == DefaultValue ||
 70                  ((ulong)data & HighMask) == (unchecked((ulong)DefaultValue) & HighMask);
 71          }
 72  
 73          public bool TryGetResult(out long result)
 74          {
 75              result = Marshal.ReadInt64(_bufferMap);
 76  
 77              return !WaitingForValue(result);
 78          }
 79  
 80          public long AwaitResult(AutoResetEvent wakeSignal = null)
 81          {
 82              long data = DefaultValue;
 83  
 84              if (wakeSignal == null)
 85              {
 86                  while (WaitingForValue(data))
 87                  {
 88                      data = Marshal.ReadInt64(_bufferMap);
 89                  }
 90              }
 91              else
 92              {
 93                  int iterations = 0;
 94                  while (WaitingForValue(data) && iterations++ < MaxQueryRetries)
 95                  {
 96                      data = Marshal.ReadInt64(_bufferMap);
 97                      if (WaitingForValue(data))
 98                      {
 99                          wakeSignal.WaitOne(1);
100                      }
101                  }
102  
103                  if (iterations >= MaxQueryRetries)
104                  {
105                      Logger.Error?.Print(LogClass.Gpu, $"Error: Query result timed out. Took more than {MaxQueryRetries} tries.");
106                  }
107              }
108  
109              return data;
110          }
111  
112          public void Dispose()
113          {
114              GL.BindBuffer(BufferTarget.QueryBuffer, _buffer);
115              GL.UnmapBuffer(BufferTarget.QueryBuffer);
116              GL.DeleteBuffer(_buffer);
117              GL.DeleteQuery(Query);
118          }
119      }
120  }