VulkanImage.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Linq;
  5. using System.Runtime.InteropServices;
  6. using Avalonia;
  7. using Avalonia.Platform;
  8. using Avalonia.Vulkan;
  9. using SharpDX.DXGI;
  10. using Silk.NET.Vulkan;
  11. using Silk.NET.Vulkan.Extensions.KHR;
  12. using SilkNetDemo;
  13. using SkiaSharp;
  14. using Device = Silk.NET.Vulkan.Device;
  15. using Format = Silk.NET.Vulkan.Format;
  16. namespace GpuInterop.VulkanDemo;
  17. public unsafe class VulkanImage : IDisposable
  18. {
  19. private readonly VulkanContext _vk;
  20. private readonly Instance _instance;
  21. private readonly Device _device;
  22. private readonly PhysicalDevice _physicalDevice;
  23. private readonly VulkanCommandBufferPool _commandBufferPool;
  24. private ImageLayout _currentLayout;
  25. private AccessFlags _currentAccessFlags;
  26. private ImageUsageFlags _imageUsageFlags { get; }
  27. private ImageView _imageView { get; set; }
  28. private DeviceMemory _imageMemory { get; set; }
  29. private readonly SharpDX.Direct3D11.Texture2D? _d3dTexture2D;
  30. internal Image InternalHandle { get; private set; }
  31. internal Format Format { get; }
  32. internal ImageAspectFlags AspectFlags { get; }
  33. public ulong Handle => InternalHandle.Handle;
  34. public ulong ViewHandle => _imageView.Handle;
  35. public uint UsageFlags => (uint) _imageUsageFlags;
  36. public ulong MemoryHandle => _imageMemory.Handle;
  37. public DeviceMemory DeviceMemory => _imageMemory;
  38. public uint MipLevels { get; }
  39. public Vk Api { get; }
  40. public PixelSize Size { get; }
  41. public ulong MemorySize { get; }
  42. public uint CurrentLayout => (uint) _currentLayout;
  43. public VulkanImage(VulkanContext vk, uint format, PixelSize size,
  44. bool exportable, IReadOnlyList<string> supportedHandleTypes)
  45. {
  46. _vk = vk;
  47. _instance = vk.Instance;
  48. _device = vk.Device;
  49. _physicalDevice = vk.PhysicalDevice;
  50. _commandBufferPool = vk.Pool;
  51. Format = (Format)format;
  52. Api = vk.Api;
  53. Size = size;
  54. MipLevels = 1;//mipLevels;
  55. _imageUsageFlags =
  56. ImageUsageFlags.ColorAttachmentBit | ImageUsageFlags.TransferDstBit |
  57. ImageUsageFlags.TransferSrcBit | ImageUsageFlags.SampledBit;
  58. //MipLevels = MipLevels != 0 ? MipLevels : (uint)Math.Floor(Math.Log(Math.Max(Size.Width, Size.Height), 2));
  59. var handleType = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ?
  60. (supportedHandleTypes.Contains(KnownPlatformGraphicsExternalImageHandleTypes.D3D11TextureNtHandle)
  61. && !supportedHandleTypes.Contains(KnownPlatformGraphicsExternalImageHandleTypes.VulkanOpaqueNtHandle) ?
  62. ExternalMemoryHandleTypeFlags.D3D11TextureBit :
  63. ExternalMemoryHandleTypeFlags.OpaqueWin32Bit) :
  64. ExternalMemoryHandleTypeFlags.OpaqueFDBit;
  65. var externalMemoryCreateInfo = new ExternalMemoryImageCreateInfo
  66. {
  67. SType = StructureType.ExternalMemoryImageCreateInfo,
  68. HandleTypes = handleType
  69. };
  70. var imageCreateInfo = new ImageCreateInfo
  71. {
  72. PNext = exportable ? &externalMemoryCreateInfo : null,
  73. SType = StructureType.ImageCreateInfo,
  74. ImageType = ImageType.Type2D,
  75. Format = Format,
  76. Extent =
  77. new Extent3D((uint?)Size.Width,
  78. (uint?)Size.Height, 1),
  79. MipLevels = MipLevels,
  80. ArrayLayers = 1,
  81. Samples = SampleCountFlags.Count1Bit,
  82. Tiling = Tiling,
  83. Usage = _imageUsageFlags,
  84. SharingMode = SharingMode.Exclusive,
  85. InitialLayout = ImageLayout.Undefined,
  86. Flags = ImageCreateFlags.CreateMutableFormatBit
  87. };
  88. Api
  89. .CreateImage(_device, imageCreateInfo, null, out var image).ThrowOnError();
  90. InternalHandle = image;
  91. Api.GetImageMemoryRequirements(_device, InternalHandle,
  92. out var memoryRequirements);
  93. var dedicatedAllocation = new MemoryDedicatedAllocateInfoKHR
  94. {
  95. SType = StructureType.MemoryDedicatedAllocateInfoKhr,
  96. Image = image
  97. };
  98. var fdExport = new ExportMemoryAllocateInfo
  99. {
  100. HandleTypes = handleType, SType = StructureType.ExportMemoryAllocateInfo,
  101. PNext = &dedicatedAllocation
  102. };
  103. ImportMemoryWin32HandleInfoKHR handleImport = default;
  104. if (handleType == ExternalMemoryHandleTypeFlags.D3D11TextureBit && exportable)
  105. {
  106. _d3dTexture2D = D3DMemoryHelper.CreateMemoryHandle(vk.D3DDevice, size, Format);
  107. using var dxgi = _d3dTexture2D.QueryInterface<SharpDX.DXGI.Resource1>();
  108. handleImport = new ImportMemoryWin32HandleInfoKHR
  109. {
  110. PNext = &dedicatedAllocation,
  111. SType = StructureType.ImportMemoryWin32HandleInfoKhr,
  112. HandleType = ExternalMemoryHandleTypeFlags.D3D11TextureBit,
  113. Handle = dxgi.CreateSharedHandle(null, SharedResourceFlags.Read | SharedResourceFlags.Write),
  114. };
  115. }
  116. var memoryAllocateInfo = new MemoryAllocateInfo
  117. {
  118. PNext =
  119. exportable ? handleImport.Handle != IntPtr.Zero ? &handleImport : &fdExport : null,
  120. SType = StructureType.MemoryAllocateInfo,
  121. AllocationSize = memoryRequirements.Size,
  122. MemoryTypeIndex = (uint)VulkanMemoryHelper.FindSuitableMemoryTypeIndex(
  123. Api,
  124. _physicalDevice,
  125. memoryRequirements.MemoryTypeBits, MemoryPropertyFlags.DeviceLocalBit)
  126. };
  127. Api.AllocateMemory(_device, memoryAllocateInfo, null,
  128. out var imageMemory).ThrowOnError();
  129. _imageMemory = imageMemory;
  130. MemorySize = memoryRequirements.Size;
  131. Api.BindImageMemory(_device, InternalHandle, _imageMemory, 0).ThrowOnError();
  132. var componentMapping = new ComponentMapping(
  133. ComponentSwizzle.Identity,
  134. ComponentSwizzle.Identity,
  135. ComponentSwizzle.Identity,
  136. ComponentSwizzle.Identity);
  137. AspectFlags = ImageAspectFlags.ColorBit;
  138. var subresourceRange = new ImageSubresourceRange(AspectFlags, 0, MipLevels, 0, 1);
  139. var imageViewCreateInfo = new ImageViewCreateInfo
  140. {
  141. SType = StructureType.ImageViewCreateInfo,
  142. Image = InternalHandle,
  143. ViewType = ImageViewType.Type2D,
  144. Format = Format,
  145. Components = componentMapping,
  146. SubresourceRange = subresourceRange
  147. };
  148. Api
  149. .CreateImageView(_device, imageViewCreateInfo, null, out var imageView)
  150. .ThrowOnError();
  151. _imageView = imageView;
  152. _currentLayout = ImageLayout.Undefined;
  153. TransitionLayout(ImageLayout.ColorAttachmentOptimal, AccessFlags.NoneKhr);
  154. }
  155. public int ExportFd()
  156. {
  157. if (!Api.TryGetDeviceExtension<KhrExternalMemoryFd>(_instance, _device, out var ext))
  158. throw new InvalidOperationException();
  159. var info = new MemoryGetFdInfoKHR
  160. {
  161. Memory = _imageMemory,
  162. SType = StructureType.MemoryGetFDInfoKhr,
  163. HandleType = ExternalMemoryHandleTypeFlags.OpaqueFDBit
  164. };
  165. ext.GetMemoryF(_device, info, out var fd).ThrowOnError();
  166. return fd;
  167. }
  168. public IntPtr ExportOpaqueNtHandle()
  169. {
  170. if (!Api.TryGetDeviceExtension<KhrExternalMemoryWin32>(_instance, _device, out var ext))
  171. throw new InvalidOperationException();
  172. var info = new MemoryGetWin32HandleInfoKHR()
  173. {
  174. Memory = _imageMemory,
  175. SType = StructureType.MemoryGetWin32HandleInfoKhr,
  176. HandleType = ExternalMemoryHandleTypeFlags.OpaqueWin32Bit
  177. };
  178. ext.GetMemoryWin32Handle(_device, info, out var fd).ThrowOnError();
  179. return fd;
  180. }
  181. public IPlatformHandle Export()
  182. {
  183. if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
  184. {
  185. if (_d3dTexture2D != null)
  186. {
  187. using var dxgi = _d3dTexture2D!.QueryInterface<Resource1>();
  188. return new PlatformHandle(
  189. dxgi.CreateSharedHandle(null, SharedResourceFlags.Read | SharedResourceFlags.Write),
  190. KnownPlatformGraphicsExternalImageHandleTypes.D3D11TextureNtHandle);
  191. }
  192. return new PlatformHandle(ExportOpaqueNtHandle(),
  193. KnownPlatformGraphicsExternalImageHandleTypes.VulkanOpaqueNtHandle);
  194. }
  195. else
  196. return new PlatformHandle(new IntPtr(ExportFd()),
  197. KnownPlatformGraphicsExternalImageHandleTypes.VulkanOpaquePosixFileDescriptor);
  198. }
  199. public ImageTiling Tiling => ImageTiling.Optimal;
  200. public bool IsDirectXBacked => _d3dTexture2D != null;
  201. internal void TransitionLayout(CommandBuffer commandBuffer,
  202. ImageLayout fromLayout, AccessFlags fromAccessFlags,
  203. ImageLayout destinationLayout, AccessFlags destinationAccessFlags)
  204. {
  205. VulkanMemoryHelper.TransitionLayout(Api, commandBuffer, InternalHandle,
  206. fromLayout,
  207. fromAccessFlags,
  208. destinationLayout, destinationAccessFlags,
  209. MipLevels);
  210. _currentLayout = destinationLayout;
  211. _currentAccessFlags = destinationAccessFlags;
  212. }
  213. internal void TransitionLayout(CommandBuffer commandBuffer,
  214. ImageLayout destinationLayout, AccessFlags destinationAccessFlags)
  215. => TransitionLayout(commandBuffer, _currentLayout, _currentAccessFlags, destinationLayout,
  216. destinationAccessFlags);
  217. internal void TransitionLayout(ImageLayout destinationLayout, AccessFlags destinationAccessFlags)
  218. {
  219. var commandBuffer = _commandBufferPool.CreateCommandBuffer();
  220. commandBuffer.BeginRecording();
  221. TransitionLayout(commandBuffer.InternalHandle, destinationLayout, destinationAccessFlags);
  222. commandBuffer.EndRecording();
  223. commandBuffer.Submit();
  224. }
  225. public void TransitionLayout(uint destinationLayout, uint destinationAccessFlags)
  226. {
  227. TransitionLayout((ImageLayout)destinationLayout, (AccessFlags)destinationAccessFlags);
  228. }
  229. public unsafe void Dispose()
  230. {
  231. Api.DestroyImageView(_device, _imageView, null);
  232. Api.DestroyImage(_device, InternalHandle, null);
  233. Api.FreeMemory(_device, _imageMemory, null);
  234. _imageView = default;
  235. InternalHandle = default;
  236. _imageMemory = default;
  237. }
  238. public void SaveTexture(string path)
  239. {
  240. _vk.GrContext.ResetContext();
  241. var _image = this;
  242. var imageInfo = new GRVkImageInfo()
  243. {
  244. CurrentQueueFamily = _vk.QueueFamilyIndex,
  245. Format = (uint)_image.Format,
  246. Image = _image.Handle,
  247. ImageLayout = (uint)_image.CurrentLayout,
  248. ImageTiling = (uint)_image.Tiling,
  249. ImageUsageFlags = (uint)_image.UsageFlags,
  250. LevelCount = _image.MipLevels,
  251. SampleCount = 1,
  252. Protected = false,
  253. Alloc = new GRVkAlloc()
  254. {
  255. Memory = _image.MemoryHandle, Flags = 0, Offset = 0, Size = _image.MemorySize
  256. }
  257. };
  258. using (var backendTexture = new GRBackendRenderTarget(_image.Size.Width, _image.Size.Height, 1,
  259. imageInfo))
  260. using (var surface = SKSurface.Create(_vk.GrContext, backendTexture,
  261. GRSurfaceOrigin.TopLeft,
  262. SKColorType.Rgba8888, SKColorSpace.CreateSrgb()))
  263. {
  264. using var snap = surface.Snapshot();
  265. using var encoded = snap.Encode();
  266. using (var s = File.Create(path))
  267. encoded.SaveTo(s);
  268. }
  269. }
  270. }