atagen

monolith

public
Star0
Watch0
824bf02b6e554e9c0b86328878fc8eb8caa6926d
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 {
824bf02b6e55atagen2026-10-16 09:15 UTC10
    pub allocator: Mutex<Allocator>,
824bf02b6e55atagen2026-10-16 09:15 UTC11
    pub device: Device,
824bf02b6e55atagen2026-10-16 09:15 UTC12
}
824bf02b6e55atagen2026-10-16 09:15 UTC13
824bf02b6e55atagen2026-10-16 09:15 UTC14
pub(crate) struct Device {
824bf02b6e55atagen2026-10-16 09:15 UTC15
    pub _entry: ash::Entry,
824bf02b6e55atagen2026-10-16 09:15 UTC16
    pub instance: ash::Instance,
824bf02b6e55atagen2026-10-16 09:15 UTC17
    #[expect(
824bf02b6e55atagen2026-10-16 09:15 UTC18
        dead_code,
824bf02b6e55atagen2026-10-16 09:15 UTC19
        reason = "dmabuf import queries format properties through it"
824bf02b6e55atagen2026-10-16 09:15 UTC20
    )]
824bf02b6e55atagen2026-10-16 09:15 UTC21
    pub physical: vk::PhysicalDevice,
824bf02b6e55atagen2026-10-16 09:15 UTC22
    pub device: ash::Device,
824bf02b6e55atagen2026-10-16 09:15 UTC23
    pub queue: Mutex<vk::Queue>,
824bf02b6e55atagen2026-10-16 09:15 UTC24
    pub queue_family: u32,
824bf02b6e55atagen2026-10-16 09:15 UTC25
    pub push_descriptor: ash::khr::push_descriptor::Device,
824bf02b6e55atagen2026-10-16 09:15 UTC26
    pub name: String,
