wgpu_hal/vulkan/
drm.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
#![cfg(all(unix, not(target_vendor = "apple"), not(target_family = "wasm")))]

use core::mem::MaybeUninit;
use std::{string::ToString, vec::Vec};

use ash::{ext, khr, vk};

impl super::Instance {
    /// Creates a new surface from the given drm configuration.
    ///
    /// # Safety
    ///
    /// - All parameters must point to valid DRM values.
    pub unsafe fn create_surface_from_drm(
        &self,
        fd: i32,
        plane: u32,
        connector_id: u32,
        width: u32,
        height: u32,
        refresh_rate: u32,
    ) -> Result<super::Surface, crate::InstanceError> {
        if !self
            .shared
            .extensions
            .contains(&ext::acquire_drm_display::NAME)
        {
            return Err(crate::InstanceError::new(
                "Vulkan driver does not support VK_EXT_acquire_drm_display".to_string(),
            ));
        }

        let drm_stat = {
            let mut stat = MaybeUninit::<libc::stat>::uninit();

            if unsafe { libc::fstat(fd, stat.as_mut_ptr()) } != 0 {
                return Err(crate::InstanceError::new(
                    "Unable to fstat drm device".to_string(),
                ));
            }

            unsafe { stat.assume_init() }
        };

        let raw_devices = match unsafe { self.shared.raw.enumerate_physical_devices() } {
            Ok(devices) => devices,
            Err(err) => {
                log::error!("enumerate_adapters: {}", err);
                Vec::new()
            }
        };

        let mut physical_device = None;

        for device in raw_devices {
            let properties2 = vk::PhysicalDeviceProperties2KHR::default();

            let mut drm_props = vk::PhysicalDeviceDrmPropertiesEXT::default();
            let mut properties2 = properties2.push_next(&mut drm_props);

            unsafe {
                self.shared
                    .raw
                    .get_physical_device_properties2(device, &mut properties2)
            };

            /*
                The makedev call is just bit manipulation to combine major and minor device numbers into a Unix device ID.
                It doesn't perform any filesystem operations, only bitshifting.
                See: https://github.com/rust-lang/libc/blob/268e1b3810ac07ed637d9005bc1a54e49218c958/src/unix/linux_like/linux/mod.rs#L6049
                We use the resulting device IDs to check if the Vulkan raw device from enumerate_physical_devices
                matches the DRM device referred to by our file descriptor.
            */

            let primary_devid =
                libc::makedev(drm_props.primary_major as _, drm_props.primary_minor as _);
            let render_devid =
                libc::makedev(drm_props.render_major as _, drm_props.render_minor as _);

            // Various platforms use different widths between `dev_t` and `c_int`, so just
            // force-convert to `u64` to keep things portable.
            #[allow(clippy::useless_conversion)]
            if [primary_devid, render_devid]
                .map(u64::from)
                .contains(&drm_stat.st_rdev)
            {
                physical_device = Some(device)
            }
        }

        let physical_device = physical_device.ok_or(crate::InstanceError::new(
            "Failed to find suitable drm device".to_string(),
        ))?;

        let acquire_drm_display_instance =
            ext::acquire_drm_display::Instance::new(&self.shared.entry, &self.shared.raw);

        let display = unsafe {
            acquire_drm_display_instance
                .get_drm_display(physical_device, fd, connector_id)
                .expect("Failed to get drm display")
        };

        unsafe {
            acquire_drm_display_instance
                .acquire_drm_display(physical_device, fd, display)
                .expect("Failed to acquire drm display")
        }

        let display_instance = khr::display::Instance::new(&self.shared.entry, &self.shared.raw);

        let modes = unsafe {
            display_instance
                .get_display_mode_properties(physical_device, display)
                .expect("Failed to get display modes")
        };

        let mut mode = None;

        for current_mode in modes {
            log::trace!(
                "Comparing mode {}x{}@{} with {width}x{height}@{refresh_rate}",
                current_mode.parameters.visible_region.width,
                current_mode.parameters.visible_region.height,
                current_mode.parameters.refresh_rate
            );
            if current_mode.parameters.refresh_rate == refresh_rate
                && current_mode.parameters.visible_region.width == width
                && current_mode.parameters.visible_region.height == height
            {
                mode = Some(current_mode)
            }
        }

        let mode = mode.ok_or(crate::InstanceError::new(
            "Failed to find suitable display mode".to_string(),
        ))?;

        let create_info = vk::DisplaySurfaceCreateInfoKHR::default()
            .display_mode(mode.display_mode)
            .image_extent(mode.parameters.visible_region)
            .transform(vk::SurfaceTransformFlagsKHR::IDENTITY)
            .alpha_mode(vk::DisplayPlaneAlphaFlagsKHR::OPAQUE)
            .plane_index(plane);

        let surface = unsafe { display_instance.create_display_plane_surface(&create_info, None) }
            .expect("Failed to create DRM surface");

        Ok(self.create_surface_from_vk_surface_khr(surface))
    }
}