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 }