element: Refactor element acquisition to be push based

This commit is contained in:
Victoria Brekenfeld 2026-03-04 18:57:37 +01:00 committed by Victoria Brekenfeld
parent 178c6593dc
commit e4c0716951
18 changed files with 1096 additions and 1137 deletions

View file

@ -3,7 +3,7 @@
use crate::{
backend::render::{
element::AsGlowRenderer,
wayland::{SurfaceRenderElement, render_elements_from_surface_tree},
wayland::{SurfaceRenderElement, push_render_elements_from_surface_tree},
},
utils::prelude::*,
wayland::handlers::compositor::FRAME_TIME_FILTER,
@ -185,8 +185,8 @@ pub fn draw_surface_cursor<R>(
surface: &wl_surface::WlSurface,
location: Point<f64, Logical>,
scale: impl Into<Scale<f64>>,
) -> Vec<(CursorRenderElement<R>, Point<i32, Physical>)>
where
push: &mut dyn FnMut(CursorRenderElement<R>, Point<i32, Physical>),
) where
R: Renderer + ImportAll + AsGlowRenderer,
R::TextureId: Clone + 'static,
{
@ -202,7 +202,7 @@ where
.to_physical_precise_round(scale)
});
render_elements_from_surface_tree(
push_render_elements_from_surface_tree(
renderer,
surface,
location.to_physical(scale).to_i32_round(),
@ -212,10 +212,9 @@ where
false,
[0; 4],
Kind::Cursor,
)
.into_iter()
.map(|elem| (elem, h))
.collect()
&mut |elem| push(elem.into(), h),
None,
);
}
#[profiling::function]
@ -224,8 +223,8 @@ pub fn draw_dnd_icon<R>(
surface: &wl_surface::WlSurface,
location: Point<f64, Logical>,
scale: impl Into<Scale<f64>>,
) -> Vec<SurfaceRenderElement<R>>
where
push: &mut dyn FnMut(SurfaceRenderElement<R>),
) where
R: Renderer + ImportAll + AsGlowRenderer,
R::TextureId: Clone + 'static,
{
@ -236,7 +235,7 @@ where
);
}
let scale = scale.into();
render_elements_from_surface_tree(
push_render_elements_from_surface_tree(
renderer,
surface,
location.to_physical(scale).to_i32_round(),
@ -246,7 +245,9 @@ where
false,
[0; 4],
FRAME_TIME_FILTER,
)
push,
None,
);
}
pub type CursorState = Mutex<CursorStateInner>;
@ -330,8 +331,8 @@ pub fn draw_cursor<R>(
buffer_scale: f64,
time: Time<Monotonic>,
draw_default: bool,
) -> Vec<(CursorRenderElement<R>, Point<i32, Physical>)>
where
push: &mut dyn FnMut(CursorRenderElement<R>, Point<i32, Physical>),
) where
R: Renderer + ImportMem + ImportAll + AsGlowRenderer,
R::TextureId: Send + Clone + 'static,
{
@ -343,7 +344,7 @@ where
let state = &mut *state_ref;
if state.hidden {
return Vec::new();
return;
}
let named_cursor = state.current_cursor.or(match cursor_status {
@ -352,7 +353,7 @@ where
});
if let Some(current_cursor) = named_cursor {
if !draw_default && current_cursor == CursorIcon::Default {
return Vec::new();
return;
}
let integer_scale = (scale.x.max(scale.y) * buffer_scale).ceil() as u32;
@ -389,7 +390,7 @@ where
);
state.current_image = Some(frame);
return vec![(
push(
CursorRenderElement::Static(
MemoryRenderBufferRenderElement::from_buffer(
renderer,
@ -403,11 +404,9 @@ where
.expect("Failed to import cursor bitmap"),
),
hotspot.to_physical_precise_round(scale),
)];
);
} else if let CursorImageStatus::Surface(ref wl_surface) = cursor_status {
return draw_surface_cursor(renderer, wl_surface, location, scale);
} else {
Vec::new()
draw_surface_cursor(renderer, wl_surface, location, scale, push);
}
}

