VulkanDisplay.cs 17 KB

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