VulkanDisplay.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456
  1. using System;
  2. using System.Linq;
  3. using System.Threading;
  4. using Avalonia;
  5. using Ryujinx.Ava.Ui.Vulkan.Surfaces;
  6. using Silk.NET.Vulkan;
  7. using Silk.NET.Vulkan.Extensions.KHR;
  8. namespace Ryujinx.Ava.Ui.Vulkan
  9. {
  10. internal class VulkanDisplay : IDisposable
  11. {
  12. private static KhrSwapchain _swapchainExtension;
  13. private readonly VulkanInstance _instance;
  14. private readonly VulkanPhysicalDevice _physicalDevice;
  15. private readonly VulkanSemaphorePair _semaphorePair;
  16. private readonly VulkanDevice _device;
  17. private uint _nextImage;
  18. private readonly VulkanSurface _surface;
  19. private SurfaceFormatKHR _surfaceFormat;
  20. private SwapchainKHR _swapchain;
  21. private Extent2D _swapchainExtent;
  22. private Image[] _swapchainImages;
  23. private ImageView[] _swapchainImageViews = Array.Empty<ImageView>();
  24. private bool _vsyncStateChanged;
  25. private bool _vsyncEnabled;
  26. private bool _surfaceChanged;
  27. public event EventHandler Presented;
  28. public VulkanCommandBufferPool CommandBufferPool { get; set; }
  29. public object Lock => _device.Lock;
  30. private VulkanDisplay(VulkanInstance instance, VulkanDevice device,
  31. VulkanPhysicalDevice physicalDevice, VulkanSurface surface, SwapchainKHR swapchain,
  32. Extent2D swapchainExtent)
  33. {
  34. _instance = instance;
  35. _device = device;
  36. _physicalDevice = physicalDevice;
  37. _swapchain = swapchain;
  38. _swapchainExtent = swapchainExtent;
  39. _surface = surface;
  40. CreateSwapchainImages();
  41. _semaphorePair = new VulkanSemaphorePair(_device);
  42. CommandBufferPool = new VulkanCommandBufferPool(device, physicalDevice);
  43. }
  44. public PixelSize Size { get; private set; }
  45. public uint QueueFamilyIndex => _physicalDevice.QueueFamilyIndex;
  46. internal SurfaceFormatKHR SurfaceFormat
  47. {
  48. get
  49. {
  50. if (_surfaceFormat.Format == Format.Undefined)
  51. {
  52. _surfaceFormat = _surface.GetSurfaceFormat(_physicalDevice);
  53. }
  54. return _surfaceFormat;
  55. }
  56. }
  57. public void Dispose()
  58. {
  59. _device.WaitIdle();
  60. _semaphorePair?.Dispose();
  61. DestroyCurrentImageViews();
  62. _swapchainExtension.DestroySwapchain(_device.InternalHandle, _swapchain, Span<AllocationCallbacks>.Empty);
  63. CommandBufferPool.Dispose();
  64. }
  65. public bool IsSurfaceChanged()
  66. {
  67. var changed = _surfaceChanged;
  68. _surfaceChanged = false;
  69. return changed;
  70. }
  71. private static unsafe SwapchainKHR CreateSwapchain(VulkanInstance instance, VulkanDevice device,
  72. VulkanPhysicalDevice physicalDevice, VulkanSurface surface, out Extent2D swapchainExtent,
  73. SwapchainKHR? oldswapchain = null, bool vsyncEnabled = true)
  74. {
  75. if (_swapchainExtension == null)
  76. {
  77. instance.Api.TryGetDeviceExtension(instance.InternalHandle, device.InternalHandle, out _swapchainExtension);
  78. }
  79. while (!surface.CanSurfacePresent(physicalDevice))
  80. {
  81. Thread.Sleep(16);
  82. }
  83. VulkanSurface.SurfaceExtension.GetPhysicalDeviceSurfaceCapabilities(physicalDevice.InternalHandle,
  84. surface.ApiHandle, out var capabilities);
  85. var imageCount = capabilities.MinImageCount + 1;
  86. if (capabilities.MaxImageCount > 0 && imageCount > capabilities.MaxImageCount)
  87. {
  88. imageCount = capabilities.MaxImageCount;
  89. }
  90. var surfaceFormat = surface.GetSurfaceFormat(physicalDevice);
  91. bool supportsIdentityTransform = capabilities.SupportedTransforms.HasFlag(SurfaceTransformFlagsKHR.SurfaceTransformIdentityBitKhr);
  92. bool isRotated = capabilities.CurrentTransform.HasFlag(SurfaceTransformFlagsKHR.SurfaceTransformRotate90BitKhr) ||
  93. capabilities.CurrentTransform.HasFlag(SurfaceTransformFlagsKHR.SurfaceTransformRotate270BitKhr);
  94. swapchainExtent = GetSwapchainExtent(surface, capabilities);
  95. CompositeAlphaFlagsKHR compositeAlphaFlags = GetSuitableCompositeAlphaFlags(capabilities);
  96. PresentModeKHR presentMode = GetSuitablePresentMode(physicalDevice, surface, vsyncEnabled);
  97. var swapchainCreateInfo = new SwapchainCreateInfoKHR
  98. {
  99. SType = StructureType.SwapchainCreateInfoKhr,
  100. Surface = surface.ApiHandle,
  101. MinImageCount = imageCount,
  102. ImageFormat = surfaceFormat.Format,
  103. ImageColorSpace = surfaceFormat.ColorSpace,
  104. ImageExtent = swapchainExtent,
  105. ImageUsage =
  106. ImageUsageFlags.ImageUsageColorAttachmentBit | ImageUsageFlags.ImageUsageTransferDstBit,
  107. ImageSharingMode = SharingMode.Exclusive,
  108. ImageArrayLayers = 1,
  109. PreTransform = supportsIdentityTransform && isRotated ?
  110. SurfaceTransformFlagsKHR.SurfaceTransformIdentityBitKhr :
  111. capabilities.CurrentTransform,
  112. CompositeAlpha = compositeAlphaFlags,
  113. PresentMode = presentMode,
  114. Clipped = true,
  115. OldSwapchain = oldswapchain ?? new SwapchainKHR()
  116. };
  117. _swapchainExtension.CreateSwapchain(device.InternalHandle, swapchainCreateInfo, null, out var swapchain)
  118. .ThrowOnError();
  119. if (oldswapchain != null)
  120. {
  121. _swapchainExtension.DestroySwapchain(device.InternalHandle, oldswapchain.Value, null);
  122. }
  123. return swapchain;
  124. }
  125. private static unsafe Extent2D GetSwapchainExtent(VulkanSurface surface, SurfaceCapabilitiesKHR capabilities)
  126. {
  127. Extent2D swapchainExtent;
  128. if (capabilities.CurrentExtent.Width != uint.MaxValue)
  129. {
  130. swapchainExtent = capabilities.CurrentExtent;
  131. }
  132. else
  133. {
  134. var surfaceSize = surface.SurfaceSize;
  135. var width = Math.Clamp((uint)surfaceSize.Width, capabilities.MinImageExtent.Width, capabilities.MaxImageExtent.Width);
  136. var height = Math.Clamp((uint)surfaceSize.Height, capabilities.MinImageExtent.Height, capabilities.MaxImageExtent.Height);
  137. swapchainExtent = new Extent2D(width, height);
  138. }
  139. return swapchainExtent;
  140. }
  141. private static unsafe CompositeAlphaFlagsKHR GetSuitableCompositeAlphaFlags(SurfaceCapabilitiesKHR capabilities)
  142. {
  143. var compositeAlphaFlags = CompositeAlphaFlagsKHR.CompositeAlphaOpaqueBitKhr;
  144. if (capabilities.SupportedCompositeAlpha.HasFlag(CompositeAlphaFlagsKHR.CompositeAlphaPostMultipliedBitKhr))
  145. {
  146. compositeAlphaFlags = CompositeAlphaFlagsKHR.CompositeAlphaPostMultipliedBitKhr;
  147. }
  148. else if (capabilities.SupportedCompositeAlpha.HasFlag(CompositeAlphaFlagsKHR.CompositeAlphaPreMultipliedBitKhr))
  149. {
  150. compositeAlphaFlags = CompositeAlphaFlagsKHR.CompositeAlphaPreMultipliedBitKhr;
  151. }
  152. return compositeAlphaFlags;
  153. }
  154. private static unsafe PresentModeKHR GetSuitablePresentMode(VulkanPhysicalDevice physicalDevice, VulkanSurface surface, bool vsyncEnabled)
  155. {
  156. uint presentModesCount;
  157. VulkanSurface.SurfaceExtension.GetPhysicalDeviceSurfacePresentModes(physicalDevice.InternalHandle,
  158. surface.ApiHandle,
  159. &presentModesCount, null);
  160. var presentModes = new PresentModeKHR[presentModesCount];
  161. fixed (PresentModeKHR* pPresentModes = presentModes)
  162. {
  163. VulkanSurface.SurfaceExtension.GetPhysicalDeviceSurfacePresentModes(physicalDevice.InternalHandle,
  164. surface.ApiHandle, &presentModesCount, pPresentModes);
  165. }
  166. var modes = presentModes.ToList();
  167. if (!vsyncEnabled && modes.Contains(PresentModeKHR.PresentModeImmediateKhr))
  168. {
  169. return PresentModeKHR.PresentModeImmediateKhr;
  170. }
  171. else if (modes.Contains(PresentModeKHR.PresentModeMailboxKhr))
  172. {
  173. return PresentModeKHR.PresentModeMailboxKhr;
  174. }
  175. else if (modes.Contains(PresentModeKHR.PresentModeFifoKhr))
  176. {
  177. return PresentModeKHR.PresentModeFifoKhr;
  178. }
  179. else
  180. {
  181. return PresentModeKHR.PresentModeImmediateKhr;
  182. }
  183. }
  184. internal static VulkanDisplay CreateDisplay(VulkanInstance instance, VulkanDevice device,
  185. VulkanPhysicalDevice physicalDevice, VulkanSurface surface)
  186. {
  187. var swapchain = CreateSwapchain(instance, device, physicalDevice, surface, out var extent, null, true);
  188. return new VulkanDisplay(instance, device, physicalDevice, surface, swapchain, extent);
  189. }
  190. private unsafe void CreateSwapchainImages()
  191. {
  192. DestroyCurrentImageViews();
  193. Size = new PixelSize((int)_swapchainExtent.Width, (int)_swapchainExtent.Height);
  194. uint imageCount = 0;
  195. _swapchainExtension.GetSwapchainImages(_device.InternalHandle, _swapchain, &imageCount, null);
  196. _swapchainImages = new Image[imageCount];
  197. fixed (Image* pSwapchainImages = _swapchainImages)
  198. {
  199. _swapchainExtension.GetSwapchainImages(_device.InternalHandle, _swapchain, &imageCount, pSwapchainImages);
  200. }
  201. _swapchainImageViews = new ImageView[imageCount];
  202. var surfaceFormat = SurfaceFormat;
  203. for (var i = 0; i < imageCount; i++)
  204. {
  205. _swapchainImageViews[i] = CreateSwapchainImageView(_swapchainImages[i], surfaceFormat.Format);
  206. }
  207. }
  208. private void DestroyCurrentImageViews()
  209. {
  210. for (var i = 0; i < _swapchainImageViews.Length; i++)
  211. {
  212. _instance.Api.DestroyImageView(_device.InternalHandle, _swapchainImageViews[i], Span<AllocationCallbacks>.Empty);
  213. }
  214. }
  215. internal void ChangeVSyncMode(bool vsyncEnabled)
  216. {
  217. _vsyncStateChanged = true;
  218. _vsyncEnabled = vsyncEnabled;
  219. }
  220. private void Recreate()
  221. {
  222. _device.WaitIdle();
  223. _swapchain = CreateSwapchain(_instance, _device, _physicalDevice, _surface, out _swapchainExtent, _swapchain, _vsyncEnabled);
  224. CreateSwapchainImages();
  225. _surfaceChanged = true;
  226. }
  227. private unsafe ImageView CreateSwapchainImageView(Image swapchainImage, Format format)
  228. {
  229. var componentMapping = new ComponentMapping(
  230. ComponentSwizzle.Identity,
  231. ComponentSwizzle.Identity,
  232. ComponentSwizzle.Identity,
  233. ComponentSwizzle.Identity);
  234. var subresourceRange = new ImageSubresourceRange(ImageAspectFlags.ImageAspectColorBit, 0, 1, 0, 1);
  235. var imageCreateInfo = new ImageViewCreateInfo
  236. {
  237. SType = StructureType.ImageViewCreateInfo,
  238. Image = swapchainImage,
  239. ViewType = ImageViewType.ImageViewType2D,
  240. Format = format,
  241. Components = componentMapping,
  242. SubresourceRange = subresourceRange
  243. };
  244. _instance.Api.CreateImageView(_device.InternalHandle, imageCreateInfo, null, out var imageView).ThrowOnError();
  245. return imageView;
  246. }
  247. public bool EnsureSwapchainAvailable()
  248. {
  249. if (Size != _surface.SurfaceSize || _vsyncStateChanged)
  250. {
  251. _vsyncStateChanged = false;
  252. Recreate();
  253. return false;
  254. }
  255. return true;
  256. }
  257. internal VulkanCommandBufferPool.VulkanCommandBuffer StartPresentation()
  258. {
  259. _nextImage = 0;
  260. while (true)
  261. {
  262. var acquireResult = _swapchainExtension.AcquireNextImage(
  263. _device.InternalHandle,
  264. _swapchain,
  265. ulong.MaxValue,
  266. _semaphorePair.ImageAvailableSemaphore,
  267. new Fence(),
  268. ref _nextImage);
  269. if (acquireResult == Result.ErrorOutOfDateKhr ||
  270. acquireResult == Result.SuboptimalKhr)
  271. {
  272. Recreate();
  273. }
  274. else
  275. {
  276. acquireResult.ThrowOnError();
  277. break;
  278. }
  279. }
  280. var commandBuffer = CommandBufferPool.CreateCommandBuffer();
  281. commandBuffer.BeginRecording();
  282. VulkanMemoryHelper.TransitionLayout(_device, commandBuffer.InternalHandle,
  283. _swapchainImages[_nextImage], ImageLayout.Undefined,
  284. AccessFlags.AccessNoneKhr,
  285. ImageLayout.TransferDstOptimal,
  286. AccessFlags.AccessTransferWriteBit,
  287. 1);
  288. return commandBuffer;
  289. }
  290. internal void BlitImageToCurrentImage(VulkanSurfaceRenderTarget renderTarget, CommandBuffer commandBuffer)
  291. {
  292. var image = renderTarget.GetImage();
  293. VulkanMemoryHelper.TransitionLayout(_device, commandBuffer,
  294. image.InternalHandle.Value, (ImageLayout)image.CurrentLayout,
  295. AccessFlags.AccessNoneKhr,
  296. ImageLayout.TransferSrcOptimal,
  297. AccessFlags.AccessTransferReadBit,
  298. renderTarget.MipLevels);
  299. var srcBlitRegion = new ImageBlit
  300. {
  301. SrcOffsets = new ImageBlit.SrcOffsetsBuffer
  302. {
  303. Element0 = new Offset3D(0, 0, 0),
  304. Element1 = new Offset3D(renderTarget.Size.Width, renderTarget.Size.Height, 1),
  305. },
  306. DstOffsets = new ImageBlit.DstOffsetsBuffer
  307. {
  308. Element0 = new Offset3D(0, 0, 0),
  309. Element1 = new Offset3D(Size.Width, Size.Height, 1),
  310. },
  311. SrcSubresource = new ImageSubresourceLayers
  312. {
  313. AspectMask = ImageAspectFlags.ImageAspectColorBit,
  314. BaseArrayLayer = 0,
  315. LayerCount = 1,
  316. MipLevel = 0
  317. },
  318. DstSubresource = new ImageSubresourceLayers
  319. {
  320. AspectMask = ImageAspectFlags.ImageAspectColorBit,
  321. BaseArrayLayer = 0,
  322. LayerCount = 1,
  323. MipLevel = 0
  324. }
  325. };
  326. _device.Api.CmdBlitImage(commandBuffer, image.InternalHandle.Value,
  327. ImageLayout.TransferSrcOptimal,
  328. _swapchainImages[_nextImage],
  329. ImageLayout.TransferDstOptimal,
  330. 1,
  331. srcBlitRegion,
  332. Filter.Linear);
  333. VulkanMemoryHelper.TransitionLayout(_device, commandBuffer,
  334. image.InternalHandle.Value, ImageLayout.TransferSrcOptimal,
  335. AccessFlags.AccessTransferReadBit,
  336. (ImageLayout)image.CurrentLayout,
  337. AccessFlags.AccessNoneKhr,
  338. renderTarget.MipLevels);
  339. }
  340. internal unsafe void EndPresentation(VulkanCommandBufferPool.VulkanCommandBuffer commandBuffer)
  341. {
  342. VulkanMemoryHelper.TransitionLayout(_device, commandBuffer.InternalHandle,
  343. _swapchainImages[_nextImage], ImageLayout.TransferDstOptimal,
  344. AccessFlags.AccessNoneKhr,
  345. ImageLayout.PresentSrcKhr,
  346. AccessFlags.AccessNoneKhr,
  347. 1);
  348. commandBuffer.Submit(
  349. stackalloc[] { _semaphorePair.ImageAvailableSemaphore },
  350. stackalloc[] { PipelineStageFlags.PipelineStageColorAttachmentOutputBit },
  351. stackalloc[] { _semaphorePair.RenderFinishedSemaphore });
  352. var semaphore = _semaphorePair.RenderFinishedSemaphore;
  353. var swapchain = _swapchain;
  354. var nextImage = _nextImage;
  355. Result result;
  356. var presentInfo = new PresentInfoKHR
  357. {
  358. SType = StructureType.PresentInfoKhr,
  359. WaitSemaphoreCount = 1,
  360. PWaitSemaphores = &semaphore,
  361. SwapchainCount = 1,
  362. PSwapchains = &swapchain,
  363. PImageIndices = &nextImage,
  364. PResults = &result
  365. };
  366. lock (_device.Lock)
  367. {
  368. _swapchainExtension.QueuePresent(_device.PresentQueue.InternalHandle, presentInfo);
  369. }
  370. CommandBufferPool.FreeUsedCommandBuffers();
  371. Presented?.Invoke(this, null);
  372. }
  373. }
  374. }