Load the runtime that fits the GPU, not the first one found

A runtime carries one vendor's providers, only one loads per process,
and a device can now hold several: the package's OpenVINO or WebGPU
build, a CUDA build the user fetched, the distribution's ROCm build.
`api::install` opens each it finds, lists its providers with
GetAvailableProviders, and installs the one scoring highest against the
GPUs `hardware::detect` reads from files — a vendor rung on its own
vendor's GPU above OpenVINO on an Intel one above the generic WebGPU
rung above a CPU-only build. Equal scores keep the old first-found
order, and DARKROOM_ORT_DIR still wins outright. The losers stay mapped
rather than unloaded.

The Linux fingerprint now names the OpenCL drivers too, so installing
Intel's re-probes. `ladder` takes DARKROOM_ORT_DIRS to show the choice.
This commit is contained in:
2026-10-04 21:00:15 -04:00
parent 87c405eb46
commit 85dee4375b
6 changed files with 317 additions and 36 deletions
+23 -7
View File
@@ -391,19 +391,35 @@ fn providers_beside(runtime: &Path) -> String {
#[cfg(target_os = "linux")]
fn device_identity() -> String {
// The NVIDIA driver's version line, or the ROCm release the AMD stack
// came from (`rocm-core` writes it; the kernel driver has no version
// of its own). Absent means neither.
// The NVIDIA driver's version line, the ROCm release the AMD stack came
// from (`rocm-core` writes it; the kernel driver has no version of its
// own), and the OpenCL drivers registered — OpenVINO reaches the GPU
// through one, and installing Intel's is what makes the Iris Xe a rung.
let mut parts = Vec::new();
if let Some(line) = std::fs::read_to_string("/proc/driver/nvidia/version")
.ok()
.and_then(|s| s.lines().next().map(str::to_string))
{
return line;
parts.push(line);
}
if let Ok(rocm) = std::fs::read_to_string("/opt/rocm/.info/version") {
return format!("rocm {}", rocm.trim());
parts.push(format!("rocm {}", rocm.trim()));
}
let mut icds: Vec<String> = std::fs::read_dir("/etc/OpenCL/vendors")
.into_iter()
.flatten()
.filter_map(|e| e.ok())
.map(|e| e.file_name().to_string_lossy().into_owned())
.collect();
icds.sort();
if !icds.is_empty() {
parts.push(format!("opencl {}", icds.join(" ")));
}
if parts.is_empty() {
"no nvidia driver, no rocm, no opencl".into()
} else {
parts.join("; ")
}
"no nvidia driver, no rocm".into()
}
#[cfg(target_os = "android")]
@@ -418,7 +434,7 @@ fn device_identity() -> String {
}
#[cfg(target_os = "android")]
fn system_property(name: &str) -> String {
pub(crate) fn system_property(name: &str) -> String {
extern "C" {
fn __system_property_get(
name: *const std::ffi::c_char,