wgpu_hal/vulkan/
drm.rs

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