feat: GSS2.Test - UI+DI, Vulkan render test

This commit is contained in:
2026-01-22 08:04:04 +03:00
parent 2ce8d3e966
commit 5af1c508f5
23 changed files with 1093 additions and 131 deletions
+209
View File
@@ -0,0 +1,209 @@
// Основан на примере 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<string> instanceExtensions =
[
"VK_KHR_get_physical_device_properties2",
"VK_KHR_external_memory_capabilities",
"VK_KHR_external_semaphore_capabilities",
"VK_EXT_debug_utils"
];
List<string> instanceLayers =
[
];
List<string> 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<KhrExternalSemaphoreFd>(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<string> extentions, List<string> 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<string> 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<string> 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);
}
}