forked from amkovkov/GranuSightSoftware2
feat: GSS2.Test - UI+DI, Vulkan render test
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
// Основан на примере GpuInterop Avalonia
|
||||
// https://github.com/AvaloniaUI/Avalonia/blob/5f3dbae22244e830b464fc680c6c28ca124be41a/samples/GpuInterop/VulkanDemo/ByteString.cs
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace GSS2.Vulkan;
|
||||
|
||||
unsafe sealed class ByteString : IDisposable
|
||||
{
|
||||
private bool _disposedValue;
|
||||
private readonly byte* _pointer;
|
||||
|
||||
public ByteString(string s)
|
||||
{
|
||||
_pointer = (byte*)Marshal.StringToHGlobalAnsi(s);
|
||||
}
|
||||
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (!_disposedValue)
|
||||
{
|
||||
Marshal.FreeHGlobal(new IntPtr(_pointer));
|
||||
_disposedValue = true;
|
||||
}
|
||||
}
|
||||
~ByteString() => Dispose(disposing: false);
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(disposing: true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
public static implicit operator byte*(ByteString obj) => obj._pointer;
|
||||
}
|
||||
|
||||
unsafe sealed class ByteStringList : IDisposable
|
||||
{
|
||||
private bool _disposedValue;
|
||||
private readonly List<ByteString> _list;
|
||||
private readonly byte** _pointer;
|
||||
|
||||
public int Count => _list.Count;
|
||||
|
||||
public ByteStringList(IEnumerable<string> list)
|
||||
{
|
||||
_list = list.Select(x => new ByteString(x)).ToList();
|
||||
_pointer = (byte**)Marshal.AllocHGlobal(IntPtr.Size * _list.Count + 1);
|
||||
for (var c = 0; c < _list.Count; c++)
|
||||
_pointer[c] = (byte*)_list[c];
|
||||
}
|
||||
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (!_disposedValue)
|
||||
{
|
||||
Marshal.FreeHGlobal(new IntPtr(_pointer));
|
||||
_disposedValue = true;
|
||||
}
|
||||
}
|
||||
~ByteStringList() => Dispose(disposing: false);
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(disposing: true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
public static implicit operator byte**(ByteStringList obj) => obj._pointer;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="10.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="10.0.0" />
|
||||
<PackageReference Include="Avalonia" Version="11.3.11" />
|
||||
<PackageReference Include="Avalonia.Desktop" Version="11.3.11" />
|
||||
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.3.11" />
|
||||
<PackageReference Include="Avalonia.Fonts.Inter" Version="11.3.11" />
|
||||
<!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
|
||||
<PackageReference Include="Avalonia.Diagnostics" Version="11.3.11">
|
||||
<IncludeAssets Condition="'$(Configuration)' != 'Debug'">None</IncludeAssets>
|
||||
<PrivateAssets Condition="'$(Configuration)' != 'Debug'">All</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Silk.NET.Direct3D11" Version="2.22.0" />
|
||||
<PackageReference Include="Silk.NET.Direct3D.Compilers" Version="2.22.0" />
|
||||
<PackageReference Include="Silk.NET.Vulkan" Version="2.22.0" />
|
||||
<PackageReference Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.22.0" />
|
||||
<PackageReference Include="Silk.NET.Vulkan.Extensions.KHR" Version="2.22.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
// Основан на примере GpuInterop Avalonia
|
||||
// https://github.com/AvaloniaUI/Avalonia/blob/5f3dbae22244e830b464fc680c6c28ca124be41a/samples/GpuInterop/VulkanDemo/VulkanExtensions.cs
|
||||
|
||||
using Silk.NET.Vulkan;
|
||||
|
||||
namespace GSS2.Vulkan;
|
||||
|
||||
public static class VulkanExtensions
|
||||
{
|
||||
public static void ThrowOnError(this Result result)
|
||||
{
|
||||
if (result != Result.Success)
|
||||
throw new Exception($"Unexpected API error \"{result}\"");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user