wgpu_hal/vulkan/
drm.rs

1#![cfg(all(unix, not(target_vendor = "apple"), not(target_family = "wasm")))]
2
3use alloc::{string::ToString, vec::Vec};
4use core::mem::MaybeUninit;
5
6use ash::{ext, khr, vk};
7
8macro_rules! to_u64 {
9    ($expr:expr) => {{
10        #[allow(trivial_numeric_casts)]
11        let expr = $expr as u64;
12        assert!(size_of_val(&expr) <= size_of::<u64>());
13        expr
14    }};
15}
16
17impl super::Instance {
18    /// Creates a new surface from the given drm configuration.
19    ///
20    /// # Safety
21    ///
22    /// - All parameters must point to valid DRM values.
23    pub unsafe fn create_surface_from_drm(
24        &self,
25        fd: i32,
26        plane: u32,
27        connector_id: u32,
28        width: u32,
29        height: u32,
30        refresh_rate: u32,
31    ) -> Result<super::Surface, crate::InstanceError> {
32        if !self
33            .shared
34            .extensions
35            .contains(&ext::acquire_drm_display::NAME)
36        {
37            return Err(crate::InstanceError::new(
38                "Vulkan driver does not support VK_EXT_acquire_drm_display".to_string(),
39            ));
40        }
41
42        let drm_stat = {
43            let mut stat = MaybeUninit::<libc::stat>::uninit();
44
45            if unsafe { libc::fstat(fd, stat.as_mut_ptr()) } != 0 {
46                return Err(crate::InstanceError::new(
47                    "Unable to fstat drm device".to_string(),
48                ));
49            }
50
51            unsafe { stat.assume_init() }
52        };
53
54        let raw_devices = match unsafe { self.shared.raw.enumerate_physical_devices() } {
55            Ok(devices) => devices,
56            Err(err) => {
57                log::error!("enumerate_adapters: {err}");
58                Vec::new()
59            }
60        };
61
62        let mut physical_device = None;
63
64        for device in raw_devices {
65            let properties2 = vk::PhysicalDeviceProperties2KHR::default();
66
67            let mut drm_props = vk::PhysicalDeviceDrmPropertiesEXT::default();
68            let mut properties2 = properties2.push_next(&mut drm_props);
69
70            unsafe {
71                self.shared
72                    .raw
73                    .get_physical_device_properties2(device, &mut properties2)
74            };
75
76            /*
77                The makedev call is just bit manipulation to combine major and minor device numbers into a Unix device ID.
78                It doesn't perform any filesystem operations, only bitshifting.
79                See: https://github.com/rust-lang/libc/blob/268e1b3810ac07ed637d9005bc1a54e49218c958/src/unix/linux_like/linux/mod.rs#L6049
80                We use the resulting device IDs to check if the Vulkan raw device from enumerate_physical_devices
81                matches the DRM device referred to by our file descriptor.
82            */
83
84            let primary_devid =
85                libc::makedev(drm_props.primary_major as _, drm_props.primary_minor as _);
86            let render_devid =
87                libc::makedev(drm_props.render_major as _, drm_props.render_minor as _);
88
89            // On most platforms, both `*_devid`s and `st_rdev` are `dev_t`s (which is generally
90            // observed to be an unsigned integral type no greater than 64 bits). However, on some
91            // platforms, there divergences from this pattern:
92            //
93            // - `armv7-linux-androideabi`: `dev_t` is `c_ulong`, and `*_devid`s are `dev_t`, but
94            //   `st_rdev` is `c_ulonglong`. So, we can't just do a `==` comparison.
95            // - OpenBSD has `dev_t` on both sides, but is `i32` (N.B., unsigned). Therefore, we
96            //   can't just use `u64::from`.
97            #[allow(clippy::useless_conversion)]
98            if [primary_devid, render_devid]
99                .map(|devid| to_u64!(devid))
100                .contains(&to_u64!(drm_stat.st_rdev))
101            {
102                physical_device = Some(device)
103            }
104        }
105
106        let physical_device = physical_device.ok_or(crate::InstanceError::new(
107            "Failed to find suitable drm device".to_string(),
108        ))?;
109
110        let acquire_drm_display_instance =
111            ext::acquire_drm_display::Instance::new(&self.shared.entry, &self.shared.raw);
112
113        let display = unsafe {
114            acquire_drm_display_instance
115                .get_drm_display(physical_device, fd, connector_id)
116                .expect("Failed to get drm display")
117        };
118
119        unsafe {
120            acquire_drm_display_instance
121                .acquire_drm_display(physical_device, fd, display)
122                .expect("Failed to acquire drm display")
123        }
124
125        let display_instance = khr::display::Instance::new(&self.shared.entry, &self.shared.raw);
126
127        let modes = unsafe {
128            display_instance
129                .get_display_mode_properties(physical_device, display)
130                .expect("Failed to get display modes")
131        };
132
133        let mut mode = None;
134
135        for current_mode in modes {
136            log::trace!(
137                "Comparing mode {}x{}@{} with {width}x{height}@{refresh_rate}",
138                current_mode.parameters.visible_region.width,
139                current_mode.parameters.visible_region.height,
140                current_mode.parameters.refresh_rate
141            );
142            if current_mode.parameters.refresh_rate == refresh_rate
143                && current_mode.parameters.visible_region.width == width
144                && current_mode.parameters.visible_region.height == height
145            {
146                mode = Some(current_mode)
147            }
148        }
149
150        let mode = mode.ok_or(crate::InstanceError::new(
151            "Failed to find suitable display mode".to_string(),
152        ))?;
153
154        let create_info = vk::DisplaySurfaceCreateInfoKHR::default()
155            .display_mode(mode.display_mode)
156            .image_extent(mode.parameters.visible_region)
157            .transform(vk::SurfaceTransformFlagsKHR::IDENTITY)
158            .alpha_mode(vk::DisplayPlaneAlphaFlagsKHR::OPAQUE)
159            .plane_index(plane);
160
161        let surface = unsafe { display_instance.create_display_plane_surface(&create_info, None) }
162            .expect("Failed to create DRM surface");
163
164        Ok(self.create_surface_from_vk_surface_khr(surface))
165    }
166}