atagen

monolith

public
Star0
Watch0
b4b7a4a30098c5e8560753b7d326a45fb5ee09fc
1
ssh://brewery@pls.lobotomise.me:33/atagen/monolith.git

Blame: crates/render-vk/src/device.rs

824bf02b6e55atagen2026-10-16 09:15 UTC1
use crate::Error;
824bf02b6e55atagen2026-10-16 09:15 UTC2
use ash::vk;
cacda79c0faaatagen2026-10-19 06:11 UTC3
use gpu_allocator::AllocationSizes;
824bf02b6e55atagen2026-10-16 09:15 UTC4
use gpu_allocator::vulkan::{Allocator, AllocatorCreateDesc};
8c3320fe9165atagen2026-10-19 10:22 UTC5
use std::mem::ManuallyDrop;
824bf02b6e55atagen2026-10-16 09:15 UTC6
use std::sync::Mutex;
824bf02b6e55atagen2026-10-16 09:15 UTC7
824bf02b6e55atagen2026-10-16 09:15 UTC8
/// Everything that must outlive every Vulkan object the renderer hands out.
824bf02b6e55atagen2026-10-16 09:15 UTC9
/// Fields drop in declaration order, so the allocator frees its memory before
824bf02b6e55atagen2026-10-16 09:15 UTC10
/// the device is destroyed.
824bf02b6e55atagen2026-10-16 09:15 UTC11
pub(crate) struct Shared {
8c3320fe9165atagen2026-10-19 10:22 UTC12
    pub allocator: ManuallyDrop<Mutex<Allocator>>,
824bf02b6e55atagen2026-10-16 09:15 UTC13
    pub device: Device,
824bf02b6e55atagen2026-10-16 09:15 UTC14
}
824bf02b6e55atagen2026-10-16 09:15 UTC15
8c3320fe9165atagen2026-10-19 10:22 UTC16
impl Drop for Shared {
8c3320fe9165atagen2026-10-19 10:22 UTC17
    fn drop(&mut self) {
8c3320fe9165atagen2026-10-19 10:22 UTC18
        if !crate::error::device_lost() {
8c3320fe9165atagen2026-10-19 10:22 UTC19
            unsafe { ManuallyDrop::drop(&mut self.allocator) };
8c3320fe9165atagen2026-10-19 10:22 UTC20
        }
8c3320fe9165atagen2026-10-19 10:22 UTC21
    }
8c3320fe9165atagen2026-10-19 10:22 UTC22
}
8c3320fe9165atagen2026-10-19 10:22 UTC23
7de49c2aa70batagen2026-10-16 09:27 UTC24
/// Loaders for the extensions that dmabuf import, export and fence export
7de49c2aa70batagen2026-10-16 09:27 UTC25
/// need. Absent when the device lacks any of them.
7de49c2aa70batagen2026-10-16 09:27 UTC26
pub(crate) struct DmabufExt {
7de49c2aa70batagen2026-10-16 09:27 UTC27
    pub memory_fd: ash::khr::external_memory_fd::Device,
7de49c2aa70batagen2026-10-16 09:27 UTC28
    pub fence_fd: ash::khr::external_fence_fd::Device,
7de49c2aa70batagen2026-10-16 09:27 UTC29
    pub modifier: ash::ext::image_drm_format_modifier::Device,
7de49c2aa70batagen2026-10-16 09:27 UTC30
}
7de49c2aa70batagen2026-10-16 09:27 UTC31
7de49c2aa70batagen2026-10-16 09:27 UTC32
const DMABUF_EXTENSIONS: [&std::ffi::CStr; 5] = [
7de49c2aa70batagen2026-10-16 09:27 UTC33
    ash::khr::external_memory_fd::NAME,
7de49c2aa70batagen2026-10-16 09:27 UTC34
    ash::ext::external_memory_dma_buf::NAME,
7de49c2aa70batagen2026-10-16 09:27 UTC35
    ash::ext::image_drm_format_modifier::NAME,
7de49c2aa70batagen2026-10-16 09:27 UTC36
    ash::ext::queue_family_foreign::NAME,
7de49c2aa70batagen2026-10-16 09:27 UTC37
    ash::khr::external_fence_fd::NAME,
7de49c2aa70batagen2026-10-16 09:27 UTC38
];
7de49c2aa70batagen2026-10-16 09:27 UTC39
824bf02b6e55atagen2026-10-16 09:15 UTC40
pub(crate) struct Device {
824bf02b6e55atagen2026-10-16 09:15 UTC41
    pub _entry: ash::Entry,
824bf02b6e55atagen2026-10-16 09:15 UTC42
    pub instance: ash::Instance,
824bf02b6e55atagen2026-10-16 09:15 UTC43
    pub physical: vk::PhysicalDevice,
824bf02b6e55atagen2026-10-16 09:15 UTC44
    pub device: ash::Device,
824bf02b6e55atagen2026-10-16 09:15 UTC45
    pub queue: Mutex<vk::Queue>,
824bf02b6e55atagen2026-10-16 09:15 UTC46
    pub queue_family: u32,
824bf02b6e55atagen2026-10-16 09:15 UTC47
    pub push_descriptor: ash::khr::push_descriptor::Device,
7de49c2aa70batagen2026-10-16 09:27 UTC48
    pub dmabuf: Option<DmabufExt>,
824bf02b6e55atagen2026-10-16 09:15 UTC49
    pub name: String,
2454f8b9122datagen2026-10-19 06:05 UTC50
    pub drm_node: Option<u64>,
d5eca1c82213atagen2026-10-16 13:56 UTC51
    /// Signalled fences kept for reuse, see `FenceInner`.
d5eca1c82213atagen2026-10-16 13:56 UTC52
    pub spare_fences: Mutex<Vec<vk::Fence>>,
824bf02b6e55atagen2026-10-16 09:15 UTC53
}
824bf02b6e55atagen2026-10-16 09:15 UTC54
8c3320fe9165atagen2026-10-19 10:22 UTC55
/// A lost or hung device is leaked. Waiting on it or destroying it can block
8c3320fe9165atagen2026-10-19 10:22 UTC56
/// forever in the driver, and the process is about to exit anyway.
824bf02b6e55atagen2026-10-16 09:15 UTC57
impl Drop for Device {
824bf02b6e55atagen2026-10-16 09:15 UTC58
    fn drop(&mut self) {
8c3320fe9165atagen2026-10-19 10:22 UTC59
        if crate::error::device_lost() {
8c3320fe9165atagen2026-10-19 10:22 UTC60
            return;
8c3320fe9165atagen2026-10-19 10:22 UTC61
        }
824bf02b6e55atagen2026-10-16 09:15 UTC62
        unsafe {
824bf02b6e55atagen2026-10-16 09:15 UTC63
            let _ = self.device.device_wait_idle();
d5eca1c82213atagen2026-10-16 13:56 UTC64
            for f in self
d5eca1c82213atagen2026-10-16 13:56 UTC65
                .spare_fences
d5eca1c82213atagen2026-10-16 13:56 UTC66
                .get_mut()
d5eca1c82213atagen2026-10-16 13:56 UTC67
                .unwrap_or_else(|e| e.into_inner())
d5eca1c82213atagen2026-10-16 13:56 UTC68
                .drain(..)
d5eca1c82213atagen2026-10-16 13:56 UTC69
            {
d5eca1c82213atagen2026-10-16 13:56 UTC70
                self.device.destroy_fence(f, None);
d5eca1c82213atagen2026-10-16 13:56 UTC71
            }
824bf02b6e55atagen2026-10-16 09:15 UTC72
            self.device.destroy_device(None);
824bf02b6e55atagen2026-10-16 09:15 UTC73
            self.instance.destroy_instance(None);
824bf02b6e55atagen2026-10-16 09:15 UTC74
        }
824bf02b6e55atagen2026-10-16 09:15 UTC75
    }
824bf02b6e55atagen2026-10-16 09:15 UTC76
}
824bf02b6e55atagen2026-10-16 09:15 UTC77
824bf02b6e55atagen2026-10-16 09:15 UTC78
impl Shared {
824bf02b6e55atagen2026-10-16 09:15 UTC79
    /// Opens the first suitable device, or the one whose name contains
f27fa4c2ade9atagen2026-10-16 12:59 UTC80
    /// `MONOLITH_VK_DEVICE` when that is set. With `node`, only the device
f27fa4c2ade9atagen2026-10-16 12:59 UTC81
    /// behind that DRM device number qualifies.
f27fa4c2ade9atagen2026-10-16 12:59 UTC82
    pub fn new(node: Option<u64>) -> Result<Self, Error> {
824bf02b6e55atagen2026-10-16 09:15 UTC83
        let entry = unsafe { ash::Entry::load()? };
824bf02b6e55atagen2026-10-16 09:15 UTC84
        let app = vk::ApplicationInfo::default()
824bf02b6e55atagen2026-10-16 09:15 UTC85
            .application_name(c"monolith")
824bf02b6e55atagen2026-10-16 09:15 UTC86
            .engine_name(c"monolith")
824bf02b6e55atagen2026-10-16 09:15 UTC87
            .api_version(vk::API_VERSION_1_3);
824bf02b6e55atagen2026-10-16 09:15 UTC88
        let wanted = std::env::var("MONOLITH_VK_DEVICE").ok();
b70469d195a1atagen2026-10-19 06:23 UTC89
        let mut env = node.map(driver_env).unwrap_or_default();
6118d58e7ed3atagen2026-10-19 06:08 UTC90
        let (instance, physical, queue_family, name) = loop {
b70469d195a1atagen2026-10-19 06:23 UTC91
            for (var, value) in &env {
6118d58e7ed3atagen2026-10-19 06:08 UTC92
                // SAFETY: the compositor creates its renderer before starting any
6118d58e7ed3atagen2026-10-19 06:08 UTC93
                // other thread, so nothing reads the environment concurrently.
b70469d195a1atagen2026-10-19 06:23 UTC94
                unsafe { std::env::set_var(var, value) };
6118d58e7ed3atagen2026-10-19 06:08 UTC95
            }
6118d58e7ed3atagen2026-10-19 06:08 UTC96
            let instance = unsafe {
6118d58e7ed3atagen2026-10-19 06:08 UTC97
                entry.create_instance(
6118d58e7ed3atagen2026-10-19 06:08 UTC98
                    &vk::InstanceCreateInfo::default().application_info(&app),
6118d58e7ed3atagen2026-10-19 06:08 UTC99
                    None,
6118d58e7ed3atagen2026-10-19 06:08 UTC100
                )
6118d58e7ed3atagen2026-10-19 06:08 UTC101
            };
b70469d195a1atagen2026-10-19 06:23 UTC102
            for (var, _) in &env {
b70469d195a1atagen2026-10-19 06:23 UTC103
                // SAFETY: as above. Removing them keeps clients from inheriting them.
b70469d195a1atagen2026-10-19 06:23 UTC104
                unsafe { std::env::remove_var(var) };
6118d58e7ed3atagen2026-10-19 06:08 UTC105
            }
6118d58e7ed3atagen2026-10-19 06:08 UTC106
            let instance = match instance {
6118d58e7ed3atagen2026-10-19 06:08 UTC107
                Ok(i) => i,
b70469d195a1atagen2026-10-19 06:23 UTC108
                Err(_) if !std::mem::take(&mut env).is_empty() => continue,
6118d58e7ed3atagen2026-10-19 06:08 UTC109
                Err(e) => return Err(e.into()),
6118d58e7ed3atagen2026-10-19 06:08 UTC110
            };
6118d58e7ed3atagen2026-10-19 06:08 UTC111
            match unsafe { pick_physical(&instance, wanted.as_deref(), node) } {
6118d58e7ed3atagen2026-10-19 06:08 UTC112
                Some((p, f, n)) => break (instance, p, f, n),
6118d58e7ed3atagen2026-10-19 06:08 UTC113
                None => {
6118d58e7ed3atagen2026-10-19 06:08 UTC114
                    unsafe { instance.destroy_instance(None) };
b70469d195a1atagen2026-10-19 06:23 UTC115
                    if std::mem::take(&mut env).is_empty() {
6118d58e7ed3atagen2026-10-19 06:08 UTC116
                        return Err(Error::NoSuitableDevice);
6118d58e7ed3atagen2026-10-19 06:08 UTC117
                    }
6118d58e7ed3atagen2026-10-19 06:08 UTC118
                }
6118d58e7ed3atagen2026-10-19 06:08 UTC119
            }
824bf02b6e55atagen2026-10-16 09:15 UTC120
        };
824bf02b6e55atagen2026-10-16 09:15 UTC121
        let priorities = [1.0];
7de49c2aa70batagen2026-10-16 09:27 UTC122
        let available: Vec<_> = unsafe { instance.enumerate_device_extension_properties(physical) }
7de49c2aa70batagen2026-10-16 09:27 UTC123
            .unwrap_or_default()
7de49c2aa70batagen2026-10-16 09:27 UTC124
            .iter()
7de49c2aa70batagen2026-10-16 09:27 UTC125
            .filter_map(|e| e.extension_name_as_c_str().ok().map(ToOwned::to_owned))
7de49c2aa70batagen2026-10-16 09:27 UTC126
            .collect();
2454f8b9122datagen2026-10-19 06:05 UTC127
        let drm_node = available
2454f8b9122datagen2026-10-19 06:05 UTC128
            .iter()
2454f8b9122datagen2026-10-19 06:05 UTC129
            .any(|a| a.as_c_str() == ash::ext::physical_device_drm::NAME)
2454f8b9122datagen2026-10-19 06:05 UTC130
            .then(|| unsafe { drm_node(&instance, physical) })
2454f8b9122datagen2026-10-19 06:05 UTC131
            .flatten();
7de49c2aa70batagen2026-10-16 09:27 UTC132
        let has_dmabuf = DMABUF_EXTENSIONS
7de49c2aa70batagen2026-10-16 09:27 UTC133
            .iter()
7de49c2aa70batagen2026-10-16 09:27 UTC134
            .all(|n| available.iter().any(|a| a.as_c_str() == *n));
7de49c2aa70batagen2026-10-16 09:27 UTC135
        let mut extensions = vec![ash::khr::push_descriptor::NAME.as_ptr()];
7de49c2aa70batagen2026-10-16 09:27 UTC136
        if has_dmabuf {
7de49c2aa70batagen2026-10-16 09:27 UTC137
            extensions.extend(DMABUF_EXTENSIONS.iter().map(|n| n.as_ptr()));
7de49c2aa70batagen2026-10-16 09:27 UTC138
        }
796c963a4b55atagen2026-10-16 22:46 UTC139
        let global_priority = [
796c963a4b55atagen2026-10-16 22:46 UTC140
            ash::khr::global_priority::NAME,
796c963a4b55atagen2026-10-16 22:46 UTC141
            ash::ext::global_priority::NAME,
796c963a4b55atagen2026-10-16 22:46 UTC142
        ]
796c963a4b55atagen2026-10-16 22:46 UTC143
        .into_iter()
796c963a4b55atagen2026-10-16 22:46 UTC144
        .find(|n| available.iter().any(|a| a.as_c_str() == *n));
796c963a4b55atagen2026-10-16 22:46 UTC145
        let mut priorities_to_try = vec![None];
796c963a4b55atagen2026-10-16 22:46 UTC146
        if let Some(name) = global_priority {
796c963a4b55atagen2026-10-16 22:46 UTC147
            extensions.push(name.as_ptr());
796c963a4b55atagen2026-10-16 22:46 UTC148
            priorities_to_try = vec![
796c963a4b55atagen2026-10-16 22:46 UTC149
                Some(vk::QueueGlobalPriorityKHR::REALTIME),
796c963a4b55atagen2026-10-16 22:46 UTC150
                Some(vk::QueueGlobalPriorityKHR::HIGH),
796c963a4b55atagen2026-10-16 22:46 UTC151
                None,
796c963a4b55atagen2026-10-16 22:46 UTC152
            ];
796c963a4b55atagen2026-10-16 22:46 UTC153
        }
796c963a4b55atagen2026-10-16 22:46 UTC154
        let mut created = Err(vk::Result::ERROR_INITIALIZATION_FAILED);
796c963a4b55atagen2026-10-16 22:46 UTC155
        for priority in priorities_to_try {
796c963a4b55atagen2026-10-16 22:46 UTC156
            let mut features13 = vk::PhysicalDeviceVulkan13Features::default()
796c963a4b55atagen2026-10-16 22:46 UTC157
                .dynamic_rendering(true)
796c963a4b55atagen2026-10-16 22:46 UTC158
                .synchronization2(true);
796c963a4b55atagen2026-10-16 22:46 UTC159
            let mut global = vk::DeviceQueueGlobalPriorityCreateInfoKHR::default()
796c963a4b55atagen2026-10-16 22:46 UTC160
                .global_priority(priority.unwrap_or(vk::QueueGlobalPriorityKHR::MEDIUM));
796c963a4b55atagen2026-10-16 22:46 UTC161
            let mut queue = vk::DeviceQueueCreateInfo::default()
796c963a4b55atagen2026-10-16 22:46 UTC162
                .queue_family_index(queue_family)
796c963a4b55atagen2026-10-16 22:46 UTC163
                .queue_priorities(&priorities);
796c963a4b55atagen2026-10-16 22:46 UTC164
            if priority.is_some() {
796c963a4b55atagen2026-10-16 22:46 UTC165
                queue = queue.push_next(&mut global);
796c963a4b55atagen2026-10-16 22:46 UTC166
            }
796c963a4b55atagen2026-10-16 22:46 UTC167
            let queues = [queue];
796c963a4b55atagen2026-10-16 22:46 UTC168
            let info = vk::DeviceCreateInfo::default()
796c963a4b55atagen2026-10-16 22:46 UTC169
                .queue_create_infos(&queues)
796c963a4b55atagen2026-10-16 22:46 UTC170
                .enabled_extension_names(&extensions)
796c963a4b55atagen2026-10-16 22:46 UTC171
                .push_next(&mut features13);
796c963a4b55atagen2026-10-16 22:46 UTC172
            created = unsafe { instance.create_device(physical, &info, None) };
796c963a4b55atagen2026-10-16 22:46 UTC173
            match &created {
796c963a4b55atagen2026-10-16 22:46 UTC174
                Ok(_) => {
796c963a4b55atagen2026-10-16 22:46 UTC175
                    tracing::info!(?priority, "queue global priority");
796c963a4b55atagen2026-10-16 22:46 UTC176
                    break;
796c963a4b55atagen2026-10-16 22:46 UTC177
                }
796c963a4b55atagen2026-10-16 22:46 UTC178
                Err(e) => tracing::debug!(?priority, "queue global priority refused: {e}"),
796c963a4b55atagen2026-10-16 22:46 UTC179
            }
796c963a4b55atagen2026-10-16 22:46 UTC180
        }
796c963a4b55atagen2026-10-16 22:46 UTC181
        let device = match created {
824bf02b6e55atagen2026-10-16 09:15 UTC182
            Ok(d) => d,
824bf02b6e55atagen2026-10-16 09:15 UTC183
            Err(e) => {
824bf02b6e55atagen2026-10-16 09:15 UTC184
                unsafe { instance.destroy_instance(None) };
824bf02b6e55atagen2026-10-16 09:15 UTC185
                return Err(e.into());
824bf02b6e55atagen2026-10-16 09:15 UTC186
            }
824bf02b6e55atagen2026-10-16 09:15 UTC187
        };
824bf02b6e55atagen2026-10-16 09:15 UTC188
        let queue = unsafe { device.get_device_queue(queue_family, 0) };
824bf02b6e55atagen2026-10-16 09:15 UTC189
        let push_descriptor = ash::khr::push_descriptor::Device::new(&instance, &device);
7de49c2aa70batagen2026-10-16 09:27 UTC190
        let dmabuf = has_dmabuf.then(|| DmabufExt {
7de49c2aa70batagen2026-10-16 09:27 UTC191
            memory_fd: ash::khr::external_memory_fd::Device::new(&instance, &device),
7de49c2aa70batagen2026-10-16 09:27 UTC192
            fence_fd: ash::khr::external_fence_fd::Device::new(&instance, &device),
7de49c2aa70batagen2026-10-16 09:27 UTC193
            modifier: ash::ext::image_drm_format_modifier::Device::new(&instance, &device),
7de49c2aa70batagen2026-10-16 09:27 UTC194
        });
824bf02b6e55atagen2026-10-16 09:15 UTC195
        let allocator = Allocator::new(&AllocatorCreateDesc {
824bf02b6e55atagen2026-10-16 09:15 UTC196
            instance: instance.clone(),
824bf02b6e55atagen2026-10-16 09:15 UTC197
            device: device.clone(),
824bf02b6e55atagen2026-10-16 09:15 UTC198
            physical_device: physical,
824bf02b6e55atagen2026-10-16 09:15 UTC199
            debug_settings: Default::default(),
824bf02b6e55atagen2026-10-16 09:15 UTC200
            buffer_device_address: false,
cacda79c0faaatagen2026-10-19 06:11 UTC201
            // Host blocks are pinned system RAM that stays resident while any
cacda79c0faaatagen2026-10-19 06:11 UTC202
            // allocation lives in them, and the default 64 MB block is mostly idle.
3521a747467eatagen2026-10-19 07:20 UTC203
            allocation_sizes: AllocationSizes::new(64 << 20, 4 << 20),
824bf02b6e55atagen2026-10-16 09:15 UTC204
        });
824bf02b6e55atagen2026-10-16 09:15 UTC205
        let device = Device {
824bf02b6e55atagen2026-10-16 09:15 UTC206
            _entry: entry,
824bf02b6e55atagen2026-10-16 09:15 UTC207
            instance,
824bf02b6e55atagen2026-10-16 09:15 UTC208
            physical,
824bf02b6e55atagen2026-10-16 09:15 UTC209
            device,
824bf02b6e55atagen2026-10-16 09:15 UTC210
            queue: Mutex::new(queue),
824bf02b6e55atagen2026-10-16 09:15 UTC211
            queue_family,
824bf02b6e55atagen2026-10-16 09:15 UTC212
            push_descriptor,
7de49c2aa70batagen2026-10-16 09:27 UTC213
            dmabuf,
824bf02b6e55atagen2026-10-16 09:15 UTC214
            name,
2454f8b9122datagen2026-10-19 06:05 UTC215
            drm_node,
d5eca1c82213atagen2026-10-16 13:56 UTC216
            spare_fences: Mutex::new(Vec::new()),
824bf02b6e55atagen2026-10-16 09:15 UTC217
        };
824bf02b6e55atagen2026-10-16 09:15 UTC218
        Ok(Self {
8c3320fe9165atagen2026-10-19 10:22 UTC219
            allocator: ManuallyDrop::new(Mutex::new(allocator?)),
824bf02b6e55atagen2026-10-16 09:15 UTC220
            device,
824bf02b6e55atagen2026-10-16 09:15 UTC221
        })
824bf02b6e55atagen2026-10-16 09:15 UTC222
    }
824bf02b6e55atagen2026-10-16 09:15 UTC223
}
824bf02b6e55atagen2026-10-16 09:15 UTC224
f27fa4c2ade9atagen2026-10-16 12:59 UTC225
/// Whether `physical` is the device behind DRM device number `node`, as its
f27fa4c2ade9atagen2026-10-16 12:59 UTC226
/// primary or render node.
f27fa4c2ade9atagen2026-10-16 12:59 UTC227
unsafe fn serves_node(instance: &ash::Instance, physical: vk::PhysicalDevice, node: u64) -> bool {
f27fa4c2ade9atagen2026-10-16 12:59 UTC228
    let mut drm = vk::PhysicalDeviceDrmPropertiesEXT::default();
f27fa4c2ade9atagen2026-10-16 12:59 UTC229
    let mut props = vk::PhysicalDeviceProperties2::default().push_next(&mut drm);
f27fa4c2ade9atagen2026-10-16 12:59 UTC230
    unsafe { instance.get_physical_device_properties2(physical, &mut props) };
f27fa4c2ade9atagen2026-10-16 12:59 UTC231
    let (major, minor) = (
f27fa4c2ade9atagen2026-10-16 12:59 UTC232
        rustix::fs::major(node) as i64,
f27fa4c2ade9atagen2026-10-16 12:59 UTC233
        rustix::fs::minor(node) as i64,
f27fa4c2ade9atagen2026-10-16 12:59 UTC234
    );
f27fa4c2ade9atagen2026-10-16 12:59 UTC235
    (drm.has_primary == vk::TRUE && (drm.primary_major, drm.primary_minor) == (major, minor))
f27fa4c2ade9atagen2026-10-16 12:59 UTC236
        || (drm.has_render == vk::TRUE && (drm.render_major, drm.render_minor) == (major, minor))
f27fa4c2ade9atagen2026-10-16 12:59 UTC237
}
f27fa4c2ade9atagen2026-10-16 12:59 UTC238
b70469d195a1atagen2026-10-19 06:23 UTC239
/// Variables set only while the instance is created, unless the user already
b70469d195a1atagen2026-10-19 06:23 UTC240
/// picks drivers themselves. The loader keeps every ICD it finds loaded for the
b70469d195a1atagen2026-10-19 06:23 UTC241
/// instance's lifetime, on NixOS including lavapipe and 26 MB of resident
b70469d195a1atagen2026-10-19 06:23 UTC242
/// libLLVM. Without an X server NVIDIA's ICD also initialises through glvnd's
b70469d195a1atagen2026-10-19 06:23 UTC243
/// libEGL, which loads every EGL vendor, and Mesa's pulls in LLVM again.
b70469d195a1atagen2026-10-19 06:23 UTC244
fn driver_env(node: u64) -> Vec<(&'static str, std::ffi::OsString)> {
b70469d195a1atagen2026-10-19 06:23 UTC245
    let set = |vars: &[&str]| vars.iter().any(|v| std::env::var_os(v).is_some());
6118d58e7ed3atagen2026-10-19 06:08 UTC246
    let (major, minor) = (rustix::fs::major(node), rustix::fs::minor(node));
b70469d195a1atagen2026-10-19 06:23 UTC247
    let Ok(driver) = std::fs::read_link(format!("/sys/dev/char/{major}:{minor}/device/driver"))
b70469d195a1atagen2026-10-19 06:23 UTC248
    else {
b70469d195a1atagen2026-10-19 06:23 UTC249
        return Vec::new();
b70469d195a1atagen2026-10-19 06:23 UTC250
    };
7a16cb5ea593atagen2026-10-19 08:02 UTC251
    let driver = driver
7a16cb5ea593atagen2026-10-19 08:02 UTC252
        .file_name()
7a16cb5ea593atagen2026-10-19 08:02 UTC253
        .and_then(|d| d.to_str())
7a16cb5ea593atagen2026-10-19 08:02 UTC254
        .unwrap_or_default();
b70469d195a1atagen2026-10-19 06:23 UTC255
    let mut env = Vec::new();
b70469d195a1atagen2026-10-19 06:23 UTC256
    let glob = match driver {
6118d58e7ed3atagen2026-10-19 06:08 UTC257
        "nvidia" => Some("*nvidia*"),
6118d58e7ed3atagen2026-10-19 06:08 UTC258
        "amdgpu" | "radeon" => Some("*radeon*"),
6118d58e7ed3atagen2026-10-19 06:08 UTC259
        "i915" | "xe" => Some("*intel*"),
6118d58e7ed3atagen2026-10-19 06:08 UTC260
        "nouveau" => Some("*nouveau*"),
6118d58e7ed3atagen2026-10-19 06:08 UTC261
        _ => None,
b70469d195a1atagen2026-10-19 06:23 UTC262
    };
b70469d195a1atagen2026-10-19 06:23 UTC263
    let loader = [
b70469d195a1atagen2026-10-19 06:23 UTC264
        "VK_LOADER_DRIVERS_SELECT",
b70469d195a1atagen2026-10-19 06:23 UTC265
        "VK_LOADER_DRIVERS_DISABLE",
b70469d195a1atagen2026-10-19 06:23 UTC266
        "VK_DRIVER_FILES",
b70469d195a1atagen2026-10-19 06:23 UTC267
        "VK_ICD_FILENAMES",
b70469d195a1atagen2026-10-19 06:23 UTC268
        "VK_ADD_DRIVER_FILES",
b70469d195a1atagen2026-10-19 06:23 UTC269
    ];
b70469d195a1atagen2026-10-19 06:23 UTC270
    if let Some(glob) = glob
b70469d195a1atagen2026-10-19 06:23 UTC271
        && !set(&loader)
b70469d195a1atagen2026-10-19 06:23 UTC272
    {
b70469d195a1atagen2026-10-19 06:23 UTC273
        env.push(("VK_LOADER_DRIVERS_SELECT", glob.into()));
6118d58e7ed3atagen2026-10-19 06:08 UTC274
    }
7a16cb5ea593atagen2026-10-19 08:02 UTC275
    let glvnd = [
7a16cb5ea593atagen2026-10-19 08:02 UTC276
        "__EGL_VENDOR_LIBRARY_FILENAMES",
7a16cb5ea593atagen2026-10-19 08:02 UTC277
        "__EGL_VENDOR_LIBRARY_DIRS",
7a16cb5ea593atagen2026-10-19 08:02 UTC278
    ];
b70469d195a1atagen2026-10-19 06:23 UTC279
    if driver == "nvidia"
b70469d195a1atagen2026-10-19 06:23 UTC280
        && !set(&glvnd)
b70469d195a1atagen2026-10-19 06:23 UTC281
        && let Some(vendor) = nvidia_egl_vendor()
b70469d195a1atagen2026-10-19 06:23 UTC282
    {
b70469d195a1atagen2026-10-19 06:23 UTC283
        env.push(("__EGL_VENDOR_LIBRARY_FILENAMES", vendor.into()));
b70469d195a1atagen2026-10-19 06:23 UTC284
    }
b70469d195a1atagen2026-10-19 06:23 UTC285
    env
b70469d195a1atagen2026-10-19 06:23 UTC286
}
b70469d195a1atagen2026-10-19 06:23 UTC287
b70469d195a1atagen2026-10-19 06:23 UTC288
/// NVIDIA's glvnd EGL vendor file, from glvnd's default search path.
b70469d195a1atagen2026-10-19 06:23 UTC289
fn nvidia_egl_vendor() -> Option<std::path::PathBuf> {
b70469d195a1atagen2026-10-19 06:23 UTC290
    [
b70469d195a1atagen2026-10-19 06:23 UTC291
        "/run/opengl-driver/share/glvnd/egl_vendor.d",
b70469d195a1atagen2026-10-19 06:23 UTC292
        "/etc/glvnd/egl_vendor.d",
b70469d195a1atagen2026-10-19 06:23 UTC293
        "/usr/share/glvnd/egl_vendor.d",
b70469d195a1atagen2026-10-19 06:23 UTC294
    ]
b70469d195a1atagen2026-10-19 06:23 UTC295
    .into_iter()
b70469d195a1atagen2026-10-19 06:23 UTC296
    .filter_map(|dir| std::fs::read_dir(dir).ok())
b70469d195a1atagen2026-10-19 06:23 UTC297
    .flatten()
b70469d195a1atagen2026-10-19 06:23 UTC298
    .filter_map(Result::ok)
b70469d195a1atagen2026-10-19 06:23 UTC299
    .map(|e| e.path())
b70469d195a1atagen2026-10-19 06:23 UTC300
    .find(|p| {
b70469d195a1atagen2026-10-19 06:23 UTC301
        p.extension().is_some_and(|e| e == "json")
b70469d195a1atagen2026-10-19 06:23 UTC302
            && p.file_name()
b70469d195a1atagen2026-10-19 06:23 UTC303
                .and_then(|n| n.to_str())
b70469d195a1atagen2026-10-19 06:23 UTC304
                .is_some_and(|n| n.contains("nvidia"))
b70469d195a1atagen2026-10-19 06:23 UTC305
    })
6118d58e7ed3atagen2026-10-19 06:08 UTC306
}
6118d58e7ed3atagen2026-10-19 06:08 UTC307
2454f8b9122datagen2026-10-19 06:05 UTC308
/// The render node of `physical`, or its primary node when it has none.
2454f8b9122datagen2026-10-19 06:05 UTC309
unsafe fn drm_node(instance: &ash::Instance, physical: vk::PhysicalDevice) -> Option<u64> {
2454f8b9122datagen2026-10-19 06:05 UTC310
    let mut drm = vk::PhysicalDeviceDrmPropertiesEXT::default();
2454f8b9122datagen2026-10-19 06:05 UTC311
    let mut props = vk::PhysicalDeviceProperties2::default().push_next(&mut drm);
2454f8b9122datagen2026-10-19 06:05 UTC312
    unsafe { instance.get_physical_device_properties2(physical, &mut props) };
2454f8b9122datagen2026-10-19 06:05 UTC313
    let node = |major: i64, minor: i64| rustix::fs::makedev(major as u32, minor as u32);
2454f8b9122datagen2026-10-19 06:05 UTC314
    if drm.has_render == vk::TRUE {
2454f8b9122datagen2026-10-19 06:05 UTC315
        Some(node(drm.render_major, drm.render_minor))
2454f8b9122datagen2026-10-19 06:05 UTC316
    } else if drm.has_primary == vk::TRUE {
2454f8b9122datagen2026-10-19 06:05 UTC317
        Some(node(drm.primary_major, drm.primary_minor))
2454f8b9122datagen2026-10-19 06:05 UTC318
    } else {
2454f8b9122datagen2026-10-19 06:05 UTC319
        None
2454f8b9122datagen2026-10-19 06:05 UTC320
    }
2454f8b9122datagen2026-10-19 06:05 UTC321
}
2454f8b9122datagen2026-10-19 06:05 UTC322
824bf02b6e55atagen2026-10-16 09:15 UTC323
unsafe fn pick_physical(
824bf02b6e55atagen2026-10-16 09:15 UTC324
    instance: &ash::Instance,
824bf02b6e55atagen2026-10-16 09:15 UTC325
    wanted: Option<&str>,
f27fa4c2ade9atagen2026-10-16 12:59 UTC326
    node: Option<u64>,
824bf02b6e55atagen2026-10-16 09:15 UTC327
) -> Option<(vk::PhysicalDevice, u32, String)> {
824bf02b6e55atagen2026-10-16 09:15 UTC328
    let devices = unsafe { instance.enumerate_physical_devices() }.ok()?;
824bf02b6e55atagen2026-10-16 09:15 UTC329
    let mut candidates = Vec::new();
824bf02b6e55atagen2026-10-16 09:15 UTC330
    for physical in devices {
824bf02b6e55atagen2026-10-16 09:15 UTC331
        let props = unsafe { instance.get_physical_device_properties(physical) };
824bf02b6e55atagen2026-10-16 09:15 UTC332
        if props.api_version < vk::API_VERSION_1_3 {
824bf02b6e55atagen2026-10-16 09:15 UTC333
            continue;
824bf02b6e55atagen2026-10-16 09:15 UTC334
        }
824bf02b6e55atagen2026-10-16 09:15 UTC335
        let name = props
824bf02b6e55atagen2026-10-16 09:15 UTC336
            .device_name_as_c_str()
824bf02b6e55atagen2026-10-16 09:15 UTC337
            .unwrap_or(c"unknown")
824bf02b6e55atagen2026-10-16 09:15 UTC338
            .to_string_lossy()
824bf02b6e55atagen2026-10-16 09:15 UTC339
            .into_owned();
824bf02b6e55atagen2026-10-16 09:15 UTC340
        if wanted.is_some_and(|w| !name.contains(w)) {
824bf02b6e55atagen2026-10-16 09:15 UTC341
            continue;
824bf02b6e55atagen2026-10-16 09:15 UTC342
        }
824bf02b6e55atagen2026-10-16 09:15 UTC343
        let mut f13 = vk::PhysicalDeviceVulkan13Features::default();
824bf02b6e55atagen2026-10-16 09:15 UTC344
        let mut f2 = vk::PhysicalDeviceFeatures2::default().push_next(&mut f13);
824bf02b6e55atagen2026-10-16 09:15 UTC345
        unsafe { instance.get_physical_device_features2(physical, &mut f2) };
824bf02b6e55atagen2026-10-16 09:15 UTC346
        if f13.dynamic_rendering == vk::FALSE || f13.synchronization2 == vk::FALSE {
824bf02b6e55atagen2026-10-16 09:15 UTC347
            continue;
824bf02b6e55atagen2026-10-16 09:15 UTC348
        }
f27fa4c2ade9atagen2026-10-16 12:59 UTC349
        let extensions =
f27fa4c2ade9atagen2026-10-16 12:59 UTC350
            unsafe { instance.enumerate_device_extension_properties(physical) }.unwrap_or_default();
f27fa4c2ade9atagen2026-10-16 12:59 UTC351
        let has = |n: &std::ffi::CStr| {
f27fa4c2ade9atagen2026-10-16 12:59 UTC352
            extensions
f27fa4c2ade9atagen2026-10-16 12:59 UTC353
                .iter()
f27fa4c2ade9atagen2026-10-16 12:59 UTC354
                .any(|e| e.extension_name_as_c_str().ok() == Some(n))
f27fa4c2ade9atagen2026-10-16 12:59 UTC355
        };
f27fa4c2ade9atagen2026-10-16 12:59 UTC356
        if !has(ash::khr::push_descriptor::NAME) {
f27fa4c2ade9atagen2026-10-16 12:59 UTC357
            continue;
f27fa4c2ade9atagen2026-10-16 12:59 UTC358
        }
f27fa4c2ade9atagen2026-10-16 12:59 UTC359
        if let Some(node) = node
f27fa4c2ade9atagen2026-10-16 12:59 UTC360
            && !(has(ash::ext::physical_device_drm::NAME)
f27fa4c2ade9atagen2026-10-16 12:59 UTC361
                && unsafe { serves_node(instance, physical, node) })
f27fa4c2ade9atagen2026-10-16 12:59 UTC362
        {
824bf02b6e55atagen2026-10-16 09:15 UTC363
            continue;
824bf02b6e55atagen2026-10-16 09:15 UTC364
        }
824bf02b6e55atagen2026-10-16 09:15 UTC365
        let families = unsafe { instance.get_physical_device_queue_family_properties(physical) };
824bf02b6e55atagen2026-10-16 09:15 UTC366
        let Some(family) = families
824bf02b6e55atagen2026-10-16 09:15 UTC367
            .iter()
824bf02b6e55atagen2026-10-16 09:15 UTC368
            .position(|f| f.queue_flags.contains(vk::QueueFlags::GRAPHICS))
824bf02b6e55atagen2026-10-16 09:15 UTC369
        else {
824bf02b6e55atagen2026-10-16 09:15 UTC370
            continue;
824bf02b6e55atagen2026-10-16 09:15 UTC371
        };
824bf02b6e55atagen2026-10-16 09:15 UTC372
        let rank = match props.device_type {
824bf02b6e55atagen2026-10-16 09:15 UTC373
            vk::PhysicalDeviceType::DISCRETE_GPU => 0,
824bf02b6e55atagen2026-10-16 09:15 UTC374
            vk::PhysicalDeviceType::INTEGRATED_GPU => 1,
824bf02b6e55atagen2026-10-16 09:15 UTC375
            vk::PhysicalDeviceType::VIRTUAL_GPU => 2,
824bf02b6e55atagen2026-10-16 09:15 UTC376
            _ => 3,
824bf02b6e55atagen2026-10-16 09:15 UTC377
        };
824bf02b6e55atagen2026-10-16 09:15 UTC378
        candidates.push((rank, physical, family as u32, name));
824bf02b6e55atagen2026-10-16 09:15 UTC379
    }
824bf02b6e55atagen2026-10-16 09:15 UTC380
    candidates.sort_by_key(|c| c.0);
824bf02b6e55atagen2026-10-16 09:15 UTC381
    candidates.into_iter().next().map(|(_, p, f, n)| (p, f, n))
824bf02b6e55atagen2026-10-16 09:15 UTC382
}