Window.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469
  1. using Ryujinx.Graphics.GAL;
  2. using Silk.NET.Vulkan;
  3. using Silk.NET.Vulkan.Extensions.KHR;
  4. using System;
  5. using System.Linq;
  6. using VkFormat = Silk.NET.Vulkan.Format;
  7. namespace Ryujinx.Graphics.Vulkan
  8. {
  9. class Window : WindowBase, IDisposable
  10. {
  11. private const int SurfaceWidth = 1280;
  12. private const int SurfaceHeight = 720;
  13. private readonly VulkanRenderer _gd;
  14. private readonly SurfaceKHR _surface;
  15. private readonly PhysicalDevice _physicalDevice;
  16. private readonly Device _device;
  17. private SwapchainKHR _swapchain;
  18. private Image[] _swapchainImages;
  19. private Auto<DisposableImageView>[] _swapchainImageViews;
  20. private Semaphore _imageAvailableSemaphore;
  21. private Semaphore _renderFinishedSemaphore;
  22. private int _width;
  23. private int _height;
  24. private bool _vsyncEnabled;
  25. private bool _vsyncModeChanged;
  26. private VkFormat _format;
  27. public unsafe Window(VulkanRenderer gd, SurfaceKHR surface, PhysicalDevice physicalDevice, Device device)
  28. {
  29. _gd = gd;
  30. _physicalDevice = physicalDevice;
  31. _device = device;
  32. _surface = surface;
  33. CreateSwapchain();
  34. var semaphoreCreateInfo = new SemaphoreCreateInfo()
  35. {
  36. SType = StructureType.SemaphoreCreateInfo
  37. };
  38. gd.Api.CreateSemaphore(device, semaphoreCreateInfo, null, out _imageAvailableSemaphore).ThrowOnError();
  39. gd.Api.CreateSemaphore(device, semaphoreCreateInfo, null, out _renderFinishedSemaphore).ThrowOnError();
  40. }
  41. private void RecreateSwapchain()
  42. {
  43. var oldSwapchain = _swapchain;
  44. int imageCount = _swapchainImageViews.Length;
  45. _vsyncModeChanged = false;
  46. for (int i = 0; i < imageCount; i++)
  47. {
  48. _swapchainImageViews[i].Dispose();
  49. }
  50. // Destroy old Swapchain.
  51. _gd.Api.DeviceWaitIdle(_device);
  52. _gd.SwapchainApi.DestroySwapchain(_device, oldSwapchain, Span<AllocationCallbacks>.Empty);
  53. CreateSwapchain();
  54. }
  55. private unsafe void CreateSwapchain()
  56. {
  57. _gd.SurfaceApi.GetPhysicalDeviceSurfaceCapabilities(_physicalDevice, _surface, out var capabilities);
  58. uint surfaceFormatsCount;
  59. _gd.SurfaceApi.GetPhysicalDeviceSurfaceFormats(_physicalDevice, _surface, &surfaceFormatsCount, null);
  60. var surfaceFormats = new SurfaceFormatKHR[surfaceFormatsCount];
  61. fixed (SurfaceFormatKHR* pSurfaceFormats = surfaceFormats)
  62. {
  63. _gd.SurfaceApi.GetPhysicalDeviceSurfaceFormats(_physicalDevice, _surface, &surfaceFormatsCount, pSurfaceFormats);
  64. }
  65. uint presentModesCount;
  66. _gd.SurfaceApi.GetPhysicalDeviceSurfacePresentModes(_physicalDevice, _surface, &presentModesCount, null);
  67. var presentModes = new PresentModeKHR[presentModesCount];
  68. fixed (PresentModeKHR* pPresentModes = presentModes)
  69. {
  70. _gd.SurfaceApi.GetPhysicalDeviceSurfacePresentModes(_physicalDevice, _surface, &presentModesCount, pPresentModes);
  71. }
  72. uint imageCount = capabilities.MinImageCount + 1;
  73. if (capabilities.MaxImageCount > 0 && imageCount > capabilities.MaxImageCount)
  74. {
  75. imageCount = capabilities.MaxImageCount;
  76. }
  77. var surfaceFormat = ChooseSwapSurfaceFormat(surfaceFormats);
  78. var extent = ChooseSwapExtent(capabilities);
  79. _width = (int)extent.Width;
  80. _height = (int)extent.Height;
  81. _format = surfaceFormat.Format;
  82. var oldSwapchain = _swapchain;
  83. var swapchainCreateInfo = new SwapchainCreateInfoKHR()
  84. {
  85. SType = StructureType.SwapchainCreateInfoKhr,
  86. Surface = _surface,
  87. MinImageCount = imageCount,
  88. ImageFormat = surfaceFormat.Format,
  89. ImageColorSpace = surfaceFormat.ColorSpace,
  90. ImageExtent = extent,
  91. ImageUsage = ImageUsageFlags.ColorAttachmentBit | ImageUsageFlags.TransferDstBit,
  92. ImageSharingMode = SharingMode.Exclusive,
  93. ImageArrayLayers = 1,
  94. PreTransform = capabilities.CurrentTransform,
  95. CompositeAlpha = ChooseCompositeAlpha(capabilities.SupportedCompositeAlpha),
  96. PresentMode = ChooseSwapPresentMode(presentModes, _vsyncEnabled),
  97. Clipped = true
  98. };
  99. _gd.SwapchainApi.CreateSwapchain(_device, swapchainCreateInfo, null, out _swapchain).ThrowOnError();
  100. _gd.SwapchainApi.GetSwapchainImages(_device, _swapchain, &imageCount, null);
  101. _swapchainImages = new Image[imageCount];
  102. fixed (Image* pSwapchainImages = _swapchainImages)
  103. {
  104. _gd.SwapchainApi.GetSwapchainImages(_device, _swapchain, &imageCount, pSwapchainImages);
  105. }
  106. _swapchainImageViews = new Auto<DisposableImageView>[imageCount];
  107. for (int i = 0; i < imageCount; i++)
  108. {
  109. _swapchainImageViews[i] = CreateSwapchainImageView(_swapchainImages[i], surfaceFormat.Format);
  110. }
  111. }
  112. private unsafe Auto<DisposableImageView> CreateSwapchainImageView(Image swapchainImage, VkFormat format)
  113. {
  114. var componentMapping = new ComponentMapping(
  115. ComponentSwizzle.R,
  116. ComponentSwizzle.G,
  117. ComponentSwizzle.B,
  118. ComponentSwizzle.A);
  119. var aspectFlags = ImageAspectFlags.ColorBit;
  120. var subresourceRange = new ImageSubresourceRange(aspectFlags, 0, 1, 0, 1);
  121. var imageCreateInfo = new ImageViewCreateInfo()
  122. {
  123. SType = StructureType.ImageViewCreateInfo,
  124. Image = swapchainImage,
  125. ViewType = ImageViewType.Type2D,
  126. Format = format,
  127. Components = componentMapping,
  128. SubresourceRange = subresourceRange
  129. };
  130. _gd.Api.CreateImageView(_device, imageCreateInfo, null, out var imageView).ThrowOnError();
  131. return new Auto<DisposableImageView>(new DisposableImageView(_gd.Api, _device, imageView));
  132. }
  133. private static SurfaceFormatKHR ChooseSwapSurfaceFormat(SurfaceFormatKHR[] availableFormats)
  134. {
  135. if (availableFormats.Length == 1 && availableFormats[0].Format == VkFormat.Undefined)
  136. {
  137. return new SurfaceFormatKHR(VkFormat.B8G8R8A8Unorm, ColorSpaceKHR.PaceSrgbNonlinearKhr);
  138. }
  139. foreach (var format in availableFormats)
  140. {
  141. if (format.Format == VkFormat.B8G8R8A8Unorm && format.ColorSpace == ColorSpaceKHR.PaceSrgbNonlinearKhr)
  142. {
  143. return format;
  144. }
  145. }
  146. return availableFormats[0];
  147. }
  148. private static CompositeAlphaFlagsKHR ChooseCompositeAlpha(CompositeAlphaFlagsKHR supportedFlags)
  149. {
  150. if (supportedFlags.HasFlag(CompositeAlphaFlagsKHR.OpaqueBitKhr))
  151. {
  152. return CompositeAlphaFlagsKHR.OpaqueBitKhr;
  153. }
  154. else if (supportedFlags.HasFlag(CompositeAlphaFlagsKHR.PreMultipliedBitKhr))
  155. {
  156. return CompositeAlphaFlagsKHR.PreMultipliedBitKhr;
  157. }
  158. else
  159. {
  160. return CompositeAlphaFlagsKHR.InheritBitKhr;
  161. }
  162. }
  163. private static PresentModeKHR ChooseSwapPresentMode(PresentModeKHR[] availablePresentModes, bool vsyncEnabled)
  164. {
  165. if (!vsyncEnabled && availablePresentModes.Contains(PresentModeKHR.ImmediateKhr))
  166. {
  167. return PresentModeKHR.ImmediateKhr;
  168. }
  169. else if (availablePresentModes.Contains(PresentModeKHR.MailboxKhr))
  170. {
  171. return PresentModeKHR.MailboxKhr;
  172. }
  173. else
  174. {
  175. return PresentModeKHR.FifoKhr;
  176. }
  177. }
  178. public static Extent2D ChooseSwapExtent(SurfaceCapabilitiesKHR capabilities)
  179. {
  180. if (capabilities.CurrentExtent.Width != uint.MaxValue)
  181. {
  182. return capabilities.CurrentExtent;
  183. }
  184. else
  185. {
  186. uint width = Math.Max(capabilities.MinImageExtent.Width, Math.Min(capabilities.MaxImageExtent.Width, SurfaceWidth));
  187. uint height = Math.Max(capabilities.MinImageExtent.Height, Math.Min(capabilities.MaxImageExtent.Height, SurfaceHeight));
  188. return new Extent2D(width, height);
  189. }
  190. }
  191. public unsafe override void Present(ITexture texture, ImageCrop crop, Action swapBuffersCallback)
  192. {
  193. _gd.PipelineInternal.AutoFlush.Present();
  194. uint nextImage = 0;
  195. while (true)
  196. {
  197. var acquireResult = _gd.SwapchainApi.AcquireNextImage(
  198. _device,
  199. _swapchain,
  200. ulong.MaxValue,
  201. _imageAvailableSemaphore,
  202. new Fence(),
  203. ref nextImage);
  204. if (acquireResult == Result.ErrorOutOfDateKhr ||
  205. acquireResult == Result.SuboptimalKhr ||
  206. _vsyncModeChanged)
  207. {
  208. RecreateSwapchain();
  209. }
  210. else
  211. {
  212. acquireResult.ThrowOnError();
  213. break;
  214. }
  215. }
  216. var swapchainImage = _swapchainImages[nextImage];
  217. _gd.FlushAllCommands();
  218. var cbs = _gd.CommandBufferPool.Rent();
  219. Transition(
  220. cbs.CommandBuffer,
  221. swapchainImage,
  222. 0,
  223. AccessFlags.TransferWriteBit,
  224. ImageLayout.Undefined,
  225. ImageLayout.General);
  226. var view = (TextureView)texture;
  227. int srcX0, srcX1, srcY0, srcY1;
  228. float scale = view.ScaleFactor;
  229. if (crop.Left == 0 && crop.Right == 0)
  230. {
  231. srcX0 = 0;
  232. srcX1 = (int)(view.Width / scale);
  233. }
  234. else
  235. {
  236. srcX0 = crop.Left;
  237. srcX1 = crop.Right;
  238. }
  239. if (crop.Top == 0 && crop.Bottom == 0)
  240. {
  241. srcY0 = 0;
  242. srcY1 = (int)(view.Height / scale);
  243. }
  244. else
  245. {
  246. srcY0 = crop.Top;
  247. srcY1 = crop.Bottom;
  248. }
  249. if (scale != 1f)
  250. {
  251. srcX0 = (int)(srcX0 * scale);
  252. srcY0 = (int)(srcY0 * scale);
  253. srcX1 = (int)Math.Ceiling(srcX1 * scale);
  254. srcY1 = (int)Math.Ceiling(srcY1 * scale);
  255. }
  256. if (ScreenCaptureRequested)
  257. {
  258. CaptureFrame(view, srcX0, srcY0, srcX1 - srcX0, srcY1 - srcY0, view.Info.Format.IsBgr(), crop.FlipX, crop.FlipY);
  259. ScreenCaptureRequested = false;
  260. }
  261. float ratioX = crop.IsStretched ? 1.0f : MathF.Min(1.0f, _height * crop.AspectRatioX / (_width * crop.AspectRatioY));
  262. float ratioY = crop.IsStretched ? 1.0f : MathF.Min(1.0f, _width * crop.AspectRatioY / (_height * crop.AspectRatioX));
  263. int dstWidth = (int)(_width * ratioX);
  264. int dstHeight = (int)(_height * ratioY);
  265. int dstPaddingX = (_width - dstWidth) / 2;
  266. int dstPaddingY = (_height - dstHeight) / 2;
  267. int dstX0 = crop.FlipX ? _width - dstPaddingX : dstPaddingX;
  268. int dstX1 = crop.FlipX ? dstPaddingX : _width - dstPaddingX;
  269. int dstY0 = crop.FlipY ? dstPaddingY : _height - dstPaddingY;
  270. int dstY1 = crop.FlipY ? _height - dstPaddingY : dstPaddingY;
  271. _gd.HelperShader.BlitColor(
  272. _gd,
  273. cbs,
  274. view,
  275. _swapchainImageViews[nextImage],
  276. _width,
  277. _height,
  278. 1,
  279. _format,
  280. false,
  281. new Extents2D(srcX0, srcY0, srcX1, srcY1),
  282. new Extents2D(dstX0, dstY1, dstX1, dstY0),
  283. true,
  284. true);
  285. Transition(
  286. cbs.CommandBuffer,
  287. swapchainImage,
  288. 0,
  289. 0,
  290. ImageLayout.General,
  291. ImageLayout.PresentSrcKhr);
  292. _gd.CommandBufferPool.Return(
  293. cbs,
  294. stackalloc[] { _imageAvailableSemaphore },
  295. stackalloc[] { PipelineStageFlags.ColorAttachmentOutputBit },
  296. stackalloc[] { _renderFinishedSemaphore });
  297. // TODO: Present queue.
  298. var semaphore = _renderFinishedSemaphore;
  299. var swapchain = _swapchain;
  300. Result result;
  301. var presentInfo = new PresentInfoKHR()
  302. {
  303. SType = StructureType.PresentInfoKhr,
  304. WaitSemaphoreCount = 1,
  305. PWaitSemaphores = &semaphore,
  306. SwapchainCount = 1,
  307. PSwapchains = &swapchain,
  308. PImageIndices = &nextImage,
  309. PResults = &result
  310. };
  311. lock (_gd.QueueLock)
  312. {
  313. _gd.SwapchainApi.QueuePresent(_gd.Queue, presentInfo);
  314. }
  315. }
  316. private unsafe void Transition(
  317. CommandBuffer commandBuffer,
  318. Image image,
  319. AccessFlags srcAccess,
  320. AccessFlags dstAccess,
  321. ImageLayout srcLayout,
  322. ImageLayout dstLayout)
  323. {
  324. var subresourceRange = new ImageSubresourceRange(ImageAspectFlags.ColorBit, 0, 1, 0, 1);
  325. var barrier = new ImageMemoryBarrier()
  326. {
  327. SType = StructureType.ImageMemoryBarrier,
  328. SrcAccessMask = srcAccess,
  329. DstAccessMask = dstAccess,
  330. OldLayout = srcLayout,
  331. NewLayout = dstLayout,
  332. SrcQueueFamilyIndex = Vk.QueueFamilyIgnored,
  333. DstQueueFamilyIndex = Vk.QueueFamilyIgnored,
  334. Image = image,
  335. SubresourceRange = subresourceRange
  336. };
  337. _gd.Api.CmdPipelineBarrier(
  338. commandBuffer,
  339. PipelineStageFlags.TopOfPipeBit,
  340. PipelineStageFlags.AllCommandsBit,
  341. 0,
  342. 0,
  343. null,
  344. 0,
  345. null,
  346. 1,
  347. barrier);
  348. }
  349. private void CaptureFrame(TextureView texture, int x, int y, int width, int height, bool isBgra, bool flipX, bool flipY)
  350. {
  351. byte[] bitmap = texture.GetData(x, y, width, height);
  352. _gd.OnScreenCaptured(new ScreenCaptureImageInfo(width, height, isBgra, bitmap, flipX, flipY));
  353. }
  354. public override void SetSize(int width, int height)
  355. {
  356. // Not needed as we can get the size from the surface.
  357. }
  358. public override void ChangeVSyncMode(bool vsyncEnabled)
  359. {
  360. _vsyncEnabled = vsyncEnabled;
  361. _vsyncModeChanged = true;
  362. }
  363. protected virtual void Dispose(bool disposing)
  364. {
  365. if (disposing)
  366. {
  367. unsafe
  368. {
  369. _gd.Api.DestroySemaphore(_device, _renderFinishedSemaphore, null);
  370. _gd.Api.DestroySemaphore(_device, _imageAvailableSemaphore, null);
  371. for (int i = 0; i < _swapchainImageViews.Length; i++)
  372. {
  373. _swapchainImageViews[i].Dispose();
  374. }
  375. _gd.SwapchainApi.DestroySwapchain(_device, _swapchain, null);
  376. }
  377. }
  378. }
  379. public override void Dispose()
  380. {
  381. Dispose(true);
  382. }
  383. }
  384. }