ImageWindow.cs 14 KB

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