diff --git a/llama/server/CMakeLists.txt b/llama/server/CMakeLists.txt index 86e9d21ac..2c57315e5 100644 --- a/llama/server/CMakeLists.txt +++ b/llama/server/CMakeLists.txt @@ -501,26 +501,6 @@ if(OLLAMA_RUNNER_DIR) RUNTIME DESTINATION "${_base_dest}/${OLLAMA_RUNNER_DIR}" COMPONENT llama-server LIBRARY DESTINATION "${_base_dest}/${OLLAMA_RUNNER_DIR}" COMPONENT llama-server ) - if(WIN32) - # Bundle the system Vulkan loader, not an arbitrary SDK copy. - # The loader is supplied by the installed Vulkan runtime or - # GPU driver; using that copy avoids PATH-dependent SDK drift. - if(DEFINED ENV{SystemRoot}) - file(TO_CMAKE_PATH "$ENV{SystemRoot}" _ollama_windows_root) - else() - set(_ollama_windows_root "C:/Windows") - endif() - find_file(_vulkan_loader_dll - NAMES vulkan-1.dll - HINTS "${_ollama_windows_root}/System32" - NO_DEFAULT_PATH) - if(NOT _vulkan_loader_dll) - message(FATAL_ERROR "Could not find vulkan-1.dll in the Windows Vulkan runtime. Install the Vulkan runtime from the GPU driver or VULKAN_SDK/Helpers/VulkanRT.exe.") - endif() - install(FILES "${_vulkan_loader_dll}" - DESTINATION "${_base_dest}/${OLLAMA_RUNNER_DIR}" - COMPONENT llama-server) - endif() endif() endif() else() diff --git a/llm/vulkan_windows.go b/llm/vulkan_windows.go index b492e4c0c..de3e591ad 100644 --- a/llm/vulkan_windows.go +++ b/llm/vulkan_windows.go @@ -5,6 +5,7 @@ package llm import ( "errors" "log/slog" + "os" "path/filepath" "strings" @@ -13,25 +14,12 @@ import ( const windowsVulkanRuntimeDLLName = "vulkan-1.dll" -type windowsVulkanRuntimeDLL struct { - path string - source string - bundledPath string - bundledVer windowsFileVersion - bundledVerOK bool - systemPath string - systemVer windowsFileVersion - systemVerOK bool - systemDir string - bundledDir string -} - func WindowsVulkanRuntimeDLLPath(libDirs []string) (string, error) { - choice, err := windowsVulkanRuntimeDLLChoice(libDirs) + systemDir, err := windows.GetSystemDirectory() if err != nil { return "", err } - return choice.path, nil + return windowsVulkanRuntimeDLLPath(systemDir, os.Getenv("PATH"), libDirs, fileExists) } func adjustWindowsVulkanLibraryPaths(paths, gpuLibs []string) []string { @@ -40,74 +28,68 @@ func adjustWindowsVulkanLibraryPaths(paths, gpuLibs []string) []string { return paths } - choice, err := windowsVulkanRuntimeDLLChoice(gpuLibs) + vulkanPath, err := WindowsVulkanRuntimeDLLPath(gpuLibs) if err != nil { slog.Debug("windows Vulkan loader selection unavailable", "error", err) return paths } - slog.Debug("selected windows Vulkan loader", - "loader_source", choice.source, - "path", choice.path, - "bundled", choice.bundledPath, - "bundled_version", choice.bundledVer.String(), - "system", choice.systemPath, - "system_version", choice.systemVer.String(), - ) + slog.Debug("selected windows Vulkan loader", "path", vulkanPath) - if choice.source != "system" || choice.systemDir == "" { - return paths - } - - return insertPathBefore(paths, choice.systemDir, vulkanDir) + return insertPathBefore(paths, filepath.Dir(vulkanPath), vulkanDir) } -// Prefer the system Vulkan loader when present, but keep a bundled loader as a -// fallback for hosts without a Vulkan runtime. If both versions are available -// and the bundled loader is newer, select the bundled copy. -func windowsVulkanRuntimeDLLChoice(libDirs []string) (windowsVulkanRuntimeDLL, error) { - systemDir, err := windows.GetSystemDirectory() - if err != nil { - return windowsVulkanRuntimeDLL{}, err - } +// Use the host Vulkan loader supplied by the installed Vulkan runtime or GPU +// driver. Ollama no longer packages the loader; exclude backend library +// directories from PATH probing so stale app-local copies from older installs +// cannot shadow the host runtime. +func windowsVulkanRuntimeDLLPath( + systemDir string, + pathEnv string, + libDirs []string, + exists func(string) bool, +) (string, error) { systemDir = filepath.Clean(systemDir) - bundledPath := firstExistingFile(libDirs, windowsVulkanRuntimeDLLName) systemPath := filepath.Join(systemDir, windowsVulkanRuntimeDLLName) - systemExists := fileExists(systemPath) - - choice := windowsVulkanRuntimeDLL{ - path: bundledPath, - source: "bundled", - bundledPath: bundledPath, - systemPath: systemPath, - systemDir: systemDir, - } - if bundledPath != "" { - choice.bundledDir = filepath.Dir(bundledPath) - choice.bundledVer, choice.bundledVerOK = readWindowsFileVersion(bundledPath) - } - if systemExists { - choice.systemVer, choice.systemVerOK = readWindowsFileVersion(systemPath) + if exists(systemPath) { + return systemPath, nil } - switch { - case bundledPath != "" && systemExists: - if choice.bundledVerOK && choice.systemVerOK && choice.bundledVer.Compare(choice.systemVer) > 0 { - return choice, nil + if path := firstWindowsVulkanRuntimeDLLOnPath(pathEnv, libDirs, exists); path != "" { + return path, nil + } + + return "", errors.New("no host vulkan-1.dll runtime DLL found") +} + +func firstWindowsVulkanRuntimeDLLOnPath(pathEnv string, excludedDirs []string, exists func(string) bool) string { + for _, dir := range filepath.SplitList(pathEnv) { + dir = strings.Trim(filepath.Clean(strings.Trim(dir, `"`)), `"`) + if dir == "." || dir == "" || windowsDirInList(dir, excludedDirs) { + continue + } + + path := filepath.Join(dir, windowsVulkanRuntimeDLLName) + if exists(path) { + return filepath.Clean(path) } - choice.path = systemPath - choice.source = "system" - return choice, nil - case systemExists: - choice.path = systemPath - choice.source = "system" - return choice, nil - case bundledPath != "": - return choice, nil - default: - return windowsVulkanRuntimeDLL{}, errors.New("no vulkan-1.dll runtime DLL found") } + return "" +} + +func windowsDirInList(dir string, dirs []string) bool { + dir = strings.ToLower(filepath.Clean(dir)) + for _, candidate := range dirs { + candidate = strings.ToLower(filepath.Clean(candidate)) + if candidate == "" || candidate == "." { + continue + } + if dir == candidate || strings.HasPrefix(dir, candidate+string(filepath.Separator)) { + return true + } + } + return false } func firstWindowsVulkanLibDir(libDirs []string) string { diff --git a/scripts/build_windows.ps1 b/scripts/build_windows.ps1 index a31e2ec85..2654bfde2 100644 --- a/scripts/build_windows.ps1 +++ b/scripts/build_windows.ps1 @@ -800,7 +800,7 @@ function newDependencyAuditJob($payloadDir, $label, $reportPath, $dependencyDirs "winhttp.dll", "winmm.dll", "ws2_32.dll" ) $driverDlls = @( - "nvcuda.dll", "nvml.dll" + "nvcuda.dll", "nvml.dll", "vulkan-1.dll" ) $dependencyRoots = @()