aboutsummaryrefslogtreecommitdiff
path: root/src/Ryujinx.Graphics.OpenGL/Queries/BufferedQuery.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/Ryujinx.Graphics.OpenGL/Queries/BufferedQuery.cs')
-rw-r--r--src/Ryujinx.Graphics.OpenGL/Queries/BufferedQuery.cs120
1 files changed, 120 insertions, 0 deletions
diff --git a/src/Ryujinx.Graphics.OpenGL/Queries/BufferedQuery.cs b/src/Ryujinx.Graphics.OpenGL/Queries/BufferedQuery.cs
new file mode 100644
index 00000000..9d43f6c3
--- /dev/null
+++ b/src/Ryujinx.Graphics.OpenGL/Queries/BufferedQuery.cs
@@ -0,0 +1,120 @@
+using OpenTK.Graphics.OpenGL;
+using Ryujinx.Common.Logging;
+using System;
+using System.Runtime.InteropServices;
+using System.Threading;
+
+namespace Ryujinx.Graphics.OpenGL.Queries
+{
+ class BufferedQuery : IDisposable
+ {
+ private const int MaxQueryRetries = 5000;
+ private const long DefaultValue = -1;
+ private const ulong HighMask = 0xFFFFFFFF00000000;
+
+ public int Query { get; }
+
+ private int _buffer;
+ private IntPtr _bufferMap;
+ private QueryTarget _type;
+
+ public BufferedQuery(QueryTarget type)
+ {
+ _buffer = GL.GenBuffer();
+ Query = GL.GenQuery();
+ _type = type;
+
+ GL.BindBuffer(BufferTarget.QueryBuffer, _buffer);
+
+ unsafe
+ {
+ long defaultValue = DefaultValue;
+ GL.BufferStorage(BufferTarget.QueryBuffer, sizeof(long), (IntPtr)(&defaultValue), BufferStorageFlags.MapReadBit | BufferStorageFlags.MapWriteBit | BufferStorageFlags.MapPersistentBit);
+ }
+ _bufferMap = GL.MapBufferRange(BufferTarget.QueryBuffer, IntPtr.Zero, sizeof(long), BufferAccessMask.MapReadBit | BufferAccessMask.MapWriteBit | BufferAccessMask.MapPersistentBit);
+ }
+
+ public void Reset()
+ {
+ GL.EndQuery(_type);
+ GL.BeginQuery(_type, Query);
+ }
+
+ public void Begin()
+ {
+ GL.BeginQuery(_type, Query);
+ }
+
+ public unsafe void End(bool withResult)
+ {
+ GL.EndQuery(_type);
+
+ if (withResult)
+ {
+ GL.BindBuffer(BufferTarget.QueryBuffer, _buffer);
+
+ Marshal.WriteInt64(_bufferMap, -1L);
+ GL.GetQueryObject(Query, GetQueryObjectParam.QueryResult, (long*)0);
+ GL.MemoryBarrier(MemoryBarrierFlags.QueryBufferBarrierBit | MemoryBarrierFlags.ClientMappedBufferBarrierBit);
+ }
+ else
+ {
+ // Dummy result, just return 0.
+ Marshal.WriteInt64(_bufferMap, 0L);
+ }
+ }
+
+ private bool WaitingForValue(long data)
+ {
+ return data == DefaultValue ||
+ ((ulong)data & HighMask) == (unchecked((ulong)DefaultValue) & HighMask);
+ }
+
+ public bool TryGetResult(out long result)
+ {
+ result = Marshal.ReadInt64(_bufferMap);
+
+ return !WaitingForValue(result);
+ }
+
+ public long AwaitResult(AutoResetEvent wakeSignal = null)
+ {
+ long data = DefaultValue;
+
+ if (wakeSignal == null)
+ {
+ while (WaitingForValue(data))
+ {
+ data = Marshal.ReadInt64(_bufferMap);
+ }
+ }
+ else
+ {
+ int iterations = 0;
+ while (WaitingForValue(data) && iterations++ < MaxQueryRetries)
+ {
+ data = Marshal.ReadInt64(_bufferMap);
+ if (WaitingForValue(data))
+ {
+ wakeSignal.WaitOne(1);
+ }
+ }
+
+ if (iterations >= MaxQueryRetries)
+ {
+ Logger.Error?.Print(LogClass.Gpu, $"Error: Query result timed out. Took more than {MaxQueryRetries} tries.");
+ }
+ }
+
+ return data;
+ }
+
+ public void Dispose()
+ {
+ GL.BindBuffer(BufferTarget.QueryBuffer, _buffer);
+ GL.UnmapBuffer(BufferTarget.QueryBuffer);
+ GL.DeleteBuffer(_buffer);
+ GL.DeleteQuery(Query);
+ }
+ }
+}