123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392 |
- using System;
- using System.Collections.Generic;
- using System.IO;
- using System.Linq;
- using System.Runtime.InteropServices;
- using Avalonia.Platform;
- using Avalonia.Rendering.Composition;
- using Avalonia.Vulkan;
- using Silk.NET.Core;
- using Silk.NET.Core.Contexts;
- using Silk.NET.Core.Loader;
- using Silk.NET.Vulkan;
- using Silk.NET.Vulkan.Extensions.EXT;
- using Silk.NET.Vulkan.Extensions.KHR;
- using SilkNetDemo;
- using SkiaSharp;
- using D3DDevice = SharpDX.Direct3D11.Device;
- #pragma warning disable CS0162 // Unreachable code detected
- namespace GpuInterop.VulkanDemo;
- public unsafe class VulkanContext : IDisposable
- {
- public required Vk Api { get; init; }
- public required Instance Instance { get; init; }
- public required PhysicalDevice PhysicalDevice { get; init; }
- public required Device Device { get; init; }
- public required Queue Queue { get; init; }
- public required uint QueueFamilyIndex { get; init; }
- public required VulkanCommandBufferPool Pool { get; init; }
- public required GRContext? GrContext { get; init; }
- public required DescriptorPool DescriptorPool { get; init; }
- public required D3DDevice? D3DDevice { get; init; }
- public static (VulkanContext? result, string info) TryCreate(ICompositionGpuInterop gpuInterop)
- {
- using var appName = new ByteString("GpuInterop");
- using var engineName = new ByteString("Test");
- var applicationInfo = new ApplicationInfo
- {
- SType = StructureType.ApplicationInfo,
- PApplicationName = appName,
- ApiVersion = new Version32(1, 1, 0),
- PEngineName = appName,
- EngineVersion = new Version32(1, 0, 0),
- ApplicationVersion = new Version32(1, 0, 0)
- };
- var enabledExtensions = new List<string>()
- {
- "VK_KHR_get_physical_device_properties2"
- };
- if(RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
- enabledExtensions.Add("VK_KHR_portability_enumeration");
- if (!RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
- {
- enabledExtensions.AddRange([
- "VK_KHR_external_memory_capabilities",
- "VK_KHR_external_semaphore_capabilities"
- ]);
- }
- var enabledLayers = new List<string>();
-
- Vk api = GetApi();
- enabledExtensions.Add("VK_EXT_debug_utils");
- if (IsLayerAvailable(api, "VK_LAYER_KHRONOS_validation"))
- enabledLayers.Add("VK_LAYER_KHRONOS_validation");
- Device device = default;
- DescriptorPool descriptorPool = default;
- VulkanCommandBufferPool? pool = null;
- GRContext? grContext = null;
- bool success = false;
- try
- {
- enabledLayers.Clear();
- using var pRequiredExtensions = new ByteStringList(enabledExtensions);
- using var pEnabledLayers = new ByteStringList(enabledLayers);
- api.CreateInstance(new InstanceCreateInfo
- {
- SType = StructureType.InstanceCreateInfo,
- PApplicationInfo = &applicationInfo,
- PpEnabledExtensionNames = pRequiredExtensions,
- EnabledExtensionCount = pRequiredExtensions.UCount,
- PpEnabledLayerNames = pEnabledLayers,
- EnabledLayerCount = pEnabledLayers.UCount,
- Flags = RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? InstanceCreateFlags.EnumeratePortabilityBitKhr : default
- }, null, out var vkInstance).ThrowOnError();
- if (api.TryGetInstanceExtension(vkInstance, out ExtDebugUtils debugUtils))
- {
- var debugCreateInfo = new DebugUtilsMessengerCreateInfoEXT
- {
- SType = StructureType.DebugUtilsMessengerCreateInfoExt,
- MessageSeverity = DebugUtilsMessageSeverityFlagsEXT.VerboseBitExt |
- DebugUtilsMessageSeverityFlagsEXT.WarningBitExt |
- DebugUtilsMessageSeverityFlagsEXT.ErrorBitExt,
- MessageType = DebugUtilsMessageTypeFlagsEXT.GeneralBitExt |
- DebugUtilsMessageTypeFlagsEXT.ValidationBitExt |
- DebugUtilsMessageTypeFlagsEXT.PerformanceBitExt,
- PfnUserCallback = new PfnDebugUtilsMessengerCallbackEXT(LogCallback),
- };
- debugUtils.CreateDebugUtilsMessenger(vkInstance, debugCreateInfo, null, out _);
- }
- var requireDeviceExtensions = new List<string>();
- if(!RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
- {
- requireDeviceExtensions.AddRange([
- "VK_KHR_external_memory",
- "VK_KHR_external_semaphore"
- ]);
- };
- if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
- {
- if (!(gpuInterop.SupportedImageHandleTypes.Contains(KnownPlatformGraphicsExternalImageHandleTypes
- .D3D11TextureGlobalSharedHandle)
- || gpuInterop.SupportedImageHandleTypes.Contains(KnownPlatformGraphicsExternalImageHandleTypes
- .VulkanOpaqueNtHandle))
- )
- return (null, "Image sharing is not supported by the current backend");
- requireDeviceExtensions.Add(KhrExternalMemoryWin32.ExtensionName);
- requireDeviceExtensions.Add(KhrExternalSemaphoreWin32.ExtensionName);
- requireDeviceExtensions.Add("VK_KHR_dedicated_allocation");
- requireDeviceExtensions.Add("VK_KHR_get_memory_requirements2");
- }
- else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
- {
- if (!gpuInterop.SupportedImageHandleTypes.Contains(KnownPlatformGraphicsExternalImageHandleTypes
- .IOSurfaceRef)
- )
- return (null, "Image sharing is not supported by the current backend");
- requireDeviceExtensions.AddRange(["VK_EXT_metal_objects", "VK_KHR_timeline_semaphore"]);
- }
- else
- {
- if (!gpuInterop.SupportedImageHandleTypes.Contains(KnownPlatformGraphicsExternalImageHandleTypes
- .VulkanOpaquePosixFileDescriptor)
- || !gpuInterop.SupportedSemaphoreTypes.Contains(KnownPlatformGraphicsExternalSemaphoreHandleTypes
- .VulkanOpaquePosixFileDescriptor)
- )
- return (null, "Image sharing is not supported by the current backend");
- requireDeviceExtensions.Add(KhrExternalMemoryFd.ExtensionName);
- requireDeviceExtensions.Add(KhrExternalSemaphoreFd.ExtensionName);
- }
- uint count = 0;
- api.EnumeratePhysicalDevices(vkInstance, ref count, null).ThrowOnError();
- var physicalDevices = stackalloc PhysicalDevice[(int)count];
- api.EnumeratePhysicalDevices(vkInstance, ref count, physicalDevices)
- .ThrowOnError();
- for (uint c = 0; c < count; c++)
- {
- if (requireDeviceExtensions.Any(ext => !api.IsDeviceExtensionPresent(physicalDevices[c], ext)))
- continue;
- var physicalDeviceIDProperties = new PhysicalDeviceIDProperties()
- {
- SType = StructureType.PhysicalDeviceIDProperties
- };
- var physicalDeviceProperties2 = new PhysicalDeviceProperties2()
- {
- SType = StructureType.PhysicalDeviceProperties2,
- PNext = &physicalDeviceIDProperties
- };
- api.GetPhysicalDeviceProperties2(physicalDevices[c], &physicalDeviceProperties2);
- if (gpuInterop.DeviceLuid != null && physicalDeviceIDProperties.DeviceLuidvalid)
- {
- if (!new Span<byte>(physicalDeviceIDProperties.DeviceLuid, 8)
- .SequenceEqual(gpuInterop.DeviceLuid))
- continue;
- }
- else if (gpuInterop.DeviceUuid != null)
- {
- if (!new Span<byte>(physicalDeviceIDProperties.DeviceUuid, 16)
- .SequenceEqual(gpuInterop.DeviceUuid))
- continue;
- }
- var physicalDevice = physicalDevices[c];
- var name = Marshal.PtrToStringAnsi(new IntPtr(physicalDeviceProperties2.Properties.DeviceName))!;
- uint queueFamilyCount = 0;
- api.GetPhysicalDeviceQueueFamilyProperties(physicalDevice, ref queueFamilyCount, null);
- var familyProperties = stackalloc QueueFamilyProperties[(int)queueFamilyCount];
- api.GetPhysicalDeviceQueueFamilyProperties(physicalDevice, ref queueFamilyCount, familyProperties);
- for (uint queueFamilyIndex = 0; queueFamilyIndex < queueFamilyCount; queueFamilyIndex++)
- {
- var family = familyProperties[queueFamilyIndex];
- if (!family.QueueFlags.HasFlag(QueueFlags.GraphicsBit))
- continue;
- var queuePriorities = stackalloc float[(int)family.QueueCount];
- for (var i = 0; i < family.QueueCount; i++)
- queuePriorities[i] = 1f;
- var features = new PhysicalDeviceFeatures();
- var queueCreateInfo = new DeviceQueueCreateInfo
- {
- SType = StructureType.DeviceQueueCreateInfo,
- QueueFamilyIndex = queueFamilyIndex,
- QueueCount = family.QueueCount,
- PQueuePriorities = queuePriorities
- };
- using var pEnabledDeviceExtensions = new ByteStringList(requireDeviceExtensions);
- var deviceCreateInfo = new DeviceCreateInfo
- {
- SType = StructureType.DeviceCreateInfo,
- QueueCreateInfoCount = 1,
- PQueueCreateInfos = &queueCreateInfo,
- PpEnabledExtensionNames = pEnabledDeviceExtensions,
- EnabledExtensionCount = pEnabledDeviceExtensions.UCount,
- PEnabledFeatures = &features
- };
- api.CreateDevice(physicalDevice, in deviceCreateInfo, null, out device)
- .ThrowOnError();
- api.GetDeviceQueue(device, queueFamilyIndex, 0, out var queue);
- var descriptorPoolSize = new DescriptorPoolSize
- {
- Type = DescriptorType.UniformBuffer, DescriptorCount = 16
- };
- var descriptorPoolInfo = new DescriptorPoolCreateInfo
- {
- SType = StructureType.DescriptorPoolCreateInfo,
- PoolSizeCount = 1,
- PPoolSizes = &descriptorPoolSize,
- MaxSets = 16,
- Flags = DescriptorPoolCreateFlags.FreeDescriptorSetBit
- };
-
- api.CreateDescriptorPool(device, &descriptorPoolInfo, null, out descriptorPool)
- .ThrowOnError();
- pool = new VulkanCommandBufferPool(api, device, queue, queueFamilyIndex);
- if (!RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
- {
- grContext = GRContext.CreateVulkan(new GRVkBackendContext
- {
- VkInstance = vkInstance.Handle,
- VkDevice = device.Handle,
- VkQueue = queue.Handle,
- GraphicsQueueIndex = queueFamilyIndex,
- VkPhysicalDevice = physicalDevice.Handle,
- GetProcedureAddress = (proc, _, _) =>
- {
- var rv = api.GetDeviceProcAddr(device, proc);
- if (rv != IntPtr.Zero)
- return rv;
- rv = api.GetInstanceProcAddr(vkInstance, proc);
- if (rv != IntPtr.Zero)
- return rv;
- return api.GetInstanceProcAddr(default, proc);
- }
- });
- if (grContext == null)
- return (null, "Can't create Skia GrContext, device is likely broken");
- }
- D3DDevice? d3dDevice = null;
- if (physicalDeviceIDProperties.DeviceLuidvalid &&
- RuntimeInformation.IsOSPlatform(OSPlatform.Windows) &&
- !gpuInterop.SupportedImageHandleTypes.Contains(KnownPlatformGraphicsExternalImageHandleTypes.VulkanOpaqueNtHandle)
- )
- d3dDevice = D3DMemoryHelper.CreateDeviceByLuid(
- new Span<byte>(physicalDeviceIDProperties.DeviceLuid, 8));
- success = true;
- return (new VulkanContext
- {
- Api = api,
- Device = device,
- Instance = vkInstance,
- PhysicalDevice = physicalDevice,
- Queue = queue,
- QueueFamilyIndex = queueFamilyIndex,
- Pool = pool,
- DescriptorPool = descriptorPool,
- GrContext = grContext,
- D3DDevice = d3dDevice
- }, name);
- }
- return (null, "No suitable device queue found");
- }
- return (null, "Suitable device not found");
- }
- catch (Exception e)
- {
- return (null, e.ToString());
- }
- finally
- {
- if (!success)
- {
- pool?.Dispose();
- if (descriptorPool.Handle != default)
- api.DestroyDescriptorPool(device, descriptorPool, null);
- if (device.Handle != default)
- api.DestroyDevice(device, null);
- }
- }
- }
- private static unsafe bool IsLayerAvailable(Vk api, string layerName)
- {
- uint layerPropertiesCount;
- api.EnumerateInstanceLayerProperties(&layerPropertiesCount, null).ThrowOnError();
- var layerProperties = new LayerProperties[layerPropertiesCount];
- fixed (LayerProperties* pLayerProperties = layerProperties)
- {
- api.EnumerateInstanceLayerProperties(&layerPropertiesCount, layerProperties).ThrowOnError();
- for (var i = 0; i < layerPropertiesCount; i++)
- {
- var currentLayerName = Marshal.PtrToStringAnsi((IntPtr)pLayerProperties[i].LayerName);
- if (currentLayerName == layerName) return true;
- }
- }
- return false;
- }
- private static unsafe uint LogCallback(DebugUtilsMessageSeverityFlagsEXT messageSeverity, DebugUtilsMessageTypeFlagsEXT messageTypes, DebugUtilsMessengerCallbackDataEXT* pCallbackData, void* pUserData)
- {
- if (messageSeverity != DebugUtilsMessageSeverityFlagsEXT.VerboseBitExt)
- {
- var message = Marshal.PtrToStringAnsi((nint)pCallbackData->PMessage);
- Console.WriteLine(message);
- }
- return Vk.False;
- }
- private const string MacVulkanSdkGlobalPath = "/usr/local/lib/libvulkan.dylib";
- class MacLibraryNameContainer : SearchPathContainer
- {
- public override string Windows64 { get; }
- public override string Windows86 { get; }
- public override string Linux { get; }
- public override string MacOS { get; } = MacVulkanSdkGlobalPath;
- }
- private static Vk GetApi()
- {
- if (!RuntimeInformation.IsOSPlatform(OSPlatform.OSX) || !File.Exists(MacVulkanSdkGlobalPath))
- return Vk.GetApi();
- var ctx = new MultiNativeContext(new INativeContext[2]
- {
- Vk.CreateDefaultContext(new MacLibraryNameContainer().GetLibraryName()),
- null!
- });
- var ret = new Vk(ctx);
- ctx.Contexts[1] = new LamdaNativeContext((Func<string, IntPtr>) ((x) =>
- {
- if (x.EndsWith("ProcAddr"))
- return IntPtr.Zero;
- IntPtr deviceProcAddr = (IntPtr) ret.GetDeviceProcAddr(ret.CurrentDevice.GetValueOrDefault(), x);
- return deviceProcAddr != IntPtr.Zero ? deviceProcAddr : (IntPtr) ret.GetInstanceProcAddr(ret.CurrentInstance.GetValueOrDefault(), x);
- }));
- return ret;
- }
-
- public void Dispose()
- {
- D3DDevice?.Dispose();
- GrContext?.Dispose();
- Pool.Dispose();
- Api.DestroyDescriptorPool(Device, DescriptorPool, null);
- Api.DestroyDevice(Device, null);
- }
- }
|