feat: Add a DRM/KMS backend

This adds a DRM/KMS based backend to the system, as per #42. This system finds a CRTC and a connector, then uses that to create a frame buffer and a DUMB buffer that it can render to.

There's much more to do, and is left as an exercise for anyone with a significant DRM-based use case to pick up and fix.

Signed-off-by: John Nunley <dev@notgull.net>
This commit is contained in:
John Nunley 2023-08-12 13:39:13 -07:00 committed by GitHub
parent 2689cec2ca
commit ac0b7f5e14
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 665 additions and 3 deletions

3
.github/CODEOWNERS vendored
View file

@ -4,6 +4,9 @@
# Apple platforms # Apple platforms
/src/cg.rs @madsmtm /src/cg.rs @madsmtm
# DRM/KMS (no maintainer)
/src/kms.rs
# Redox # Redox
/src/orbital.rs @jackpot51 /src/orbital.rs @jackpot51

View file

@ -43,9 +43,10 @@ jobs:
- { target: x86_64-unknown-linux-gnu, os: ubuntu-latest, } - { target: x86_64-unknown-linux-gnu, os: ubuntu-latest, }
- { target: x86_64-unknown-linux-gnu, os: ubuntu-latest, options: --no-default-features, features: "x11,x11-dlopen" } - { target: x86_64-unknown-linux-gnu, os: ubuntu-latest, options: --no-default-features, features: "x11,x11-dlopen" }
- { target: x86_64-unknown-linux-gnu, os: ubuntu-latest, options: --no-default-features, features: "wayland,wayland-dlopen" } - { target: x86_64-unknown-linux-gnu, os: ubuntu-latest, options: --no-default-features, features: "wayland,wayland-dlopen" }
- { target: x86_64-unknown-linux-gnu, os: ubuntu-latest, options: --no-default-features, features: "kms" }
- { target: x86_64-unknown-redox, os: ubuntu-latest, } - { target: x86_64-unknown-redox, os: ubuntu-latest, }
- { target: x86_64-unknown-freebsd, os: ubuntu-latest, } - { target: x86_64-unknown-freebsd, os: ubuntu-latest, }
- { target: x86_64-unknown-netbsd, os: ubuntu-latest, } - { target: x86_64-unknown-netbsd, os: ubuntu-latest, options: --no-default-features, features: "x11,x11-dlopen,wayland,wayland-dlopen" }
- { target: x86_64-apple-darwin, os: macos-latest, } - { target: x86_64-apple-darwin, os: macos-latest, }
- { target: wasm32-unknown-unknown, os: ubuntu-latest, } - { target: wasm32-unknown-unknown, os: ubuntu-latest, }
include: include:

View file

