1
0

VulkanImage.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382
  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.EXT;
  12. using Silk.NET.Vulkan.Extensions.KHR;
  13. using SilkNetDemo;
  14. using SkiaSharp;
  15. using Device = Silk.NET.Vulkan.Device;
  16. using Format = Silk.NET.Vulkan.Format;
  17. namespace GpuInterop.VulkanDemo;
  18. public unsafe class VulkanImage : IDisposable
  19. {
  20. private readonly VulkanContext _vk;
  21. private readonly Instance _instance;
  22. private readonly Device _device;
  23. private readonly PhysicalDevice _physicalDevice;
  24. private readonly VulkanCommandBufferPool _commandBufferPool;
  25. private ImageLayout _currentLayout;
  26. private AccessFlags _currentAccessFlags;
  27. private ImageUsageFlags _imageUsageFlags { get; }
  28. private ImageView _imageView { get; set; }
  29. private DeviceMemory _imageMemory { get; set; }
  30. private readonly SharpDX.Direct3D11.Texture2D? _d3dTexture2D;
  31. internal Image InternalHandle { get; private set; }
  32. internal Format Format { get; }
  33. internal ImageAspectFlags AspectFlags { get; }
  34. public ulong Handle => InternalHandle.Handle;
  35. public ulong ViewHandle => _imageView.Handle;
  36. public uint UsageFlags => (uint) _imageUsageFlags;
  37. public ulong MemoryHandle => _imageMemory.Handle;
  38. public DeviceMemory DeviceMemory => _imageMemory;
  39. public uint MipLevels { get; }
  40. public Vk Api { get; }
  41. public PixelSize Size { get; }
  42. public ulong MemorySize { get; }
  43. public uint CurrentLayout => (uint) _currentLayout;
  44. private bool _hasIOSurface;
  45. public VulkanImage(VulkanContext vk, uint format, PixelSize size,
  46. bool exportable, IReadOnlyList<string> supportedHandleTypes)
  47. {
  48. _vk = vk;
  49. _instance = vk.Instance;
  50. _device = vk.Device;
  51. _physicalDevice = vk.PhysicalDevice;
  52. _commandBufferPool = vk.Pool;
  53. Format = (Format)format;
  54. Api = vk.Api;
  55. Size = size;
  56. MipLevels = 1;//mipLevels;
  57. _imageUsageFlags =
  58. ImageUsageFlags.ColorAttachmentBit | ImageUsageFlags.TransferDstBit |
  59. ImageUsageFlags.TransferSrcBit | ImageUsageFlags.SampledBit;
  60. //MipLevels = MipLevels != 0 ? MipLevels : (uint)Math.Floor(Math.Log(Math.Max(Size.Width, Size.Height), 2));
  61. var handleType = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ?
  62. (supportedHandleTypes.Contains(KnownPlatformGraphicsExternalImageHandleTypes.D3D11TextureNtHandle)
  63. && !supportedHandleTypes.Contains(KnownPlatformGraphicsExternalImageHandleTypes.VulkanOpaqueNtHandle) ?
  64. ExternalMemoryHandleTypeFlags.D3D11TextureBit :
  65. ExternalMemoryHandleTypeFlags.OpaqueWin32Bit) :
  66. ExternalMemoryHandleTypeFlags.OpaqueFDBit;
  67. var externalMemoryCreateInfo = new ExternalMemoryImageCreateInfo
  68. {
  69. SType = StructureType.ExternalMemoryImageCreateInfo,
  70. HandleTypes = handleType
  71. };
  72. var ioSurfaceCreateInfo = new ExportMetalObjectCreateInfoEXT
  73. {
  74. SType = StructureType.ExportMetalObjectCreateInfoExt,
  75. ExportObjectType = ExportMetalObjectTypeFlagsEXT.IosurfaceBitExt
  76. };
  77. _hasIOSurface = exportable && RuntimeInformation.IsOSPlatform(OSPlatform.OSX);
  78. var imageCreateInfo = new ImageCreateInfo
  79. {
  80. PNext = exportable ?
  81. RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ?
  82. &ioSurfaceCreateInfo :
  83. &externalMemoryCreateInfo : null,
  84. SType = StructureType.ImageCreateInfo,
  85. ImageType = ImageType.Type2D,
  86. Format = Format,
  87. Extent =
  88. new Extent3D((uint?)Size.Width,
  89. (uint?)Size.Height, 1),
  90. MipLevels = MipLevels,
  91. ArrayLayers = 1,
  92. Samples = SampleCountFlags.Count1Bit,
  93. Tiling = Tiling,
  94. Usage = _imageUsageFlags,
  95. SharingMode = SharingMode.Exclusive,
  96. InitialLayout = ImageLayout.Undefined,
  97. Flags = ImageCreateFlags.CreateMutableFormatBit
  98. };
  99. Api
  100. .CreateImage(_device, imageCreateInfo, null, out var image).ThrowOnError();
  101. InternalHandle = image;
  102. if (!exportable || !RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
  103. {
  104. Api.GetImageMemoryRequirements(_device, InternalHandle,
  105. out var memoryRequirements);
  106. var dedicatedAllocation = new MemoryDedicatedAllocateInfoKHR
  107. {
  108. SType = StructureType.MemoryDedicatedAllocateInfoKhr, Image = image
  109. };
  110. var fdExport = new ExportMemoryAllocateInfo
  111. {
  112. HandleTypes = handleType,
  113. SType = StructureType.ExportMemoryAllocateInfo,
  114. PNext = &dedicatedAllocation
  115. };
  116. ImportMemoryWin32HandleInfoKHR handleImport = default;
  117. if (handleType == ExternalMemoryHandleTypeFlags.D3D11TextureBit && exportable)
  118. {
  119. var d3dDevice = vk.D3DDevice ?? throw new NotSupportedException("Vulkan D3DDevice wasn't created");
  120. _d3dTexture2D = D3DMemoryHelper.CreateMemoryHandle(d3dDevice, size, Format);
  121. using var dxgi = _d3dTexture2D.QueryInterface<SharpDX.DXGI.Resource1>();
  122. handleImport = new ImportMemoryWin32HandleInfoKHR
  123. {
  124. PNext = &dedicatedAllocation,
  125. SType = StructureType.ImportMemoryWin32HandleInfoKhr,
  126. HandleType = ExternalMemoryHandleTypeFlags.D3D11TextureBit,
  127. Handle = dxgi.CreateSharedHandle(null, SharedResourceFlags.Read | SharedResourceFlags.Write),
  128. };
  129. }
  130. var memoryAllocateInfo = new MemoryAllocateInfo
  131. {
  132. PNext =
  133. exportable ? handleImport.Handle != IntPtr.Zero ? &handleImport : &fdExport : null,
  134. SType = StructureType.MemoryAllocateInfo,
  135. AllocationSize = memoryRequirements.Size,
  136. MemoryTypeIndex = (uint)VulkanMemoryHelper.FindSuitableMemoryTypeIndex(
  137. Api,
  138. _physicalDevice,
  139. memoryRequirements.MemoryTypeBits, MemoryPropertyFlags.DeviceLocalBit)
  140. };
  141. Api.AllocateMemory(_device, memoryAllocateInfo, null,
  142. out var imageMemory).ThrowOnError();
  143. _imageMemory = imageMemory;
  144. MemorySize = memoryRequirements.Size;
  145. Api.BindImageMemory(_device, InternalHandle, _imageMemory, 0).ThrowOnError();
  146. }
  147. var componentMapping = new ComponentMapping(
  148. ComponentSwizzle.Identity,
  149. ComponentSwizzle.Identity,
  150. ComponentSwizzle.Identity,
  151. ComponentSwizzle.Identity);
  152. AspectFlags = ImageAspectFlags.ColorBit;
  153. var subresourceRange = new ImageSubresourceRange(AspectFlags, 0, MipLevels, 0, 1);
  154. var imageViewCreateInfo = new ImageViewCreateInfo
  155. {
  156. SType = StructureType.ImageViewCreateInfo,
  157. Image = InternalHandle,
  158. ViewType = ImageViewType.Type2D,
  159. Format = Format,
  160. Components = componentMapping,
  161. SubresourceRange = subresourceRange
  162. };
  163. Api
  164. .CreateImageView(_device, imageViewCreateInfo, null, out var imageView)
  165. .ThrowOnError();
  166. _imageView = imageView;
  167. _currentLayout = ImageLayout.Undefined;
  168. TransitionLayout(ImageLayout.ColorAttachmentOptimal, AccessFlags.NoneKhr);
  169. }
  170. public int ExportFd()
  171. {
  172. if (!Api.TryGetDeviceExtension<KhrExternalMemoryFd>(_instance, _device, out var ext))
  173. throw new InvalidOperationException();
  174. var info = new MemoryGetFdInfoKHR
  175. {
  176. Memory = _imageMemory,
  177. SType = StructureType.MemoryGetFDInfoKhr,
  178. HandleType = ExternalMemoryHandleTypeFlags.OpaqueFDBit
  179. };
  180. ext.GetMemoryF(_device, info, out var fd).ThrowOnError();
  181. return fd;
  182. }
  183. public IntPtr ExportOpaqueNtHandle()
  184. {
  185. if (!Api.TryGetDeviceExtension<KhrExternalMemoryWin32>(_instance, _device, out var ext))
  186. throw new InvalidOperationException();
  187. var info = new MemoryGetWin32HandleInfoKHR()
  188. {
  189. Memory = _imageMemory,
  190. SType = StructureType.MemoryGetWin32HandleInfoKhr,
  191. HandleType = ExternalMemoryHandleTypeFlags.OpaqueWin32Bit
  192. };
  193. ext.GetMemoryWin32Handle(_device, info, out var fd).ThrowOnError();
  194. return fd;
  195. }
  196. public IntPtr ExportIOSurface()
  197. {
  198. if (!Api.TryGetDeviceExtension<ExtMetalObjects>(_instance, _device, out var ext))
  199. throw new InvalidOperationException();
  200. var surfaceExport = new ExportMetalIOSurfaceInfoEXT
  201. {
  202. SType = StructureType.ExportMetalIOSurfaceInfoExt,
  203. Image = InternalHandle
  204. };
  205. var export = new ExportMetalObjectsInfoEXT()
  206. {
  207. SType = StructureType.ExportMetalObjectsInfoExt,
  208. PNext = &surfaceExport
  209. };
  210. ext.ExportMetalObjects(_device, ref export);
  211. if (surfaceExport.IoSurface == IntPtr.Zero)
  212. throw new Exception("Unable to export IOSurfaceRef");
  213. return surfaceExport.IoSurface;
  214. }
  215. public IPlatformHandle Export()
  216. {
  217. if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
  218. {
  219. if (_d3dTexture2D != null)
  220. {
  221. using var dxgi = _d3dTexture2D!.QueryInterface<Resource1>();
  222. return new PlatformHandle(
  223. dxgi.CreateSharedHandle(null, SharedResourceFlags.Read | SharedResourceFlags.Write),
  224. KnownPlatformGraphicsExternalImageHandleTypes.D3D11TextureNtHandle);
  225. }
  226. return new PlatformHandle(ExportOpaqueNtHandle(),
  227. KnownPlatformGraphicsExternalImageHandleTypes.VulkanOpaqueNtHandle);
  228. }
  229. else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
  230. return new PlatformHandle(ExportIOSurface(),
  231. KnownPlatformGraphicsExternalImageHandleTypes.IOSurfaceRef);
  232. else
  233. return new PlatformHandle(new IntPtr(ExportFd()),
  234. KnownPlatformGraphicsExternalImageHandleTypes.VulkanOpaquePosixFileDescriptor);
  235. }
  236. public ImageTiling Tiling => ImageTiling.Optimal;
  237. public bool IsDirectXBacked => _d3dTexture2D != null;
  238. internal void TransitionLayout(CommandBuffer commandBuffer,
  239. ImageLayout fromLayout, AccessFlags fromAccessFlags,
  240. ImageLayout destinationLayout, AccessFlags destinationAccessFlags)
  241. {
  242. VulkanMemoryHelper.TransitionLayout(Api, commandBuffer, InternalHandle,
  243. fromLayout,
  244. fromAccessFlags,
  245. destinationLayout, destinationAccessFlags,
  246. MipLevels);
  247. _currentLayout = destinationLayout;
  248. _currentAccessFlags = destinationAccessFlags;
  249. }
  250. internal void TransitionLayout(CommandBuffer commandBuffer,
  251. ImageLayout destinationLayout, AccessFlags destinationAccessFlags)
  252. => TransitionLayout(commandBuffer, _currentLayout, _currentAccessFlags, destinationLayout,
  253. destinationAccessFlags);
  254. internal void TransitionLayout(ImageLayout destinationLayout, AccessFlags destinationAccessFlags)
  255. {
  256. var commandBuffer = _commandBufferPool.CreateCommandBuffer();
  257. commandBuffer.BeginRecording();
  258. TransitionLayout(commandBuffer.InternalHandle, destinationLayout, destinationAccessFlags);
  259. commandBuffer.EndRecording();
  260. commandBuffer.Submit();
  261. }
  262. public void TransitionLayout(uint destinationLayout, uint destinationAccessFlags)
  263. {
  264. TransitionLayout((ImageLayout)destinationLayout, (AccessFlags)destinationAccessFlags);
  265. }
  266. public unsafe void Dispose()
  267. {
  268. Api.DestroyImageView(_device, _imageView, null);
  269. Api.DestroyImage(_device, InternalHandle, null);
  270. Api.FreeMemory(_device, _imageMemory, null);
  271. _imageView = default;
  272. InternalHandle = default;
  273. _imageMemory = default;
  274. }
  275. public void SaveTexture(string path)
  276. {
  277. if (_vk.GrContext == null)
  278. {
  279. if (_hasIOSurface)
  280. {
  281. var surf = ExportIOSurface();
  282. if (NativeMethods.IOSurfaceLock(surf, 0, IntPtr.Zero) != 0)
  283. throw new Exception("IOSurfaceLock failed");
  284. var w = (int)NativeMethods.IOSurfaceGetWidth(surf);
  285. var h = (int)NativeMethods.IOSurfaceGetHeight(surf);
  286. var sstride = NativeMethods.IOSurfaceGetBytesPerRow(surf);
  287. var pSurface = NativeMethods.IOSurfaceGetBaseAddress(surf);
  288. using var b = new Avalonia.Media.Imaging.Bitmap(PixelFormat.Bgra8888,
  289. AlphaFormat.Premul, pSurface, new PixelSize(w, h),
  290. new Vector(96, 96), (int)sstride);
  291. b.Save(path);
  292. NativeMethods.IOSurfaceUnlock(surf, 0, IntPtr.Zero);
  293. return;
  294. }
  295. else
  296. throw new NotSupportedException("Need skia to dump textures, sorry");
  297. }
  298. _vk.GrContext.ResetContext();
  299. var _image = this;
  300. var imageInfo = new GRVkImageInfo()
  301. {
  302. CurrentQueueFamily = _vk.QueueFamilyIndex,
  303. Format = (uint)_image.Format,
  304. Image = _image.Handle,
  305. ImageLayout = (uint)_image.CurrentLayout,
  306. ImageTiling = (uint)_image.Tiling,
  307. ImageUsageFlags = (uint)_image.UsageFlags,
  308. LevelCount = _image.MipLevels,
  309. SampleCount = 1,
  310. Protected = false,
  311. Alloc = new GRVkAlloc()
  312. {
  313. Memory = _image.MemoryHandle, Flags = 0, Offset = 0, Size = _image.MemorySize
  314. }
  315. };
  316. using (var backendTexture = new GRBackendRenderTarget(_image.Size.Width, _image.Size.Height, 1,
  317. imageInfo))
  318. using (var surface = SKSurface.Create(_vk.GrContext, backendTexture,
  319. GRSurfaceOrigin.TopLeft,
  320. SKColorType.Rgba8888, SKColorSpace.CreateSrgb()))
  321. {
  322. using var snap = surface.Snapshot();
  323. using var encoded = snap.Encode();
  324. using (var s = File.Create(path))
  325. encoded.SaveTo(s);
  326. }
  327. }
  328. }