ImageWindow.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
  1. using Ryujinx.Graphics.GAL;
  2. using Silk.NET.Vulkan;
  3. using System;
  4. using VkFormat = Silk.NET.Vulkan.Format;
  5. namespace Ryujinx.Graphics.Vulkan
  6. {
  7. class ImageWindow : WindowBase, IWindow, IDisposable
  8. {
  9. private const int ImageCount = 5;
  10. private const int SurfaceWidth = 1280;
  11. private const int SurfaceHeight = 720;
  12. private readonly VulkanRenderer _gd;
  13. private readonly PhysicalDevice _physicalDevice;
  14. private readonly Device _device;
  15. private Auto<DisposableImage>[] _images;
  16. private Auto<DisposableImageView>[] _imageViews;
  17. private Auto<MemoryAllocation>[] _imageAllocationAuto;
  18. private ulong[] _imageSizes;
  19. private ulong[] _imageOffsets;
  20. private Semaphore _imageAvailableSemaphore;
  21. private Semaphore _renderFinishedSemaphore;
  22. private int _width = SurfaceWidth;
  23. private int _height = SurfaceHeight;
  24. private VkFormat _format;
  25. private bool _recreateImages;
  26. private int _nextImage;
  27. internal new bool ScreenCaptureRequested { get; set; }
  28. public unsafe ImageWindow(VulkanRenderer gd, PhysicalDevice physicalDevice, Device device)
  29. {
  30. _gd = gd;
  31. _physicalDevice = physicalDevice;
  32. _device = device;
  33. _format = VkFormat.R8G8B8A8Unorm;
  34. _images = new Auto<DisposableImage>[ImageCount];
  35. _imageAllocationAuto = new Auto<MemoryAllocation>[ImageCount];
  36. _imageSizes = new ulong[ImageCount];
  37. _imageOffsets = new ulong[ImageCount];
  38. CreateImages();
  39. var semaphoreCreateInfo = new SemaphoreCreateInfo()
  40. {
  41. SType = StructureType.SemaphoreCreateInfo
  42. };
  43. gd.Api.CreateSemaphore(device, semaphoreCreateInfo, null, out _imageAvailableSemaphore).ThrowOnError();
  44. gd.Api.CreateSemaphore(device, semaphoreCreateInfo, null, out _renderFinishedSemaphore).ThrowOnError();
  45. }
  46. private void RecreateImages()
  47. {
  48. for (int i = 0; i < ImageCount; i++)
  49. {
  50. _imageViews[i]?.Dispose();
  51. _imageAllocationAuto[i]?.Dispose();
  52. _images[i]?.Dispose();
  53. }
  54. CreateImages();
  55. }
  56. private unsafe void CreateImages()
  57. {
  58. _imageViews = new Auto<DisposableImageView>[ImageCount];
  59. var cbs = _gd.CommandBufferPool.Rent();
  60. for (int i = 0; i < _images.Length; i++)
  61. {
  62. var imageCreateInfo = new ImageCreateInfo
  63. {
  64. SType = StructureType.ImageCreateInfo,
  65. ImageType = ImageType.ImageType2D,
  66. Format = _format,
  67. Extent =
  68. new Extent3D((uint?)_width,
  69. (uint?)_height, 1),
  70. MipLevels = 1,
  71. ArrayLayers = 1,
  72. Samples = SampleCountFlags.SampleCount1Bit,
  73. Tiling = ImageTiling.Optimal,
  74. Usage = ImageUsageFlags.ImageUsageColorAttachmentBit | ImageUsageFlags.ImageUsageTransferSrcBit | ImageUsageFlags.ImageUsageTransferDstBit,
  75. SharingMode = SharingMode.Exclusive,
  76. InitialLayout = ImageLayout.Undefined,
  77. Flags = ImageCreateFlags.ImageCreateMutableFormatBit
  78. };
  79. _gd.Api.CreateImage(_device, imageCreateInfo, null, out var image).ThrowOnError();
  80. _images[i] = new Auto<DisposableImage>(new DisposableImage(_gd.Api, _device, image));
  81. _gd.Api.GetImageMemoryRequirements(_device, image,
  82. out var memoryRequirements);
  83. var allocation = _gd.MemoryAllocator.AllocateDeviceMemory(_physicalDevice, memoryRequirements, MemoryPropertyFlags.MemoryPropertyDeviceLocalBit);
  84. _imageSizes[i] = allocation.Size;
  85. _imageOffsets[i] = allocation.Offset;
  86. _imageAllocationAuto[i] = new Auto<MemoryAllocation>(allocation);
  87. _gd.Api.BindImageMemory(_device, image, allocation.Memory, allocation.Offset);
  88. _imageViews[i] = CreateImageView(image, _format);
  89. Transition(
  90. cbs.CommandBuffer,
  91. image,
  92. 0,
  93. 0,
  94. ImageLayout.Undefined,
  95. ImageLayout.ColorAttachmentOptimal);
  96. }
  97. _gd.CommandBufferPool.Return(cbs);
  98. }
  99. private unsafe Auto<DisposableImageView> CreateImageView(Image image, VkFormat format)
  100. {
  101. var componentMapping = new ComponentMapping(
  102. ComponentSwizzle.R,
  103. ComponentSwizzle.G,
  104. ComponentSwizzle.B,
  105. ComponentSwizzle.A);
  106. var subresourceRange = new ImageSubresourceRange(ImageAspectFlags.ImageAspectColorBit, 0, 1, 0, 1);
  107. var imageCreateInfo = new ImageViewCreateInfo()
  108. {
  109. SType = StructureType.ImageViewCreateInfo,
  110. Image = image,
  111. ViewType = ImageViewType.ImageViewType2D,
  112. Format = format,
  113. Components = componentMapping,
  114. SubresourceRange = subresourceRange
  115. };
  116. _gd.Api.CreateImageView(_device, imageCreateInfo, null, out var imageView).ThrowOnError();
  117. return new Auto<DisposableImageView>(new DisposableImageView(_gd.Api, _device, imageView));
  118. }
  119. public override unsafe void Present(ITexture texture, ImageCrop crop, Action<object> swapBuffersCallback)
  120. {
  121. if (_recreateImages)
  122. {
  123. RecreateImages();
  124. _recreateImages = false;
  125. }
  126. var image = _images[_nextImage];
  127. _gd.FlushAllCommands();
  128. var cbs = _gd.CommandBufferPool.Rent();
  129. Transition(
  130. cbs.CommandBuffer,
  131. image.GetUnsafe().Value,
  132. 0,
  133. AccessFlags.AccessTransferWriteBit,
  134. ImageLayout.ColorAttachmentOptimal,
  135. ImageLayout.General);
  136. var view = (TextureView)texture;
  137. int srcX0, srcX1, srcY0, srcY1;
  138. float scale = view.ScaleFactor;
  139. if (crop.Left == 0 && crop.Right == 0)
  140. {
  141. srcX0 = 0;
  142. srcX1 = (int)(view.Width / scale);
  143. }
  144. else
  145. {
  146. srcX0 = crop.Left;
  147. srcX1 = crop.Right;
  148. }
  149. if (crop.Top == 0 && crop.Bottom == 0)
  150. {
  151. srcY0 = 0;
  152. srcY1 = (int)(view.Height / scale);
  153. }
  154. else
  155. {
  156. srcY0 = crop.Top;
  157. srcY1 = crop.Bottom;
  158. }
  159. if (scale != 1f)
  160. {
  161. srcX0 = (int)(srcX0 * scale);
  162. srcY0 = (int)(srcY0 * scale);
  163. srcX1 = (int)Math.Ceiling(srcX1 * scale);
  164. srcY1 = (int)Math.Ceiling(srcY1 * scale);
  165. }
  166. if (ScreenCaptureRequested)
  167. {
  168. CaptureFrame(view, srcX0, srcY0, srcX1 - srcX0, srcY1 - srcY0, view.Info.Format.IsBgr(), crop.FlipX, crop.FlipY);
  169. ScreenCaptureRequested = false;
  170. }
  171. float ratioX = crop.IsStretched ? 1.0f : MathF.Min(1.0f, _height * crop.AspectRatioX / (_width * crop.AspectRatioY));
  172. float ratioY = crop.IsStretched ? 1.0f : MathF.Min(1.0f, _width * crop.AspectRatioY / (_height * crop.AspectRatioX));
  173. int dstWidth = (int)(_width * ratioX);
  174. int dstHeight = (int)(_height * ratioY);
  175. int dstPaddingX = (_width - dstWidth) / 2;
  176. int dstPaddingY = (_height - dstHeight) / 2;
  177. int dstX0 = crop.FlipX ? _width - dstPaddingX : dstPaddingX;
  178. int dstX1 = crop.FlipX ? dstPaddingX : _width - dstPaddingX;
  179. int dstY0 = crop.FlipY ? dstPaddingY : _height - dstPaddingY;
  180. int dstY1 = crop.FlipY ? _height - dstPaddingY : dstPaddingY;
  181. _gd.HelperShader.Blit(
  182. _gd,
  183. cbs,
  184. view,
  185. _imageViews[_nextImage],
  186. _width,
  187. _height,
  188. _format,
  189. new Extents2D(srcX0, srcY0, srcX1, srcY1),
  190. new Extents2D(dstX0, dstY1, dstX1, dstY0),
  191. true,
  192. true);
  193. Transition(
  194. cbs.CommandBuffer,
  195. image.GetUnsafe().Value,
  196. 0,
  197. 0,
  198. ImageLayout.General,
  199. ImageLayout.ColorAttachmentOptimal);
  200. _gd.CommandBufferPool.Return(
  201. cbs,
  202. null,
  203. stackalloc[] { PipelineStageFlags.PipelineStageColorAttachmentOutputBit },
  204. null);
  205. var memory = _imageAllocationAuto[_nextImage].GetUnsafe().Memory;
  206. var presentInfo = new PresentImageInfo(image.GetUnsafe().Value, memory, _imageSizes[_nextImage], _imageOffsets[_nextImage], _renderFinishedSemaphore, _imageAvailableSemaphore);
  207. swapBuffersCallback(presentInfo);
  208. _nextImage %= ImageCount;
  209. }
  210. private unsafe void Transition(
  211. CommandBuffer commandBuffer,
  212. Image image,
  213. AccessFlags srcAccess,
  214. AccessFlags dstAccess,
  215. ImageLayout srcLayout,
  216. ImageLayout dstLayout)
  217. {
  218. var subresourceRange = new ImageSubresourceRange(ImageAspectFlags.ImageAspectColorBit, 0, 1, 0, 1);
  219. var barrier = new ImageMemoryBarrier()
  220. {
  221. SType = StructureType.ImageMemoryBarrier,
  222. SrcAccessMask = srcAccess,
  223. DstAccessMask = dstAccess,
  224. OldLayout = srcLayout,
  225. NewLayout = dstLayout,
  226. SrcQueueFamilyIndex = Vk.QueueFamilyIgnored,
  227. DstQueueFamilyIndex = Vk.QueueFamilyIgnored,
  228. Image = image,
  229. SubresourceRange = subresourceRange
  230. };
  231. _gd.Api.CmdPipelineBarrier(
  232. commandBuffer,
  233. PipelineStageFlags.PipelineStageTopOfPipeBit,
  234. PipelineStageFlags.PipelineStageAllCommandsBit,
  235. 0,
  236. 0,
  237. null,
  238. 0,
  239. null,
  240. 1,
  241. barrier);
  242. }
  243. private void CaptureFrame(TextureView texture, int x, int y, int width, int height, bool isBgra, bool flipX, bool flipY)
  244. {
  245. byte[] bitmap = texture.GetData(x, y, width, height);
  246. _gd.OnScreenCaptured(new ScreenCaptureImageInfo(width, height, isBgra, bitmap, flipX, flipY));
  247. }
  248. public override void SetSize(int width, int height)
  249. {
  250. if (_width != width || _height != height)
  251. {
  252. _recreateImages = true;
  253. }
  254. _width = width;
  255. _height = height;
  256. }
  257. protected virtual void Dispose(bool disposing)
  258. {
  259. if (disposing)
  260. {
  261. unsafe
  262. {
  263. _gd.Api.DestroySemaphore(_device, _renderFinishedSemaphore, null);
  264. _gd.Api.DestroySemaphore(_device, _imageAvailableSemaphore, null);
  265. for (int i = 0; i < ImageCount; i++)
  266. {
  267. _imageViews[i]?.Dispose();
  268. _imageAllocationAuto[i]?.Dispose();
  269. _images[i]?.Dispose();
  270. }
  271. }
  272. }
  273. }
  274. public override void Dispose()
  275. {
  276. Dispose(true);
  277. }
  278. }
  279. public class PresentImageInfo
  280. {
  281. public Image Image { get; }
  282. public DeviceMemory Memory { get; }
  283. public ulong MemorySize { get; set; }
  284. public ulong MemoryOffset { get; set; }
  285. public Semaphore ReadySemaphore { get; }
  286. public Semaphore AvailableSemaphore { get; }
  287. public PresentImageInfo(Image image, DeviceMemory memory, ulong memorySize, ulong memoryOffset, Semaphore readySemaphore, Semaphore availableSemaphore)
  288. {
  289. this.Image = image;
  290. this.Memory = memory;
  291. this.MemorySize = memorySize;
  292. this.MemoryOffset = memoryOffset;
  293. this.ReadySemaphore = readySemaphore;
  294. this.AvailableSemaphore = availableSemaphore;
  295. }
  296. }
  297. }