Skip to content

Commit

Permalink
Add basic drm support to vulkan backend
Browse files Browse the repository at this point in the history
  • Loading branch information
morr0ne committed Feb 26, 2025
1 parent 93f64dc commit c90fc90
Show file tree
Hide file tree
Showing 4 changed files with 244 additions and 0 deletions.
67 changes: 67 additions & 0 deletions wgpu-core/src/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,48 @@ impl Instance {
}
}

pub unsafe fn create_surface_from_drm(
&self,
fd: i32,
plane: u32,
connector_id: u32,
width: u32,
height: u32,
refresh_rate: u32,
) -> Result<Surface, CreateSurfaceError> {
profiling::scope!("Instance::create_surface_from_drm");

let mut errors = HashMap::default();
let mut surface_per_backend: HashMap<Backend, Box<dyn hal::DynSurface>> =
HashMap::default();

let instance = unsafe { self.as_hal::<hal::api::Vulkan>() }
.ok_or(CreateSurfaceError::BackendNotEnabled(Backend::Vulkan))?;

match instance.create_surface_from_drm(fd, plane, connector_id, width, height, refresh_rate)
{
Ok(surface) => {
surface_per_backend.insert(Backend::Vulkan, Box::new(surface));
}
Err(err) => {
errors.insert(Backend::Vulkan, err);
}
}

if surface_per_backend.is_empty() {
Err(CreateSurfaceError::FailedToCreateSurfaceForAnyBackend(
errors,
))
} else {
let surface = Surface {
presentation: Mutex::new(rank::SURFACE_PRESENTATION, None),
surface_per_backend,
};

Ok(surface)
}
}

/// # Safety
///
/// `layer` must be a valid pointer.
Expand Down Expand Up @@ -772,6 +814,31 @@ impl Global {
Ok(id)
}

pub unsafe fn instance_create_surface_from_drm(
&self,
fd: i32,
plane: u32,
connector_id: u32,
width: u32,
height: u32,
refresh_rate: u32,
id_in: Option<SurfaceId>,
) -> Result<SurfaceId, CreateSurfaceError> {
let surface = unsafe {
self.instance.create_surface_from_drm(
fd,
plane,
connector_id,
width,
height,
refresh_rate,
)
}?;
let id = self.surfaces.prepare(id_in).assign(Arc::new(surface));

Ok(id)
}

/// # Safety
///
/// `layer` must be a valid pointer.
Expand Down
134 changes: 134 additions & 0 deletions wgpu-hal/src/vulkan/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use std::{
borrow::ToOwned as _,
boxed::Box,
ffi::{c_void, CStr, CString},
mem::MaybeUninit,
slice,
str::FromStr,
string::{String, ToString as _},
Expand Down Expand Up @@ -259,6 +260,12 @@ impl super::Instance {
// VK_KHR_surface
extensions.push(khr::surface::NAME);

// Extensions needed for drm support
extensions.push(khr::display::NAME);
extensions.push(ext::physical_device_drm::NAME);
extensions.push(khr::get_display_properties2::NAME);
extensions.push(ext::acquire_drm_display::NAME);

// Platform-specific WSI extensions
if cfg!(all(
unix,
Expand Down Expand Up @@ -523,6 +530,133 @@ impl super::Instance {
Ok(self.create_surface_from_vk_surface_khr(surface))
}

pub 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(&khr::display::NAME) {
return Err(crate::InstanceError::new(String::from(
"Vulkan driver does not support VK_KHR_display",
)));
}

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)
};

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 _);

if primary_devid == drm_stat.st_rdev || render_devid == 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(),
))?;

// FIXME: consider implementing this strategy on working vulkan drivers
// let displays = unsafe {
// display_instance
// .get_physical_device_display_properties(physical_device)
// .expect("Failed to get displays")
// };

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))
}

#[cfg(metal)]
fn create_surface_from_view(
&self,
Expand Down
24 changes: 24 additions & 0 deletions wgpu/src/api/surface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,30 @@ pub enum SurfaceTargetUnsafe {
raw_window_handle: raw_window_handle::RawWindowHandle,
},

/// Surface from a DRM device.
///
/// If the specified DRM configuration is not supported by any of the backends, then the surface
/// will not be supported by any adapters.
///
/// # Safety
///
/// - All parameters must point to valid DRM values and remain valid for as long as the resulting [`Surface`] exists.
/// - The file descriptor (`fd`), plane, connector, and mode configuration must be valid and compatible.
Drm {
/// The file descriptor of the DRM device.
fd: i32,
/// The plane index on which to create the surface.
plane: u32,
/// The ID of the connector associated with the selected mode.
connector_id: u32,
/// The display width of the selected mode.
width: u32,
/// The display height of the selected mode.
height: u32,
/// The display refresh rate of the selected mode multiplied by 1000 (e.g., 60Hz → 60000).
refresh_rate: u32,
},

/// Surface from `CoreAnimationLayer`.
///
/// # Safety
Expand Down
19 changes: 19 additions & 0 deletions wgpu/src/backend/wgpu_core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -794,6 +794,25 @@ impl dispatch::InstanceInterface for ContextWgpuCore {
.instance_create_surface(raw_display_handle, raw_window_handle, None)
},

SurfaceTargetUnsafe::Drm {
fd,
plane,
connector_id,
width,
height,
refresh_rate,
} => unsafe {
self.0.instance_create_surface_from_drm(
fd,
plane,
connector_id,
width,
height,
refresh_rate,
None,
)
},

#[cfg(metal)]
SurfaceTargetUnsafe::CoreAnimationLayer(layer) => unsafe {
self.0.instance_create_surface_metal(layer, None)
Expand Down

0 comments on commit c90fc90

Please sign in to comment.