VulkanCommandBufferPool.cs 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. using System;
  2. using System.Collections.Generic;
  3. using Silk.NET.Vulkan;
  4. using SilkNetDemo;
  5. namespace Avalonia.Vulkan
  6. {
  7. public class VulkanCommandBufferPool : IDisposable
  8. {
  9. private readonly Vk _api;
  10. private readonly Device _device;
  11. private readonly Queue _queue;
  12. private readonly CommandPool _commandPool;
  13. private readonly List<VulkanCommandBuffer> _usedCommandBuffers = new();
  14. private readonly object _lock = new();
  15. public unsafe VulkanCommandBufferPool(Vk api, Device device, Queue queue, uint queueFamilyIndex)
  16. {
  17. _api = api;
  18. _device = device;
  19. _queue = queue;
  20. var commandPoolCreateInfo = new CommandPoolCreateInfo
  21. {
  22. SType = StructureType.CommandPoolCreateInfo,
  23. Flags = CommandPoolCreateFlags.ResetCommandBufferBit,
  24. QueueFamilyIndex = queueFamilyIndex
  25. };
  26. _api.CreateCommandPool(_device, commandPoolCreateInfo, null, out _commandPool)
  27. .ThrowOnError();
  28. }
  29. public unsafe void Dispose()
  30. {
  31. lock (_lock)
  32. {
  33. FreeUsedCommandBuffers();
  34. _api.DestroyCommandPool(_device, _commandPool, null);
  35. }
  36. }
  37. private CommandBuffer AllocateCommandBuffer()
  38. {
  39. var commandBufferAllocateInfo = new CommandBufferAllocateInfo
  40. {
  41. SType = StructureType.CommandBufferAllocateInfo,
  42. CommandPool = _commandPool,
  43. CommandBufferCount = 1,
  44. Level = CommandBufferLevel.Primary
  45. };
  46. lock (_lock)
  47. {
  48. _api.AllocateCommandBuffers(_device, commandBufferAllocateInfo, out var commandBuffer);
  49. return commandBuffer;
  50. }
  51. }
  52. public VulkanCommandBuffer CreateCommandBuffer()
  53. {
  54. return new(_api, _device, _queue, this);
  55. }
  56. public void FreeUsedCommandBuffers()
  57. {
  58. lock (_lock)
  59. {
  60. foreach (var usedCommandBuffer in _usedCommandBuffers) usedCommandBuffer.Dispose();
  61. _usedCommandBuffers.Clear();
  62. }
  63. }
  64. private void DisposeCommandBuffer(VulkanCommandBuffer commandBuffer)
  65. {
  66. lock (_lock)
  67. {
  68. _usedCommandBuffers.Add(commandBuffer);
  69. }
  70. }
  71. public class VulkanCommandBuffer : IDisposable
  72. {
  73. private readonly VulkanCommandBufferPool _commandBufferPool;
  74. private readonly Vk _api;
  75. private readonly Device _device;
  76. private readonly Queue _queue;
  77. private readonly Fence _fence;
  78. private bool _hasEnded;
  79. private bool _hasStarted;
  80. public IntPtr Handle => InternalHandle.Handle;
  81. internal CommandBuffer InternalHandle { get; }
  82. internal unsafe VulkanCommandBuffer(Vk api, Device device, Queue queue, VulkanCommandBufferPool commandBufferPool)
  83. {
  84. _api = api;
  85. _device = device;
  86. _queue = queue;
  87. _commandBufferPool = commandBufferPool;
  88. InternalHandle = _commandBufferPool.AllocateCommandBuffer();
  89. var fenceCreateInfo = new FenceCreateInfo()
  90. {
  91. SType = StructureType.FenceCreateInfo,
  92. Flags = FenceCreateFlags.SignaledBit
  93. };
  94. api.CreateFence(device, fenceCreateInfo, null, out _fence);
  95. }
  96. public unsafe void Dispose()
  97. {
  98. _api.WaitForFences(_device, 1, _fence, true, ulong.MaxValue);
  99. lock (_commandBufferPool._lock)
  100. {
  101. _api.FreeCommandBuffers(_device, _commandBufferPool._commandPool, 1, InternalHandle);
  102. }
  103. _api.DestroyFence(_device, _fence, null);
  104. }
  105. public void BeginRecording()
  106. {
  107. if (!_hasStarted)
  108. {
  109. _hasStarted = true;
  110. var beginInfo = new CommandBufferBeginInfo
  111. {
  112. SType = StructureType.CommandBufferBeginInfo,
  113. Flags = CommandBufferUsageFlags.OneTimeSubmitBit
  114. };
  115. _api.BeginCommandBuffer(InternalHandle, beginInfo);
  116. }
  117. }
  118. public void EndRecording()
  119. {
  120. if (_hasStarted && !_hasEnded)
  121. {
  122. _hasEnded = true;
  123. _api.EndCommandBuffer(InternalHandle);
  124. }
  125. }
  126. public void Submit()
  127. {
  128. Submit(null, null, null, _fence);
  129. }
  130. public class KeyedMutexSubmitInfo
  131. {
  132. public ulong? AcquireKey { get; set; }
  133. public ulong? ReleaseKey { get; set; }
  134. public DeviceMemory DeviceMemory { get; set; }
  135. }
  136. public unsafe void Submit(
  137. ReadOnlySpan<Semaphore> waitSemaphores,
  138. ReadOnlySpan<PipelineStageFlags> waitDstStageMask = default,
  139. ReadOnlySpan<Semaphore> signalSemaphores = default,
  140. Fence? fence = null,
  141. KeyedMutexSubmitInfo? keyedMutex = null)
  142. {
  143. EndRecording();
  144. if (!fence.HasValue)
  145. fence = _fence;
  146. ulong acquireKey = keyedMutex?.AcquireKey ?? 0, releaseKey = keyedMutex?.ReleaseKey ?? 0;
  147. DeviceMemory devMem = keyedMutex?.DeviceMemory ?? default;
  148. uint timeout = uint.MaxValue;
  149. Win32KeyedMutexAcquireReleaseInfoKHR mutex = default;
  150. if (keyedMutex != null)
  151. mutex = new Win32KeyedMutexAcquireReleaseInfoKHR
  152. {
  153. SType = StructureType.Win32KeyedMutexAcquireReleaseInfoKhr,
  154. AcquireCount = keyedMutex.AcquireKey.HasValue ? 1u : 0u,
  155. ReleaseCount = keyedMutex.ReleaseKey.HasValue ? 1u : 0u,
  156. PAcquireKeys = &acquireKey,
  157. PReleaseKeys = &releaseKey,
  158. PAcquireSyncs = &devMem,
  159. PReleaseSyncs = &devMem,
  160. PAcquireTimeouts = &timeout
  161. };
  162. fixed (Semaphore* pWaitSemaphores = waitSemaphores, pSignalSemaphores = signalSemaphores)
  163. {
  164. fixed (PipelineStageFlags* pWaitDstStageMask = waitDstStageMask)
  165. {
  166. var commandBuffer = InternalHandle;
  167. var submitInfo = new SubmitInfo
  168. {
  169. PNext = keyedMutex != null ? &mutex : null,
  170. SType = StructureType.SubmitInfo,
  171. WaitSemaphoreCount = waitSemaphores != null ? (uint)waitSemaphores.Length : 0,
  172. PWaitSemaphores = pWaitSemaphores,
  173. PWaitDstStageMask = pWaitDstStageMask,
  174. CommandBufferCount = 1,
  175. PCommandBuffers = &commandBuffer,
  176. SignalSemaphoreCount = signalSemaphores != null ? (uint)signalSemaphores.Length : 0,
  177. PSignalSemaphores = pSignalSemaphores,
  178. };
  179. _api.ResetFences(_device, 1, fence.Value);
  180. _api.QueueSubmit(_queue, 1, submitInfo, fence.Value);
  181. }
  182. }
  183. _commandBufferPool.DisposeCommandBuffer(this);
  184. }
  185. }
  186. }
  187. }