Syncpoint.cs
  1  using System;
  2  using System.Collections.Generic;
  3  using System.Threading;
  4  
  5  namespace Ryujinx.Graphics.Gpu.Synchronization
  6  {
  7      /// <summary>
  8      /// Represents GPU hardware syncpoint.
  9      /// </summary>
 10      class Syncpoint
 11      {
 12          private int _storedValue;
 13  
 14          public readonly uint Id;
 15  
 16          /// <summary>
 17          /// The value of the syncpoint.
 18          /// </summary>
 19          public uint Value => (uint)_storedValue;
 20  
 21          // TODO: switch to something handling concurrency?
 22          private readonly List<SyncpointWaiterHandle> _waiters;
 23  
 24          public Syncpoint(uint id)
 25          {
 26              Id = id;
 27              _waiters = new List<SyncpointWaiterHandle>();
 28          }
 29  
 30          /// <summary>
 31          /// Register a new callback for a target threshold.
 32          /// The callback will be called once the threshold is reached and will automatically be unregistered.
 33          /// </summary>
 34          /// <param name="threshold">The target threshold</param>
 35          /// <param name="callback">The callback to call when the threshold is reached</param>
 36          /// <returns>The created SyncpointWaiterHandle object or null if already past threshold</returns>
 37          public SyncpointWaiterHandle RegisterCallback(uint threshold, Action<SyncpointWaiterHandle> callback)
 38          {
 39              lock (_waiters)
 40              {
 41                  if (Value >= threshold)
 42                  {
 43                      callback(null);
 44  
 45                      return null;
 46                  }
 47                  else
 48                  {
 49                      SyncpointWaiterHandle waiterInformation = new()
 50                      {
 51                          Threshold = threshold,
 52                          Callback = callback,
 53                      };
 54  
 55                      _waiters.Add(waiterInformation);
 56  
 57                      return waiterInformation;
 58                  }
 59              }
 60          }
 61  
 62          public void UnregisterCallback(SyncpointWaiterHandle waiterInformation)
 63          {
 64              lock (_waiters)
 65              {
 66                  _waiters.Remove(waiterInformation);
 67              }
 68          }
 69  
 70          /// <summary>
 71          /// Increment the syncpoint
 72          /// </summary>
 73          /// <returns>The incremented value of the syncpoint</returns>
 74          public uint Increment()
 75          {
 76              uint currentValue = (uint)Interlocked.Increment(ref _storedValue);
 77  
 78              SyncpointWaiterHandle expired = null;
 79              List<SyncpointWaiterHandle> expiredList = null;
 80  
 81              lock (_waiters)
 82              {
 83                  _waiters.RemoveAll(item =>
 84                  {
 85                      bool isPastThreshold = currentValue >= item.Threshold;
 86  
 87                      if (isPastThreshold)
 88                      {
 89                          if (expired == null)
 90                          {
 91                              expired = item;
 92                          }
 93                          else
 94                          {
 95                              expiredList ??= new List<SyncpointWaiterHandle>();
 96  
 97                              expiredList.Add(item);
 98                          }
 99                      }
100  
101                      return isPastThreshold;
102                  });
103              }
104  
105              // Call the callbacks as a separate step.
106              // As we don't know what the callback will be doing,
107              // and it could block execution for a indefinite amount of time,
108              // we can't call it inside the lock.
109              if (expired != null)
110              {
111                  expired.Callback(expired);
112  
113                  if (expiredList != null)
114                  {
115                      for (int i = 0; i < expiredList.Count; i++)
116                      {
117                          expiredList[i].Callback(expiredList[i]);
118                      }
119                  }
120              }
121  
122              return currentValue;
123          }
124      }
125  }