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 }