feat: update screencopy to version 3

This commit is contained in:
Michael Yang
2024-07-30 13:38:25 +10:00
committed by Ivan Molodetskikh
parent 1cf5cfce06
commit b6a7b3e9e4
4 changed files with 374 additions and 142 deletions
+24 -7
View File
@@ -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);
+176 -45
View File
@@ -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();
self.layout.update_render_elements(output);
let region_loc = screencopy.region_loc();
let elements = elements.map(|element| {
RelocateRenderElement::from_element(
element,
region_loc.upscale(-1),
Relocate::Relative,
)
});
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 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 render_result = Self::render_for_screencopy_internal(
renderer,
output,
&elements,
false,
damage_tracker,
&screencopy,
)
.map(|_damage| ());
screencopy.submit(false);
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
}
Ok(())
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");
};
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);
}
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;
+156 -72
View File
@@ -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();
// }
// }
// }
}
+18 -18
View File
@@ -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");