diff --git a/.vscode/launch.json b/.vscode/launch.json index 4df4537c0..aca383d19 100755 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -26,11 +26,8 @@ "type": "coreclr", "request": "launch", "preLaunchTask": "buildVulkan", - "program": "${workspaceFolder}/Examples/gpu/UltralightNet.Vulkan.TestApp/bin/Debug/net6.0/UltralightNet.Vulkan.TestApp.dll", - "args": [], - "cwd": "${workspaceFolder}/Examples/gpu/UltralightNet.Vulkan.TestApp/bin/Debug/net6.0", - "console": "internalConsole", - "stopAtEntry": false, + "program": "${workspaceFolder}/Examples/gpu/UltralightNet.Vulkan.TestApp/bin/Debug/net7.0/UltralightNet.Vulkan.TestApp.dll", + "cwd": "${workspaceFolder}/Examples/gpu/UltralightNet.Vulkan.TestApp/bin/Debug/net7.0", "envFile": "${workspaceFolder}/.env" }, { diff --git a/Examples/gpu/UltralightNet.Vulkan.TestApp/Program2.cs b/Examples/gpu/UltralightNet.Vulkan.TestApp/Program2.cs deleted file mode 100644 index 9b8af8fce..000000000 --- a/Examples/gpu/UltralightNet.Vulkan.TestApp/Program2.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace VulkanExample; - -public static class Program2 -{ - public static void Main() - { - using var app = new Application(); - app.Run(); - } -} diff --git a/Examples/gpu/UltralightNet.Vulkan.TestApp/Startup/Utils.cs b/Examples/gpu/UltralightNet.Vulkan.TestApp/Startup/Utils.cs index 8282f11f9..db2cd2ed0 100644 --- a/Examples/gpu/UltralightNet.Vulkan.TestApp/Startup/Utils.cs +++ b/Examples/gpu/UltralightNet.Vulkan.TestApp/Startup/Utils.cs @@ -1,51 +1,59 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using Silk.NET.Core; +using Silk.NET.Core.Native; using Silk.NET.Vulkan; internal unsafe static class Utils { + const bool Debug = +#if DEBUG + true; +#else + false; +#endif + public static void Check(this Result result) { if (result is not Result.Success) throw new Exception("Result is not OK"); } private static byte* ToPointer(this ReadOnlySpan span) => (byte*)Unsafe.AsPointer(ref Unsafe.AsRef(in span[0])); - private static bool HasInstanceLayer(this Vk vk, ReadOnlySpan layer) - { - uint propertyCount = 0; - vk.EnumerateInstanceLayerProperties(ref propertyCount, null).Check(); - var properties = stackalloc LayerProperties[(int)propertyCount]; - vk.EnumerateInstanceLayerProperties(ref propertyCount, properties).Check(); - for (int i = 0; i < propertyCount; i++) - if (layer.SequenceEqual(MemoryMarshal.CreateReadOnlySpanFromNullTerminated(properties[i].LayerName))) return true; - return false; - } - public static void CreateInstance(Vk vk, uint extensionCount, byte** extensions, out Instance instance) + public static void CreateInstance(Vk vk, string[] extensions, out Instance instance) { ApplicationInfo applicationInfo = new( pApplicationName: "VulkanExample"u8.ToPointer(), apiVersion: Vk.Version11 ); - ReadOnlySpan validationLayer = "VK_LAYER_KHRONOS_validation"u8; - byte** layers = stackalloc byte*[1] { ToPointer(validationLayer) }; - - delegate* unmanaged[Cdecl] a = &DebugLogger; - DebugUtilsMessengerCreateInfoEXT debugUtilsMessengerCreateInfo = new( - messageSeverity: DebugUtilsMessageSeverityFlagsEXT.DebugUtilsMessageSeverityWarningBitExt | DebugUtilsMessageSeverityFlagsEXT.DebugUtilsMessageSeverityWarningBitExt, - messageType: DebugUtilsMessageTypeFlagsEXT.DebugUtilsMessageTypeValidationBitExt, - pfnUserCallback: (delegate* unmanaged[Cdecl])&DebugLogger - ); + void* next = null; - vk.CreateInstance(new InstanceCreateInfo( - pNext: vk.HasInstanceLayer(validationLayer) ? &debugUtilsMessengerCreateInfo : null, - pApplicationInfo: &applicationInfo, - enabledLayerCount: vk.HasInstanceLayer(validationLayer) ? 1u : 0u, - ppEnabledLayerNames: layers, - enabledExtensionCount: 0, - ppEnabledExtensionNames: extensions - ), null, out instance).Check(); + if (Debug) + { + DebugUtilsMessengerCreateInfoEXT debugUtilsMessengerCreateInfo = new( + messageSeverity: DebugUtilsMessageSeverityFlagsEXT.DebugUtilsMessageSeverityWarningBitExt | DebugUtilsMessageSeverityFlagsEXT.DebugUtilsMessageSeverityWarningBitExt, + messageType: DebugUtilsMessageTypeFlagsEXT.DebugUtilsMessageTypeValidationBitExt | DebugUtilsMessageTypeFlagsEXT.DebugUtilsMessageTypeGeneralBitExt | DebugUtilsMessageTypeFlagsEXT.DebugUtilsMessageTypePerformanceBitExt, + pfnUserCallback: (delegate* unmanaged[Cdecl])&DebugLogger + ); + next = &debugUtilsMessengerCreateInfo; + } + byte** extensionsPtr = (byte**)SilkMarshal.StringArrayToPtr(extensions); + try + { + vk.CreateInstance(new InstanceCreateInfo( + pNext: next, + pApplicationInfo: &applicationInfo, + enabledLayerCount: 0u, + ppEnabledLayerNames: null, + enabledExtensionCount: (uint)extensions.Length, + ppEnabledExtensionNames: extensionsPtr + ), null, out instance).Check(); + } + finally + { + for (uint i = 0; i < extensions.Length; i++) SilkMarshal.FreeString((nint)extensionsPtr[i]); + SilkMarshal.Free((nint)extensionsPtr); + } } [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })] diff --git a/Examples/gpu/UltralightNet.Vulkan.TestApp/UltralightNet.Vulkan.TestApp.csproj b/Examples/gpu/UltralightNet.Vulkan.TestApp/UltralightNet.Vulkan.TestApp.csproj index 795eb9133..b2dde6f3c 100644 --- a/Examples/gpu/UltralightNet.Vulkan.TestApp/UltralightNet.Vulkan.TestApp.csproj +++ b/Examples/gpu/UltralightNet.Vulkan.TestApp/UltralightNet.Vulkan.TestApp.csproj @@ -36,15 +36,14 @@ - - + + - + - - + diff --git a/Examples/gpu/UltralightNet.Vulkan.TestApp/VulkanExample.cs b/Examples/gpu/UltralightNet.Vulkan.TestApp/VulkanExample.cs index 910c396f8..43f657f87 100644 --- a/Examples/gpu/UltralightNet.Vulkan.TestApp/VulkanExample.cs +++ b/Examples/gpu/UltralightNet.Vulkan.TestApp/VulkanExample.cs @@ -1,6 +1,11 @@ //namespace VulkanExample; +using System.Diagnostics; +using Silk.NET.Core; +using Silk.NET.Core.Native; using Silk.NET.Vulkan; +using Silk.NET.Vulkan.Extensions.EXT; +using Silk.NET.Vulkan.Extensions.KHR; using Silk.NET.Windowing; using Application app = new(); @@ -8,13 +13,25 @@ internal unsafe partial class Application : IDisposable { - //static Application() => Window.PrioritizeSdl(); + readonly Stopwatch stopwatch = Stopwatch.StartNew(); + readonly IWindow window = Window.Create(WindowOptions.DefaultVulkan); readonly Vk vk = Vk.GetApi(); readonly Instance instance; + readonly KhrSurface khrSurface; + readonly ExtDebugUtils? debugUtils; + + readonly SurfaceKHR surface; readonly PhysicalDevice physicalDevice; + readonly uint graphicsQueueFamily = uint.MaxValue; + readonly uint presentQueueFamily = uint.MaxValue; + + readonly Device device; + readonly Queue graphicsQueue; + readonly Queue presentQueue; + public Application() { { // Window @@ -22,8 +39,17 @@ public Application() if (window.VkSurface is null) throw new PlatformNotSupportedException("Vulkan surface not found."); } { // Instance - var extensions = window.VkSurface.GetRequiredExtensions(out uint extensionCount); - Utils.CreateInstance(vk, extensionCount, extensions, out instance); + var extensions = SilkMarshal.PtrToStringArray((nint)window.VkSurface.GetRequiredExtensions(out uint surfaceExtensionCount), (int)surfaceExtensionCount).ToList(); + extensions.Add(KhrSurface.ExtensionName); + if (vk.IsInstanceExtensionPresent(ExtDebugUtils.ExtensionName)) extensions.Add(ExtDebugUtils.ExtensionName); + + Utils.CreateInstance(vk, extensions.ToArray(), out instance); + } + { // Instance extensions + if (!vk.TryGetInstanceExtension(instance, out khrSurface)) throw new Exception($"{KhrSurface.ExtensionName} extension not found."); + surface = window.VkSurface.Create(instance.ToHandle(), null).ToSurface(); + + vk.TryGetInstanceExtension(instance, out debugUtils); } { // Physical Device uint deviceCount = 0; @@ -32,8 +58,57 @@ public Application() var devices = stackalloc PhysicalDevice[(int)deviceCount]; vk.EnumeratePhysicalDevices(instance, ref deviceCount, devices).Check(); physicalDevice = devices[0]; // idc + if (!vk.IsDeviceExtensionPresent(physicalDevice, KhrSwapchain.ExtensionName)) throw new Exception($"Physical device doesn't support ${KhrSwapchain.ExtensionName}"); + + uint queueFamilityCount = 0; + vk.GetPhysicalDeviceQueueFamilyProperties(physicalDevice, ref queueFamilityCount, null); + var queueFamilyProperties = stackalloc QueueFamilyProperties[(int)queueFamilityCount]; + vk.GetPhysicalDeviceQueueFamilyProperties(physicalDevice, ref queueFamilityCount, queueFamilyProperties); + + for (uint i = 0; i < queueFamilityCount; i++) + { + if (graphicsQueueFamily is uint.MaxValue && queueFamilyProperties[i].QueueFlags.HasFlag(QueueFlags.QueueGraphicsBit)) graphicsQueueFamily = i; + if (presentQueueFamily is uint.MaxValue) + { + khrSurface.GetPhysicalDeviceSurfaceSupport(physicalDevice, i, surface, out Bool32 supported).Check(); + if ((bool)supported is true) presentQueueFamily = i; + } + if ((graphicsQueueFamily | presentQueueFamily) is not uint.MaxValue) break; + } + if ((graphicsQueueFamily | presentQueueFamily) is uint.MaxValue) throw new Exception("Suitable queue families not found."); + } + { // Device + Queues + float priority = 1.0f; + var queueCreateInfos = stackalloc DeviceQueueCreateInfo[2]; + queueCreateInfos[0] = new(queueFamilyIndex: graphicsQueueFamily, queueCount: 1, pQueuePriorities: &priority); + queueCreateInfos[1] = new(queueFamilyIndex: presentQueueFamily, queueCount: 1, pQueuePriorities: &priority); + + PhysicalDeviceFeatures physicalDeviceFeatures = new() { SamplerAnisotropy = true }; + + var extensions = new string[] { KhrSwapchain.ExtensionName }; + byte** extensionsPtr = (byte**)SilkMarshal.StringArrayToPtr(extensions); + + try + { + DeviceCreateInfo deviceCreateInfo = new( + queueCreateInfoCount: graphicsQueueFamily == presentQueueFamily ? 1u : 2u, pQueueCreateInfos: queueCreateInfos, + enabledExtensionCount: (uint)extensions.Length, ppEnabledExtensionNames: extensionsPtr, + pEnabledFeatures: &physicalDeviceFeatures); + + vk.CreateDevice(physicalDevice, &deviceCreateInfo, null, out device).Check(); + + vk.GetDeviceQueue(device, graphicsQueueFamily, 0, out graphicsQueue); + if (graphicsQueueFamily == presentQueueFamily) presentQueue = graphicsQueue; + else vk.GetDeviceQueue(device, presentQueueFamily, 0, out presentQueue); + } + finally + { + for (uint i = 0; i < extensions.Length; i++) SilkMarshal.FreeString((nint)extensionsPtr[i]); + SilkMarshal.Free((nint)extensionsPtr); + } } - Console.WriteLine(); + + Console.WriteLine($"Initialized Application in {stopwatch.Elapsed}"); } @@ -45,7 +120,10 @@ public void Run() void IDisposable.Dispose() { + vk.DestroyDevice(device, null); + khrSurface.DestroySurface(instance, surface, null); vk.DestroyInstance(instance, null); + vk.Dispose(); window.Dispose(); } }