@ -17,7 +17,8 @@ name = "buffer_mut"
harness = false harness = false
[features] [features]
default = ["x11", "x11-dlopen", "wayland", "wayland-dlopen"] default = ["kms", "x11", "x11-dlopen", "wayland", "wayland-dlopen"]
kms = ["bytemuck", "drm", "drm-sys", "nix"]
wayland = ["wayland-backend", "wayland-client", "memmap2", "nix", "fastrand"] wayland = ["wayland-backend", "wayland-client", "memmap2", "nix", "fastrand"]
wayland-dlopen = ["wayland-sys/dlopen"] wayland-dlopen = ["wayland-sys/dlopen"]
x11 = ["as-raw-xcb-connection", "bytemuck", "nix", "tiny-xlib", "x11rb"] x11 = ["as-raw-xcb-connection", "bytemuck", "nix", "tiny-xlib", "x11rb"]
@ -30,6 +31,8 @@ raw-window-handle = "0.5.0"
[target.'cfg(all(unix, not(any(target_vendor = "apple", target_os = "android", target_os = "redox"))))'.dependencies] [target.'cfg(all(unix, not(any(target_vendor = "apple", target_os = "android", target_os = "redox"))))'.dependencies]
as-raw-xcb-connection = { version = "1.0.0", optional = true } as-raw-xcb-connection = { version = "1.0.0", optional = true }
bytemuck = { version = "1.12.3", optional = true } bytemuck = { version = "1.12.3", optional = true }
drm = { version = "0.9.0", default-features = false, optional = true }
drm-sys = { version = "0.4.0", default-features = false, optional = true }
memmap2 = { version = "0.7.1", optional = true } memmap2 = { version = "0.7.1", optional = true }
nix = { version = "0.26.1", optional = true } nix = { version = "0.26.1", optional = true }
tiny-xlib = { version = "0.2.1", optional = true } tiny-xlib = { version = "0.2.1", optional = true }
@ -76,6 +79,7 @@ redox_syscall = "0.3"
cfg_aliases = "0.1.1" cfg_aliases = "0.1.1"
[dev-dependencies] [dev-dependencies]
colorous = "1.0.12"
criterion = { version = "0.4.0", default-features = false, features = ["cargo_bench_support"] } criterion = { version = "0.4.0", default-features = false, features = ["cargo_bench_support"] }
instant = "0.1.12" instant = "0.1.12"
winit = "0.28.1" winit = "0.28.1"
@ -95,6 +99,9 @@ rayon = "1.5.1"
[target.'cfg(target_arch = "wasm32")'.dev-dependencies] [target.'cfg(target_arch = "wasm32")'.dev-dependencies]
wasm-bindgen-test = "0.3" wasm-bindgen-test = "0.3"
[target.'cfg(all(unix, not(any(target_vendor = "apple", target_os = "android", target_os = "redox"))))'.dev-dependencies]
rustix = { version = "0.38.8", features = ["event"] }
[workspace] [workspace]
members = [ members = [
"run-wasm", "run-wasm",

View file

@ -1,6 +1,7 @@
fn main() { fn main() {
cfg_aliases::cfg_aliases! { cfg_aliases::cfg_aliases! {
free_unix: { all(unix, not(any(target_vendor = "apple", target_os = "android", target_os = "redox"))) }, free_unix: { all(unix, not(any(target_vendor = "apple", target_os = "android", target_os = "redox"))) },
kms_platform: { all(feature = "kms", free_unix, not(target_arch = "wasm32")) },
x11_platform: { all(feature = "x11", free_unix, not(target_arch = "wasm32")) }, x11_platform: { all(feature = "x11", free_unix, not(target_arch = "wasm32")) },
wayland_platform: { all(feature = "wayland", free_unix, not(target_arch = "wasm32")) }, wayland_platform: { all(feature = "wayland", free_unix, not(target_arch = "wasm32")) },
} }

220
examples/drm.rs Normal file
View file

@ -0,0 +1,220 @@
//! Example of using softbuffer with drm-rs.
#[cfg(kms_platform)]
mod imple {
use drm::control::{connector, Device as CtrlDevice, Event, ModeTypeFlags, PlaneType};
use drm::Device;
use raw_window_handle::{DrmDisplayHandle, DrmWindowHandle};
use softbuffer::{Context, Surface};
use std::num::NonZeroU32;
use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd};
use std::path::Path;
use std::time::{Duration, Instant};
pub(super) fn entry() -> Result<(), Box<dyn std::error::Error>> {
// Open a new device.
let device = Card::find()?;
// Create the softbuffer context.
let context = unsafe {
Context::from_raw({
let mut handle = DrmDisplayHandle::empty();
handle.fd = device.as_fd().as_raw_fd();
handle.into()
})
}?;
// Get the DRM handles.
let handles = device.resource_handles()?;
// Get the list of connectors and CRTCs.
let connectors = handles
.connectors()
.iter()
.map(|&con| device.get_connector(con, true))
.collect::<Result<Vec<_>, _>>()?;
let crtcs = handles
.crtcs()
.iter()
.map(|&crtc| device.get_crtc(crtc))
.collect::<Result<Vec<_>, _>>()?;
// Find a connected crtc.
let con = connectors
.iter()
.find(|con| con.state() == connector::State::Connected)
.ok_or("No connected connectors")?;
// Get the first CRTC.
let crtc = crtcs.first().ok_or("No CRTCs")?;
// Find a mode to use.
let mode = con
.modes()
.iter()
.find(|mode| mode.mode_type().contains(ModeTypeFlags::PREFERRED))
.or_else(|| con.modes().first())
.ok_or("No modes")?;
// Look for a primary plane compatible with our CRTC.
let planes = device.plane_handles()?;
let planes = planes
.iter()
.filter(|&&plane| {
device.get_plane(plane).map_or(false, |plane| {
let crtcs = handles.filter_crtcs(plane.possible_crtcs());
crtcs.contains(&crtc.handle())
})
})
.collect::<Vec<_>>();
// Find the first primary plane or take the first one period.
let plane = planes
.iter()
.find(|&&&plane| {
if let Ok(props) = device.get_properties(plane) {
let (ids, vals) = props.as_props_and_values();
for (&id, &val) in ids.iter().zip(vals.iter()) {
if let Ok(info) = device.get_property(id) {
if info.name().to_str().map_or(false, |x| x == "type") {
return val == PlaneType::Primary as u32 as u64;
}
}
}
}
false
})
.or(planes.first())
.ok_or("No planes")?;
// Create the surface on top of this plane.
// Note: This requires root on DRM/KMS.
let mut surface = unsafe {
Surface::from_raw(&context, {
let mut handle = DrmWindowHandle::empty();
handle.plane = (**plane).into();
handle.into()
})
}?;
// Resize the surface.
let (width, height) = mode.size();
surface.resize(
NonZeroU32::new(width as u32).unwrap(),
NonZeroU32::new(height as u32).unwrap(),
)?;
// Start drawing to it.
let start = Instant::now();
let mut tick = 0;
while Instant::now().duration_since(start) < Duration::from_secs(2) {
tick += 1;
println!("Drawing tick {tick}");
// Start drawing.
let mut buffer = surface.buffer_mut()?;
draw_to_buffer(&mut buffer, tick);
buffer.present()?;
// Wait for the page flip to happen.
rustix::event::poll(
&mut [rustix::event::PollFd::new(
&device,
rustix::event::PollFlags::IN,
)],
-1,
)?;
// Receive the events.
let events = device.receive_events()?;
println!("Got some events...");
for event in events {
match event {
Event::PageFlip(_) => {
println!("Page flip event.");
}
Event::Vblank(_) => {
println!("Vblank event.");
}
_ => {
println!("Unknown event.");
}
}
}
}
Ok(())
}
fn draw_to_buffer(buf: &mut [u32], tick: usize) {
let scale = colorous::SINEBOW;
let mut i = (tick as f64) / 20.0;
while i > 1.0 {
i -= 1.0;
}
let color = scale.eval_continuous(i);
let pixel = (color.r as u32) << 16 | (color.g as u32) << 8 | (color.b as u32);
buf.fill(pixel);
}
struct Card(std::fs::File);
impl Card {
fn find() -> Result<Card, Box<dyn std::error::Error>> {
for i in 0..10 {
let path = format!("/dev/dri/card{i}");
let device = Card::open(path)?;
// Only use it if it has connectors.
let handles = match device.resource_handles() {
Ok(handles) => handles,
Err(_) => continue,
};
if handles
.connectors
.iter()
.filter_map(|c| device.get_connector(*c, false).ok())
.any(|c| c.state() == connector::State::Connected)
{
return Ok(device);
}
}
Err("No DRM device found".into())
}
fn open(path: impl AsRef<Path>) -> Result<Card, Box<dyn std::error::Error>> {
let file = std::fs::OpenOptions::new()
.read(true)
.write(true)
.open(path)?;
Ok(Card(file))
}
}
impl AsFd for Card {
fn as_fd(&self) -> BorrowedFd<'_> {
self.0.as_fd()
}
}
impl Device for Card {}
impl CtrlDevice for Card {}
}
#[cfg(not(kms_platform))]
mod imple {
pub(super) fn entry() -> Result<(), Box<dyn std::error::Error>> {
eprintln!("This example requires the `kms` feature.");
Ok(())
}
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
imple::entry()
}

409
src/kms.rs Normal file
View file

@ -0,0 +1,409 @@
//! Backend for DRM/KMS for raw rendering directly to the screen.
//!
//! This strategy uses dumb buffers for rendering.
use drm::buffer::{Buffer, DrmFourcc};
use drm::control::dumbbuffer::{DumbBuffer, DumbMapping};
use drm::control::{connector, crtc, framebuffer, plane, Device as CtrlDevice, PageFlipFlags};
use drm::Device;
use raw_window_handle::{DrmDisplayHandle, DrmWindowHandle};
use std::collections::HashSet;
use std::num::NonZeroU32;
use std::os::unix::io::{AsFd, BorrowedFd};
use std::rc::Rc;
use crate::error::{SoftBufferError, SwResultExt};
#[derive(Debug)]
pub(crate) struct KmsDisplayImpl {
/// The underlying raw device file descriptor.
///
/// Once rwh v0.6 support is merged, this an be made safe. Until then,
/// we use this hacky workaround, since this FD's validity is guaranteed by
/// the unsafe constructor.
fd: BorrowedFd<'static>,
}
impl AsFd for KmsDisplayImpl {
fn as_fd(&self) -> BorrowedFd<'_> {
self.fd
}
}
impl Device for KmsDisplayImpl {}
impl CtrlDevice for KmsDisplayImpl {}
impl KmsDisplayImpl {
/// SAFETY: The underlying fd must not outlive the display.
pub(crate) unsafe fn new(handle: DrmDisplayHandle) -> Result<KmsDisplayImpl, SoftBufferError> {
let fd = handle.fd;
if fd == -1 {
return Err(SoftBufferError::IncompleteDisplayHandle);
}
// SAFETY: Invariants guaranteed by the user.
let fd = unsafe { BorrowedFd::borrow_raw(fd) };
Ok(KmsDisplayImpl { fd })
}
}
/// All the necessary types for the Drm/Kms backend.
#[derive(Debug)]
pub(crate) struct KmsImpl {
/// The display implementation.
display: Rc<KmsDisplayImpl>,
/// The connectors to use.
connectors: Vec<connector::Handle>,
/// The CRTC to render to.
crtc: crtc::Info,
/// The dumb buffer we're using as a buffer.
buffer: Option<Buffers>,
}
#[derive(Debug)]
struct Buffers {
/// The involved set of buffers.
buffers: [SharedBuffer; 2],
/// Whether to use the first buffer or the second buffer as the front buffer.
first_is_front: bool,
/// A buffer full of zeroes.
zeroes: Box<[u32]>,
}
/// The buffer implementation.
pub(crate) struct BufferImpl<'a> {
/// The mapping of the dump buffer.
mapping: DumbMapping<'a>,
/// The framebuffer object of the current front buffer.
front_fb: framebuffer::Handle,
/// The CRTC handle.
crtc_handle: crtc::Handle,
/// This is used to change the front buffer.
first_is_front: &'a mut bool,
/// Buffer full of zeroes.
zeroes: &'a [u32],
/// The current size.
size: (NonZeroU32, NonZeroU32),
/// The display implementation.
display: &'a KmsDisplayImpl,
/// Age of the front buffer.
front_age: &'a mut u8,
/// Age of the back buffer.
back_age: &'a mut u8,
}
/// The combined frame buffer and dumb buffer.
#[derive(Debug)]
struct SharedBuffer {
/// The frame buffer.
fb: framebuffer::Handle,
/// The dumb buffer.
db: DumbBuffer,
/// The age of this buffer.
age: u8,
}
impl KmsImpl {
/// Create a new KMS backend.
///
/// # Safety
///
/// The plane must be valid for the lifetime of the backend.
pub(crate) unsafe fn new(
window_handle: DrmWindowHandle,
display: Rc<KmsDisplayImpl>,
) -> Result<Self, SoftBufferError> {
log::trace!("new: window_handle={:X}", window_handle.plane);
// Make sure that the window handle is valid.
let plane_handle = match NonZeroU32::new(window_handle.plane) {
Some(handle) => plane::Handle::from(handle),
None => return Err(SoftBufferError::IncompleteWindowHandle),
};
let plane_info = display
.get_plane(plane_handle)
.swbuf_err("failed to get plane info")?;
let handles = display
.resource_handles()
.swbuf_err("failed to get resource handles")?;
// Use either the attached CRTC or the primary CRTC.
let crtc = {
let handle = match plane_info.crtc() {
Some(crtc) => crtc,
None => {
log::warn!("no CRTC attached to plane, falling back to primary CRTC");
handles
.filter_crtcs(plane_info.possible_crtcs())
.first()
.copied()
.swbuf_err("failed to find a primary CRTC")?
}
};
// Get info about the CRTC.
display
.get_crtc(handle)
.swbuf_err("failed to get CRTC info")?
};
// Figure out all of the encoders that are attached to this CRTC.
let encoders = handles
.encoders
.iter()
.flat_map(|handle| display.get_encoder(*handle))
.filter(|encoder| encoder.crtc() == Some(crtc.handle()))
.map(|encoder| encoder.handle())
.collect::<HashSet<_>>();
// Get a list of every connector that the CRTC is connected to via encoders.
let connectors = handles
.connectors
.iter()
.flat_map(|handle| display.get_connector(*handle, false))
.filter(|connector| {
connector
.current_encoder()
.map_or(false, |encoder| encoders.contains(&encoder))
})
.map(|info| info.handle())
.collect::<Vec<_>>();
Ok(Self {
crtc,
connectors,
display,
buffer: None,
})
}
/// Resize the internal buffer to the given size.
pub(crate) fn resize(
&mut self,
width: NonZeroU32,
height: NonZeroU32,
) -> Result<(), SoftBufferError> {
// Don't resize if we don't have to.
if let Some(buffer) = &self.buffer {
let (buffer_width, buffer_height) = buffer.size();
if buffer_width == width && buffer_height == height {
return Ok(());
}
}
// Create a new buffer set.
let front_buffer = SharedBuffer::new(&self.display, width, height)?;
let back_buffer = SharedBuffer::new(&self.display, width, height)?;
self.buffer = Some(Buffers {
first_is_front: true,
buffers: [front_buffer, back_buffer],
zeroes: vec![0; width.get() as usize * height.get() as usize].into_boxed_slice(),
});
Ok(())
}
/// Fetch the buffer from the window.
pub(crate) fn fetch(&mut self) -> Result<Vec<u32>, SoftBufferError> {
// TODO: Implement this!
Err(SoftBufferError::Unimplemented)
}
/// Get a mutable reference to the buffer.
pub(crate) fn buffer_mut(&mut self) -> Result<BufferImpl<'_>, SoftBufferError> {
// Map the dumb buffer.
let set = self
.buffer
.as_mut()
.expect("Must set size of surface before calling `buffer_mut()`");
let size = set.size();
let [first_buffer, second_buffer] = &mut set.buffers;
let (front_buffer, back_buffer) = if set.first_is_front {
(first_buffer, second_buffer)
} else {
(second_buffer, first_buffer)
};
let front_fb = front_buffer.fb;
let front_age = &mut front_buffer.age;
let back_age = &mut back_buffer.age;
let mapping = self
.display
.map_dumb_buffer(&mut front_buffer.db)
.swbuf_err("failed to map dumb buffer")?;
Ok(BufferImpl {
mapping,
size,
first_is_front: &mut set.first_is_front,
front_fb,
crtc_handle: self.crtc.handle(),
display: &self.display,
zeroes: &set.zeroes,
front_age,
back_age,
})
}
}
impl Drop for KmsImpl {
fn drop(&mut self) {
// Map the CRTC to the information that was there before.
self.display
.set_crtc(
self.crtc.handle(),
self.crtc.framebuffer(),
self.crtc.position(),
&self.connectors,
self.crtc.mode(),
)
.ok();
}
}
impl BufferImpl<'_> {
#[inline]
pub fn pixels(&self) -> &[u32] {
// drm-rs doesn't let us have the immutable reference... so just use a bunch of zeroes.
// TODO: There has to be a better way of doing this!
self.zeroes
}
#[inline]
pub fn pixels_mut(&mut self) -> &mut [u32] {
bytemuck::cast_slice_mut(self.mapping.as_mut())
}
#[inline]
pub fn age(&self) -> u8 {
*self.front_age
}
#[inline]
pub fn present_with_damage(self, damage: &[crate::Rect]) -> Result<(), SoftBufferError> {
let rectangles = damage
.iter()
.map(|&rect| {
let err = || SoftBufferError::DamageOutOfRange { rect };
Ok(drm_sys::drm_clip_rect {
x1: rect.x.try_into().map_err(|_| err())?,
y1: rect.y.try_into().map_err(|_| err())?,
x2: rect
.x
.checked_add(rect.width.get())
.and_then(|x| x.try_into().ok())
.ok_or_else(err)?,
y2: rect
.y
.checked_add(rect.height.get())
.and_then(|y| y.try_into().ok())
.ok_or_else(err)?,
})
})
.collect::<Result<Vec<_>, _>>()?;
// Dirty the framebuffer with out damage rectangles.
//
// Some drivers don't support this, so we just ignore the `ENOSYS` error.
// TODO: It would be nice to not have to heap-allocate the above rectangles if we know that
// this is going to fail. Low hanging fruit PR: add a flag that's set to false if this
// returns `ENOSYS` and check that before allocating the above and running this.
match self.display.dirty_framebuffer(self.front_fb, &rectangles) {
Ok(())
| Err(drm::SystemError::Unknown {
errno: nix::errno::Errno::ENOSYS,
}) => {}
Err(e) => {
return Err(SoftBufferError::PlatformError(
Some("failed to dirty framebuffer".into()),
Some(e.into()),
));
}
}
// Swap the buffers.
// TODO: Use atomic commits here!
self.display
.page_flip(self.crtc_handle, self.front_fb, PageFlipFlags::EVENT, None)
.swbuf_err("failed to page flip")?;
// Flip the front and back buffers.
*self.first_is_front = !*self.first_is_front;
// Set the ages.
*self.front_age = 1;
if *self.back_age != 0 {
*self.back_age += 1;
}
Ok(())
}
#[inline]
pub fn present(self) -> Result<(), SoftBufferError> {
let (width, height) = self.size;
self.present_with_damage(&[crate::Rect {
x: 0,
y: 0,
width,
height,
}])
}
}
impl SharedBuffer {
/// Create a new buffer set.
pub(crate) fn new(
display: &KmsDisplayImpl,
width: NonZeroU32,
height: NonZeroU32,
) -> Result<Self, SoftBufferError> {
let db = display
.create_dumb_buffer((width.get(), height.get()), DrmFourcc::Xrgb8888, 32)
.swbuf_err("failed to create dumb buffer")?;
let fb = display
.add_framebuffer(&db, 24, 32)
.swbuf_err("failed to add framebuffer")?;
Ok(SharedBuffer { fb, db, age: 0 })
}
/// Get the size of this buffer.
pub(crate) fn size(&self) -> (NonZeroU32, NonZeroU32) {
let (width, height) = self.db.size();
NonZeroU32::new(width)
.and_then(|width| NonZeroU32::new(height).map(|height| (width, height)))
.expect("buffer size is zero")
}
}
impl Buffers {
/// Get the size of this buffer.
pub(crate) fn size(&self) -> (NonZeroU32, NonZeroU32) {
self.buffers[0].size()
}
}

