Window.cs 20 KB

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