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 }