From 9ceec8f4a0fa2f68e8a5cbe9c52cf5dac0f90978 Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Tue, 6 Feb 2024 16:27:12 +0100 Subject: [PATCH] wayland: implement zwp_linux_dmabuf v4 --- src/compositor.rs | 2 + src/drm_feedback.rs | 58 +++++++++++ src/ifs.rs | 1 + src/ifs/zwp_linux_dmabuf_feedback_v1.rs | 124 ++++++++++++++++++++++++ src/ifs/zwp_linux_dmabuf_v1.rs | 72 +++++++++++--- src/main.rs | 1 + src/state.rs | 26 ++++- 7 files changed, 268 insertions(+), 16 deletions(-) create mode 100644 src/drm_feedback.rs create mode 100644 src/ifs/zwp_linux_dmabuf_feedback_v1.rs diff --git a/src/compositor.rs b/src/compositor.rs index d201ba7d..190b18f8 100644 --- a/src/compositor.rs +++ b/src/compositor.rs @@ -127,6 +127,8 @@ fn start_compositor2( default_keymap: xkb_keymap, eng: engine.clone(), render_ctx: Default::default(), + drm_feedback: Default::default(), + drm_feedback_consumers: Default::default(), render_ctx_version: NumCell::new(1), render_ctx_ever_initialized: Cell::new(false), cursors: Default::default(), diff --git a/src/drm_feedback.rs b/src/drm_feedback.rs new file mode 100644 index 00000000..4f54a608 --- /dev/null +++ b/src/drm_feedback.rs @@ -0,0 +1,58 @@ +use { + crate::{gfx_api::GfxContext, utils::oserror::OsError}, + byteorder::{NativeEndian, WriteBytesExt}, + std::{io::Write, rc::Rc}, + thiserror::Error, + uapi::{c, OwnedFd}, +}; + +pub struct DrmFeedback { + pub fd: Rc, + pub size: usize, + pub indices: Vec, + pub main_device: c::dev_t, +} + +impl DrmFeedback { + pub fn new(ctx: &dyn GfxContext) -> Result { + let dev_t = uapi::fstat(ctx.gbm().drm.raw()) + .map_err(OsError::from)? + .st_rdev; + let data = create_fd_data(ctx); + let mut memfd = + uapi::memfd_create("drm_feedback", c::MFD_CLOEXEC | c::MFD_ALLOW_SEALING).unwrap(); + memfd.write_all(&data).unwrap(); + uapi::lseek(memfd.raw(), 0, c::SEEK_SET).unwrap(); + uapi::fcntl_add_seals( + memfd.raw(), + c::F_SEAL_SEAL | c::F_SEAL_GROW | c::F_SEAL_SHRINK | c::F_SEAL_WRITE, + ) + .unwrap(); + let num_indices = data.len() / 16; + let indices = (0..num_indices).map(|v| v as u16).collect(); + Ok(Self { + fd: Rc::new(memfd), + size: data.len(), + indices, + main_device: dev_t, + }) + } +} + +fn create_fd_data(ctx: &dyn GfxContext) -> Vec { + let mut vec = vec![]; + for (format, info) in &*ctx.formats() { + for modifier in info.modifiers.keys() { + vec.write_u32::(*format).unwrap(); + vec.write_u32::(0).unwrap(); + vec.write_u64::(*modifier).unwrap(); + } + } + vec +} + +#[derive(Debug, Error)] +pub enum DrmFeedbackError { + #[error("Could not stat drm device")] + Stat(#[from] OsError), +} diff --git a/src/ifs.rs b/src/ifs.rs index ac406a85..2418eddf 100644 --- a/src/ifs.rs +++ b/src/ifs.rs @@ -40,6 +40,7 @@ pub mod zwlr_screencopy_frame_v1; pub mod zwlr_screencopy_manager_v1; pub mod zwp_idle_inhibit_manager_v1; pub mod zwp_linux_buffer_params_v1; +pub mod zwp_linux_dmabuf_feedback_v1; pub mod zwp_linux_dmabuf_v1; pub mod zxdg_decoration_manager_v1; pub mod zxdg_output_manager_v1; diff --git a/src/ifs/zwp_linux_dmabuf_feedback_v1.rs b/src/ifs/zwp_linux_dmabuf_feedback_v1.rs new file mode 100644 index 00000000..37e3ce61 --- /dev/null +++ b/src/ifs/zwp_linux_dmabuf_feedback_v1.rs @@ -0,0 +1,124 @@ +use { + crate::{ + client::{Client, ClientError}, + drm_feedback::DrmFeedback, + leaks::Tracker, + object::Object, + utils::buffd::{MsgParser, MsgParserError}, + wire::{zwp_linux_dmabuf_feedback_v1::*, ZwpLinuxDmabufFeedbackV1Id}, + }, + std::rc::Rc, + thiserror::Error, + uapi::{c, OwnedFd}, +}; + +#[allow(dead_code)] +pub const SCANOUT: u32 = 1; + +pub struct ZwpLinuxDmabufFeedbackV1 { + pub id: ZwpLinuxDmabufFeedbackV1Id, + pub client: Rc, + pub tracker: Tracker, +} + +impl ZwpLinuxDmabufFeedbackV1 { + pub fn new(id: ZwpLinuxDmabufFeedbackV1Id, client: &Rc) -> Self { + Self { + id, + client: client.clone(), + tracker: Default::default(), + } + } + + pub fn send_feedback(&self, feedback: &DrmFeedback) { + self.send_format_table(&feedback.fd, feedback.size); + self.send_main_device(feedback.main_device); + self.send_tranche_target_device(feedback.main_device); + self.send_tranche_formats(&feedback.indices); + self.send_tranche_flags(0); + self.send_tranche_done(); + self.send_done(); + } + + fn send_done(&self) { + self.client.event(Done { self_id: self.id }); + } + + fn send_format_table(&self, fd: &Rc, size: usize) { + self.client.event(FormatTable { + self_id: self.id, + fd: fd.clone(), + size: size as _, + }); + } + + fn send_main_device(&self, dev: c::dev_t) { + self.client.event(MainDevice { + self_id: self.id, + device: dev, + }); + } + + fn send_tranche_done(&self) { + self.client.event(TrancheDone { self_id: self.id }); + } + + fn send_tranche_target_device(&self, dev: c::dev_t) { + self.client.event(TrancheTargetDevice { + self_id: self.id, + device: dev, + }); + } + + fn send_tranche_formats(&self, indices: &[u16]) { + self.client.event(TrancheFormats { + self_id: self.id, + indices, + }); + } + + fn send_tranche_flags(&self, flags: u32) { + self.client.event(TrancheFlags { + self_id: self.id, + flags, + }); + } + + fn destroy(&self, parser: MsgParser<'_, '_>) -> Result<(), ZwpLinuxDmabufFeedbackV1Error> { + let _req: Destroy = self.client.parse(self, parser)?; + self.detach(); + self.client.remove_obj(self)?; + Ok(()) + } + + fn detach(&self) { + self.client + .state + .drm_feedback_consumers + .remove(&(self.client.id, self.id)); + } +} + +object_base! { + self = ZwpLinuxDmabufFeedbackV1; + + DESTROY => destroy, +} + +impl Object for ZwpLinuxDmabufFeedbackV1 { + fn break_loops(&self) { + self.detach(); + } +} + +simple_add_obj!(ZwpLinuxDmabufFeedbackV1); + +#[derive(Debug, Error)] +pub enum ZwpLinuxDmabufFeedbackV1Error { + #[error(transparent)] + ClientError(Box), + #[error("Parsing failed")] + MsgParserError(#[source] Box), +} +efrom!(ZwpLinuxDmabufFeedbackV1Error, ClientError); +efrom!(ZwpLinuxDmabufFeedbackV1Error, MsgParserError); diff --git a/src/ifs/zwp_linux_dmabuf_v1.rs b/src/ifs/zwp_linux_dmabuf_v1.rs index b449a386..3b506cf0 100644 --- a/src/ifs/zwp_linux_dmabuf_v1.rs +++ b/src/ifs/zwp_linux_dmabuf_v1.rs @@ -2,11 +2,14 @@ use { crate::{ client::{Client, ClientError}, globals::{Global, GlobalName}, - ifs::zwp_linux_buffer_params_v1::ZwpLinuxBufferParamsV1, + ifs::{ + zwp_linux_buffer_params_v1::ZwpLinuxBufferParamsV1, + zwp_linux_dmabuf_feedback_v1::ZwpLinuxDmabufFeedbackV1, + }, leaks::Tracker, object::Object, utils::buffd::{MsgParser, MsgParserError}, - wire::{zwp_linux_dmabuf_v1::*, ZwpLinuxDmabufV1Id}, + wire::{zwp_linux_dmabuf_v1::*, ZwpLinuxDmabufFeedbackV1Id, ZwpLinuxDmabufV1Id}, }, std::rc::Rc, thiserror::Error, @@ -35,19 +38,21 @@ impl ZwpLinuxDmabufV1Global { }); track!(client, obj); client.add_client_obj(&obj)?; - if let Some(ctx) = client.state.render_ctx.get() { - let formats = ctx.formats(); - for format in formats.values() { - if format.implicit_external_only && !ctx.supports_external_texture() { - continue; - } - obj.send_format(format.format.drm); - if version >= MODIFIERS_SINCE_VERSION { - for modifier in format.modifiers.values() { - if modifier.external_only && !ctx.supports_external_texture() { - continue; + if version < FEEDBACK_SINCE_VERSION { + if let Some(ctx) = client.state.render_ctx.get() { + let formats = ctx.formats(); + for format in formats.values() { + if format.implicit_external_only && !ctx.supports_external_texture() { + continue; + } + obj.send_format(format.format.drm); + if version >= MODIFIERS_SINCE_VERSION { + for modifier in format.modifiers.values() { + if modifier.external_only && !ctx.supports_external_texture() { + continue; + } + obj.send_modifier(format.format.drm, modifier.modifier); } - obj.send_modifier(format.format.drm, modifier.modifier); } } } @@ -57,6 +62,7 @@ impl ZwpLinuxDmabufV1Global { } const MODIFIERS_SINCE_VERSION: u32 = 3; +const FEEDBACK_SINCE_VERSION: u32 = 4; global_base!( ZwpLinuxDmabufV1Global, @@ -70,7 +76,7 @@ impl Global for ZwpLinuxDmabufV1Global { } fn version(&self) -> u32 { - 3 + 5 } } @@ -116,6 +122,40 @@ impl ZwpLinuxDmabufV1 { self.client.add_client_obj(¶ms)?; Ok(()) } + + fn get_feedback( + self: &Rc, + id: ZwpLinuxDmabufFeedbackV1Id, + ) -> Result<(), ZwpLinuxDmabufV1Error> { + let fb = Rc::new(ZwpLinuxDmabufFeedbackV1::new(id, &self.client)); + track!(self.client, fb); + self.client.add_client_obj(&fb)?; + self.client + .state + .drm_feedback_consumers + .set((self.client.id, id), fb.clone()); + if let Some(feedback) = self.client.state.drm_feedback.get() { + fb.send_feedback(&feedback); + } + Ok(()) + } + + fn get_default_feedback( + self: &Rc, + parser: MsgParser<'_, '_>, + ) -> Result<(), ZwpLinuxDmabufV1Error> { + let req: GetDefaultFeedback = self.client.parse(&**self, parser)?; + self.get_feedback(req.id) + } + + fn get_surface_feedback( + self: &Rc, + parser: MsgParser<'_, '_>, + ) -> Result<(), ZwpLinuxDmabufV1Error> { + let req: GetSurfaceFeedback = self.client.parse(&**self, parser)?; + let _surface = self.client.lookup(req.surface)?; + self.get_feedback(req.id) + } } object_base! { @@ -123,6 +163,8 @@ object_base! { DESTROY => destroy, CREATE_PARAMS => create_params, + GET_DEFAULT_FEEDBACK => get_default_feedback if self.version >= 4, + GET_SURFACE_FEEDBACK => get_surface_feedback if self.version >= 4, } impl Object for ZwpLinuxDmabufV1 {} diff --git a/src/main.rs b/src/main.rs index bd871451..b1748ec7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -58,6 +58,7 @@ mod compositor; mod config; mod cursor; mod dbus; +mod drm_feedback; mod edid; mod fixed; mod forker; diff --git a/src/state.rs b/src/state.rs index da830015..3d164de2 100644 --- a/src/state.rs +++ b/src/state.rs @@ -12,6 +12,7 @@ use { config::ConfigProxy, cursor::{Cursor, ServerCursors}, dbus::Dbus, + drm_feedback::DrmFeedback, forker::ForkerProxy, gfx_api::GfxContext, globals::{Globals, GlobalsError, WaylandGlobal}, @@ -26,6 +27,7 @@ use { zwp_idle_inhibitor_v1::{IdleInhibitorId, IdleInhibitorIds, ZwpIdleInhibitorV1}, NoneSurfaceExt, WlSurface, }, + zwp_linux_dmabuf_feedback_v1::ZwpLinuxDmabufFeedbackV1, zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1Global, }, io_uring::IoUring, @@ -44,7 +46,9 @@ use { queue::AsyncQueue, refcounted::RefCounted, run_toplevel::RunToplevel, }, wheel::Wheel, - wire::{JayRenderCtxId, JaySeatEventsId, JayWorkspaceWatcherId}, + wire::{ + JayRenderCtxId, JaySeatEventsId, JayWorkspaceWatcherId, ZwpLinuxDmabufFeedbackV1Id, + }, xkbcommon::{XkbContext, XkbKeymap}, xwayland::{self, XWaylandEvent}, }, @@ -70,6 +74,9 @@ pub struct State { pub default_keymap: Rc, pub eng: Rc, pub render_ctx: CloneCell>>, + pub drm_feedback: CloneCell>>, + pub drm_feedback_consumers: + CopyHashMap<(ClientId, ZwpLinuxDmabufFeedbackV1Id), Rc>, pub render_ctx_version: NumCell, pub render_ctx_ever_initialized: Cell, pub cursors: CloneCell>>, @@ -309,6 +316,23 @@ impl State { self.render_ctx.set(ctx.clone()); self.render_ctx_version.fetch_add(1); self.cursors.set(None); + self.drm_feedback.set(None); + + 'handle_new_feedback: { + if let Some(ctx) = &ctx { + let feedback = match DrmFeedback::new(&**ctx) { + Ok(fb) => fb, + Err(e) => { + log::error!("Could not create new DRM feedback: {}", ErrorFmt(e)); + break 'handle_new_feedback; + } + }; + for watcher in self.drm_feedback_consumers.lock().values() { + watcher.send_feedback(&feedback); + } + self.drm_feedback.set(Some(Rc::new(feedback))); + } + } { struct Walker;