824bf02b6e55atagen2026-10-16 09:15 UTC27
}
824bf02b6e55atagen2026-10-16 09:15 UTC28
824bf02b6e55atagen2026-10-16 09:15 UTC29
impl Drop for Device {
824bf02b6e55atagen2026-10-16 09:15 UTC30
    fn drop(&mut self) {
824bf02b6e55atagen2026-10-16 09:15 UTC31
        unsafe {
824bf02b6e55atagen2026-10-16 09:15 UTC32
            let _ = self.device.device_wait_idle();
824bf02b6e55atagen2026-10-16 09:15 UTC33
            self.device.destroy_device(None);
824bf02b6e55atagen2026-10-16 09:15 UTC34
            self.instance.destroy_instance(None);
824bf02b6e55atagen2026-10-16 09:15 UTC35
        }
824bf02b6e55atagen2026-10-16 09:15 UTC36
    }
824bf02b6e55atagen2026-10-16 09:15 UTC37
}
824bf02b6e55atagen2026-10-16 09:15 UTC38
824bf02b6e55atagen2026-10-16 09:15 UTC39
impl Shared {
824bf02b6e55atagen2026-10-16 09:15 UTC40
    /// Opens the first suitable device, or the one whose name contains
824bf02b6e55atagen2026-10-16 09:15 UTC41
    /// `MONOLITH_VK_DEVICE` when that is set.
824bf02b6e55atagen2026-10-16 09:15 UTC42
    pub fn new() -> Result<Self, Error> {
824bf02b6e55atagen2026-10-16 09:15 UTC43
        let entry = unsafe { ash::Entry::load()? };
824bf02b6e55atagen2026-10-16 09:15 UTC44
        let app = vk::ApplicationInfo::default()
824bf02b6e55atagen2026-10-16 09:15 UTC45
            .application_name(c"monolith")
824bf02b6e55atagen2026-10-16 09:15 UTC46
            .engine_name(c"monolith")
824bf02b6e55atagen2026-10-16 09:15 UTC47
            .api_version(vk::API_VERSION_1_3);
824bf02b6e55atagen2026-10-16 09:15 UTC48
        let instance = unsafe {
824bf02b6e55atagen2026-10-16 09:15 UTC49
            entry.create_instance(
824bf02b6e55atagen2026-10-16 09:15 UTC50
                &vk::InstanceCreateInfo::default().application_info(&app),
824bf02b6e55atagen2026-10-16 09:15 UTC51
                None,
824bf02b6e55atagen2026-10-16 09:15 UTC52
            )?
824bf02b6e55atagen2026-10-16 09:15 UTC53
        };
824bf02b6e55atagen2026-10-16 09:15 UTC54
        let wanted = std::env::var("MONOLITH_VK_DEVICE").ok();
824bf02b6e55atagen2026-10-16 09:15 UTC55
        let picked = unsafe { pick_physical(&instance, wanted.as_deref()) };
824bf02b6e55atagen2026-10-16 09:15 UTC56
        let Some((physical, queue_family, name)) = picked else {
824bf02b6e55atagen2026-10-16 09:15 UTC57
            unsafe { instance.destroy_instance(None) };
824bf02b6e55atagen2026-10-16 09:15 UTC58
            return Err(Error::NoSuitableDevice);
824bf02b6e55atagen2026-10-16 09:15 UTC59
        };
824bf02b6e55atagen2026-10-16 09:15 UTC60
        let priorities = [1.0];
824bf02b6e55atagen2026-10-16 09:15 UTC61
        let queues = [vk::DeviceQueueCreateInfo::default()
824bf02b6e55atagen2026-10-16 09:15 UTC62
            .queue_family_index(queue_family)
824bf02b6e55atagen2026-10-16 09:15 UTC63
            .queue_priorities(&priorities)];
824bf02b6e55atagen2026-10-16 09:15 UTC64
        let extensions = [ash::khr::push_descriptor::NAME.as_ptr()];
824bf02b6e55atagen2026-10-16 09:15 UTC65
        let mut features13 = vk::PhysicalDeviceVulkan13Features::default()
824bf02b6e55atagen2026-10-16 09:15 UTC66
            .dynamic_rendering(true)
824bf02b6e55atagen2026-10-16 09:15 UTC67
            .synchronization2(true);
824bf02b6e55atagen2026-10-16 09:15 UTC68
        let info = vk::DeviceCreateInfo::default()
824bf02b6e55atagen2026-10-16 09:15 UTC69
            .queue_create_infos(&queues)
824bf02b6e55atagen2026-10-16 09:15 UTC70
            .enabled_extension_names(&extensions)
824bf02b6e55atagen2026-10-16 09:15 UTC71
            .push_next(&mut features13);
824bf02b6e55atagen2026-10-16 09:15 UTC72
        let device = match unsafe { instance.create_device(physical, &info, None) } {
824bf02b6e55atagen2026-10-16 09:15 UTC73
            Ok(d) => d,
824bf02b6e55atagen2026-10-16 09:15 UTC74
            Err(e) => {
824bf02b6e55atagen2026-10-16 09:15 UTC75
                unsafe { instance.destroy_instance(None) };
824bf02b6e55atagen2026-10-16 09:15 UTC76
                return Err(e.into());
824bf02b6e55atagen2026-10-16 09:15 UTC77
            }
824bf02b6e55atagen2026-10-16 09:15 UTC78
        };
824bf02b6e55atagen2026-10-16 09:15 UTC79
        let queue = unsafe { device.get_device_queue(queue_family, 0) };
824bf02b6e55atagen2026-10-16 09:15 UTC80
        let push_descriptor = ash::khr::push_descriptor::Device::new(&instance, &device);
824bf02b6e55atagen2026-10-16 09:15 UTC81
        let allocator = Allocator::new(&AllocatorCreateDesc {
824bf02b6e55atagen2026-10-16 09:15 UTC82
            instance: instance.clone(),
824bf02b6e55atagen2026-10-16 09:15 UTC83
            device: device.clone(),
824bf02b6e55atagen2026-10-16 09:15 UTC84
            physical_device: physical,
824bf02b6e55atagen2026-10-16 09:15 UTC85
            debug_settings: Default::default(),
824bf02b6e55atagen2026-10-16 09:15 UTC86
            buffer_device_address: false,
824bf02b6e55atagen2026-10-16 09:15 UTC87
            allocation_sizes: Default::default(),
824bf02b6e55atagen2026-10-16 09:15 UTC88
        });
824bf02b6e55atagen2026-10-16 09:15 UTC89
        let device = Device {
824bf02b6e55atagen2026-10-16 09:15 UTC90
            _entry: entry,
824bf02b6e55atagen2026-10-16 09:15 UTC91
            instance,
824bf02b6e55atagen2026-10-16 09:15 UTC92
            physical,
824bf02b6e55atagen2026-10-16 09:15 UTC93
            device,
824bf02b6e55atagen2026-10-16 09:15 UTC94
            queue: Mutex::new(queue),
824bf02b6e55atagen2026-10-16 09:15 UTC95
            queue_family,
824bf02b6e55atagen2026-10-16 09:15 UTC96
            push_descriptor,
824bf02b6e55atagen2026-10-16 09:15 UTC97
            name,
824bf02b6e55atagen2026-10-16 09:15 UTC98
        };
824bf02b6e55atagen2026-10-16 09:15 UTC99
        Ok(Self {
824bf02b6e55atagen2026-10-16 09:15 UTC100
            allocator: Mutex::new(allocator?),
824bf02b6e55atagen2026-10-16 09:15 UTC101
            device,
824bf02b6e55atagen2026-10-16 09:15 UTC102
        })
824bf02b6e55atagen2026-10-16 09:15 UTC103
    }
824bf02b6e55atagen2026-10-16 09:15 UTC104
}
824bf02b6e55atagen2026-10-16 09:15 UTC105
824bf02b6e55atagen2026-10-16 09:15 UTC106
unsafe fn pick_physical(
824bf02b6e55atagen2026-10-16 09:15 UTC107
    instance: &ash::Instance,
824bf02b6e55atagen2026-10-16 09:15 UTC108
    wanted: Option<&str>,
824bf02b6e55atagen2026-10-16 09:15 UTC109
) -> Option<(vk::PhysicalDevice, u32, String)> {
824bf02b6e55atagen2026-10-16 09:15 UTC110
    let devices = unsafe { instance.enumerate_physical_devices() }.ok()?;
824bf02b6e55atagen2026-10-16 09:15 UTC111
    let mut candidates = Vec::new();
824bf02b6e55atagen2026-10-16 09:15 UTC112
    for physical in devices {
824bf02b6e55atagen2026-10-16 09:15 UTC113
        let props = unsafe { instance.get_physical_device_properties(physical) };
824bf02b6e55atagen2026-10-16 09:15 UTC114
        if props.api_version < vk::API_VERSION_1_3 {
824bf02b6e55atagen2026-10-16 09:15 UTC115
            continue;
824bf02b6e55atagen2026-10-16 09:15 UTC116
        }
824bf02b6e55atagen2026-10-16 09:15 UTC117
        let name = props
824bf02b6e55atagen2026-10-16 09:15 UTC118
            .device_name_as_c_str()
824bf02b6e55atagen2026-10-16 09:15 UTC119
            .unwrap_or(c"unknown")
824bf02b6e55atagen2026-10-16 09:15 UTC120
            .to_string_lossy()
824bf02b6e55atagen2026-10-16 09:15 UTC121
            .into_owned();
824bf02b6e55atagen2026-10-16 09:15 UTC122
        if wanted.is_some_and(|w| !name.contains(w)) {
824bf02b6e55atagen2026-10-16 09:15 UTC123
            continue;
824bf02b6e55atagen2026-10-16 09:15 UTC124
        }
824bf02b6e55atagen2026-10-16 09:15 UTC125
        let mut f13 = vk::PhysicalDeviceVulkan13Features::default();
824bf02b6e55atagen2026-10-16 09:15 UTC126
        let mut f2 = vk::PhysicalDeviceFeatures2::default().push_next(&mut f13);
824bf02b6e55atagen2026-10-16 09:15 UTC127
        unsafe { instance.get_physical_device_features2(physical, &mut f2) };
824bf02b6e55atagen2026-10-16 09:15 UTC128
        if f13.dynamic_rendering == vk::FALSE || f13.synchronization2 == vk::FALSE {
824bf02b6e55atagen2026-10-16 09:15 UTC129
            continue;
824bf02b6e55atagen2026-10-16 09:15 UTC130
        }
824bf02b6e55atagen2026-10-16 09:15 UTC131
        let has_push = unsafe { instance.enumerate_device_extension_properties(physical) }
824bf02b6e55atagen2026-10-16 09:15 UTC132
            .unwrap_or_default()
824bf02b6e55atagen2026-10-16 09:15 UTC133
            .iter()
824bf02b6e55atagen2026-10-16 09:15 UTC134
            .any(|e| e.extension_name_as_c_str().ok() == Some(ash::khr::push_descriptor::NAME));
824bf02b6e55atagen2026-10-16 09:15 UTC135
        if !has_push {
824bf02b6e55atagen2026-10-16 09:15 UTC136
            continue;
824bf02b6e55atagen2026-10-16 09:15 UTC137
        }
824bf02b6e55atagen2026-10-16 09:15 UTC138
        let families = unsafe { instance.get_physical_device_queue_family_properties(physical) };
824bf02b6e55atagen2026-10-16 09:15 UTC139
        let Some(family) = families
824bf02b6e55atagen2026-10-16 09:15 UTC140
            .iter()
824bf02b6e55atagen2026-10-16 09:15 UTC141
            .position(|f| f.queue_flags.contains(vk::QueueFlags::GRAPHICS))
824bf02b6e55atagen2026-10-16 09:15 UTC142
        else {
824bf02b6e55atagen2026-10-16 09:15 UTC143
            continue;
824bf02b6e55atagen2026-10-16 09:15 UTC144
        };
824bf02b6e55atagen2026-10-16 09:15 UTC145
        let rank = match props.device_type {
824bf02b6e55atagen2026-10-16 09:15 UTC146
            vk::PhysicalDeviceType::DISCRETE_GPU => 0,
824bf02b6e55atagen2026-10-16 09:15 UTC147
            vk::PhysicalDeviceType::INTEGRATED_GPU => 1,
824bf02b6e55atagen2026-10-16 09:15 UTC148
            vk::PhysicalDeviceType::VIRTUAL_GPU => 2,
824bf02b6e55atagen2026-10-16 09:15 UTC149
            _ => 3,
824bf02b6e55atagen2026-10-16 09:15 UTC150
        };
824bf02b6e55atagen2026-10-16 09:15 UTC151
        candidates.push((rank, physical, family as u32, name));
824bf02b6e55atagen2026-10-16 09:15 UTC152
    }
824bf02b6e55atagen2026-10-16 09:15 UTC153
    candidates.sort_by_key(|c| c.0);
824bf02b6e55atagen2026-10-16 09:15 UTC154
    candidates.into_iter().next().map(|(_, p, f, n)| (p, f, n))
824bf02b6e55atagen2026-10-16 09:15 UTC155
}