atagen

monolith

public
Star0
Watch0
5b7cddd2e177149d7bdb4006fcb9b174e08cb3cc
1
ssh://brewery@pls.lobotomise.me:33/atagen/monolith.git
crates/render-vk/src/device.rsRust185 lines6.9 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
}
41
42
impl Drop for Device {
43
    fn drop(&mut self) {
44
        unsafe {
45
            let _ = self.device.device_wait_idle();
46
            self.device.destroy_device(None);
47
            self.instance.destroy_instance(None);
48
        }
49
    }
50
}
51
52
impl Shared {
53
    /// Opens the first suitable device, or the one whose name contains
54
    /// `MONOLITH_VK_DEVICE` when that is set.
55
    pub fn new() -> Result<Self, Error> {
56
        let entry = unsafe { ash::Entry::load()? };
57
        let app = vk::ApplicationInfo::default()
58
            .application_name(c"monolith")
59
            .engine_name(c"monolith")
60
            .api_version(vk::API_VERSION_1_3);
61
        let instance = unsafe {
62
            entry.create_instance(
63
                &vk::InstanceCreateInfo::default().application_info(&app),
64
                None,
65
            )?
66
        };
67
        let wanted = std::env::var("MONOLITH_VK_DEVICE").ok();
68
        let picked = unsafe { pick_physical(&instance, wanted.as_deref()) };
69
        let Some((physical, queue_family, name)) = picked else {
70
            unsafe { instance.destroy_instance(None) };
71
            return Err(Error::NoSuitableDevice);
72
        };
73
        let priorities = [1.0];
74
        let queues = [vk::DeviceQueueCreateInfo::default()
75
            .queue_family_index(queue_family)
76
            .queue_priorities(&priorities)];
77
        let available: Vec<_> = unsafe { instance.enumerate_device_extension_properties(physical) }
78
            .unwrap_or_default()
79
            .iter()
80
            .filter_map(|e| e.extension_name_as_c_str().ok().map(ToOwned::to_owned))
81
            .collect();
82
        let has_dmabuf = DMABUF_EXTENSIONS
83
            .iter()
84
            .all(|n| available.iter().any(|a| a.as_c_str() == *n));
85
        let mut extensions = vec![ash::khr::push_descriptor::NAME.as_ptr()];
86
        if has_dmabuf {
87
            extensions.extend(DMABUF_EXTENSIONS.iter().map(|n| n.as_ptr()));
88
        }
89
        let mut features13 = vk::PhysicalDeviceVulkan13Features::default()
90
            .dynamic_rendering(true)
91
            .synchronization2(true);
92
        let info = vk::DeviceCreateInfo::default()
93
            .queue_create_infos(&queues)
94
            .enabled_extension_names(&extensions)
95
            .push_next(&mut features13);
96
        let device = match unsafe { instance.create_device(physical, &info, None) } {
97
            Ok(d) => d,
98
            Err(e) => {
99
                unsafe { instance.destroy_instance(None) };
100
                return Err(e.into());
101
            }
102
        };
103
        let queue = unsafe { device.get_device_queue(queue_family, 0) };
104
        let push_descriptor = ash::khr::push_descriptor::Device::new(&instance, &device);
105
        let dmabuf = has_dmabuf.then(|| DmabufExt {
106
            memory_fd: ash::khr::external_memory_fd::Device::new(&instance, &device),
107
            fence_fd: ash::khr::external_fence_fd::Device::new(&instance, &device),
108
            modifier: ash::ext::image_drm_format_modifier::Device::new(&instance, &device),
109
        });
110
        let allocator = Allocator::new(&AllocatorCreateDesc {
111
            instance: instance.clone(),
112
            device: device.clone(),
113
            physical_device: physical,
114
            debug_settings: Default::default(),
115
            buffer_device_address: false,
116
            allocation_sizes: Default::default(),
117
        });
118
        let device = Device {
119
            _entry: entry,
120
            instance,
121
            physical,
122
            device,
123
            queue: Mutex::new(queue),
124
            queue_family,
125
            push_descriptor,
126
            dmabuf,
127
            name,
128
        };
129
        Ok(Self {
130
            allocator: Mutex::new(allocator?),
131
            device,
132
        })
133
    }
134
}
135
136
unsafe fn pick_physical(
137
    instance: &ash::Instance,
138
    wanted: Option<&str>,
139
) -> Option<(vk::PhysicalDevice, u32, String)> {
140
    let devices = unsafe { instance.enumerate_physical_devices() }.ok()?;
141
    let mut candidates = Vec::new();
142
    for physical in devices {
143
        let props = unsafe { instance.get_physical_device_properties(physical) };
144
        if props.api_version < vk::API_VERSION_1_3 {
145
            continue;
146
        }
147
        let name = props
148
            .device_name_as_c_str()
149
            .unwrap_or(c"unknown")
150
            .to_string_lossy()
151
            .into_owned();
152
        if wanted.is_some_and(|w| !name.contains(w)) {
153
            continue;
154
        }
155
        let mut f13 = vk::PhysicalDeviceVulkan13Features::default();
156
        let mut f2 = vk::PhysicalDeviceFeatures2::default().push_next(&mut f13);
157
        unsafe { instance.get_physical_device_features2(physical, &mut f2) };
158
        if f13.dynamic_rendering == vk::FALSE || f13.synchronization2 == vk::FALSE {
159
            continue;
160
        }
161
        let has_push = unsafe { instance.enumerate_device_extension_properties(physical) }
162
            .unwrap_or_default()
163
            .iter()
164
            .any(|e| e.extension_name_as_c_str().ok() == Some(ash::khr::push_descriptor::NAME));
165
        if !has_push {
166
            continue;
167
        }
168
        let families = unsafe { instance.get_physical_device_queue_family_properties(physical) };
169
        let Some(family) = families
170
            .iter()
171
            .position(|f| f.queue_flags.contains(vk::QueueFlags::GRAPHICS))
172
        else {
173
            continue;
174
        };
175
        let rank = match props.device_type {
176
            vk::PhysicalDeviceType::DISCRETE_GPU => 0,
177
            vk::PhysicalDeviceType::INTEGRATED_GPU => 1,
178
            vk::PhysicalDeviceType::VIRTUAL_GPU => 2,
179
            _ => 3,
180
        };
181
        candidates.push((rank, physical, family as u32, name));
182
    }
183
    candidates.sort_by_key(|c| c.0);
184
    candidates.into_iter().next().map(|(_, p, f, n)| (p, f, n))
185
}