SynchronizationManager.cs
1 using Ryujinx.Common.Logging; 2 using Ryujinx.Graphics.Device; 3 using System; 4 using System.Threading; 5 6 namespace Ryujinx.Graphics.Gpu.Synchronization 7 { 8 /// <summary> 9 /// GPU synchronization manager. 10 /// </summary> 11 public class SynchronizationManager : ISynchronizationManager 12 { 13 /// <summary> 14 /// The maximum number of syncpoints supported by the GM20B. 15 /// </summary> 16 public const int MaxHardwareSyncpoints = 192; 17 18 /// <summary> 19 /// Array containing all hardware syncpoints. 20 /// </summary> 21 private readonly Syncpoint[] _syncpoints; 22 23 public SynchronizationManager() 24 { 25 _syncpoints = new Syncpoint[MaxHardwareSyncpoints]; 26 27 for (uint i = 0; i < _syncpoints.Length; i++) 28 { 29 _syncpoints[i] = new Syncpoint(i); 30 } 31 } 32 33 /// <inheritdoc/> 34 public uint IncrementSyncpoint(uint id) 35 { 36 ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(id, (uint)MaxHardwareSyncpoints); 37 38 return _syncpoints[id].Increment(); 39 } 40 41 /// <inheritdoc/> 42 public uint GetSyncpointValue(uint id) 43 { 44 ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(id, (uint)MaxHardwareSyncpoints); 45 46 return _syncpoints[id].Value; 47 } 48 49 /// <summary> 50 /// Register a new callback on a syncpoint with a given id at a target threshold. 51 /// The callback will be called once the threshold is reached and will automatically be unregistered. 52 /// </summary> 53 /// <param name="id">The id of the syncpoint</param> 54 /// <param name="threshold">The target threshold</param> 55 /// <param name="callback">The callback to call when the threshold is reached</param> 56 /// <exception cref="System.ArgumentOutOfRangeException">Thrown when id >= MaxHardwareSyncpoints</exception> 57 /// <returns>The created SyncpointWaiterHandle object or null if already past threshold</returns> 58 public SyncpointWaiterHandle RegisterCallbackOnSyncpoint(uint id, uint threshold, Action<SyncpointWaiterHandle> callback) 59 { 60 ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(id, (uint)MaxHardwareSyncpoints); 61 62 return _syncpoints[id].RegisterCallback(threshold, callback); 63 } 64 65 /// <summary> 66 /// Unregister a callback on a given syncpoint. 67 /// </summary> 68 /// <param name="id">The id of the syncpoint</param> 69 /// <param name="waiterInformation">The waiter information to unregister</param> 70 /// <exception cref="System.ArgumentOutOfRangeException">Thrown when id >= MaxHardwareSyncpoints</exception> 71 public void UnregisterCallback(uint id, SyncpointWaiterHandle waiterInformation) 72 { 73 ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(id, (uint)MaxHardwareSyncpoints); 74 75 _syncpoints[id].UnregisterCallback(waiterInformation); 76 } 77 78 /// <inheritdoc/> 79 public bool WaitOnSyncpoint(uint id, uint threshold, TimeSpan timeout) 80 { 81 ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(id, (uint)MaxHardwareSyncpoints); 82 83 // TODO: Remove this when GPU channel scheduling will be implemented. 84 if (timeout == Timeout.InfiniteTimeSpan) 85 { 86 timeout = TimeSpan.FromSeconds(1); 87 } 88 89 using ManualResetEvent waitEvent = new(false); 90 var info = _syncpoints[id].RegisterCallback(threshold, (x) => waitEvent.Set()); 91 92 if (info == null) 93 { 94 return false; 95 } 96 97 bool signaled = waitEvent.WaitOne(timeout); 98 99 if (!signaled && info != null) 100 { 101 Logger.Error?.Print(LogClass.Gpu, $"Wait on syncpoint {id} for threshold {threshold} took more than {timeout.TotalMilliseconds}ms, resuming execution..."); 102 103 _syncpoints[id].UnregisterCallback(info); 104 } 105 106 return !signaled; 107 } 108 } 109 }