// Основан на примере 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 ImageExportedSemaphore { get; init; } public Semaphore ImageRenderedSemaphore { get; init; } public CommandPool CommandPool { get; init; } public CommandBuffer CommandBuffer { get; init; } public KhrExternalMemoryFd KhrExternalMemoryFd { get; init; } public KhrExternalSemaphoreFd KhrExternalSemaphoreFd { get; init; } public KhrExternalFenceFd KhrExternalFenceFd { 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_fence_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", "VK_KHR_external_fence", "VK_KHR_external_fence_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); ImageExportedSemaphore = CreateSemaphore(Api, Device); ImageRenderedSemaphore = CreateSemaphore(Api, Device); CommandPool = CreateCommandPool(Api, Device, QueueFamilyIndex); CommandBuffer = CreateCommandBuffer(Api, Device, CommandPool); if (!Api.TryGetDeviceExtension(Instance, Device, out var khrExternalMemoryFd)) throw new Exception("KhrExternalMemoryFd import error"); KhrExternalMemoryFd = khrExternalMemoryFd; if (!Api.TryGetDeviceExtension(Instance, Device, out var khrExternalSemaphoreFd)) throw new Exception("KhrExternalMemoryFd import error"); KhrExternalSemaphoreFd = khrExternalSemaphoreFd; if (!Api.TryGetDeviceExtension(Instance, Device, out var khrExternalFenceFd)) throw new Exception("KhrExternalFenceFd import error"); KhrExternalFenceFd = khrExternalFenceFd; } 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; } private static CommandPool CreateCommandPool(Vk api, Device device, uint queueFamilyIndex) { var commandPoolCreateInfo = new CommandPoolCreateInfo { SType = StructureType.CommandPoolCreateInfo, Flags = CommandPoolCreateFlags.ResetCommandBufferBit, QueueFamilyIndex = queueFamilyIndex }; api.CreateCommandPool(device, &commandPoolCreateInfo, null, out var commandPool).ThrowOnError(); return commandPool; } private static CommandBuffer CreateCommandBuffer(Vk api, Device device, CommandPool commandPool) { var commandBufferAllocateInfo = new CommandBufferAllocateInfo { SType = StructureType.CommandBufferAllocateInfo, CommandPool = commandPool, Level = CommandBufferLevel.Primary, CommandBufferCount = 1 }; api.AllocateCommandBuffers(device, &commandBufferAllocateInfo, out var commandBuffer).ThrowOnError(); return commandBuffer; } protected virtual void Dispose(bool disposing) { if (!_disposedValue) { if (disposing) { if (Api is null) return; Api.DestroySemaphore(Device, ImageExportedSemaphore, null); Api.DestroySemaphore(Device, ImageRenderedSemaphore, null); Api.FreeCommandBuffers(Device, CommandPool, [CommandBuffer]); Api.DestroyCommandPool(Device, CommandPool, null); Api.DestroyDevice(Device, null); } _disposedValue = true; } } public void Dispose() { Dispose(disposing: true); GC.SuppressFinalize(this); } }