Window.cs 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655
  1. using Ryujinx.Graphics.GAL;
  2. using Ryujinx.Graphics.Vulkan.Effects;
  3. using Silk.NET.Vulkan;
  4. using Silk.NET.Vulkan.Extensions.KHR;
  5. using System;
  6. using System.Linq;
  7. using VkFormat = Silk.NET.Vulkan.Format;
  8. namespace Ryujinx.Graphics.Vulkan
  9. {
  10. class Window : WindowBase, IDisposable
  11. {
  12. private const int SurfaceWidth = 1280;
  13. private const int SurfaceHeight = 720;
  14. private readonly VulkanRenderer _gd;
  15. private readonly SurfaceKHR _surface;
  16. private readonly PhysicalDevice _physicalDevice;
  17. private readonly Device _device;
  18. private SwapchainKHR _swapchain;
  19. private Image[] _swapchainImages;
  20. private Auto<DisposableImageView>[] _swapchainImageViews;
  21. private Semaphore[] _imageAvailableSemaphores;
  22. private Semaphore[] _renderFinishedSemaphores;
  23. private int _frameIndex;
  24. private int _width;
  25. private int _height;
  26. private bool _vsyncEnabled;
  27. private bool _swapchainIsDirty;
  28. private VkFormat _format;
  29. private AntiAliasing _currentAntiAliasing;
  30. private bool _updateEffect;
  31. private IPostProcessingEffect _effect;
  32. private IScalingFilter _scalingFilter;
  33. private bool _isLinear;
  34. private float _scalingFilterLevel;
  35. private bool _updateScalingFilter;
  36. private ScalingFilter _currentScalingFilter;
  37. private bool _colorSpacePassthroughEnabled;
  38. public unsafe Window(VulkanRenderer gd, SurfaceKHR surface, PhysicalDevice physicalDevice, Device device)
  39. {
  40. _gd = gd;
  41. _physicalDevice = physicalDevice;
  42. _device = device;
  43. _surface = surface;
  44. CreateSwapchain();
  45. }
  46. private void RecreateSwapchain()
  47. {
  48. var oldSwapchain = _swapchain;
  49. _swapchainIsDirty = false;
  50. for (int i = 0; i < _swapchainImageViews.Length; i++)
  51. {
  52. _swapchainImageViews[i].Dispose();
  53. }
  54. // Destroy old Swapchain.
  55. _gd.Api.DeviceWaitIdle(_device);
  56. unsafe
  57. {
  58. for (int i = 0; i < _imageAvailableSemaphores.Length; i++)
  59. {
  60. _gd.Api.DestroySemaphore(_device, _imageAvailableSemaphores[i], null);
  61. }
  62. for (int i = 0; i < _renderFinishedSemaphores.Length; i++)
  63. {
  64. _gd.Api.DestroySemaphore(_device, _renderFinishedSemaphores[i], null);
  65. }
  66. }
  67. _gd.SwapchainApi.DestroySwapchain(_device, oldSwapchain, Span<AllocationCallbacks>.Empty);
  68. CreateSwapchain();
  69. }
  70. private unsafe void CreateSwapchain()
  71. {
  72. _gd.SurfaceApi.GetPhysicalDeviceSurfaceCapabilities(_physicalDevice, _surface, out var capabilities);
  73. uint surfaceFormatsCount;
  74. _gd.SurfaceApi.GetPhysicalDeviceSurfaceFormats(_physicalDevice, _surface, &surfaceFormatsCount, null);
  75. var surfaceFormats = new SurfaceFormatKHR[surfaceFormatsCount];
  76. fixed (SurfaceFormatKHR* pSurfaceFormats = surfaceFormats)
  77. {
  78. _gd.SurfaceApi.GetPhysicalDeviceSurfaceFormats(_physicalDevice, _surface, &surfaceFormatsCount, pSurfaceFormats);
  79. }
  80. uint presentModesCount;
  81. _gd.SurfaceApi.GetPhysicalDeviceSurfacePresentModes(_physicalDevice, _surface, &presentModesCount, null);
  82. var presentModes = new PresentModeKHR[presentModesCount];
  83. fixed (PresentModeKHR* pPresentModes = presentModes)
  84. {
  85. _gd.SurfaceApi.GetPhysicalDeviceSurfacePresentModes(_physicalDevice, _surface, &presentModesCount, pPresentModes);
  86. }
  87. uint imageCount = capabilities.MinImageCount + 1;
  88. if (capabilities.MaxImageCount > 0 && imageCount > capabilities.MaxImageCount)
  89. {
  90. imageCount = capabilities.MaxImageCount;
  91. }
  92. var surfaceFormat = ChooseSwapSurfaceFormat(surfaceFormats, _colorSpacePassthroughEnabled);
  93. var extent = ChooseSwapExtent(capabilities);
  94. _width = (int)extent.Width;
  95. _height = (int)extent.Height;
  96. _format = surfaceFormat.Format;
  97. var oldSwapchain = _swapchain;
  98. var swapchainCreateInfo = new SwapchainCreateInfoKHR
  99. {
  100. SType = StructureType.SwapchainCreateInfoKhr,
  101. Surface = _surface,
  102. MinImageCount = imageCount,
  103. ImageFormat = surfaceFormat.Format,
  104. ImageColorSpace = surfaceFormat.ColorSpace,
  105. ImageExtent = extent,
  106. ImageUsage = ImageUsageFlags.ColorAttachmentBit | ImageUsageFlags.TransferDstBit | ImageUsageFlags.StorageBit,
  107. ImageSharingMode = SharingMode.Exclusive,
  108. ImageArrayLayers = 1,
  109. PreTransform = capabilities.CurrentTransform,
  110. CompositeAlpha = ChooseCompositeAlpha(capabilities.SupportedCompositeAlpha),
  111. PresentMode = ChooseSwapPresentMode(presentModes, _vsyncEnabled),
  112. Clipped = true,
  113. };
  114. _gd.SwapchainApi.CreateSwapchain(_device, swapchainCreateInfo, null, out _swapchain).ThrowOnError();
  115. _gd.SwapchainApi.GetSwapchainImages(_device, _swapchain, &imageCount, null);
  116. _swapchainImages = new Image[imageCount];
  117. fixed (Image* pSwapchainImages = _swapchainImages)
  118. {
  119. _gd.SwapchainApi.GetSwapchainImages(_device, _swapchain, &imageCount, pSwapchainImages);
  120. }
  121. _swapchainImageViews = new Auto<DisposableImageView>[imageCount];
  122. for (int i = 0; i < _swapchainImageViews.Length; i++)
  123. {
  124. _swapchainImageViews[i] = CreateSwapchainImageView(_swapchainImages[i], surfaceFormat.Format);
  125. }
  126. var semaphoreCreateInfo = new SemaphoreCreateInfo
  127. {
  128. SType = StructureType.SemaphoreCreateInfo,
  129. };
  130. _imageAvailableSemaphores = new Semaphore[imageCount];
  131. for (int i = 0; i < _imageAvailableSemaphores.Length; i++)
  132. {
  133. _gd.Api.CreateSemaphore(_device, semaphoreCreateInfo, null, out _imageAvailableSemaphores[i]).ThrowOnError();
  134. }
  135. _renderFinishedSemaphores = new Semaphore[imageCount];
  136. for (int i = 0; i < _renderFinishedSemaphores.Length; i++)
  137. {
  138. _gd.Api.CreateSemaphore(_device, semaphoreCreateInfo, null, out _renderFinishedSemaphores[i]).ThrowOnError();
  139. }
  140. }
  141. private unsafe Auto<DisposableImageView> CreateSwapchainImageView(Image swapchainImage, VkFormat format)
  142. {
  143. var componentMapping = new ComponentMapping(
  144. ComponentSwizzle.R,
  145. ComponentSwizzle.G,
  146. ComponentSwizzle.B,
  147. ComponentSwizzle.A);
  148. var aspectFlags = ImageAspectFlags.ColorBit;
  149. var subresourceRange = new ImageSubresourceRange(aspectFlags, 0, 1, 0, 1);
  150. var imageCreateInfo = new ImageViewCreateInfo
  151. {
  152. SType = StructureType.ImageViewCreateInfo,
  153. Image = swapchainImage,
  154. ViewType = ImageViewType.Type2D,
  155. Format = format,
  156. Components = componentMapping,
  157. SubresourceRange = subresourceRange,
  158. };
  159. _gd.Api.CreateImageView(_device, imageCreateInfo, null, out var imageView).ThrowOnError();
  160. return new Auto<DisposableImageView>(new DisposableImageView(_gd.Api, _device, imageView));
  161. }
  162. private static SurfaceFormatKHR ChooseSwapSurfaceFormat(SurfaceFormatKHR[] availableFormats, bool colorSpacePassthroughEnabled)
  163. {
  164. if (availableFormats.Length == 1 && availableFormats[0].Format == VkFormat.Undefined)
  165. {
  166. return new SurfaceFormatKHR(VkFormat.B8G8R8A8Unorm, ColorSpaceKHR.PaceSrgbNonlinearKhr);
  167. }
  168. var formatToReturn = availableFormats[0];
  169. if (colorSpacePassthroughEnabled)
  170. {
  171. foreach (var format in availableFormats)
  172. {
  173. if (format.Format == VkFormat.B8G8R8A8Unorm && format.ColorSpace == ColorSpaceKHR.SpacePassThroughExt)
  174. {
  175. formatToReturn = format;
  176. break;
  177. }
  178. else if (format.Format == VkFormat.B8G8R8A8Unorm && format.ColorSpace == ColorSpaceKHR.PaceSrgbNonlinearKhr)
  179. {
  180. formatToReturn = format;
  181. }
  182. }
  183. }
  184. else
  185. {
  186. foreach (var format in availableFormats)
  187. {
  188. if (format.Format == VkFormat.B8G8R8A8Unorm && format.ColorSpace == ColorSpaceKHR.PaceSrgbNonlinearKhr)
  189. {
  190. formatToReturn = format;
  191. break;
  192. }
  193. }
  194. }
  195. return formatToReturn;
  196. }
  197. private static CompositeAlphaFlagsKHR ChooseCompositeAlpha(CompositeAlphaFlagsKHR supportedFlags)
  198. {
  199. if (supportedFlags.HasFlag(CompositeAlphaFlagsKHR.OpaqueBitKhr))
  200. {
  201. return CompositeAlphaFlagsKHR.OpaqueBitKhr;
  202. }
  203. else if (supportedFlags.HasFlag(CompositeAlphaFlagsKHR.PreMultipliedBitKhr))
  204. {
  205. return CompositeAlphaFlagsKHR.PreMultipliedBitKhr;
  206. }
  207. else
  208. {
  209. return CompositeAlphaFlagsKHR.InheritBitKhr;
  210. }
  211. }
  212. private static PresentModeKHR ChooseSwapPresentMode(PresentModeKHR[] availablePresentModes, bool vsyncEnabled)
  213. {
  214. if (!vsyncEnabled && availablePresentModes.Contains(PresentModeKHR.ImmediateKhr))
  215. {
  216. return PresentModeKHR.ImmediateKhr;
  217. }
  218. else if (availablePresentModes.Contains(PresentModeKHR.MailboxKhr))
  219. {
  220. return PresentModeKHR.MailboxKhr;
  221. }
  222. else
  223. {
  224. return PresentModeKHR.FifoKhr;
  225. }
  226. }
  227. public static Extent2D ChooseSwapExtent(SurfaceCapabilitiesKHR capabilities)
  228. {
  229. if (capabilities.CurrentExtent.Width != uint.MaxValue)
  230. {
  231. return capabilities.CurrentExtent;
  232. }
  233. uint width = Math.Max(capabilities.MinImageExtent.Width, Math.Min(capabilities.MaxImageExtent.Width, SurfaceWidth));
  234. uint height = Math.Max(capabilities.MinImageExtent.Height, Math.Min(capabilities.MaxImageExtent.Height, SurfaceHeight));
  235. return new Extent2D(width, height);
  236. }
  237. public unsafe override void Present(ITexture texture, ImageCrop crop, Action swapBuffersCallback)
  238. {
  239. _gd.PipelineInternal.AutoFlush.Present();
  240. uint nextImage = 0;
  241. int semaphoreIndex = _frameIndex++ % _imageAvailableSemaphores.Length;
  242. while (true)
  243. {
  244. var acquireResult = _gd.SwapchainApi.AcquireNextImage(
  245. _device,
  246. _swapchain,
  247. ulong.MaxValue,
  248. _imageAvailableSemaphores[semaphoreIndex],
  249. new Fence(),
  250. ref nextImage);
  251. if (acquireResult == Result.ErrorOutOfDateKhr ||
  252. acquireResult == Result.SuboptimalKhr ||
  253. _swapchainIsDirty)
  254. {
  255. RecreateSwapchain();
  256. }
  257. else
  258. {
  259. acquireResult.ThrowOnError();
  260. break;
  261. }
  262. }
  263. var swapchainImage = _swapchainImages[nextImage];
  264. _gd.FlushAllCommands();
  265. var cbs = _gd.CommandBufferPool.Rent();
  266. Transition(
  267. cbs.CommandBuffer,
  268. swapchainImage,
  269. 0,
  270. AccessFlags.TransferWriteBit,
  271. ImageLayout.Undefined,
  272. ImageLayout.General);
  273. var view = (TextureView)texture;
  274. UpdateEffect();
  275. if (_effect != null)
  276. {
  277. view = _effect.Run(view, cbs, _width, _height);
  278. }
  279. int srcX0, srcX1, srcY0, srcY1;
  280. if (crop.Left == 0 && crop.Right == 0)
  281. {
  282. srcX0 = 0;
  283. srcX1 = view.Width;
  284. }
  285. else
  286. {
  287. srcX0 = crop.Left;
  288. srcX1 = crop.Right;
  289. }
  290. if (crop.Top == 0 && crop.Bottom == 0)
  291. {
  292. srcY0 = 0;
  293. srcY1 = view.Height;
  294. }
  295. else
  296. {
  297. srcY0 = crop.Top;
  298. srcY1 = crop.Bottom;
  299. }
  300. if (ScreenCaptureRequested)
  301. {
  302. if (_effect != null)
  303. {
  304. _gd.CommandBufferPool.Return(
  305. cbs,
  306. null,
  307. stackalloc[] { PipelineStageFlags.ColorAttachmentOutputBit },
  308. null);
  309. _gd.FlushAllCommands();
  310. cbs.GetFence().Wait();
  311. cbs = _gd.CommandBufferPool.Rent();
  312. }
  313. CaptureFrame(view, srcX0, srcY0, srcX1 - srcX0, srcY1 - srcY0, view.Info.Format.IsBgr(), crop.FlipX, crop.FlipY);
  314. ScreenCaptureRequested = false;
  315. }
  316. float ratioX = crop.IsStretched ? 1.0f : MathF.Min(1.0f, _height * crop.AspectRatioX / (_width * crop.AspectRatioY));
  317. float ratioY = crop.IsStretched ? 1.0f : MathF.Min(1.0f, _width * crop.AspectRatioY / (_height * crop.AspectRatioX));
  318. int dstWidth = (int)(_width * ratioX);
  319. int dstHeight = (int)(_height * ratioY);
  320. int dstPaddingX = (_width - dstWidth) / 2;
  321. int dstPaddingY = (_height - dstHeight) / 2;
  322. int dstX0 = crop.FlipX ? _width - dstPaddingX : dstPaddingX;
  323. int dstX1 = crop.FlipX ? dstPaddingX : _width - dstPaddingX;
  324. int dstY0 = crop.FlipY ? dstPaddingY : _height - dstPaddingY;
  325. int dstY1 = crop.FlipY ? _height - dstPaddingY : dstPaddingY;
  326. if (_scalingFilter != null)
  327. {
  328. _scalingFilter.Run(
  329. view,
  330. cbs,
  331. _swapchainImageViews[nextImage],
  332. _format,
  333. _width,
  334. _height,
  335. new Extents2D(srcX0, srcY0, srcX1, srcY1),
  336. new Extents2D(dstX0, dstY0, dstX1, dstY1)
  337. );
  338. }
  339. else
  340. {
  341. _gd.HelperShader.BlitColor(
  342. _gd,
  343. cbs,
  344. view,
  345. _swapchainImageViews[nextImage],
  346. _width,
  347. _height,
  348. 1,
  349. _format,
  350. false,
  351. new Extents2D(srcX0, srcY0, srcX1, srcY1),
  352. new Extents2D(dstX0, dstY1, dstX1, dstY0),
  353. _isLinear,
  354. true);
  355. }
  356. Transition(
  357. cbs.CommandBuffer,
  358. swapchainImage,
  359. 0,
  360. 0,
  361. ImageLayout.General,
  362. ImageLayout.PresentSrcKhr);
  363. _gd.CommandBufferPool.Return(
  364. cbs,
  365. stackalloc[] { _imageAvailableSemaphores[semaphoreIndex] },
  366. stackalloc[] { PipelineStageFlags.ColorAttachmentOutputBit },
  367. stackalloc[] { _renderFinishedSemaphores[semaphoreIndex] });
  368. // TODO: Present queue.
  369. var semaphore = _renderFinishedSemaphores[semaphoreIndex];
  370. var swapchain = _swapchain;
  371. Result result;
  372. var presentInfo = new PresentInfoKHR
  373. {
  374. SType = StructureType.PresentInfoKhr,
  375. WaitSemaphoreCount = 1,
  376. PWaitSemaphores = &semaphore,
  377. SwapchainCount = 1,
  378. PSwapchains = &swapchain,
  379. PImageIndices = &nextImage,
  380. PResults = &result,
  381. };
  382. lock (_gd.QueueLock)
  383. {
  384. _gd.SwapchainApi.QueuePresent(_gd.Queue, presentInfo);
  385. }
  386. }
  387. public override void SetAntiAliasing(AntiAliasing effect)
  388. {
  389. if (_currentAntiAliasing == effect && _effect != null)
  390. {
  391. return;
  392. }
  393. _currentAntiAliasing = effect;
  394. _updateEffect = true;
  395. }
  396. public override void SetScalingFilter(ScalingFilter type)
  397. {
  398. if (_currentScalingFilter == type && _effect != null)
  399. {
  400. return;
  401. }
  402. _currentScalingFilter = type;
  403. _updateScalingFilter = true;
  404. }
  405. public override void SetColorSpacePassthrough(bool colorSpacePassthroughEnabled)
  406. {
  407. _colorSpacePassthroughEnabled = colorSpacePassthroughEnabled;
  408. _swapchainIsDirty = true;
  409. }
  410. private void UpdateEffect()
  411. {
  412. if (_updateEffect)
  413. {
  414. _updateEffect = false;
  415. switch (_currentAntiAliasing)
  416. {
  417. case AntiAliasing.Fxaa:
  418. _effect?.Dispose();
  419. _effect = new FxaaPostProcessingEffect(_gd, _device);
  420. break;
  421. case AntiAliasing.None:
  422. _effect?.Dispose();
  423. _effect = null;
  424. break;
  425. case AntiAliasing.SmaaLow:
  426. case AntiAliasing.SmaaMedium:
  427. case AntiAliasing.SmaaHigh:
  428. case AntiAliasing.SmaaUltra:
  429. var quality = _currentAntiAliasing - AntiAliasing.SmaaLow;
  430. if (_effect is SmaaPostProcessingEffect smaa)
  431. {
  432. smaa.Quality = quality;
  433. }
  434. else
  435. {
  436. _effect?.Dispose();
  437. _effect = new SmaaPostProcessingEffect(_gd, _device, quality);
  438. }
  439. break;
  440. }
  441. }
  442. if (_updateScalingFilter)
  443. {
  444. _updateScalingFilter = false;
  445. switch (_currentScalingFilter)
  446. {
  447. case ScalingFilter.Bilinear:
  448. case ScalingFilter.Nearest:
  449. _scalingFilter?.Dispose();
  450. _scalingFilter = null;
  451. _isLinear = _currentScalingFilter == ScalingFilter.Bilinear;
  452. break;
  453. case ScalingFilter.Fsr:
  454. if (_scalingFilter is not FsrScalingFilter)
  455. {
  456. _scalingFilter?.Dispose();
  457. _scalingFilter = new FsrScalingFilter(_gd, _device);
  458. }
  459. _scalingFilter.Level = _scalingFilterLevel;
  460. break;
  461. }
  462. }
  463. }
  464. public override void SetScalingFilterLevel(float level)
  465. {
  466. _scalingFilterLevel = level;
  467. _updateScalingFilter = true;
  468. }
  469. private unsafe void Transition(
  470. CommandBuffer commandBuffer,
  471. Image image,
  472. AccessFlags srcAccess,
  473. AccessFlags dstAccess,
  474. ImageLayout srcLayout,
  475. ImageLayout dstLayout)
  476. {
  477. var subresourceRange = new ImageSubresourceRange(ImageAspectFlags.ColorBit, 0, 1, 0, 1);
  478. var barrier = new ImageMemoryBarrier
  479. {
  480. SType = StructureType.ImageMemoryBarrier,
  481. SrcAccessMask = srcAccess,
  482. DstAccessMask = dstAccess,
  483. OldLayout = srcLayout,
  484. NewLayout = dstLayout,
  485. SrcQueueFamilyIndex = Vk.QueueFamilyIgnored,
  486. DstQueueFamilyIndex = Vk.QueueFamilyIgnored,
  487. Image = image,
  488. SubresourceRange = subresourceRange,
  489. };
  490. _gd.Api.CmdPipelineBarrier(
  491. commandBuffer,
  492. PipelineStageFlags.TopOfPipeBit,
  493. PipelineStageFlags.AllCommandsBit,
  494. 0,
  495. 0,
  496. null,
  497. 0,
  498. null,
  499. 1,
  500. barrier);
  501. }
  502. private void CaptureFrame(TextureView texture, int x, int y, int width, int height, bool isBgra, bool flipX, bool flipY)
  503. {
  504. byte[] bitmap = texture.GetData(x, y, width, height);
  505. _gd.OnScreenCaptured(new ScreenCaptureImageInfo(width, height, isBgra, bitmap, flipX, flipY));
  506. }
  507. public override void SetSize(int width, int height)
  508. {
  509. // Not needed as we can get the size from the surface.
  510. }
  511. public override void ChangeVSyncMode(bool vsyncEnabled)
  512. {
  513. _vsyncEnabled = vsyncEnabled;
  514. _swapchainIsDirty = true;
  515. }
  516. protected virtual void Dispose(bool disposing)
  517. {
  518. if (disposing)
  519. {
  520. unsafe
  521. {
  522. for (int i = 0; i < _swapchainImageViews.Length; i++)
  523. {
  524. _swapchainImageViews[i].Dispose();
  525. }
  526. for (int i = 0; i < _imageAvailableSemaphores.Length; i++)
  527. {
  528. _gd.Api.DestroySemaphore(_device, _imageAvailableSemaphores[i], null);
  529. }
  530. for (int i = 0; i < _renderFinishedSemaphores.Length; i++)
  531. {
  532. _gd.Api.DestroySemaphore(_device, _renderFinishedSemaphores[i], null);
  533. }
  534. _gd.SwapchainApi.DestroySwapchain(_device, _swapchain, null);
  535. }
  536. _effect?.Dispose();
  537. _scalingFilter?.Dispose();
  538. }
  539. }
  540. public override void Dispose()
  541. {
  542. Dispose(true);
  543. }
  544. }
  545. }