atagen

monolith

public
Star0
Watch0
a76842e12aca6c84e9870fd7ac94268136311b8d
1
ssh://brewery@pls.lobotomise.me:33/atagen/monolith.git
crates/render-vk/src/device.rsRust253 lines9.7 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
/// Loaders for the extensions that dmabuf import, export and fence export
15
/// need. Absent when the device lacks any of them.
16
pub(crate) struct DmabufExt {
17
    pub memory_fd: ash::khr::external_memory_fd::Device,
18
    pub fence_fd: ash::khr::external_fence_fd::Device,
19
    pub modifier: ash::ext::image_drm_format_modifier::Device,
20
}
21
22
const DMABUF_EXTENSIONS: [&std::ffi::CStr; 5] = [
23
    ash::khr::external_memory_fd::NAME,
24
    ash::ext::external_memory_dma_buf::NAME,
25
    ash::ext::image_drm_format_modifier::NAME,
26
    ash::ext::queue_family_foreign::NAME,
27
    ash::khr::external_fence_fd::NAME,
28
];
29
30
pub(crate) struct Device {
31
    pub _entry: ash::Entry,
32
    pub instance: ash::Instance,
33
    pub physical: vk::PhysicalDevice,
34
    pub device: ash::Device,
35
    pub queue: Mutex<vk::Queue>,
36
    pub queue_family: u32,
37
    pub push_descriptor: ash::khr::push_descriptor::Device,
38
    pub dmabuf: Option<DmabufExt>,
39
    pub name: String,
40
    /// Signalled fences kept for reuse, see `FenceInner`.
41
    pub spare_fences: Mutex<Vec<vk::Fence>>,
42
}
43
44
impl Drop for Device {
45
    fn drop(&mut self) {
46
        unsafe {
47
            let _ = self.device.device_wait_idle();
48
            for f in self
49
                .spare_fences
50
                .get_mut()
51
                .unwrap_or_else(|e| e.into_inner())
52
                .drain(..)
53
            {
54
                self.device.destroy_fence(f, None);
55
            }
56
            self.device.destroy_device(None);
57
            self.instance.destroy_instance(None);
58
        }
59
    }
60
}
61
62
impl Shared {
63
    /// Opens the first suitable device, or the one whose name contains
64
    /// `MONOLITH_VK_DEVICE` when that is set. With `node`, only the device
65
    /// behind that DRM device number qualifies.
66
    pub fn new(node: Option<u64>) -> Result<Self, Error> {
67
        let entry = unsafe { ash::Entry::load()? };
68
        let app = vk::ApplicationInfo::default()
69
            .application_name(c"monolith")
70
            .engine_name(c"monolith")
71
            .api_version(vk::API_VERSION_1_3);
72
        let instance = unsafe {
73
            entry.create_instance(
74
                &vk::InstanceCreateInfo::default().application_info(&app),
75
                None,
76
            )?
77
        };
78
        let wanted = std::env::var("MONOLITH_VK_DEVICE").ok();
79
        let picked = unsafe { pick_physical(&instance, wanted.as_deref(), node) };
80
        let Some((physical, queue_family, name)) = picked else {
81
            unsafe { instance.destroy_instance(None) };
82
            return Err(Error::NoSuitableDevice);
83
        };
84
        let priorities = [1.0];
85
        let available: Vec<_> = unsafe { instance.enumerate_device_extension_properties(physical) }
86
            .unwrap_or_default()
87
            .iter()
88
            .filter_map(|e| e.extension_name_as_c_str().ok().map(ToOwned::to_owned))
89
            .collect();
90
        let has_dmabuf = DMABUF_EXTENSIONS
91
            .iter()
92
            .all(|n| available.iter().any(|a| a.as_c_str() == *n));
93
        let mut extensions = vec![ash::khr::push_descriptor::NAME.as_ptr()];
94
        if has_dmabuf {
95
            extensions.extend(DMABUF_EXTENSIONS.iter().map(|n| n.as_ptr()));
96
        }
97
        let global_priority = [
98
            ash::khr::global_priority::NAME,
99
            ash::ext::global_priority::NAME,
100
        ]
101
        .into_iter()
102
        .find(|n| available.iter().any(|a| a.as_c_str() == *n));
103
        let mut priorities_to_try = vec![None];
104
        if let Some(name) = global_priority {
105
            extensions.push(name.as_ptr());
106
            priorities_to_try = vec![
107
                Some(vk::QueueGlobalPriorityKHR::REALTIME),
108
                Some(vk::QueueGlobalPriorityKHR::HIGH),
109
                None,
110
            ];
111
        }
112
        let mut created = Err(vk::Result::ERROR_INITIALIZATION_FAILED);
113
        for priority in priorities_to_try {
114
            let mut features13 = vk::PhysicalDeviceVulkan13Features::default()
115
                .dynamic_rendering(true)
116
                .synchronization2(true);
117
            let mut global = vk::DeviceQueueGlobalPriorityCreateInfoKHR::default()
118
                .global_priority(priority.unwrap_or(vk::QueueGlobalPriorityKHR::MEDIUM));
119
            let mut queue = vk::DeviceQueueCreateInfo::default()
120
                .queue_family_index(queue_family)
121
                .queue_priorities(&priorities);
122
            if priority.is_some() {
123
                queue = queue.push_next(&mut global);
124
            }
125
            let queues = [queue];
126
            let info = vk::DeviceCreateInfo::default()
127
                .queue_create_infos(&queues)
128
                .enabled_extension_names(&extensions)
129
                .push_next(&mut features13);
130
            created = unsafe { instance.create_device(physical, &info, None) };
131
            match &created {
132
                Ok(_) => {
133
                    tracing::info!(?priority, "queue global priority");
134
                    break;
135
                }
136
                Err(e) => tracing::debug!(?priority, "queue global priority refused: {e}"),
137
            }
138
        }
139
        let device = match created {
140
            Ok(d) => d,
141
            Err(e) => {
142
                unsafe { instance.destroy_instance(None) };
143
                return Err(e.into());
144
            }
145
        };
146
        let queue = unsafe { device.get_device_queue(queue_family, 0) };
147
        let push_descriptor = ash::khr::push_descriptor::Device::new(&instance, &device);
148
        let dmabuf = has_dmabuf.then(|| DmabufExt {
149
            memory_fd: ash::khr::external_memory_fd::Device::new(&instance, &device),
150
            fence_fd: ash::khr::external_fence_fd::Device::new(&instance, &device),
151
            modifier: ash::ext::image_drm_format_modifier::Device::new(&instance, &device),
152
        });
153
        let allocator = Allocator::new(&AllocatorCreateDesc {
154
            instance: instance.clone(),
155
            device: device.clone(),
156
            physical_device: physical,
157
            debug_settings: Default::default(),
158
            buffer_device_address: false,
159
            allocation_sizes: Default::default(),
160
        });
161
        let device = Device {
162
            _entry: entry,
163
            instance,
164
            physical,
165
            device,
166
            queue: Mutex::new(queue),
167
            queue_family,
168
            push_descriptor,
169
            dmabuf,
170
            name,
171
            spare_fences: Mutex::new(Vec::new()),
172
        };
173
        Ok(Self {
174
            allocator: Mutex::new(allocator?),
175
            device,
176
        })
177
    }
178
}
179
180
/// Whether `physical` is the device behind DRM device number `node`, as its
181
/// primary or render node.
182
unsafe fn serves_node(instance: &ash::Instance, physical: vk::PhysicalDevice, node: u64) -> bool {
183
    let mut drm = vk::PhysicalDeviceDrmPropertiesEXT::default();
184
    let mut props = vk::PhysicalDeviceProperties2::default().push_next(&mut drm);
185
    unsafe { instance.get_physical_device_properties2(physical, &mut props) };
186
    let (major, minor) = (
187
        rustix::fs::major(node) as i64,
188
        rustix::fs::minor(node) as i64,
189
    );
190
    (drm.has_primary == vk::TRUE && (drm.primary_major, drm.primary_minor) == (major, minor))
191
        || (drm.has_render == vk::TRUE && (drm.render_major, drm.render_minor) == (major, minor))
192
}
193
194
unsafe fn pick_physical(
195
    instance: &ash::Instance,
196
    wanted: Option<&str>,
197
    node: Option<u64>,
198
) -> Option<(vk::PhysicalDevice, u32, String)> {
199
    let devices = unsafe { instance.enumerate_physical_devices() }.ok()?;
200
    let mut candidates = Vec::new();
201
    for physical in devices {
202
        let props = unsafe { instance.get_physical_device_properties(physical) };
203
        if props.api_version < vk::API_VERSION_1_3 {
204
            continue;
205
        }
206
        let name = props
207
            .device_name_as_c_str()
208
            .unwrap_or(c"unknown")
209
            .to_string_lossy()
210
            .into_owned();
211
        if wanted.is_some_and(|w| !name.contains(w)) {
212
            continue;
213
        }
214
        let mut f13 = vk::PhysicalDeviceVulkan13Features::default();
215
        let mut f2 = vk::PhysicalDeviceFeatures2::default().push_next(&mut f13);
216
        unsafe { instance.get_physical_device_features2(physical, &mut f2) };
217
        if f13.dynamic_rendering == vk::FALSE || f13.synchronization2 == vk::FALSE {
218
            continue;
219
        }
220
        let extensions =
221
            unsafe { instance.enumerate_device_extension_properties(physical) }.unwrap_or_default();
222
        let has = |n: &std::ffi::CStr| {
223
            extensions
224
                .iter()
225
                .any(|e| e.extension_name_as_c_str().ok() == Some(n))
226
        };
227
        if !has(ash::khr::push_descriptor::NAME) {
228
            continue;
229
        }
230
        if let Some(node) = node
231
            && !(has(ash::ext::physical_device_drm::NAME)
232
                && unsafe { serves_node(instance, physical, node) })
233
        {
234
            continue;
235
        }
236
        let families = unsafe { instance.get_physical_device_queue_family_properties(physical) };
237
        let Some(family) = families
238
            .iter()
239
            .position(|f| f.queue_flags.contains(vk::QueueFlags::GRAPHICS))
240
        else {
241
            continue;
242
        };
243
        let rank = match props.device_type {
244
            vk::PhysicalDeviceType::DISCRETE_GPU => 0,
245
            vk::PhysicalDeviceType::INTEGRATED_GPU => 1,
246
            vk::PhysicalDeviceType::VIRTUAL_GPU => 2,
247
            _ => 3,
248
        };
249
        candidates.push((rank, physical, family as u32, name));
250
    }
251
    candidates.sort_by_key(|c| c.0);
252
    candidates.into_iter().next().map(|(_, p, f, n)| (p, f, n))
253
}