@@ -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'))
|
||||
})
|
||||
@@ -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) => {
|
||||
|
||||
Reference in New Issue
Block a user