using OpenTK.Graphics.OpenGL;
using Ryujinx.Common.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Ryujinx.Graphics.OpenGL
{
class Sync : IDisposable
private class SyncHandle
public ulong ID;
public IntPtr Handle;
}
private ulong _firstHandle = 0;
private ClientWaitSyncFlags _syncFlags => HwCapabilities.RequiresSyncFlush ? ClientWaitSyncFlags.None : ClientWaitSyncFlags.SyncFlushCommandsBit;
private List<SyncHandle> _handles = new List<SyncHandle>();
public void Create(ulong id)
SyncHandle handle = new SyncHandle
ID = id,
Handle = GL.FenceSync(SyncCondition.SyncGpuCommandsComplete, WaitSyncFlags.None)
};
if (HwCapabilities.RequiresSyncFlush)
// Force commands to flush up to the syncpoint.
GL.ClientWaitSync(handle.Handle, ClientWaitSyncFlags.SyncFlushCommandsBit, 0);
lock (_handles)
_handles.Add(handle);
public ulong GetCurrent()
ulong lastHandle = _firstHandle;
foreach (SyncHandle handle in _handles)
lock (handle)
if (handle.Handle == IntPtr.Zero)
continue;
if (handle.ID > lastHandle)
WaitSyncStatus syncResult = GL.ClientWaitSync(handle.Handle, _syncFlags, 0);
if (syncResult == WaitSyncStatus.AlreadySignaled)
lastHandle = handle.ID;
return lastHandle;
public void Wait(ulong id)
SyncHandle result = null;
if ((long)(_firstHandle - id) > 0)
return; // The handle has already been signalled or deleted.
if (handle.ID == id)
result = handle;
break;
if (result != null)
lock (result)
if (result.Handle == IntPtr.Zero)
return;
WaitSyncStatus syncResult = GL.ClientWaitSync(result.Handle, _syncFlags, 1000000000);
if (syncResult == WaitSyncStatus.TimeoutExpired)
Logger.Error?.PrintMsg(LogClass.Gpu, $"GL Sync Object {result.ID} failed to signal within 1000ms. Continuing...");
public void Cleanup()
// Iterate through handles and remove any that have already been signalled.
while (true)
SyncHandle first = null;
first = _handles.FirstOrDefault();
if (first == null) break;
WaitSyncStatus syncResult = GL.ClientWaitSync(first.Handle, _syncFlags, 0);
// Delete the sync object.
lock (first)
_firstHandle = first.ID + 1;
_handles.RemoveAt(0);
GL.DeleteSync(first.Handle);
first.Handle = IntPtr.Zero;
} else
// This sync handle and any following have not been reached yet.
public void Dispose()
GL.DeleteSync(handle.Handle);
handle.Handle = IntPtr.Zero;
_handles.Clear();