atagen

monolith

public
Star0
Watch0

Commit 6118d58e7ed3

tarball

atagen <boss@atagen.co>2026-10-19 06:11 UTC

1 file changed+59-10
render-vk: load only the vulkan driver for the chosen gpu

The loader keeps every ICD it finds loaded for the instance's lifetime. On NixOS that means lavapipe, dzn, asahi and friends, with libLLVM alone holding 26 MB resident, which measured 37 MB of RSS for drivers that never run. VK_LOADER_DRIVERS_SELECT is set only around vkCreateInstance so clients do not inherit it, and an unmatched glob falls back to loading everything.
Modified file: crates/render-vk/src/device.rs+59 additions−10 deletions
@@ -87,17 +87,38 @@ impl Shared {
8787 .application_name(c"monolith")
8888 .engine_name(c"monolith")
8989 .api_version(vk::API_VERSION_1_3);
90- let instance = unsafe {
91- entry.create_instance(
92- &vk::InstanceCreateInfo::default().application_info(&app),
93- None,
94- )?
95- };
9690 let wanted = std::env::var("MONOLITH_VK_DEVICE").ok();
97- let picked = unsafe { pick_physical(&instance, wanted.as_deref(), node) };
98- let Some((physical, queue_family, name)) = picked else {
99- unsafe { instance.destroy_instance(None) };
100- return Err(Error::NoSuitableDevice);
91+ let mut filter = node.and_then(icd_filter);
92+ let (instance, physical, queue_family, name) = loop {
93+ if let Some(glob) = filter {
94+ // SAFETY: the compositor creates its renderer before starting any
95+ // other thread, so nothing reads the environment concurrently.
96+ unsafe { std::env::set_var(DRIVERS_SELECT, glob) };
97+ }
98+ let instance = unsafe {
99+ entry.create_instance(
100+ &vk::InstanceCreateInfo::default().application_info(&app),
101+ None,
102+ )
103+ };
104+ if filter.is_some() {
105+ // SAFETY: as above. Removing it keeps clients from inheriting it.
106+ unsafe { std::env::remove_var(DRIVERS_SELECT) };
107+ }
108+ let instance = match instance {
109+ Ok(i) => i,
110+ Err(_) if filter.take().is_some() => continue,
111+ Err(e) => return Err(e.into()),
112+ };
113+ match unsafe { pick_physical(&instance, wanted.as_deref(), node) } {
114+ Some((p, f, n)) => break (instance, p, f, n),
115+ None => {
116+ unsafe { instance.destroy_instance(None) };
117+ if filter.take().is_none() {
118+ return Err(Error::NoSuitableDevice);
119+ }
120+ }
121+ }
101122 };
102123 let priorities = [1.0];
103124 let available: Vec<_> = unsafe { instance.enumerate_device_extension_properties(physical) }
@@ -216,6 +237,34 @@ unsafe fn serves_node(instance: &ash::Instance, physical: vk::PhysicalDevice, no
216237 || (drm.has_render == vk::TRUE && (drm.render_major, drm.render_minor) == (major, minor))
217238 }
218239
240+const DRIVERS_SELECT: &str = "VK_LOADER_DRIVERS_SELECT";
241+
242+/// A loader glob matching the ICD for the kernel driver behind `node`, unless
243+/// the user already picks drivers themselves. The loader keeps every ICD it
244+/// finds loaded for the instance's lifetime, and on NixOS that includes
245+/// lavapipe and its 26 MB of resident libLLVM.
246+fn icd_filter(node: u64) -> Option<&'static str> {
247+ let chosen = [
248+ DRIVERS_SELECT,
249+ "VK_LOADER_DRIVERS_DISABLE",
250+ "VK_DRIVER_FILES",
251+ "VK_ICD_FILENAMES",
252+ "VK_ADD_DRIVER_FILES",
253+ ];
254+ if chosen.iter().any(|v| std::env::var_os(v).is_some()) {
255+ return None;
256+ }
257+ let (major, minor) = (rustix::fs::major(node), rustix::fs::minor(node));
258+ let driver = std::fs::read_link(format!("/sys/dev/char/{major}:{minor}/device/driver")).ok()?;
259+ match driver.file_name()?.to_str()? {
260+ "nvidia" => Some("*nvidia*"),
261+ "amdgpu" | "radeon" => Some("*radeon*"),
262+ "i915" | "xe" => Some("*intel*"),
263+ "nouveau" => Some("*nouveau*"),
264+ _ => None,
265+ }
266+}
267+
219268 /// The render node of `physical`, or its primary node when it has none.
220269 unsafe fn drm_node(instance: &ash::Instance, physical: vk::PhysicalDevice) -> Option<u64> {
221270 let mut drm = vk::PhysicalDeviceDrmPropertiesEXT::default();