atagen

monolith

public
Star0
Watch0
824bf02b6e554e9c0b86328878fc8eb8caa6926d
1
ssh://brewery@pls.lobotomise.me:33/atagen/monolith.git
crates/render-vk/src/device.rsRust155 lines5.5 KB
1
use crate::Error;
2
use ash::vk;
3
use gpu_allocator::vulkan::{Allocator, AllocatorCreateDesc};
4
use std::sync::Mutex;
5
6
/// Everything that must outlive every Vulkan object the renderer hands out.
7
/// Fields drop in declaration order, so the allocator frees its memory before
8
/// the device is destroyed.
9
pub(crate) struct Shared {
10
    pub allocator: Mutex<Allocator>,
11
    pub device: Device,
12
}
13
14
pub(crate) struct Device {
15
    pub _entry: ash::Entry,
16
    pub instance: ash::Instance,
17
    #[expect(
18
        dead_code,
19
        reason = "dmabuf import queries format properties through it"
20
    )]
21
    pub physical: vk::PhysicalDevice,
22
    pub device: ash::Device,
23
    pub queue: Mutex<vk::Queue>,
24
    pub queue_family: u32,
25
    pub push_descriptor: ash::khr::push_descriptor::Device,
26
    pub name: String,
27
}
28
29
impl Drop for Device {
30
    fn drop(&mut self) {
31
        unsafe {
32
            let _ = self.device.device_wait_idle();
33
            self.device.destroy_device(None);
34
            self.instance.destroy_instance(None);
35
        }
36
    }
37
}
38
39
impl Shared {
40
    /// Opens the first suitable device, or the one whose name contains
41
    /// `MONOLITH_VK_DEVICE` when that is set.
42
    pub fn new() -> Result<Self, Error> {
43
        let entry = unsafe { ash::Entry::load()? };
44
        let app = vk::ApplicationInfo::default()
45
            .application_name(c"monolith")
46
            .engine_name(c"monolith")
47
            .api_version(vk::API_VERSION_1_3);
48
        let instance = unsafe {
49
            entry.create_instance(
50
                &vk::InstanceCreateInfo::default().application_info(&app),
51
                None,
52
            )?
53
        };
54
        let wanted = std::env::var("MONOLITH_VK_DEVICE").ok();
55
        let picked = unsafe { pick_physical(&instance, wanted.as_deref()) };
56
        let Some((physical, queue_family, name)) = picked else {
57
            unsafe { instance.destroy_instance(None) };
58
            return Err(Error::NoSuitableDevice);
59
        };
60
        let priorities = [1.0];
61
        let queues = [vk::DeviceQueueCreateInfo::default()
62
            .queue_family_index(queue_family)
63
            .queue_priorities(&priorities)];
64
        let extensions = [ash::khr::push_descriptor::NAME.as_ptr()];
65
        let mut features13 = vk::PhysicalDeviceVulkan13Features::default()
66
            .dynamic_rendering(true)
67
            .synchronization2(true);
68
        let info = vk::DeviceCreateInfo::default()
69
            .queue_create_infos(&queues)
70
            .enabled_extension_names(&extensions)
71
            .push_next(&mut features13);
72
        let device = match unsafe { instance.create_device(physical, &info, None) } {
73
            Ok(d) => d,
74
            Err(e) => {
75
                unsafe { instance.destroy_instance(None) };
76
                return Err(e.into());
77
            }
78
        };
79
        let queue = unsafe { device.get_device_queue(queue_family, 0) };
80
        let push_descriptor = ash::khr::push_descriptor::Device::new(&instance, &device);
81
        let allocator = Allocator::new(&AllocatorCreateDesc {
82
            instance: instance.clone(),
83
            device: device.clone(),
84
            physical_device: physical,
85
            debug_settings: Default::default(),
86
            buffer_device_address: false,
87
            allocation_sizes: Default::default(),
88
        });
89
        let device = Device {
90
            _entry: entry,
91
            instance,
92
            physical,
93
            device,
94
            queue: Mutex::new(queue),
95
            queue_family,
96
            push_descriptor,
97
            name,
98
        };
99
        Ok(Self {
100
            allocator: Mutex::new(allocator?),
101
            device,
102
        })
103
    }
104
}
105
106
unsafe fn pick_physical(
107
    instance: &ash::Instance,
108
    wanted: Option<&str>,
109
) -> Option<(vk::PhysicalDevice, u32, String)> {
110
    let devices = unsafe { instance.enumerate_physical_devices() }.ok()?;
111
    let mut candidates = Vec::new();
112
    for physical in devices {
113
        let props = unsafe { instance.get_physical_device_properties(physical) };
114
        if props.api_version < vk::API_VERSION_1_3 {
115
            continue;
116
        }
117
        let name = props
118
            .device_name_as_c_str()
119
            .unwrap_or(c"unknown")
120
            .to_string_lossy()
121
            .into_owned();
122
        if wanted.is_some_and(|w| !name.contains(w)) {
123
            continue;
124
        }
125
        let mut f13 = vk::PhysicalDeviceVulkan13Features::default();
126
        let mut f2 = vk::PhysicalDeviceFeatures2::default().push_next(&mut f13);
127
        unsafe { instance.get_physical_device_features2(physical, &mut f2) };
128
        if f13.dynamic_rendering == vk::FALSE || f13.synchronization2 == vk::FALSE {
129
            continue;
130
        }
131
        let has_push = unsafe { instance.enumerate_device_extension_properties(physical) }
132
            .unwrap_or_default()
133
            .iter()
134
            .any(|e| e.extension_name_as_c_str().ok() == Some(ash::khr::push_descriptor::NAME));
135
        if !has_push {
136
            continue;
137
        }
138
        let families = unsafe { instance.get_physical_device_queue_family_properties(physical) };
139
        let Some(family) = families
140
            .iter()
141
            .position(|f| f.queue_flags.contains(vk::QueueFlags::GRAPHICS))
142
        else {
143
            continue;
144
        };
145
        let rank = match props.device_type {
146
            vk::PhysicalDeviceType::DISCRETE_GPU => 0,
147
            vk::PhysicalDeviceType::INTEGRATED_GPU => 1,
148
            vk::PhysicalDeviceType::VIRTUAL_GPU => 2,
149
            _ => 3,
150
        };
151
        candidates.push((rank, physical, family as u32, name));
152
    }
153
    candidates.sort_by_key(|c| c.0);
154
    candidates.into_iter().next().map(|(_, p, f, n)| (p, f, n))
155
}