MultiFenceHolder.cs 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. using SharpMetal.Metal;
  2. using System;
  3. using System.Runtime.Versioning;
  4. namespace Ryujinx.Graphics.Metal
  5. {
  6. /// <summary>
  7. /// Holder for multiple host GPU fences.
  8. /// </summary>
  9. [SupportedOSPlatform("macos")]
  10. class MultiFenceHolder
  11. {
  12. private const int BufferUsageTrackingGranularity = 4096;
  13. private readonly FenceHolder[] _fences;
  14. private readonly BufferUsageBitmap _bufferUsageBitmap;
  15. /// <summary>
  16. /// Creates a new instance of the multiple fence holder.
  17. /// </summary>
  18. public MultiFenceHolder()
  19. {
  20. _fences = new FenceHolder[CommandBufferPool.MaxCommandBuffers];
  21. }
  22. /// <summary>
  23. /// Creates a new instance of the multiple fence holder, with a given buffer size in mind.
  24. /// </summary>
  25. /// <param name="size">Size of the buffer</param>
  26. public MultiFenceHolder(int size)
  27. {
  28. _fences = new FenceHolder[CommandBufferPool.MaxCommandBuffers];
  29. _bufferUsageBitmap = new BufferUsageBitmap(size, BufferUsageTrackingGranularity);
  30. }
  31. /// <summary>
  32. /// Adds read/write buffer usage information to the uses list.
  33. /// </summary>
  34. /// <param name="cbIndex">Index of the command buffer where the buffer is used</param>
  35. /// <param name="offset">Offset of the buffer being used</param>
  36. /// <param name="size">Size of the buffer region being used, in bytes</param>
  37. /// <param name="write">Whether the access is a write or not</param>
  38. public void AddBufferUse(int cbIndex, int offset, int size, bool write)
  39. {
  40. _bufferUsageBitmap.Add(cbIndex, offset, size, false);
  41. if (write)
  42. {
  43. _bufferUsageBitmap.Add(cbIndex, offset, size, true);
  44. }
  45. }
  46. /// <summary>
  47. /// Removes all buffer usage information for a given command buffer.
  48. /// </summary>
  49. /// <param name="cbIndex">Index of the command buffer where the buffer is used</param>
  50. public void RemoveBufferUses(int cbIndex)
  51. {
  52. _bufferUsageBitmap?.Clear(cbIndex);
  53. }
  54. /// <summary>
  55. /// Checks if a given range of a buffer is being used by a command buffer still being processed by the GPU.
  56. /// </summary>
  57. /// <param name="cbIndex">Index of the command buffer where the buffer is used</param>
  58. /// <param name="offset">Offset of the buffer being used</param>
  59. /// <param name="size">Size of the buffer region being used, in bytes</param>
  60. /// <returns>True if in use, false otherwise</returns>
  61. public bool IsBufferRangeInUse(int cbIndex, int offset, int size)
  62. {
  63. return _bufferUsageBitmap.OverlapsWith(cbIndex, offset, size);
  64. }
  65. /// <summary>
  66. /// Checks if a given range of a buffer is being used by any command buffer still being processed by the GPU.
  67. /// </summary>
  68. /// <param name="offset">Offset of the buffer being used</param>
  69. /// <param name="size">Size of the buffer region being used, in bytes</param>
  70. /// <param name="write">True if only write usages should count</param>
  71. /// <returns>True if in use, false otherwise</returns>
  72. public bool IsBufferRangeInUse(int offset, int size, bool write)
  73. {
  74. return _bufferUsageBitmap.OverlapsWith(offset, size, write);
  75. }
  76. /// <summary>
  77. /// Adds a fence to the holder.
  78. /// </summary>
  79. /// <param name="cbIndex">Command buffer index of the command buffer that owns the fence</param>
  80. /// <param name="fence">Fence to be added</param>
  81. /// <returns>True if the command buffer's previous fence value was null</returns>
  82. public bool AddFence(int cbIndex, FenceHolder fence)
  83. {
  84. ref FenceHolder fenceRef = ref _fences[cbIndex];
  85. if (fenceRef == null)
  86. {
  87. fenceRef = fence;
  88. return true;
  89. }
  90. return false;
  91. }
  92. /// <summary>
  93. /// Removes a fence from the holder.
  94. /// </summary>
  95. /// <param name="cbIndex">Command buffer index of the command buffer that owns the fence</param>
  96. public void RemoveFence(int cbIndex)
  97. {
  98. _fences[cbIndex] = null;
  99. }
  100. /// <summary>
  101. /// Determines if a fence referenced on the given command buffer.
  102. /// </summary>
  103. /// <param name="cbIndex">Index of the command buffer to check if it's used</param>
  104. /// <returns>True if referenced, false otherwise</returns>
  105. public bool HasFence(int cbIndex)
  106. {
  107. return _fences[cbIndex] != null;
  108. }
  109. /// <summary>
  110. /// Wait until all the fences on the holder are signaled.
  111. /// </summary>
  112. public void WaitForFences()
  113. {
  114. WaitForFencesImpl(0, 0, true);
  115. }
  116. /// <summary>
  117. /// Wait until all the fences on the holder with buffer uses overlapping the specified range are signaled.
  118. /// </summary>
  119. /// <param name="offset">Start offset of the buffer range</param>
  120. /// <param name="size">Size of the buffer range in bytes</param>
  121. public void WaitForFences(int offset, int size)
  122. {
  123. WaitForFencesImpl(offset, size, true);
  124. }
  125. /// <summary>
  126. /// Wait until all the fences on the holder with buffer uses overlapping the specified range are signaled.
  127. /// </summary>
  128. // TODO: Add a proper timeout!
  129. public bool WaitForFences(bool indefinite)
  130. {
  131. return WaitForFencesImpl(0, 0, indefinite);
  132. }
  133. /// <summary>
  134. /// Wait until all the fences on the holder with buffer uses overlapping the specified range are signaled.
  135. /// </summary>
  136. /// <param name="offset">Start offset of the buffer range</param>
  137. /// <param name="size">Size of the buffer range in bytes</param>
  138. /// <param name="indefinite">Indicates if this should wait indefinitely</param>
  139. /// <returns>True if all fences were signaled before the timeout expired, false otherwise</returns>
  140. private bool WaitForFencesImpl(int offset, int size, bool indefinite)
  141. {
  142. Span<FenceHolder> fenceHolders = new FenceHolder[CommandBufferPool.MaxCommandBuffers];
  143. int count = size != 0 ? GetOverlappingFences(fenceHolders, offset, size) : GetFences(fenceHolders);
  144. Span<MTLCommandBuffer> fences = stackalloc MTLCommandBuffer[count];
  145. int fenceCount = 0;
  146. for (int i = 0; i < count; i++)
  147. {
  148. if (fenceHolders[i].TryGet(out MTLCommandBuffer fence))
  149. {
  150. fences[fenceCount] = fence;
  151. if (fenceCount < i)
  152. {
  153. fenceHolders[fenceCount] = fenceHolders[i];
  154. }
  155. fenceCount++;
  156. }
  157. }
  158. if (fenceCount == 0)
  159. {
  160. return true;
  161. }
  162. bool signaled = true;
  163. if (indefinite)
  164. {
  165. foreach (MTLCommandBuffer fence in fences)
  166. {
  167. fence.WaitUntilCompleted();
  168. }
  169. }
  170. else
  171. {
  172. foreach (MTLCommandBuffer fence in fences)
  173. {
  174. if (fence.Status != MTLCommandBufferStatus.Completed)
  175. {
  176. signaled = false;
  177. }
  178. }
  179. }
  180. for (int i = 0; i < fenceCount; i++)
  181. {
  182. fenceHolders[i].Put();
  183. }
  184. return signaled;
  185. }
  186. /// <summary>
  187. /// Gets fences to wait for.
  188. /// </summary>
  189. /// <param name="storage">Span to store fences in</param>
  190. /// <returns>Number of fences placed in storage</returns>
  191. private int GetFences(Span<FenceHolder> storage)
  192. {
  193. int count = 0;
  194. for (int i = 0; i < _fences.Length; i++)
  195. {
  196. FenceHolder fence = _fences[i];
  197. if (fence != null)
  198. {
  199. storage[count++] = fence;
  200. }
  201. }
  202. return count;
  203. }
  204. /// <summary>
  205. /// Gets fences to wait for use of a given buffer region.
  206. /// </summary>
  207. /// <param name="storage">Span to store overlapping fences in</param>
  208. /// <param name="offset">Offset of the range</param>
  209. /// <param name="size">Size of the range in bytes</param>
  210. /// <returns>Number of fences for the specified region placed in storage</returns>
  211. private int GetOverlappingFences(Span<FenceHolder> storage, int offset, int size)
  212. {
  213. int count = 0;
  214. for (int i = 0; i < _fences.Length; i++)
  215. {
  216. FenceHolder fence = _fences[i];
  217. if (fence != null && _bufferUsageBitmap.OverlapsWith(i, offset, size))
  218. {
  219. storage[count++] = fence;
  220. }
  221. }
  222. return count;
  223. }
  224. }
  225. }