diff options
| author | Michael Yang <admin@my4ng.dev> | 2024-07-30 13:38:25 +1000 |
|---|---|---|
| committer | Ivan Molodetskikh <yalterz@gmail.com> | 2024-08-08 13:32:37 +0300 |
| commit | b6a7b3e9e4715c099b1d0e67252c0a149238d683 (patch) | |
| tree | e0f93b6c04c1f1e136c946b64dcd4b5647579f79 /src | |
| parent | 1cf5cfce064ba9926b9777df34cb5a4add0e1f64 (diff) | |
| download | niri-b6a7b3e9e4715c099b1d0e67252c0a149238d683.tar.gz niri-b6a7b3e9e4715c099b1d0e67252c0a149238d683.tar.bz2 niri-b6a7b3e9e4715c099b1d0e67252c0a149238d683.zip | |
feat: update screencopy to version 3
Diffstat (limited to 'src')
| -rw-r--r-- | src/handlers/mod.rs | 31 | ||||
| -rw-r--r-- | src/niri.rs | 223 | ||||
| -rw-r--r-- | src/protocols/screencopy.rs | 228 | ||||
| -rw-r--r-- | src/render_helpers/mod.rs | 36 |
4 files changed, 375 insertions, 143 deletions
diff --git a/src/handlers/mod.rs b/src/handlers/mod.rs index ce2896b2..b5c4392d 100644 --- a/src/handlers/mod.rs +++ b/src/handlers/mod.rs @@ -17,6 +17,7 @@ use smithay::input::{keyboard, Seat, SeatHandler, SeatState}; use smithay::output::Output; use smithay::reexports::rustix::fs::{fcntl_setfl, OFlags}; use smithay::reexports::wayland_protocols::xdg::shell::server::xdg_toplevel; +use smithay::reexports::wayland_protocols_wlr::screencopy::v1::server::zwlr_screencopy_manager_v1::ZwlrScreencopyManagerV1; use smithay::reexports::wayland_server::protocol::wl_data_source::WlDataSource; use smithay::reexports::wayland_server::protocol::wl_output::WlOutput; use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface; @@ -69,7 +70,7 @@ use crate::protocols::foreign_toplevel::{ }; use crate::protocols::gamma_control::{GammaControlHandler, GammaControlManagerState}; use crate::protocols::output_management::{OutputManagementHandler, OutputManagementManagerState}; -use crate::protocols::screencopy::{Screencopy, ScreencopyHandler}; +use crate::protocols::screencopy::{Screencopy, ScreencopyHandler, ScreencopyManagerState}; use crate::utils::{output_size, send_scale_transform}; use crate::{ delegate_foreign_toplevel, delegate_gamma_control, delegate_output_management, @@ -419,14 +420,30 @@ impl ForeignToplevelHandler for State { delegate_foreign_toplevel!(State); impl ScreencopyHandler for State { - fn frame(&mut self, screencopy: Screencopy) { - if let Err(err) = self - .niri - .render_for_screencopy(&mut self.backend, screencopy) - { - warn!("error rendering for screencopy: {err:?}"); + fn frame(&mut self, manager: &ZwlrScreencopyManagerV1, screencopy: Screencopy) { + // If with_damage then push it onto the queue for redraw of the output, + // otherwise render it immediately. + if screencopy.with_damage() { + let Some(queue) = self.niri.screencopy_state.get_queue_mut(manager) else { + trace!("screencopy manager destroyed already"); + return; + }; + queue.push(screencopy); + } else { + self.backend.with_primary_renderer(|renderer| { + if let Err(err) = self + .niri + .render_for_screencopy_without_damage(renderer, manager, screencopy) + { + warn!("error rendering for screencopy: {err:?}"); + } + }); } } + + fn screencopy_state(&mut self) -> &mut ScreencopyManagerState { + &mut self.niri.screencopy_state + } } delegate_screencopy!(State); diff --git a/src/niri.rs b/src/niri.rs index 7ee4d2f7..d5103eed 100644 --- a/src/niri.rs +++ b/src/niri.rs @@ -1,4 +1,4 @@ -use std::cell::{Cell, RefCell}; +use std::cell::{Cell, OnceCell, RefCell}; use std::collections::{HashMap, HashSet}; use std::ffi::OsString; use std::path::PathBuf; @@ -9,7 +9,7 @@ use std::time::{Duration, Instant}; use std::{env, mem, thread}; use _server_decoration::server::org_kde_kwin_server_decoration_manager::Mode as KdeDecorationsMode; -use anyhow::{ensure, Context}; +use anyhow::{bail, ensure, Context}; use calloop::futures::Scheduler; use niri_config::{ Config, FloatOrInt, Key, Modifiers, PreviewRender, TrackLayout, WorkspaceReference, @@ -31,6 +31,7 @@ use smithay::backend::renderer::element::{ PrimaryScanoutOutput, RenderElementStates, }; use smithay::backend::renderer::gles::GlesRenderer; +use smithay::backend::renderer::Unbind; use smithay::desktop::utils::{ bbox_from_surface_tree, output_update, send_dmabuf_feedback_surface_tree, send_frames_surface_tree, surface_presentation_feedback_flags_from_states, @@ -44,7 +45,7 @@ use smithay::desktop::{ use smithay::input::keyboard::{Layout as KeyboardLayout, XkbContextHandler}; use smithay::input::pointer::{CursorIcon, CursorImageAttributes, CursorImageStatus, MotionEvent}; use smithay::input::{Seat, SeatState}; -use smithay::output::{self, Output, PhysicalProperties, Subpixel}; +use smithay::output::{self, Output, OutputModeSource, PhysicalProperties, Subpixel}; use smithay::reexports::calloop::generic::Generic; use smithay::reexports::calloop::timer::{TimeoutAction, Timer}; use smithay::reexports::calloop::{ @@ -53,6 +54,7 @@ use smithay::reexports::calloop::{ use smithay::reexports::wayland_protocols::ext::session_lock::v1::server::ext_session_lock_v1::ExtSessionLockV1; use smithay::reexports::wayland_protocols::xdg::shell::server::xdg_toplevel::WmCapabilities; use smithay::reexports::wayland_protocols_misc::server_decoration as _server_decoration; +use smithay::reexports::wayland_protocols_wlr::screencopy::v1::server::zwlr_screencopy_manager_v1::ZwlrScreencopyManagerV1; use smithay::reexports::wayland_server::backend::{ ClientData, ClientId, DisconnectReason, GlobalId, }; @@ -116,7 +118,7 @@ use crate::layout::{Layout, LayoutElement as _, MonitorRenderElement}; use crate::protocols::foreign_toplevel::{self, ForeignToplevelManagerState}; use crate::protocols::gamma_control::GammaControlManagerState; use crate::protocols::output_management::OutputManagementManagerState; -use crate::protocols::screencopy::{Screencopy, ScreencopyManagerState}; +use crate::protocols::screencopy::{Screencopy, ScreencopyBuffer, ScreencopyManagerState}; use crate::pw_utils::{Cast, PipeWire}; #[cfg(feature = "xdp-gnome-screencast")] use crate::pw_utils::{CastSizeChange, CastTarget, PwToNiri}; @@ -125,8 +127,8 @@ use crate::render_helpers::primary_gpu_texture::PrimaryGpuTextureRenderElement; use crate::render_helpers::renderer::NiriRenderer; use crate::render_helpers::texture::TextureBuffer; use crate::render_helpers::{ - render_to_encompassing_texture, render_to_shm, render_to_texture, render_to_vec, shaders, - RenderTarget, + render_to_dmabuf, render_to_encompassing_texture, render_to_shm, render_to_texture, + render_to_vec, shaders, RenderTarget, }; use crate::ui::config_error_notification::ConfigErrorNotification; use crate::ui::exit_confirm_dialog::ExitConfirmDialog; @@ -2041,6 +2043,8 @@ impl Niri { #[cfg(feature = "xdp-gnome-screencast")] self.stop_casts_for_target(CastTarget::Output(output.downgrade())); + self.remove_screencopy_output(output); + // Disable the output global and remove some time later to give the clients some time to // process it. let global = state.global; @@ -3091,16 +3095,20 @@ impl Niri { // However, this should probably be restricted to sending frame callbacks to more surfaces, // to err on the safe side. self.send_frame_callbacks(output); - - #[cfg(feature = "xdp-gnome-screencast")] backend.with_primary_renderer(|renderer| { - // Render and send to PipeWire screencast streams. - self.render_for_screen_cast(renderer, output, target_presentation_time); + #[cfg(feature = "xdp-gnome-screencast")] + { + // Render and send to PipeWire screencast streams. + self.render_for_screen_cast(renderer, output, target_presentation_time); + + // FIXME: when a window is hidden, it should probably still receive frame callbacks + // and get rendered for screen cast. This is currently + // unimplemented, but happens to work by chance, since output + // redrawing is more eager than it should be. + self.render_windows_for_screen_cast(renderer, output, target_presentation_time); + } - // FIXME: when a window is hidden, it should probably still receive frame callbacks and - // get rendered for screen cast. This is currently unimplemented, but happens to work - // by chance, since output redrawing is more eager than it should be. - self.render_windows_for_screen_cast(renderer, output, target_presentation_time); + self.render_for_screencopy_with_damage(renderer, output); }); } @@ -3736,47 +3744,163 @@ impl Niri { } } - pub fn render_for_screencopy( + pub fn render_for_screencopy_with_damage( &mut self, - backend: &mut Backend, + renderer: &mut GlesRenderer, + output: &Output, + ) { + let _span = tracy_client::span!("Niri::render_for_screencopy_with_damage"); + + let mut screencopy_state = mem::take(&mut self.screencopy_state); + let elements = OnceCell::new(); + + for queue in screencopy_state.queues_mut() { + let (damage_tracker, screencopy) = queue.split(); + if let Some(screencopy) = screencopy { + if screencopy.output() == output { + let elements = elements.get_or_init(|| { + self.render(renderer, output, true, RenderTarget::ScreenCapture) + }); + // FIXME: skip elements if not including pointers + let render_result = Self::render_for_screencopy_internal( + renderer, + output, + elements, + true, + damage_tracker, + screencopy, + ); + match render_result { + Ok(damages) => { + if let Some(damages) = damages { + screencopy.damage(damages); + queue.pop().submit(false); + } else { + trace!("no damage found, waiting till next redraw"); + } + } + Err(err) => { + // Recreate damage tracker to report full damage next check. + *damage_tracker = + OutputDamageTracker::new((0, 0), 1.0, Transform::Normal); + queue.pop(); + warn!("error rendering for screencopy: {err:?}"); + } + } + }; + } + } + + self.screencopy_state = screencopy_state; + } + + pub fn render_for_screencopy_without_damage( + &mut self, + renderer: &mut GlesRenderer, + manager: &ZwlrScreencopyManagerV1, screencopy: Screencopy, ) -> anyhow::Result<()> { - let output = screencopy.output().clone(); - ensure!(self.output_state.contains_key(&output), "output is missing"); + let _span = tracy_client::span!("Niri::render_for_screencopy"); - self.layout.update_render_elements(&output); + let output = screencopy.output(); + ensure!( + self.output_state.contains_key(output), + "screencopy output missing" + ); - backend - .with_primary_renderer(move |renderer| { - let elements = self - .render( - renderer, - &output, - screencopy.overlay_cursor(), - RenderTarget::ScreenCapture, - ) - .into_iter() - .rev(); - - let region_loc = screencopy.region_loc(); - let elements = elements.map(|element| { - RelocateRenderElement::from_element( - element, - region_loc.upscale(-1), - Relocate::Relative, - ) - }); + self.layout.update_render_elements(output); - let scale = output.current_scale().fractional_scale().into(); - let transform = output.current_transform(); - render_to_shm(renderer, screencopy.buffer(), scale, transform, elements) - .context("error rendering to screencopy shm buffer: {err:?}")?; + let elements = self.render( + renderer, + output, + screencopy.overlay_cursor(), + RenderTarget::ScreenCapture, + ); + let Some(queue) = self.screencopy_state.get_queue_mut(manager) else { + bail!("screencopy manager destroyed already"); + }; + let damage_tracker = queue.split().0; + + let render_result = Self::render_for_screencopy_internal( + renderer, + output, + &elements, + false, + damage_tracker, + &screencopy, + ) + .map(|_damage| ()); + + match render_result { + Ok(()) => screencopy.submit(false), + Err(_) => { + // Recreate damage tracker to report full damage next check. + *damage_tracker = OutputDamageTracker::new((0, 0), 1.0, Transform::Normal); + } + } + render_result + } + + fn render_for_screencopy_internal<'a>( + renderer: &mut GlesRenderer, + output: &Output, + elements: &[OutputRenderElements<GlesRenderer>], + with_damage: bool, + damage_tracker: &'a mut OutputDamageTracker, + screencopy: &Screencopy, + ) -> anyhow::Result<Option<&'a Vec<Rectangle<i32, Physical>>>> { + let OutputModeSource::Static { + size: last_size, + scale: last_scale, + transform: last_transform, + } = damage_tracker.mode().clone() + else { + unreachable!("damage tracker must have static mode"); + }; - screencopy.submit(false); + let size = screencopy.buffer_size(); + let scale: Scale<f64> = output.current_scale().fractional_scale().into(); + let transform = output.current_transform(); + + if size != last_size || scale != last_scale || transform != last_transform { + *damage_tracker = OutputDamageTracker::new(size, scale, transform); + } - Ok(()) + let region_loc = screencopy.region_loc(); + let elements = elements + .iter() + .map(|element| { + RelocateRenderElement::from_element( + element, + region_loc.upscale(-1), + Relocate::Relative, + ) }) - .context("primary renderer is missing")? + .collect::<Vec<_>>(); + + // Just checked damage tracker has static mode + let damages = damage_tracker.damage_output(1, &elements).unwrap().0; + if with_damage && damages.is_none() { + return Ok(None); + } + + let elements = elements.iter().rev(); + + match screencopy.buffer() { + ScreencopyBuffer::Dmabuf(dmabuf) => { + let _sync = + render_to_dmabuf(renderer, dmabuf.clone(), size, scale, transform, elements) + .context("error rendering to screencopy dmabuf")?; + } + ScreencopyBuffer::Shm(wl_buffer) => { + render_to_shm(renderer, wl_buffer, size, scale, transform, elements) + .context("error rendering to screencopy shm buffer")?; + } + } + if let Err(err) = renderer.unbind() { + warn!("error unbinding after rendering for screencopy: {err:?}"); + } + Ok(damages) } #[cfg(feature = "xdp-gnome-screencast")] @@ -3829,6 +3953,13 @@ impl Niri { } } + pub fn remove_screencopy_output(&mut self, output: &Output) { + let _span = tracy_client::span!("Niri::remove_screencopy_output"); + for queue in self.screencopy_state.queues_mut() { + queue.remove_output(output); + } + } + pub fn debug_toggle_damage(&mut self) { self.debug_draw_damage = !self.debug_draw_damage; diff --git a/src/protocols/screencopy.rs b/src/protocols/screencopy.rs index fd6bda68..74c891d0 100644 --- a/src/protocols/screencopy.rs +++ b/src/protocols/screencopy.rs @@ -1,7 +1,11 @@ +use std::collections::HashMap; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; use std::time::UNIX_EPOCH; +use smithay::backend::allocator::dmabuf::Dmabuf; +use smithay::backend::allocator::{Buffer, Fourcc}; +use smithay::backend::renderer::damage::OutputDamageTracker; use smithay::output::Output; use smithay::reexports::wayland_protocols_wlr::screencopy::v1::server::zwlr_screencopy_frame_v1::{ Flags, ZwlrScreencopyFrameV1, @@ -11,17 +15,60 @@ use smithay::reexports::wayland_protocols_wlr::screencopy::v1::server::{ zwlr_screencopy_frame_v1, zwlr_screencopy_manager_v1, }; use smithay::reexports::wayland_server::protocol::wl_buffer::WlBuffer; -use smithay::reexports::wayland_server::protocol::wl_shm; +use smithay::reexports::wayland_server::protocol::wl_shm::Format; use smithay::reexports::wayland_server::{ Client, DataInit, Dispatch, DisplayHandle, GlobalDispatch, New, Resource, }; -use smithay::utils::{Physical, Point, Rectangle, Size}; -use smithay::wayland::shm; +use smithay::utils::{Physical, Point, Rectangle, Size, Transform}; +use smithay::wayland::{dmabuf, shm}; -// We do not support copy_with_damage() semantics yet. -const VERSION: u32 = 1; +const VERSION: u32 = 3; -pub struct ScreencopyManagerState; +pub struct ScreencopyQueue { + damage_tracker: OutputDamageTracker, + screencopies: Vec<Screencopy>, +} + +impl Default for ScreencopyQueue { + fn default() -> Self { + Self::new() + } +} + +impl ScreencopyQueue { + pub fn new() -> Self { + Self { + damage_tracker: OutputDamageTracker::new((0, 0), 1.0, Transform::Normal), + screencopies: Vec::new(), + } + } + + pub fn split(&mut self) -> (&mut OutputDamageTracker, Option<&Screencopy>) { + let ScreencopyQueue { + damage_tracker, + screencopies, + } = self; + (damage_tracker, screencopies.first()) + } + + pub fn push(&mut self, screencopy: Screencopy) { + self.screencopies.push(screencopy); + } + + pub fn pop(&mut self) -> Screencopy { + self.screencopies.pop().unwrap() + } + + pub fn remove_output(&mut self, output: &Output) { + self.screencopies + .retain(|screencopy| screencopy.output() != output); + } +} + +#[derive(Default)] +pub struct ScreencopyManagerState { + queues: HashMap<ZwlrScreencopyManagerV1, ScreencopyQueue>, +} pub struct ScreencopyManagerGlobalData { filter: Box<dyn for<'c> Fn(&'c Client) -> bool + Send + Sync>, @@ -42,7 +89,28 @@ impl ScreencopyManagerState { }; display.create_global::<D, ZwlrScreencopyManagerV1, _>(VERSION, global_data); - Self + Self { + queues: HashMap::new(), + } + } + + pub fn bind(&mut self, manager: &ZwlrScreencopyManagerV1) { + // Clean up all entries if its manager is dead and its queue is empty. + self.queues + .retain(|k, v| k.is_alive() || !v.screencopies.is_empty()); + + self.queues.insert(manager.clone(), ScreencopyQueue::new()); + } + + pub fn get_queue_mut( + &mut self, + manager: &ZwlrScreencopyManagerV1, + ) -> Option<&mut ScreencopyQueue> { + self.queues.get_mut(manager) + } + + pub fn queues_mut(&mut self) -> impl Iterator<Item = &mut ScreencopyQueue> { + self.queues.values_mut() } } @@ -56,14 +124,15 @@ where D: 'static, { fn bind( - _state: &mut D, + state: &mut D, _display: &DisplayHandle, _client: &Client, manager: New<ZwlrScreencopyManagerV1>, _manager_state: &ScreencopyManagerGlobalData, data_init: &mut DataInit<'_, D>, ) { - data_init.init(manager, ()); + let manager = data_init.init(manager, ()); + state.screencopy_state().bind(&manager); } fn can_view(client: Client, global_data: &ScreencopyManagerGlobalData) -> bool { @@ -82,7 +151,7 @@ where fn request( _state: &mut D, _client: &Client, - _manager: &ZwlrScreencopyManagerV1, + manager: &ZwlrScreencopyManagerV1, request: zwlr_screencopy_manager_v1::Request, _data: &(), _display: &DisplayHandle, @@ -174,6 +243,7 @@ where let frame = data_init.init( frame, ScreencopyFrameState::Pending { + manager: manager.clone(), info, copied: Arc::new(AtomicBool::new(false)), }, @@ -181,30 +251,31 @@ where // Send desired SHM buffer parameters. frame.buffer( - wl_shm::Format::Argb8888, + Format::Xrgb8888, buffer_size.w as u32, buffer_size.h as u32, buffer_size.w as u32 * 4, ); - // if manager.version() >= 3 { - // // Send desired DMA buffer parameters. - // frame.linux_dmabuf( - // Fourcc::Argb8888 as u32, - // buffer_size.w as u32, - // buffer_size.h as u32, - // ); - // - // // Notify client that all supported buffers were enumerated. - // frame.buffer_done(); - // } + if frame.version() >= 3 { + // Send desired DMA buffer parameters. + frame.linux_dmabuf( + Fourcc::Xrgb8888 as u32, + buffer_size.w as u32, + buffer_size.h as u32, + ); + + // Notify client that all supported buffers were enumerated. + frame.buffer_done(); + } } } /// Handler trait for wlr-screencopy. pub trait ScreencopyHandler { /// Handle new screencopy request. - fn frame(&mut self, frame: Screencopy); + fn frame(&mut self, manager: &ZwlrScreencopyManagerV1, screencopy: Screencopy); + fn screencopy_state(&mut self) -> &mut ScreencopyManagerState; } #[allow(missing_docs)] @@ -236,6 +307,7 @@ pub struct ScreencopyFrameInfo { pub enum ScreencopyFrameState { Failed, Pending { + manager: ZwlrScreencopyManagerV1, info: ScreencopyFrameInfo, copied: Arc<AtomicBool>, }, @@ -260,9 +332,13 @@ where return; } - let (info, copied) = match data { - ScreencopyFrameState::Failed => return, - ScreencopyFrameState::Pending { info, copied } => (info, copied), + let ScreencopyFrameState::Pending { + manager, + info, + copied, + } = data + else { + return; }; if copied.load(Ordering::SeqCst) { @@ -275,44 +351,71 @@ where let (buffer, with_damage) = match request { zwlr_screencopy_frame_v1::Request::Copy { buffer } => (buffer, false), - // zwlr_screencopy_frame_v1::Request::CopyWithDamage { buffer } => (buffer, true), + zwlr_screencopy_frame_v1::Request::CopyWithDamage { buffer } => (buffer, true), _ => unreachable!(), }; - if !shm::with_buffer_contents(&buffer, |_buf, shm_len, buffer_data| { - buffer_data.format == wl_shm::Format::Argb8888 - && buffer_data.stride == info.buffer_size.w * 4 - && buffer_data.height == info.buffer_size.h - && shm_len as i32 == buffer_data.stride * buffer_data.height + let size = info.buffer_size; + + let buffer = if let Ok(dmabuf) = dmabuf::get_dmabuf(&buffer) { + if dmabuf.format().code == Fourcc::Xrgb8888 + && dmabuf.width() == size.w as u32 + && dmabuf.height() == size.h as u32 + { + ScreencopyBuffer::Dmabuf(dmabuf.clone()) + } else { + frame.post_error( + zwlr_screencopy_frame_v1::Error::InvalidBuffer, + "invalid dmabuf parameters", + ); + return; + } + } else if shm::with_buffer_contents(&buffer, |_, shm_len, buffer_data| { + buffer_data.format == Format::Xrgb8888 + && buffer_data.width == size.w + && buffer_data.height == size.h + && buffer_data.stride == size.w * 4 + && shm_len == buffer_data.stride as usize * buffer_data.height as usize }) .unwrap_or(false) { + ScreencopyBuffer::Shm(buffer) + } else { frame.post_error( zwlr_screencopy_frame_v1::Error::InvalidBuffer, "invalid buffer", ); return; - } + }; copied.store(true, Ordering::SeqCst); - state.frame(Screencopy { - with_damage, - buffer, - frame: frame.clone(), - info: info.clone(), - submitted: false, - }); + state.frame( + manager, + Screencopy { + buffer, + frame: frame.clone(), + info: info.clone(), + with_damage, + submitted: false, + }, + ); } } +/// Screencopy buffer. +#[derive(Clone)] +pub enum ScreencopyBuffer { + Dmabuf(Dmabuf), + Shm(WlBuffer), +} + /// Screencopy frame. pub struct Screencopy { info: ScreencopyFrameInfo, frame: ZwlrScreencopyFrameV1, - #[allow(unused)] + buffer: ScreencopyBuffer, with_damage: bool, - buffer: WlBuffer, submitted: bool, } @@ -326,7 +429,7 @@ impl Drop for Screencopy { impl Screencopy { /// Get the target buffer to copy to. - pub fn buffer(&self) -> &WlBuffer { + pub fn buffer(&self) -> &ScreencopyBuffer { &self.buffer } @@ -346,14 +449,16 @@ impl Screencopy { self.info.overlay_cursor } - // pub fn damage(&mut self, damage: &[Rectangle<i32, Physical>]) { - // assert!(self.with_damage); - // - // for Rectangle { loc, size } in damage { - // self.frame - // .damage(loc.x as u32, loc.y as u32, size.w as u32, size.h as u32); - // } - // } + pub fn with_damage(&self) -> bool { + self.with_damage + } + + pub fn damage(&self, damages: &[Rectangle<i32, Physical>]) { + for Rectangle { loc, size } in damages { + self.frame + .damage(loc.x as u32, loc.y as u32, size.w as u32, size.h as u32); + } + } /// Submit the copied content. pub fn submit(mut self, y_invert: bool) { @@ -374,25 +479,4 @@ impl Screencopy { // Mark frame as submitted to ensure destructor isn't run. self.submitted = true; } - - // pub fn submit_after_sync<T>( - // self, - // y_invert: bool, - // sync_point: Option<OwnedFd>, - // event_loop: &LoopHandle<'_, T>, - // ) { - // match sync_point { - // None => self.submit(y_invert), - // Some(sync_fd) => { - // let source = Generic::new(sync_fd, Interest::READ, Mode::OneShot); - // let mut screencopy = Some(self); - // event_loop - // .insert_source(source, move |_, _, _| { - // screencopy.take().unwrap().submit(y_invert); - // Ok(PostAction::Remove) - // }) - // .unwrap(); - // } - // } - // } } diff --git a/src/render_helpers/mod.rs b/src/render_helpers/mod.rs index 12ebd024..f69eec05 100644 --- a/src/render_helpers/mod.rs +++ b/src/render_helpers/mod.rs @@ -2,12 +2,13 @@ use std::ptr; use anyhow::{ensure, Context}; use niri_config::BlockOutFrom; -use smithay::backend::allocator::Fourcc; +use smithay::backend::allocator::dmabuf::Dmabuf; +use smithay::backend::allocator::{Buffer, Fourcc}; use smithay::backend::renderer::element::utils::{Relocate, RelocateRenderElement}; use smithay::backend::renderer::element::{Kind, RenderElement}; use smithay::backend::renderer::gles::{GlesMapping, GlesRenderer, GlesTexture}; use smithay::backend::renderer::sync::SyncPoint; -use smithay::backend::renderer::{buffer_dimensions, Bind, ExportMem, Frame, Offscreen, Renderer}; +use smithay::backend::renderer::{Bind, ExportMem, Frame, Offscreen, Renderer}; use smithay::reexports::wayland_server::protocol::wl_buffer::WlBuffer; use smithay::reexports::wayland_server::protocol::wl_shm; use smithay::utils::{Logical, Physical, Point, Rectangle, Scale, Size, Transform}; @@ -233,16 +234,19 @@ pub fn render_to_vec( Ok(copy.to_vec()) } -#[cfg(feature = "xdp-gnome-screencast")] pub fn render_to_dmabuf( renderer: &mut GlesRenderer, - dmabuf: smithay::backend::allocator::dmabuf::Dmabuf, + dmabuf: Dmabuf, size: Size<i32, Physical>, scale: Scale<f64>, transform: Transform, elements: impl Iterator<Item = impl RenderElement<GlesRenderer>>, ) -> anyhow::Result<SyncPoint> { let _span = tracy_client::span!(); + ensure!( + dmabuf.width() == size.w as u32 && dmabuf.height() == size.h as u32, + "invalid buffer size" + ); renderer.bind(dmabuf).context("error binding texture")?; render_elements(renderer, size, scale, transform, elements) } @@ -250,32 +254,28 @@ pub fn render_to_dmabuf( pub fn render_to_shm( renderer: &mut GlesRenderer, buffer: &WlBuffer, + size: Size<i32, Physical>, scale: Scale<f64>, transform: Transform, elements: impl Iterator<Item = impl RenderElement<GlesRenderer>>, ) -> anyhow::Result<()> { let _span = tracy_client::span!(); - - let buffer_size = buffer_dimensions(buffer).context("error getting buffer dimensions")?; - let size = buffer_size.to_logical(1, Transform::Normal).to_physical(1); - - let mapping = - render_and_download(renderer, size, scale, transform, Fourcc::Argb8888, elements)?; - let bytes = renderer - .map_texture(&mapping) - .context("error mapping texture")?; - shm::with_buffer_contents_mut(buffer, |shm_buffer, shm_len, buffer_data| { ensure!( // The buffer prefers pixels in little endian ... - buffer_data.format == wl_shm::Format::Argb8888 - && buffer_data.stride == size.w * 4 + buffer_data.format == wl_shm::Format::Xrgb8888 + && buffer_data.width == size.w && buffer_data.height == size.h - && shm_len as i32 == buffer_data.stride * buffer_data.height, + && buffer_data.stride == size.w * 4 + && shm_len == buffer_data.stride as usize * buffer_data.height as usize, "invalid buffer format or size" ); + let mapping = + render_and_download(renderer, size, scale, transform, Fourcc::Xrgb8888, elements)?; - ensure!(bytes.len() == shm_len, "mapped buffer has wrong length"); + let bytes = renderer + .map_texture(&mapping) + .context("error mapping texture")?; unsafe { let _span = tracy_client::span!("copy_nonoverlapping"); |
