diff --git a/Cargo.lock b/Cargo.lock index 1e534143..853d6566 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -731,6 +731,7 @@ dependencies = [ "jay-cpu-worker", "jay-criteria", "jay-dbus-core", + "jay-drm-feedback", "jay-edid", "jay-eventfd-cache", "jay-formats", @@ -840,6 +841,17 @@ dependencies = [ "uapi", ] +[[package]] +name = "jay-drm-feedback" +version = "0.1.0" +dependencies = [ + "ahash", + "byteorder", + "jay-video-types", + "thiserror", + "uapi", +] + [[package]] name = "jay-edid" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 6310bbb7..d4494401 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,6 +48,7 @@ members = [ "clientmem", "allocator", "output-schedule", + "drm-feedback", "pango", "libinput", "toml-config", @@ -99,6 +100,7 @@ jay-theme = { version = "0.1.0", path = "theme" } jay-clientmem = { version = "0.1.0", path = "clientmem" } jay-allocator = { version = "0.1.0", path = "allocator" } jay-output-schedule = { version = "0.1.0", path = "output-schedule" } +jay-drm-feedback = { version = "0.1.0", path = "drm-feedback" } jay-pango = { version = "0.1.0", path = "pango" } jay-libinput = { version = "0.1.0", path = "libinput" } diff --git a/drm-feedback/Cargo.toml b/drm-feedback/Cargo.toml new file mode 100644 index 00000000..aa3c25ee --- /dev/null +++ b/drm-feedback/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "jay-drm-feedback" +version = "0.1.0" +edition = "2024" +license = "GPL-3.0-only" + +[dependencies] +jay-video-types = { version = "0.1.0", path = "../video-types" } + +ahash = "0.8.7" +byteorder = "1.5.0" +thiserror = "2.0.11" +uapi = "0.2.13" diff --git a/drm-feedback/src/lib.rs b/drm-feedback/src/lib.rs new file mode 100644 index 00000000..69ac676d --- /dev/null +++ b/drm-feedback/src/lib.rs @@ -0,0 +1,149 @@ +use { + ahash::AHashMap, + byteorder::{NativeEndian, WriteBytesExt}, + jay_video_types::Modifier, + std::{io::Write, rc::Rc}, + thiserror::Error, + uapi::{OwnedFd, c}, +}; + +#[derive(Debug)] +pub struct DrmFeedbackIds { + next: std::cell::Cell, +} + +impl Default for DrmFeedbackIds { + fn default() -> Self { + Self { + next: std::cell::Cell::new(1), + } + } +} + +impl DrmFeedbackIds { + pub fn next(&self) -> DrmFeedbackId { + let id = self.next.get(); + self.next.set(id + 1); + DrmFeedbackId(id) + } +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +pub struct DrmFeedbackId(u64); + +#[derive(Debug)] +pub struct DrmFeedbackShared { + pub fd: Rc, + pub size: usize, + pub main_device: c::dev_t, + pub indices: AHashMap<(u32, Modifier), u16>, +} + +#[derive(Debug)] +pub struct DrmFeedback { + pub id: DrmFeedbackId, + pub shared: Rc, + pub tranches: Vec, +} + +#[derive(Clone, Debug)] +pub struct DrmFeedbackTranche { + pub device: c::dev_t, + pub indices: Vec, + pub scanout: bool, +} + +impl DrmFeedback { + pub fn new( + ids: &DrmFeedbackIds, + render_ctx: &C, + ) -> Result { + let main_device = match render_ctx.main_device() { + Some(dev) => dev, + _ => return Err(DrmFeedbackError::NoDrmDevice), + }; + let (data, index_map) = create_fd_data(render_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(); + Ok(Self { + id: ids.next(), + tranches: vec![DrmFeedbackTranche { + device: main_device, + indices: (0..index_map.len()).map(|v| v as u16).collect(), + scanout: false, + }], + shared: Rc::new(DrmFeedbackShared { + fd: Rc::new(memfd), + size: data.len(), + main_device, + indices: index_map, + }), + }) + } + + pub fn for_scanout( + &self, + ids: &DrmFeedbackIds, + devnum: c::dev_t, + formats: &[(u32, Modifier)], + ) -> Result, DrmFeedbackError> { + let mut tranches = vec![]; + { + let mut indices = vec![]; + for (format, modifier) in formats { + if let Some(idx) = self.shared.indices.get(&(*format, *modifier)) { + indices.push(*idx); + } + } + if indices.len() > 0 { + tranches.push(DrmFeedbackTranche { + device: devnum, + indices, + scanout: true, + }); + } else { + return Ok(None); + } + } + tranches.extend(self.tranches.iter().cloned()); + Ok(Some(Self { + id: ids.next(), + shared: self.shared.clone(), + tranches, + })) + } +} + +pub trait DrmFeedbackContext { + fn main_device(&self) -> Option; + fn for_each_read_format(&self, f: &mut dyn FnMut(u32, Modifier)); +} + +fn create_fd_data( + ctx: &C, +) -> (Vec, AHashMap<(u32, Modifier), u16>) { + let mut vec = vec![]; + let mut map = AHashMap::new(); + let mut pos = 0; + ctx.for_each_read_format(&mut |format, modifier| { + vec.write_u32::(format).unwrap(); + vec.write_u32::(0).unwrap(); + vec.write_u64::(modifier).unwrap(); + map.insert((format, modifier), pos); + pos += 1; + }); + (vec, map) +} + +#[derive(Debug, Error)] +pub enum DrmFeedbackError { + #[error("Graphics API does not have a DRM device")] + NoDrmDevice, +} diff --git a/src/drm_feedback.rs b/src/drm_feedback.rs index 7ac9b0ee..df936d41 100644 --- a/src/drm_feedback.rs +++ b/src/drm_feedback.rs @@ -1,124 +1,17 @@ -use { - crate::{gfx_api::GfxContext, utils::oserror::OsError, video::Modifier}, - ahash::AHashMap, - byteorder::{NativeEndian, WriteBytesExt}, - std::{io::Write, rc::Rc}, - thiserror::Error, - uapi::{OwnedFd, c}, -}; +pub use jay_drm_feedback::*; -linear_ids!(DrmFeedbackIds, DrmFeedbackId); +use crate::{gfx_api::GfxContext, video::Modifier}; -#[derive(Debug)] -pub struct DrmFeedbackShared { - pub fd: Rc, - pub size: usize, - pub main_device: c::dev_t, - pub indices: AHashMap<(u32, Modifier), u16>, -} - -#[derive(Debug)] -pub struct DrmFeedback { - pub id: DrmFeedbackId, - pub shared: Rc, - pub tranches: Vec, -} - -#[derive(Clone, Debug)] -pub struct DrmFeedbackTranche { - pub device: c::dev_t, - pub indices: Vec, - pub scanout: bool, -} - -impl DrmFeedback { - pub fn new( - ids: &DrmFeedbackIds, - render_ctx: &dyn GfxContext, - ) -> Result { - let main_device = match render_ctx.allocator().drm() { - Some(drm) => drm.dev(), - _ => return Err(DrmFeedbackError::NoDrmDevice), - }; - let (data, index_map) = create_fd_data(render_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(); - Ok(Self { - id: ids.next(), - tranches: vec![DrmFeedbackTranche { - device: main_device, - indices: (0..index_map.len()).map(|v| v as u16).collect(), - scanout: false, - }], - shared: Rc::new(DrmFeedbackShared { - fd: Rc::new(memfd), - size: data.len(), - main_device, - indices: index_map, - }), - }) +impl DrmFeedbackContext for dyn GfxContext + '_ { + fn main_device(&self) -> Option { + self.allocator().drm().map(|drm| drm.dev()) } - pub fn for_scanout( - &self, - ids: &DrmFeedbackIds, - devnum: c::dev_t, - formats: &[(u32, Modifier)], - ) -> Result, DrmFeedbackError> { - let mut tranches = vec![]; - { - let mut indices = vec![]; - for (format, modifier) in formats { - if let Some(idx) = self.shared.indices.get(&(*format, *modifier)) { - indices.push(*idx); - } - } - if indices.len() > 0 { - tranches.push(DrmFeedbackTranche { - device: devnum, - indices, - scanout: true, - }); - } else { - return Ok(None); + fn for_each_read_format(&self, f: &mut dyn FnMut(u32, Modifier)) { + for (format, info) in &**self.formats() { + for modifier in &info.read_modifiers { + f(*format, *modifier); } } - tranches.extend(self.tranches.iter().cloned()); - Ok(Some(Self { - id: ids.next(), - shared: self.shared.clone(), - tranches, - })) } } - -fn create_fd_data(ctx: &dyn GfxContext) -> (Vec, AHashMap<(u32, Modifier), u16>) { - let mut vec = vec![]; - let mut map = AHashMap::new(); - let mut pos = 0; - for (format, info) in &**ctx.formats() { - for modifier in &info.read_modifiers { - vec.write_u32::(*format).unwrap(); - vec.write_u32::(0).unwrap(); - vec.write_u64::(*modifier).unwrap(); - map.insert((*format, *modifier), pos); - pos += 1; - } - } - (vec, map) -} - -#[derive(Debug, Error)] -pub enum DrmFeedbackError { - #[error("Could not stat drm device")] - Stat(#[from] OsError), - #[error("Graphics API does not have a DRM device")] - NoDrmDevice, -}