SyncManager.cs 5.5 KB

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