View file

@ -10,6 +10,8 @@ extern crate core;
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
mod cg; mod cg;
#[cfg(kms_platform)]
mod kms;
#[cfg(target_os = "redox")] #[cfg(target_os = "redox")]
mod orbital; mod orbital;
#[cfg(wayland_platform)] #[cfg(wayland_platform)]
@ -27,7 +29,7 @@ mod util;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::num::NonZeroU32; use std::num::NonZeroU32;
use std::ops; use std::ops;
#[cfg(any(wayland_platform, x11_platform))] #[cfg(any(wayland_platform, x11_platform, kms_platform))]
use std::rc::Rc; use std::rc::Rc;
pub use error::SoftBufferError; pub use error::SoftBufferError;
@ -73,6 +75,7 @@ macro_rules! make_dispatch {
} }
} }
#[allow(clippy::large_enum_variant)] // it's boxed anyways
enum SurfaceDispatch { enum SurfaceDispatch {
$( $(
$(#[$attr])* $(#[$attr])*
@ -174,6 +177,8 @@ make_dispatch! {
X11(Rc<x11::X11DisplayImpl>, x11::X11Impl, x11::BufferImpl<'a>), X11(Rc<x11::X11DisplayImpl>, x11::X11Impl, x11::BufferImpl<'a>),
#[cfg(wayland_platform)] #[cfg(wayland_platform)]
Wayland(Rc<wayland::WaylandDisplayImpl>, wayland::WaylandImpl, wayland::BufferImpl<'a>), Wayland(Rc<wayland::WaylandDisplayImpl>, wayland::WaylandImpl, wayland::BufferImpl<'a>),
#[cfg(kms_platform)]
Kms(Rc<kms::KmsDisplayImpl>, kms::KmsImpl, kms::BufferImpl<'a>),
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
Win32((), win32::Win32Impl, win32::BufferImpl<'a>), Win32((), win32::Win32Impl, win32::BufferImpl<'a>),
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
@ -213,6 +218,10 @@ impl Context {
RawDisplayHandle::Wayland(wayland_handle) => unsafe { RawDisplayHandle::Wayland(wayland_handle) => unsafe {
ContextDispatch::Wayland(Rc::new(wayland::WaylandDisplayImpl::new(wayland_handle)?)) ContextDispatch::Wayland(Rc::new(wayland::WaylandDisplayImpl::new(wayland_handle)?))
}, },
#[cfg(kms_platform)]
RawDisplayHandle::Drm(drm_handle) => unsafe {
ContextDispatch::Kms(Rc::new(kms::KmsDisplayImpl::new(drm_handle)?))
},
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
RawDisplayHandle::Windows(_) => ContextDispatch::Win32(()), RawDisplayHandle::Windows(_) => ContextDispatch::Win32(()),
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
@ -303,6 +312,12 @@ impl Surface {
) => SurfaceDispatch::Wayland(unsafe { ) => SurfaceDispatch::Wayland(unsafe {
wayland::WaylandImpl::new(wayland_window_handle, wayland_display_impl.clone())? wayland::WaylandImpl::new(wayland_window_handle, wayland_display_impl.clone())?
}), }),
#[cfg(kms_platform)]
(ContextDispatch::Kms(kms_display_impl), RawWindowHandle::Drm(drm_window_handle)) => {
SurfaceDispatch::Kms(unsafe {
kms::KmsImpl::new(drm_window_handle, kms_display_impl.clone())?
})
}
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
(ContextDispatch::Win32(()), RawWindowHandle::Win32(win32_handle)) => { (ContextDispatch::Win32(()), RawWindowHandle::Win32(win32_handle)) => {
SurfaceDispatch::Win32(unsafe { win32::Win32Impl::new(&win32_handle)? }) SurfaceDispatch::Win32(unsafe { win32::Win32Impl::new(&win32_handle)? })
@ -361,6 +376,12 @@ impl Surface {
/// Return a [`Buffer`] that the next frame should be rendered into. The size must /// Return a [`Buffer`] that the next frame should be rendered into. The size must
/// be set with [`Surface::resize`] first. The initial contents of the buffer may be zeroed, or /// be set with [`Surface::resize`] first. The initial contents of the buffer may be zeroed, or
/// may contain a previous frame. Call [`Buffer::age`] to determine this. /// may contain a previous frame. Call [`Buffer::age`] to determine this.
///
/// ## Platform Dependent Behavior
///
/// - On DRM/KMS, there is no reliable and sound way to wait for the page flip to happen from within
/// `softbuffer`. Therefore it is the responsibility of the user to wait for the page flip before
/// sending another frame.
pub fn buffer_mut(&mut self) -> Result<Buffer, SoftBufferError> { pub fn buffer_mut(&mut self) -> Result<Buffer, SoftBufferError> {
Ok(Buffer { Ok(Buffer {
buffer_impl: self.surface_impl.buffer_mut()?, buffer_impl: self.surface_impl.buffer_mut()?,