atagen

monolith

public
Star0
Watch0
e1b42c2ebb09230a2f535fe9c84a25b86136729e
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;
824bf02b6e55atagen2026-10-16 09:15 UTC3
use gpu_allocator::vulkan::{Allocator, AllocatorCreateDesc};
824bf02b6e55atagen2026-10-16 09:15 UTC4
use std::sync::Mutex;
824bf02b6e55atagen2026-10-16 09:15 UTC5
824bf02b6e55atagen2026-10-16 09:15 UTC6
/// Everything that must outlive every Vulkan object the renderer hands out.
824bf02b6e55atagen2026-10-16 09:15 UTC7
/// Fields drop in declaration order, so the allocator frees its memory before
824bf02b6e55atagen2026-10-16 09:15 UTC8
/// the device is destroyed.
824bf02b6e55atagen2026-10-16 09:15 UTC9
pub(crate) struct Shared {
04f0de436841atagen2026-10-17 04:23 UTC10
    /// Staging buffers kept for reuse, see `Staging`.
04f0de436841atagen2026-10-17 04:23 UTC11
    pub staging_pool: Mutex<Vec<crate::image::Pooled>>,
824bf02b6e55atagen2026-10-16 09:15 UTC12
    pub allocator: Mutex<Allocator>,
824bf02b6e55atagen2026-10-16 09:15 UTC13
    pub device: Device,
824bf02b6e55atagen2026-10-16 09:15 UTC14
}
824bf02b6e55atagen2026-10-16 09:15 UTC15
04f0de436841atagen2026-10-17 04:23 UTC16
impl Drop for Shared {
04f0de436841atagen2026-10-17 04:23 UTC17
    fn drop(&mut self) {
04f0de436841atagen2026-10-17 04:23 UTC18
        let pool = std::mem::take(
04f0de436841atagen2026-10-17 04:23 UTC19
            self.staging_pool
04f0de436841atagen2026-10-17 04:23 UTC20
                .get_mut()
04f0de436841atagen2026-10-17 04:23 UTC21
                .unwrap_or_else(|e| e.into_inner()),
04f0de436841atagen2026-10-17 04:23 UTC22
        );
04f0de436841atagen2026-10-17 04:23 UTC23
        let allocator = self.allocator.get_mut().unwrap_or_else(|e| e.into_inner());
04f0de436841atagen2026-10-17 04:23 UTC24
        for p in pool {
04f0de436841atagen2026-10-17 04:23 UTC25
            unsafe { self.device.device.destroy_buffer(p.buffer, None) };
04f0de436841atagen2026-10-17 04:23 UTC26
            let _ = allocator.free(p.allocation);
04f0de436841atagen2026-10-17 04:23 UTC27
        }
04f0de436841atagen2026-10-17 04:23 UTC28
    }
04f0de436841atagen2026-10-17 04:23 UTC29
}
04f0de436841atagen2026-10-17 04:23 UTC30
7de49c2aa70batagen2026-10-16 09:27 UTC31
/// Loaders for the extensions that dmabuf import, export and fence export
7de49c2aa70batagen2026-10-16 09:27 UTC32
/// need. Absent when the device lacks any of them.
7de49c2aa70batagen2026-10-16 09:27 UTC33
pub(crate) struct DmabufExt {
7de49c2aa70batagen2026-10-16 09:27 UTC34
    pub memory_fd: ash::khr::external_memory_fd::Device,
7de49c2aa70batagen2026-10-16 09:27 UTC35
    pub fence_fd: ash::khr::external_fence_fd::Device,
7de49c2aa70batagen2026-10-16 09:27 UTC36
    pub modifier: ash::ext::image_drm_format_modifier::Device,
7de49c2aa70batagen2026-10-16 09:27 UTC37
}
7de49c2aa70batagen2026-10-16 09:27 UTC38
7de49c2aa70batagen2026-10-16 09:27 UTC39
const DMABUF_EXTENSIONS: [&std::ffi::CStr; 5] = [
7de49c2aa70batagen2026-10-16 09:27 UTC40
    ash::khr::external_memory_fd::NAME,
7de49c2aa70batagen2026-10-16 09:27 UTC41
    ash::ext::external_memory_dma_buf::NAME,
7de49c2aa70batagen2026-10-16 09:27 UTC42
    ash::ext::image_drm_format_modifier::NAME,
7de49c2aa70batagen2026-10-16 09:27 UTC43
    ash::ext::queue_family_foreign::NAME,
7de49c2aa70batagen2026-10-16 09:27 UTC44
    ash::khr::external_fence_fd::NAME,
7de49c2aa70batagen2026-10-16 09:27 UTC45
];
7de49c2aa70batagen2026-10-16 09:27 UTC46
824bf02b6e55atagen2026-10-16 09:15 UTC47
pub(crate) struct Device {
824bf02b6e55atagen2026-10-16 09:15 UTC48
    pub _entry: ash::Entry,
824bf02b6e55atagen2026-10-16 09:15 UTC49
    pub instance: ash::Instance,
824bf02b6e55atagen2026-10-16 09:15 UTC50
    pub physical: vk::PhysicalDevice,
824bf02b6e55atagen2026-10-16 09:15 UTC51
    pub device: ash::Device,
824bf02b6e55atagen2026-10-16 09:15 UTC52
    pub queue: Mutex<vk::Queue>,
824bf02b6e55atagen2026-10-16 09:15 UTC53
    pub queue_family: u32,
824bf02b6e55atagen2026-10-16 09:15 UTC54
    pub push_descriptor: ash::khr::push_descriptor::Device,
7de49c2aa70batagen2026-10-16 09:27 UTC55
    pub dmabuf: Option<DmabufExt>,
824bf02b6e55atagen2026-10-16 09:15 UTC56
    pub name: String,
d5eca1c82213atagen2026-10-16 13:56 UTC57
    /// Signalled fences kept for reuse, see `FenceInner`.
d5eca1c82213atagen2026-10-16 13:56 UTC58
    pub spare_fences: Mutex<Vec<vk::Fence>>,
824bf02b6e55atagen2026-10-16 09:15 UTC59
}
824bf02b6e55atagen2026-10-16 09:15 UTC60
824bf02b6e55atagen2026-10-16 09:15 UTC61
impl Drop for Device {
824bf02b6e55atagen2026-10-16 09:15 UTC62
    fn drop(&mut self) {
824bf02b6e55atagen2026-10-16 09:15 UTC63
        unsafe {
824bf02b6e55atagen2026-10-16 09:15 UTC64
            let _ = self.device.device_wait_idle();
d5eca1c82213atagen2026-10-16 13:56 UTC65
            for f in self
d5eca1c82213atagen2026-10-16 13:56 UTC66
                .spare_fences
d5eca1c82213atagen2026-10-16 13:56 UTC67
                .get_mut()
d5eca1c82213atagen2026-10-16 13:56 UTC68
                .unwrap_or_else(|e| e.into_inner())
d5eca1c82213atagen2026-10-16 13:56 UTC69
                .drain(..)
d5eca1c82213atagen2026-10-16 13:56 UTC70
            {
d5eca1c82213atagen2026-10-16 13:56 UTC71
                self.device.destroy_fence(f, None);
d5eca1c82213atagen2026-10-16 13:56 UTC72
            }
824bf02b6e55atagen2026-10-16 09:15 UTC73
            self.device.destroy_device(None);
824bf02b6e55atagen2026-10-16 09:15 UTC74
            self.instance.destroy_instance(None);
824bf02b6e55atagen2026-10-16 09:15 UTC75
        }
824bf02b6e55atagen2026-10-16 09:15 UTC76
    }
824bf02b6e55atagen2026-10-16 09:15 UTC77
}
824bf02b6e55atagen2026-10-16 09:15 UTC78
824bf02b6e55atagen2026-10-16 09:15 UTC79
impl Shared {
824bf02b6e55atagen2026-10-16 09:15 UTC80
    /// Opens the first suitable device, or the one whose name contains
f27fa4c2ade9atagen2026-10-16 12:59 UTC81
    /// `MONOLITH_VK_DEVICE` when that is set. With `node`, only the device
f27fa4c2ade9atagen2026-10-16 12:59 UTC82
    /// behind that DRM device number qualifies.
f27fa4c2ade9atagen2026-10-16 12:59 UTC83
    pub fn new(node: Option<u64>) -> Result<Self, Error> {
824bf02b6e55atagen2026-10-16 09:15 UTC84
        let entry = unsafe { ash::Entry::load()? };
824bf02b6e55atagen2026-10-16 09:15 UTC85
        let app = vk::ApplicationInfo::default()
824bf02b6e55atagen2026-10-16 09:15 UTC86
            .application_name(c"monolith")
824bf02b6e55atagen2026-10-16 09:15 UTC87
            .engine_name(c"monolith")
824bf02b6e55atagen2026-10-16 09:15 UTC88
            .api_version(vk::API_VERSION_1_3);
824bf02b6e55atagen2026-10-16 09:15 UTC89
        let instance = unsafe {
824bf02b6e55atagen2026-10-16 09:15 UTC90
            entry.create_instance(
824bf02b6e55atagen2026-10-16 09:15 UTC91
                &vk::InstanceCreateInfo::default().application_info(&app),
824bf02b6e55atagen2026-10-16 09:15 UTC92
                None,
824bf02b6e55atagen2026-10-16 09:15 UTC93
            )?
824bf02b6e55atagen2026-10-16 09:15 UTC94
        };
824bf02b6e55atagen2026-10-16 09:15 UTC95
        let wanted = std::env::var("MONOLITH_VK_DEVICE").ok();
f27fa4c2ade9atagen2026-10-16 12:59 UTC96
        let picked = unsafe { pick_physical(&instance, wanted.as_deref(), node) };
824bf02b6e55atagen2026-10-16 09:15 UTC97
        let Some((physical, queue_family, name)) = picked else {
824bf02b6e55atagen2026-10-16 09:15 UTC98
            unsafe { instance.destroy_instance(None) };
824bf02b6e55atagen2026-10-16 09:15 UTC99
            return Err(Error::NoSuitableDevice);
824bf02b6e55atagen2026-10-16 09:15 UTC100
        };
824bf02b6e55atagen2026-10-16 09:15 UTC101
        let priorities = [1.0];
7de49c2aa70batagen2026-10-16 09:27 UTC102
        let available: Vec<_> = unsafe { instance.enumerate_device_extension_properties(physical) }
7de49c2aa70batagen2026-10-16 09:27 UTC103
            .unwrap_or_default()
7de49c2aa70batagen2026-10-16 09:27 UTC104
            .iter()
7de49c2aa70batagen2026-10-16 09:27 UTC105
            .filter_map(|e| e.extension_name_as_c_str().ok().map(ToOwned::to_owned))
7de49c2aa70batagen2026-10-16 09:27 UTC106
            .collect();
7de49c2aa70batagen2026-10-16 09:27 UTC107
        let has_dmabuf = DMABUF_EXTENSIONS
7de49c2aa70batagen2026-10-16 09:27 UTC108
            .iter()
7de49c2aa70batagen2026-10-16 09:27 UTC109
            .all(|n| available.iter().any(|a| a.as_c_str() == *n));
7de49c2aa70batagen2026-10-16 09:27 UTC110
        let mut extensions = vec![ash::khr::push_descriptor::NAME.as_ptr()];
7de49c2aa70batagen2026-10-16 09:27 UTC111
        if has_dmabuf {
7de49c2aa70batagen2026-10-16 09:27 UTC112
            extensions.extend(DMABUF_EXTENSIONS.iter().map(|n| n.as_ptr()));
7de49c2aa70batagen2026-10-16 09:27 UTC113
        }
796c963a4b55atagen2026-10-16 22:46 UTC114
        let global_priority = [
796c963a4b55atagen2026-10-16 22:46 UTC115
            ash::khr::global_priority::NAME,
796c963a4b55atagen2026-10-16 22:46 UTC116
            ash::ext::global_priority::NAME,
796c963a4b55atagen2026-10-16 22:46 UTC117
        ]
796c963a4b55atagen2026-10-16 22:46 UTC118
        .into_iter()
796c963a4b55atagen2026-10-16 22:46 UTC119
        .find(|n| available.iter().any(|a| a.as_c_str() == *n));
796c963a4b55atagen2026-10-16 22:46 UTC120
        let mut priorities_to_try = vec![None];
796c963a4b55atagen2026-10-16 22:46 UTC121
        if let Some(name) = global_priority {
796c963a4b55atagen2026-10-16 22:46 UTC122
            extensions.push(name.as_ptr());
796c963a4b55atagen2026-10-16 22:46 UTC123
            priorities_to_try = vec![
796c963a4b55atagen2026-10-16 22:46 UTC124
                Some(vk::QueueGlobalPriorityKHR::REALTIME),
796c963a4b55atagen2026-10-16 22:46 UTC125
                Some(vk::QueueGlobalPriorityKHR::HIGH),
796c963a4b55atagen2026-10-16 22:46 UTC126
                None,
796c963a4b55atagen2026-10-16 22:46 UTC127
            ];
796c963a4b55atagen2026-10-16 22:46 UTC128
        }
796c963a4b55atagen2026-10-16 22:46 UTC129
        let mut created = Err(vk::Result::ERROR_INITIALIZATION_FAILED);
796c963a4b55atagen2026-10-16 22:46 UTC130
        for priority in priorities_to_try {
796c963a4b55atagen2026-10-16 22:46 UTC131
            let mut features13 = vk::PhysicalDeviceVulkan13Features::default()
796c963a4b55atagen2026-10-16 22:46 UTC132
                .dynamic_rendering(true)
796c963a4b55atagen2026-10-16 22:46 UTC133
                .synchronization2(true);
796c963a4b55atagen2026-10-16 22:46 UTC134
            let mut global = vk::DeviceQueueGlobalPriorityCreateInfoKHR::default()
796c963a4b55atagen2026-10-16 22:46 UTC135
                .global_priority(priority.unwrap_or(vk::QueueGlobalPriorityKHR::MEDIUM));
796c963a4b55atagen2026-10-16 22:46 UTC136
            let mut queue = vk::DeviceQueueCreateInfo::default()
796c963a4b55atagen2026-10-16 22:46 UTC137
                .queue_family_index(queue_family)
796c963a4b55atagen2026-10-16 22:46 UTC138
                .queue_priorities(&priorities);
796c963a4b55atagen2026-10-16 22:46 UTC139
            if priority.is_some() {
796c963a4b55atagen2026-10-16 22:46 UTC140
                queue = queue.push_next(&mut global);
796c963a4b55atagen2026-10-16 22:46 UTC141
            }
796c963a4b55atagen2026-10-16 22:46 UTC142
            let queues = [queue];
796c963a4b55atagen2026-10-16 22:46 UTC143
            let info = vk::DeviceCreateInfo::default()
796c963a4b55atagen2026-10-16 22:46 UTC144
                .queue_create_infos(&queues)
796c963a4b55atagen2026-10-16 22:46 UTC145
                .enabled_extension_names(&extensions)
796c963a4b55atagen2026-10-16 22:46 UTC146
                .push_next(&mut features13);
796c963a4b55atagen2026-10-16 22:46 UTC147
            created = unsafe { instance.create_device(physical, &info, None) };
796c963a4b55atagen2026-10-16 22:46 UTC148
            match &created {
796c963a4b55atagen2026-10-16 22:46 UTC149
                Ok(_) => {
796c963a4b55atagen2026-10-16 22:46 UTC150
                    tracing::info!(?priority, "queue global priority");
796c963a4b55atagen2026-10-16 22:46 UTC151
                    break;
796c963a4b55atagen2026-10-16 22:46 UTC152
                }
796c963a4b55atagen2026-10-16 22:46 UTC153
                Err(e) => tracing::debug!(?priority, "queue global priority refused: {e}"),
796c963a4b55atagen2026-10-16 22:46 UTC154
            }
796c963a4b55atagen2026-10-16 22:46 UTC155
        }
796c963a4b55atagen2026-10-16 22:46 UTC156
        let device = match created {
824bf02b6e55atagen2026-10-16 09:15 UTC157
            Ok(d) => d,
824bf02b6e55atagen2026-10-16 09:15 UTC158
            Err(e) => {
824bf02b6e55atagen2026-10-16 09:15 UTC159
                unsafe { instance.destroy_instance(None) };
824bf02b6e55atagen2026-10-16 09:15 UTC160
                return Err(e.into());
824bf02b6e55atagen2026-10-16 09:15 UTC161
            }
824bf02b6e55atagen2026-10-16 09:15 UTC162
        };
824bf02b6e55atagen2026-10-16 09:15 UTC163
        let queue = unsafe { device.get_device_queue(queue_family, 0) };
824bf02b6e55atagen2026-10-16 09:15 UTC164
        let push_descriptor = ash::khr::push_descriptor::Device::new(&instance, &device);
7de49c2aa70batagen2026-10-16 09:27 UTC165
        let dmabuf = has_dmabuf.then(|| DmabufExt {
7de49c2aa70batagen2026-10-16 09:27 UTC166
            memory_fd: ash::khr::external_memory_fd::Device::new(&instance, &device),
7de49c2aa70batagen2026-10-16 09:27 UTC167
            fence_fd: ash::khr::external_fence_fd::Device::new(&instance, &device),
7de49c2aa70batagen2026-10-16 09:27 UTC168
            modifier: ash::ext::image_drm_format_modifier::Device::new(&instance, &device),
7de49c2aa70batagen2026-10-16 09:27 UTC169
        });
824bf02b6e55atagen2026-10-16 09:15 UTC170
        let allocator = Allocator::new(&AllocatorCreateDesc {
824bf02b6e55atagen2026-10-16 09:15 UTC171
            instance: instance.clone(),
824bf02b6e55atagen2026-10-16 09:15 UTC172
            device: device.clone(),
824bf02b6e55atagen2026-10-16 09:15 UTC173
            physical_device: physical,
824bf02b6e55atagen2026-10-16 09:15 UTC174
            debug_settings: Default::default(),
824bf02b6e55atagen2026-10-16 09:15 UTC175
            buffer_device_address: false,
824bf02b6e55atagen2026-10-16 09:15 UTC176
            allocation_sizes: Default::default(),
824bf02b6e55atagen2026-10-16 09:15 UTC177
        });
824bf02b6e55atagen2026-10-16 09:15 UTC178
        let device = Device {
824bf02b6e55atagen2026-10-16 09:15 UTC179
            _entry: entry,
824bf02b6e55atagen2026-10-16 09:15 UTC180
            instance,
824bf02b6e55atagen2026-10-16 09:15 UTC181
            physical,
824bf02b6e55atagen2026-10-16 09:15 UTC182
            device,
824bf02b6e55atagen2026-10-16 09:15 UTC183
            queue: Mutex::new(queue),
824bf02b6e55atagen2026-10-16 09:15 UTC184
            queue_family,
824bf02b6e55atagen2026-10-16 09:15 UTC185
            push_descriptor,
7de49c2aa70batagen2026-10-16 09:27 UTC186
            dmabuf,
824bf02b6e55atagen2026-10-16 09:15 UTC187
            name,
d5eca1c82213atagen2026-10-16 13:56 UTC188
            spare_fences: Mutex::new(Vec::new()),
824bf02b6e55atagen2026-10-16 09:15 UTC189
        };
824bf02b6e55atagen2026-10-16 09:15 UTC190
        Ok(Self {
04f0de436841atagen2026-10-17 04:23 UTC191
            staging_pool: Mutex::new(Vec::new()),
824bf02b6e55atagen2026-10-16 09:15 UTC192
            allocator: Mutex::new(allocator?),
824bf02b6e55atagen2026-10-16 09:15 UTC193
            device,
824bf02b6e55atagen2026-10-16 09:15 UTC194
        })
824bf02b6e55atagen2026-10-16 09:15 UTC195
    }
824bf02b6e55atagen2026-10-16 09:15 UTC196
}
824bf02b6e55atagen2026-10-16 09:15 UTC197
f27fa4c2ade9atagen2026-10-16 12:59 UTC198
/// Whether `physical` is the device behind DRM device number `node`, as its
f27fa4c2ade9atagen2026-10-16 12:59 UTC199
/// primary or render node.
f27fa4c2ade9atagen2026-10-16 12:59 UTC200
unsafe fn serves_node(instance: &ash::Instance, physical: vk::PhysicalDevice, node: u64) -> bool {
f27fa4c2ade9atagen2026-10-16 12:59 UTC201
    let mut drm = vk::PhysicalDeviceDrmPropertiesEXT::default();
f27fa4c2ade9atagen2026-10-16 12:59 UTC202
    let mut props = vk::PhysicalDeviceProperties2::default().push_next(&mut drm);
f27fa4c2ade9atagen2026-10-16 12:59 UTC203
    unsafe { instance.get_physical_device_properties2(physical, &mut props) };
f27fa4c2ade9atagen2026-10-16 12:59 UTC204
    let (major, minor) = (
f27fa4c2ade9atagen2026-10-16 12:59 UTC205
        rustix::fs::major(node) as i64,
f27fa4c2ade9atagen2026-10-16 12:59 UTC206
        rustix::fs::minor(node) as i64,
f27fa4c2ade9atagen2026-10-16 12:59 UTC207
    );
f27fa4c2ade9atagen2026-10-16 12:59 UTC208
    (drm.has_primary == vk::TRUE && (drm.primary_major, drm.primary_minor) == (major, minor))
f27fa4c2ade9atagen2026-10-16 12:59 UTC209
        || (drm.has_render == vk::TRUE && (drm.render_major, drm.render_minor) == (major, minor))
f27fa4c2ade9atagen2026-10-16 12:59 UTC210
}
f27fa4c2ade9atagen2026-10-16 12:59 UTC211
824bf02b6e55atagen2026-10-16 09:15 UTC212
unsafe fn pick_physical(
824bf02b6e55atagen2026-10-16 09:15 UTC213
    instance: &ash::Instance,
824bf02b6e55atagen2026-10-16 09:15 UTC214
    wanted: Option<&str>,
f27fa4c2ade9atagen2026-10-16 12:59 UTC215
    node: Option<u64>,
824bf02b6e55atagen2026-10-16 09:15 UTC216
) -> Option<(vk::PhysicalDevice, u32, String)> {
824bf02b6e55atagen2026-10-16 09:15 UTC217
    let devices = unsafe { instance.enumerate_physical_devices() }.ok()?;
824bf02b6e55atagen2026-10-16 09:15 UTC218
    let mut candidates = Vec::new();
824bf02b6e55atagen2026-10-16 09:15 UTC219
    for physical in devices {
824bf02b6e55atagen2026-10-16 09:15 UTC220
        let props = unsafe { instance.get_physical_device_properties(physical) };
824bf02b6e55atagen2026-10-16 09:15 UTC221
        if props.api_version < vk::API_VERSION_1_3 {
824bf02b6e55atagen2026-10-16 09:15 UTC222
            continue;
824bf02b6e55atagen2026-10-16 09:15 UTC223
        }
824bf02b6e55atagen2026-10-16 09:15 UTC224
        let name = props
824bf02b6e55atagen2026-10-16 09:15 UTC225
            .device_name_as_c_str()
824bf02b6e55atagen2026-10-16 09:15 UTC226
            .unwrap_or(c"unknown")
824bf02b6e55atagen2026-10-16 09:15 UTC227
            .to_string_lossy()
824bf02b6e55atagen2026-10-16 09:15 UTC228
            .into_owned();
824bf02b6e55atagen2026-10-16 09:15 UTC229
        if wanted.is_some_and(|w| !name.contains(w)) {
824bf02b6e55atagen2026-10-16 09:15 UTC230
            continue;
824bf02b6e55atagen2026-10-16 09:15 UTC231
        }
824bf02b6e55atagen2026-10-16 09:15 UTC232
        let mut f13 = vk::PhysicalDeviceVulkan13Features::default();
824bf02b6e55atagen2026-10-16 09:15 UTC233
        let mut f2 = vk::PhysicalDeviceFeatures2::default().push_next(&mut f13);
824bf02b6e55atagen2026-10-16 09:15 UTC234
        unsafe { instance.get_physical_device_features2(physical, &mut f2) };
824bf02b6e55atagen2026-10-16 09:15 UTC235
        if f13.dynamic_rendering == vk::FALSE || f13.synchronization2 == vk::FALSE {
824bf02b6e55atagen2026-10-16 09:15 UTC236
            continue;
824bf02b6e55atagen2026-10-16 09:15 UTC237
        }
f27fa4c2ade9atagen2026-10-16 12:59 UTC238
        let extensions =
f27fa4c2ade9atagen2026-10-16 12:59 UTC239
            unsafe { instance.enumerate_device_extension_properties(physical) }.unwrap_or_default();
f27fa4c2ade9atagen2026-10-16 12:59 UTC240
        let has = |n: &std::ffi::CStr| {
f27fa4c2ade9atagen2026-10-16 12:59 UTC241
            extensions
f27fa4c2ade9atagen2026-10-16 12:59 UTC242
                .iter()
f27fa4c2ade9atagen2026-10-16 12:59 UTC243
                .any(|e| e.extension_name_as_c_str().ok() == Some(n))
f27fa4c2ade9atagen2026-10-16 12:59 UTC244
        };
f27fa4c2ade9atagen2026-10-16 12:59 UTC245
        if !has(ash::khr::push_descriptor::NAME) {
f27fa4c2ade9atagen2026-10-16 12:59 UTC246
            continue;
f27fa4c2ade9atagen2026-10-16 12:59 UTC247
        }
f27fa4c2ade9atagen2026-10-16 12:59 UTC248
        if let Some(node) = node
f27fa4c2ade9atagen2026-10-16 12:59 UTC249
            && !(has(ash::ext::physical_device_drm::NAME)
f27fa4c2ade9atagen2026-10-16 12:59 UTC250
                && unsafe { serves_node(instance, physical, node) })
f27fa4c2ade9atagen2026-10-16 12:59 UTC251
        {
824bf02b6e55atagen2026-10-16 09:15 UTC252
            continue;
824bf02b6e55atagen2026-10-16 09:15 UTC253
        }
824bf02b6e55atagen2026-10-16 09:15 UTC254
        let families = unsafe { instance.get_physical_device_queue_family_properties(physical) };
824bf02b6e55atagen2026-10-16 09:15 UTC255
        let Some(family) = families
824bf02b6e55atagen2026-10-16 09:15 UTC256
            .iter()
824bf02b6e55atagen2026-10-16 09:15 UTC257
            .position(|f| f.queue_flags.contains(vk::QueueFlags::GRAPHICS))
824bf02b6e55atagen2026-10-16 09:15 UTC258
        else {
824bf02b6e55atagen2026-10-16 09:15 UTC259
            continue;
824bf02b6e55atagen2026-10-16 09:15 UTC260
        };
824bf02b6e55atagen2026-10-16 09:15 UTC261
        let rank = match props.device_type {
824bf02b6e55atagen2026-10-16 09:15 UTC262
            vk::PhysicalDeviceType::DISCRETE_GPU => 0,
824bf02b6e55atagen2026-10-16 09:15 UTC263
            vk::PhysicalDeviceType::INTEGRATED_GPU => 1,
824bf02b6e55atagen2026-10-16 09:15 UTC264
            vk::PhysicalDeviceType::VIRTUAL_GPU => 2,
824bf02b6e55atagen2026-10-16 09:15 UTC265
            _ => 3,
824bf02b6e55atagen2026-10-16 09:15 UTC266
        };
824bf02b6e55atagen2026-10-16 09:15 UTC267
        candidates.push((rank, physical, family as u32, name));
824bf02b6e55atagen2026-10-16 09:15 UTC268
    }
824bf02b6e55atagen2026-10-16 09:15 UTC269
    candidates.sort_by_key(|c| c.0);
824bf02b6e55atagen2026-10-16 09:15 UTC270
    candidates.into_iter().next().map(|(_, p, f, n)| (p, f, n))
824bf02b6e55atagen2026-10-16 09:15 UTC271
}