/ src / Ryujinx.Graphics.Gpu / Synchronization / SynchronizationManager.cs
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  }