VulkanImage.cs 11 KB

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