Window.cs 20 KB

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