Commit b70469d195a1
tarball1 file changed+62-27
@@ -89,12 +89,12 @@ impl Shared {
8989
.engine_name(c"monolith")9090
.api_version(vk::API_VERSION_1_3);9191
let wanted = std::env::var("MONOLITH_VK_DEVICE").ok();92
- let mut filter = node.and_then(icd_filter);92
+ let mut env = node.map(driver_env).unwrap_or_default();9393
let (instance, physical, queue_family, name) = loop {94
- if let Some(glob) = filter {94
+ for (var, value) in &env {9595
// SAFETY: the compositor creates its renderer before starting any9696
// other thread, so nothing reads the environment concurrently.97
- unsafe { std::env::set_var(DRIVERS_SELECT, glob) };97
+ unsafe { std::env::set_var(var, value) };9898
}9999
let instance = unsafe {100100
entry.create_instance(@@ -102,20 +102,20 @@ impl Shared {
102102
None,103103
)104104
};105
- if filter.is_some() {106
- // SAFETY: as above. Removing it keeps clients from inheriting it.107
- unsafe { std::env::remove_var(DRIVERS_SELECT) };105
+ for (var, _) in &env {106
+ // SAFETY: as above. Removing them keeps clients from inheriting them.107
+ unsafe { std::env::remove_var(var) };108108
}109109
let instance = match instance {110110
Ok(i) => i,111
- Err(_) if filter.take().is_some() => continue,111
+ Err(_) if !std::mem::take(&mut env).is_empty() => continue,112112
Err(e) => return Err(e.into()),113113
};114114
match unsafe { pick_physical(&instance, wanted.as_deref(), node) } {115115
Some((p, f, n)) => break (instance, p, f, n),116116
None => {117117
unsafe { instance.destroy_instance(None) };118
- if filter.take().is_none() {118
+ if std::mem::take(&mut env).is_empty() {119119
return Err(Error::NoSuitableDevice);120120
}121121
}@@ -240,32 +240,67 @@ unsafe fn serves_node(instance: &ash::Instance, physical: vk::PhysicalDevice, no
240240
|| (drm.has_render == vk::TRUE && (drm.render_major, drm.render_minor) == (major, minor))241241
}242242
243
-const DRIVERS_SELECT: &str = "VK_LOADER_DRIVERS_SELECT";244
-245
-/// A loader glob matching the ICD for the kernel driver behind `node`, unless246
-/// the user already picks drivers themselves. The loader keeps every ICD it247
-/// finds loaded for the instance's lifetime, and on NixOS that includes248
-/// lavapipe and its 26 MB of resident libLLVM.249
-fn icd_filter(node: u64) -> Option<&'static str> {250
- let chosen = [251
- DRIVERS_SELECT,252
- "VK_LOADER_DRIVERS_DISABLE",253
- "VK_DRIVER_FILES",254
- "VK_ICD_FILENAMES",255
- "VK_ADD_DRIVER_FILES",256
- ];257
- if chosen.iter().any(|v| std::env::var_os(v).is_some()) {258
- return None;259
- }243
+/// Variables set only while the instance is created, unless the user already244
+/// picks drivers themselves. The loader keeps every ICD it finds loaded for the245
+/// instance's lifetime, on NixOS including lavapipe and 26 MB of resident246
+/// libLLVM. Without an X server NVIDIA's ICD also initialises through glvnd's247
+/// libEGL, which loads every EGL vendor, and Mesa's pulls in LLVM again.248
+fn driver_env(node: u64) -> Vec<(&'static str, std::ffi::OsString)> {249
+ let set = |vars: &[&str]| vars.iter().any(|v| std::env::var_os(v).is_some());260250
let (major, minor) = (rustix::fs::major(node), rustix::fs::minor(node));261
- let driver = std::fs::read_link(format!("/sys/dev/char/{major}:{minor}/device/driver")).ok()?;262
- match driver.file_name()?.to_str()? {251
+ let Ok(driver) = std::fs::read_link(format!("/sys/dev/char/{major}:{minor}/device/driver"))252
+ else {253
+ return Vec::new();254
+ };255
+ let driver = driver.file_name().and_then(|d| d.to_str()).unwrap_or_default();256
+ let mut env = Vec::new();257
+ let glob = match driver {263258
"nvidia" => Some("*nvidia*"),264259
"amdgpu" | "radeon" => Some("*radeon*"),265260
"i915" | "xe" => Some("*intel*"),266261
"nouveau" => Some("*nouveau*"),267262
_ => None,263
+ };264
+ let loader = [265
+ "VK_LOADER_DRIVERS_SELECT",266
+ "VK_LOADER_DRIVERS_DISABLE",267
+ "VK_DRIVER_FILES",268
+ "VK_ICD_FILENAMES",269
+ "VK_ADD_DRIVER_FILES",270
+ ];271
+ if let Some(glob) = glob272
+ && !set(&loader)273
+ {274
+ env.push(("VK_LOADER_DRIVERS_SELECT", glob.into()));268275
}276
+ let glvnd = ["__EGL_VENDOR_LIBRARY_FILENAMES", "__EGL_VENDOR_LIBRARY_DIRS"];277
+ if driver == "nvidia"278
+ && !set(&glvnd)279
+ && let Some(vendor) = nvidia_egl_vendor()280
+ {281
+ env.push(("__EGL_VENDOR_LIBRARY_FILENAMES", vendor.into()));282
+ }283
+ env284
+}285
+286
+/// NVIDIA's glvnd EGL vendor file, from glvnd's default search path.287
+fn nvidia_egl_vendor() -> Option<std::path::PathBuf> {288
+ [289
+ "/run/opengl-driver/share/glvnd/egl_vendor.d",290
+ "/etc/glvnd/egl_vendor.d",291
+ "/usr/share/glvnd/egl_vendor.d",292
+ ]293
+ .into_iter()294
+ .filter_map(|dir| std::fs::read_dir(dir).ok())295
+ .flatten()296
+ .filter_map(Result::ok)297
+ .map(|e| e.path())298
+ .find(|p| {299
+ p.extension().is_some_and(|e| e == "json")300
+ && p.file_name()301
+ .and_then(|n| n.to_str())302
+ .is_some_and(|n| n.contains("nvidia"))303
+ })269304
}270305
271306
/// The render node of `physical`, or its primary node when it has none.