// Основан на примере GpuInterop Avalonia // https://github.com/AvaloniaUI/Avalonia/blob/5f3dbae22244e830b464fc680c6c28ca124be41a/samples/GpuInterop/VulkanDemo/VulkanContext.cs using Silk.NET.Core; using Silk.NET.Vulkan; using Silk.NET.Vulkan.Extensions.KHR; using Semaphore = Silk.NET.Vulkan.Semaphore; namespace GSS2.Vulkan; unsafe public class VulkanContext : IDisposable { private bool _disposedValue; public Vk Api { get; init; } public Instance Instance { get; init; } public PhysicalDevice PhysicalDevice { get; init; } public Device Device { get; init; } public uint QueueFamilyIndex { get; init; } public Queue Queue { get; init; } public Semaphore ImageAvailableSemaphore { get; init; } public Semaphore RenderFinishedSemaphore { get; init; } public VulkanContext(string applicationName, string engineName) { Api = Vk.GetApi(); List instanceExtensions = [ "VK_KHR_get_physical_device_properties2", "VK_KHR_external_memory_capabilities", "VK_KHR_external_semaphore_capabilities", "VK_EXT_debug_utils" ]; List instanceLayers = [ ]; List deviceExtensions = [ "VK_KHR_external_memory", "VK_KHR_external_memory_fd", "VK_KHR_external_semaphore", "VK_KHR_external_semaphore_fd" ]; Instance = CreateInstance(Api, applicationName, engineName, instanceExtensions, instanceLayers); PhysicalDevice = FindPhysicalDevice(Api, Instance, deviceExtensions); (Device, QueueFamilyIndex) = CreateDevice(Api, Instance, PhysicalDevice, deviceExtensions); Queue = CreateQueue(Api, Device, QueueFamilyIndex); ImageAvailableSemaphore = CreateSemaphore(Api, Device); RenderFinishedSemaphore = CreateSemaphore(Api, Device); } public int ExportSemaphoreFd(Semaphore semaphore) { if (!Api.TryGetDeviceExtension(Instance, Device, out var extension)) throw new InvalidOperationException($"No {KhrExternalSemaphoreFd.ExtensionName} device extension"); var info = new SemaphoreGetFdInfoKHR() { SType = StructureType.SemaphoreGetFDInfoKhr, Semaphore = semaphore, HandleType = ExternalSemaphoreHandleTypeFlags.OpaqueFDBit }; extension.GetSemaphoreF(Device, in info, out var fd).ThrowOnError(); return fd; } private static Instance CreateInstance(Vk api, string applicationName, string engineName, List extentions, List layers) { using var pApplicationName = new ByteString(applicationName); using var pEngineName = new ByteString(engineName); var applicationInfo = new ApplicationInfo { SType = StructureType.ApplicationInfo, PApplicationName = pApplicationName, PEngineName = pEngineName, ApiVersion = new Version32(1, 1, 0), EngineVersion = new Version32(1, 0, 0), ApplicationVersion = new Version32(1, 0, 0) }; using var pExtensions = new ByteStringList(extentions); using var pLayers = new ByteStringList(layers); var instanceCreateInfo = new InstanceCreateInfo { SType = StructureType.InstanceCreateInfo, PApplicationInfo = &applicationInfo, PpEnabledExtensionNames = pExtensions, EnabledExtensionCount = checked((uint)pExtensions.Count), PpEnabledLayerNames = pLayers, EnabledLayerCount = checked((uint)pLayers.Count), Flags = default }; api.CreateInstance(in instanceCreateInfo, null, out var Instance).ThrowOnError(); return Instance; } private static PhysicalDevice FindPhysicalDevice(Vk api, Instance instance, List extensions) { uint count = 0; api.EnumeratePhysicalDevices(instance, ref count, null).ThrowOnError(); var physicalDevices = stackalloc PhysicalDevice[(int)count]; api.EnumeratePhysicalDevices(instance, ref count, physicalDevices).ThrowOnError(); for (uint c = 0; c < count; c++) { if (extensions.Any(e => !api.IsDeviceExtensionPresent(physicalDevices[c], e))) continue; return physicalDevices[c]; } throw new Exception("Suшtable Device not found"); } private static (Device device, uint queueFamilyIndex) CreateDevice(Vk api, Instance instance, PhysicalDevice physicalDevice, List extensions) { uint queueFamilyCount = 0; api.GetPhysicalDeviceQueueFamilyProperties(physicalDevice, ref queueFamilyCount, null); var queueFamilyProperties = new QueueFamilyProperties[(int)queueFamilyCount]; fixed (QueueFamilyProperties* pQueueFamilyProperties = queueFamilyProperties) api.GetPhysicalDeviceQueueFamilyProperties(physicalDevice, ref queueFamilyCount, pQueueFamilyProperties); for (uint i = 0; i < queueFamilyCount; i++) { var queueFamily = queueFamilyProperties[i]; if (!queueFamily.QueueFlags.HasFlag(QueueFlags.GraphicsBit)) continue; var queuePriorities = new float[(int)queueFamily.QueueCount]; for (var j = 0; j < queueFamily.QueueCount; j++) queuePriorities[j] = 1f; var features = new PhysicalDeviceFeatures(); Device device; fixed (float* pQueuePriorities = queuePriorities) { var queueCreateInfo = new DeviceQueueCreateInfo { SType = StructureType.DeviceQueueCreateInfo, QueueFamilyIndex = i, QueueCount = queueFamily.QueueCount, PQueuePriorities = pQueuePriorities }; using var pEnabledDeviceExtensions = new ByteStringList(extensions); var deviceCreateInfo = new DeviceCreateInfo { SType = StructureType.DeviceCreateInfo, QueueCreateInfoCount = 1, PQueueCreateInfos = &queueCreateInfo, PpEnabledExtensionNames = pEnabledDeviceExtensions, EnabledExtensionCount = checked((uint)pEnabledDeviceExtensions.Count), PEnabledFeatures = &features }; api.CreateDevice(physicalDevice, in deviceCreateInfo, null, out device).ThrowOnError(); return (device, i); } } throw new Exception("Cannot create device"); } private static Queue CreateQueue(Vk api, Device device, uint queueFamilyIndex) { api.GetDeviceQueue(device, queueFamilyIndex, 0, out var queue); return queue; } private static Semaphore CreateSemaphore(Vk api, Device device) { var semaphoreExportInfo = new ExportSemaphoreCreateInfo { SType = StructureType.ExportSemaphoreCreateInfo, HandleTypes = ExternalSemaphoreHandleTypeFlags.OpaqueFDBit }; var semaphoreCreateInfo = new SemaphoreCreateInfo { SType = StructureType.SemaphoreCreateInfo, PNext = &semaphoreExportInfo }; api.CreateSemaphore(device, in semaphoreCreateInfo, null, out var semaphore).ThrowOnError(); return semaphore; } protected virtual void Dispose(bool disposing) { if (!_disposedValue) { if (disposing) { if (Api is null) return; Api.DestroySemaphore(Device, ImageAvailableSemaphore, null); Api.DestroySemaphore(Device, RenderFinishedSemaphore, null); } _disposedValue = true; } } public void Dispose() { Dispose(disposing: true); GC.SuppressFinalize(this); } }