From b732491fb0d2a85f1e6b480fa9cdc50dcc68688e Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Thu, 7 Mar 2024 18:49:46 -0800 Subject: [PATCH] WIP linux-drm-syncobj-v1 --- Cargo.toml | 22 +-- anvil/Cargo.toml | 2 +- anvil/src/state.rs | 2 + src/wayland/drm_syncobj/mod.rs | 239 +++++++++++++++++++++++++++++++++ src/wayland/mod.rs | 2 + 5 files changed, 255 insertions(+), 12 deletions(-) create mode 100644 src/wayland/drm_syncobj/mod.rs diff --git a/Cargo.toml b/Cargo.toml index 064e578e8651..20ff61060310 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -70,15 +70,15 @@ smallvec = "1.11" pixman = { version = "0.1.0", features = ["drm-fourcc"], optional = true } [patch.crates-io] -wayland-egl = { git = "https://github.com/PolyMeilex/wayland-rs.git", branch = "new-delegate-v2"} -wayland-protocols = { git = "https://github.com/PolyMeilex/wayland-rs.git", branch = "new-delegate-v2" } -wayland-protocols-wlr = { git = "https://github.com/PolyMeilex/wayland-rs.git", branch = "new-delegate-v2" } -wayland-protocols-misc = { git = "https://github.com/PolyMeilex/wayland-rs.git", branch = "new-delegate-v2" } -wayland-server = { git = "https://github.com/PolyMeilex/wayland-rs.git", branch = "new-delegate-v2" } -wayland-client = { git = "https://github.com/PolyMeilex/wayland-rs.git", branch = "new-delegate-v2" } -wayland-sys = { git = "https://github.com/PolyMeilex/wayland-rs.git", branch = "new-delegate-v2" } -wayland-backend = { git = "https://github.com/PolyMeilex/wayland-rs.git", branch = "new-delegate-v2" } -wayland-scanner = { git = "https://github.com/PolyMeilex/wayland-rs.git", branch = "new-delegate-v2" } +wayland-egl = { git = "https://github.com/ids1024/wayland-rs", branch = "drm-syncobj" } +wayland-protocols = { git = "https://github.com/ids1024/wayland-rs", branch = "drm-syncobj" } +wayland-protocols-wlr = { git = "https://github.com/ids1024/wayland-rs", branch = "drm-syncobj" } +wayland-protocols-misc = { git = "https://github.com/ids1024/wayland-rs", branch = "drm-syncobj" } +wayland-server = { git = "https://github.com/ids1024/wayland-rs", branch = "drm-syncobj" } +wayland-client = { git = "https://github.com/ids1024/wayland-rs", branch = "drm-syncobj" } +wayland-sys = { git = "https://github.com/ids1024/wayland-rs", branch = "drm-syncobj" } +wayland-backend = { git = "https://github.com/ids1024/wayland-rs", branch = "drm-syncobj" } +wayland-scanner = { git = "https://github.com/ids1024/wayland-rs", branch = "drm-syncobj" } # wayland-egl = { path = "../wayland-rs/wayland-egl/" } # wayland-protocols = { path = "../wayland-rs/wayland-protocols/" } # wayland-protocols-wlr = { path = "../wayland-rs/wayland-protocols-wlr/" } @@ -102,7 +102,7 @@ pkg-config = { version = "0.3.17", optional = true } cc = { version = "1.0.79", optional = true } [features] -default = ["backend_drm", "backend_gbm", "backend_libinput", "backend_udev", "backend_session_libseat", "backend_x11", "backend_winit", "desktop", "renderer_gl", "renderer_pixman", "renderer_multi", "xwayland", "wayland_frontend", "backend_vulkan"] +default = ["backend_drm", "backend_gbm", "backend_libinput", "backend_udev", "backend_session_libseat", "backend_x11", "desktop", "renderer_gl", "renderer_pixman", "renderer_multi", "xwayland", "wayland_frontend", "backend_vulkan"] # XXX backend_winit = ["winit", "backend_egl", "wayland-egl", "renderer_gl"] backend_x11 = ["x11rb", "x11rb/dri3", "x11rb/xfixes", "x11rb/present", "x11rb_event_source", "backend_gbm", "backend_drm", "backend_egl"] backend_drm = ["drm", "drm-ffi"] @@ -154,4 +154,4 @@ harness = false [profile.release-with-debug] inherits = "release" -debug = true \ No newline at end of file +debug = true diff --git a/anvil/Cargo.toml b/anvil/Cargo.toml index 7b9713831d23..a92f31a00d50 100644 --- a/anvil/Cargo.toml +++ b/anvil/Cargo.toml @@ -37,7 +37,7 @@ gl_generator = "0.14" [features] debug = ["fps_ticker", "image/png", "renderdoc"] -default = ["egl", "winit", "x11", "udev", "xwayland"] +default = ["egl", "x11", "udev", "xwayland"] # XXX egl = ["smithay/use_system_lib", "smithay/backend_egl"] test_all_features = ["default", "debug"] udev = [ diff --git a/anvil/src/state.rs b/anvil/src/state.rs index ba8d7b711acf..8449b1810240 100644 --- a/anvil/src/state.rs +++ b/anvil/src/state.rs @@ -39,6 +39,7 @@ use smithay::{ wayland::{ compositor::{get_parent, with_states, CompositorClientState, CompositorState}, dmabuf::DmabufFeedback, + drm_syncobj::DrmSyncobjState, fractional_scale::{with_fractional_scale, FractionalScaleHandler, FractionalScaleManagerState}, input_method::{InputMethodHandler, InputMethodManagerState, PopupSurface}, keyboard_shortcuts_inhibit::{ @@ -547,6 +548,7 @@ impl AnvilState { .get_data::() .map_or(true, |client_state| client_state.security_context.is_none()) }); + DrmSyncobjState::new::(&dh); // init input let seat_name = backend_data.seat_name(); diff --git a/src/wayland/drm_syncobj/mod.rs b/src/wayland/drm_syncobj/mod.rs new file mode 100644 index 000000000000..e8d087313f57 --- /dev/null +++ b/src/wayland/drm_syncobj/mod.rs @@ -0,0 +1,239 @@ +use std::{cell::RefCell, os::unix::io::OwnedFd, sync::Arc}; +use wayland_protocols::wp::linux_drm_syncobj::v1::server::{ + wp_linux_drm_syncobj_manager_v1::{self, WpLinuxDrmSyncobjManagerV1}, + wp_linux_drm_syncobj_surface_v1::{self, WpLinuxDrmSyncobjSurfaceV1}, + wp_linux_drm_syncobj_timeline_v1::{self, WpLinuxDrmSyncobjTimelineV1}, +}; +use wayland_server::{ + protocol::wl_surface::WlSurface, Client, DataInit, Dispatch, DisplayHandle, GlobalDispatch, New, + Resource, Weak, +}; + +use super::compositor::{self, with_states, BufferAssignment, Cacheable, SurfaceAttributes}; + +#[derive(Clone)] +struct SyncPoint { + fd: Arc, + point: u64, +} + +#[derive(Default)] +struct DrmSyncobjCachedState { + acquire_point: Option, + release_point: Option, +} + +impl Cacheable for DrmSyncobjCachedState { + fn commit(&mut self, _dh: &DisplayHandle) -> Self { + Self { + acquire_point: None, + release_point: None, + } + } + + fn merge_into(self, into: &mut Self, _dh: &DisplayHandle) { + if self.acquire_point.is_some() && self.release_point.is_some() { + into.acquire_point = self.acquire_point; + into.release_point = self.release_point; + } else { + into.acquire_point = None; + into.release_point = None; + } + } +} + +pub struct DrmSyncobjState {} + +impl DrmSyncobjState { + pub fn new(display: &DisplayHandle) -> Self { + display.create_delegated_global::(1, ()); + Self {} + } + + // TODO new_with_filter +} + +impl GlobalDispatch for DrmSyncobjState { + fn bind( + state: &mut D, + dh: &DisplayHandle, + client: &Client, + resource: New, + _global_data: &(), + data_init: &mut DataInit<'_, D>, + ) { + data_init.init_delegated::<_, _, Self>(resource, ()); + } +} + +fn commit_hook(_: &mut D, _dh: &DisplayHandle, surface: &WlSurface) { + compositor::with_states(&surface, |states| { + let cached = &states.cached_state; + let has_new_buffer = matches!( + cached.pending::().buffer, + Some(BufferAssignment::NewBuffer(_)) + ); + // TODO what if syncobj surface is destroyed? + if let Some(data) = states + .data_map + .get::>>() + { + if let Some(syncobj_surface) = data.borrow().as_ref() { + let pending = cached.pending::(); + if pending.acquire_point.is_some() && !has_new_buffer { + syncobj_surface.post_error( + wp_linux_drm_syncobj_surface_v1::Error::NoBuffer as u32, + "acquire point without buffer".to_string(), + ); + } else if pending.acquire_point.is_some() && pending.release_point.is_none() { + syncobj_surface.post_error( + wp_linux_drm_syncobj_surface_v1::Error::NoReleasePoint as u32, + "acquire point without release point".to_string(), + ); + } else if pending.acquire_point.is_none() && pending.release_point.is_some() { + syncobj_surface.post_error( + wp_linux_drm_syncobj_surface_v1::Error::NoAcquirePoint as u32, + "release point without acquire point".to_string(), + ); + } else if let (Some(acquire), Some(release)) = + (pending.acquire_point.as_ref(), pending.release_point.as_ref()) + { + if Arc::ptr_eq(&acquire.fd, &release.fd) && acquire.point <= release.point { + syncobj_surface.post_error( + wp_linux_drm_syncobj_surface_v1::Error::ConflictingPoints as u32, + format!( + "release point '{}' is not greater than acquire point '{}'", + release.point, acquire.point + ), + ); + } + } + // TODO unsupported buffer error + } + } + }); +} + +impl Dispatch for DrmSyncobjState { + fn request( + state: &mut D, + _client: &Client, + resource: &WpLinuxDrmSyncobjManagerV1, + request: wp_linux_drm_syncobj_manager_v1::Request, + _data: &(), + _dh: &DisplayHandle, + data_init: &mut DataInit<'_, D>, + ) { + match request { + wp_linux_drm_syncobj_manager_v1::Request::GetSurface { id, surface } => { + let already_exists = with_states(&surface, |states| { + states + .data_map + .get::>>() + .map(|v| v.borrow().is_some()) + .unwrap_or(false) + }); + if already_exists { + resource.post_error( + wp_linux_drm_syncobj_manager_v1::Error::SurfaceExists as u32, + "the surface already has a syncobj_surface object associated".to_string(), + ); + return; + } + let syncobj_surface = data_init.init_delegated::<_, _, Self>( + id, + DrmSyncobjSurfaceData { + surface: surface.downgrade(), + }, + ); + with_states(&surface, |states| { + states + .data_map + .insert_if_missing(|| RefCell::new(Some(syncobj_surface))) + }); + compositor::add_pre_commit_hook::(&surface, commit_hook); + } + wp_linux_drm_syncobj_manager_v1::Request::ImportTimeline { id, fd } => { + data_init.init_delegated::<_, _, Self>(id, DrmSyncobjTimelineData { fd: Arc::new(fd) }); + // TODO import, protocol error if it fails? On which GPU? + } + wp_linux_drm_syncobj_manager_v1::Request::Destroy => {} + _ => unreachable!(), + } + } +} + +struct DrmSyncobjSurfaceData { + surface: Weak, +} + +impl Dispatch for DrmSyncobjState { + fn request( + state: &mut D, + _client: &Client, + _resource: &WpLinuxDrmSyncobjSurfaceV1, + request: wp_linux_drm_syncobj_surface_v1::Request, + data: &DrmSyncobjSurfaceData, + _dh: &DisplayHandle, + data_init: &mut DataInit<'_, D>, + ) { + let Ok(surface) = data.surface.upgrade() else { + return; + }; + match request { + wp_linux_drm_syncobj_surface_v1::Request::Destroy => { + // TODO + } + wp_linux_drm_syncobj_surface_v1::Request::SetAcquirePoint { + timeline, + point_hi, + point_lo, + } => { + let sync_point = SyncPoint { + fd: timeline.data::().unwrap().fd.clone(), + point: ((point_hi as u64) << 32) + (point_lo as u64), + }; + with_states(&surface, |states| { + let mut cached_state = states.cached_state.pending::(); + cached_state.acquire_point = Some(sync_point); + }); + } + wp_linux_drm_syncobj_surface_v1::Request::SetReleasePoint { + timeline, + point_hi, + point_lo, + } => { + let sync_point = SyncPoint { + fd: timeline.data::().unwrap().fd.clone(), + point: ((point_hi as u64) << 32) + (point_lo as u64), + }; + with_states(&surface, |states| { + let mut cached_state = states.cached_state.pending::(); + cached_state.release_point = Some(sync_point); + }); + } + _ => unreachable!(), + } + } +} + +struct DrmSyncobjTimelineData { + fd: Arc, +} + +impl Dispatch for DrmSyncobjState { + fn request( + state: &mut D, + _client: &Client, + _resource: &WpLinuxDrmSyncobjTimelineV1, + request: wp_linux_drm_syncobj_timeline_v1::Request, + _data: &DrmSyncobjTimelineData, + _dh: &DisplayHandle, + data_init: &mut DataInit<'_, D>, + ) { + match request { + wp_linux_drm_syncobj_timeline_v1::Request::Destroy => {} + _ => unreachable!(), + } + } +} diff --git a/src/wayland/mod.rs b/src/wayland/mod.rs index 871f637f8c51..64e4209dc977 100644 --- a/src/wayland/mod.rs +++ b/src/wayland/mod.rs @@ -52,6 +52,8 @@ pub mod cursor_shape; pub mod dmabuf; #[cfg(feature = "backend_drm")] pub mod drm_lease; +#[cfg(feature = "backend_drm")] +pub mod drm_syncobj; pub mod fractional_scale; pub mod idle_inhibit; pub mod idle_notify;