From 55da27514a9aeb42c084c35bb288c90cd78b62d8 Mon Sep 17 00:00:00 2001 From: Brooks Date: Thu, 1 Oct 2026 09:33:46 +0800 Subject: [PATCH] Detect Vulkan via the loader before falling back to vulkaninfo SystemInfo.GetVulkanVersion relied solely on spawning `vulkaninfo --summary`. That tool ships with the Vulkan SDK / some driver bundles, not with every Vulkan-capable driver (e.g. Windows machines with only an Intel GPU), and the external process can be slow or time out (#930). When it is missing the Vulkan backend is silently skipped and only the CPU backend is offered. Query the loader in-process with vkEnumerateInstanceVersion first (vulkan-1.dll on Windows, libvulkan.so.1 on Linux) and only fall back to vulkaninfo when the loader is absent or the call fails. Behaviour on machines without Vulkan is unchanged. Co-Authored-By: Claude Fable 5.1 --- LLama/Native/Load/SystemInfo.cs | 60 +++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/LLama/Native/Load/SystemInfo.cs b/LLama/Native/Load/SystemInfo.cs index 0f48ce44f..c6e63147b 100644 --- a/LLama/Native/Load/SystemInfo.cs +++ b/LLama/Native/Load/SystemInfo.cs @@ -44,6 +44,15 @@ public static SystemInfo Get() #region Vulkan version private static string? GetVulkanVersion() { + // Prefer asking the Vulkan loader directly. `vulkaninfo` is a separate tool which is not present on every + // machine that has a working Vulkan driver (e.g. Windows machines with only an Intel GPU), and spawning it + // can be slow or time out (see #930). Without it the Vulkan backend is skipped and only the CPU is offered. + string? loaderVersion = GetVulkanVersionFromLoader(); + if (loaderVersion != null) + { + return loaderVersion; + } + // Get Vulkan Summary string? vulkanSummary = GetVulkanSummary(); // If we have a Vulkan summary @@ -117,6 +126,57 @@ public static SystemInfo Get() // Return null if no match is found return null; } + + /// + /// Query the Vulkan loader in-process with vkEnumerateInstanceVersion (available since Vulkan 1.1). + /// + /// The loader version as "major.minor.patch", or null if the loader is missing, predates Vulkan 1.1 or the call fails. + private static string? GetVulkanVersionFromLoader() + { + try + { + int result; + uint apiVersion; + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + result = VulkanLoaderWindows.vkEnumerateInstanceVersion(out apiVersion); + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + { + result = VulkanLoaderLinux.vkEnumerateInstanceVersion(out apiVersion); + } + else + { + return null; + } + + // VK_SUCCESS == 0 + if (result != 0) + { + return null; + } + + // VK_API_VERSION_MAJOR / MINOR / PATCH (the variant bits above bit 29 are ignored) + return $"{(apiVersion >> 22) & 0x7F}.{(apiVersion >> 12) & 0x3FF}.{apiVersion & 0xFFF}"; + } + catch + { + // DllNotFoundException, EntryPointNotFoundException, etc. Fall back to `vulkaninfo`. + return null; + } + } + + private static class VulkanLoaderWindows + { + [DllImport("vulkan-1", CallingConvention = CallingConvention.Winapi)] + public static extern int vkEnumerateInstanceVersion(out uint pApiVersion); + } + + private static class VulkanLoaderLinux + { + [DllImport("libvulkan.so.1", CallingConvention = CallingConvention.Cdecl)] + public static extern int vkEnumerateInstanceVersion(out uint pApiVersion); + } #endregion #region CUDA version