Sync.cs
  1  using OpenTK.Graphics.OpenGL;
  2  using Ryujinx.Common.Logging;
  3  using System;
  4  using System.Collections.Generic;
  5  using System.Linq;
  6  
  7  namespace Ryujinx.Graphics.OpenGL
  8  {
  9      class Sync : IDisposable
 10      {
 11          private class SyncHandle
 12          {
 13              public ulong ID;
 14              public IntPtr Handle;
 15          }
 16  
 17          private ulong _firstHandle = 0;
 18          private static ClientWaitSyncFlags SyncFlags => HwCapabilities.RequiresSyncFlush ? ClientWaitSyncFlags.None : ClientWaitSyncFlags.SyncFlushCommandsBit;
 19  
 20          private readonly List<SyncHandle> _handles = new();
 21  
 22          public void Create(ulong id)
 23          {
 24              SyncHandle handle = new()
 25              {
 26                  ID = id,
 27                  Handle = GL.FenceSync(SyncCondition.SyncGpuCommandsComplete, WaitSyncFlags.None),
 28              };
 29  
 30  
 31              if (HwCapabilities.RequiresSyncFlush)
 32              {
 33                  // Force commands to flush up to the syncpoint.
 34                  GL.ClientWaitSync(handle.Handle, ClientWaitSyncFlags.SyncFlushCommandsBit, 0);
 35              }
 36  
 37              lock (_handles)
 38              {
 39                  _handles.Add(handle);
 40              }
 41          }
 42  
 43          public ulong GetCurrent()
 44          {
 45              lock (_handles)
 46              {
 47                  ulong lastHandle = _firstHandle;
 48  
 49                  foreach (SyncHandle handle in _handles)
 50                  {
 51                      lock (handle)
 52                      {
 53                          if (handle.Handle == IntPtr.Zero)
 54                          {
 55                              continue;
 56                          }
 57  
 58                          if (handle.ID > lastHandle)
 59                          {
 60                              WaitSyncStatus syncResult = GL.ClientWaitSync(handle.Handle, SyncFlags, 0);
 61  
 62                              if (syncResult == WaitSyncStatus.AlreadySignaled)
 63                              {
 64                                  lastHandle = handle.ID;
 65                              }
 66                          }
 67                      }
 68                  }
 69  
 70                  return lastHandle;
 71              }
 72          }
 73  
 74          public void Wait(ulong id)
 75          {
 76              SyncHandle result = null;
 77  
 78              lock (_handles)
 79              {
 80                  if ((long)(_firstHandle - id) > 0)
 81                  {
 82                      return; // The handle has already been signalled or deleted.
 83                  }
 84  
 85                  foreach (SyncHandle handle in _handles)
 86                  {
 87                      if (handle.ID == id)
 88                      {
 89                          result = handle;
 90                          break;
 91                      }
 92                  }
 93              }
 94  
 95              if (result != null)
 96              {
 97                  lock (result)
 98                  {
 99                      if (result.Handle == IntPtr.Zero)
100                      {
101                          return;
102                      }
103  
104                      WaitSyncStatus syncResult = GL.ClientWaitSync(result.Handle, SyncFlags, 1000000000);
105  
106                      if (syncResult == WaitSyncStatus.TimeoutExpired)
107                      {
108                          Logger.Error?.PrintMsg(LogClass.Gpu, $"GL Sync Object {result.ID} failed to signal within 1000ms. Continuing...");
109                      }
110                  }
111              }
112          }
113  
114          public void Cleanup()
115          {
116              // Iterate through handles and remove any that have already been signalled.
117  
118              while (true)
119              {
120                  SyncHandle first = null;
121                  lock (_handles)
122                  {
123                      first = _handles.FirstOrDefault();
124                  }
125  
126                  if (first == null)
127                  {
128                      break;
129                  }
130  
131                  WaitSyncStatus syncResult = GL.ClientWaitSync(first.Handle, SyncFlags, 0);
132  
133                  if (syncResult == WaitSyncStatus.AlreadySignaled)
134                  {
135                      // Delete the sync object.
136                      lock (_handles)
137                      {
138                          lock (first)
139                          {
140                              _firstHandle = first.ID + 1;
141                              _handles.RemoveAt(0);
142                              GL.DeleteSync(first.Handle);
143                              first.Handle = IntPtr.Zero;
144                          }
145                      }
146                  }
147                  else
148                  {
149                      // This sync handle and any following have not been reached yet.
150                      break;
151                  }
152              }
153          }
154  
155          public void Dispose()
156          {
157              lock (_handles)
158              {
159                  foreach (SyncHandle handle in _handles)
160                  {
161                      lock (handle)
162                      {
163                          GL.DeleteSync(handle.Handle);
164                          handle.Handle = IntPtr.Zero;
165                      }
166                  }
167  
168                  _handles.Clear();
169              }
170          }
171      }
172  }