SyncManager.cs
1 using Ryujinx.Common.Logging; 2 using Silk.NET.Vulkan; 3 using System.Collections.Generic; 4 using System.Diagnostics; 5 using System.Linq; 6 7 namespace Ryujinx.Graphics.Vulkan 8 { 9 class SyncManager 10 { 11 private class SyncHandle 12 { 13 public ulong ID; 14 public MultiFenceHolder Waitable; 15 public ulong FlushId; 16 public bool Signalled; 17 18 public bool NeedsFlush(ulong currentFlushId) 19 { 20 return (long)(FlushId - currentFlushId) >= 0; 21 } 22 } 23 24 private ulong _firstHandle; 25 26 private readonly VulkanRenderer _gd; 27 private readonly Device _device; 28 private readonly List<SyncHandle> _handles; 29 private ulong _flushId; 30 private long _waitTicks; 31 32 public SyncManager(VulkanRenderer gd, Device device) 33 { 34 _gd = gd; 35 _device = device; 36 _handles = new List<SyncHandle>(); 37 } 38 39 public void RegisterFlush() 40 { 41 _flushId++; 42 } 43 44 public void Create(ulong id, bool strict) 45 { 46 ulong flushId = _flushId; 47 MultiFenceHolder waitable = new(); 48 if (strict || _gd.InterruptAction == null) 49 { 50 _gd.FlushAllCommands(); 51 _gd.CommandBufferPool.AddWaitable(waitable); 52 } 53 else 54 { 55 // Don't flush commands, instead wait for the current command buffer to finish. 56 // If this sync is waited on before the command buffer is submitted, interrupt the gpu thread and flush it manually. 57 58 _gd.CommandBufferPool.AddInUseWaitable(waitable); 59 } 60 61 SyncHandle handle = new() 62 { 63 ID = id, 64 Waitable = waitable, 65 FlushId = flushId, 66 }; 67 68 lock (_handles) 69 { 70 _handles.Add(handle); 71 } 72 } 73 74 public ulong GetCurrent() 75 { 76 lock (_handles) 77 { 78 ulong lastHandle = _firstHandle; 79 80 foreach (SyncHandle handle in _handles) 81 { 82 lock (handle) 83 { 84 if (handle.Waitable == null) 85 { 86 continue; 87 } 88 89 if (handle.ID > lastHandle) 90 { 91 bool signaled = handle.Signalled || handle.Waitable.WaitForFences(_gd.Api, _device, 0); 92 if (signaled) 93 { 94 lastHandle = handle.ID; 95 handle.Signalled = true; 96 } 97 } 98 } 99 } 100 101 return lastHandle; 102 } 103 } 104 105 public void Wait(ulong id) 106 { 107 SyncHandle result = null; 108 109 lock (_handles) 110 { 111 if ((long)(_firstHandle - id) > 0) 112 { 113 return; // The handle has already been signalled or deleted. 114 } 115 116 foreach (SyncHandle handle in _handles) 117 { 118 if (handle.ID == id) 119 { 120 result = handle; 121 break; 122 } 123 } 124 } 125 126 if (result != null) 127 { 128 if (result.Waitable == null) 129 { 130 return; 131 } 132 133 long beforeTicks = Stopwatch.GetTimestamp(); 134 135 if (result.NeedsFlush(_flushId)) 136 { 137 _gd.InterruptAction(() => 138 { 139 if (result.NeedsFlush(_flushId)) 140 { 141 _gd.FlushAllCommands(); 142 } 143 }); 144 } 145 146 lock (result) 147 { 148 if (result.Waitable == null) 149 { 150 return; 151 } 152 153 bool signaled = result.Signalled || result.Waitable.WaitForFences(_gd.Api, _device, 1000000000); 154 155 if (!signaled) 156 { 157 Logger.Error?.PrintMsg(LogClass.Gpu, $"VK Sync Object {result.ID} failed to signal within 1000ms. Continuing..."); 158 } 159 else 160 { 161 _waitTicks += Stopwatch.GetTimestamp() - beforeTicks; 162 result.Signalled = true; 163 } 164 } 165 } 166 } 167 168 public void Cleanup() 169 { 170 // Iterate through handles and remove any that have already been signalled. 171 172 while (true) 173 { 174 SyncHandle first = null; 175 lock (_handles) 176 { 177 first = _handles.FirstOrDefault(); 178 } 179 180 if (first == null || first.NeedsFlush(_flushId)) 181 { 182 break; 183 } 184 185 bool signaled = first.Waitable.WaitForFences(_gd.Api, _device, 0); 186 if (signaled) 187 { 188 // Delete the sync object. 189 lock (_handles) 190 { 191 lock (first) 192 { 193 _firstHandle = first.ID + 1; 194 _handles.RemoveAt(0); 195 first.Waitable = null; 196 } 197 } 198 } 199 else 200 { 201 // This sync handle and any following have not been reached yet. 202 break; 203 } 204 } 205 } 206 207 public long GetAndResetWaitTicks() 208 { 209 long result = _waitTicks; 210 _waitTicks = 0; 211 212 return result; 213 } 214 } 215 }