824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 1 | use crate::Error; |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 2 | use ash::vk; |
cacda79c0faa | atagen | 2026-10-19 06:11 UTC | 3 | use gpu_allocator::AllocationSizes; |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 4 | use gpu_allocator::vulkan::{Allocator, AllocatorCreateDesc}; |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 5 | use std::sync::Mutex; |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 6 | |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 7 | /// Everything that must outlive every Vulkan object the renderer hands out. |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 8 | /// Fields drop in declaration order, so the allocator frees its memory before |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 9 | /// the device is destroyed. |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 10 | pub(crate) struct Shared { |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 11 | pub allocator: Mutex<Allocator>, |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 12 | pub device: Device, |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 13 | } |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 14 | |
7de49c2aa70b | atagen | 2026-10-16 09:27 UTC | 15 | /// Loaders for the extensions that dmabuf import, export and fence export |
7de49c2aa70b | atagen | 2026-10-16 09:27 UTC | 16 | /// need. Absent when the device lacks any of them. |
7de49c2aa70b | atagen | 2026-10-16 09:27 UTC | 17 | pub(crate) struct DmabufExt { |
7de49c2aa70b | atagen | 2026-10-16 09:27 UTC | 18 | pub memory_fd: ash::khr::external_memory_fd::Device, |
7de49c2aa70b | atagen | 2026-10-16 09:27 UTC | 19 | pub fence_fd: ash::khr::external_fence_fd::Device, |
7de49c2aa70b | atagen | 2026-10-16 09:27 UTC | 20 | pub modifier: ash::ext::image_drm_format_modifier::Device, |
7de49c2aa70b | atagen | 2026-10-16 09:27 UTC | 21 | } |
7de49c2aa70b | atagen | 2026-10-16 09:27 UTC | 22 | |
7de49c2aa70b | atagen | 2026-10-16 09:27 UTC | 23 | const DMABUF_EXTENSIONS: [&std::ffi::CStr; 5] = [ |
7de49c2aa70b | atagen | 2026-10-16 09:27 UTC | 24 | ash::khr::external_memory_fd::NAME, |
7de49c2aa70b | atagen | 2026-10-16 09:27 UTC | 25 | ash::ext::external_memory_dma_buf::NAME, |
7de49c2aa70b | atagen | 2026-10-16 09:27 UTC | 26 | ash::ext::image_drm_format_modifier::NAME, |
7de49c2aa70b | atagen | 2026-10-16 09:27 UTC | 27 | ash::ext::queue_family_foreign::NAME, |
7de49c2aa70b | atagen | 2026-10-16 09:27 UTC | 28 | ash::khr::external_fence_fd::NAME, |
7de49c2aa70b | atagen | 2026-10-16 09:27 UTC | 29 | ]; |
7de49c2aa70b | atagen | 2026-10-16 09:27 UTC | 30 | |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 31 | pub(crate) struct Device { |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 32 | pub _entry: ash::Entry, |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 33 | pub instance: ash::Instance, |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 34 | pub physical: vk::PhysicalDevice, |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 35 | pub device: ash::Device, |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 36 | pub queue: Mutex<vk::Queue>, |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 37 | pub queue_family: u32, |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 38 | pub push_descriptor: ash::khr::push_descriptor::Device, |
7de49c2aa70b | atagen | 2026-10-16 09:27 UTC | 39 | pub dmabuf: Option<DmabufExt>, |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 40 | pub name: String, |
2454f8b9122d | atagen | 2026-10-19 06:05 UTC | 41 | pub drm_node: Option<u64>, |
d5eca1c82213 | atagen | 2026-10-16 13:56 UTC | 42 | /// Signalled fences kept for reuse, see `FenceInner`. |
d5eca1c82213 | atagen | 2026-10-16 13:56 UTC | 43 | pub spare_fences: Mutex<Vec<vk::Fence>>, |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 44 | } |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 45 | |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 46 | impl Drop for Device { |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 47 | fn drop(&mut self) { |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 48 | unsafe { |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 49 | let _ = self.device.device_wait_idle(); |
d5eca1c82213 | atagen | 2026-10-16 13:56 UTC | 50 | for f in self |
d5eca1c82213 | atagen | 2026-10-16 13:56 UTC | 51 | .spare_fences |
d5eca1c82213 | atagen | 2026-10-16 13:56 UTC | 52 | .get_mut() |
d5eca1c82213 | atagen | 2026-10-16 13:56 UTC | 53 | .unwrap_or_else(|e| e.into_inner()) |
d5eca1c82213 | atagen | 2026-10-16 13:56 UTC | 54 | .drain(..) |
d5eca1c82213 | atagen | 2026-10-16 13:56 UTC | 55 | { |
d5eca1c82213 | atagen | 2026-10-16 13:56 UTC | 56 | self.device.destroy_fence(f, None); |
d5eca1c82213 | atagen | 2026-10-16 13:56 UTC | 57 | } |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 58 | self.device.destroy_device(None); |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 59 | self.instance.destroy_instance(None); |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 60 | } |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 61 | } |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 62 | } |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 63 | |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 64 | impl Shared { |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 65 | /// Opens the first suitable device, or the one whose name contains |
f27fa4c2ade9 | atagen | 2026-10-16 12:59 UTC | 66 | /// `MONOLITH_VK_DEVICE` when that is set. With `node`, only the device |
f27fa4c2ade9 | atagen | 2026-10-16 12:59 UTC | 67 | /// behind that DRM device number qualifies. |
f27fa4c2ade9 | atagen | 2026-10-16 12:59 UTC | 68 | pub fn new(node: Option<u64>) -> Result<Self, Error> { |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 69 | let entry = unsafe { ash::Entry::load()? }; |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 70 | let app = vk::ApplicationInfo::default() |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 71 | .application_name(c"monolith") |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 72 | .engine_name(c"monolith") |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 73 | .api_version(vk::API_VERSION_1_3); |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 74 | let wanted = std::env::var("MONOLITH_VK_DEVICE").ok(); |
b70469d195a1 | atagen | 2026-10-19 06:23 UTC | 75 | let mut env = node.map(driver_env).unwrap_or_default(); |
6118d58e7ed3 | atagen | 2026-10-19 06:08 UTC | 76 | let (instance, physical, queue_family, name) = loop { |
b70469d195a1 | atagen | 2026-10-19 06:23 UTC | 77 | for (var, value) in &env { |
6118d58e7ed3 | atagen | 2026-10-19 06:08 UTC | 78 | // SAFETY: the compositor creates its renderer before starting any |
6118d58e7ed3 | atagen | 2026-10-19 06:08 UTC | 79 | // other thread, so nothing reads the environment concurrently. |
b70469d195a1 | atagen | 2026-10-19 06:23 UTC | 80 | unsafe { std::env::set_var(var, value) }; |
6118d58e7ed3 | atagen | 2026-10-19 06:08 UTC | 81 | } |
6118d58e7ed3 | atagen | 2026-10-19 06:08 UTC | 82 | let instance = unsafe { |
6118d58e7ed3 | atagen | 2026-10-19 06:08 UTC | 83 | entry.create_instance( |
6118d58e7ed3 | atagen | 2026-10-19 06:08 UTC | 84 | &vk::InstanceCreateInfo::default().application_info(&app), |
6118d58e7ed3 | atagen | 2026-10-19 06:08 UTC | 85 | None, |
6118d58e7ed3 | atagen | 2026-10-19 06:08 UTC | 86 | ) |
6118d58e7ed3 | atagen | 2026-10-19 06:08 UTC | 87 | }; |
b70469d195a1 | atagen | 2026-10-19 06:23 UTC | 88 | for (var, _) in &env { |
b70469d195a1 | atagen | 2026-10-19 06:23 UTC | 89 | // SAFETY: as above. Removing them keeps clients from inheriting them. |
b70469d195a1 | atagen | 2026-10-19 06:23 UTC | 90 | unsafe { std::env::remove_var(var) }; |
6118d58e7ed3 | atagen | 2026-10-19 06:08 UTC | 91 | } |
6118d58e7ed3 | atagen | 2026-10-19 06:08 UTC | 92 | let instance = match instance { |
6118d58e7ed3 | atagen | 2026-10-19 06:08 UTC | 93 | Ok(i) => i, |
b70469d195a1 | atagen | 2026-10-19 06:23 UTC | 94 | Err(_) if !std::mem::take(&mut env).is_empty() => continue, |
6118d58e7ed3 | atagen | 2026-10-19 06:08 UTC | 95 | Err(e) => return Err(e.into()), |
6118d58e7ed3 | atagen | 2026-10-19 06:08 UTC | 96 | }; |
6118d58e7ed3 | atagen | 2026-10-19 06:08 UTC | 97 | match unsafe { pick_physical(&instance, wanted.as_deref(), node) } { |
6118d58e7ed3 | atagen | 2026-10-19 06:08 UTC | 98 | Some((p, f, n)) => break (instance, p, f, n), |
6118d58e7ed3 | atagen | 2026-10-19 06:08 UTC | 99 | None => { |
6118d58e7ed3 | atagen | 2026-10-19 06:08 UTC | 100 | unsafe { instance.destroy_instance(None) }; |
b70469d195a1 | atagen | 2026-10-19 06:23 UTC | 101 | if std::mem::take(&mut env).is_empty() { |
6118d58e7ed3 | atagen | 2026-10-19 06:08 UTC | 102 | return Err(Error::NoSuitableDevice); |
6118d58e7ed3 | atagen | 2026-10-19 06:08 UTC | 103 | } |
6118d58e7ed3 | atagen | 2026-10-19 06:08 UTC | 104 | } |
6118d58e7ed3 | atagen | 2026-10-19 06:08 UTC | 105 | } |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 106 | }; |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 107 | let priorities = [1.0]; |
7de49c2aa70b | atagen | 2026-10-16 09:27 UTC | 108 | let available: Vec<_> = unsafe { instance.enumerate_device_extension_properties(physical) } |
7de49c2aa70b | atagen | 2026-10-16 09:27 UTC | 109 | .unwrap_or_default() |
7de49c2aa70b | atagen | 2026-10-16 09:27 UTC | 110 | .iter() |
7de49c2aa70b | atagen | 2026-10-16 09:27 UTC | 111 | .filter_map(|e| e.extension_name_as_c_str().ok().map(ToOwned::to_owned)) |
7de49c2aa70b | atagen | 2026-10-16 09:27 UTC | 112 | .collect(); |
2454f8b9122d | atagen | 2026-10-19 06:05 UTC | 113 | let drm_node = available |
2454f8b9122d | atagen | 2026-10-19 06:05 UTC | 114 | .iter() |
2454f8b9122d | atagen | 2026-10-19 06:05 UTC | 115 | .any(|a| a.as_c_str() == ash::ext::physical_device_drm::NAME) |
2454f8b9122d | atagen | 2026-10-19 06:05 UTC | 116 | .then(|| unsafe { drm_node(&instance, physical) }) |
2454f8b9122d | atagen | 2026-10-19 06:05 UTC | 117 | .flatten(); |
7de49c2aa70b | atagen | 2026-10-16 09:27 UTC | 118 | let has_dmabuf = DMABUF_EXTENSIONS |
7de49c2aa70b | atagen | 2026-10-16 09:27 UTC | 119 | .iter() |
7de49c2aa70b | atagen | 2026-10-16 09:27 UTC | 120 | .all(|n| available.iter().any(|a| a.as_c_str() == *n)); |
7de49c2aa70b | atagen | 2026-10-16 09:27 UTC | 121 | let mut extensions = vec![ash::khr::push_descriptor::NAME.as_ptr()]; |
7de49c2aa70b | atagen | 2026-10-16 09:27 UTC | 122 | if has_dmabuf { |
7de49c2aa70b | atagen | 2026-10-16 09:27 UTC | 123 | extensions.extend(DMABUF_EXTENSIONS.iter().map(|n| n.as_ptr())); |
7de49c2aa70b | atagen | 2026-10-16 09:27 UTC | 124 | } |
796c963a4b55 | atagen | 2026-10-16 22:46 UTC | 125 | let global_priority = [ |
796c963a4b55 | atagen | 2026-10-16 22:46 UTC | 126 | ash::khr::global_priority::NAME, |
796c963a4b55 | atagen | 2026-10-16 22:46 UTC | 127 | ash::ext::global_priority::NAME, |
796c963a4b55 | atagen | 2026-10-16 22:46 UTC | 128 | ] |
796c963a4b55 | atagen | 2026-10-16 22:46 UTC | 129 | .into_iter() |
796c963a4b55 | atagen | 2026-10-16 22:46 UTC | 130 | .find(|n| available.iter().any(|a| a.as_c_str() == *n)); |
796c963a4b55 | atagen | 2026-10-16 22:46 UTC | 131 | let mut priorities_to_try = vec![None]; |
796c963a4b55 | atagen | 2026-10-16 22:46 UTC | 132 | if let Some(name) = global_priority { |
796c963a4b55 | atagen | 2026-10-16 22:46 UTC | 133 | extensions.push(name.as_ptr()); |
796c963a4b55 | atagen | 2026-10-16 22:46 UTC | 134 | priorities_to_try = vec![ |
796c963a4b55 | atagen | 2026-10-16 22:46 UTC | 135 | Some(vk::QueueGlobalPriorityKHR::REALTIME), |
796c963a4b55 | atagen | 2026-10-16 22:46 UTC | 136 | Some(vk::QueueGlobalPriorityKHR::HIGH), |
796c963a4b55 | atagen | 2026-10-16 22:46 UTC | 137 | None, |
796c963a4b55 | atagen | 2026-10-16 22:46 UTC | 138 | ]; |
796c963a4b55 | atagen | 2026-10-16 22:46 UTC | 139 | } |
796c963a4b55 | atagen | 2026-10-16 22:46 UTC | 140 | let mut created = Err(vk::Result::ERROR_INITIALIZATION_FAILED); |
796c963a4b55 | atagen | 2026-10-16 22:46 UTC | 141 | for priority in priorities_to_try { |
796c963a4b55 | atagen | 2026-10-16 22:46 UTC | 142 | let mut features13 = vk::PhysicalDeviceVulkan13Features::default() |
796c963a4b55 | atagen | 2026-10-16 22:46 UTC | 143 | .dynamic_rendering(true) |
796c963a4b55 | atagen | 2026-10-16 22:46 UTC | 144 | .synchronization2(true); |
796c963a4b55 | atagen | 2026-10-16 22:46 UTC | 145 | let mut global = vk::DeviceQueueGlobalPriorityCreateInfoKHR::default() |
796c963a4b55 | atagen | 2026-10-16 22:46 UTC | 146 | .global_priority(priority.unwrap_or(vk::QueueGlobalPriorityKHR::MEDIUM)); |
796c963a4b55 | atagen | 2026-10-16 22:46 UTC | 147 | let mut queue = vk::DeviceQueueCreateInfo::default() |
796c963a4b55 | atagen | 2026-10-16 22:46 UTC | 148 | .queue_family_index(queue_family) |
796c963a4b55 | atagen | 2026-10-16 22:46 UTC | 149 | .queue_priorities(&priorities); |
796c963a4b55 | atagen | 2026-10-16 22:46 UTC | 150 | if priority.is_some() { |
796c963a4b55 | atagen | 2026-10-16 22:46 UTC | 151 | queue = queue.push_next(&mut global); |
796c963a4b55 | atagen | 2026-10-16 22:46 UTC | 152 | } |
796c963a4b55 | atagen | 2026-10-16 22:46 UTC | 153 | let queues = [queue]; |
796c963a4b55 | atagen | 2026-10-16 22:46 UTC | 154 | let info = vk::DeviceCreateInfo::default() |
796c963a4b55 | atagen | 2026-10-16 22:46 UTC | 155 | .queue_create_infos(&queues) |
796c963a4b55 | atagen | 2026-10-16 22:46 UTC | 156 | .enabled_extension_names(&extensions) |
796c963a4b55 | atagen | 2026-10-16 22:46 UTC | 157 | .push_next(&mut features13); |
796c963a4b55 | atagen | 2026-10-16 22:46 UTC | 158 | created = unsafe { instance.create_device(physical, &info, None) }; |
796c963a4b55 | atagen | 2026-10-16 22:46 UTC | 159 | match &created { |
796c963a4b55 | atagen | 2026-10-16 22:46 UTC | 160 | Ok(_) => { |
796c963a4b55 | atagen | 2026-10-16 22:46 UTC | 161 | tracing::info!(?priority, "queue global priority"); |
796c963a4b55 | atagen | 2026-10-16 22:46 UTC | 162 | break; |
796c963a4b55 | atagen | 2026-10-16 22:46 UTC | 163 | } |
796c963a4b55 | atagen | 2026-10-16 22:46 UTC | 164 | Err(e) => tracing::debug!(?priority, "queue global priority refused: {e}"), |
796c963a4b55 | atagen | 2026-10-16 22:46 UTC | 165 | } |
796c963a4b55 | atagen | 2026-10-16 22:46 UTC | 166 | } |
796c963a4b55 | atagen | 2026-10-16 22:46 UTC | 167 | let device = match created { |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 168 | Ok(d) => d, |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 169 | Err(e) => { |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 170 | unsafe { instance.destroy_instance(None) }; |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 171 | return Err(e.into()); |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 172 | } |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 173 | }; |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 174 | let queue = unsafe { device.get_device_queue(queue_family, 0) }; |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 175 | let push_descriptor = ash::khr::push_descriptor::Device::new(&instance, &device); |
7de49c2aa70b | atagen | 2026-10-16 09:27 UTC | 176 | let dmabuf = has_dmabuf.then(|| DmabufExt { |
7de49c2aa70b | atagen | 2026-10-16 09:27 UTC | 177 | memory_fd: ash::khr::external_memory_fd::Device::new(&instance, &device), |
7de49c2aa70b | atagen | 2026-10-16 09:27 UTC | 178 | fence_fd: ash::khr::external_fence_fd::Device::new(&instance, &device), |
7de49c2aa70b | atagen | 2026-10-16 09:27 UTC | 179 | modifier: ash::ext::image_drm_format_modifier::Device::new(&instance, &device), |
7de49c2aa70b | atagen | 2026-10-16 09:27 UTC | 180 | }); |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 181 | let allocator = Allocator::new(&AllocatorCreateDesc { |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 182 | instance: instance.clone(), |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 183 | device: device.clone(), |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 184 | physical_device: physical, |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 185 | debug_settings: Default::default(), |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 186 | buffer_device_address: false, |
cacda79c0faa | atagen | 2026-10-19 06:11 UTC | 187 | // Host blocks are pinned system RAM that stays resident while any |
cacda79c0faa | atagen | 2026-10-19 06:11 UTC | 188 | // allocation lives in them, and the default 64 MB block is mostly idle. |
3521a747467e | atagen | 2026-10-19 07:20 UTC | 189 | allocation_sizes: AllocationSizes::new(64 << 20, 4 << 20), |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 190 | }); |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 191 | let device = Device { |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 192 | _entry: entry, |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 193 | instance, |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 194 | physical, |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 195 | device, |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 196 | queue: Mutex::new(queue), |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 197 | queue_family, |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 198 | push_descriptor, |
7de49c2aa70b | atagen | 2026-10-16 09:27 UTC | 199 | dmabuf, |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 200 | name, |
2454f8b9122d | atagen | 2026-10-19 06:05 UTC | 201 | drm_node, |
d5eca1c82213 | atagen | 2026-10-16 13:56 UTC | 202 | spare_fences: Mutex::new(Vec::new()), |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 203 | }; |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 204 | Ok(Self { |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 205 | allocator: Mutex::new(allocator?), |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 206 | device, |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 207 | }) |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 208 | } |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 209 | } |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 210 | |
f27fa4c2ade9 | atagen | 2026-10-16 12:59 UTC | 211 | /// Whether `physical` is the device behind DRM device number `node`, as its |
f27fa4c2ade9 | atagen | 2026-10-16 12:59 UTC | 212 | /// primary or render node. |
f27fa4c2ade9 | atagen | 2026-10-16 12:59 UTC | 213 | unsafe fn serves_node(instance: &ash::Instance, physical: vk::PhysicalDevice, node: u64) -> bool { |
f27fa4c2ade9 | atagen | 2026-10-16 12:59 UTC | 214 | let mut drm = vk::PhysicalDeviceDrmPropertiesEXT::default(); |
f27fa4c2ade9 | atagen | 2026-10-16 12:59 UTC | 215 | let mut props = vk::PhysicalDeviceProperties2::default().push_next(&mut drm); |
f27fa4c2ade9 | atagen | 2026-10-16 12:59 UTC | 216 | unsafe { instance.get_physical_device_properties2(physical, &mut props) }; |
f27fa4c2ade9 | atagen | 2026-10-16 12:59 UTC | 217 | let (major, minor) = ( |
f27fa4c2ade9 | atagen | 2026-10-16 12:59 UTC | 218 | rustix::fs::major(node) as i64, |
f27fa4c2ade9 | atagen | 2026-10-16 12:59 UTC | 219 | rustix::fs::minor(node) as i64, |
f27fa4c2ade9 | atagen | 2026-10-16 12:59 UTC | 220 | ); |
f27fa4c2ade9 | atagen | 2026-10-16 12:59 UTC | 221 | (drm.has_primary == vk::TRUE && (drm.primary_major, drm.primary_minor) == (major, minor)) |
f27fa4c2ade9 | atagen | 2026-10-16 12:59 UTC | 222 | || (drm.has_render == vk::TRUE && (drm.render_major, drm.render_minor) == (major, minor)) |
f27fa4c2ade9 | atagen | 2026-10-16 12:59 UTC | 223 | } |
f27fa4c2ade9 | atagen | 2026-10-16 12:59 UTC | 224 | |
b70469d195a1 | atagen | 2026-10-19 06:23 UTC | 225 | /// Variables set only while the instance is created, unless the user already |
b70469d195a1 | atagen | 2026-10-19 06:23 UTC | 226 | /// picks drivers themselves. The loader keeps every ICD it finds loaded for the |
b70469d195a1 | atagen | 2026-10-19 06:23 UTC | 227 | /// instance's lifetime, on NixOS including lavapipe and 26 MB of resident |
b70469d195a1 | atagen | 2026-10-19 06:23 UTC | 228 | /// libLLVM. Without an X server NVIDIA's ICD also initialises through glvnd's |
b70469d195a1 | atagen | 2026-10-19 06:23 UTC | 229 | /// libEGL, which loads every EGL vendor, and Mesa's pulls in LLVM again. |
b70469d195a1 | atagen | 2026-10-19 06:23 UTC | 230 | fn driver_env(node: u64) -> Vec<(&'static str, std::ffi::OsString)> { |
b70469d195a1 | atagen | 2026-10-19 06:23 UTC | 231 | let set = |vars: &[&str]| vars.iter().any(|v| std::env::var_os(v).is_some()); |
6118d58e7ed3 | atagen | 2026-10-19 06:08 UTC | 232 | let (major, minor) = (rustix::fs::major(node), rustix::fs::minor(node)); |
b70469d195a1 | atagen | 2026-10-19 06:23 UTC | 233 | let Ok(driver) = std::fs::read_link(format!("/sys/dev/char/{major}:{minor}/device/driver")) |
b70469d195a1 | atagen | 2026-10-19 06:23 UTC | 234 | else { |
b70469d195a1 | atagen | 2026-10-19 06:23 UTC | 235 | return Vec::new(); |
b70469d195a1 | atagen | 2026-10-19 06:23 UTC | 236 | }; |
b70469d195a1 | atagen | 2026-10-19 06:23 UTC | 237 | let driver = driver.file_name().and_then(|d| d.to_str()).unwrap_or_default(); |
b70469d195a1 | atagen | 2026-10-19 06:23 UTC | 238 | let mut env = Vec::new(); |
b70469d195a1 | atagen | 2026-10-19 06:23 UTC | 239 | let glob = match driver { |
6118d58e7ed3 | atagen | 2026-10-19 06:08 UTC | 240 | "nvidia" => Some("*nvidia*"), |
6118d58e7ed3 | atagen | 2026-10-19 06:08 UTC | 241 | "amdgpu" | "radeon" => Some("*radeon*"), |
6118d58e7ed3 | atagen | 2026-10-19 06:08 UTC | 242 | "i915" | "xe" => Some("*intel*"), |
6118d58e7ed3 | atagen | 2026-10-19 06:08 UTC | 243 | "nouveau" => Some("*nouveau*"), |
6118d58e7ed3 | atagen | 2026-10-19 06:08 UTC | 244 | _ => None, |
b70469d195a1 | atagen | 2026-10-19 06:23 UTC | 245 | }; |
b70469d195a1 | atagen | 2026-10-19 06:23 UTC | 246 | let loader = [ |
b70469d195a1 | atagen | 2026-10-19 06:23 UTC | 247 | "VK_LOADER_DRIVERS_SELECT", |
b70469d195a1 | atagen | 2026-10-19 06:23 UTC | 248 | "VK_LOADER_DRIVERS_DISABLE", |
b70469d195a1 | atagen | 2026-10-19 06:23 UTC | 249 | "VK_DRIVER_FILES", |
b70469d195a1 | atagen | 2026-10-19 06:23 UTC | 250 | "VK_ICD_FILENAMES", |
b70469d195a1 | atagen | 2026-10-19 06:23 UTC | 251 | "VK_ADD_DRIVER_FILES", |
b70469d195a1 | atagen | 2026-10-19 06:23 UTC | 252 | ]; |
b70469d195a1 | atagen | 2026-10-19 06:23 UTC | 253 | if let Some(glob) = glob |
b70469d195a1 | atagen | 2026-10-19 06:23 UTC | 254 | && !set(&loader) |
b70469d195a1 | atagen | 2026-10-19 06:23 UTC | 255 | { |
b70469d195a1 | atagen | 2026-10-19 06:23 UTC | 256 | env.push(("VK_LOADER_DRIVERS_SELECT", glob.into())); |
6118d58e7ed3 | atagen | 2026-10-19 06:08 UTC | 257 | } |
b70469d195a1 | atagen | 2026-10-19 06:23 UTC | 258 | let glvnd = ["__EGL_VENDOR_LIBRARY_FILENAMES", "__EGL_VENDOR_LIBRARY_DIRS"]; |
b70469d195a1 | atagen | 2026-10-19 06:23 UTC | 259 | if driver == "nvidia" |
b70469d195a1 | atagen | 2026-10-19 06:23 UTC | 260 | && !set(&glvnd) |
b70469d195a1 | atagen | 2026-10-19 06:23 UTC | 261 | && let Some(vendor) = nvidia_egl_vendor() |
b70469d195a1 | atagen | 2026-10-19 06:23 UTC | 262 | { |
b70469d195a1 | atagen | 2026-10-19 06:23 UTC | 263 | env.push(("__EGL_VENDOR_LIBRARY_FILENAMES", vendor.into())); |
b70469d195a1 | atagen | 2026-10-19 06:23 UTC | 264 | } |
b70469d195a1 | atagen | 2026-10-19 06:23 UTC | 265 | env |
b70469d195a1 | atagen | 2026-10-19 06:23 UTC | 266 | } |
b70469d195a1 | atagen | 2026-10-19 06:23 UTC | 267 | |
b70469d195a1 | atagen | 2026-10-19 06:23 UTC | 268 | /// NVIDIA's glvnd EGL vendor file, from glvnd's default search path. |
b70469d195a1 | atagen | 2026-10-19 06:23 UTC | 269 | fn nvidia_egl_vendor() -> Option<std::path::PathBuf> { |
b70469d195a1 | atagen | 2026-10-19 06:23 UTC | 270 | [ |
b70469d195a1 | atagen | 2026-10-19 06:23 UTC | 271 | "/run/opengl-driver/share/glvnd/egl_vendor.d", |
b70469d195a1 | atagen | 2026-10-19 06:23 UTC | 272 | "/etc/glvnd/egl_vendor.d", |
b70469d195a1 | atagen | 2026-10-19 06:23 UTC | 273 | "/usr/share/glvnd/egl_vendor.d", |
b70469d195a1 | atagen | 2026-10-19 06:23 UTC | 274 | ] |
b70469d195a1 | atagen | 2026-10-19 06:23 UTC | 275 | .into_iter() |
b70469d195a1 | atagen | 2026-10-19 06:23 UTC | 276 | .filter_map(|dir| std::fs::read_dir(dir).ok()) |
b70469d195a1 | atagen | 2026-10-19 06:23 UTC | 277 | .flatten() |
b70469d195a1 | atagen | 2026-10-19 06:23 UTC | 278 | .filter_map(Result::ok) |
b70469d195a1 | atagen | 2026-10-19 06:23 UTC | 279 | .map(|e| e.path()) |
b70469d195a1 | atagen | 2026-10-19 06:23 UTC | 280 | .find(|p| { |
b70469d195a1 | atagen | 2026-10-19 06:23 UTC | 281 | p.extension().is_some_and(|e| e == "json") |
b70469d195a1 | atagen | 2026-10-19 06:23 UTC | 282 | && p.file_name() |
b70469d195a1 | atagen | 2026-10-19 06:23 UTC | 283 | .and_then(|n| n.to_str()) |
b70469d195a1 | atagen | 2026-10-19 06:23 UTC | 284 | .is_some_and(|n| n.contains("nvidia")) |
b70469d195a1 | atagen | 2026-10-19 06:23 UTC | 285 | }) |
6118d58e7ed3 | atagen | 2026-10-19 06:08 UTC | 286 | } |
6118d58e7ed3 | atagen | 2026-10-19 06:08 UTC | 287 | |
2454f8b9122d | atagen | 2026-10-19 06:05 UTC | 288 | /// The render node of `physical`, or its primary node when it has none. |
2454f8b9122d | atagen | 2026-10-19 06:05 UTC | 289 | unsafe fn drm_node(instance: &ash::Instance, physical: vk::PhysicalDevice) -> Option<u64> { |
2454f8b9122d | atagen | 2026-10-19 06:05 UTC | 290 | let mut drm = vk::PhysicalDeviceDrmPropertiesEXT::default(); |
2454f8b9122d | atagen | 2026-10-19 06:05 UTC | 291 | let mut props = vk::PhysicalDeviceProperties2::default().push_next(&mut drm); |
2454f8b9122d | atagen | 2026-10-19 06:05 UTC | 292 | unsafe { instance.get_physical_device_properties2(physical, &mut props) }; |
2454f8b9122d | atagen | 2026-10-19 06:05 UTC | 293 | let node = |major: i64, minor: i64| rustix::fs::makedev(major as u32, minor as u32); |
2454f8b9122d | atagen | 2026-10-19 06:05 UTC | 294 | if drm.has_render == vk::TRUE { |
2454f8b9122d | atagen | 2026-10-19 06:05 UTC | 295 | Some(node(drm.render_major, drm.render_minor)) |
2454f8b9122d | atagen | 2026-10-19 06:05 UTC | 296 | } else if drm.has_primary == vk::TRUE { |
2454f8b9122d | atagen | 2026-10-19 06:05 UTC | 297 | Some(node(drm.primary_major, drm.primary_minor)) |
2454f8b9122d | atagen | 2026-10-19 06:05 UTC | 298 | } else { |
2454f8b9122d | atagen | 2026-10-19 06:05 UTC | 299 | None |
2454f8b9122d | atagen | 2026-10-19 06:05 UTC | 300 | } |
2454f8b9122d | atagen | 2026-10-19 06:05 UTC | 301 | } |
2454f8b9122d | atagen | 2026-10-19 06:05 UTC | 302 | |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 303 | unsafe fn pick_physical( |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 304 | instance: &ash::Instance, |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 305 | wanted: Option<&str>, |
f27fa4c2ade9 | atagen | 2026-10-16 12:59 UTC | 306 | node: Option<u64>, |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 307 | ) -> Option<(vk::PhysicalDevice, u32, String)> { |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 308 | let devices = unsafe { instance.enumerate_physical_devices() }.ok()?; |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 309 | let mut candidates = Vec::new(); |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 310 | for physical in devices { |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 311 | let props = unsafe { instance.get_physical_device_properties(physical) }; |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 312 | if props.api_version < vk::API_VERSION_1_3 { |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 313 | continue; |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 314 | } |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 315 | let name = props |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 316 | .device_name_as_c_str() |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 317 | .unwrap_or(c"unknown") |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 318 | .to_string_lossy() |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 319 | .into_owned(); |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 320 | if wanted.is_some_and(|w| !name.contains(w)) { |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 321 | continue; |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 322 | } |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 323 | let mut f13 = vk::PhysicalDeviceVulkan13Features::default(); |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 324 | let mut f2 = vk::PhysicalDeviceFeatures2::default().push_next(&mut f13); |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 325 | unsafe { instance.get_physical_device_features2(physical, &mut f2) }; |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 326 | if f13.dynamic_rendering == vk::FALSE || f13.synchronization2 == vk::FALSE { |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 327 | continue; |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 328 | } |
f27fa4c2ade9 | atagen | 2026-10-16 12:59 UTC | 329 | let extensions = |
f27fa4c2ade9 | atagen | 2026-10-16 12:59 UTC | 330 | unsafe { instance.enumerate_device_extension_properties(physical) }.unwrap_or_default(); |
f27fa4c2ade9 | atagen | 2026-10-16 12:59 UTC | 331 | let has = |n: &std::ffi::CStr| { |
f27fa4c2ade9 | atagen | 2026-10-16 12:59 UTC | 332 | extensions |
f27fa4c2ade9 | atagen | 2026-10-16 12:59 UTC | 333 | .iter() |
f27fa4c2ade9 | atagen | 2026-10-16 12:59 UTC | 334 | .any(|e| e.extension_name_as_c_str().ok() == Some(n)) |
f27fa4c2ade9 | atagen | 2026-10-16 12:59 UTC | 335 | }; |
f27fa4c2ade9 | atagen | 2026-10-16 12:59 UTC | 336 | if !has(ash::khr::push_descriptor::NAME) { |
f27fa4c2ade9 | atagen | 2026-10-16 12:59 UTC | 337 | continue; |
f27fa4c2ade9 | atagen | 2026-10-16 12:59 UTC | 338 | } |
f27fa4c2ade9 | atagen | 2026-10-16 12:59 UTC | 339 | if let Some(node) = node |
f27fa4c2ade9 | atagen | 2026-10-16 12:59 UTC | 340 | && !(has(ash::ext::physical_device_drm::NAME) |
f27fa4c2ade9 | atagen | 2026-10-16 12:59 UTC | 341 | && unsafe { serves_node(instance, physical, node) }) |
f27fa4c2ade9 | atagen | 2026-10-16 12:59 UTC | 342 | { |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 343 | continue; |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 344 | } |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 345 | let families = unsafe { instance.get_physical_device_queue_family_properties(physical) }; |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 346 | let Some(family) = families |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 347 | .iter() |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 348 | .position(|f| f.queue_flags.contains(vk::QueueFlags::GRAPHICS)) |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 349 | else { |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 350 | continue; |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 351 | }; |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 352 | let rank = match props.device_type { |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 353 | vk::PhysicalDeviceType::DISCRETE_GPU => 0, |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 354 | vk::PhysicalDeviceType::INTEGRATED_GPU => 1, |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 355 | vk::PhysicalDeviceType::VIRTUAL_GPU => 2, |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 356 | _ => 3, |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 357 | }; |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 358 | candidates.push((rank, physical, family as u32, name)); |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 359 | } |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 360 | candidates.sort_by_key(|c| c.0); |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 361 | candidates.into_iter().next().map(|(_, p, f, n)| (p, f, n)) |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 362 | } |