Skip to content

Commit 4ca4ba4

Browse files
darthcavclaude
andcommitted
feat(windows): add Vulkan GPU detection for Intel Arc and other non-CUDA GPUs
CanUseGPU on Windows only checked NVIDIA CUDA (amd64) and Qualcomm Adreno OpenCL (arm64), so Intel Arc and other Vulkan-capable GPUs were silently ignored and fell back to the CPU llama.cpp variant. Add hasVulkanCapableGPU (PCI-based, excludes NVIDIA and Adreno which are handled by their own backends) and hasVulkan (probes vulkan-1.dll, mirroring the existing OpenCL.dll probe). Update CanUseGPU to call hasVulkan on amd64 when no CUDA GPU is found, and wire up a "vulkan" variant in ensureLatestLlamaCpp with priority CUDA > Vulkan > CPU. A TODO marks the point where a "vulkan" image variant of docker/docker-model-backend-llamacpp needs to be published to Docker Hub to complete the end-to-end fix. Fixes #925 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent a7e0cdf commit 4ca4ba4

2 files changed

Lines changed: 60 additions & 2 deletions

File tree

pkg/inference/backends/llamacpp/download_windows.go

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ func (l *llamaCpp) ensureLatestLlamaCpp(ctx context.Context, log logging.Logger,
1515
llamaCppPath, vendoredServerStoragePath string,
1616
) error {
1717
nvGPUInfoBin := filepath.Join(vendoredServerStoragePath, "com.docker.nv-gpu-info.exe")
18-
var canUseCUDA11, canUseOpenCL bool
18+
var canUseCUDA11, canUseOpenCL, canUseVulkan bool
1919
var err error
2020
ShouldUseGPUVariantLock.Lock()
2121
defer ShouldUseGPUVariantLock.Unlock()
@@ -27,6 +27,17 @@ func (l *llamaCpp) ensureLatestLlamaCpp(ctx context.Context, log logging.Logger,
2727
l.status = inference.FormatError(fmt.Sprintf("failed to check CUDA 11 capability: %v", err))
2828
return fmt.Errorf("failed to check CUDA 11 capability: %w", err)
2929
}
30+
if !canUseCUDA11 {
31+
// Check for Vulkan-capable GPUs (Intel Arc, AMD, etc.) when CUDA
32+
// is not available.
33+
// TODO: publish a "vulkan" variant of docker/docker-model-backend-llamacpp
34+
// to Docker Hub so this detection selects a Vulkan-optimised build.
35+
canUseVulkan, err = hasVulkan()
36+
if err != nil {
37+
l.status = inference.FormatError(fmt.Sprintf("failed to check Vulkan capability: %v", err))
38+
return fmt.Errorf("failed to check Vulkan capability: %w", err)
39+
}
40+
}
3041
case "arm64":
3142
canUseOpenCL, err = hasOpenCL()
3243
if err != nil {
@@ -39,6 +50,8 @@ func (l *llamaCpp) ensureLatestLlamaCpp(ctx context.Context, log logging.Logger,
3950
desiredVariant := "cpu"
4051
if canUseCUDA11 {
4152
desiredVariant = "cuda"
53+
} else if canUseVulkan {
54+
desiredVariant = "vulkan"
4255
} else if canUseOpenCL {
4356
desiredVariant = "opencl"
4457
}

pkg/inference/backends/llamacpp/gpuinfo_windows.go

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,14 +99,59 @@ func hasOpenCL() (bool, error) {
9999
return true, nil
100100
}
101101

102+
// hasVulkanCapableGPU returns true if at least one GPU that is neither
103+
// NVIDIA (handled via CUDA) nor a Qualcomm Adreno (handled via OpenCL)
104+
// is detected. Intel Arc, AMD, and other Vulkan-capable discrete or
105+
// integrated GPUs fall into this category.
106+
func hasVulkanCapableGPU() (bool, error) {
107+
gpus, err := ghw.GPU()
108+
if err != nil {
109+
return false, err
110+
}
111+
for _, gpu := range gpus.GraphicsCards {
112+
vendor := strings.ToLower(gpu.DeviceInfo.Vendor.Name)
113+
product := gpu.DeviceInfo.Product.Name
114+
isNVIDIA := vendor == "nvidia"
115+
isAdreno := strings.Contains(product, "Adreno") || strings.Contains(product, "Qualcomm")
116+
if !isNVIDIA && !isAdreno {
117+
return true, nil
118+
}
119+
}
120+
return false, nil
121+
}
122+
123+
// hasVulkan returns true when a non-CUDA/non-OpenCL GPU is present AND
124+
// the Vulkan runtime library (vulkan-1.dll) is loadable. This mirrors
125+
// the OpenCL.dll probe used by hasOpenCL.
126+
func hasVulkan() (bool, error) {
127+
capable, err := hasVulkanCapableGPU()
128+
if !capable || err != nil {
129+
return false, err
130+
}
131+
h, err := syscall.LoadLibrary("vulkan-1.dll")
132+
if err != nil {
133+
if errors.Is(err, syscall.ERROR_MOD_NOT_FOUND) {
134+
return false, nil
135+
}
136+
return false, fmt.Errorf("unable to load Vulkan DLL: %w", err)
137+
}
138+
syscall.FreeLibrary(h)
139+
return true, nil
140+
}
141+
102142
func CanUseGPU(ctx context.Context, nvGPUInfoBin string) (bool, error) {
103143
// We don't ship com.docker.nv-gpu-info.exe on Windows/ARM64 at the moment,
104-
// so skip the CUDA check there for now. The OpenCL check is portable.
144+
// so skip the CUDA and Vulkan checks there for now. The OpenCL check is portable.
105145
if runtime.GOARCH == "amd64" {
106146
haveCUDA11GPU, err := hasCUDA11CapableGPU(ctx, nvGPUInfoBin)
107147
if haveCUDA11GPU || err != nil {
108148
return haveCUDA11GPU, err
109149
}
150+
// No CUDA GPU found: check for Vulkan-capable GPUs (Intel Arc, AMD, etc.).
151+
haveVulkan, err := hasVulkan()
152+
if haveVulkan || err != nil {
153+
return haveVulkan, err
154+
}
110155
}
111156
return hasOpenCL()
112157
}

0 commit comments

Comments
 (0)