VulkanContext.cs 14 KB

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