SyncManager.cs 5.9 KB

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