cosmic-screencopy-v2

This commit is contained in:
Victoria Brekenfeld 2024-03-12 19:42:48 +01:00 committed by Victoria Brekenfeld
parent 973cfed87b
commit b40d153809
27 changed files with 3647 additions and 2743 deletions

View file

@ -1,19 +1,23 @@
// SPDX-License-Identifier: GPL-3.0-only
use crate::{
backend::render::{workspace_elements, CLEAR_COLOR},
backend::render::{
element::{CosmicElement, DamageElement},
workspace_elements, CLEAR_COLOR,
},
config::OutputConfig,
shell::Shell,
state::{BackendData, Common, Fps, SurfaceDmabufFeedback},
utils::prelude::*,
wayland::{
handlers::screencopy::{render_session, UserdataExt},
protocols::screencopy::{BufferParams, Session as ScreencopySession},
handlers::screencopy::{submit_buffer, FrameHolder, SessionData},
protocols::screencopy::{
FailureReason, Frame as ScreencopyFrame, Session as ScreencopySession,
},
},
};
use anyhow::{Context, Result};
use cosmic_protocols::screencopy::v1::server::zcosmic_screencopy_session_v1::FailureReason;
use libc::dev_t;
use smithay::{
backend::{
@ -31,8 +35,8 @@ use smithay::{
libinput::{LibinputInputBackend, LibinputSessionInterface},
renderer::{
buffer_dimensions,
damage::{Error as RenderError, RenderOutputResult},
element::Element,
damage::Error as RenderError,
element::{Element, RenderElementStates},
gles::GlesRenderbuffer,
glow::GlowRenderer,
multigpu::{gbm::GbmGlesBackend, Error as MultiError, GpuManager},
@ -66,7 +70,7 @@ use smithay::{
Client, DisplayHandle, Weak,
},
},
utils::{DeviceFd, Size, Transform},
utils::{Buffer as BufferCoords, DeviceFd, Physical, Rectangle, Size, Transform},
wayland::{
dmabuf::{get_dmabuf, DmabufFeedbackBuilder, DmabufGlobal},
drm_lease::{DrmLease, DrmLeaseState},
@ -82,6 +86,7 @@ use std::{
collections::{HashMap, HashSet},
fmt,
path::PathBuf,
sync::mpsc::Receiver,
time::Duration,
};
@ -93,6 +98,14 @@ use super::render::{element::AsGlowRenderer, init_shaders, CursorMode, GlMultiRe
// for now we assume we need at least 3ms
const MIN_DISPLAY_TIME: Duration = Duration::from_millis(3);
/*
TODO Screencopy:
- Fixup blit, include additional damage
- Send accurate presentation time
- Handle early exit no damage case
- input...
*/
#[derive(Debug)]
pub struct KmsState {
pub devices: HashMap<DrmNode, Device>,
@ -154,7 +167,10 @@ pub struct Surface {
pub type GbmDrmCompositor = DrmCompositor<
GbmAllocator<DrmDeviceFd>,
GbmDevice<DrmDeviceFd>,
Option<OutputPresentationFeedback>,
Option<(
OutputPresentationFeedback,
Receiver<(ScreencopyFrame, Vec<Rectangle<i32, BufferCoords>>)>,
)>,
DrmDeviceFd,
>;
@ -192,7 +208,6 @@ pub fn init_backend(
&state.common.event_loop_handle,
output,
None,
None,
) {
error!(
?err,
@ -328,16 +343,10 @@ pub fn init_backend(
surface.pending = false;
}
for output in state.common.shell.outputs() {
let sessions = output.pending_buffers().collect::<Vec<_>>();
if let Err(err) = state.backend.kms().schedule_render(
&state.common.event_loop_handle,
output,
None,
if !sessions.is_empty() {
Some(sessions)
} else {
None
},
) {
error!(
?err,
@ -472,7 +481,7 @@ impl State {
match surface.surface.as_mut().map(|x| x.frame_submitted()) {
Some(Ok(feedback)) => {
if let Some(mut feedback) = feedback.flatten() {
if let Some((mut feedback, frames)) = feedback.flatten() {
let submit_time =
match metadata.take().map(|data| data.time) {
Some(DrmEventTime::Monotonic(tp)) => Some(tp),
@ -511,6 +520,14 @@ impl State {
seq as u64,
flags,
);
while let Ok((frame, damage)) = frames.recv() {
frame.success(
surface.output.current_transform(),
damage,
clock,
);
}
}
surface.pending = false;
@ -533,21 +550,11 @@ impl State {
};
if let Some((output, avg_time)) = rescheduled {
let mut scheduled_sessions =
state.workspace_session_for_output(&output);
let mut output_sessions = output.pending_buffers().peekable();
if output_sessions.peek().is_some() {
scheduled_sessions
.get_or_insert_with(Vec::new)
.extend(output_sessions);
}
let _estimated_rendertime = std::cmp::max(avg_time, MIN_DISPLAY_TIME);
if let Err(err) = state.backend.kms().schedule_render(
&state.common.event_loop_handle,
&output,
None, //Some(estimated_rendertime),
scheduled_sessions,
) {
warn!(?err, "Failed to schedule render.");
}
@ -1160,7 +1167,6 @@ impl Surface {
render_node: Option<&DrmNode>,
target_node: &DrmNode,
state: &mut Common,
screencopy: Option<&[(ScreencopySession, BufferParams)]>,
) -> Result<()> {
if self.surface.is_none() {
return Ok(());
@ -1192,7 +1198,7 @@ impl Surface {
.map(|((w, start), idx)| (w.handle, idx, start));
let workspace = (workspace.handle, idx);
let elements = workspace_elements(
let mut elements = workspace_elements(
Some(&render_node),
&mut renderer,
state,
@ -1208,6 +1214,65 @@ impl Surface {
})?;
self.fps.elements();
let frames: Vec<(
ScreencopySession,
ScreencopyFrame,
Result<(Option<Vec<Rectangle<i32, Physical>>>, RenderElementStates), OutputNoMode>,
)> = self
.output
.take_pending_frames()
.into_iter()
.map(|(session, frame)| {
let additional_damage = frame.damage();
let session_data = session.user_data().get::<SessionData>().unwrap();
let mut damage_tracking = session_data.borrow_mut();
let old_len = if !additional_damage.is_empty() {
let area = self
.output
.current_mode()
.unwrap()
/* TODO: Mode is Buffer..., why is this Physical in the first place */
.size
.to_logical(1)
.to_buffer(1, Transform::Normal)
.to_f64();
let old_len = elements.len();
elements.extend(
additional_damage
.into_iter()
.map(|rect| {
rect.to_f64()
.to_logical(
self.output.current_scale().fractional_scale(),
self.output.current_transform(),
&area,
)
.to_i32_round()
})
.map(DamageElement::new)
.map(Into::into),
);
Some(old_len)
} else {
None
};
let buffer = frame.buffer();
let age = damage_tracking.age_for_buffer(&buffer);
let res = damage_tracking.dt.damage_output(age, &elements);
if let Some(old_len) = old_len {
elements.truncate(old_len);
}
std::mem::drop(damage_tracking);
(session, frame, res)
})
.collect();
let res = compositor.render_frame(
&mut renderer,
&elements,
@ -1217,8 +1282,13 @@ impl Surface {
match res {
Ok(frame_result) => {
let (tx, rx) = std::sync::mpsc::channel();
let feedback = if !frame_result.is_empty {
Some(state.take_presentation_feedback(&self.output, &frame_result.states))
Some((
state.take_presentation_feedback(&self.output, &frame_result.states),
rx,
))
} else {
None
};
@ -1228,93 +1298,152 @@ impl Surface {
elem.sync.wait();
}
}
match compositor.queue_frame(feedback) {
Ok(()) => {
self.pending = true;
}
Err(FrameError::EmptyFrame) => {
tracing::debug!("Stopped rendering");
}
Err(err) => {
return Err(err).with_context(|| "Failed to submit result for display")
}
};
x @ Ok(()) | x @ Err(FrameError::EmptyFrame) => {
for (session, frame, res) in frames {
let damage = match res {
Ok((damage, _)) => damage,
Err(err) => {
tracing::warn!(?err, "Failed to screencopy");
session
.user_data()
.get::<SessionData>()
.unwrap()
.borrow_mut()
.reset();
frame.fail(FailureReason::Unknown);
continue;
}
};
if let Some(screencopy) = screencopy {
for (session, params) in screencopy {
match render_session(
Some(*render_node),
&mut renderer,
&session,
params,
self.output.current_transform(),
|_node, buffer, renderer, dt, age| {
let res = dt.damage_output(age, &elements)?;
let mut sync = SyncPoint::default();
let mut sync = SyncPoint::default();
if let (Some(ref damage), _) = &res {
if let Ok(dmabuf) = get_dmabuf(buffer) {
renderer.bind(dmabuf).map_err(RenderError::Rendering)?;
} else {
let size = buffer_dimensions(buffer).ok_or(
RenderError::Rendering(MultiError::ImportFailed),
)?;
let format =
with_buffer_contents(buffer, |_, _, data| shm_format_to_fourcc(data.format))
.map_err(|_| OutputNoMode)? // eh, we have to do some error
.expect("We should be able to convert all hardcoded shm screencopy formats");
let render_buffer =
Offscreen::<GlesRenderbuffer>::create_buffer(
renderer, format, size,
)
.map_err(RenderError::Rendering)?;
renderer
.bind(render_buffer)
.map_err(RenderError::Rendering)?;
}
let (output_size, output_scale, output_transform) = (
self.output.current_mode().ok_or(OutputNoMode)?.size,
self.output.current_scale().fractional_scale(),
self.output.current_transform(),
);
sync = frame_result
.blit_frame_result(
output_size,
output_transform,
output_scale,
renderer,
damage.iter().copied(),
// TODO: Filter cursor element
elements.iter().map(|e| e.id().clone()),
if let Some(ref damage) = damage {
let buffer = frame.buffer();
if let Ok(dmabuf) = get_dmabuf(&buffer) {
renderer
.bind(dmabuf)
.map_err(RenderError::<GlMultiRenderer>::Rendering)?;
} else {
let size = buffer_dimensions(&buffer).ok_or(RenderError::<
GlMultiRenderer,
>::Rendering(
MultiError::ImportFailed,
))?;
let format =
with_buffer_contents(&buffer, |_, _, data| shm_format_to_fourcc(data.format))
.map_err(|_| OutputNoMode)? // eh, we have to do some error
.expect("We should be able to convert all hardcoded shm screencopy formats");
let render_buffer =
Offscreen::<GlesRenderbuffer>::create_buffer(
&mut renderer,
format,
size,
)
.map_err(|err| match err {
BlitFrameResultError::Rendering(err) => {
RenderError::Rendering(err)
}
BlitFrameResultError::Export(_) => {
RenderError::Rendering(MultiError::DeviceMissing)
}
})?;
.map_err(RenderError::<GlMultiRenderer>::Rendering)?;
renderer
.bind(render_buffer)
.map_err(RenderError::<GlMultiRenderer>::Rendering)?;
}
Ok(RenderOutputResult {
damage: res.0,
states: res.1,
sync,
})
},
) {
Ok(true) => {} // success
Ok(false) => state.still_pending(session.clone(), params.clone()),
Err(err) => {
warn!(?err, "Error rendering to screencopy session.");
session.failed(FailureReason::Unspec);
let (output_size, output_scale, output_transform) = (
self.output.current_mode().ok_or(OutputNoMode)?.size,
self.output.current_scale().fractional_scale(),
self.output.current_transform(),
);
let filter = (!session.draw_cursor())
.then(|| {
elements.iter().filter_map(|elem| {
if let CosmicElement::Cursor(_) = elem {
Some(elem.id().clone())
} else {
None
}
})
})
.into_iter()
.flatten();
match frame_result
.blit_frame_result(
output_size,
output_transform,
output_scale,
&mut renderer,
damage.iter().copied(),
filter,
)
.map_err(|err| match err {
BlitFrameResultError::Rendering(err) => {
RenderError::<GlMultiRenderer>::Rendering(err)
}
BlitFrameResultError::Export(_) => {
RenderError::<GlMultiRenderer>::Rendering(
MultiError::DeviceMissing,
)
}
}) {
Ok(new_sync) => {
sync = new_sync;
}
Err(err) => {
tracing::warn!(?err, "Failed to screencopy");
session
.user_data()
.get::<SessionData>()
.unwrap()
.borrow_mut()
.reset();
frame.fail(FailureReason::Unknown);
continue;
}
};
}
let transform = self.output.current_transform();
match submit_buffer(frame, &mut renderer, transform, damage, sync) {
Ok(Some((frame, damage))) => {
if frame_result.is_empty {
frame.success(transform, damage, state.clock.now());
} else {
let _ = tx.send((frame, damage));
}
}
Ok(None) => {}
Err(err) => {
session
.user_data()
.get::<SessionData>()
.unwrap()
.borrow_mut()
.reset();
tracing::warn!(?err, "Failed to screencopy");
}
}
}
if x.is_ok() {
self.pending = true;
} else {
tracing::debug!("Stopped rendering");
}
}
self.fps.screencopy();
}
Err(err) => {
for (session, frame, _) in frames {
session
.user_data()
.get::<SessionData>()
.unwrap()
.borrow_mut()
.reset();
frame.fail(FailureReason::Unknown);
}
return Err(err).with_context(|| "Failed to submit result for display");
}
};
state.send_frames(&self.output, &frame_result.states, |source_node| {
Some(
@ -1494,17 +1623,7 @@ impl KmsState {
};
if recreated {
let sessions = output.pending_buffers().collect::<Vec<_>>();
if let Err(err) = self.schedule_render(
loop_handle,
output,
None,
if !sessions.is_empty() {
Some(sessions)
} else {
None
},
) {
if let Err(err) = self.schedule_render(loop_handle, output, None) {
error!(
?err,
"Error scheduling event loop for output {}.",
@ -1592,7 +1711,6 @@ impl KmsState {
loop_handle: &LoopHandle<'_, State>,
output: &Output,
estimated_rendertime: Option<Duration>,
mut screencopy_sessions: Option<Vec<(ScreencopySession, BufferParams)>>,
) -> Result<(), InsertError<Timer>> {
if let Some((device, crtc, surface)) = self
.devices
@ -1601,13 +1719,6 @@ impl KmsState {
.find(|(_, _, s)| s.output == *output)
{
if surface.surface.is_none() {
if let Some(sessions) = screencopy_sessions {
loop_handle.insert_idle(move |state| {
for (session, params) in sessions.into_iter() {
state.common.still_pending(session, params);
}
});
}
return Ok(());
}
if !surface.scheduled && !surface.pending {
@ -1655,16 +1766,9 @@ impl KmsState {
Some(&render_device.render_node),
&target_node,
common,
screencopy_sessions.as_deref(),
)
} else {
surface.render_output(
&mut backend.api,
None,
&target_node,
common,
screencopy_sessions.as_deref(),
)
surface.render_output(&mut backend.api, None, &target_node, common)
};
profiling::finish_frame!();
@ -1687,24 +1791,11 @@ impl KmsState {
};
}
if let Some(sessions) = screencopy_sessions.as_mut() {
for (session, params) in sessions.drain(..) {
state.common.still_pending(session, params);
}
}
TimeoutAction::Drop
},
)?);
trace!(?surface.render_timer_token, ?crtc, "Frame scheduled");
surface.scheduled = true;
} else {
if let Some(sessions) = screencopy_sessions {
loop_handle.insert_idle(|state| {
for (session, params) in sessions.into_iter() {
state.common.still_pending(session, params);
}
});
}
}
}
Ok(())

View file

@ -19,7 +19,9 @@ use smithay::{
},
reexports::wayland_server::protocol::wl_surface,
render_elements,
utils::{IsAlive, Logical, Monotonic, Point, Scale, Time, Transform},
utils::{
Buffer as BufferCoords, IsAlive, Logical, Monotonic, Point, Scale, Size, Time, Transform,
},
wayland::compositor::{get_role, with_states},
};
use std::{cell::RefCell, collections::HashMap, io::Read, sync::Mutex, time::Duration};
@ -156,12 +158,12 @@ pub fn draw_surface_cursor<R>(
surface: &wl_surface::WlSurface,
location: impl Into<Point<i32, Logical>>,
scale: impl Into<Scale<f64>>,
) -> Vec<CursorRenderElement<R>>
) -> Vec<(CursorRenderElement<R>, Point<i32, BufferCoords>)>
where
R: Renderer + ImportAll,
<R as Renderer>::TextureId: 'static,
{
let mut position = location.into();
let position = location.into();
let scale = scale.into();
let h = with_states(&surface, |states| {
states
@ -171,8 +173,12 @@ where
.lock()
.unwrap()
.hotspot
.to_buffer(
1,
Transform::Normal,
&Size::from((1, 1)), /* Size doesn't matter for Transform::Normal */
)
});
position -= h;
render_elements_from_surface_tree(
renderer,
@ -182,6 +188,9 @@ where
1.0,
Kind::Cursor,
)
.into_iter()
.map(|elem| (elem, h))
.collect()
}
#[profiling::function]
@ -190,7 +199,7 @@ pub fn draw_dnd_icon<R>(
surface: &wl_surface::WlSurface,
location: impl Into<Point<i32, Logical>>,
scale: impl Into<Scale<f64>>,
) -> Vec<CursorRenderElement<R>>
) -> Vec<WaylandSurfaceRenderElement<R>>
where
R: Renderer + ImportAll,
<R as Renderer>::TextureId: 'static,
@ -307,7 +316,7 @@ pub fn draw_cursor<R>(
scale: Scale<f64>,
time: Time<Monotonic>,
draw_default: bool,
) -> Vec<CursorRenderElement<R>>
) -> Vec<(CursorRenderElement<R>, Point<i32, BufferCoords>)>
where
R: Renderer + ImportMem + ImportAll,
<R as Renderer>::TextureId: Clone + 'static,
@ -367,20 +376,23 @@ where
}
};
let hotspot = Point::<i32, Logical>::from((frame.xhot as i32, frame.yhot as i32)).to_f64();
let hotspot = Point::<i32, BufferCoords>::from((frame.xhot as i32, frame.yhot as i32));
*state.current_image.borrow_mut() = Some(frame);
return vec![CursorRenderElement::Static(
MemoryRenderBufferRenderElement::from_buffer(
renderer,
(location - hotspot).to_physical(scale),
pointer_image,
None,
None,
None,
Kind::Cursor,
)
.expect("Failed to import cursor bitmap"),
return vec![(
CursorRenderElement::Static(
MemoryRenderBufferRenderElement::from_buffer(
renderer,
location.to_physical(scale),
pointer_image,
None,
None,
None,
Kind::Cursor,
)
.expect("Failed to import cursor bitmap"),
),
hotspot,
)];
} else {
Vec::new()

View file

@ -3,13 +3,15 @@ use crate::shell::{CosmicMappedRenderElement, WorkspaceRenderElement};
use smithay::{
backend::renderer::{
element::{
surface::WaylandSurfaceRenderElement,
utils::{Relocate, RelocateRenderElement},
Element, RenderElement, UnderlyingStorage,
Element, Id, RenderElement, UnderlyingStorage,
},
glow::{GlowFrame, GlowRenderer},
utils::CommitCounter,
Frame, ImportAll, ImportMem, Renderer,
},
utils::{Buffer as BufferCoords, Physical, Point, Rectangle, Scale},
utils::{Buffer as BufferCoords, Logical, Physical, Point, Rectangle, Scale},
};
#[cfg(feature = "debug")]
@ -24,8 +26,10 @@ where
CosmicMappedRenderElement<R>: RenderElement<R>,
{
Workspace(RelocateRenderElement<WorkspaceRenderElement<R>>),
Cursor(CursorRenderElement<R>),
Cursor(RelocateRenderElement<CursorRenderElement<R>>),
Dnd(WaylandSurfaceRenderElement<R>),
MoveGrab(CosmicMappedRenderElement<R>),
AdditionalDamage(DamageElement),
#[cfg(feature = "debug")]
Egui(TextureRenderElement<GlesTexture>),
}
@ -36,21 +40,25 @@ where
<R as Renderer>::TextureId: 'static,
CosmicMappedRenderElement<R>: RenderElement<R>,
{
fn id(&self) -> &smithay::backend::renderer::element::Id {
fn id(&self) -> &Id {
match self {
CosmicElement::Workspace(elem) => elem.id(),
CosmicElement::Cursor(elem) => elem.id(),
CosmicElement::Dnd(elem) => elem.id(),
CosmicElement::MoveGrab(elem) => elem.id(),
CosmicElement::AdditionalDamage(elem) => elem.id(),
#[cfg(feature = "debug")]
CosmicElement::Egui(elem) => elem.id(),
}
}
fn current_commit(&self) -> smithay::backend::renderer::utils::CommitCounter {
fn current_commit(&self) -> CommitCounter {
match self {
CosmicElement::Workspace(elem) => elem.current_commit(),
CosmicElement::Cursor(elem) => elem.current_commit(),
CosmicElement::Dnd(elem) => elem.current_commit(),
CosmicElement::MoveGrab(elem) => elem.current_commit(),
CosmicElement::AdditionalDamage(elem) => elem.current_commit(),
#[cfg(feature = "debug")]
CosmicElement::Egui(elem) => elem.current_commit(),
}
@ -60,7 +68,9 @@ where
match self {
CosmicElement::Workspace(elem) => elem.src(),
CosmicElement::Cursor(elem) => elem.src(),
CosmicElement::Dnd(elem) => elem.src(),
CosmicElement::MoveGrab(elem) => elem.src(),
CosmicElement::AdditionalDamage(elem) => elem.src(),
#[cfg(feature = "debug")]
CosmicElement::Egui(elem) => elem.src(),
}
@ -70,7 +80,9 @@ where
match self {
CosmicElement::Workspace(elem) => elem.geometry(scale),
CosmicElement::Cursor(elem) => elem.geometry(scale),
CosmicElement::Dnd(elem) => elem.geometry(scale),
CosmicElement::MoveGrab(elem) => elem.geometry(scale),
CosmicElement::AdditionalDamage(elem) => elem.geometry(scale),
#[cfg(feature = "debug")]
CosmicElement::Egui(elem) => elem.geometry(scale),
}
@ -80,7 +92,9 @@ where
match self {
CosmicElement::Workspace(elem) => elem.location(scale),
CosmicElement::Cursor(elem) => elem.location(scale),
CosmicElement::Dnd(elem) => elem.location(scale),
CosmicElement::MoveGrab(elem) => elem.location(scale),
CosmicElement::AdditionalDamage(elem) => elem.location(scale),
#[cfg(feature = "debug")]
CosmicElement::Egui(elem) => elem.location(scale),
}
@ -90,7 +104,9 @@ where
match self {
CosmicElement::Workspace(elem) => elem.transform(),
CosmicElement::Cursor(elem) => elem.transform(),
CosmicElement::Dnd(elem) => elem.transform(),
CosmicElement::MoveGrab(elem) => elem.transform(),
CosmicElement::AdditionalDamage(elem) => elem.transform(),
#[cfg(feature = "debug")]
CosmicElement::Egui(elem) => elem.transform(),
}
@ -99,12 +115,14 @@ where
fn damage_since(
&self,
scale: Scale<f64>,
commit: Option<smithay::backend::renderer::utils::CommitCounter>,
commit: Option<CommitCounter>,
) -> Vec<Rectangle<i32, Physical>> {
match self {
CosmicElement::Workspace(elem) => elem.damage_since(scale, commit),
CosmicElement::Cursor(elem) => elem.damage_since(scale, commit),
CosmicElement::Dnd(elem) => elem.damage_since(scale, commit),
CosmicElement::MoveGrab(elem) => elem.damage_since(scale, commit),
CosmicElement::AdditionalDamage(elem) => elem.damage_since(scale, commit),
#[cfg(feature = "debug")]
CosmicElement::Egui(elem) => elem.damage_since(scale, commit),
}
@ -114,7 +132,9 @@ where
match self {
CosmicElement::Workspace(elem) => elem.opaque_regions(scale),
CosmicElement::Cursor(elem) => elem.opaque_regions(scale),
CosmicElement::Dnd(elem) => elem.opaque_regions(scale),
CosmicElement::MoveGrab(elem) => elem.opaque_regions(scale),
CosmicElement::AdditionalDamage(elem) => elem.opaque_regions(scale),
#[cfg(feature = "debug")]
CosmicElement::Egui(elem) => elem.opaque_regions(scale),
}
@ -124,7 +144,9 @@ where
match self {
CosmicElement::Workspace(elem) => elem.alpha(),
CosmicElement::Cursor(elem) => elem.alpha(),
CosmicElement::Dnd(elem) => elem.alpha(),
CosmicElement::MoveGrab(elem) => elem.alpha(),
CosmicElement::AdditionalDamage(elem) => elem.alpha(),
#[cfg(feature = "debug")]
CosmicElement::Egui(elem) => elem.alpha(),
}
@ -142,7 +164,11 @@ impl RenderElement<GlowRenderer> for CosmicElement<GlowRenderer> {
match self {
CosmicElement::Workspace(elem) => elem.draw(frame, src, dst, damage),
CosmicElement::Cursor(elem) => elem.draw(frame, src, dst, damage),
CosmicElement::Dnd(elem) => elem.draw(frame, src, dst, damage),
CosmicElement::MoveGrab(elem) => elem.draw(frame, src, dst, damage),
CosmicElement::AdditionalDamage(elem) => {
RenderElement::<GlowRenderer>::draw(elem, frame, src, dst, damage)
}
#[cfg(feature = "debug")]
CosmicElement::Egui(elem) => {
RenderElement::<GlowRenderer>::draw(elem, frame, src, dst, damage)
@ -154,7 +180,9 @@ impl RenderElement<GlowRenderer> for CosmicElement<GlowRenderer> {
match self {
CosmicElement::Workspace(elem) => elem.underlying_storage(renderer),
CosmicElement::Cursor(elem) => elem.underlying_storage(renderer),
CosmicElement::Dnd(elem) => elem.underlying_storage(renderer),
CosmicElement::MoveGrab(elem) => elem.underlying_storage(renderer),
CosmicElement::AdditionalDamage(elem) => elem.underlying_storage(renderer),
#[cfg(feature = "debug")]
CosmicElement::Egui(elem) => elem.underlying_storage(renderer),
}
@ -172,7 +200,11 @@ impl<'a> RenderElement<GlMultiRenderer<'a>> for CosmicElement<GlMultiRenderer<'a
match self {
CosmicElement::Workspace(elem) => elem.draw(frame, src, dst, damage),
CosmicElement::Cursor(elem) => elem.draw(frame, src, dst, damage),
CosmicElement::Dnd(elem) => elem.draw(frame, src, dst, damage),
CosmicElement::MoveGrab(elem) => elem.draw(frame, src, dst, damage),
CosmicElement::AdditionalDamage(elem) => {
RenderElement::<GlMultiRenderer<'a>>::draw(elem, frame, src, dst, damage)
}
#[cfg(feature = "debug")]
CosmicElement::Egui(elem) => {
let elem = {
@ -189,7 +221,9 @@ impl<'a> RenderElement<GlMultiRenderer<'a>> for CosmicElement<GlMultiRenderer<'a
match self {
CosmicElement::Workspace(elem) => elem.underlying_storage(renderer),
CosmicElement::Cursor(elem) => elem.underlying_storage(renderer),
CosmicElement::Dnd(elem) => elem.underlying_storage(renderer),
CosmicElement::MoveGrab(elem) => elem.underlying_storage(renderer),
CosmicElement::AdditionalDamage(elem) => elem.underlying_storage(renderer),
#[cfg(feature = "debug")]
CosmicElement::Egui(elem) => {
let glow_renderer = renderer.glow_renderer_mut();
@ -219,17 +253,6 @@ where
}
}
impl<R> From<CursorRenderElement<R>> for CosmicElement<R>
where
R: Renderer + ImportAll + ImportMem + AsGlowRenderer,
<R as Renderer>::TextureId: 'static,
CosmicMappedRenderElement<R>: RenderElement<R>,
{
fn from(elem: CursorRenderElement<R>) -> Self {
Self::Cursor(elem)
}
}
impl<R> From<CosmicMappedRenderElement<R>> for CosmicElement<R>
where
R: Renderer + ImportAll + ImportMem + AsGlowRenderer,
@ -241,6 +264,17 @@ where
}
}
impl<R> From<DamageElement> for CosmicElement<R>
where
R: Renderer + ImportAll + ImportMem + AsGlowRenderer,
<R as Renderer>::TextureId: 'static,
CosmicMappedRenderElement<R>: RenderElement<R>,
{
fn from(elem: DamageElement) -> Self {
Self::AdditionalDamage(elem)
}
}
#[cfg(feature = "debug")]
impl<R> From<TextureRenderElement<GlesTexture>> for CosmicElement<R>
where
@ -304,3 +338,58 @@ impl<'renderer, 'frame> AsGlowFrame<'frame> for GlMultiFrame<'renderer, 'frame>
self.as_mut()
}
}
pub struct DamageElement {
id: Id,
geometry: Rectangle<i32, Logical>,
}
impl DamageElement {
pub fn new(geometry: Rectangle<i32, Logical>) -> DamageElement {
DamageElement {
id: Id::new(),
geometry,
}
}
}
impl Element for DamageElement {
fn id(&self) -> &Id {
&self.id
}
fn current_commit(&self) -> CommitCounter {
CommitCounter::default()
}
fn src(&self) -> Rectangle<f64, BufferCoords> {
Rectangle::from_loc_and_size((0.0, 0.0), (1.0, 1.0))
}
fn geometry(&self, scale: Scale<f64>) -> Rectangle<i32, Physical> {
self.geometry.to_f64().to_physical(scale).to_i32_round()
}
fn damage_since(
&self,
scale: Scale<f64>,
_commit: Option<CommitCounter>,
) -> Vec<Rectangle<i32, Physical>> {
vec![Rectangle::from_loc_and_size(
(0, 0),
self.geometry(scale).size,
)]
}
}
impl<R: Renderer> RenderElement<R> for DamageElement {
fn draw(
&self,
_frame: &mut <R as Renderer>::Frame<'_>,
_src: Rectangle<f64, BufferCoords>,
_dst: Rectangle<i32, Physical>,
_damage: &[Rectangle<i32, Physical>],
) -> Result<(), <R as Renderer>::Error> {
Ok(())
}
}

View file

@ -11,6 +11,7 @@ use std::{
#[cfg(feature = "debug")]
use crate::debug::fps_ui;
use crate::{
backend::render::element::DamageElement,
shell::{
focus::target::WindowGroup,
grabs::{SeatMenuGrabState, SeatMoveGrabState},
@ -23,19 +24,13 @@ use crate::{
wayland::{
handlers::{
data_device::get_dnd_icon,
screencopy::{render_session, WORKSPACE_OVERVIEW_NAMESPACE},
},
protocols::{
screencopy::{
BufferParams, CursorMode as ScreencopyCursorMode, Session as ScreencopySession,
},
workspace::WorkspaceHandle,
screencopy::{render_session, FrameHolder, SessionData, WORKSPACE_OVERVIEW_NAMESPACE},
},
protocols::workspace::WorkspaceHandle,
},
};
use cosmic_comp_config::workspace::WorkspaceLayout;
use cosmic_protocols::screencopy::v1::server::zcosmic_screencopy_session_v1::FailureReason;
use keyframe::{ease, functions::EaseInOutCubic};
use smithay::{
backend::{
@ -61,19 +56,17 @@ use smithay::{
},
desktop::{layer_map_for_output, PopupManager},
output::{Output, OutputNoMode},
utils::{IsAlive, Logical, Point, Rectangle, Scale},
utils::{IsAlive, Logical, Point, Rectangle, Scale, Transform},
wayland::{
dmabuf::get_dmabuf,
shell::wlr_layer::Layer,
shm::{shm_format_to_fourcc, with_buffer_contents},
},
};
use tracing::warn;
pub mod animations;
pub mod cursor;
use self::cursor::CursorRenderElement;
pub mod element;
use self::element::{AsGlowRenderer, CosmicElement};
@ -392,17 +385,16 @@ pub enum CursorMode {
}
#[profiling::function]
pub fn cursor_elements<'frame, E, R>(
pub fn cursor_elements<'frame, R>(
renderer: &mut R,
state: &Common,
output: &Output,
mode: CursorMode,
) -> Vec<E>
) -> Vec<CosmicElement<R>>
where
R: Renderer + ImportAll + ImportMem + AsGlowRenderer,
<R as Renderer>::TextureId: Clone + 'static,
CosmicMappedRenderElement<R>: RenderElement<R>,
E: From<CursorRenderElement<R>> + From<CosmicMappedRenderElement<R>>,
{
let scale = output.current_scale().fractional_scale();
let mut elements = Vec::new();
@ -425,7 +417,13 @@ where
mode != CursorMode::NotDefault,
)
.into_iter()
.map(E::from),
.map(|(elem, hotspot)| {
CosmicElement::Cursor(RelocateRenderElement::from_element(
elem,
Point::from((-hotspot.x, -hotspot.y)),
Relocate::Relative,
))
}),
);
}
@ -433,7 +431,7 @@ where
elements.extend(
cursor::draw_dnd_icon(renderer, &wl_surface, location.to_i32_round(), scale)
.into_iter()
.map(E::from),
.map(CosmicElement::Dnd),
);
}
@ -444,7 +442,7 @@ where
.unwrap()
.borrow()
.as_ref()
.map(|state| state.render::<E, R>(renderer, seat, output, theme))
.map(|state| state.render::<CosmicElement<R>, R>(renderer, seat, output, theme))
{
elements.extend(grab_elements);
}
@ -940,7 +938,7 @@ where
}
#[profiling::function]
pub fn render_output<R, Target, OffTarget, Source>(
pub fn render_output<R, Target, OffTarget>(
gpu: Option<&DrmNode>,
renderer: &mut R,
target: Target,
@ -949,7 +947,6 @@ pub fn render_output<R, Target, OffTarget, Source>(
state: &mut Common,
output: &Output,
cursor_mode: CursorMode,
screencopy: Option<(Source, &[(ScreencopySession, BufferParams)])>,
fps: Option<&mut Fps>,
) -> Result<RenderOutputResult, RenderError<R>>
where
@ -960,14 +957,14 @@ where
+ Bind<Dmabuf>
+ Bind<Target>
+ Offscreen<OffTarget>
+ Blit<Source>
+ Blit<Target>
+ AsGlowRenderer,
<R as Renderer>::TextureId: Clone + 'static,
<R as Renderer>::Error: From<GlesError>,
CosmicElement<R>: RenderElement<R>,
CosmicMappedRenderElement<R>: RenderElement<R>,
WorkspaceRenderElement<R>: RenderElement<R>,
Source: Clone,
Target: Clone,
{
let (previous_workspace, workspace) = state.shell.workspaces.active(output);
let (previous_idx, idx) = state.shell.workspaces.active_num(output);
@ -979,38 +976,128 @@ where
let result = render_workspace(
gpu,
renderer,
target,
target.clone(),
damage_tracker,
age,
None,
state,
output,
previous_workspace,
workspace,
cursor_mode,
screencopy,
fps,
false,
);
result
match result {
Ok((res, mut elements)) => {
for (session, frame) in output.take_pending_frames() {
if let Some((frame, damage)) = render_session(
renderer,
&session.user_data().get::<SessionData>().unwrap(),
frame,
output.current_transform(),
|buffer, renderer, dt, age, additional_damage| {
let old_len = if !additional_damage.is_empty() {
let area = output
.current_mode()
.ok_or(RenderError::OutputNoMode(OutputNoMode))
.map(
|mode| {
mode.size
.to_logical(1)
.to_buffer(1, Transform::Normal)
.to_f64()
}, /* TODO: Mode is Buffer..., why is this Physical in the first place */
)?;
let old_len = elements.len();
elements.extend(
additional_damage
.into_iter()
.map(|rect| {
rect.to_f64()
.to_logical(
output.current_scale().fractional_scale(),
output.current_transform(),
&area,
)
.to_i32_round()
})
.map(DamageElement::new)
.map(Into::into),
);
Some(old_len)
} else {
None
};
let res = dt.damage_output(age, &elements)?;
if let Some(old_len) = old_len {
elements.truncate(old_len);
}
if let (Some(ref damage), _) = &res {
if let Ok(dmabuf) = get_dmabuf(buffer) {
renderer.bind(dmabuf).map_err(RenderError::Rendering)?;
} else {
let size = buffer_dimensions(buffer).unwrap();
let format = with_buffer_contents(buffer, |_, _, data| {
shm_format_to_fourcc(data.format)
})
.map_err(|_| OutputNoMode)? // eh, we have to do some error
.expect(
"We should be able to convert all hardcoded shm screencopy formats",
);
let render_buffer = renderer
.create_buffer(format, size)
.map_err(RenderError::Rendering)?;
renderer
.bind(render_buffer)
.map_err(RenderError::Rendering)?;
}
for rect in damage {
renderer
.blit_from(target.clone(), *rect, *rect, TextureFilter::Nearest)
.map_err(RenderError::Rendering)?;
}
}
Ok(RenderOutputResult {
damage: res.0,
sync: SyncPoint::default(),
states: res.1,
})
},
)? {
frame.success(output.current_transform(), damage, state.clock.now());
}
}
Ok(res)
}
Err(err) => Err(err),
}
}
#[profiling::function]
pub fn render_workspace<R, Target, OffTarget, Source>(
pub fn render_workspace<R, Target, OffTarget>(
gpu: Option<&DrmNode>,
renderer: &mut R,
target: Target,
damage_tracker: &mut OutputDamageTracker,
age: usize,
additional_damage: Option<Vec<Rectangle<i32, Logical>>>,
state: &mut Common,
output: &Output,
previous: Option<(WorkspaceHandle, usize, WorkspaceDelta)>,
current: (WorkspaceHandle, usize),
mut cursor_mode: CursorMode,
screencopy: Option<(Source, &[(ScreencopySession, BufferParams)])>,
cursor_mode: CursorMode,
mut fps: Option<&mut Fps>,
exclude_workspace_overview: bool,
) -> Result<RenderOutputResult, RenderError<R>>
) -> Result<(RenderOutputResult, Vec<CosmicElement<R>>), RenderError<R>>
where
R: Renderer
+ ImportAll
@ -1019,14 +1106,12 @@ where
+ Bind<Dmabuf>
+ Bind<Target>
+ Offscreen<OffTarget>
+ Blit<Source>
+ AsGlowRenderer,
<R as Renderer>::TextureId: Clone + 'static,
<R as Renderer>::Error: From<GlesError>,
CosmicElement<R>: RenderElement<R>,
CosmicMappedRenderElement<R>: RenderElement<R>,
WorkspaceRenderElement<R>: RenderElement<R>,
Source: Clone,
{
if let Some(ref mut fps) = fps {
fps.start();
@ -1039,22 +1124,7 @@ where
}
}
let screencopy_contains_embedded = screencopy.as_ref().map_or(false, |(_, sessions)| {
sessions
.iter()
.any(|(s, _)| s.cursor_mode() == ScreencopyCursorMode::Embedded)
});
// cursor handling without a cursor_plane in this case is horrible.
// because what if some session disagree and/or the backend wants to render with a different mode?
// It seems we would need to render to an offscreen buffer in those cases (and do multiple renders, which messes with damage tracking).
// So for now, we just pick the worst mode (embedded), if any requires it.
//
// Once we move to a cursor_plane, the default framebuffer will never contain a cursor and we can just composite the cursor for each session separately on top (or not).
if screencopy_contains_embedded {
cursor_mode = CursorMode::All;
};
let elements: Vec<CosmicElement<R>> = workspace_elements(
let mut elements: Vec<CosmicElement<R>> = workspace_elements(
gpu,
renderer,
state,
@ -1065,6 +1135,18 @@ where
&mut fps,
exclude_workspace_overview,
)?;
if let Some(additional_damage) = additional_damage {
let output_geo = output.geometry().to_local(&output).as_logical();
elements.extend(
additional_damage
.into_iter()
.filter_map(|rect| rect.intersection(output_geo))
.map(DamageElement::new)
.map(Into::<CosmicElement<R>>::into),
);
}
if let Some(fps) = fps.as_mut() {
fps.elements();
}
@ -1081,62 +1163,6 @@ where
fps.render();
}
if let Some((source, buffers)) = screencopy {
if res.is_ok() {
for (session, params) in buffers {
match render_session(
gpu.cloned(),
renderer,
&session,
params,
output.current_transform(),
|_node, buffer, renderer, dt, age| {
let res = dt.damage_output(age, &elements)?;
if let (Some(ref damage), _) = &res {
if let Ok(dmabuf) = get_dmabuf(buffer) {
renderer.bind(dmabuf).map_err(RenderError::Rendering)?;
} else {
let size = buffer_dimensions(buffer).unwrap();
let format =
with_buffer_contents(buffer, |_, _, data| shm_format_to_fourcc(data.format))
.map_err(|_| OutputNoMode)? // eh, we have to do some error
.expect("We should be able to convert all hardcoded shm screencopy formats");
let render_buffer = renderer
.create_buffer(format, size)
.map_err(RenderError::Rendering)?;
renderer
.bind(render_buffer)
.map_err(RenderError::Rendering)?;
}
for rect in damage {
renderer
.blit_from(source.clone(), *rect, *rect, TextureFilter::Nearest)
.map_err(RenderError::Rendering)?;
}
}
Ok(RenderOutputResult {
damage: res.0,
sync: SyncPoint::default(),
states: res.1,
})
},
) {
Ok(true) => {} // success
Ok(false) => state.still_pending(session.clone(), params.clone()),
Err(err) => {
warn!(?err, "Error rendering to screencopy session.");
session.failed(FailureReason::Unspec);
}
}
}
}
if let Some(fps) = fps.as_mut() {
fps.screencopy();
}
}
#[cfg(feature = "debug")]
if let Some(ref mut fps) = fps {
if let Some(rd) = fps.rd.as_mut() {
@ -1147,5 +1173,5 @@ where
}
}
res
res.map(|res| (res, elements))
}

View file

@ -6,7 +6,6 @@ use crate::{
input::Devices,
state::{BackendData, Common},
utils::prelude::*,
wayland::protocols::screencopy::{BufferParams, Session as ScreencopySession},
};
use anyhow::{anyhow, Context, Result};
use smithay::{
@ -45,7 +44,6 @@ pub struct WinitState {
pub backend: WinitGraphicsBackend<GlowRenderer>,
output: Output,
damage_tracker: OutputDamageTracker,
screencopy: Vec<(ScreencopySession, BufferParams)>,
#[cfg(feature = "debug")]
fps: Fps,
}
@ -59,7 +57,7 @@ impl WinitState {
let age = self.backend.buffer_age().unwrap_or(0);
let surface = self.backend.egl_surface();
match render::render_output::<_, _, GlesRenderbuffer, _>(
match render::render_output::<_, _, GlesRenderbuffer>(
None,
self.backend.renderer(),
surface.clone(),
@ -68,11 +66,6 @@ impl WinitState {
state,
&self.output,
CursorMode::NotDefault,
if !self.screencopy.is_empty() {
Some((surface, &self.screencopy))
} else {
None
},
#[cfg(not(feature = "debug"))]
None,
#[cfg(feature = "debug")]
@ -85,7 +78,6 @@ impl WinitState {
self.backend
.submit(damage.as_deref())
.with_context(|| "Failed to submit buffer for display")?;
self.screencopy.clear();
#[cfg(feature = "debug")]
self.fps.displayed();
state.send_frames(&self.output, &states, |_| None);
@ -104,9 +96,6 @@ impl WinitState {
}
}
Err(err) => {
for (session, params) in self.screencopy.drain(..) {
state.still_pending(session, params)
}
anyhow::bail!("Rendering failed: {}", err);
}
};
@ -136,12 +125,6 @@ impl WinitState {
Ok(())
}
}
pub fn pending_screencopy(&mut self, new: Option<Vec<(ScreencopySession, BufferParams)>>) {
if let Some(sessions) = new {
self.screencopy.extend(sessions);
}
}
}
pub fn init_backend(
@ -233,7 +216,6 @@ pub fn init_backend(
backend,
output: output.clone(),
damage_tracker: OutputDamageTracker::from_output(&output),
screencopy: Vec::new(),
#[cfg(feature = "debug")]
fps,
});

View file

@ -6,7 +6,6 @@ use crate::{
input::Devices,
state::{BackendData, Common},
utils::prelude::*,
wayland::protocols::screencopy::{BufferParams, Session as ScreencopySession},
};
use anyhow::{Context, Result};
use smithay::{
@ -150,7 +149,6 @@ impl X11State {
render: ping.clone(),
dirty: false,
pending: true,
screencopy: Vec::new(),
#[cfg(feature = "debug")]
fps: Fps::new(&mut self.renderer),
});
@ -160,16 +158,9 @@ impl X11State {
Ok(output)
}
pub fn schedule_render(
&mut self,
output: &Output,
screencopy: Option<Vec<(ScreencopySession, BufferParams)>>,
) {
pub fn schedule_render(&mut self, output: &Output) {
if let Some(surface) = self.surfaces.iter_mut().find(|s| s.output == *output) {
surface.dirty = true;
if let Some(sessions) = screencopy {
surface.screencopy.extend(sessions);
}
if !surface.pending {
surface.render.ping();
}
@ -210,7 +201,6 @@ impl X11State {
pub struct Surface {
window: Window,
damage_tracker: OutputDamageTracker,
screencopy: Vec<(ScreencopySession, BufferParams)>,
surface: X11Surface,
output: Output,
render: ping::Ping,
@ -226,7 +216,7 @@ impl Surface {
.surface
.buffer()
.with_context(|| "Failed to allocate buffer")?;
match render::render_output::<_, _, GlesRenderbuffer, _>(
match render::render_output::<_, _, GlesRenderbuffer>(
None,
renderer,
buffer.clone(),
@ -235,18 +225,12 @@ impl Surface {
state,
&self.output,
render::CursorMode::NotDefault,
if !self.screencopy.is_empty() {
Some((buffer, &self.screencopy))
} else {
None
},
#[cfg(not(feature = "debug"))]
None,
#[cfg(feature = "debug")]
Some(&mut self.fps),
) {
Ok(RenderOutputResult { damage, states, .. }) => {
self.screencopy.clear();
self.surface
.submit()
.with_context(|| "Failed to submit buffer for display")?;
@ -268,9 +252,6 @@ impl Surface {
}
}
Err(err) => {
for (session, params) in self.screencopy.drain(..) {
state.still_pending(session, params)
}
self.surface.reset_buffers();
anyhow::bail!("Rendering failed: {}", err);
}
@ -540,7 +521,7 @@ impl State {
self.process_input_event(event, false);
// TODO actually figure out the output
for output in self.common.shell.outputs() {
self.backend.x11().schedule_render(output, None);
self.backend.x11().schedule_render(output);
}
}
}