update
Release rolling / release (push) Successful in 10m52s

This commit is contained in:
Raven Scott
2026-07-31 20:18:12 -04:00
parent 2691f59872
commit 7d872fa898
4 changed files with 389 additions and 17 deletions
@@ -2,11 +2,23 @@
* Probe host GPUs for QVAC load ordering (highest device-local memory first).
* Uses vulkaninfo when available; nvidia-smi / sysfs as weak fallbacks.
*
* Also prefers hardware Vulkan ICDs (NVIDIA/Intel/AMD) and disables Mesa
* lavapipe/llvmpipe so llama.cpp does not "succeed" on a CPU Vulkan device.
*
* Static bare-subprocess import: dynamic import() from this module was not
* linked in the bare-pack resolution map (MODULE_NOT_FOUND → sdk worker crash).
*/
import bareSubprocess from 'bare-subprocess'
import hostFs from '#host-fs'
import path from '#host-path'
/** Standard Vulkan ICD manifest directories (Linux). */
export const BARE_OS_QVAC_VULKAN_ICD_DIRS = [
'/usr/share/vulkan/icd.d',
'/etc/vulkan/icd.d',
'/usr/local/share/vulkan/icd.d'
]
/**
* @typedef {{
@@ -241,13 +253,238 @@ export function bareOsQvacMergeNvidiaHints(vulkan, nvidia) {
})
}
/**
* True for Mesa lavapipe / llvmpipe / SwiftShader / Vulkan CPU devices.
* @param {{ name?: string, deviceType?: string } | null | undefined} g
*/
export function bareOsQvacIsSoftwareVulkanDevice(g) {
if (!g || typeof g !== 'object') return false
const name = String(g.name || '').toLowerCase()
const type = String(g.deviceType || '').toLowerCase()
if (
/llvmpipe|lavapipe|swiftshader|softpipe|cpu rasterizer|microsoft basic render/.test(
name
)
) {
return true
}
if (
type === 'cpu' ||
type.includes('physical_device_type_cpu') ||
/(^|_)cpu($|_)/.test(type)
) {
return true
}
return false
}
/**
* @param {BareOsQvacGpuInfo[]} gpus
* @returns {BareOsQvacGpuInfo[]}
*/
export function bareOsQvacHardwareGpus(gpus) {
return (Array.isArray(gpus) ? gpus : []).filter(
(g) => !bareOsQvacIsSoftwareVulkanDevice(g)
)
}
/**
* Score an ICD JSON filename — higher is preferred; software drivers are negative.
* @param {string} filename
*/
export function bareOsQvacScoreVulkanIcdFilename(filename) {
const n = String(filename || '').toLowerCase()
if (/lvp|llvmpipe|lavapipe|swiftshader/.test(n)) return -100
if (/nvidia/.test(n)) return 100
if (/radeon|amd|radv/.test(n)) return 80
if (/intel|anv|iris/.test(n)) return 50
if (/moltenvk|apple/.test(n)) return 40
return 10
}
/**
* List hardware Vulkan ICD manifest paths (software ICDs excluded).
* @param {{
* dirs?: string[],
* readdirSync?: (dir: string) => string[],
* existsSync?: (p: string) => boolean
* }} [opts]
* @returns {string[]}
*/
export function bareOsQvacDiscoverHardwareVulkanIcds(opts = {}) {
const dirs = Array.isArray(opts.dirs) ? opts.dirs : BARE_OS_QVAC_VULKAN_ICD_DIRS
const exists =
typeof opts.existsSync === 'function'
? opts.existsSync
: (p) => {
try {
return hostFs.existsSync(p)
} catch {
return false
}
}
const readdir =
typeof opts.readdirSync === 'function'
? opts.readdirSync
: (dir) => {
try {
return hostFs.readdirSync(dir)
} catch {
return []
}
}
/** @type {{ path: string, score: number }[]} */
const found = []
for (const dir of dirs) {
if (!exists(dir)) continue
let names = []
try {
names = readdir(dir) || []
} catch {
continue
}
for (const name of names) {
const base = String(name)
if (!base.toLowerCase().endsWith('.json')) continue
const score = bareOsQvacScoreVulkanIcdFilename(base)
if (score < 0) continue
found.push({ path: path.join(dir, base), score })
}
}
found.sort((a, b) => b.score - a.score || a.path.localeCompare(b.path))
return found.map((x) => x.path)
}
/**
* Prefer real GPU Vulkan drivers before llama.cpp / vulkaninfo enumerate.
* Disables lavapipe and, on Optimus laptops, nudges NVIDIA offload.
*
* @param {Record<string, unknown>} [env] guest/host env overlay (BARE_OS_*)
* @param {{
* procEnv?: Record<string, string | undefined> | null,
* icds?: string[],
* hasNvidiaSmi?: boolean
* }} [opts]
* @returns {{
* note: string,
* icds: string[],
* disabledSoftware: boolean,
* preferNvidia: boolean
* }}
*/
export function bareOsQvacApplyHardwareVulkanEnv(env = {}, opts = {}) {
const procEnv =
opts.procEnv !== undefined
? opts.procEnv
: globalThis.process && globalThis.process.env
? globalThis.process.env
: null
const empty = {
note: '',
icds: /** @type {string[]} */ ([]),
disabledSoftware: false,
preferNvidia: false
}
if (!procEnv) return empty
const keep = String(
env.BARE_OS_QVAC_KEEP_VK_ENV ?? procEnv.BARE_OS_QVAC_KEEP_VK_ENV ?? ''
)
.trim()
.toLowerCase()
if (keep === '1' || keep === 'true') {
return { ...empty, note: 'BARE_OS_QVAC_KEEP_VK_ENV — left Vulkan env unchanged' }
}
const softwareDisableGlob = '*lvp*,*lavapipe*,*swiftshader*'
const prevDisable = String(procEnv.VK_LOADER_DRIVERS_DISABLE || '').trim()
if (!prevDisable) {
procEnv.VK_LOADER_DRIVERS_DISABLE = softwareDisableGlob
} else if (!/lvp|lavapipe|swiftshader/i.test(prevDisable)) {
procEnv.VK_LOADER_DRIVERS_DISABLE = prevDisable + ',' + softwareDisableGlob
}
const icds =
Array.isArray(opts.icds) && opts.icds.length
? opts.icds
: bareOsQvacDiscoverHardwareVulkanIcds()
const preferNvidia =
icds.some((p) => /nvidia/i.test(path.basename(p))) ||
Boolean(opts.hasNvidiaSmi)
const userDrivers = String(
env.BARE_OS_QVAC_VK_DRIVER_FILES ??
procEnv.BARE_OS_QVAC_VK_DRIVER_FILES ??
''
).trim()
const alreadyForced = Boolean(
String(procEnv.VK_DRIVER_FILES || '').trim() ||
String(procEnv.VK_ICD_FILENAMES || '').trim() ||
userDrivers
)
if (userDrivers && !String(procEnv.VK_DRIVER_FILES || '').trim()) {
procEnv.VK_DRIVER_FILES = userDrivers
} else if (!alreadyForced && icds.length) {
// Prefer hardware manifests; exclude lavapipe so ggml cannot pick CPU Vulkan.
procEnv.VK_DRIVER_FILES = icds.join(':')
}
if (preferNvidia) {
if (!String(procEnv.__NV_PRIME_RENDER_OFFLOAD || '').trim()) {
procEnv.__NV_PRIME_RENDER_OFFLOAD = '1'
}
if (!String(procEnv.__GLX_VENDOR_LIBRARY_NAME || '').trim()) {
procEnv.__GLX_VENDOR_LIBRARY_NAME = 'nvidia'
}
if (!String(procEnv.__VK_LAYER_NV_optimus || '').trim()) {
procEnv.__VK_LAYER_NV_optimus = 'NVIDIA_only'
}
const selectMode = String(
env.BARE_OS_QVAC_VK_SELECT ?? procEnv.BARE_OS_QVAC_VK_SELECT ?? 'auto'
)
.trim()
.toLowerCase()
// Hybrid Intel+NVIDIA: bind NVIDIA only so ggml never lands on iGPU/llvmpipe.
// Set BARE_OS_QVAC_VK_SELECT=off to keep every hardware ICD.
if (
(selectMode === 'auto' || selectMode === 'nvidia') &&
!String(procEnv.VK_LOADER_DRIVERS_SELECT || '').trim()
) {
procEnv.VK_LOADER_DRIVERS_SELECT = '*nvidia*'
}
}
const noteParts = ['disabled software Vulkan ICDs (lavapipe/llvmpipe)']
if (icds.length) {
noteParts.push(
'hardware ICDs=' + icds.map((p) => path.basename(p)).join(',')
)
}
if (preferNvidia) noteParts.push('NVIDIA Optimus offload env set')
if (String(procEnv.VK_LOADER_DRIVERS_SELECT || '').includes('nvidia')) {
noteParts.push('VK_LOADER_DRIVERS_SELECT=*nvidia*')
}
return {
note: noteParts.join('; '),
icds,
disabledSoftware: true,
preferNvidia
}
}
/**
* Sort GPUs for load attempts: highest memory first; discrete before integrated on ties.
* Software Vulkan devices are ranked last.
* @param {BareOsQvacGpuInfo[]} gpus
* @returns {BareOsQvacGpuInfo[]}
*/
export function bareOsQvacRankGpus(gpus) {
return [...gpus].sort((a, b) => {
const as = bareOsQvacIsSoftwareVulkanDevice(a) ? 1 : 0
const bs = bareOsQvacIsSoftwareVulkanDevice(b) ? 1 : 0
if (as !== bs) return as - bs
if (b.memoryBytes !== a.memoryBytes) return b.memoryBytes - a.memoryBytes
const ad = /DISCRETE/i.test(a.deviceType) ? 1 : 0
const bd = /DISCRETE/i.test(b.deviceType) ? 1 : 0
@@ -272,7 +509,10 @@ export function bareOsQvacMainGpuCandidates(ranked) {
out.push(v)
}
const vulkanBacked = ranked.filter((g) => String(g.source || '').includes('vulkaninfo'))
const hardware = bareOsQvacHardwareGpus(ranked)
const vulkanBacked = hardware.filter((g) =>
String(g.source || '').includes('vulkaninfo')
)
if (vulkanBacked.length) {
for (const g of vulkanBacked) push(g.index)
push('dedicated')
@@ -280,7 +520,7 @@ export function bareOsQvacMainGpuCandidates(ranked) {
}
// nvidia-smi indices ≠ Vulkan indices on hybrid laptops — prefer class + common slots.
if (ranked.length) {
if (hardware.length) {
push('dedicated')
push(1)
push(0)
@@ -332,6 +572,7 @@ export async function bareOsQvacProbeGpus(opts = {}) {
}))
}
// Drop software adapters from the preferred list (keep rank helper for tests).
return bareOsQvacRankGpus(vulkan)
} catch {
return []
@@ -8,7 +8,11 @@
import * as qvacSdkBind from './bare-os-qvac-sdk-bind.mjs'
import {
bareOsQvacProbeGpus,
bareOsQvacMainGpuCandidates
bareOsQvacMainGpuCandidates,
bareOsQvacHardwareGpus,
bareOsQvacIsSoftwareVulkanDevice,
bareOsQvacApplyHardwareVulkanEnv,
bareOsQvacRunHostCommand
} from './bare-os-qvac-gpu-probe.mjs'
import {
bareOsQvacDiagnoseGguf,
@@ -164,7 +168,7 @@ export function bareOsQvacBuildModelConfig(p) {
* @returns {{ index: number | null, note: string }}
*/
export function bareOsQvacPickVulkanPin(ranked) {
const list = Array.isArray(ranked) ? ranked : []
const list = bareOsQvacHardwareGpus(Array.isArray(ranked) ? ranked : [])
const vulkanBacked = list.filter((g) =>
String(g.source || '').includes('vulkaninfo')
)
@@ -184,11 +188,12 @@ export function bareOsQvacPickVulkanPin(ranked) {
}
const nvidia = list.find((g) => /nvidia/i.test(String(g.name || '')))
if (nvidia) {
// Hybrid laptops usually expose Intel as Vulkan0 and NVIDIA as Vulkan1.
// After VK_LOADER_DRIVERS_SELECT=*nvidia*, NVIDIA is usually Vulkan0.
// Still try index 0 first via candidates; pin 0 when only nvidia-smi hint.
return {
index: 1,
index: 0,
note:
'pin GGML_VK_VISIBLE_DEVICES=1 (nvidia-smi heuristic for ' +
'pin GGML_VK_VISIBLE_DEVICES=0 (nvidia-smi heuristic for ' +
nvidia.name +
')'
}
@@ -334,6 +339,8 @@ export function createBareOsQvacBridge(opts = {}) {
? opts.ensureModelsDrive
: null
const skip = bareOsQvacSkipFromEnv(env)
// Prefer NVIDIA / hardware Vulkan ICDs before any native ggml load.
bareOsQvacApplyHardwareVulkanEnv(env)
bareOsQvacApplyVulkanEnv(env)
/** @type {null | Record<string, any>} */
@@ -546,6 +553,22 @@ export function createBareOsQvacBridge(opts = {}) {
/** @type {number | null} */
let vkPin = null
// Prefer hardware Vulkan ICDs + disable lavapipe BEFORE probe/ensureApi —
// backends init Vulkan once per process.
let hasNvidiaSmi = false
try {
const smi = await bareOsQvacRunHostCommand('nvidia-smi', ['-L'], {
timeoutMs: 4000
})
hasNvidiaSmi = Boolean(smi && /GPU\s+\d+:/i.test(smi))
} catch {
hasNvidiaSmi = false
}
const hwVk = bareOsQvacApplyHardwareVulkanEnv(env, { hasNvidiaSmi })
if (hwVk.note) {
probeNote = hwVk.note
}
const existingVk = String(
(globalThis.process && globalThis.process.env
? globalThis.process.env.GGML_VK_VISIBLE_DEVICES
@@ -554,24 +577,34 @@ export function createBareOsQvacBridge(opts = {}) {
''
).trim()
// Probe + pin Vulkan BEFORE ensureApi/loadModel — backends init once per process.
if (resolved.device === 'cpu') {
gpuCandidates = []
probeNote = 'device=cpu'
} else if (existingVk) {
// Caller already pinned (e.g. GGML_VK_VISIBLE_DEVICES=1) — one attempt only.
gpuCandidates = [0]
probeNote = 'using GGML_VK_VISIBLE_DEVICES=' + existingVk
probeNote =
(probeNote ? probeNote + '; ' : '') +
'using GGML_VK_VISIBLE_DEVICES=' +
existingVk
} else if (resolved.mainGpu === 'auto') {
try {
const ranked = await bareOsQvacProbeGpus()
const hardware = bareOsQvacHardwareGpus(ranked)
const onlySoftware =
ranked.length > 0 &&
hardware.length === 0 &&
ranked.every((g) => bareOsQvacIsSoftwareVulkanDevice(g))
const pin = bareOsQvacPickVulkanPin(ranked)
vkPin = pin.index
if (pin.note) probeNote = pin.note
if (ranked.length && !probeNote) {
if (pin.note) {
probeNote = (probeNote ? probeNote + '; ' : '') + pin.note
}
if (hardware.length && !pin.note) {
probeNote =
(probeNote ? probeNote + '; ' : '') +
'probed ' +
ranked
hardware
.map(
(g) =>
'GPU' +
@@ -584,8 +617,14 @@ export function createBareOsQvacBridge(opts = {}) {
)
.join(', ')
}
if (vkPin != null) {
// After pin, the selected adapter is Vulkan0 inside llama.cpp.
if (onlySoftware && !hwVk.preferNvidia && !hwVk.icds.length) {
// No hardware ICD and only llvmpipe — skip fake "GPU" load.
gpuCandidates = []
probeNote =
(probeNote ? probeNote + '; ' : '') +
'only software Vulkan (llvmpipe); skipping GPU'
} else if (vkPin != null) {
// After pin / nvidia-only select, selected adapter is Vulkan0.
gpuCandidates = [0]
} else {
gpuCandidates = bareOsQvacMainGpuCandidates(ranked).slice(0, 2)
@@ -593,14 +632,20 @@ export function createBareOsQvacBridge(opts = {}) {
}
} catch {
gpuCandidates = ['dedicated', 0]
probeNote = 'probe failed; trying dedicated/0'
probeNote =
(probeNote ? probeNote + '; ' : '') +
'probe failed; trying dedicated/0'
}
} else if (
typeof resolved.mainGpu === 'number' &&
Number.isFinite(resolved.mainGpu)
) {
vkPin = Math.floor(resolved.mainGpu)
probeNote = 'pin GGML_VK_VISIBLE_DEVICES=' + vkPin + ' (explicit)'
probeNote =
(probeNote ? probeNote + '; ' : '') +
'pin GGML_VK_VISIBLE_DEVICES=' +
vkPin +
' (explicit)'
gpuCandidates = [0]
} else {
gpuCandidates = [resolved.mainGpu]
@@ -99,3 +99,78 @@ test('probe with injected runCommand', async (t) => {
t.is(ranked[0].index, 1)
t.ok(String(ranked[0].source).includes('vulkaninfo'))
})
test('software Vulkan devices are detected and ranked last', async (t) => {
const {
bareOsQvacIsSoftwareVulkanDevice,
bareOsQvacHardwareGpus,
bareOsQvacScoreVulkanIcdFilename,
bareOsQvacDiscoverHardwareVulkanIcds,
bareOsQvacApplyHardwareVulkanEnv
} = await import('./lib/bare-os-qvac-gpu-probe.mjs')
t.ok(
bareOsQvacIsSoftwareVulkanDevice({
name: 'llvmpipe (LLVM 22.1.8, 256 bits)',
deviceType: 'PHYSICAL_DEVICE_TYPE_CPU'
})
)
t.absent(
bareOsQvacIsSoftwareVulkanDevice({
name: 'NVIDIA GeForce RTX 3050 Ti',
deviceType: 'PHYSICAL_DEVICE_TYPE_DISCRETE_GPU'
})
)
const mixed = bareOsQvacRankGpus([
{
index: 0,
name: 'llvmpipe',
memoryBytes: 64 * 1024 * 1024 * 1024,
deviceType: 'PHYSICAL_DEVICE_TYPE_CPU',
source: 'vulkaninfo'
},
{
index: 1,
name: 'NVIDIA GeForce RTX 3050 Ti',
memoryBytes: 4 * 1024 * 1024 * 1024,
deviceType: 'PHYSICAL_DEVICE_TYPE_DISCRETE_GPU',
source: 'vulkaninfo'
}
])
t.is(mixed[0].index, 1)
t.is(bareOsQvacHardwareGpus(mixed).length, 1)
t.ok(bareOsQvacScoreVulkanIcdFilename('nvidia_icd.json') > 0)
t.ok(bareOsQvacScoreVulkanIcdFilename('lvp_icd.x86_64.json') < 0)
const icds = bareOsQvacDiscoverHardwareVulkanIcds({
dirs: ['/fake/icd'],
existsSync: () => true,
readdirSync: () => [
'lvp_icd.x86_64.json',
'nvidia_icd.json',
'intel_icd.x86_64.json'
]
})
t.alike(icds, [
'/fake/icd/nvidia_icd.json',
'/fake/icd/intel_icd.x86_64.json'
])
/** @type {Record<string, string>} */
const procEnv = {}
const applied = bareOsQvacApplyHardwareVulkanEnv(
{},
{
procEnv,
icds: ['/usr/share/vulkan/icd.d/nvidia_icd.json'],
hasNvidiaSmi: true
}
)
t.ok(applied.preferNvidia)
t.ok(String(procEnv.VK_LOADER_DRIVERS_DISABLE).includes('lvp'))
t.is(procEnv.VK_LOADER_DRIVERS_SELECT, '*nvidia*')
t.is(procEnv.__NV_PRIME_RENDER_OFFLOAD, '1')
t.ok(String(procEnv.VK_DRIVER_FILES).includes('nvidia_icd.json'))
})
+12 -1
View File
@@ -87,7 +87,18 @@ test('pick vulkan pin prefers highest-VRAM vulkaninfo device', async (t) => {
source: 'nvidia-smi'
}
])
t.is(nvidiaOnly.index, 1)
t.is(nvidiaOnly.index, 0)
const softOnly = bareOsQvacPickVulkanPin([
{
index: 0,
name: 'llvmpipe (LLVM 22.1.8, 256 bits)',
memoryBytes: 8 * 1024 * 1024 * 1024,
deviceType: 'PHYSICAL_DEVICE_TYPE_CPU',
source: 'vulkaninfo'
}
])
t.is(softOnly.index, null)
})
test('flatten tools nested → flat', async (t) => {