VulkanSwapchain.cs 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. using System;
  2. using System.Runtime.InteropServices;
  3. using System.Threading.Tasks;
  4. using Avalonia;
  5. using Avalonia.Platform;
  6. using Avalonia.Rendering;
  7. using Avalonia.Rendering.Composition;
  8. using Avalonia.Vulkan;
  9. using Silk.NET.Vulkan;
  10. namespace GpuInterop.VulkanDemo;
  11. class VulkanSwapchain : SwapchainBase<VulkanSwapchainImage>
  12. {
  13. private readonly VulkanContext _vk;
  14. public VulkanSwapchain(VulkanContext vk, ICompositionGpuInterop interop, CompositionDrawingSurface target) : base(interop, target)
  15. {
  16. _vk = vk;
  17. }
  18. protected override VulkanSwapchainImage CreateImage(PixelSize size)
  19. {
  20. return new VulkanSwapchainImage(_vk, size, Interop, Target);
  21. }
  22. public IDisposable BeginDraw(PixelSize size, out VulkanImage image)
  23. {
  24. _vk.Pool.FreeUsedCommandBuffers();
  25. var rv = BeginDrawCore(size, out var swapchainImage);
  26. image = swapchainImage.Image;
  27. return rv;
  28. }
  29. }
  30. class VulkanSwapchainImage : ISwapchainImage
  31. {
  32. private readonly VulkanContext _vk;
  33. private readonly ICompositionGpuInterop _interop;
  34. private readonly CompositionDrawingSurface _target;
  35. private readonly VulkanImage _image;
  36. private readonly VulkanSemaphorePair? _semaphorePair;
  37. private readonly VulkanTimelineSemaphore? _timelineSemaphore;
  38. private ulong _timelineCounter;
  39. private ICompositionImportedGpuSemaphore? _availableSemaphore, _renderCompletedSemaphore, _importedTimelineSemaphore;
  40. private ICompositionImportedGpuImage? _importedImage;
  41. private Task? _lastPresent;
  42. public VulkanImage Image => _image;
  43. private bool _initial = true;
  44. public VulkanSwapchainImage(VulkanContext vk, PixelSize size, ICompositionGpuInterop interop, CompositionDrawingSurface target)
  45. {
  46. _vk = vk;
  47. _interop = interop;
  48. _target = target;
  49. Size = size;
  50. var format = RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? Format.B8G8R8A8Unorm : Format.R8G8B8A8Unorm;
  51. _image = new VulkanImage(vk, (uint)format, size, true, interop.SupportedImageHandleTypes);
  52. if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
  53. _timelineSemaphore = new(vk);
  54. else
  55. _semaphorePair = new VulkanSemaphorePair(vk, true);
  56. }
  57. public async ValueTask DisposeAsync()
  58. {
  59. if (LastPresent != null)
  60. await LastPresent;
  61. if (_importedImage != null)
  62. await _importedImage.DisposeAsync();
  63. if (_availableSemaphore != null)
  64. await _availableSemaphore.DisposeAsync();
  65. if (_renderCompletedSemaphore != null)
  66. await _renderCompletedSemaphore.DisposeAsync();
  67. _semaphorePair?.Dispose();
  68. _timelineSemaphore?.Dispose();
  69. _image.Dispose();
  70. }
  71. public PixelSize Size { get; }
  72. public Task? LastPresent => _lastPresent;
  73. public void BeginDraw()
  74. {
  75. var buffer = _vk.Pool.CreateCommandBuffer();
  76. buffer.BeginRecording();
  77. _image.TransitionLayout(buffer.InternalHandle,
  78. ImageLayout.Undefined, AccessFlags.None,
  79. ImageLayout.ColorAttachmentOptimal, AccessFlags.ColorAttachmentReadBit);
  80. if(_image.IsDirectXBacked)
  81. buffer.Submit(null,null,null, null, new VulkanCommandBufferPool.VulkanCommandBuffer.KeyedMutexSubmitInfo
  82. {
  83. AcquireKey = 0,
  84. DeviceMemory = _image.DeviceMemory
  85. });
  86. else if (_timelineSemaphore != null)
  87. {
  88. unsafe
  89. {
  90. var wait = _timelineCounter;
  91. var submitInfo = new TimelineSemaphoreSubmitInfo
  92. {
  93. PWaitSemaphoreValues = &wait,
  94. WaitSemaphoreValueCount = 1,
  95. SType = StructureType.TimelineSemaphoreSubmitInfo
  96. };
  97. var waitSemaphores = new[] { _timelineSemaphore.Handle };
  98. buffer.Submit(waitSemaphores, pNext: (IntPtr)(&submitInfo));
  99. }
  100. }
  101. else if (_initial)
  102. {
  103. _initial = false;
  104. buffer.Submit();
  105. }
  106. else
  107. buffer.Submit(new[] { _semaphorePair.ImageAvailableSemaphore },
  108. new[]
  109. {
  110. PipelineStageFlags.AllGraphicsBit
  111. });
  112. }
  113. public void Present()
  114. {
  115. var buffer = _vk.Pool.CreateCommandBuffer();
  116. buffer.BeginRecording();
  117. _image.TransitionLayout(buffer.InternalHandle, ImageLayout.TransferSrcOptimal, AccessFlags.TransferWriteBit);
  118. if (_image.IsDirectXBacked)
  119. {
  120. buffer.Submit(null, null, null, null,
  121. new VulkanCommandBufferPool.VulkanCommandBuffer.KeyedMutexSubmitInfo
  122. {
  123. DeviceMemory = _image.DeviceMemory, ReleaseKey = 1
  124. });
  125. }
  126. else if (_timelineSemaphore != null)
  127. {
  128. unsafe
  129. {
  130. var signal = _timelineCounter + 1;
  131. var submitInfo = new TimelineSemaphoreSubmitInfo
  132. {
  133. PSignalSemaphoreValues = &signal,
  134. SignalSemaphoreValueCount = 1,
  135. SType = StructureType.TimelineSemaphoreSubmitInfo
  136. };
  137. var signalSemaphores = new[] { _timelineSemaphore.Handle };
  138. buffer.Submit(default, signalSemaphores: signalSemaphores, pNext: (IntPtr)(&submitInfo));
  139. }
  140. }
  141. else
  142. buffer.Submit(null, null, new[] { _semaphorePair.RenderFinishedSemaphore });
  143. if (_timelineSemaphore != null)
  144. {
  145. _importedTimelineSemaphore ??= _interop.ImportSemaphore(_timelineSemaphore.Export());
  146. }
  147. else if (!_image.IsDirectXBacked)
  148. {
  149. _availableSemaphore ??= _interop.ImportSemaphore(_semaphorePair.Export(false));
  150. _renderCompletedSemaphore ??= _interop.ImportSemaphore(_semaphorePair.Export(true));
  151. }
  152. _importedImage ??= _interop.ImportImage(_image.Export(),
  153. new PlatformGraphicsExternalImageProperties
  154. {
  155. Format = RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? PlatformGraphicsExternalImageFormat.B8G8R8A8UNorm : PlatformGraphicsExternalImageFormat.R8G8B8A8UNorm,
  156. Width = Size.Width,
  157. Height = Size.Height,
  158. MemorySize = _image.MemorySize
  159. });
  160. if (_importedTimelineSemaphore != null)
  161. {
  162. _lastPresent = _target.UpdateWithTimelineSemaphoresAsync(_importedImage,
  163. _importedTimelineSemaphore, _timelineCounter + 1, _importedTimelineSemaphore, _timelineCounter + 2);
  164. _timelineCounter += 2;
  165. }
  166. else if (_image.IsDirectXBacked)
  167. _lastPresent = _target.UpdateWithKeyedMutexAsync(_importedImage, 1, 0);
  168. else
  169. _lastPresent = _target.UpdateWithSemaphoresAsync(_importedImage, _renderCompletedSemaphore!, _availableSemaphore!);
  170. }
  171. }