VulkanContext.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Runtime.InteropServices;
  5. using Avalonia;
  6. using Avalonia.Platform;
  7. using Avalonia.Rendering.Composition;
  8. using Avalonia.Vulkan;
  9. using Silk.NET.Core;
  10. using Silk.NET.Core.Native;
  11. using Silk.NET.Vulkan;
  12. using Silk.NET.Vulkan.Extensions.EXT;
  13. using Silk.NET.Vulkan.Extensions.KHR;
  14. using SilkNetDemo;
  15. using SkiaSharp;
  16. using D3DDevice = SharpDX.Direct3D11.Device;
  17. using DxgiDevice = SharpDX.DXGI.Device;
  18. namespace GpuInterop.VulkanDemo;
  19. public unsafe class VulkanContext : IDisposable
  20. {
  21. public Vk Api { get; init; }
  22. public Instance Instance { get; init; }
  23. public PhysicalDevice PhysicalDevice { get; init; }
  24. public Device Device { get; init; }
  25. public Queue Queue { get; init; }
  26. public uint QueueFamilyIndex { get; init; }
  27. public VulkanCommandBufferPool Pool { get; init; }
  28. public GRContext GrContext { get; init; }
  29. public DescriptorPool DescriptorPool { get; init; }
  30. public D3DDevice? D3DDevice { get; init; }
  31. public static (VulkanContext? result, string info) TryCreate(ICompositionGpuInterop gpuInterop)
  32. {
  33. using var appName = new ByteString("GpuInterop");
  34. using var engineName = new ByteString("Test");
  35. var applicationInfo = new ApplicationInfo
  36. {
  37. SType = StructureType.ApplicationInfo,
  38. PApplicationName = appName,
  39. ApiVersion = new Version32(1, 1, 0),
  40. PEngineName = appName,
  41. EngineVersion = new Version32(1, 0, 0),
  42. ApplicationVersion = new Version32(1, 0, 0)
  43. };
  44. var enabledExtensions = new List<string>()
  45. {
  46. "VK_KHR_get_physical_device_properties2",
  47. "VK_KHR_external_memory_capabilities",
  48. "VK_KHR_external_semaphore_capabilities"
  49. };
  50. var enabledLayers = new List<string>();
  51. Vk api = Vk.GetApi();
  52. enabledExtensions.Add("VK_EXT_debug_utils");
  53. if (IsLayerAvailable(api, "VK_LAYER_KHRONOS_validation"))
  54. enabledLayers.Add("VK_LAYER_KHRONOS_validation");
  55. Instance vkInstance = default;
  56. Silk.NET.Vulkan.PhysicalDevice physicalDevice = default;
  57. Device device = default;
  58. DescriptorPool descriptorPool = default;
  59. VulkanCommandBufferPool? pool = null;
  60. GRContext? grContext = null;
  61. try
  62. {
  63. using var pRequiredExtensions = new ByteStringList(enabledExtensions);
  64. using var pEnabledLayers = new ByteStringList(enabledLayers);
  65. api.CreateInstance(new InstanceCreateInfo
  66. {
  67. SType = StructureType.InstanceCreateInfo,
  68. PApplicationInfo = &applicationInfo,
  69. PpEnabledExtensionNames = pRequiredExtensions,
  70. EnabledExtensionCount = pRequiredExtensions.UCount,
  71. PpEnabledLayerNames = pEnabledLayers,
  72. EnabledLayerCount = pEnabledLayers.UCount
  73. }, null, out vkInstance).ThrowOnError();
  74. if (api.TryGetInstanceExtension(vkInstance, out ExtDebugUtils debugUtils))
  75. {
  76. var debugCreateInfo = new DebugUtilsMessengerCreateInfoEXT
  77. {
  78. SType = StructureType.DebugUtilsMessengerCreateInfoExt,
  79. MessageSeverity = DebugUtilsMessageSeverityFlagsEXT.DebugUtilsMessageSeverityVerboseBitExt |
  80. DebugUtilsMessageSeverityFlagsEXT.DebugUtilsMessageSeverityWarningBitExt |
  81. DebugUtilsMessageSeverityFlagsEXT.DebugUtilsMessageSeverityErrorBitExt,
  82. MessageType = DebugUtilsMessageTypeFlagsEXT.DebugUtilsMessageTypeGeneralBitExt |
  83. DebugUtilsMessageTypeFlagsEXT.DebugUtilsMessageTypeValidationBitExt |
  84. DebugUtilsMessageTypeFlagsEXT.DebugUtilsMessageTypePerformanceBitExt,
  85. PfnUserCallback = new PfnDebugUtilsMessengerCallbackEXT(LogCallback),
  86. };
  87. debugUtils.CreateDebugUtilsMessenger(vkInstance, debugCreateInfo, null, out var messenger);
  88. }
  89. var requireDeviceExtensions = new List<string>
  90. {
  91. "VK_KHR_external_memory",
  92. "VK_KHR_external_semaphore"
  93. };
  94. if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
  95. {
  96. if (!gpuInterop.SupportedImageHandleTypes.Contains(KnownPlatformGraphicsExternalImageHandleTypes
  97. .D3D11TextureGlobalSharedHandle)
  98. )
  99. return (null, "Image sharing is not supported by the current backend");
  100. requireDeviceExtensions.Add(KhrExternalMemoryWin32.ExtensionName);
  101. requireDeviceExtensions.Add(KhrExternalSemaphoreWin32.ExtensionName);
  102. requireDeviceExtensions.Add("VK_KHR_dedicated_allocation");
  103. requireDeviceExtensions.Add("VK_KHR_get_memory_requirements2");
  104. }
  105. else
  106. {
  107. if (!gpuInterop.SupportedImageHandleTypes.Contains(KnownPlatformGraphicsExternalImageHandleTypes
  108. .VulkanOpaquePosixFileDescriptor)
  109. || !gpuInterop.SupportedSemaphoreTypes.Contains(KnownPlatformGraphicsExternalSemaphoreHandleTypes
  110. .VulkanOpaquePosixFileDescriptor)
  111. )
  112. return (null, "Image sharing is not supported by the current backend");
  113. requireDeviceExtensions.Add(KhrExternalMemoryFd.ExtensionName);
  114. requireDeviceExtensions.Add(KhrExternalSemaphoreFd.ExtensionName);
  115. }
  116. uint count = 0;
  117. api.EnumeratePhysicalDevices(vkInstance, ref count, null).ThrowOnError();
  118. var physicalDevices = stackalloc PhysicalDevice[(int)count];
  119. api.EnumeratePhysicalDevices(vkInstance, ref count, physicalDevices)
  120. .ThrowOnError();
  121. for (uint c = 0; c < count; c++)
  122. {
  123. if (requireDeviceExtensions.Any(ext => !api.IsDeviceExtensionPresent(physicalDevices[c], ext)))
  124. continue;
  125. var physicalDeviceIDProperties = new PhysicalDeviceIDProperties()
  126. {
  127. SType = StructureType.PhysicalDeviceIDProperties
  128. };
  129. var physicalDeviceProperties2 = new PhysicalDeviceProperties2()
  130. {
  131. SType = StructureType.PhysicalDeviceProperties2,
  132. PNext = &physicalDeviceIDProperties
  133. };
  134. api.GetPhysicalDeviceProperties2(physicalDevices[c], &physicalDeviceProperties2);
  135. if (gpuInterop.DeviceLuid != null && physicalDeviceIDProperties.DeviceLuidvalid)
  136. {
  137. if (!new Span<byte>(physicalDeviceIDProperties.DeviceLuid, 8)
  138. .SequenceEqual(gpuInterop.DeviceLuid))
  139. continue;
  140. }
  141. else if (gpuInterop.DeviceUuid != null)
  142. {
  143. if (!new Span<byte>(physicalDeviceIDProperties.DeviceUuid, 16)
  144. .SequenceEqual(gpuInterop?.DeviceUuid))
  145. continue;
  146. }
  147. physicalDevice = physicalDevices[c];
  148. var name = Marshal.PtrToStringAnsi(new IntPtr(physicalDeviceProperties2.Properties.DeviceName))!;
  149. uint queueFamilyCount = 0;
  150. api.GetPhysicalDeviceQueueFamilyProperties(physicalDevice, ref queueFamilyCount, null);
  151. var familyProperties = stackalloc QueueFamilyProperties[(int)queueFamilyCount];
  152. api.GetPhysicalDeviceQueueFamilyProperties(physicalDevice, ref queueFamilyCount, familyProperties);
  153. for (uint queueFamilyIndex = 0; queueFamilyIndex < queueFamilyCount; queueFamilyIndex++)
  154. {
  155. var family = familyProperties[c];
  156. if (!family.QueueFlags.HasAllFlags(QueueFlags.GraphicsBit))
  157. continue;
  158. var queuePriorities = stackalloc float[(int)family.QueueCount];
  159. for (var i = 0; i < family.QueueCount; i++)
  160. queuePriorities[i] = 1f;
  161. var features = new PhysicalDeviceFeatures();
  162. var queueCreateInfo = new DeviceQueueCreateInfo
  163. {
  164. SType = StructureType.DeviceQueueCreateInfo,
  165. QueueFamilyIndex = queueFamilyIndex,
  166. QueueCount = family.QueueCount,
  167. PQueuePriorities = queuePriorities
  168. };
  169. using var pEnabledDeviceExtensions = new ByteStringList(requireDeviceExtensions);
  170. var deviceCreateInfo = new DeviceCreateInfo
  171. {
  172. SType = StructureType.DeviceCreateInfo,
  173. QueueCreateInfoCount = 1,
  174. PQueueCreateInfos = &queueCreateInfo,
  175. PpEnabledExtensionNames = pEnabledDeviceExtensions,
  176. EnabledExtensionCount = pEnabledDeviceExtensions.UCount,
  177. PEnabledFeatures = &features
  178. };
  179. api.CreateDevice(physicalDevice, in deviceCreateInfo, null, out device)
  180. .ThrowOnError();
  181. api.GetDeviceQueue(device, queueFamilyIndex, 0, out var queue);
  182. var descriptorPoolSize = new DescriptorPoolSize
  183. {
  184. Type = DescriptorType.UniformBuffer, DescriptorCount = 16
  185. };
  186. var descriptorPoolInfo = new DescriptorPoolCreateInfo
  187. {
  188. SType = StructureType.DescriptorPoolCreateInfo,
  189. PoolSizeCount = 1,
  190. PPoolSizes = &descriptorPoolSize,
  191. MaxSets = 16,
  192. Flags = DescriptorPoolCreateFlags.FreeDescriptorSetBit
  193. };
  194. api.CreateDescriptorPool(device, &descriptorPoolInfo, null, out descriptorPool)
  195. .ThrowOnError();
  196. pool = new VulkanCommandBufferPool(api, device, queue, queueFamilyIndex);
  197. grContext = GRContext.CreateVulkan(new GRVkBackendContext
  198. {
  199. VkInstance = vkInstance.Handle,
  200. VkDevice = device.Handle,
  201. VkQueue = queue.Handle,
  202. GraphicsQueueIndex = queueFamilyIndex,
  203. VkPhysicalDevice = physicalDevice.Handle,
  204. GetProcedureAddress = (proc, _, _) =>
  205. {
  206. var rv = api.GetDeviceProcAddr(device, proc);
  207. if (rv != IntPtr.Zero)
  208. return rv;
  209. rv = api.GetInstanceProcAddr(vkInstance, proc);
  210. if (rv != IntPtr.Zero)
  211. return rv;
  212. return api.GetInstanceProcAddr(default, proc);
  213. }
  214. });
  215. D3DDevice? d3dDevice = null;
  216. if (physicalDeviceIDProperties.DeviceLuidvalid &&
  217. RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
  218. d3dDevice = D3DMemoryHelper.CreateDeviceByLuid(
  219. new Span<byte>(physicalDeviceIDProperties.DeviceLuid, 8));
  220. var dxgiDevice = d3dDevice?.QueryInterface<DxgiDevice>();
  221. return (new VulkanContext
  222. {
  223. Api = api,
  224. Device = device,
  225. Instance = vkInstance,
  226. PhysicalDevice = physicalDevice,
  227. Queue = queue,
  228. QueueFamilyIndex = queueFamilyIndex,
  229. Pool = pool,
  230. DescriptorPool = descriptorPool,
  231. GrContext = grContext,
  232. D3DDevice = d3dDevice
  233. }, name);
  234. }
  235. return (null, "No suitable device queue found");
  236. }
  237. return (null, "Suitable device not found");
  238. }
  239. catch (Exception e)
  240. {
  241. return (null, e.ToString());
  242. }
  243. finally
  244. {
  245. if (grContext == null && api != null)
  246. {
  247. pool?.Dispose();
  248. if (descriptorPool.Handle != default)
  249. api.DestroyDescriptorPool(device, descriptorPool, null);
  250. if (device.Handle != default)
  251. api.DestroyDevice(device, null);
  252. }
  253. }
  254. }
  255. private static unsafe bool IsLayerAvailable(Vk api, string layerName)
  256. {
  257. uint layerPropertiesCount;
  258. api.EnumerateInstanceLayerProperties(&layerPropertiesCount, null).ThrowOnError();
  259. var layerProperties = new LayerProperties[layerPropertiesCount];
  260. fixed (LayerProperties* pLayerProperties = layerProperties)
  261. {
  262. api.EnumerateInstanceLayerProperties(&layerPropertiesCount, layerProperties).ThrowOnError();
  263. for (var i = 0; i < layerPropertiesCount; i++)
  264. {
  265. var currentLayerName = Marshal.PtrToStringAnsi((IntPtr)pLayerProperties[i].LayerName);
  266. if (currentLayerName == layerName) return true;
  267. }
  268. }
  269. return false;
  270. }
  271. private static unsafe uint LogCallback(DebugUtilsMessageSeverityFlagsEXT messageSeverity, DebugUtilsMessageTypeFlagsEXT messageTypes, DebugUtilsMessengerCallbackDataEXT* pCallbackData, void* pUserData)
  272. {
  273. if (messageSeverity != DebugUtilsMessageSeverityFlagsEXT.VerboseBitExt)
  274. {
  275. var message = Marshal.PtrToStringAnsi((nint)pCallbackData->PMessage);
  276. Console.WriteLine(message);
  277. }
  278. return Vk.False;
  279. }
  280. public void Dispose()
  281. {
  282. D3DDevice?.Dispose();
  283. GrContext.Dispose();
  284. Pool.Dispose();
  285. Api.DestroyDescriptorPool(Device, DescriptorPool, null);
  286. Api.DestroyDevice(Device, null);
  287. }
  288. }