diff --git a/src/compositor.rs b/src/compositor.rs index f854ead1..d41ff96c 100644 --- a/src/compositor.rs +++ b/src/compositor.rs @@ -180,7 +180,7 @@ fn start_compositor2( pending_float_titles: Default::default(), pending_input_popup_positioning: Default::default(), pending_toplevel_screencasts: Default::default(), - pending_toplevel_screencast_reallocs: Default::default(), + pending_screencast_reallocs_or_reconfigures: Default::default(), dbus: Dbus::new(&engine, &ring, &run_toplevel), fdcloser: FdCloser::new(), logger: logger.clone(), diff --git a/src/gfx_api.rs b/src/gfx_api.rs index 9ebdd9c0..2da1d71d 100644 --- a/src/gfx_api.rs +++ b/src/gfx_api.rs @@ -586,3 +586,39 @@ impl Debug for GfxError { Debug::fmt(&self.0, f) } } + +impl GfxFormat { + pub fn cross_intersect(&self, other: &GfxFormat) -> GfxFormat { + assert_eq!(self.format, other.format); + GfxFormat { + format: self.format, + read_modifiers: self + .read_modifiers + .intersection(&other.write_modifiers) + .copied() + .collect(), + write_modifiers: self + .write_modifiers + .intersection(&other.read_modifiers) + .copied() + .collect(), + } + } +} + +pub fn cross_intersect_formats( + local: &AHashMap, + remote: &AHashMap, +) -> AHashMap { + let mut res = AHashMap::new(); + for lf in local.values() { + if let Some(rf) = remote.get(&lf.format.drm) { + let f = lf.cross_intersect(rf); + if f.read_modifiers.is_empty() && f.write_modifiers.is_empty() { + continue; + } + res.insert(f.format.drm, f); + } + } + res +} diff --git a/src/ifs/jay_compositor.rs b/src/ifs/jay_compositor.rs index d9152077..4f40d054 100644 --- a/src/ifs/jay_compositor.rs +++ b/src/ifs/jay_compositor.rs @@ -70,7 +70,7 @@ impl Global for JayCompositorGlobal { } fn version(&self) -> u32 { - 6 + 7 } fn required_caps(&self) -> ClientCaps { @@ -309,6 +309,7 @@ impl JayCompositorRequestHandler for JayCompositor { id: req.id, client: self.client.clone(), tracker: Default::default(), + version: self.version, }); track!(self.client, ctx); self.client.add_client_obj(&ctx)?; @@ -340,7 +341,7 @@ impl JayCompositorRequestHandler for JayCompositor { } fn create_screencast(&self, req: CreateScreencast, _slf: &Rc) -> Result<(), Self::Error> { - let sc = Rc::new_cyclic(|slf| JayScreencast::new(req.id, &self.client, slf)); + let sc = Rc::new_cyclic(|slf| JayScreencast::new(req.id, &self.client, slf, self.version)); track!(self.client, sc); self.client.add_client_obj(&sc)?; Ok(()) diff --git a/src/ifs/jay_render_ctx.rs b/src/ifs/jay_render_ctx.rs index f15e7129..ee7152d6 100644 --- a/src/ifs/jay_render_ctx.rs +++ b/src/ifs/jay_render_ctx.rs @@ -11,16 +11,41 @@ use { thiserror::Error, }; +pub const FORMATS_SINCE: Version = Version(7); + pub struct JayRenderCtx { pub id: JayRenderCtxId, pub client: Rc, pub tracker: Tracker, + pub version: Version, } impl JayRenderCtx { pub fn send_render_ctx(&self, ctx: Option>) { let mut fd = None; if let Some(ctx) = ctx { + if self.version >= FORMATS_SINCE { + for format in ctx.formats().values() { + self.client.event(Format { + self_id: self.id, + format: format.format.drm, + }); + for modifier in &format.write_modifiers { + self.client.event(WriteModifier { + self_id: self.id, + format: format.format.drm, + modifier: *modifier, + }); + } + for modifier in &format.read_modifiers { + self.client.event(ReadModifier { + self_id: self.id, + format: format.format.drm, + modifier: *modifier, + }); + } + } + } let allocator = ctx.allocator(); match allocator.drm() { Some(drm) => match drm.dup_render() { @@ -33,8 +58,6 @@ impl JayRenderCtx { log::error!("Allocator does not have a DRM device"); } } - } else { - self.client.event(NoDevice { self_id: self.id }); } match fd { Some(fd) => self.client.event(Device { diff --git a/src/ifs/jay_screencast.rs b/src/ifs/jay_screencast.rs index 120874d6..9e43f03f 100644 --- a/src/ifs/jay_screencast.rs +++ b/src/ifs/jay_screencast.rs @@ -4,7 +4,7 @@ use { client::{Client, ClientError}, format::XRGB8888, gfx_api::{GfxContext, GfxError, GfxFramebuffer, GfxTexture}, - ifs::{jay_output::JayOutput, jay_toplevel::JayToplevel}, + ifs::{jay_output::JayOutput, jay_toplevel::JayToplevel, wl_buffer::WlBufferStorage}, leaks::Tracker, object::{Object, Version}, scale::Scale, @@ -39,8 +39,11 @@ pub async fn perform_toplevel_screencasts(state: Rc) { pub async fn perform_screencast_realloc(state: Rc) { loop { - let screencast = state.pending_toplevel_screencast_reallocs.pop().await; - screencast.realloc_scheduled.set(false); + let screencast = state + .pending_screencast_reallocs_or_reconfigures + .pop() + .await; + screencast.realloc_or_reconfigure_scheduled.set(false); match state.render_ctx.get() { None => screencast.do_destroy(), Some(ctx) => { @@ -52,8 +55,11 @@ pub async fn perform_screencast_realloc(state: Rc) { } } +pub const CLIENT_BUFFERS_SINCE: Version = Version(7); + pub struct JayScreencast { pub id: JayScreencastId, + pub version: Version, pub client: Rc, pub tracker: Tracker, config_serial: NumCell, @@ -69,8 +75,8 @@ pub struct JayScreencast { show_workspaces: RefCell>, linear: Cell, pending: Pending, - need_realloc: Cell, - realloc_scheduled: Cell, + need_realloc_or_reconfigure: Cell, + realloc_or_reconfigure_scheduled: Cell, latch_listener: EventListener, } @@ -100,11 +106,13 @@ struct Pending { target: Cell>>, show_all: Cell>, show_workspaces: RefCell>>, + clear_buffers: Cell, + buffers: RefCell>>, } struct ScreencastBuffer { - _bo: Rc, - dmabuf: DmaBuf, + _bo: Option>, + dmabuf: Option, fb: Rc, free: bool, } @@ -122,9 +130,15 @@ impl JayScreencast { false } - pub fn new(id: JayScreencastId, client: &Rc, slf: &Weak) -> Self { + pub fn new( + id: JayScreencastId, + client: &Rc, + slf: &Weak, + version: Version, + ) -> Self { Self { id, + version, client: client.clone(), tracker: Default::default(), config_serial: Default::default(), @@ -140,8 +154,8 @@ impl JayScreencast { show_workspaces: Default::default(), linear: Cell::new(false), pending: Default::default(), - need_realloc: Cell::new(false), - realloc_scheduled: Cell::new(false), + need_realloc_or_reconfigure: Cell::new(false), + realloc_or_reconfigure_scheduled: Cell::new(false), latch_listener: EventListener::new(slf.clone()), } } @@ -210,7 +224,12 @@ impl JayScreencast { let serial = self.buffers_serial.fetch_add(1) + 1; let buffers = self.buffers.borrow_mut(); for buffer in buffers.iter() { - for plane in &buffer.dmabuf.planes { + let Some(dmabuf) = &buffer.dmabuf else { + log::error!("Trying to send buffers but buffers are client allocated"); + self.do_destroy(); + return; + }; + for plane in &dmabuf.planes { self.client.event(Plane { self_id: self.id, fd: plane.fd.clone(), @@ -220,10 +239,10 @@ impl JayScreencast { } self.client.event(Buffer { self_id: self.id, - format: buffer.dmabuf.format.drm, - modifier: buffer.dmabuf.modifier, - width: buffer.dmabuf.width, - height: buffer.dmabuf.height, + format: dmabuf.format.drm, + modifier: dmabuf.modifier, + width: dmabuf.width, + height: dmabuf.height, }); } self.client.event(BuffersDone { @@ -232,11 +251,19 @@ impl JayScreencast { }); } - #[allow(dead_code)] fn send_config(&self) { + self.need_realloc_or_reconfigure.set(false); self.config_acked.set(false); let serial = self.config_serial.fetch_add(1) + 1; if let Some(target) = self.target.get() { + let (width, height) = target_size(Some(&target)); + if self.version >= CLIENT_BUFFERS_SINCE { + self.client.event(ConfigSize { + self_id: self.id, + width, + height, + }); + } if let Target::Output(output) = target { self.client.event(ConfigOutput { self_id: self.id, @@ -339,30 +366,39 @@ impl JayScreencast { pub fn do_destroy(&self) { self.detach(); + self.buffers.borrow_mut().clear(); self.destroyed.set(true); self.client.event(Destroyed { self_id: self.id }); } - pub fn schedule_realloc(self: &Rc) { - self.need_realloc.set(true); - if !self.realloc_scheduled.replace(true) { + pub fn schedule_realloc_or_reconfigure(self: &Rc) { + self.need_realloc_or_reconfigure.set(true); + if !self.realloc_or_reconfigure_scheduled.replace(true) { self.client .state - .pending_toplevel_screencast_reallocs + .pending_screencast_reallocs_or_reconfigures .push(self.clone()); } } fn realloc(&self, ctx: &Rc) -> Result<(), JayScreencastError> { - if !self.destroyed.get() && self.buffers_acked.get() { - self.do_realloc(ctx) - } else { - Ok(()) + if self.destroyed.get() { + return Ok(()); } + if self.version < CLIENT_BUFFERS_SINCE { + if self.buffers_acked.get() { + return self.do_realloc(ctx); + } + } else { + if self.config_acked.get() { + self.send_config(); + } + } + Ok(()) } fn do_realloc(&self, ctx: &Rc) -> Result<(), JayScreencastError> { - self.need_realloc.set(false); + self.need_realloc_or_reconfigure.set(false); let mut buffers = vec![]; let formats = ctx.formats(); let format = match formats.get(&XRGB8888.drm) { @@ -395,14 +431,14 @@ impl JayScreencast { &self.client.state.dma_buf_ids, width, height, - XRGB8888, + format.format, &modifiers, usage, )?; let fb = ctx.clone().dmabuf_img(buffer.dmabuf())?.to_framebuffer()?; buffers.push(ScreencastBuffer { - dmabuf: buffer.dmabuf().clone(), - _bo: buffer, + dmabuf: Some(buffer.dmabuf().clone()), + _bo: Some(buffer), fb, free: true, }); @@ -457,7 +493,7 @@ impl JayScreencastRequestHandler for JayScreencast { } else { None }; - if self.destroyed.get() || !self.config_acked.get() { + if self.destroyed.get() { return Ok(()); } self.pending.target.set(Some(output)); @@ -469,7 +505,7 @@ impl JayScreencastRequestHandler for JayScreencast { req: SetAllowAllWorkspaces, _slf: &Rc, ) -> Result<(), Self::Error> { - if self.destroyed.get() || !self.config_acked.get() { + if self.destroyed.get() { return Ok(()); } self.pending.show_all.set(Some(req.allow_all != 0)); @@ -478,7 +514,7 @@ impl JayScreencastRequestHandler for JayScreencast { fn allow_workspace(&self, req: AllowWorkspace, _slf: &Rc) -> Result<(), Self::Error> { let ws = self.client.lookup(req.workspace)?; - if self.destroyed.get() || !self.config_acked.get() { + if self.destroyed.get() { return Ok(()); } let mut sw = self.pending.show_workspaces.borrow_mut(); @@ -494,7 +530,7 @@ impl JayScreencastRequestHandler for JayScreencast { _req: TouchAllowedWorkspaces, _slf: &Rc, ) -> Result<(), Self::Error> { - if self.destroyed.get() || !self.config_acked.get() { + if self.destroyed.get() { return Ok(()); } self.pending @@ -509,7 +545,7 @@ impl JayScreencastRequestHandler for JayScreencast { req: SetUseLinearBuffers, _slf: &Rc, ) -> Result<(), Self::Error> { - if self.destroyed.get() || !self.config_acked.get() { + if self.destroyed.get() { return Ok(()); } self.pending.linear.set(Some(req.use_linear != 0)); @@ -517,7 +553,7 @@ impl JayScreencastRequestHandler for JayScreencast { } fn set_running(&self, req: SetRunning, _slf: &Rc) -> Result<(), Self::Error> { - if self.destroyed.get() || !self.config_acked.get() { + if self.destroyed.get() { return Ok(()); } self.pending.running.set(Some(req.running != 0)); @@ -525,11 +561,11 @@ impl JayScreencastRequestHandler for JayScreencast { } fn configure(&self, _req: Configure, slf: &Rc) -> Result<(), Self::Error> { - if self.destroyed.get() || !self.config_acked.get() { + if self.destroyed.get() { return Ok(()); } - let mut need_realloc = false; + let mut need_realloc_or_reconfigure = false; if let Some(target) = self.pending.target.take() { self.detach(); @@ -561,15 +597,26 @@ impl JayScreencastRequestHandler for JayScreencast { } } if target_size(new_target.as_ref()) != target_size(self.target.get().as_ref()) { - need_realloc = true; + need_realloc_or_reconfigure = true; } self.target.set(new_target); } if let Some(linear) = self.pending.linear.take() { if self.linear.replace(linear) != linear { - need_realloc = true; + need_realloc_or_reconfigure = true; } } + if self.pending.clear_buffers.take() { + self.buffers.borrow_mut().clear(); + } + for buffer in self.pending.buffers.borrow_mut().drain(..) { + self.buffers.borrow_mut().push(ScreencastBuffer { + _bo: None, + dmabuf: None, + fb: buffer, + free: true, + }); + } let mut capture_rules_changed = false; if let Some(show_all) = self.pending.show_all.take() { self.show_all.set(show_all); @@ -583,8 +630,8 @@ impl JayScreencastRequestHandler for JayScreencast { self.running.set(running); } - if need_realloc { - slf.schedule_realloc(); + if need_realloc_or_reconfigure { + slf.schedule_realloc_or_reconfigure(); } if capture_rules_changed { @@ -606,19 +653,22 @@ impl JayScreencastRequestHandler for JayScreencast { } if req.serial == self.buffers_serial.get() { self.buffers_acked.set(true); - if self.need_realloc.get() { - slf.schedule_realloc(); + if self.need_realloc_or_reconfigure.get() { + slf.schedule_realloc_or_reconfigure(); } } Ok(()) } - fn ack_config(&self, req: AckConfig, _slf: &Rc) -> Result<(), Self::Error> { + fn ack_config(&self, req: AckConfig, slf: &Rc) -> Result<(), Self::Error> { if self.destroyed.get() { return Ok(()); } if req.serial == self.config_serial.get() { self.config_acked.set(true); + if self.need_realloc_or_reconfigure.get() { + slf.schedule_realloc_or_reconfigure(); + } } Ok(()) } @@ -628,7 +678,7 @@ impl JayScreencastRequestHandler for JayScreencast { return Ok(()); } let idx = req.idx as usize; - if idx > self.buffers.borrow_mut().len() { + if idx >= self.buffers.borrow_mut().len() { return Err(JayScreencastError::OutOfBounds(req.idx)); } self.buffers.borrow_mut()[idx].free = true; @@ -644,17 +694,46 @@ impl JayScreencastRequestHandler for JayScreencast { } else { None }; - if self.destroyed.get() || !self.config_acked.get() { + if self.destroyed.get() { return Ok(()); } self.pending.target.set(Some(toplevel)); Ok(()) } + + fn clear_buffers(&self, _req: ClearBuffers, _slf: &Rc) -> Result<(), Self::Error> { + if self.destroyed.get() { + return Ok(()); + } + self.pending.clear_buffers.set(true); + Ok(()) + } + + fn add_buffer(&self, req: AddBuffer, _slf: &Rc) -> Result<(), Self::Error> { + if self.destroyed.get() { + return Ok(()); + } + let buffer = self.client.lookup(req.buffer)?; + if let Some(WlBufferStorage::Dmabuf { img, .. }) = &*buffer.storage.borrow() { + match img.clone().to_framebuffer() { + Ok(fb) => self.pending.buffers.borrow_mut().push(fb), + Err(e) => { + log::warn!( + "Could not turn GfxImage into GfxFramebuffer: {}", + ErrorFmt(e) + ); + self.do_destroy(); + } + } + return Ok(()); + } + Err(JayScreencastError::NotDmabuf) + } } object_base! { self = JayScreencast; - version = Version(1); + version = self.version; } impl Object for JayScreencast { @@ -681,6 +760,8 @@ pub enum JayScreencastError { XRGB8888Writing, #[error("Render context supports neither linear or invalid modifier")] Modifier, + #[error("Buffer is not a dmabuf")] + NotDmabuf, } efrom!(JayScreencastError, ClientError); diff --git a/src/ifs/wl_surface.rs b/src/ifs/wl_surface.rs index 3ab3e797..8fcbb2f4 100644 --- a/src/ifs/wl_surface.rs +++ b/src/ifs/wl_surface.rs @@ -141,7 +141,7 @@ pub struct SurfaceSendPreferredScaleVisitor; impl SurfaceSendPreferredScaleVisitor { fn schedule_realloc(&self, tl: &impl ToplevelNode) { for sc in tl.tl_data().jay_screencasts.lock().values() { - sc.schedule_realloc(); + sc.schedule_realloc_or_reconfigure(); } } } diff --git a/src/pipewire/pw_ifs/pw_client_node.rs b/src/pipewire/pw_ifs/pw_client_node.rs index f48099fa..f998a584 100644 --- a/src/pipewire/pw_ifs/pw_client_node.rs +++ b/src/pipewire/pw_ifs/pw_client_node.rs @@ -12,9 +12,9 @@ use { pw_pod::{ pw_node_activation, spa_chunk, spa_io_buffers, spa_meta_bitmap, spa_meta_busy, spa_meta_cursor, spa_meta_header, spa_meta_region, PW_CHOICE_Enum, PW_CHOICE_Flags, - PW_OBJECT_Format, PW_OBJECT_ParamBuffers, PW_OBJECT_ParamMeta, PwIoType, - PwPodFraction, PwPodObject, PwPodRectangle, PwPropFlag, SPA_DATA_DmaBuf, - SPA_DATA_MemFd, SPA_DATA_MemPtr, SPA_FORMAT_VIDEO_format, + PW_OBJECT_Format, PW_OBJECT_ParamBuffers, PW_OBJECT_ParamMeta, PW_TYPE_Long, + PwIoType, PwPod, PwPodFraction, PwPodObject, PwPodRectangle, PwPropFlag, + SPA_DATA_DmaBuf, SPA_DATA_MemFd, SPA_DATA_MemPtr, SPA_FORMAT_VIDEO_format, SPA_FORMAT_VIDEO_framerate, SPA_FORMAT_VIDEO_modifier, SPA_FORMAT_VIDEO_size, SPA_FORMAT_mediaSubtype, SPA_FORMAT_mediaType, SPA_IO_Buffers, SPA_META_Bitmap, SPA_META_Busy, SPA_META_Control, SPA_META_Cursor, SPA_META_Header, @@ -24,7 +24,9 @@ use { SPA_PARAM_EnumFormat, SPA_PARAM_Format, SPA_PARAM_META_size, SPA_PARAM_META_type, SPA_PARAM_Meta, SpaDataFlags, SpaDataType, SpaDirection, SpaIoType, SpaMediaSubtype, SpaMediaType, SpaMetaType, SpaNodeBuffersFlags, SpaNodeCommand, - SpaParamType, SpaVideoFormat, SPA_DATA_FLAG_READABLE, SPA_DIRECTION_INPUT, + SpaParamType, SpaVideoFormat, PW_NODE_ACTIVATION_FINISHED, + PW_NODE_ACTIVATION_NOT_TRIGGERED, PW_NODE_ACTIVATION_TRIGGERED, + PW_PROP_DONT_FIXATE, SPA_DATA_FLAG_READABLE, SPA_DIRECTION_INPUT, SPA_DIRECTION_OUTPUT, SPA_NODE_BUFFERS_FLAG_ALLOC, SPA_PARAM_INFO, SPA_PARAM_INFO_READ, SPA_PARAM_INFO_SERIAL, SPA_PORT_FLAG, SPA_PORT_FLAG_CAN_ALLOC_BUFFERS, @@ -32,15 +34,15 @@ use { }, utils::{ bitfield::Bitfield, buf::TypedBuf, clonecell::CloneCell, copyhashmap::CopyHashMap, - errorfmt::ErrorFmt, + errorfmt::ErrorFmt, option_ext::OptionExt, }, - video::dmabuf::DmaBuf, + video::{dmabuf::DmaBuf, Modifier}, }, std::{ cell::{Cell, RefCell}, mem, - ops::Deref, rc::Rc, + sync::atomic::Ordering::{Relaxed, Release}, }, thiserror::Error, uapi::OwnedFd, @@ -78,7 +80,7 @@ pub trait PwClientNodeOwner { fn port_format_changed(&self, port: &Rc) { let _ = port; } - fn use_buffers(&self, port: &Rc) { + fn use_buffers(self: Rc, port: &Rc) { let _ = port; } fn start(self: Rc) {} @@ -111,14 +113,14 @@ pub struct PwClientNodePort { pub _destroyed: Cell, - pub effective_format: Cell, - pub supported_formats: RefCell>, + pub negotiated_format: RefCell, + pub supported_formats: RefCell, pub supported_metas: Cell, pub can_alloc_buffers: Cell, pub buffers: RefCell>>, - pub buffer_config: Cell>, + pub buffer_config: RefCell, pub io_buffers: CloneCell>>>, @@ -127,11 +129,8 @@ pub struct PwClientNodePort { #[derive(Copy, Clone, Debug, Default)] pub struct PwClientNodeBufferConfig { - pub num_buffers: usize, - pub planes: usize, - pub _size: Option, - pub _stride: Option, - pub _align: usize, + pub num_buffers: Option, + pub planes: Option, pub data_type: SpaDataType, } @@ -143,21 +142,27 @@ pub struct PwClientNodeBuffer { pub _slices: Vec>, } +#[derive(Clone, Debug)] +pub struct PwClientNodePortSupportedFormat { + pub format: &'static Format, + pub modifiers: Vec, +} + #[derive(Clone, Debug, Default)] pub struct PwClientNodePortSupportedFormats { pub media_type: Option, pub media_sub_type: Option, pub video_size: Option, - pub formats: Vec<&'static Format>, - pub modifiers: Vec, + pub formats: Vec, } -#[derive(Copy, Clone, Debug, Default)] +#[derive(Clone, Debug, Default)] pub struct PwClientNodePortFormat { pub media_type: Option, pub media_sub_type: Option, pub video_size: Option, pub format: Option<&'static Format>, + pub modifiers: Option>, pub framerate: Option, } @@ -181,8 +186,8 @@ pub struct PwClientNode { } pub struct PwNodeActivation { - pub _activation: Rc>, - pub _fd: Rc, + pub activation: Rc>, + pub fd: Rc, } // pub struct PwNodeBuffer { @@ -241,7 +246,12 @@ impl PwClientNode { }); } - pub fn create_port(self: &Rc, output: bool) -> Rc { + pub fn create_port( + self: &Rc, + output: bool, + supported_formats: PwClientNodePortSupportedFormats, + num_buffers: Option, + ) -> Rc { let (ids, direction) = match output { true => (&self.port_out_free, SPA_DIRECTION_OUTPUT), false => (&self.port_in_free, SPA_DIRECTION_INPUT), @@ -251,12 +261,16 @@ impl PwClientNode { direction, id: ids.borrow_mut().acquire(), _destroyed: Cell::new(false), - effective_format: Cell::new(Default::default()), - supported_formats: RefCell::new(None), + negotiated_format: Default::default(), + supported_formats: RefCell::new(supported_formats), supported_metas: Cell::new(PwClientNodePortSupportedMetas::none()), can_alloc_buffers: Cell::new(false), buffers: RefCell::new(vec![]), - buffer_config: Cell::new(None), + buffer_config: RefCell::new(PwClientNodeBufferConfig { + num_buffers, + planes: None, + data_type: SPA_DATA_DmaBuf, + }), io_buffers: Default::default(), serial: Cell::new(false), }); @@ -295,10 +309,8 @@ impl PwClientNode { }); } - pub fn send_port_update(&self, port: &PwClientNodePort, re_init: bool) { - if re_init { - port.serial.set(!port.serial.get()); - } + pub fn send_port_update(&self, port: &PwClientNodePort, fixate: bool) { + port.serial.set(!port.serial.get()); let serial = match port.serial.get() { true => SPA_PARAM_INFO_SERIAL, false => SPA_PARAM_INFO::none(), @@ -322,18 +334,14 @@ impl PwClientNode { if sm.contains(SUPPORTED_META_VIDEO_CROP) { metas.push((SPA_META_VideoCrop, mem::size_of::())); } - let sf = port.supported_formats.borrow_mut(); - let bc = port.buffer_config.get(); - let mut num_params = metas.len() as u32; - if sf.is_some() { - num_params += 1; - } - if bc.is_some() { - num_params += 1; - } + let sf = &*port.supported_formats.borrow(); + let num_formats = sf.formats.len() as u32; + let bc = &*port.buffer_config.borrow(); + let num_params = metas.len() as u32 + num_formats + 1; + // num params f.write_uint(num_params); - if let Some(sf) = sf.deref() { + for format in &sf.formats { f.write_object(PW_OBJECT_Format, SPA_PARAM_EnumFormat.0, |f| { if let Some(mt) = sf.media_type { f.write_property(SPA_FORMAT_mediaType.0, PwPropFlag::none(), |f| { @@ -345,30 +353,28 @@ impl PwClientNode { f.write_id(mst.0); }); } - if sf.formats.len() > 0 { - f.write_property(SPA_FORMAT_VIDEO_format.0, PwPropFlag::none(), |f| { + f.write_property(SPA_FORMAT_VIDEO_format.0, PwPropFlag::none(), |f| { + f.write_choice(PW_CHOICE_Enum, 0, |f| { + f.write_id(format.format.pipewire.0); + f.write_id(format.format.pipewire.0); + }); + }); + f.write_property( + SPA_FORMAT_VIDEO_modifier.0, + if fixate { + PwPropFlag::none() + } else { + PW_PROP_DONT_FIXATE + }, + |f| { f.write_choice(PW_CHOICE_Enum, 0, |f| { - f.write_id(sf.formats[0].pipewire.0); - for format in &sf.formats { - f.write_id(format.pipewire.0); + f.write_ulong(format.modifiers[0]); + for modifier in &format.modifiers { + f.write_ulong(*modifier); } }); - }); - } - if sf.modifiers.len() > 0 { - f.write_property( - SPA_FORMAT_VIDEO_modifier.0, - PwPropFlag::none(), - |f| { - f.write_choice(PW_CHOICE_Enum, 0, |f| { - f.write_ulong(sf.modifiers[0]); - for modifier in &sf.modifiers { - f.write_ulong(*modifier); - } - }); - }, - ); - } + }, + ); if let Some(vs) = sf.video_size { f.write_property(SPA_FORMAT_VIDEO_size.0, PwPropFlag::none(), |f| { f.write_choice(PW_CHOICE_Enum, 0, |f| { @@ -379,34 +385,23 @@ impl PwClientNode { } }); } - if let Some(bc) = &bc { - f.write_object(PW_OBJECT_ParamBuffers, SPA_PARAM_Buffers.0, |f| { + f.write_object(PW_OBJECT_ParamBuffers, SPA_PARAM_Buffers.0, |f| { + if let Some(num_buffers) = bc.num_buffers { f.write_property(SPA_PARAM_BUFFERS_buffers.0, PwPropFlag::none(), |f| { - f.write_uint(bc.num_buffers as _); + f.write_uint(num_buffers as _); }); + } + if let Some(planes) = bc.planes { f.write_property(SPA_PARAM_BUFFERS_blocks.0, PwPropFlag::none(), |f| { - f.write_uint(bc.planes as _); + f.write_uint(planes as _); }); - // if let Some(size) = bc.size { - // f.write_property(SPA_PARAM_BUFFERS_size.0, PwPropFlag::none(), |f| { - // f.write_uint(size as _); - // }); - // } - // if let Some(stride) = bc.stride { - // f.write_property(SPA_PARAM_BUFFERS_stride.0, PwPropFlag::none(), |f| { - // f.write_uint(stride as _); - // }); - // } - // f.write_property(SPA_PARAM_BUFFERS_align.0, PwPropFlag::none(), |f| { - // f.write_uint(bc.align as _); - // }); - f.write_property(SPA_PARAM_BUFFERS_dataType.0, PwPropFlag::none(), |f| { - f.write_choice(PW_CHOICE_Flags, 0, |f| { - f.write_uint(1 << bc.data_type.0); - }); + } + f.write_property(SPA_PARAM_BUFFERS_dataType.0, PwPropFlag::none(), |f| { + f.write_choice(PW_CHOICE_Flags, 0, |f| { + f.write_uint(1 << bc.data_type.0); }); }); - } + }); for (key, size) in metas { f.write_object(PW_OBJECT_ParamMeta, SPA_PARAM_Meta.0, |f| { f.write_property(SPA_PARAM_META_type.0, PwPropFlag::none(), |f| { @@ -437,23 +432,13 @@ impl PwClientNode { f.write_int(1); // num props f.write_int(0); - let mut num_params = 1; - if sf.is_some() { - num_params += 1; - } - if bc.is_some() { - num_params += 1; - } + let num_params = 3; // num params f.write_uint(num_params); - if sf.is_some() { - f.write_id(SPA_PARAM_EnumFormat.0); - f.write_uint((SPA_PARAM_INFO_READ | serial).0); - } - if bc.is_some() { - f.write_id(SPA_PARAM_Buffers.0); - f.write_uint((SPA_PARAM_INFO_READ | serial).0); - } + f.write_id(SPA_PARAM_EnumFormat.0); + f.write_uint((SPA_PARAM_INFO_READ | serial).0); + f.write_id(SPA_PARAM_Buffers.0); + f.write_uint((SPA_PARAM_INFO_READ | serial).0); f.write_id(SPA_PARAM_Meta.0); f.write_uint(SPA_PARAM_INFO_READ.0); }); @@ -535,7 +520,7 @@ impl PwClientNode { let mut obj = match obj { Some(obj) => obj, _ => { - port.effective_format.take(); + port.negotiated_format.take(); return Ok(()); } }; @@ -554,10 +539,25 @@ impl PwClientNode { format.format = Some(*fmt); } } + if let Some(mt) = obj.get_param(SPA_FORMAT_VIDEO_modifier.0)? { + if let PwPod::Choice(mods) = mt.pod { + let mut p1 = mods.elements.elements; + p1.read_pod_body_packed(PW_TYPE_Long, 8)?; + while p1.len() > 0 { + let modifier = p1.read_pod_body_packed(PW_TYPE_Long, 8)?; + if let PwPod::Long(modifier) = modifier { + format + .modifiers + .get_or_insert_default_ext() + .push(modifier as u64); + } + } + } + } if let Some(mt) = obj.get_param(SPA_FORMAT_VIDEO_framerate.0)? { format.framerate = Some(mt.pod.get_fraction()?); } - port.effective_format.set(format); + *port.negotiated_format.borrow_mut() = format; Ok(()) } @@ -663,6 +663,7 @@ impl PwClientNode { let maxsize = p1.read_uint()?; chunks.push(mem.typed_at(offset)); + offset += size_of::(); if !buffer_flags.contains(SPA_NODE_BUFFERS_FLAG_ALLOC) { if ty == SPA_DATA_MemPtr { @@ -709,8 +710,16 @@ impl PwClientNode { if mem_id == !0 { port.io_buffers.take(); } else { - port.io_buffers - .set(Some(self.con.mem.map(mem_id, offset, size)?.typed())); + let io_buffers = self + .con + .mem + .map(mem_id, offset, size)? + .typed::(); + unsafe { + io_buffers.read().buffer_id.store(!0, Relaxed); + io_buffers.read().status.store(0, Relaxed); + } + port.io_buffers.set(Some(io_buffers)); } } _ => {} @@ -765,8 +774,8 @@ impl PwClientNode { self.activations.set( node, Rc::new(PwNodeActivation { - _activation: typed, - _fd: signalfd, + activation: typed, + fd: signalfd, }), ); } else { @@ -811,27 +820,29 @@ impl PwClientNode { ) { let mut buf = TypedBuf::::new(); loop { - // unsafe { - // log::info!("transport = {:#?}", activation.read()); - // } - // log::info!("transport in"); - // for port in self.ports.lock().values() { - // for io in port.io_buffers.lock().values() { - // unsafe { - // log::info!("status = {:?}", io.read().status); - // } - // } - // } - // unsafe { - // log::info!("state = {:#?}", activation.read().state[0]); - // } if let Err(e) = self.con.ring.read(&fd, buf.buf()).await { log::error!("Could not read from eventfd: {}", ErrorFmt(e)); return; } - let n = buf.t(); - if n > 1 { - log::warn!("Missed {} transport changes", n - 1); + if let Some(activation) = self.activation.get() { + let activation = unsafe { activation.read() }; + activation + .status + .store(PW_NODE_ACTIVATION_FINISHED.0, Relaxed); + } + } + } + + pub fn drive(&self) { + for activation in self.activations.lock().values() { + let a = unsafe { activation.activation.read() }; + let required = a.state[0].required.load(Relaxed); + a.state[0].pending.store(required - 1, Relaxed); + if required == 1 { + a.status.store(PW_NODE_ACTIVATION_TRIGGERED.0, Release); + let _ = uapi::eventfd_write(activation.fd.raw(), 1); + } else { + a.status.store(PW_NODE_ACTIVATION_NOT_TRIGGERED.0, Release); } } } diff --git a/src/pipewire/pw_pod.rs b/src/pipewire/pw_pod.rs index a67d5613..bd720436 100644 --- a/src/pipewire/pw_pod.rs +++ b/src/pipewire/pw_pod.rs @@ -7,7 +7,7 @@ use { bstr::BStr, std::{ fmt::{Debug, Formatter}, - sync::atomic::AtomicU32, + sync::atomic::{AtomicI32, AtomicU32}, }, uapi::{c, Pod}, }; @@ -1344,8 +1344,8 @@ pub struct spa_io_position { #[derive(Debug)] pub struct pw_node_activation_state { pub status: c::c_int, - pub required: i32, - pub pending: i32, + pub required: AtomicI32, + pub pending: AtomicI32, } ty! { @@ -1368,7 +1368,7 @@ ty! { #[repr(C)] #[derive(Debug)] pub struct pw_node_activation { - pub status: PW_NODE_ACTIVATION, + pub status: AtomicU32, pub flags: c::c_uint, diff --git a/src/portal/ptl_display.rs b/src/portal/ptl_display.rs index 675bb597..71103501 100644 --- a/src/portal/ptl_display.rs +++ b/src/portal/ptl_display.rs @@ -1,11 +1,15 @@ use { crate::{ + gfx_api::{cross_intersect_formats, GfxFormat}, gfx_apis::create_gfx_context, ifs::wl_seat::POINTER, object::Version, portal::{ - ptl_remote_desktop::RemoteDesktopSession, ptl_render_ctx::PortalRenderCtx, - ptl_screencast::ScreencastSession, ptr_gui::WindowData, PortalState, + ptl_remote_desktop::RemoteDesktopSession, + ptl_render_ctx::{PortalRenderCtx, PortalServerRenderCtx}, + ptl_screencast::ScreencastSession, + ptr_gui::WindowData, + PortalState, }, utils::{ bitflags::BitflagsExt, clonecell::CloneCell, copyhashmap::CopyHashMap, @@ -67,7 +71,7 @@ pub struct PortalDisplay { pub comp: Rc, pub fsm: Rc, pub vp: Rc, - pub render_ctx: CloneCell>>, + pub render_ctx: CloneCell>>, pub outputs: CopyHashMap>, pub seats: CopyHashMap>, @@ -156,7 +160,7 @@ impl UsrJayRenderCtxOwner for PortalDisplay { self.render_ctx.take(); } - fn device(&self, fd: Rc) { + fn device(&self, fd: Rc, server_formats: Option>) { self.render_ctx.take(); let dev_id = match uapi::fstat(fd.raw()) { Ok(s) => s.st_rdev, @@ -165,12 +169,13 @@ impl UsrJayRenderCtxOwner for PortalDisplay { return; } }; + let mut render_ctx = None; if let Some(ctx) = self.state.render_ctxs.get(&dev_id) { if let Some(ctx) = ctx.upgrade() { - self.render_ctx.set(Some(ctx)); + render_ctx = Some(ctx); } } - if self.render_ctx.is_none() { + if render_ctx.is_none() { let drm = Drm::open_existing(fd); let ctx = match create_gfx_context(&self.state.eng, &self.state.ring, &drm, GfxApi::OpenGl) { @@ -187,8 +192,22 @@ impl UsrJayRenderCtxOwner for PortalDisplay { _dev_id: dev_id, ctx, }); - self.render_ctx.set(Some(ctx.clone())); self.state.render_ctxs.set(dev_id, Rc::downgrade(&ctx)); + render_ctx = Some(ctx); + } + if let Some(ctx) = render_ctx { + let client_formats = ctx.ctx.formats(); + let usable_formats = match &server_formats { + None => client_formats, + Some(server_formats) => { + Rc::new(cross_intersect_formats(&client_formats, server_formats)) + } + }; + self.render_ctx.set(Some(Rc::new(PortalServerRenderCtx { + ctx, + usable_formats, + server_formats, + }))); } } } @@ -218,7 +237,7 @@ impl UsrWlRegistryOwner for PortalDisplay { version: Version(version.min(5)), }); self.con.add_object(ls.clone()); - self.registry.request_bind(name, version, ls.deref()); + self.registry.request_bind(name, ls.version.0, ls.deref()); self.dmabuf.set(Some(ls)); } } @@ -304,10 +323,10 @@ fn finish_display_connect(dpy: Rc) { con: dpy.con.clone(), owner: Default::default(), caps: Default::default(), - version: Version(version.min(5)), + version: Version(version.min(7)), }); dpy.con.add_object(jc.clone()); - dpy.registry.request_bind(name, version, jc.deref()); + dpy.registry.request_bind(name, jc.version.0, jc.deref()); jc_opt = Some(jc); } else if interface == WpFractionalScaleManagerV1.name() { let ls = Rc::new(UsrWpFractionalScaleManager { @@ -316,7 +335,7 @@ fn finish_display_connect(dpy: Rc) { version: Version(version.min(1)), }); dpy.con.add_object(ls.clone()); - dpy.registry.request_bind(name, version, ls.deref()); + dpy.registry.request_bind(name, ls.version.0, ls.deref()); fsm_opt = Some(ls); } else if interface == ZwlrLayerShellV1.name() { let ls = Rc::new(UsrWlrLayerShell { @@ -325,7 +344,7 @@ fn finish_display_connect(dpy: Rc) { version: Version(version.min(5)), }); dpy.con.add_object(ls.clone()); - dpy.registry.request_bind(name, version, ls.deref()); + dpy.registry.request_bind(name, ls.version.0, ls.deref()); ls_opt = Some(ls); } else if interface == WpViewporter.name() { let ls = Rc::new(UsrWpViewporter { @@ -334,7 +353,7 @@ fn finish_display_connect(dpy: Rc) { version: Version(version.min(1)), }); dpy.con.add_object(ls.clone()); - dpy.registry.request_bind(name, version, ls.deref()); + dpy.registry.request_bind(name, ls.version.0, ls.deref()); vp_opt = Some(ls); } else if interface == WlCompositor.name() { let ls = Rc::new(UsrWlCompositor { @@ -343,7 +362,7 @@ fn finish_display_connect(dpy: Rc) { version: Version(version.min(6)), }); dpy.con.add_object(ls.clone()); - dpy.registry.request_bind(name, version, ls.deref()); + dpy.registry.request_bind(name, ls.version.0, ls.deref()); comp_opt = Some(ls); } else if interface == ZwpLinuxDmabufV1.name() { let ls = Rc::new(UsrLinuxDmabuf { @@ -353,7 +372,7 @@ fn finish_display_connect(dpy: Rc) { version: Version(version.min(5)), }); dpy.con.add_object(ls.clone()); - dpy.registry.request_bind(name, version, ls.deref()); + dpy.registry.request_bind(name, ls.version.0, ls.deref()); dmabuf_opt = Some(ls); } else if interface == WlOutput.name() { outputs.push((name, version)); @@ -423,7 +442,7 @@ fn add_seat(dpy: &Rc, name: u32, version: u32) { version: Version(version.min(9)), }); dpy.con.add_object(wl.clone()); - dpy.registry.request_bind(name, version, wl.deref()); + dpy.registry.request_bind(name, wl.version.0, wl.deref()); let jay_pointer = dpy.jc.get_pointer(&wl); let js = Rc::new(PortalSeat { global_id: name, @@ -447,7 +466,7 @@ fn add_output(dpy: &Rc, name: u32, version: u32) { version: Version(version.min(4)), }); dpy.con.add_object(wl.clone()); - dpy.registry.request_bind(name, version, wl.deref()); + dpy.registry.request_bind(name, wl.version.0, wl.deref()); let jo = dpy.jc.get_output(&wl); let po = Rc::new(PortalOutput { global_id: name, diff --git a/src/portal/ptl_render_ctx.rs b/src/portal/ptl_render_ctx.rs index 4c72e653..b8b1541b 100644 --- a/src/portal/ptl_render_ctx.rs +++ b/src/portal/ptl_render_ctx.rs @@ -1,6 +1,17 @@ -use {crate::gfx_api::GfxContext, std::rc::Rc, uapi::c}; +use { + crate::gfx_api::{GfxContext, GfxFormat}, + ahash::AHashMap, + std::rc::Rc, + uapi::c, +}; pub struct PortalRenderCtx { pub _dev_id: c::dev_t, pub ctx: Rc, } + +pub struct PortalServerRenderCtx { + pub ctx: Rc, + pub usable_formats: Rc>, + pub server_formats: Option>, +} diff --git a/src/portal/ptl_screencast.rs b/src/portal/ptl_screencast.rs index 6b096cf1..60c40f52 100644 --- a/src/portal/ptl_screencast.rs +++ b/src/portal/ptl_screencast.rs @@ -2,16 +2,21 @@ mod screencast_gui; use { crate::{ + allocator::{AllocatorError, BufferObject, BO_USE_RENDERING}, dbus::{prelude::Variant, DbusObject, DictEntry, DynamicType, PendingReply}, + format::{Format, XRGB8888}, + ifs::jay_screencast::CLIENT_BUFFERS_SINCE, pipewire::{ pw_con::PwCon, pw_ifs::pw_client_node::{ PwClientNode, PwClientNodeBufferConfig, PwClientNodeOwner, PwClientNodePort, - PwClientNodePortSupportedFormats, SUPPORTED_META_VIDEO_CROP, + PwClientNodePortSupportedFormat, PwClientNodePortSupportedFormats, + SUPPORTED_META_VIDEO_CROP, }, pw_pod::{ spa_point, spa_rectangle, spa_region, PwPodRectangle, SPA_DATA_DmaBuf, SPA_MEDIA_SUBTYPE_raw, SPA_MEDIA_TYPE_video, SpaChunkFlags, SPA_STATUS_HAVE_DATA, + SPA_VIDEO_FORMAT_UNKNOWN, }, }, portal::{ @@ -22,9 +27,10 @@ use { utils::{ clonecell::{CloneCell, UnsafeCellCloneSafe}, copyhashmap::CopyHashMap, + errorfmt::ErrorFmt, hash_map_ext::HashMapExt, }, - video::dmabuf::{DmaBuf, PlaneVec}, + video::{dmabuf::DmaBuf, Modifier, LINEAR_MODIFIER}, wire::jay_screencast::Ready, wire_dbus::{ org, @@ -37,11 +43,15 @@ use { }, }, wl_usr::usr_ifs::{ - usr_jay_screencast::{UsrJayScreencast, UsrJayScreencastOwner}, + usr_jay_screencast::{ + UsrJayScreencast, UsrJayScreencastOwner, UsrJayScreencastServerConfig, + }, usr_jay_select_toplevel::UsrJaySelectToplevel, usr_jay_select_workspace::UsrJaySelectWorkspace, usr_jay_toplevel::UsrJayToplevel, usr_jay_workspace::UsrJayWorkspace, + usr_linux_buffer_params::{UsrLinuxBufferParams, UsrLinuxBufferParamsOwner}, + usr_wl_buffer::UsrWlBuffer, }, }, std::{ @@ -51,6 +61,7 @@ use { rc::Rc, sync::atomic::Ordering::{Acquire, Relaxed, Release}, }, + thiserror::Error, }; shared_ids!(ScreencastSessionId); @@ -120,10 +131,18 @@ pub struct StartedScreencast { session: Rc, node: Rc, port: Rc, - buffers: RefCell>, + buffer_objects: RefCell>>, + buffers: RefCell>, + pending_buffers: RefCell>>, buffers_valid: Cell, dpy: Rc, jay_screencast: Rc, + port_buffer_valid: Cell, + fixated: Cell, + format: Cell<&'static Format>, + modifier: Cell, + width: Cell, + height: Cell, } bitflags! { @@ -164,7 +183,36 @@ impl PwClientNodeOwner for StartingScreencast { results: Cow::Borrowed(variants), }); } - let port = self.node.create_port(true); + let mut supported_formats = PwClientNodePortSupportedFormats { + media_type: Some(SPA_MEDIA_TYPE_video), + media_sub_type: Some(SPA_MEDIA_SUBTYPE_raw), + video_size: None, + formats: vec![PwClientNodePortSupportedFormat { + format: XRGB8888, + modifiers: vec![LINEAR_MODIFIER], + }], + }; + if let Some(ctx) = self.dpy.render_ctx.get() { + if let Some(server_formats) = &ctx.server_formats { + supported_formats.formats.clear(); + for format in server_formats.values() { + if format.write_modifiers.is_empty() { + continue; + } + if format.format.pipewire == SPA_VIDEO_FORMAT_UNKNOWN { + continue; + } + let ptl_format = PwClientNodePortSupportedFormat { + format: format.format, + modifiers: format.write_modifiers.iter().copied().collect(), + }; + supported_formats.formats.push(ptl_format); + } + } + } + let jsc_version = self.dpy.jc.version; + let num_buffers = (jsc_version >= CLIENT_BUFFERS_SINCE).then_some(3); + let port = self.node.create_port(true, supported_formats, num_buffers); port.can_alloc_buffers.set(true); port.supported_metas.set(SUPPORTED_META_VIDEO_CROP); let jsc = self.dpy.jc.create_screencast(); @@ -194,10 +242,18 @@ impl PwClientNodeOwner for StartingScreencast { session: self.session.clone(), node: self.node.clone(), port, + buffer_objects: Default::default(), buffers: Default::default(), + pending_buffers: Default::default(), buffers_valid: Cell::new(false), dpy: self.dpy.clone(), jay_screencast: jsc, + port_buffer_valid: Cell::new(false), + fixated: Cell::new(jsc_version < CLIENT_BUFFERS_SINCE), + format: Cell::new(XRGB8888), + modifier: Cell::new(LINEAR_MODIFIER), + width: Cell::new(1), + height: Cell::new(1), }); self.session .phase @@ -209,13 +265,84 @@ impl PwClientNodeOwner for StartingScreencast { impl PwClientNodeOwner for StartedScreencast { fn port_format_changed(&self, port: &Rc) { - self.node.send_port_update(port, false); + let format = &*port.negotiated_format.borrow(); + if self.fixated.get() { + return; + } + let (Some(fmt), Some(modifiers)) = (format.format, &format.modifiers) else { + return; + }; + let modifier; + let planes; + match self.allocate_buffer(fmt, modifiers, 1, 1) { + Ok(bo) => { + let dmabuf = bo.dmabuf(); + modifier = dmabuf.modifier; + planes = dmabuf.planes.len(); + } + Err(e) => { + log::error!("Could not allocate buffer: {}", ErrorFmt(e)); + self.session.kill(); + return; + } + }; + self.port.supported_formats.borrow_mut().formats = vec![PwClientNodePortSupportedFormat { + format: fmt, + modifiers: vec![modifier], + }]; + self.port.buffer_config.borrow_mut().planes = Some(planes); + self.node.send_port_update(&self.port, true); + self.format.set(fmt); + self.modifier.set(modifier); + self.fixated.set(true); } - fn use_buffers(&self, port: &Rc) { + fn use_buffers(self: Rc, port: &Rc) { + if self.jay_screencast.version < CLIENT_BUFFERS_SINCE { + self.node + .send_port_output_buffers(port, &self.buffers.borrow_mut()); + self.buffers_valid.set(true); + return; + } + self.buffers_valid.set(false); + self.port_buffer_valid.set(false); + let Some(dmabuf) = self.dpy.dmabuf.get() else { + log::error!("Display does not support dmabuf"); + self.session.kill(); + return; + }; + self.jay_screencast.clear_buffers(); + self.jay_screencast.configure(); + self.buffer_objects.borrow_mut().clear(); + self.buffers.borrow_mut().clear(); + for buffer in self.pending_buffers.borrow_mut().drain(..) { + self.dpy.con.remove_obj(&*buffer); + } + for _ in 0..self.port.buffers.borrow().len() { + let res = self.allocate_buffer( + self.format.get(), + &[self.modifier.get()], + self.width.get(), + self.height.get(), + ); + match res { + Ok(b) => { + let params = dmabuf.create_params(); + params.create(&b.dmabuf()); + params.owner.set(Some(self.clone())); + self.buffers.borrow_mut().push(b.dmabuf().clone()); + self.buffer_objects.borrow_mut().push(b); + self.pending_buffers.borrow_mut().push(params); + } + Err(e) => { + log::error!("Could not allocate buffer: {}", ErrorFmt(e)); + self.session.kill(); + return; + } + } + } self.node - .send_port_output_buffers(port, &self.buffers.borrow_mut()); - self.buffers_valid.set(true); + .send_port_output_buffers(&self.port, &self.buffers.borrow()); } fn start(self: Rc) { @@ -234,6 +361,37 @@ impl PwClientNodeOwner for StartedScreencast { } } +#[derive(Debug, Error)] +enum BufferAllocationError { + #[error("Display has no render context")] + NoRenderContext, + #[error(transparent)] + Allocator(#[from] AllocatorError), +} + +impl StartedScreencast { + fn allocate_buffer( + &self, + format: &'static Format, + modifiers: &[Modifier], + width: i32, + height: i32, + ) -> Result, BufferAllocationError> { + let Some(ctx) = self.dpy.render_ctx.get() else { + return Err(BufferAllocationError::NoRenderContext); + }; + let buffer = ctx.ctx.ctx.allocator().create_bo( + &self.dpy.state.dma_buf_ids, + width, + height, + format, + modifiers, + BO_USE_RENDERING, + )?; + Ok(buffer) + } +} + impl SelectingScreencastCore { pub fn starting(&self, dpy: &Rc, target: ScreencastTarget) { let node = self.session.pw_con.create_client_node(&[ @@ -300,6 +458,9 @@ impl ScreencastSession { s.jay_screencast.con.remove_obj(s.jay_screencast.deref()); s.node.con.destroy_obj(s.node.deref()); s.dpy.screencasts.remove(self.session_obj.path()); + for buffer in s.pending_buffers.borrow_mut().drain(..) { + s.dpy.con.remove_obj(&*buffer); + } } } } @@ -375,32 +536,31 @@ impl ScreencastSession { } impl UsrJayScreencastOwner for StartedScreencast { - fn buffers(&self, buffers: PlaneVec) { + fn buffers(&self, buffers: Vec) { if buffers.len() == 0 { return; } let buffer = &buffers[0]; - *self.port.supported_formats.borrow_mut() = Some(PwClientNodePortSupportedFormats { + *self.port.supported_formats.borrow_mut() = PwClientNodePortSupportedFormats { media_type: Some(SPA_MEDIA_TYPE_video), media_sub_type: Some(SPA_MEDIA_SUBTYPE_raw), video_size: Some(PwPodRectangle { width: buffer.width as _, height: buffer.height as _, }), - formats: vec![buffer.format], - modifiers: vec![buffer.modifier], - }); - let bc = PwClientNodeBufferConfig { - num_buffers: buffers.len(), - planes: buffer.planes.len(), - _stride: Some(buffer.planes[0].stride), - _size: Some(buffer.planes[0].stride * buffer.height as u32), - _align: 16, + formats: vec![PwClientNodePortSupportedFormat { + format: buffer.format, + modifiers: vec![buffer.modifier], + }], + }; + *self.port.buffer_config.borrow_mut() = PwClientNodeBufferConfig { + num_buffers: Some(buffers.len()), + planes: Some(buffer.planes.len()), data_type: SPA_DATA_DmaBuf, }; - self.port.buffer_config.set(Some(bc)); self.node.send_port_update(&self.port, true); self.node.send_active(true); + self.fixated.set(true); *self.buffers.borrow_mut() = buffers; self.buffers_valid.set(false); } @@ -414,7 +574,6 @@ impl UsrJayScreencastOwner for StartedScreencast { self.jay_screencast.release_buffer(idx); }; if !self.buffers_valid.get() { - discard_buffer(); return; } let Some(io) = self.port.io_buffers.get() else { @@ -447,21 +606,47 @@ impl UsrJayScreencastOwner for StartedScreencast { }; } let buffer_id = io.buffer_id.load(Relaxed) as usize; - if buffer_id != idx { - if buffer_id < buffers.len() { - self.jay_screencast.release_buffer(buffer_id); + if self.port_buffer_valid.get() { + if buffer_id != idx { + if buffer_id < buffers.len() { + self.jay_screencast.release_buffer(buffer_id); + } } } io.buffer_id.store(ev.idx, Relaxed); io.status.store(SPA_STATUS_HAVE_DATA.0, Release); - if let Some(wfd) = self.port.node.transport_out.get() { - let _ = uapi::eventfd_write(wfd.raw(), 1); - } + self.port_buffer_valid.set(true); + self.port.node.drive(); } fn destroyed(&self) { self.session.kill(); } + + fn config(&self, config: UsrJayScreencastServerConfig) { + self.width.set(config.width.max(1)); + self.height.set(config.height.max(1)); + self.port.supported_formats.borrow_mut().video_size = Some(PwPodRectangle { + width: self.width.get() as _, + height: self.height.get() as _, + }); + self.node.send_port_update(&self.port, self.fixated.get()); + self.node.send_active(true); + } +} + +impl UsrLinuxBufferParamsOwner for StartedScreencast { + fn created(&self, buffer: Rc) { + self.buffers_valid.set(true); + self.jay_screencast.add_buffer(&buffer); + self.jay_screencast.configure(); + self.dpy.con.remove_obj(&*buffer); + } + + fn failed(&self) { + log::error!("Buffer import failed"); + self.session.kill(); + } } pub(super) fn add_screencast_dbus_members( diff --git a/src/portal/ptr_gui.rs b/src/portal/ptr_gui.rs index c6d8d0a6..fa06fcab 100644 --- a/src/portal/ptr_gui.rs +++ b/src/portal/ptr_gui.rs @@ -599,7 +599,8 @@ impl WindowData { Some(c) => c, _ => return, }; - let (mut width, mut height) = content.layout(&ctx.ctx, scale, f32::INFINITY, f32::INFINITY); + let (mut width, mut height) = + content.layout(&ctx.ctx.ctx, scale, f32::INFINITY, f32::INFINITY); content.data().width.set(width); content.data().height.set(height); width = width.max(1.0); @@ -710,7 +711,7 @@ impl WindowData { self.frame_missed.set(true); let width = (self.width.get() as f64 * self.scale.get().to_f64()).round() as i32; let height = (self.height.get() as f64 * self.scale.get().to_f64()).round() as i32; - let formats = ctx.ctx.formats(); + let formats = &ctx.usable_formats; let format = match formats.get(&ARGB8888.drm) { None => { log::error!("Render context does not support ARGB8888 format"); @@ -724,7 +725,7 @@ impl WindowData { } let modifiers: Vec<_> = format.write_modifiers.iter().copied().collect(); for _ in 0..NUM_BUFFERS { - let bo = match ctx.ctx.allocator().create_bo( + let bo = match ctx.ctx.ctx.allocator().create_bo( &self.dpy.state.dma_buf_ids, width, height, @@ -738,7 +739,7 @@ impl WindowData { return; } }; - let img = match ctx.ctx.clone().dmabuf_img(bo.dmabuf()) { + let img = match ctx.ctx.ctx.clone().dmabuf_img(bo.dmabuf()) { Ok(b) => b, Err(e) => { log::error!("Could not import dmabuf into EGL: {}", ErrorFmt(e)); diff --git a/src/state.rs b/src/state.rs index 941e37dd..50cd36d0 100644 --- a/src/state.rs +++ b/src/state.rs @@ -153,7 +153,7 @@ pub struct State { pub pending_float_titles: AsyncQueue>, pub pending_input_popup_positioning: AsyncQueue>, pub pending_toplevel_screencasts: AsyncQueue>, - pub pending_toplevel_screencast_reallocs: AsyncQueue>, + pub pending_screencast_reallocs_or_reconfigures: AsyncQueue>, pub dbus: Dbus, pub fdcloser: Arc, pub logger: Option>, @@ -814,7 +814,7 @@ impl State { self.pending_float_titles.clear(); self.pending_input_popup_positioning.clear(); self.pending_toplevel_screencasts.clear(); - self.pending_toplevel_screencast_reallocs.clear(); + self.pending_screencast_reallocs_or_reconfigures.clear(); self.render_ctx_watchers.clear(); self.workspace_watchers.clear(); self.toplevel_lists.clear(); diff --git a/src/tree/output.rs b/src/tree/output.rs index 0f157482..98bc6d6f 100644 --- a/src/tree/output.rs +++ b/src/tree/output.rs @@ -591,7 +591,7 @@ impl OutputNode { if (old_width, old_height) != (new_width, new_height) { for sc in self.screencasts.lock().values() { - sc.schedule_realloc(); + sc.schedule_realloc_or_reconfigure(); } } diff --git a/src/tree/toplevel.rs b/src/tree/toplevel.rs index 725f220e..7d193de8 100644 --- a/src/tree/toplevel.rs +++ b/src/tree/toplevel.rs @@ -134,7 +134,7 @@ impl ToplevelNode for T { let prev = data.desired_extents.replace(*rect); if prev.size() != rect.size() { for sc in data.jay_screencasts.lock().values() { - sc.schedule_realloc(); + sc.schedule_realloc_or_reconfigure(); } } if data.is_floating.get() { diff --git a/src/wl_usr/usr_ifs/usr_jay_compositor.rs b/src/wl_usr/usr_ifs/usr_jay_compositor.rs index 9963c44c..7090d29d 100644 --- a/src/wl_usr/usr_ifs/usr_jay_compositor.rs +++ b/src/wl_usr/usr_ifs/usr_jay_compositor.rs @@ -52,6 +52,7 @@ impl UsrJayCompositor { con: self.con.clone(), owner: Default::default(), version: self.version, + formats: Default::default(), }); self.con.request(GetRenderCtx { self_id: self.id, diff --git a/src/wl_usr/usr_ifs/usr_jay_render_ctx.rs b/src/wl_usr/usr_ifs/usr_jay_render_ctx.rs index fafb3fdc..88f5bc48 100644 --- a/src/wl_usr/usr_ifs/usr_jay_render_ctx.rs +++ b/src/wl_usr/usr_ifs/usr_jay_render_ctx.rs @@ -1,11 +1,15 @@ use { crate::{ + format::formats, + gfx_api::GfxFormat, + ifs::jay_render_ctx::FORMATS_SINCE, object::Version, utils::clonecell::CloneCell, wire::{jay_render_ctx::*, JayRenderCtxId}, wl_usr::{usr_object::UsrObject, UsrCon}, }, - std::{convert::Infallible, rc::Rc}, + ahash::AHashMap, + std::{cell::RefCell, convert::Infallible, rc::Rc}, uapi::OwnedFd, }; @@ -14,12 +18,14 @@ pub struct UsrJayRenderCtx { pub con: Rc, pub owner: CloneCell>>, pub version: Version, + pub formats: RefCell>, } pub trait UsrJayRenderCtxOwner { fn no_device(&self) {} - fn device(&self, fd: Rc) { + fn device(&self, fd: Rc, server_formats: Option>) { let _ = fd; + let _ = server_formats; } } @@ -27,6 +33,7 @@ impl JayRenderCtxEventHandler for UsrJayRenderCtx { type Error = Infallible; fn no_device(&self, _ev: NoDevice, _slf: &Rc) -> Result<(), Self::Error> { + self.formats.take(); if let Some(owner) = self.owner.get() { owner.no_device(); } @@ -34,8 +41,38 @@ impl JayRenderCtxEventHandler for UsrJayRenderCtx { } fn device(&self, ev: Device, _slf: &Rc) -> Result<(), Self::Error> { + let formats = self.formats.take(); + let formats = (self.version >= FORMATS_SINCE).then_some(formats); if let Some(owner) = self.owner.get() { - owner.device(ev.fd); + owner.device(ev.fd, formats); + } + Ok(()) + } + + fn read_modifier(&self, ev: ReadModifier, _slf: &Rc) -> Result<(), Self::Error> { + if let Some(format) = self.formats.borrow_mut().get_mut(&ev.format) { + format.read_modifiers.insert(ev.modifier); + } + Ok(()) + } + + fn write_modifier(&self, ev: WriteModifier, _slf: &Rc) -> Result<(), Self::Error> { + if let Some(format) = self.formats.borrow_mut().get_mut(&ev.format) { + format.write_modifiers.insert(ev.modifier); + } + Ok(()) + } + + fn format(&self, ev: Format, _slf: &Rc) -> Result<(), Self::Error> { + if let Some(format) = formats().get(&ev.format) { + self.formats.borrow_mut().insert( + ev.format, + GfxFormat { + format, + read_modifiers: Default::default(), + write_modifiers: Default::default(), + }, + ); } Ok(()) } diff --git a/src/wl_usr/usr_ifs/usr_jay_screencast.rs b/src/wl_usr/usr_ifs/usr_jay_screencast.rs index 8925b7a2..db79b472 100644 --- a/src/wl_usr/usr_ifs/usr_jay_screencast.rs +++ b/src/wl_usr/usr_ifs/usr_jay_screencast.rs @@ -8,7 +8,7 @@ use { wl_usr::{ usr_ifs::{ usr_jay_output::UsrJayOutput, usr_jay_toplevel::UsrJayToplevel, - usr_jay_workspace::UsrJayWorkspace, + usr_jay_workspace::UsrJayWorkspace, usr_wl_buffer::UsrWlBuffer, }, usr_object::UsrObject, UsrCon, @@ -24,7 +24,7 @@ pub struct UsrJayScreencast { pub owner: CloneCell>>, pub version: Version, - pub pending_buffers: RefCell>, + pub pending_buffers: RefCell>, pub pending_planes: RefCell>, pub pending_config: RefCell, @@ -37,10 +37,12 @@ pub struct UsrJayScreencastServerConfig { pub running: bool, pub use_linear_buffers: bool, pub allowed_workspaces: Vec, + pub width: i32, + pub height: i32, } pub trait UsrJayScreencastOwner { - fn buffers(&self, buffers: PlaneVec) { + fn buffers(&self, buffers: Vec) { let _ = buffers; } @@ -116,6 +118,17 @@ impl UsrJayScreencast { idx: idx as _, }); } + + pub fn clear_buffers(&self) { + self.con.request(ClearBuffers { self_id: self.id }); + } + + pub fn add_buffer(&self, buffer: &UsrWlBuffer) { + self.con.request(AddBuffer { + self_id: self.id, + buffer: buffer.id, + }); + } } impl JayScreencastEventHandler for UsrJayScreencast { @@ -228,6 +241,12 @@ impl JayScreencastEventHandler for UsrJayScreencast { }); Ok(()) } + + fn config_size(&self, ev: ConfigSize, _slf: &Rc) -> Result<(), Self::Error> { + self.pending_config.borrow_mut().width = ev.width; + self.pending_config.borrow_mut().height = ev.height; + Ok(()) + } } usr_object_base! { diff --git a/wire/jay_render_ctx.txt b/wire/jay_render_ctx.txt index f9dff00d..b0cacd85 100644 --- a/wire/jay_render_ctx.txt +++ b/wire/jay_render_ctx.txt @@ -13,3 +13,17 @@ event no_device { event device { fd: fd, } + +event read_modifier (since = 7) { + format: u32, + modifier: pod(u64), +} + +event write_modifier (since = 7) { + format: u32, + modifier: pod(u64), +} + +event format (since = 7) { + format: u32, +} diff --git a/wire/jay_screencast.txt b/wire/jay_screencast.txt index e8156ed9..0fd8bf6a 100644 --- a/wire/jay_screencast.txt +++ b/wire/jay_screencast.txt @@ -47,6 +47,13 @@ request set_toplevel { id: id(jay_toplevel), } +request clear_buffers (since = 7) { +} + +request add_buffer (since = 7) { + buffer: id(wl_buffer), +} + # events event plane { @@ -101,3 +108,8 @@ event config_running { event config_done { serial: u32, } + +event config_size (since = 7) { + width: i32, + height: i32, +}