824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 1 | use crate::Error; |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 2 | use ash::vk; |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 3 | use gpu_allocator::vulkan::{Allocator, AllocatorCreateDesc}; |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 4 | use std::sync::Mutex; |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 5 | |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 6 | /// Everything that must outlive every Vulkan object the renderer hands out. |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 7 | /// Fields drop in declaration order, so the allocator frees its memory before |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 8 | /// the device is destroyed. |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 9 | pub(crate) struct Shared { |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 10 | pub allocator: Mutex<Allocator>, |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 11 | pub device: Device, |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 12 | } |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 13 | |
7de49c2aa70b | atagen | 2026-10-16 09:27 UTC | 14 | /// Loaders for the extensions that dmabuf import, export and fence export |
7de49c2aa70b | atagen | 2026-10-16 09:27 UTC | 15 | /// need. Absent when the device lacks any of them. |
7de49c2aa70b | atagen | 2026-10-16 09:27 UTC | 16 | pub(crate) struct DmabufExt { |
7de49c2aa70b | atagen | 2026-10-16 09:27 UTC | 17 | pub memory_fd: ash::khr::external_memory_fd::Device, |
7de49c2aa70b | atagen | 2026-10-16 09:27 UTC | 18 | pub fence_fd: ash::khr::external_fence_fd::Device, |
7de49c2aa70b | atagen | 2026-10-16 09:27 UTC | 19 | pub modifier: ash::ext::image_drm_format_modifier::Device, |
7de49c2aa70b | atagen | 2026-10-16 09:27 UTC | 20 | } |
7de49c2aa70b | atagen | 2026-10-16 09:27 UTC | 21 | |
7de49c2aa70b | atagen | 2026-10-16 09:27 UTC | 22 | const DMABUF_EXTENSIONS: [&std::ffi::CStr; 5] = [ |
7de49c2aa70b | atagen | 2026-10-16 09:27 UTC | 23 | ash::khr::external_memory_fd::NAME, |
7de49c2aa70b | atagen | 2026-10-16 09:27 UTC | 24 | ash::ext::external_memory_dma_buf::NAME, |
7de49c2aa70b | atagen | 2026-10-16 09:27 UTC | 25 | ash::ext::image_drm_format_modifier::NAME, |
7de49c2aa70b | atagen | 2026-10-16 09:27 UTC | 26 | ash::ext::queue_family_foreign::NAME, |
7de49c2aa70b | atagen | 2026-10-16 09:27 UTC | 27 | ash::khr::external_fence_fd::NAME, |
7de49c2aa70b | atagen | 2026-10-16 09:27 UTC | 28 | ]; |
7de49c2aa70b | atagen | 2026-10-16 09:27 UTC | 29 | |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 30 | pub(crate) struct Device { |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 31 | pub _entry: ash::Entry, |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 32 | pub instance: ash::Instance, |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 33 | pub physical: vk::PhysicalDevice, |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 34 | pub device: ash::Device, |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 35 | pub queue: Mutex<vk::Queue>, |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 36 | pub queue_family: u32, |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 37 | pub push_descriptor: ash::khr::push_descriptor::Device, |
7de49c2aa70b | atagen | 2026-10-16 09:27 UTC | 38 | pub dmabuf: Option<DmabufExt>, |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 39 | pub name: String, |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 40 | } |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 41 | |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 42 | impl Drop for Device { |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 43 | fn drop(&mut self) { |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 44 | unsafe { |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 45 | let _ = self.device.device_wait_idle(); |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 46 | self.device.destroy_device(None); |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 47 | self.instance.destroy_instance(None); |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 48 | } |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 49 | } |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 50 | } |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 51 | |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 52 | impl Shared { |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 53 | /// Opens the first suitable device, or the one whose name contains |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 54 | /// `MONOLITH_VK_DEVICE` when that is set. |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 55 | pub fn new() -> Result<Self, Error> { |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 56 | let entry = unsafe { ash::Entry::load()? }; |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 57 | let app = vk::ApplicationInfo::default() |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 58 | .application_name(c"monolith") |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 59 | .engine_name(c"monolith") |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 60 | .api_version(vk::API_VERSION_1_3); |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 61 | let instance = unsafe { |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 62 | entry.create_instance( |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 63 | &vk::InstanceCreateInfo::default().application_info(&app), |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 64 | None, |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 65 | )? |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 66 | }; |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 67 | let wanted = std::env::var("MONOLITH_VK_DEVICE").ok(); |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 68 | let picked = unsafe { pick_physical(&instance, wanted.as_deref()) }; |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 69 | let Some((physical, queue_family, name)) = picked else { |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 70 | unsafe { instance.destroy_instance(None) }; |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 71 | return Err(Error::NoSuitableDevice); |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 72 | }; |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 73 | let priorities = [1.0]; |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 74 | let queues = [vk::DeviceQueueCreateInfo::default() |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 75 | .queue_family_index(queue_family) |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 76 | .queue_priorities(&priorities)]; |
7de49c2aa70b | atagen | 2026-10-16 09:27 UTC | 77 | let available: Vec<_> = unsafe { instance.enumerate_device_extension_properties(physical) } |
7de49c2aa70b | atagen | 2026-10-16 09:27 UTC | 78 | .unwrap_or_default() |
7de49c2aa70b | atagen | 2026-10-16 09:27 UTC | 79 | .iter() |
7de49c2aa70b | atagen | 2026-10-16 09:27 UTC | 80 | .filter_map(|e| e.extension_name_as_c_str().ok().map(ToOwned::to_owned)) |
7de49c2aa70b | atagen | 2026-10-16 09:27 UTC | 81 | .collect(); |
7de49c2aa70b | atagen | 2026-10-16 09:27 UTC | 82 | let has_dmabuf = DMABUF_EXTENSIONS |
7de49c2aa70b | atagen | 2026-10-16 09:27 UTC | 83 | .iter() |
7de49c2aa70b | atagen | 2026-10-16 09:27 UTC | 84 | .all(|n| available.iter().any(|a| a.as_c_str() == *n)); |
7de49c2aa70b | atagen | 2026-10-16 09:27 UTC | 85 | let mut extensions = vec![ash::khr::push_descriptor::NAME.as_ptr()]; |
7de49c2aa70b | atagen | 2026-10-16 09:27 UTC | 86 | if has_dmabuf { |
7de49c2aa70b | atagen | 2026-10-16 09:27 UTC | 87 | extensions.extend(DMABUF_EXTENSIONS.iter().map(|n| n.as_ptr())); |
7de49c2aa70b | atagen | 2026-10-16 09:27 UTC | 88 | } |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 89 | let mut features13 = vk::PhysicalDeviceVulkan13Features::default() |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 90 | .dynamic_rendering(true) |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 91 | .synchronization2(true); |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 92 | let info = vk::DeviceCreateInfo::default() |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 93 | .queue_create_infos(&queues) |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 94 | .enabled_extension_names(&extensions) |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 95 | .push_next(&mut features13); |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 96 | let device = match unsafe { instance.create_device(physical, &info, None) } { |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 97 | Ok(d) => d, |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 98 | Err(e) => { |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 99 | unsafe { instance.destroy_instance(None) }; |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 100 | return Err(e.into()); |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 101 | } |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 102 | }; |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 103 | let queue = unsafe { device.get_device_queue(queue_family, 0) }; |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 104 | let push_descriptor = ash::khr::push_descriptor::Device::new(&instance, &device); |
7de49c2aa70b | atagen | 2026-10-16 09:27 UTC | 105 | let dmabuf = has_dmabuf.then(|| DmabufExt { |
7de49c2aa70b | atagen | 2026-10-16 09:27 UTC | 106 | memory_fd: ash::khr::external_memory_fd::Device::new(&instance, &device), |
7de49c2aa70b | atagen | 2026-10-16 09:27 UTC | 107 | fence_fd: ash::khr::external_fence_fd::Device::new(&instance, &device), |
7de49c2aa70b | atagen | 2026-10-16 09:27 UTC | 108 | modifier: ash::ext::image_drm_format_modifier::Device::new(&instance, &device), |
7de49c2aa70b | atagen | 2026-10-16 09:27 UTC | 109 | }); |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 110 | let allocator = Allocator::new(&AllocatorCreateDesc { |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 111 | instance: instance.clone(), |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 112 | device: device.clone(), |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 113 | physical_device: physical, |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 114 | debug_settings: Default::default(), |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 115 | buffer_device_address: false, |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 116 | allocation_sizes: Default::default(), |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 117 | }); |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 118 | let device = Device { |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 119 | _entry: entry, |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 120 | instance, |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 121 | physical, |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 122 | device, |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 123 | queue: Mutex::new(queue), |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 124 | queue_family, |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 125 | push_descriptor, |
7de49c2aa70b | atagen | 2026-10-16 09:27 UTC | 126 | dmabuf, |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 127 | name, |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 128 | }; |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 129 | Ok(Self { |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 130 | allocator: Mutex::new(allocator?), |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 131 | device, |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 132 | }) |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 133 | } |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 134 | } |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 135 | |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 136 | unsafe fn pick_physical( |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 137 | instance: &ash::Instance, |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 138 | wanted: Option<&str>, |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 139 | ) -> Option<(vk::PhysicalDevice, u32, String)> { |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 140 | let devices = unsafe { instance.enumerate_physical_devices() }.ok()?; |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 141 | let mut candidates = Vec::new(); |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 142 | for physical in devices { |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 143 | let props = unsafe { instance.get_physical_device_properties(physical) }; |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 144 | if props.api_version < vk::API_VERSION_1_3 { |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 145 | continue; |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 146 | } |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 147 | let name = props |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 148 | .device_name_as_c_str() |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 149 | .unwrap_or(c"unknown") |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 150 | .to_string_lossy() |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 151 | .into_owned(); |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 152 | if wanted.is_some_and(|w| !name.contains(w)) { |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 153 | continue; |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 154 | } |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 155 | let mut f13 = vk::PhysicalDeviceVulkan13Features::default(); |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 156 | let mut f2 = vk::PhysicalDeviceFeatures2::default().push_next(&mut f13); |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 157 | unsafe { instance.get_physical_device_features2(physical, &mut f2) }; |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 158 | if f13.dynamic_rendering == vk::FALSE || f13.synchronization2 == vk::FALSE { |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 159 | continue; |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 160 | } |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 161 | let has_push = unsafe { instance.enumerate_device_extension_properties(physical) } |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 162 | .unwrap_or_default() |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 163 | .iter() |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 164 | .any(|e| e.extension_name_as_c_str().ok() == Some(ash::khr::push_descriptor::NAME)); |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 165 | if !has_push { |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 166 | continue; |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 167 | } |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 168 | let families = unsafe { instance.get_physical_device_queue_family_properties(physical) }; |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 169 | let Some(family) = families |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 170 | .iter() |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 171 | .position(|f| f.queue_flags.contains(vk::QueueFlags::GRAPHICS)) |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 172 | else { |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 173 | continue; |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 174 | }; |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 175 | let rank = match props.device_type { |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 176 | vk::PhysicalDeviceType::DISCRETE_GPU => 0, |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 177 | vk::PhysicalDeviceType::INTEGRATED_GPU => 1, |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 178 | vk::PhysicalDeviceType::VIRTUAL_GPU => 2, |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 179 | _ => 3, |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 180 | }; |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 181 | candidates.push((rank, physical, family as u32, name)); |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 182 | } |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 183 | candidates.sort_by_key(|c| c.0); |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 184 | candidates.into_iter().next().map(|(_, p, f, n)| (p, f, n)) |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 185 | } |