View file

@ -17,7 +17,11 @@ use crate::{
render::{
element::DamageElement,
shadow::{SHADOW_SHADER, ShadowShader},
wayland::clipped_surface::{CLIPPING_SHADER, ClippingShader},
wayland::{
SurfaceRenderElement,
clipped_surface::{CLIPPING_SHADER, ClippingShader},
push_render_elements_from_surface_tree,
},
},
},
config::ScreenFilter,
@ -47,11 +51,10 @@ use smithay::{
allocator::Fourcc,
drm::{DrmDeviceFd, DrmNode},
renderer::{
Color32F, ImportAll, Offscreen, Renderer, Texture, TextureFilter,
Color32F, Offscreen, Texture, TextureFilter,
damage::{Error as RenderError, OutputDamageTracker, RenderOutputResult},
element::{
Element, Id, Kind, RenderElement, WeakId,
surface::{WaylandSurfaceRenderElement, render_elements_from_surface_tree},
texture::{TextureRenderBuffer, TextureRenderElement},
utils::{
ConstrainAlign, ConstrainScaleBehavior, CropRenderElement, Relocate,
@ -68,6 +71,7 @@ use smithay::{
sync::SyncPoint,
},
},
desktop::utils::bbox_from_surface_tree,
input::Seat,
output::{Output, OutputModeSource, OutputNoMode},
utils::{
@ -475,8 +479,8 @@ pub fn cursor_elements<'a, 'frame, R>(
mode: CursorMode,
exclude_dnd_icon: bool,
scanout_node: Option<DrmNode>,
) -> Vec<CosmicElement<R>>
where
push: &mut dyn FnMut(CosmicElement<R>),
) where
R: AsGlowRenderer,
R::TextureId: Send + Clone + 'static,
CosmicMappedRenderElement<R>: RenderElement<R>,
@ -490,7 +494,6 @@ where
)
})
.unwrap_or_else(|| ((0., 0.).into(), 1.));
let mut elements = Vec::new();
for seat in seats {
let pointer = match seat.get_pointer() {
@ -500,19 +503,16 @@ where
let location = pointer.current_location() - output.current_location().to_f64();
if mode != CursorMode::None {
elements.extend(
cursor::draw_cursor(
renderer,
seat,
location,
scale.into(),
zoom_scale,
now,
mode != CursorMode::NotDefault,
)
.into_iter()
.map(|(elem, hotspot)| {
CosmicElement::Cursor(RescaleRenderElement::from_element(
cursor::draw_cursor(
renderer,
seat,
location,
scale.into(),
zoom_scale,
now,
mode != CursorMode::NotDefault,
&mut |elem, hotspot| {
push(CosmicElement::Cursor(RescaleRenderElement::from_element(
RelocateRenderElement::from_element(
elem,
Point::from((-hotspot.x, -hotspot.y)),
@ -523,70 +523,54 @@ where
.to_physical(output.current_scale().fractional_scale())
.to_i32_round(),
zoom_scale,
))
}),
)))
},
);
}
if !exclude_dnd_icon && let Some(dnd_icon) = get_dnd_icon(seat) {
elements.extend(
cursor::draw_dnd_icon(
renderer,
&dnd_icon.surface,
(location + dnd_icon.offset.to_f64()).to_i32_round(),
scale,
)
.into_iter()
.map(CosmicElement::Dnd),
cursor::draw_dnd_icon(
renderer,
&dnd_icon.surface,
(location + dnd_icon.offset.to_f64()).to_i32_round(),
scale,
&mut |elem| push(CosmicElement::Dnd(elem)),
);
}
let theme = theme.cosmic();
if let Some(grab_elements) = seat
if let Some(grab_state) = seat
.user_data()
.get::<SeatMoveGrabState>()
.unwrap()
.lock()
.unwrap()
.as_ref()
.map(|state| {
state.render::<CosmicMappedRenderElement<R>, R>(
renderer,
output,
theme,
scanout_node,
)
})
{
elements.extend(grab_elements.into_iter().map(|elem| {
CosmicElement::MoveGrab(RescaleRenderElement::from_element(
grab_state.render(renderer, output, theme, scanout_node, &mut |elem| {
push(CosmicElement::MoveGrab(RescaleRenderElement::from_element(
elem,
focal_point
.as_logical()
.to_physical(output.current_scale().fractional_scale())
.to_i32_round(),
zoom_scale,
))
}));
)));
})
}
if let Some((grab_elements, should_scale)) = seat
if let Some(grab_state) = seat
.user_data()
.get::<SeatMenuGrabState>()
.unwrap()
.lock()
.unwrap()
.as_ref()
.map(|state| {
(
state.render::<CosmicMappedRenderElement<R>, R>(renderer, output),
!state.is_in_screen_space(),
)
})
{
elements.extend(grab_elements.into_iter().map(|elem| {
CosmicElement::MoveGrab(RescaleRenderElement::from_element(
elem,
let should_scale = !grab_state.is_in_screen_space();
grab_state.render(renderer, output, &mut |elem| {
push(CosmicElement::MoveGrab(RescaleRenderElement::from_element(
elem.into(),
if should_scale {
focal_point
.as_logical()
@ -596,12 +580,10 @@ where
Point::from((0, 0))
},
if should_scale { zoom_scale } else { 1.0 },
))
}));
)));
})
}
}
elements
}
#[cfg(not(feature = "debug"))]
@ -729,7 +711,7 @@ where
CosmicMappedRenderElement<R>: RenderElement<R>,
WorkspaceRenderElement<R>: RenderElement<R>,
{
let mut elements = Vec::new();
let mut elements = Vec::<CosmicElement<R>>::new();
let shell_ref = shell.read();
let seats = shell_ref.seats.iter().cloned().collect::<Vec<_>>();
@ -742,7 +724,7 @@ where
// that is prone to deadlock with the main-thread on some grabs.
std::mem::drop(shell_ref);
elements.extend(cursor_elements(
cursor_elements(
renderer,
seats.iter(),
zoom_level,
@ -752,7 +734,8 @@ where
cursor_mode,
element_filter == ElementFilter::ExcludeWorkspaceOverview,
scanout_node,
));
&mut |elem| elements.push(elem),
);
let shell = shell.read();
let overview = shell.overview_mode();
@ -828,72 +811,81 @@ where
render_input_order::<()>(&shell, output, previous, current, element_filter, |stage| {
match stage {
Stage::ZoomUI => {
elements.extend(ZoomState::render(renderer, output));
ZoomState::render(renderer, output, &mut |elem| {
elements.push(CosmicElement::Zoom(elem))
});
}
Stage::SessionLock(lock_surface) => {
elements.extend(
session_lock_elements(renderer, output, lock_surface)
.into_iter()
.map(Into::into)
.flat_map(crop_to_output)
.map(Into::into),
);
session_lock_elements(renderer, output, lock_surface, &mut |elem| {
elements.extend(crop_to_output(elem.into()).map(Into::into))
})
}
Stage::LayerPopup {
popup, location, ..
} => {
elements.extend(
render_elements_from_surface_tree::<_, WorkspaceRenderElement<_>>(
renderer,
popup.wl_surface(),
location
.to_local(output)
.as_logical()
.to_physical_precise_round(scale),
Scale::from(scale),
1.0,
FRAME_TIME_FILTER,
)
.into_iter()
.flat_map(crop_to_output)
.map(Into::into),
);
let mut geometry = popup.geometry().as_global();
geometry.loc += location;
push_render_elements_from_surface_tree(
renderer,
popup.wl_surface(),
location
.to_local(output)
.as_logical()
.to_physical_precise_round(scale),
geometry.to_local(output).as_logical().to_f64(),
Scale::from(scale),
1.0,
false,
[0; 4],
FRAME_TIME_FILTER,
&mut |elem| elements.extend(crop_to_output(elem.into()).map(Into::into)),
None,
)
}
Stage::LayerSurface { layer, location } => {
elements.extend(
render_elements_from_surface_tree::<_, WorkspaceRenderElement<_>>(
renderer,
layer.wl_surface(),
location
.to_local(output)
.as_logical()
.to_physical_precise_round(scale),
Scale::from(scale),
1.0,
FRAME_TIME_FILTER,
)
.into_iter()
.flat_map(crop_to_output)
.map(Into::into),
let mut geometry = layer.geometry().as_global();
geometry.loc += location;
push_render_elements_from_surface_tree(
renderer,
layer.wl_surface(),
location
.to_local(output)
.as_logical()
.to_physical_precise_round(scale),
geometry.to_local(output).as_logical().to_f64(),
Scale::from(scale),
1.0,
false,
[0; 4],
FRAME_TIME_FILTER,
&mut |elem| elements.extend(crop_to_output(elem.into()).map(Into::into)),
None,
);
}
Stage::OverrideRedirect { surface, location } => {
elements.extend(surface.wl_surface().into_iter().flat_map(|surface| {
render_elements_from_surface_tree::<_, WorkspaceRenderElement<_>>(
if let Some(wl_surface) = surface.wl_surface() {
let mut geometry = surface.geometry().as_global();
geometry.loc += location;
push_render_elements_from_surface_tree(
renderer,
&surface,
&wl_surface,
location
.to_local(output)
.as_logical()
.to_physical_precise_round(scale),
geometry.to_local(output).as_logical().to_f64(),
Scale::from(scale),
1.0,
false,
[0; 4],
FRAME_TIME_FILTER,
)
.into_iter()
.flat_map(crop_to_output)
.map(Into::into)
}));
&mut |elem| elements.extend(crop_to_output(elem.into()).map(Into::into)),
None,
);
}
}
Stage::StickyPopups(layout) => {
let alpha = match &overview.0 {
@ -914,14 +906,11 @@ where
OverviewMode::None => 1.0,
};
elements.extend(
layout
.render_popups(renderer, alpha, scanout_node)
.into_iter()
.map(Into::into)
.flat_map(crop_to_output)
.map(Into::into),
);
layout.render_popups(renderer, alpha, scanout_node, &mut |elem| {
if let Some(elem) = crop_to_output(elem.into()) {
elements.push(elem.into())
}
});
}
Stage::Sticky(layout) => {
let alpha = match &overview.0 {
@ -946,82 +935,66 @@ where
.then_some(last_active_seat)
.map(|seat| workspace.focus_stack.get(seat));
elements.extend(
layout
.render(
renderer,
current_focus.as_ref().and_then(|stack| {
stack.last().and_then(|t| match t {
FocusTarget::Window(w) => Some(w),
_ => None,
})
}),
resize_indicator.clone(),
active_hint,
alpha,
theme.cosmic(),
scanout_node,
)
.into_iter()
.map(Into::into)
.flat_map(crop_to_output)
.map(Into::into),
)
layout.render(
renderer,
current_focus.as_ref().and_then(|stack| {
stack.last().and_then(|t| match t {
FocusTarget::Window(w) => Some(w),
_ => None,
})
}),
resize_indicator.clone(),
active_hint,
alpha,
theme.cosmic(),
scanout_node,
&mut |elem| {
if let Some(elem) = crop_to_output(elem.into()) {
elements.push(elem.into())
}
},
);
}
Stage::WorkspacePopups { workspace, offset } => {
elements.extend(
match workspace.render_popups(
renderer,
last_active_seat,
!move_active && is_active_space,
overview.clone(),
theme.cosmic(),
scanout_node,
) {
Ok(elements) => {
elements
.into_iter()
.flat_map(crop_to_output)
.map(|element| {
CosmicElement::Workspace(RelocateRenderElement::from_element(
element,
offset.to_physical_precise_round(scale),
Relocate::Relative,
))
})
}
Err(_) => {
return ControlFlow::Break(Err(OutputNoMode));
workspace.render_popups(
renderer,
last_active_seat,
!move_active && is_active_space,
overview.clone(),
theme.cosmic(),
scanout_node,
&mut |elem| {
if let Some(elem) = crop_to_output(elem) {
elements.push(CosmicElement::Workspace(
RelocateRenderElement::from_element(
elem,
offset.to_physical_precise_round(scale),
Relocate::Relative,
),
));
}
},
);
}
Stage::Workspace { workspace, offset } => {
elements.extend(
match workspace.render(
renderer,
last_active_seat,
!move_active && is_active_space,
overview.clone(),
resize_indicator.clone(),
active_hint,
theme.cosmic(),
scanout_node,
) {
Ok(elements) => {
elements
.into_iter()
.flat_map(crop_to_output)
.map(|element| {
CosmicElement::Workspace(RelocateRenderElement::from_element(
element,
offset.to_physical_precise_round(scale),
Relocate::Relative,
))
})
}
Err(_) => {
return ControlFlow::Break(Err(OutputNoMode));
workspace.render(
renderer,
last_active_seat,
!move_active && is_active_space,
overview.clone(),
resize_indicator.clone(),
active_hint,
theme.cosmic(),
scanout_node,
&mut |elem| {
if let Some(elem) = crop_to_output(elem) {
elements.push(CosmicElement::Workspace(
RelocateRenderElement::from_element(
elem,
offset.to_physical_precise_round(scale),
Relocate::Relative,
),
));
}
},
);
@ -1038,23 +1011,26 @@ fn session_lock_elements<R>(
renderer: &mut R,
output: &Output,
lock_surface: Option<&LockSurface>,
) -> Vec<WaylandSurfaceRenderElement<R>>
where
R: Renderer + ImportAll,
push: &mut dyn FnMut(SurfaceRenderElement<R>),
) where
R: AsGlowRenderer,
R::TextureId: Clone + 'static,
{
if let Some(surface) = lock_surface {
let scale = Scale::from(output.current_scale().fractional_scale());
render_elements_from_surface_tree(
push_render_elements_from_surface_tree(
renderer,
surface.wl_surface(),
(0, 0),
bbox_from_surface_tree(surface.wl_surface(), (0, 0)).to_f64(),
scale,
1.0,
false,
[0; 4],
FRAME_TIME_FILTER,
push,
None,
)
} else {
Vec::new()
}
}

View file

@ -23,7 +23,7 @@ render_elements! {
Wayland=WaylandSurfaceRenderElement<R>,
}
pub fn render_elements_from_surface_tree<R, E>(
pub fn push_render_elements_from_surface_tree<R>(
renderer: &mut R,
main_surface: &wl_surface::WlSurface,
location: impl Into<Point<i32, Physical>>,
@ -33,17 +33,17 @@ pub fn render_elements_from_surface_tree<R, E>(
should_clip: bool,
radii: [u8; 4],
kind: impl Into<KindEvaluation>,
) -> Vec<E>
where
push_above: &mut dyn FnMut(SurfaceRenderElement<R>),
mut push_below: Option<&mut dyn FnMut(SurfaceRenderElement<R>)>,
) where
R: Renderer + ImportAll + AsGlowRenderer,
R::TextureId: Clone + 'static,
E: From<SurfaceRenderElement<R>>,
{
let location = location.into().to_f64();
let geometry = geometry.into().to_f64();
let scale = scale.into();
let kind = kind.into();
let mut surfaces: Vec<E> = Vec::new();
let mut passed_main = false;
compositor::with_surface_tree_downward(
main_surface,
@ -94,7 +94,13 @@ where
} else {
surface.into()
};
surfaces.push(elem.into());
if let Some(push_below) = push_below.as_mut()
&& passed_main
{
push_below(elem);
} else {
push_above(elem);
}
}
Ok(None) => {} // surface is not mapped
Err(err) => {
@ -103,9 +109,11 @@ where
};
}
}
if surface == main_surface {
passed_main = true;
}
},
|_, _, _| true,
);
surfaces
}