SyncManager.cs 6.0 KB

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