Refactor rendering to push-based instead of pull-based (#3113)

Our current rendering code constructs and returns complex
`-> impl Iterator<Item = SomeRenderElement>` types that are collected
into a vector at the top level Niri::render(). This causes some
problems:
- It's hard to write logic around returning iterators. Especially things
  like conditions, since the returned iterator must have a single type,
  you can't branch and return different iterators. This will be solved
  by gen fn but alas it's not here yet.
- In many cases, the returned `-> impl Iterator` will borrow from &self
  leading to complex lifetimes. In certain cases, it is also desirable
  for it to borrow the &mut NiriRenderer, which causes a lot of issues
  because it's exclusive (&mut).
- Sometimes those issues are too hard to deal with, leading to the
  escape hatch of allocating and returning a temporary
  Vec<SomeRenderElement>, like in
  Scrolling/FloatingSpace::render_elements(). These allocations are
  unfortunate because they are not really necessary.
- It's impossible to use some downstream combinators with this
  `-> impl Iterator` approach, leading to functions like Smithay's
  render_elements_from_surface_tree() returning a Vec. This is extra
  unfortunate because it results in a temporary allocation per Wayland
  toplevel/popup.
- It's hard to properly create profiling spans for the rendering
  functions since the spans are dropped when the (lazy) iterator is
  returned and not when all the code actually completes.
- The code compiles down to complex state machines in generated iterator
  types with logic located in Iterator::next(), which makes it annoying
  to follow in debuggers and profiling tools.

This refactor changes the code to push-based iteration: rendering
functions receive a push() closure that they call to push their render
elements. It solves all of the aforementioned problems:
- The logic becomes simpler. Just use conditionals and loops as normal.
- No borrowing and lifetimes since we're not returning anything.
- All temporary Vecs are removed because the problems they worked around
  no longer exist.
- The new push_elements_from_surface_tree() helper is the same as
  render_elements_from_surface_tree() but doesn't allocate a temporary
  Vec since it's not necessary; the push() closure can be passed down.
- Profiling spans work normally since the function returns when it ran
  all of the logic.
- The code compiles down to normal functions and calls as expected.

Generally, the iterator approach gives these advantages:
- You can wrap the returned items in the upstream logic. This is
  possible in exactly the same way with the push closure.
- You can decide to cut the iterator short in the upstream logic. This
  is not possible with push-based iteration, but we don't actually use
  it anywhere.

I chose the push closure type to be &mut dyn FnMut(SomeRenderElement).
It's deliberately not a generic impl FnMut() to avoid duplicating the
rendering logic when it's called from several different places. But it's
still a normal closure that can capture the outside context.

While my original idea for this refactor was to simplify the logic while
getting rid of temporary Vecs, it also appears to have brought a
consistent 2-3x speedup to the whole render list construction. On an old
Eee PC laptop I even observed a 8x speedup.

The refactor also results in smaller binary size, presumably due to
removing many iterator combinators and state tracking.
This commit is contained in:
Ivan Molodetskikh
2025-12-25 14:26:19 +03:00
committed by GitHub
parent 1a63089d67
commit 7f132ecf95
24 changed files with 764 additions and 782 deletions
+1 -4
View File
@@ -89,11 +89,8 @@ impl TestCase for GradientArea {
1., 1.,
1., 1.,
); );
rv.extend(
self.border self.border
.render(renderer, g_loc) .render(renderer, g_loc, &mut |elem| rv.push(Box::new(elem) as _));
.map(|elem| Box::new(elem) as _),
);
rv.extend( rv.extend(
[BorderRenderElement::new( [BorderRenderElement::new(
+6 -4
View File
@@ -268,12 +268,14 @@ impl TestCase for Layout {
_size: Size<i32, Physical>, _size: Size<i32, Physical>,
) -> Vec<Box<dyn RenderElement<GlesRenderer>>> { ) -> Vec<Box<dyn RenderElement<GlesRenderer>>> {
self.layout.update_render_elements(Some(&self.output)); self.layout.update_render_elements(Some(&self.output));
let mut rv = Vec::new();
self.layout self.layout
.monitor_for_output(&self.output) .monitor_for_output(&self.output)
.unwrap() .unwrap()
.render_elements(renderer, RenderTarget::Output, true) .render_workspaces(renderer, RenderTarget::Output, true, &mut |elem| {
.flat_map(|(_, _, iter)| iter) rv.push(Box::new(elem) as _)
.map(|elem| Box::new(elem) as _) });
.collect() rv
} }
} }
+10 -4
View File
@@ -119,9 +119,15 @@ impl TestCase for Tile {
true, true,
Rectangle::new(Point::from((-location.x, -location.y)), size.to_logical(1.)), Rectangle::new(Point::from((-location.x, -location.y)), size.to_logical(1.)),
); );
self.tile
.render(renderer, location, true, RenderTarget::Output) let mut rv = Vec::new();
.map(|elem| Box::new(elem) as _) self.tile.render(
.collect() renderer,
location,
true,
RenderTarget::Output,
&mut |elem| rv.push(Box::new(elem) as _),
);
rv
} }
} }
+5 -6
View File
@@ -52,16 +52,15 @@ impl TestCase for Window {
.to_f64() .to_f64()
.downscale(2.); .downscale(2.);
self.window let mut rv = Vec::new();
.render( self.window.render_normal(
renderer, renderer,
location, location,
Scale::from(1.), Scale::from(1.),
1., 1.,
RenderTarget::Output, RenderTarget::Output,
) &mut |elem| rv.push(Box::new(elem) as _),
.into_iter() );
.map(|elem| Box::new(elem) as _) rv
.collect()
} }
} }
+10 -16
View File
@@ -9,7 +9,7 @@ use niri::layout::{
use niri::render_helpers::offscreen::OffscreenData; use niri::render_helpers::offscreen::OffscreenData;
use niri::render_helpers::renderer::NiriRenderer; use niri::render_helpers::renderer::NiriRenderer;
use niri::render_helpers::solid_color::{SolidColorBuffer, SolidColorRenderElement}; use niri::render_helpers::solid_color::{SolidColorBuffer, SolidColorRenderElement};
use niri::render_helpers::{RenderTarget, SplitElements}; use niri::render_helpers::RenderTarget;
use niri::utils::transaction::Transaction; use niri::utils::transaction::Transaction;
use niri::window::ResolvedWindowRules; use niri::window::ResolvedWindowRules;
use smithay::backend::renderer::element::Kind; use smithay::backend::renderer::element::Kind;
@@ -149,36 +149,30 @@ impl LayoutElement for TestWindow {
false false
} }
fn render<R: NiriRenderer>( fn render_normal<R: NiriRenderer>(
&self, &self,
_renderer: &mut R, _renderer: &mut R,
location: Point<f64, Logical>, location: Point<f64, Logical>,
_scale: Scale<f64>, _scale: Scale<f64>,
alpha: f32, alpha: f32,
_target: RenderTarget, _target: RenderTarget,
) -> SplitElements<LayoutElementRenderElement<R>> { push: &mut dyn FnMut(LayoutElementRenderElement<R>),
) {
let inner = self.inner.borrow(); let inner = self.inner.borrow();
SplitElements { push(
normal: vec![ SolidColorRenderElement::from_buffer(&inner.buffer, location, alpha, Kind::Unspecified)
SolidColorRenderElement::from_buffer(
&inner.buffer,
location,
alpha,
Kind::Unspecified,
)
.into(), .into(),
);
push(
SolidColorRenderElement::from_buffer( SolidColorRenderElement::from_buffer(
&inner.csd_shadow_buffer, &inner.csd_shadow_buffer,
location location - Point::from((inner.csd_shadow_width, inner.csd_shadow_width)).to_f64(),
- Point::from((inner.csd_shadow_width, inner.csd_shadow_width)).to_f64(),
alpha, alpha,
Kind::Unspecified, Kind::Unspecified,
) )
.into(), .into(),
], );
popups: vec![],
}
} }
fn request_size( fn request_size(
+44 -26
View File
@@ -1,8 +1,6 @@
use niri_config::utils::MergeWith as _; use niri_config::utils::MergeWith as _;
use niri_config::{Config, LayerRule}; use niri_config::{Config, LayerRule};
use smithay::backend::renderer::element::surface::{ use smithay::backend::renderer::element::surface::WaylandSurfaceRenderElement;
render_elements_from_surface_tree, WaylandSurfaceRenderElement,
};
use smithay::backend::renderer::element::Kind; use smithay::backend::renderer::element::Kind;
use smithay::desktop::{LayerSurface, PopupManager}; use smithay::desktop::{LayerSurface, PopupManager};
use smithay::utils::{Logical, Point, Scale, Size}; use smithay::utils::{Logical, Point, Scale, Size};
@@ -15,7 +13,8 @@ use crate::niri_render_elements;
use crate::render_helpers::renderer::NiriRenderer; use crate::render_helpers::renderer::NiriRenderer;
use crate::render_helpers::shadow::ShadowRenderElement; use crate::render_helpers::shadow::ShadowRenderElement;
use crate::render_helpers::solid_color::{SolidColorBuffer, SolidColorRenderElement}; use crate::render_helpers::solid_color::{SolidColorBuffer, SolidColorRenderElement};
use crate::render_helpers::{RenderTarget, SplitElements}; use crate::render_helpers::surface::push_elements_from_surface_tree;
use crate::render_helpers::RenderTarget;
use crate::utils::{baba_is_float_offset, round_logical_in_physical}; use crate::utils::{baba_is_float_offset, round_logical_in_physical};
#[derive(Debug)] #[derive(Debug)]
@@ -156,14 +155,13 @@ impl MappedLayer {
Point::from((0., y)) Point::from((0., y))
} }
pub fn render<R: NiriRenderer>( pub fn render_normal<R: NiriRenderer>(
&self, &self,
renderer: &mut R, renderer: &mut R,
location: Point<f64, Logical>, location: Point<f64, Logical>,
target: RenderTarget, target: RenderTarget,
) -> SplitElements<LayerSurfaceRenderElement<R>> { push: &mut dyn FnMut(LayerSurfaceRenderElement<R>),
let mut rv = SplitElements::default(); ) {
let scale = Scale::from(self.scale); let scale = Scale::from(self.scale);
let alpha = self.rules.opacity.unwrap_or(1.).clamp(0., 1.); let alpha = self.rules.opacity.unwrap_or(1.).clamp(0., 1.);
let location = location + self.bob_offset(); let location = location + self.bob_offset();
@@ -179,40 +177,60 @@ impl MappedLayer {
alpha, alpha,
Kind::Unspecified, Kind::Unspecified,
); );
rv.normal.push(elem.into()); push(elem.into());
} else { } else {
// Layer surfaces don't have extra geometry like windows. // Layer surfaces don't have extra geometry like windows.
let buf_pos = location; let buf_pos = location;
let surface = self.surface.wl_surface();
push_elements_from_surface_tree(
renderer,
surface,
buf_pos.to_physical_precise_round(scale),
scale,
alpha,
Kind::ScanoutCandidate,
&mut |elem| push(elem.into()),
);
}
let location = location.to_physical_precise_round(scale).to_logical(scale);
self.shadow
.render(renderer, location, &mut |elem| push(elem.into()));
}
pub fn render_popups<R: NiriRenderer>(
&self,
renderer: &mut R,
location: Point<f64, Logical>,
target: RenderTarget,
push: &mut dyn FnMut(LayerSurfaceRenderElement<R>),
) {
let scale = Scale::from(self.scale);
let alpha = self.rules.opacity.unwrap_or(1.).clamp(0., 1.);
let location = location + self.bob_offset();
if target.should_block_out(self.rules.block_out_from) {
return;
}
// Layer surfaces don't have extra geometry like windows.
let buf_pos = location;
let surface = self.surface.wl_surface(); let surface = self.surface.wl_surface();
for (popup, popup_offset) in PopupManager::popups_for_surface(surface) { for (popup, popup_offset) in PopupManager::popups_for_surface(surface) {
// Layer surfaces don't have extra geometry like windows. // Layer surfaces don't have extra geometry like windows.
let offset = popup_offset - popup.geometry().loc; let offset = popup_offset - popup.geometry().loc;
rv.popups.extend(render_elements_from_surface_tree( push_elements_from_surface_tree(
renderer, renderer,
popup.wl_surface(), popup.wl_surface(),
(buf_pos + offset.to_f64()).to_physical_precise_round(scale), (buf_pos + offset.to_f64()).to_physical_precise_round(scale),
scale, scale,
alpha, alpha,
Kind::ScanoutCandidate, Kind::ScanoutCandidate,
)); &mut |elem| push(elem.into()),
}
rv.normal = render_elements_from_surface_tree(
renderer,
surface,
buf_pos.to_physical_precise_round(scale),
scale,
alpha,
Kind::ScanoutCandidate,
); );
} }
let location = location.to_physical_precise_round(scale).to_logical(scale);
rv.normal
.extend(self.shadow.render(renderer, location).map(Into::into));
rv
} }
} }
+7 -11
View File
@@ -1053,15 +1053,14 @@ impl<W: LayoutElement> FloatingSpace<W> {
true true
} }
pub fn render_elements<R: NiriRenderer>( pub fn render<R: NiriRenderer>(
&self, &self,
renderer: &mut R, renderer: &mut R,
view_rect: Rectangle<f64, Logical>, view_rect: Rectangle<f64, Logical>,
target: RenderTarget, target: RenderTarget,
focus_ring: bool, focus_ring: bool,
) -> Vec<FloatingSpaceRenderElement<R>> { push: &mut dyn FnMut(FloatingSpaceRenderElement<R>),
let mut rv = Vec::new(); ) {
let scale = Scale::from(self.scale); let scale = Scale::from(self.scale);
// Draw the closing windows on top of the other windows. // Draw the closing windows on top of the other windows.
@@ -1069,7 +1068,7 @@ impl<W: LayoutElement> FloatingSpace<W> {
// FIXME: I guess this should rather preserve the stacking order when the window is closed. // FIXME: I guess this should rather preserve the stacking order when the window is closed.
for closing in self.closing_windows.iter().rev() { for closing in self.closing_windows.iter().rev() {
let elem = closing.render(renderer.as_gles_renderer(), view_rect, scale, target); let elem = closing.render(renderer.as_gles_renderer(), view_rect, scale, target);
rv.push(elem.into()); push(elem.into());
} }
let active = self.active_window_id.clone(); let active = self.active_window_id.clone();
@@ -1077,13 +1076,10 @@ impl<W: LayoutElement> FloatingSpace<W> {
// For the active tile, draw the focus ring. // For the active tile, draw the focus ring.
let focus_ring = focus_ring && Some(tile.window().id()) == active.as_ref(); let focus_ring = focus_ring && Some(tile.window().id()) == active.as_ref();
rv.extend( tile.render(renderer, tile_pos, focus_ring, target, &mut |elem| {
tile.render(renderer, tile_pos, focus_ring, target) push(elem.into())
.map(Into::into), });
);
} }
rv
} }
pub fn interactive_resize_begin(&mut self, window: W::Id, edges: ResizeEdge) -> bool { pub fn interactive_resize_begin(&mut self, window: W::Id, edges: ResizeEdge) -> bool {
+5 -9
View File
@@ -1,6 +1,5 @@
use std::iter::zip; use std::iter::zip;
use arrayvec::ArrayVec;
use niri_config::{CornerRadius, Gradient, GradientRelativeTo}; use niri_config::{CornerRadius, Gradient, GradientRelativeTo};
use smithay::backend::renderer::element::{Element as _, Kind}; use smithay::backend::renderer::element::{Element as _, Kind};
use smithay::utils::{Logical, Point, Rectangle, Size}; use smithay::utils::{Logical, Point, Rectangle, Size};
@@ -220,18 +219,17 @@ impl FocusRing {
&self, &self,
renderer: &mut impl NiriRenderer, renderer: &mut impl NiriRenderer,
location: Point<f64, Logical>, location: Point<f64, Logical>,
) -> impl Iterator<Item = FocusRingRenderElement> { push: &mut dyn FnMut(FocusRingRenderElement),
let mut rv = ArrayVec::<_, 8>::new(); ) {
if self.config.off { if self.config.off {
return rv.into_iter(); return;
} }
let border_width = -self.locations[0].y; let border_width = -self.locations[0].y;
// If drawing as a border with width = 0, then there's nothing to draw. // If drawing as a border with width = 0, then there's nothing to draw.
if self.is_border && border_width == 0. { if self.is_border && border_width == 0. {
return rv.into_iter(); return;
} }
let has_border_shader = BorderRenderElement::has_shader(renderer); let has_border_shader = BorderRenderElement::has_shader(renderer);
@@ -244,7 +242,7 @@ impl FocusRing {
SolidColorRenderElement::from_buffer(buffer, location, alpha, Kind::Unspecified) SolidColorRenderElement::from_buffer(buffer, location, alpha, Kind::Unspecified)
.into() .into()
}; };
rv.push(elem); push(elem);
}; };
if self.is_border { if self.is_border {
@@ -258,8 +256,6 @@ impl FocusRing {
location + self.locations[0], location + self.locations[0],
); );
} }
rv.into_iter()
} }
pub fn width(&self) -> f64 { pub fn width(&self) -> f64 {
+3 -2
View File
@@ -59,7 +59,8 @@ impl InsertHintElement {
&self, &self,
renderer: &mut impl NiriRenderer, renderer: &mut impl NiriRenderer,
location: Point<f64, Logical>, location: Point<f64, Logical>,
) -> impl Iterator<Item = FocusRingRenderElement> { push: &mut dyn FnMut(FocusRingRenderElement),
self.inner.render(renderer, location) ) {
self.inner.render(renderer, location, push)
} }
} }
+27 -22
View File
@@ -64,7 +64,7 @@ use crate::render_helpers::renderer::NiriRenderer;
use crate::render_helpers::snapshot::RenderSnapshot; use crate::render_helpers::snapshot::RenderSnapshot;
use crate::render_helpers::solid_color::{SolidColorBuffer, SolidColorRenderElement}; use crate::render_helpers::solid_color::{SolidColorBuffer, SolidColorRenderElement};
use crate::render_helpers::texture::TextureBuffer; use crate::render_helpers::texture::TextureBuffer;
use crate::render_helpers::{BakedBuffer, RenderTarget, SplitElements}; use crate::render_helpers::{BakedBuffer, RenderTarget};
use crate::rubber_band::RubberBand; use crate::rubber_band::RubberBand;
use crate::utils::transaction::{Transaction, TransactionBlocker}; use crate::utils::transaction::{Transaction, TransactionBlocker};
use crate::utils::{ use crate::utils::{
@@ -159,7 +159,11 @@ pub trait LayoutElement {
scale: Scale<f64>, scale: Scale<f64>,
alpha: f32, alpha: f32,
target: RenderTarget, target: RenderTarget,
) -> SplitElements<LayoutElementRenderElement<R>>; push: &mut dyn FnMut(LayoutElementRenderElement<R>),
) {
self.render_popups(renderer, location, scale, alpha, target, push);
self.render_normal(renderer, location, scale, alpha, target, push);
}
/// Renders the non-popup parts of the element. /// Renders the non-popup parts of the element.
fn render_normal<R: NiriRenderer>( fn render_normal<R: NiriRenderer>(
@@ -169,8 +173,9 @@ pub trait LayoutElement {
scale: Scale<f64>, scale: Scale<f64>,
alpha: f32, alpha: f32,
target: RenderTarget, target: RenderTarget,
) -> Vec<LayoutElementRenderElement<R>> { push: &mut dyn FnMut(LayoutElementRenderElement<R>),
self.render(renderer, location, scale, alpha, target).normal ) {
let _ = (renderer, location, scale, alpha, target, push);
} }
/// Renders the popups of the element. /// Renders the popups of the element.
@@ -181,8 +186,9 @@ pub trait LayoutElement {
scale: Scale<f64>, scale: Scale<f64>,
alpha: f32, alpha: f32,
target: RenderTarget, target: RenderTarget,
) -> Vec<LayoutElementRenderElement<R>> { push: &mut dyn FnMut(LayoutElementRenderElement<R>),
self.render(renderer, location, scale, alpha, target).popups ) {
let _ = (renderer, location, scale, alpha, target, push);
} }
/// Requests the element to change its size. /// Requests the element to change its size.
@@ -4713,38 +4719,37 @@ impl<W: LayoutElement> Layout<W> {
} }
} }
pub fn render_interactive_move_for_output<'a, R: NiriRenderer + 'a>( pub fn render_interactive_move_for_output<R: NiriRenderer>(
&'a self, &self,
renderer: &mut R, renderer: &mut R,
output: &Output, output: &Output,
target: RenderTarget, target: RenderTarget,
) -> impl Iterator<Item = RescaleRenderElement<TileRenderElement<R>>> + 'a { push: &mut dyn FnMut(RescaleRenderElement<TileRenderElement<R>>),
) {
if self.update_render_elements_time != self.clock.now() { if self.update_render_elements_time != self.clock.now() {
error!("clock moved between updating render elements and rendering"); error!("clock moved between updating render elements and rendering");
} }
let mut rv = None; let Some(InteractiveMoveState::Moving(move_)) = &self.interactive_move else {
return;
};
if &move_.output != output {
return;
}
if let Some(InteractiveMoveState::Moving(move_)) = &self.interactive_move {
if &move_.output == output {
let scale = Scale::from(move_.output.current_scale().fractional_scale()); let scale = Scale::from(move_.output.current_scale().fractional_scale());
let zoom = self.overview_zoom(); let zoom = self.overview_zoom();
let location = move_.tile_render_location(zoom); let location = move_.tile_render_location(zoom);
let iter = move_ move_
.tile .tile
.render(renderer, location, true, target) .render(renderer, location, true, target, &mut |elem| {
.map(move |elem| { push(RescaleRenderElement::from_element(
RescaleRenderElement::from_element(
elem, elem,
location.to_physical_precise_round(scale), location.to_physical_precise_round(scale),
zoom, zoom,
) ));
}); });
rv = Some(iter);
}
}
rv.into_iter().flatten()
} }
pub fn refresh(&mut self, is_active: bool) { pub fn refresh(&mut self, is_active: bool) {
+73 -95
View File
@@ -1638,40 +1638,36 @@ impl<W: LayoutElement> Monitor<W> {
pub fn render_insert_hint_between_workspaces<R: NiriRenderer>( pub fn render_insert_hint_between_workspaces<R: NiriRenderer>(
&self, &self,
renderer: &mut R, renderer: &mut R,
) -> impl Iterator<Item = MonitorRenderElement<R>> { push: &mut dyn FnMut(MonitorRenderElement<R>),
let mut rv = None; ) {
if self.options.layout.insert_hint.off {
return;
}
let Some(render_loc) = self.insert_hint_render_loc else {
return;
};
let InsertWorkspace::NewAt(_) = render_loc.workspace else {
return;
};
if !self.options.layout.insert_hint.off { self.insert_hint_element
if let Some(render_loc) = self.insert_hint_render_loc { .render(renderer, render_loc.location, &mut |elem| {
if let InsertWorkspace::NewAt(_) = render_loc.workspace { let elem = MonitorInnerRenderElement::UncroppedInsertHint(elem);
let iter = self
.insert_hint_element
.render(renderer, render_loc.location)
.map(MonitorInnerRenderElement::UncroppedInsertHint);
rv = Some(iter);
}
}
}
rv.into_iter().flatten().map(|elem| {
let elem = RescaleRenderElement::from_element(elem, Point::default(), 1.); let elem = RescaleRenderElement::from_element(elem, Point::default(), 1.);
RelocateRenderElement::from_element(elem, Point::default(), Relocate::Relative) let elem =
}) RelocateRenderElement::from_element(elem, Point::default(), Relocate::Relative);
push(elem);
});
} }
pub fn render_elements<'a, R: NiriRenderer>( pub fn render_workspaces<R: NiriRenderer>(
&'a self, &self,
renderer: &'a mut R, renderer: &mut R,
target: RenderTarget, target: RenderTarget,
focus_ring: bool, focus_ring: bool,
) -> impl Iterator< push: &mut dyn FnMut(MonitorRenderElement<R>),
Item = ( ) {
Rectangle<f64, Logical>, let _span = tracy_client::span!("Monitor::render_workspaces");
MonitorRenderElement<R>,
impl Iterator<Item = MonitorRenderElement<R>> + 'a,
),
> {
let _span = tracy_client::span!("Monitor::render_elements");
let scale = self.scale.fractional_scale(); let scale = self.scale.fractional_scale();
// Ceil the height in physical pixels. // Ceil the height in physical pixels.
@@ -1701,51 +1697,15 @@ impl<W: LayoutElement> Monitor<W> {
let zoom = self.overview_zoom(); let zoom = self.overview_zoom();
// Draw the insert hint. let insert_hint_render_loc = self
let mut insert_hint = None; .insert_hint_render_loc
if !self.options.layout.insert_hint.off { .filter(|_| !self.options.layout.insert_hint.off);
if let Some(render_loc) = self.insert_hint_render_loc {
if let InsertWorkspace::Existing(workspace_id) = render_loc.workspace {
insert_hint = Some((
workspace_id,
self.insert_hint_element
.render(renderer, render_loc.location),
));
}
}
}
self.workspaces_with_render_geo().map(move |(ws, geo)| { let scale_relocate = move |geo: Rectangle<f64, Logical>, elem| {
let map_ws_contents = move |elem: WorkspaceRenderElement<R>| {
let elem = CropRenderElement::from_element(elem, scale, crop_bounds)?;
let elem = MonitorInnerRenderElement::Workspace(elem);
Some(elem)
};
let (floating, scrolling) = ws.render_elements(renderer, target, focus_ring);
let floating = floating.filter_map(map_ws_contents);
let scrolling = scrolling.filter_map(map_ws_contents);
let hint = if matches!(insert_hint, Some((hint_ws_id, _)) if hint_ws_id == ws.id()) {
let iter = insert_hint.take().unwrap().1;
let iter = iter.filter_map(move |elem| {
let elem = CropRenderElement::from_element(elem, scale, crop_bounds)?;
let elem = MonitorInnerRenderElement::InsertHint(elem);
Some(elem)
});
Some(iter)
} else {
None
};
let hint = hint.into_iter().flatten();
let iter = floating.chain(hint).chain(scrolling);
let scale_relocate = move |elem| {
let elem = RescaleRenderElement::from_element(elem, Point::from((0, 0)), zoom); let elem = RescaleRenderElement::from_element(elem, Point::from((0, 0)), zoom);
RelocateRenderElement::from_element( RelocateRenderElement::from_element(
elem, elem,
// The offset we get from workspaces_with_render_positions() is already // The offset we get from workspaces_with_render_geo() is already
// rounded to physical pixels, but it's in the logical coordinate // rounded to physical pixels, but it's in the logical coordinate
// space, so we need to convert it to physical. // space, so we need to convert it to physical.
geo.loc.to_physical_precise_round(scale), geo.loc.to_physical_precise_round(scale),
@@ -1753,43 +1713,61 @@ impl<W: LayoutElement> Monitor<W> {
) )
}; };
let iter = iter.map(scale_relocate); for (ws, geo) in self.workspaces_with_render_geo() {
// Macro instead of closure because ws and insert hint have different elem types.
let background = ws.render_background(); macro_rules! push {
let background = scale_relocate(MonitorInnerRenderElement::SolidColor(background)); () => {{
&mut |elem| {
(geo, background, iter) let elem = CropRenderElement::from_element(elem, scale, crop_bounds);
}) if let Some(elem) = elem {
let elem = MonitorInnerRenderElement::from(elem);
push(scale_relocate(geo, elem));
}
}
}};
} }
pub fn render_workspace_shadows<'a, R: NiriRenderer>( ws.render_floating(renderer, target, focus_ring, push!());
&'a self,
renderer: &'a mut R, if let Some(loc) = insert_hint_render_loc {
) -> impl Iterator<Item = MonitorRenderElement<R>> + 'a { if loc.workspace == InsertWorkspace::Existing(ws.id()) {
self.insert_hint_element
.render(renderer, loc.location, push!());
}
}
ws.render_scrolling(renderer, target, focus_ring, push!());
}
}
pub fn render_workspace_shadows<R: NiriRenderer>(
&self,
renderer: &mut R,
push: &mut dyn FnMut(MonitorRenderElement<R>),
) {
let Some(progress) = self.overview_progress.as_ref().map(|p| p.clamped_value()) else {
return;
};
let alpha = progress.clamp(0., 1.) as f32;
let _span = tracy_client::span!("Monitor::render_workspace_shadows"); let _span = tracy_client::span!("Monitor::render_workspace_shadows");
let scale = self.scale.fractional_scale(); let scale = self.scale.fractional_scale();
let zoom = self.overview_zoom(); let zoom = self.overview_zoom();
let overview_clamped_progress = self.overview_progress.as_ref().map(|p| p.clamped_value());
self.workspaces_with_render_geo() for (ws, geo) in self.workspaces_with_render_geo() {
.flat_map(move |(ws, geo)| { ws.render_shadow(renderer, &mut |elem| {
let shadow = overview_clamped_progress.map(|value| { let elem = elem.with_alpha(alpha);
ws.render_shadow(renderer) let elem = MonitorInnerRenderElement::Shadow(elem);
.map(move |elem| elem.with_alpha(value.clamp(0., 1.) as f32))
.map(MonitorInnerRenderElement::Shadow)
});
let iter = shadow.into_iter().flatten();
iter.map(move |elem| {
let elem = RescaleRenderElement::from_element(elem, Point::from((0, 0)), zoom); let elem = RescaleRenderElement::from_element(elem, Point::from((0, 0)), zoom);
RelocateRenderElement::from_element( let elem = RelocateRenderElement::from_element(
elem, elem,
geo.loc.to_physical_precise_round(scale), geo.loc.to_physical_precise_round(scale),
Relocate::Relative, Relocate::Relative,
) );
}) push(elem);
}) });
}
} }
pub fn workspace_switch_gesture_begin(&mut self, is_touchpad: bool) { pub fn workspace_switch_gesture_begin(&mut self, is_touchpad: bool) {
+10 -13
View File
@@ -2897,25 +2897,24 @@ impl<W: LayoutElement> ScrollingSpace<W> {
.is_fullscreen() .is_fullscreen()
} }
pub fn render_elements<R: NiriRenderer>( pub fn render<R: NiriRenderer>(
&self, &self,
renderer: &mut R, renderer: &mut R,
target: RenderTarget, target: RenderTarget,
focus_ring: bool, focus_ring: bool,
) -> Vec<ScrollingSpaceRenderElement<R>> { push: &mut dyn FnMut(ScrollingSpaceRenderElement<R>),
let mut rv = vec![]; ) {
let scale = Scale::from(self.scale); let scale = Scale::from(self.scale);
// Draw the closing windows on top of the other windows. // Draw the closing windows on top of the other windows.
let view_rect = Rectangle::new(Point::from((self.view_pos(), 0.)), self.view_size); let view_rect = Rectangle::new(Point::from((self.view_pos(), 0.)), self.view_size);
for closing in self.closing_windows.iter().rev() { for closing in self.closing_windows.iter().rev() {
let elem = closing.render(renderer.as_gles_renderer(), view_rect, scale, target); let elem = closing.render(renderer.as_gles_renderer(), view_rect, scale, target);
rv.push(elem.into()); push(elem.into());
} }
if self.columns.is_empty() { if self.columns.is_empty() {
return rv; return;
} }
let mut first = true; let mut first = true;
@@ -2930,7 +2929,8 @@ impl<W: LayoutElement> ScrollingSpace<W> {
{ {
let pos = view_off + col_off + col_render_off; let pos = view_off + col_off + col_render_off;
let pos = pos.to_physical_precise_round(scale).to_logical(scale); let pos = pos.to_physical_precise_round(scale).to_logical(scale);
rv.extend(col.tab_indicator.render(renderer, pos).map(Into::into)); col.tab_indicator
.render(renderer, pos, &mut |elem| push(elem.into()));
} }
for (tile, tile_off, visible) in col.tiles_in_render_order() { for (tile, tile_off, visible) in col.tiles_in_render_order() {
@@ -2955,14 +2955,11 @@ impl<W: LayoutElement> ScrollingSpace<W> {
continue; continue;
} }
rv.extend( tile.render(renderer, tile_pos, focus_ring, target, &mut |elem| {
tile.render(renderer, tile_pos, focus_ring, target) push(elem.into())
.map(Into::into), });
);
} }
} }
rv
} }
pub fn window_under(&self, pos: Point<f64, Logical>) -> Option<(&W, HitType)> { pub fn window_under(&self, pos: Point<f64, Logical>) -> Option<(&W, HitType)> {
+7 -7
View File
@@ -166,19 +166,19 @@ impl Shadow {
&self, &self,
renderer: &mut impl NiriRenderer, renderer: &mut impl NiriRenderer,
location: Point<f64, Logical>, location: Point<f64, Logical>,
) -> impl Iterator<Item = ShadowRenderElement> + '_ { push: &mut dyn FnMut(ShadowRenderElement),
) {
if !self.config.on { if !self.config.on {
return None.into_iter().flatten(); return;
} }
let has_shadow_shader = ShadowRenderElement::has_shader(renderer); let has_shadow_shader = ShadowRenderElement::has_shader(renderer);
if !has_shadow_shader { if !has_shadow_shader {
return None.into_iter().flatten(); return;
} }
let rv = zip(&self.shaders, &self.shader_rects) for (shader, rect) in zip(&self.shaders, &self.shader_rects) {
.map(move |(shader, rect)| shader.clone().with_location(location + rect.loc)); push(shader.clone().with_location(location + rect.loc));
}
Some(rv).into_iter().flatten()
} }
} }
+7 -7
View File
@@ -294,17 +294,17 @@ impl TabIndicator {
&self, &self,
renderer: &mut impl NiriRenderer, renderer: &mut impl NiriRenderer,
pos: Point<f64, Logical>, pos: Point<f64, Logical>,
) -> impl Iterator<Item = TabIndicatorRenderElement> + '_ { push: &mut dyn FnMut(TabIndicatorRenderElement),
) {
let has_border_shader = BorderRenderElement::has_shader(renderer); let has_border_shader = BorderRenderElement::has_shader(renderer);
if !has_border_shader { if !has_border_shader {
return None.into_iter().flatten(); return;
} }
let rv = zip(&self.shaders, &self.shader_locs) for (shader, loc) in zip(&self.shaders, &self.shader_locs) {
.map(move |(shader, loc)| shader.clone().with_location(pos + *loc)) let elem = shader.clone().with_location(pos + *loc);
.map(TabIndicatorRenderElement::from); push(TabIndicatorRenderElement::from(elem));
}
Some(rv).into_iter().flatten()
} }
/// Extra size occupied by the tab indicator. /// Extra size occupied by the tab indicator.
-11
View File
@@ -166,17 +166,6 @@ impl LayoutElement for TestWindow {
false false
} }
fn render<R: NiriRenderer>(
&self,
_renderer: &mut R,
_location: Point<f64, Logical>,
_scale: Scale<f64>,
_alpha: f32,
_target: RenderTarget,
) -> SplitElements<LayoutElementRenderElement<R>> {
SplitElements::default()
}
fn request_size( fn request_size(
&mut self, &mut self,
size: Size<i32, Logical>, size: Size<i32, Logical>,
+105 -92
View File
@@ -1007,13 +1007,14 @@ impl<W: LayoutElement> Tile<W> {
Point::from((0., y)) Point::from((0., y))
} }
fn render_inner<'a, R: NiriRenderer + 'a>( fn render_inner<R: NiriRenderer>(
&'a self, &self,
renderer: &mut R, renderer: &mut R,
location: Point<f64, Logical>, location: Point<f64, Logical>,
focus_ring: bool, focus_ring: bool,
target: RenderTarget, target: RenderTarget,
) -> impl Iterator<Item = TileRenderElement<R>> + 'a { push: &mut dyn FnMut(TileRenderElement<R>),
) {
let _span = tracy_client::span!("Tile::render_inner"); let _span = tracy_client::span!("Tile::render_inner");
let scale = Scale::from(self.scale); let scale = Scale::from(self.scale);
@@ -1056,29 +1057,31 @@ impl<W: LayoutElement> Tile<W> {
.unwrap_or_default() .unwrap_or_default()
.scaled_by(1. - expanded_progress as f32); .scaled_by(1. - expanded_progress as f32);
// If we're resizing, try to render a shader, or a fallback. // Popups go on top, whether it's resize or not.
let mut resize_shader = None; self.window.render_popups(
let mut resize_popups = None; renderer,
let mut resize_fallback = None; window_render_loc,
scale,
if let Some(resize) = &self.resize_animation { win_alpha,
resize_popups = Some( target,
self.window &mut |elem| push(elem.into()),
.render_popups(renderer, window_render_loc, scale, win_alpha, target)
.into_iter()
.map(Into::into),
); );
// If we're resizing, try to render a shader, or a fallback.
let mut pushed_resize = false;
if let Some(resize) = &self.resize_animation {
if ResizeRenderElement::has_shader(renderer) { if ResizeRenderElement::has_shader(renderer) {
let gles_renderer = renderer.as_gles_renderer(); let gles_renderer = renderer.as_gles_renderer();
if let Some(texture_from) = resize.snapshot.texture(gles_renderer, scale, target) { if let Some(texture_from) = resize.snapshot.texture(gles_renderer, scale, target) {
let window_elements = self.window.render_normal( let mut window_elements = Vec::new();
self.window.render_normal(
gles_renderer, gles_renderer,
Point::from((0., 0.)), Point::from((0., 0.)),
scale, scale,
1., 1.,
target, target,
&mut |elem| window_elements.push(elem),
); );
let current = resize let current = resize
@@ -1125,46 +1128,33 @@ impl<W: LayoutElement> Tile<W> {
// This is not a problem for split popups as the code will look for them by // This is not a problem for split popups as the code will look for them by
// original id when it doesn't find them on the offscreen. // original id when it doesn't find them on the offscreen.
self.window.set_offscreen_data(Some(data)); self.window.set_offscreen_data(Some(data));
resize_shader = Some(elem.into()); push(elem.into());
pushed_resize = true;
} }
} }
} }
if resize_shader.is_none() { if !pushed_resize {
let fallback_buffer = SolidColorBuffer::new(area.size, [1., 0., 0., 1.]); let fallback_buffer = SolidColorBuffer::new(area.size, [1., 0., 0., 1.]);
resize_fallback = Some( let elem = SolidColorRenderElement::from_buffer(
SolidColorRenderElement::from_buffer(
&fallback_buffer, &fallback_buffer,
area.loc, area.loc,
win_alpha, win_alpha,
Kind::Unspecified, Kind::Unspecified,
)
.into(),
); );
push(elem.into());
pushed_resize = true;
} }
} }
// If we're not resizing, render the window itself. // If we're not resizing, render the window itself.
let mut window_surface = None;
let mut window_popups = None;
let mut rounded_corner_damage = None;
let has_border_shader = BorderRenderElement::has_shader(renderer); let has_border_shader = BorderRenderElement::has_shader(renderer);
if resize_shader.is_none() && resize_fallback.is_none() { if !pushed_resize {
let window = self
.window
.render(renderer, window_render_loc, scale, win_alpha, target);
let geo = Rectangle::new(window_render_loc, window_size); let geo = Rectangle::new(window_render_loc, window_size);
let radius = radius.fit_to(window_size.w as f32, window_size.h as f32); let radius = radius.fit_to(window_size.w as f32, window_size.h as f32);
let clip_shader = ClippedSurfaceRenderElement::shader(renderer).cloned(); let clip_shader = ClippedSurfaceRenderElement::shader(renderer).cloned();
let clip = |elem| match elem {
if clip_to_geometry && clip_shader.is_some() {
let damage = self.rounded_corner_damage.element();
rounded_corner_damage = Some(damage.with_location(window_render_loc).into());
}
window_surface = Some(window.normal.into_iter().map(move |elem| match elem {
LayoutElementRenderElement::Wayland(elem) => { LayoutElementRenderElement::Wayland(elem) => {
// If we should clip to geometry, render a clipped window. // If we should clip to geometry, render a clipped window.
if clip_to_geometry { if clip_to_geometry {
@@ -1213,21 +1203,24 @@ impl<W: LayoutElement> Tile<W> {
// Otherwise, render the solid color as is. // Otherwise, render the solid color as is.
LayoutElementRenderElement::SolidColor(elem).into() LayoutElementRenderElement::SolidColor(elem).into()
} }
})); };
window_popups = Some(window.popups.into_iter().map(Into::into)); if clip_to_geometry && clip_shader.is_some() {
let damage = self.rounded_corner_damage.element();
push(damage.with_location(window_render_loc).into());
} }
let rv = resize_popups self.window.render_normal(
.into_iter() renderer,
.flatten() window_render_loc,
.chain(resize_shader) scale,
.chain(resize_fallback) win_alpha,
.chain(window_popups.into_iter().flatten()) target,
.chain(rounded_corner_damage) &mut |elem| push(clip(elem)),
.chain(window_surface.into_iter().flatten()); );
}
let elem = (fullscreen_progress > 0.).then(|| { if fullscreen_progress > 0. {
let alpha = fullscreen_progress as f32; let alpha = fullscreen_progress as f32;
// During the un/fullscreen animation, render a border element in order to use the // During the un/fullscreen animation, render a border element in order to use the
@@ -1243,7 +1236,7 @@ impl<W: LayoutElement> Tile<W> {
let size = self.fullscreen_backdrop.size(); let size = self.fullscreen_backdrop.size();
let color = self.fullscreen_backdrop.color(); let color = self.fullscreen_backdrop.color();
BorderRenderElement::new( let elem = BorderRenderElement::new(
size, size,
Rectangle::from_size(size), Rectangle::from_size(size),
GradientInterpolation::default(), GradientInterpolation::default(),
@@ -1256,47 +1249,50 @@ impl<W: LayoutElement> Tile<W> {
scale.x as f32, scale.x as f32,
alpha, alpha,
) )
.with_location(location) .with_location(location);
.into() push(elem.into());
} else { } else {
SolidColorRenderElement::from_buffer( let elem = SolidColorRenderElement::from_buffer(
&self.fullscreen_backdrop, &self.fullscreen_backdrop,
location, location,
alpha, alpha,
Kind::Unspecified, Kind::Unspecified,
) );
.into() push(elem.into());
}
} }
});
let rv = rv.chain(elem);
let elem = self.visual_border_width().map(|width| { if let Some(width) = self.visual_border_width() {
self.border self.border.render(
.render(renderer, location + Point::from((width, width))) renderer,
.map(Into::into) location + Point::from((width, width)),
}); &mut |elem| push(elem.into()),
let rv = rv.chain(elem.into_iter().flatten()); );
}
// Hide the focus ring when maximized/fullscreened. It's not normally visible anyway due to // Hide the focus ring when maximized/fullscreened. It's not normally visible anyway due to
// being outside the monitor or obscured by a solid colored bar, but it is visible under // being outside the monitor or obscured by a solid colored bar, but it is visible under
// semitransparent bars in maximized state (which is a bit weird) and in the overview (also // semitransparent bars in maximized state (which is a bit weird) and in the overview (also
// a bit weird). // a bit weird).
let elem = (focus_ring && expanded_progress < 1.) if focus_ring && expanded_progress < 1. {
.then(|| self.focus_ring.render(renderer, location).map(Into::into)); self.focus_ring
let rv = rv.chain(elem.into_iter().flatten()); .render(renderer, location, &mut |elem| push(elem.into()));
let elem = (expanded_progress < 1.)
.then(|| self.shadow.render(renderer, location).map(Into::into));
rv.chain(elem.into_iter().flatten())
} }
pub fn render<'a, R: NiriRenderer + 'a>( if expanded_progress < 1. {
&'a self, self.shadow
.render(renderer, location, &mut |elem| push(elem.into()));
}
}
pub fn render<R: NiriRenderer>(
&self,
renderer: &mut R, renderer: &mut R,
location: Point<f64, Logical>, location: Point<f64, Logical>,
focus_ring: bool, focus_ring: bool,
target: RenderTarget, target: RenderTarget,
) -> impl Iterator<Item = TileRenderElement<R>> + 'a { push: &mut dyn FnMut(TileRenderElement<R>),
) {
let _span = tracy_client::span!("Tile::render"); let _span = tracy_client::span!("Tile::render");
let scale = Scale::from(self.scale); let scale = Scale::from(self.scale);
@@ -1306,16 +1302,19 @@ impl<W: LayoutElement> Tile<W> {
.as_ref() .as_ref()
.map_or(1., |alpha| alpha.anim.clamped_value()) as f32; .map_or(1., |alpha| alpha.anim.clamped_value()) as f32;
let mut open_anim_elem = None; let mut pushed = false;
let mut alpha_anim_elem = None;
let mut window_elems = None;
self.window().set_offscreen_data(None); self.window().set_offscreen_data(None);
if let Some(open) = &self.open_animation { if let Some(open) = &self.open_animation {
let renderer = renderer.as_gles_renderer(); let renderer = renderer.as_gles_renderer();
let elements = self.render_inner(renderer, Point::from((0., 0.)), focus_ring, target); let mut elements = Vec::new();
let elements = elements.collect::<Vec<TileRenderElement<_>>>(); self.render_inner(
renderer,
Point::from((0., 0.)),
focus_ring,
target,
&mut |elem| elements.push(elem),
);
match open.render( match open.render(
renderer, renderer,
&elements, &elements,
@@ -1326,7 +1325,8 @@ impl<W: LayoutElement> Tile<W> {
) { ) {
Ok((elem, data)) => { Ok((elem, data)) => {
self.window().set_offscreen_data(Some(data)); self.window().set_offscreen_data(Some(data));
open_anim_elem = Some(elem.into()); push(elem.into());
pushed = true;
} }
Err(err) => { Err(err) => {
warn!("error rendering window opening animation: {err:?}"); warn!("error rendering window opening animation: {err:?}");
@@ -1334,15 +1334,22 @@ impl<W: LayoutElement> Tile<W> {
} }
} else if let Some(alpha) = &self.alpha_animation { } else if let Some(alpha) = &self.alpha_animation {
let renderer = renderer.as_gles_renderer(); let renderer = renderer.as_gles_renderer();
let elements = self.render_inner(renderer, Point::from((0., 0.)), focus_ring, target); let mut elements = Vec::new();
let elements = elements.collect::<Vec<TileRenderElement<_>>>(); self.render_inner(
renderer,
Point::from((0., 0.)),
focus_ring,
target,
&mut |elem| elements.push(elem),
);
match alpha.offscreen.render(renderer, scale, &elements) { match alpha.offscreen.render(renderer, scale, &elements) {
Ok((elem, _sync, data)) => { Ok((elem, _sync, data)) => {
let offset = elem.offset(); let offset = elem.offset();
let elem = elem.with_alpha(tile_alpha).with_offset(location + offset); let elem = elem.with_alpha(tile_alpha).with_offset(location + offset);
self.window().set_offscreen_data(Some(data)); self.window().set_offscreen_data(Some(data));
alpha_anim_elem = Some(elem.into()); push(elem.into());
pushed = true;
} }
Err(err) => { Err(err) => {
warn!("error rendering tile to offscreen for alpha animation: {err:?}"); warn!("error rendering tile to offscreen for alpha animation: {err:?}");
@@ -1350,14 +1357,11 @@ impl<W: LayoutElement> Tile<W> {
} }
} }
if open_anim_elem.is_none() && alpha_anim_elem.is_none() { if !pushed {
window_elems = Some(self.render_inner(renderer, location, focus_ring, target)); self.render_inner(renderer, location, focus_ring, target, &mut |elem| {
push(elem)
});
} }
open_anim_elem
.into_iter()
.chain(alpha_anim_elem)
.chain(window_elems.into_iter().flatten())
} }
pub fn store_unmap_snapshot_if_empty(&mut self, renderer: &mut GlesRenderer) { pub fn store_unmap_snapshot_if_empty(&mut self, renderer: &mut GlesRenderer) {
@@ -1371,19 +1375,28 @@ impl<W: LayoutElement> Tile<W> {
fn render_snapshot(&self, renderer: &mut GlesRenderer) -> TileRenderSnapshot { fn render_snapshot(&self, renderer: &mut GlesRenderer) -> TileRenderSnapshot {
let _span = tracy_client::span!("Tile::render_snapshot"); let _span = tracy_client::span!("Tile::render_snapshot");
let contents = self.render(renderer, Point::from((0., 0.)), false, RenderTarget::Output); let mut contents = Vec::new();
self.render(
renderer,
Point::from((0., 0.)),
false,
RenderTarget::Output,
&mut |elem| contents.push(elem),
);
// A bit of a hack to render blocked out as for screencast, but I think it's fine here. // A bit of a hack to render blocked out as for screencast, but I think it's fine here.
let blocked_out_contents = self.render( let mut blocked_out_contents = Vec::new();
self.render(
renderer, renderer,
Point::from((0., 0.)), Point::from((0., 0.)),
false, false,
RenderTarget::Screencast, RenderTarget::Screencast,
&mut |elem| blocked_out_contents.push(elem),
); );
RenderSnapshot { RenderSnapshot {
contents: contents.collect(), contents,
blocked_out_contents: blocked_out_contents.collect(), blocked_out_contents,
block_out_from: self.window.rules().block_out_from, block_out_from: self.window.rules().block_out_from,
size: self.animated_tile_size(), size: self.animated_tile_size(),
texture: Default::default(), texture: Default::default(),
+29 -20
View File
@@ -1624,39 +1624,48 @@ impl<W: LayoutElement> Workspace<W> {
} }
} }
pub fn render_elements<R: NiriRenderer>( pub fn render_scrolling<R: NiriRenderer>(
&self, &self,
renderer: &mut R, renderer: &mut R,
target: RenderTarget, target: RenderTarget,
focus_ring: bool, focus_ring: bool,
) -> ( push: &mut dyn FnMut(WorkspaceRenderElement<R>),
impl Iterator<Item = WorkspaceRenderElement<R>>,
impl Iterator<Item = WorkspaceRenderElement<R>>,
) { ) {
let scrolling_focus_ring = focus_ring && !self.floating_is_active(); let scrolling_focus_ring = focus_ring && !self.floating_is_active();
let scrolling = self self.scrolling
.scrolling .render(renderer, target, scrolling_focus_ring, &mut |elem| {
.render_elements(renderer, target, scrolling_focus_ring); push(elem.into())
let scrolling = scrolling.into_iter().map(WorkspaceRenderElement::from);
let floating_focus_ring = focus_ring && self.floating_is_active();
let floating = self.is_floating_visible().then(|| {
let view_rect = Rectangle::from_size(self.view_size);
let floating =
self.floating
.render_elements(renderer, view_rect, target, floating_focus_ring);
floating.into_iter().map(WorkspaceRenderElement::from)
}); });
let floating = floating.into_iter().flatten(); }
(floating, scrolling) pub fn render_floating<R: NiriRenderer>(
&self,
renderer: &mut R,
target: RenderTarget,
focus_ring: bool,
push: &mut dyn FnMut(WorkspaceRenderElement<R>),
) {
if !self.is_floating_visible() {
return;
}
let view_rect = Rectangle::from_size(self.view_size);
let floating_focus_ring = focus_ring && self.floating_is_active();
self.floating.render(
renderer,
view_rect,
target,
floating_focus_ring,
&mut |elem| push(elem.into()),
);
} }
pub fn render_shadow<R: NiriRenderer>( pub fn render_shadow<R: NiriRenderer>(
&self, &self,
renderer: &mut R, renderer: &mut R,
) -> impl Iterator<Item = ShadowRenderElement> + '_ { push: &mut dyn FnMut(ShadowRenderElement),
self.shadow.render(renderer, Point::from((0., 0.))) ) {
self.shadow.render(renderer, Point::from((0., 0.)), push);
} }
pub fn render_background(&self) -> SolidColorRenderElement { pub fn render_background(&self) -> SolidColorRenderElement {
+172 -165
View File
@@ -22,9 +22,7 @@ use smithay::backend::allocator::Fourcc;
use smithay::backend::input::Keycode; use smithay::backend::input::Keycode;
use smithay::backend::renderer::damage::OutputDamageTracker; use smithay::backend::renderer::damage::OutputDamageTracker;
use smithay::backend::renderer::element::memory::MemoryRenderBufferRenderElement; use smithay::backend::renderer::element::memory::MemoryRenderBufferRenderElement;
use smithay::backend::renderer::element::surface::{ use smithay::backend::renderer::element::surface::WaylandSurfaceRenderElement;
render_elements_from_surface_tree, WaylandSurfaceRenderElement,
};
use smithay::backend::renderer::element::utils::{ use smithay::backend::renderer::element::utils::{
select_dmabuf_feedback, CropRenderElement, Relocate, RelocateRenderElement, select_dmabuf_feedback, CropRenderElement, Relocate, RelocateRenderElement,
RescaleRenderElement, RescaleRenderElement,
@@ -157,10 +155,11 @@ use crate::render_helpers::debug::draw_opaque_regions;
use crate::render_helpers::primary_gpu_texture::PrimaryGpuTextureRenderElement; use crate::render_helpers::primary_gpu_texture::PrimaryGpuTextureRenderElement;
use crate::render_helpers::renderer::NiriRenderer; use crate::render_helpers::renderer::NiriRenderer;
use crate::render_helpers::solid_color::{SolidColorBuffer, SolidColorRenderElement}; use crate::render_helpers::solid_color::{SolidColorBuffer, SolidColorRenderElement};
use crate::render_helpers::surface::push_elements_from_surface_tree;
use crate::render_helpers::texture::TextureBuffer; use crate::render_helpers::texture::TextureBuffer;
use crate::render_helpers::{ use crate::render_helpers::{
encompassing_geo, render_to_dmabuf, render_to_encompassing_texture, render_to_shm, encompassing_geo, render_to_dmabuf, render_to_encompassing_texture, render_to_shm,
render_to_texture, render_to_vec, shaders, RenderTarget, SplitElements, render_to_texture, render_to_vec, shaders, RenderTarget,
}; };
use crate::ui::config_error_notification::ConfigErrorNotification; use crate::ui::config_error_notification::ConfigErrorNotification;
use crate::ui::exit_confirm_dialog::{ExitConfirmDialog, ExitConfirmDialogRenderElement}; use crate::ui::exit_confirm_dialog::{ExitConfirmDialog, ExitConfirmDialogRenderElement};
@@ -2075,9 +2074,8 @@ impl State {
self.backend.with_primary_renderer(|renderer| { self.backend.with_primary_renderer(|renderer| {
// FIXME: pointer. // FIXME: pointer.
let elements = mapped let mut elements = Vec::new();
.render_for_screen_cast(renderer, scale) mapped.render_for_screen_cast(renderer, scale, &mut |elem| elements.push(elem));
.collect::<Vec<_>>();
if cast.dequeue_buffer_and_render(renderer, &elements, bbox.size, scale) { if cast.dequeue_buffer_and_render(renderer, &elements, bbox.size, scale) {
cast.last_frame_time = get_monotonic_time(); cast.last_frame_time = get_monotonic_time();
@@ -3865,13 +3863,14 @@ impl Niri {
} }
} }
pub fn pointer_element<R: NiriRenderer>( pub fn render_pointer<R: NiriRenderer>(
&self, &self,
renderer: &mut R, renderer: &mut R,
output: &Output, output: &Output,
) -> Vec<OutputRenderElements<R>> { push: &mut dyn FnMut(PointerRenderElements<R>),
) {
if !self.pointer_visibility.is_visible() { if !self.pointer_visibility.is_visible() {
return vec![]; return;
} }
let _span = tracy_client::span!("Niri::pointer_element"); let _span = tracy_client::span!("Niri::pointer_element");
@@ -3890,20 +3889,21 @@ impl Niri {
let output_scale = Scale::from(output.current_scale().fractional_scale()); let output_scale = Scale::from(output.current_scale().fractional_scale());
let mut pointer_elements = match render_cursor { match render_cursor {
RenderCursor::Hidden => vec![], RenderCursor::Hidden => (),
RenderCursor::Surface { surface, hotspot } => { RenderCursor::Surface { surface, hotspot } => {
let pointer_pos = let pointer_pos =
(pointer_pos - hotspot.to_f64()).to_physical_precise_round(output_scale); (pointer_pos - hotspot.to_f64()).to_physical_precise_round(output_scale);
render_elements_from_surface_tree( push_elements_from_surface_tree(
renderer, renderer,
&surface, &surface,
pointer_pos, pointer_pos,
output_scale, output_scale,
1., 1.,
Kind::Cursor, Kind::Cursor,
) &mut |elem| push(elem.into()),
);
} }
RenderCursor::Named { RenderCursor::Named {
icon, icon,
@@ -3916,8 +3916,7 @@ impl Niri {
(pointer_pos - hotspot.to_f64()).to_physical_precise_round(output_scale); (pointer_pos - hotspot.to_f64()).to_physical_precise_round(output_scale);
let texture = self.cursor_texture_cache.get(icon, scale, &cursor, idx); let texture = self.cursor_texture_cache.get(icon, scale, &cursor, idx);
let mut pointer_elements = vec![]; match MemoryRenderBufferRenderElement::from_buffer(
let pointer_element = match MemoryRenderBufferRenderElement::from_buffer(
renderer, renderer,
pointer_pos, pointer_pos,
&texture, &texture,
@@ -3926,34 +3925,27 @@ impl Niri {
None, None,
Kind::Cursor, Kind::Cursor,
) { ) {
Ok(element) => Some(element), Ok(element) => push(element.into()),
Err(err) => { Err(err) => {
warn!("error importing a cursor texture: {err:?}"); warn!("error importing a cursor texture: {err:?}");
None
} }
};
if let Some(element) = pointer_element {
pointer_elements.push(OutputRenderElements::NamedPointer(element));
} }
pointer_elements
} }
}; }
if let Some(dnd_icon) = self.dnd_icon.as_ref() { if let Some(dnd_icon) = self.dnd_icon.as_ref() {
let pointer_pos = let pointer_pos =
(pointer_pos + dnd_icon.offset.to_f64()).to_physical_precise_round(output_scale); (pointer_pos + dnd_icon.offset.to_f64()).to_physical_precise_round(output_scale);
pointer_elements.extend(render_elements_from_surface_tree( push_elements_from_surface_tree(
renderer, renderer,
&dnd_icon.surface, &dnd_icon.surface,
pointer_pos, pointer_pos,
output_scale, output_scale,
1., 1.,
Kind::ScanoutCandidate, Kind::ScanoutCandidate,
)); &mut |elem| push(elem.into()),
);
} }
pointer_elements
} }
pub fn refresh_pointer_outputs(&mut self) { pub fn refresh_pointer_outputs(&mut self) {
@@ -4317,7 +4309,7 @@ impl Niri {
// The pointer goes on the top. // The pointer goes on the top.
let mut elements = vec![]; let mut elements = vec![];
if include_pointer { if include_pointer {
elements = self.pointer_element(renderer, output); self.render_pointer(renderer, output, &mut |elem| elements.push(elem.into()));
} }
// Next, the screen transition texture. // Next, the screen transition texture.
@@ -4329,12 +4321,8 @@ impl Niri {
} }
// Next, the exit confirm dialog. // Next, the exit confirm dialog.
elements.extend(
self.exit_confirm_dialog self.exit_confirm_dialog
.render(renderer, output) .render(renderer, output, &mut |elem| elements.push(elem.into()));
.into_iter()
.map(OutputRenderElements::from),
);
// Next, the config error notification too. // Next, the config error notification too.
if let Some(element) = self.config_error_notification.render(renderer, output) { if let Some(element) = self.config_error_notification.render(renderer, output) {
@@ -4345,14 +4333,15 @@ impl Niri {
if self.is_locked() { if self.is_locked() {
let state = self.output_state.get(output).unwrap(); let state = self.output_state.get(output).unwrap();
if let Some(surface) = state.lock_surface.as_ref() { if let Some(surface) = state.lock_surface.as_ref() {
elements.extend(render_elements_from_surface_tree( push_elements_from_surface_tree(
renderer, renderer,
surface.wl_surface(), surface.wl_surface(),
(0, 0), Point::new(0, 0),
output_scale, output_scale,
1., 1.,
Kind::ScanoutCandidate, Kind::ScanoutCandidate,
)); &mut |elem| elements.push(elem.into()),
);
} }
// Draw the solid color background. // Draw the solid color background.
@@ -4384,12 +4373,8 @@ impl Niri {
// If the screenshot UI is open, draw it. // If the screenshot UI is open, draw it.
if self.screenshot_ui.is_open() { if self.screenshot_ui.is_open() {
elements.extend(
self.screenshot_ui self.screenshot_ui
.render_output(output, target) .render_output(output, target, &mut |elem| elements.push(elem.into()));
.into_iter()
.map(OutputRenderElements::from),
);
// Add the backdrop for outputs that were connected while the screenshot UI was open. // Add the backdrop for outputs that were connected while the screenshot UI was open.
elements.push(backdrop); elements.push(backdrop);
@@ -4406,13 +4391,10 @@ impl Niri {
} }
// Then, the Alt-Tab switcher. // Then, the Alt-Tab switcher.
let mru_elements = self self.window_mru_ui
.window_mru_ui .render_output(self, output, renderer, target, &mut |elem| {
.render_output(self, output, renderer, target) elements.push(elem.into())
.into_iter() });
.flatten()
.map(OutputRenderElements::from);
elements.extend(mru_elements);
// Don't draw the focus ring on the workspaces while interactively moving above those // Don't draw the focus ring on the workspaces while interactively moving above those
// workspaces, since the interactively-moved window already has a focus ring. // workspaces, since the interactively-moved window already has a focus ring.
@@ -4421,129 +4403,119 @@ impl Niri {
// Get monitor elements. // Get monitor elements.
let mon = self.layout.monitor_for_output(output).unwrap(); let mon = self.layout.monitor_for_output(output).unwrap();
let zoom = mon.overview_zoom(); let zoom = mon.overview_zoom();
let monitor_elements = Vec::from_iter(
mon.render_elements(renderer, target, focus_ring)
.map(|(geo, bg, iter)| (geo, bg, Vec::from_iter(iter))),
);
let workspace_shadow_elements = Vec::from_iter(mon.render_workspace_shadows(renderer));
let insert_hint_elements = mon.render_insert_hint_between_workspaces(renderer);
let int_move_elements: Vec<_> = self
.layout
.render_interactive_move_for_output(renderer, output, target)
.collect();
// Get layer-shell elements. // Get layer-shell elements.
let layer_map = layer_map_for_output(output); let layer_map = layer_map_for_output(output);
let mut extend_from_layer =
|elements: &mut SplitElements<LayerSurfaceRenderElement<R>>, layer, for_backdrop| { // We use macros instead of closures to avoid borrowing issues (renderer and elements go
self.render_layer(renderer, target, &layer_map, layer, elements, for_backdrop); // into different functions).
}; macro_rules! push_popups_from_layer {
($layer:expr, $backdrop:expr, $push:expr) => {{
self.render_layer_popups(renderer, target, &layer_map, $layer, $backdrop, $push);
}};
($layer:expr, true) => {{
push_popups_from_layer!($layer, true, &mut |elem| elements.push(elem.into()));
}};
($layer:expr, $push:expr) => {{
push_popups_from_layer!($layer, false, $push);
}};
($layer:expr) => {{
push_popups_from_layer!($layer, false, &mut |elem| elements.push(elem.into()));
}};
}
macro_rules! push_normal_from_layer {
($layer:expr, $backdrop:expr, $push:expr) => {{
self.render_layer_normal(renderer, target, &layer_map, $layer, $backdrop, $push);
}};
($layer:expr, true) => {{
push_normal_from_layer!($layer, true, &mut |elem| elements.push(elem.into()));
}};
($layer:expr, $push:expr) => {{
push_normal_from_layer!($layer, false, $push);
}};
($layer:expr) => {{
push_normal_from_layer!($layer, false, &mut |elem| elements.push(elem.into()));
}};
}
// The overlay layer elements go next. // The overlay layer elements go next.
let mut layer_elems = SplitElements::default(); push_popups_from_layer!(Layer::Overlay);
extend_from_layer(&mut layer_elems, Layer::Overlay, false); push_normal_from_layer!(Layer::Overlay);
elements.extend(layer_elems.into_iter().map(OutputRenderElements::from));
// Collect the top layer elements.
let mut layer_elems = SplitElements::default();
extend_from_layer(&mut layer_elems, Layer::Top, false);
let top_layer = layer_elems;
// When rendering above the top layer, we put the regular monitor elements first. // When rendering above the top layer, we put the regular monitor elements first.
// Otherwise, we will render all layer-shell pop-ups and the top layer on top. // Otherwise, we will render all layer-shell pop-ups and the top layer on top.
if mon.render_above_top_layer() { if mon.render_above_top_layer() {
// Collect all other layer-shell elements. self.layout
let mut layer_elems = SplitElements::default(); .render_interactive_move_for_output(renderer, output, target, &mut |elem| {
extend_from_layer(&mut layer_elems, Layer::Bottom, false); elements.push(elem.into())
extend_from_layer(&mut layer_elems, Layer::Background, false); });
elements.extend( mon.render_insert_hint_between_workspaces(renderer, &mut |elem| {
int_move_elements elements.push(elem.into())
.into_iter() });
.map(OutputRenderElements::from),
);
elements.extend(
insert_hint_elements
.into_iter()
.map(OutputRenderElements::from),
);
let mut ws_background = None; mon.render_workspaces(renderer, target, focus_ring, &mut |elem| {
elements.extend( elements.push(elem.into())
monitor_elements });
.into_iter()
.flat_map(|(_ws_geo, ws_bg, iter)| {
ws_background = Some(ws_bg);
iter
})
.map(OutputRenderElements::from),
);
elements.extend(top_layer.into_iter().map(OutputRenderElements::from)); push_popups_from_layer!(Layer::Top);
elements.extend(layer_elems.into_iter().map(OutputRenderElements::from)); push_normal_from_layer!(Layer::Top);
if let Some(ws_background) = ws_background { push_popups_from_layer!(Layer::Bottom);
elements.push(OutputRenderElements::from(ws_background)); push_popups_from_layer!(Layer::Background);
push_normal_from_layer!(Layer::Bottom);
push_normal_from_layer!(Layer::Background);
// We don't expect more than one workspace when render_above_top_layer().
if let Some((ws, _geo)) = mon.workspaces_with_render_geo().next() {
elements.push(ws.render_background().into());
} }
elements.extend(
workspace_shadow_elements
.into_iter()
.map(OutputRenderElements::from),
);
} else { } else {
elements.extend(top_layer.into_iter().map(OutputRenderElements::from)); push_popups_from_layer!(Layer::Top);
push_normal_from_layer!(Layer::Top);
elements.extend( self.layout
int_move_elements .render_interactive_move_for_output(renderer, output, target, &mut |elem| {
.into_iter() elements.push(elem.into())
.map(OutputRenderElements::from), });
);
elements.extend( mon.render_insert_hint_between_workspaces(renderer, &mut |elem| {
insert_hint_elements elements.push(elem.into())
.into_iter() });
.map(OutputRenderElements::from),
);
for (ws_geo, ws_background, ws_elements) in monitor_elements { // Macro instead of closure to avoid borrowing elements.
// Collect all other layer-shell elements. macro_rules! process {
let mut layer_elems = SplitElements::default(); ($geo:expr) => {{
extend_from_layer(&mut layer_elems, Layer::Bottom, false); &mut |elem| {
extend_from_layer(&mut layer_elems, Layer::Background, false); if let Some(elem) = scale_relocate_crop(elem, output_scale, zoom, $geo) {
elements.push(elem.into());
elements.extend( }
layer_elems }
.popups }};
.into_iter()
.filter_map(|elem| scale_relocate_crop(elem, output_scale, zoom, ws_geo))
.map(OutputRenderElements::from),
);
elements.extend(ws_elements.into_iter().map(OutputRenderElements::from));
elements.extend(
layer_elems
.normal
.into_iter()
.filter_map(|elem| scale_relocate_crop(elem, output_scale, zoom, ws_geo))
.map(OutputRenderElements::from),
);
elements.push(OutputRenderElements::from(ws_background));
} }
elements.extend( for (_ws, geo) in mon.workspaces_with_render_geo() {
workspace_shadow_elements push_popups_from_layer!(Layer::Bottom, process!(geo));
.into_iter() push_popups_from_layer!(Layer::Background, process!(geo));
.map(OutputRenderElements::from),
);
} }
mon.render_workspaces(renderer, target, focus_ring, &mut |elem| {
elements.push(elem.into())
});
for (ws, geo) in mon.workspaces_with_render_geo() {
push_normal_from_layer!(Layer::Bottom, process!(geo));
push_normal_from_layer!(Layer::Background, process!(geo));
process!(geo)(ws.render_background());
}
}
mon.render_workspace_shadows(renderer, &mut |elem| elements.push(elem.into()));
// Then the backdrop. // Then the backdrop.
let mut layer_elems = SplitElements::default(); push_popups_from_layer!(Layer::Background, true);
extend_from_layer(&mut layer_elems, Layer::Background, true); push_normal_from_layer!(Layer::Background, true);
elements.extend(layer_elems.into_iter().map(OutputRenderElements::from));
elements.push(backdrop); elements.push(backdrop);
@@ -4554,18 +4526,14 @@ impl Niri {
elements elements
} }
#[allow(clippy::too_many_arguments)] fn layers_in_render_order<'a>(
fn render_layer<R: NiriRenderer>( &'a self,
&self, layer_map: &'a LayerMap,
renderer: &mut R,
target: RenderTarget,
layer_map: &LayerMap,
layer: Layer, layer: Layer,
elements: &mut SplitElements<LayerSurfaceRenderElement<R>>,
for_backdrop: bool, for_backdrop: bool,
) { ) -> impl Iterator<Item = (&'a MappedLayer, Rectangle<i32, Logical>)> {
// LayerMap returns layers in reverse stacking order. // LayerMap returns layers in reverse stacking order.
let iter = layer_map.layers_on(layer).rev().filter_map(|surface| { layer_map.layers_on(layer).rev().filter_map(move |surface| {
let mapped = self.mapped_layer_surfaces.get(surface)?; let mapped = self.mapped_layer_surfaces.get(surface)?;
if for_backdrop != mapped.place_within_backdrop() { if for_backdrop != mapped.place_within_backdrop() {
@@ -4574,9 +4542,34 @@ impl Niri {
let geo = layer_map.layer_geometry(surface)?; let geo = layer_map.layer_geometry(surface)?;
Some((mapped, geo)) Some((mapped, geo))
}); })
for (mapped, geo) in iter { }
elements.extend(mapped.render(renderer, geo.loc.to_f64(), target));
fn render_layer_normal<R: NiriRenderer>(
&self,
renderer: &mut R,
target: RenderTarget,
layer_map: &LayerMap,
layer: Layer,
for_backdrop: bool,
push: &mut dyn FnMut(LayerSurfaceRenderElement<R>),
) {
for (mapped, geo) in self.layers_in_render_order(layer_map, layer, for_backdrop) {
mapped.render_normal(renderer, geo.loc.to_f64(), target, push);
}
}
fn render_layer_popups<R: NiriRenderer>(
&self,
renderer: &mut R,
target: RenderTarget,
layer_map: &LayerMap,
layer: Layer,
for_backdrop: bool,
push: &mut dyn FnMut(LayerSurfaceRenderElement<R>),
) {
for (mapped, geo) in self.layers_in_render_order(layer_map, layer, for_backdrop) {
mapped.render_popups(renderer, geo.loc.to_f64(), target, push);
} }
} }
@@ -5291,7 +5284,8 @@ impl Niri {
} }
// FIXME: pointer. // FIXME: pointer.
let elements: Vec<_> = mapped.render_for_screen_cast(renderer, scale).collect(); let mut elements = Vec::new();
mapped.render_for_screen_cast(renderer, scale, &mut |elem| elements.push(elem));
if cast.dequeue_buffer_and_render(renderer, &elements, bbox.size, scale) { if cast.dequeue_buffer_and_render(renderer, &elements, bbox.size, scale) {
cast.last_frame_time = target_presentation_time; cast.last_frame_time = target_presentation_time;
@@ -5594,7 +5588,8 @@ impl Niri {
} }
let res_output = res.ok(); let res_output = res.ok();
let pointer = self.pointer_element(renderer, &output); let mut pointer = Vec::new();
self.render_pointer(renderer, &output, &mut |elem| pointer.push(elem));
let res_pointer = if pointer.is_empty() { let res_pointer = if pointer.is_empty() {
None None
} else { } else {
@@ -5685,12 +5680,14 @@ impl Niri {
mapped.rules().opacity.unwrap_or(1.).clamp(0., 1.) mapped.rules().opacity.unwrap_or(1.).clamp(0., 1.)
}; };
// FIXME: pointer. // FIXME: pointer.
let elements = mapped.render( let mut elements = Vec::new();
mapped.render(
renderer, renderer,
mapped.window.geometry().loc.to_f64(), mapped.window.geometry().loc.to_f64(),
scale, scale,
alpha, alpha,
RenderTarget::ScreenCapture, RenderTarget::ScreenCapture,
&mut |elem| elements.push(elem),
); );
let geo = encompassing_geo(scale, elements.iter()); let geo = encompassing_geo(scale, elements.iter());
let elements = elements.iter().rev().map(|elem| { let elements = elements.iter().rev().map(|elem| {
@@ -6556,6 +6553,13 @@ fn scale_relocate_crop<E: Element>(
CropRenderElement::from_element(elem, output_scale, ws_geo) CropRenderElement::from_element(elem, output_scale, ws_geo)
} }
niri_render_elements! {
PointerRenderElements<R> => {
Wayland = WaylandSurfaceRenderElement<R>,
NamedPointer = MemoryRenderBufferRenderElement<R>,
}
}
niri_render_elements! { niri_render_elements! {
OutputRenderElements<R> => { OutputRenderElements<R> => {
Monitor = MonitorRenderElement<R>, Monitor = MonitorRenderElement<R>,
@@ -6564,8 +6568,11 @@ niri_render_elements! {
RelocatedLayerSurface = CropRenderElement<RelocateRenderElement<RescaleRenderElement< RelocatedLayerSurface = CropRenderElement<RelocateRenderElement<RescaleRenderElement<
LayerSurfaceRenderElement<R> LayerSurfaceRenderElement<R>
>>>, >>>,
RelocatedColor = CropRenderElement<RelocateRenderElement<RescaleRenderElement<
SolidColorRenderElement
>>>,
Pointer = PointerRenderElements<R>,
Wayland = WaylandSurfaceRenderElement<R>, Wayland = WaylandSurfaceRenderElement<R>,
NamedPointer = MemoryRenderBufferRenderElement<R>,
SolidColor = SolidColorRenderElement, SolidColor = SolidColorRenderElement,
ScreenshotUi = ScreenshotUiRenderElement, ScreenshotUi = ScreenshotUiRenderElement,
WindowMruUi = WindowMruUiRenderElement<R>, WindowMruUi = WindowMruUiRenderElement<R>,
-42
View File
@@ -58,13 +58,6 @@ pub struct BakedBuffer<B> {
pub dst: Option<Size<i32, Logical>>, pub dst: Option<Size<i32, Logical>>,
} }
/// Render elements split into normal and popup.
#[derive(Debug)]
pub struct SplitElements<E> {
pub normal: Vec<E>,
pub popups: Vec<E>,
}
pub trait ToRenderElement { pub trait ToRenderElement {
type RenderElement; type RenderElement;
@@ -87,41 +80,6 @@ impl RenderTarget {
} }
} }
impl<E> Default for SplitElements<E> {
fn default() -> Self {
Self {
normal: Vec::new(),
popups: Vec::new(),
}
}
}
impl<E> IntoIterator for SplitElements<E> {
type Item = E;
type IntoIter = std::iter::Chain<std::vec::IntoIter<E>, std::vec::IntoIter<E>>;
fn into_iter(self) -> Self::IntoIter {
self.popups.into_iter().chain(self.normal)
}
}
impl<E> SplitElements<E> {
pub fn iter(&self) -> std::iter::Chain<std::slice::Iter<'_, E>, std::slice::Iter<'_, E>> {
self.popups.iter().chain(&self.normal)
}
pub fn into_vec(self) -> Vec<E> {
let Self { normal, mut popups } = self;
popups.extend(normal);
popups
}
pub fn extend(&mut self, other: SplitElements<E>) {
self.popups.extend(other.popups);
self.normal.extend(other.normal);
}
}
impl ToRenderElement for BakedBuffer<TextureBuffer<GlesTexture>> { impl ToRenderElement for BakedBuffer<TextureBuffer<GlesTexture>> {
type RenderElement = PrimaryGpuTextureRenderElement; type RenderElement = PrimaryGpuTextureRenderElement;
+68 -2
View File
@@ -1,8 +1,10 @@
use smithay::backend::renderer::element::surface::WaylandSurfaceRenderElement;
use smithay::backend::renderer::element::Kind;
use smithay::backend::renderer::gles::{GlesRenderer, GlesTexture}; use smithay::backend::renderer::gles::{GlesRenderer, GlesTexture};
use smithay::backend::renderer::utils::{import_surface, RendererSurfaceStateUserData}; use smithay::backend::renderer::utils::{import_surface, RendererSurfaceStateUserData};
use smithay::backend::renderer::Renderer as _; use smithay::backend::renderer::{ImportAll, Renderer};
use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface; use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface;
use smithay::utils::{Logical, Point}; use smithay::utils::{Logical, Physical, Point, Scale};
use smithay::wayland::compositor::{with_surface_tree_downward, TraversalAction}; use smithay::wayland::compositor::{with_surface_tree_downward, TraversalAction};
use super::texture::TextureBuffer; use super::texture::TextureBuffer;
@@ -78,3 +80,67 @@ pub fn render_snapshot_from_surface_tree(
|_, _, _| true, |_, _, _| true,
); );
} }
pub fn push_elements_from_surface_tree<R>(
renderer: &mut R,
surface: &WlSurface,
// Fractional scale expects surface buffers to be aligned to physical pixels.
location: Point<i32, Physical>,
scale: Scale<f64>,
alpha: f32,
kind: Kind,
push: &mut dyn FnMut(WaylandSurfaceRenderElement<R>),
) where
R: Renderer + ImportAll,
R::TextureId: Clone + 'static,
{
let _span = tracy_client::span!("push_elements_from_surface_tree");
let location = location.to_f64();
with_surface_tree_downward(
surface,
location,
|_, states, location| {
let mut location = *location;
let data = states.data_map.get::<RendererSurfaceStateUserData>();
if let Some(data) = data {
if let Some(view) = data.lock().unwrap().view() {
location += view.offset.to_f64().to_physical(scale);
TraversalAction::DoChildren(location)
} else {
TraversalAction::SkipChildren
}
} else {
TraversalAction::SkipChildren
}
},
|surface, states, location| {
let mut location = *location;
let data = states.data_map.get::<RendererSurfaceStateUserData>();
if let Some(data) = data {
let has_view = if let Some(view) = data.lock().unwrap().view() {
location += view.offset.to_f64().to_physical(scale);
true
} else {
false
};
if has_view {
match WaylandSurfaceRenderElement::from_surface(
renderer, surface, states, location, alpha, kind,
) {
Ok(Some(surface)) => push(surface),
Ok(None) => {} // surface is not mapped
Err(err) => {
warn!("failed to import surface: {}", err);
}
};
}
}
},
|_, _, _| true,
);
}
+9 -11
View File
@@ -3,7 +3,6 @@ use std::collections::HashMap;
use std::rc::Rc; use std::rc::Rc;
use std::sync::Mutex; use std::sync::Mutex;
use arrayvec::ArrayVec;
use niri_config::Config; use niri_config::Config;
use ordered_float::NotNan; use ordered_float::NotNan;
use pangocairo::cairo::{self, ImageSurface}; use pangocairo::cairo::{self, ImageSurface};
@@ -151,14 +150,15 @@ impl ExitConfirmDialog {
&self, &self,
renderer: &mut R, renderer: &mut R,
output: &Output, output: &Output,
) -> ArrayVec<ExitConfirmDialogRenderElement, 2> { push: &mut dyn FnMut(ExitConfirmDialogRenderElement),
let mut rv = ArrayVec::new(); ) {
let (value, clamped_value) = match &self.state { let (value, clamped_value) = match &self.state {
State::Hidden => return rv, State::Hidden => return,
State::Showing(anim) | State::Hiding(anim) => (anim.value(), anim.clamped_value()), State::Showing(anim) | State::Hiding(anim) => (anim.value(), anim.clamped_value()),
State::Visible => (1., 1.), State::Visible => (1., 1.),
}; };
let _span = tracy_client::span!("ExitConfirmDialog::render");
// Can be out of range when starting from past 0. or 1. from a spring bounce. // Can be out of range when starting from past 0. or 1. from a spring bounce.
let clamped_value = clamped_value.clamp(0., 1.); let clamped_value = clamped_value.clamp(0., 1.);
@@ -168,7 +168,7 @@ impl ExitConfirmDialog {
let mut buffers = self.buffers.borrow_mut(); let mut buffers = self.buffers.borrow_mut();
let Some(fallback) = buffers[&NotNan::new(1.).unwrap()].clone() else { let Some(fallback) = buffers[&NotNan::new(1.).unwrap()].clone() else {
error!("exit confirm dialog opened without fallback buffer"); error!("exit confirm dialog opened without fallback buffer");
return rv; return;
}; };
let buffer = buffers let buffer = buffers
@@ -179,7 +179,7 @@ impl ExitConfirmDialog {
let size = buffer.logical_size(); let size = buffer.logical_size();
let Ok(buffer) = TextureBuffer::from_memory_buffer(renderer.as_gles_renderer(), buffer) let Ok(buffer) = TextureBuffer::from_memory_buffer(renderer.as_gles_renderer(), buffer)
else { else {
return rv; return;
}; };
let location = (output_size.to_point() - size.to_point()).downscale(2.); let location = (output_size.to_point() - size.to_point()).downscale(2.);
@@ -201,7 +201,7 @@ impl ExitConfirmDialog {
(location + size.downscale(2.)).to_physical_precise_round(scale), (location + size.downscale(2.)).to_physical_precise_round(scale),
value.max(0.) * 0.2 + 0.8, value.max(0.) * 0.2 + 0.8,
); );
rv.push(ExitConfirmDialogRenderElement::Texture(elem)); push(ExitConfirmDialogRenderElement::Texture(elem));
// Backdrop. // Backdrop.
let data = output.user_data().get_or_insert(|| { let data = output.user_data().get_or_insert(|| {
@@ -218,9 +218,7 @@ impl ExitConfirmDialog {
clamped_value as f32, clamped_value as f32,
Kind::Unspecified, Kind::Unspecified,
); );
rv.push(ExitConfirmDialogRenderElement::SolidColor(elem)); push(ExitConfirmDialogRenderElement::SolidColor(elem));
rv
} }
} }
+68 -77
View File
@@ -346,7 +346,8 @@ impl Thumbnail {
is_active: bool, is_active: bool,
bob_y: f64, bob_y: f64,
target: RenderTarget, target: RenderTarget,
) -> impl Iterator<Item = WindowMruUiRenderElement<R>> { push: &mut dyn FnMut(WindowMruUiRenderElement<R>),
) {
let _span = tracy_client::span!("Thumbnail::render"); let _span = tracy_client::span!("Thumbnail::render");
let round = move |logical: f64| round_logical_in_physical(scale, logical); let round = move |logical: f64| round_logical_in_physical(scale, logical);
@@ -368,11 +369,6 @@ impl Thumbnail {
}; };
let bob_offset = Point::new(0., bob_y); let bob_offset = Point::new(0., bob_y);
// FIXME: this could use mipmaps, for that it should be rendered through an offscreen.
let elems = mapped
.render_normal(renderer, Point::new(0., 0.), s, preview_alpha, target)
.into_iter();
// Clip thumbnails to their geometry. // Clip thumbnails to their geometry.
let radius = if mapped.sizing_mode().is_normal() { let radius = if mapped.sizing_mode().is_normal() {
mapped.rules().geometry_corner_radius mapped.rules().geometry_corner_radius
@@ -385,7 +381,7 @@ impl Thumbnail {
let clip_shader = ClippedSurfaceRenderElement::shader(renderer).cloned(); let clip_shader = ClippedSurfaceRenderElement::shader(renderer).cloned();
let geo = Rectangle::from_size(self.size.to_f64()); let geo = Rectangle::from_size(self.size.to_f64());
// FIXME: deduplicate code with Tile::render_inner() // FIXME: deduplicate code with Tile::render_inner()
let elems = elems.map(move |elem| match elem { let clip = move |elem| match elem {
LayoutElementRenderElement::Wayland(elem) => { LayoutElementRenderElement::Wayland(elem) => {
if let Some(shader) = clip_shader.clone() { if let Some(shader) = clip_shader.clone() {
if ClippedSurfaceRenderElement::will_clip(&elem, s, geo, radius) { if ClippedSurfaceRenderElement::will_clip(&elem, s, geo, radius) {
@@ -426,9 +422,9 @@ impl Thumbnail {
// Otherwise, render the solid color as is. // Otherwise, render the solid color as is.
LayoutElementRenderElement::SolidColor(elem).into() LayoutElementRenderElement::SolidColor(elem).into()
} }
}); };
let elems = elems.map(move |elem| { let downscale = move |elem| {
let thumb_scale = Scale { let thumb_scale = Scale {
x: preview_geo.size.w / geo.size.w, x: preview_geo.size.w / geo.size.w,
y: preview_geo.size.h / geo.size.h, y: preview_geo.size.h / geo.size.h,
@@ -445,7 +441,21 @@ impl Thumbnail {
Relocate::Relative, Relocate::Relative,
); );
WindowMruUiRenderElement::Thumbnail(elem) WindowMruUiRenderElement::Thumbnail(elem)
}); };
// FIXME: this could use mipmaps, for that it should be rendered through an offscreen.
mapped.render_normal(
renderer,
Point::new(0., 0.),
s,
preview_alpha,
target,
&mut |elem| {
let elem = clip(elem);
let elem = downscale(elem);
push(elem)
},
);
let mut title_size = None; let mut title_size = None;
let title_texture = self.title_texture(renderer.as_gles_renderer(), mapped, scale); let title_texture = self.title_texture(renderer.as_gles_renderer(), mapped, scale);
@@ -462,7 +472,7 @@ impl Thumbnail {
let should_block_out = target.should_block_out(mapped.rules().block_out_from); let should_block_out = target.should_block_out(mapped.rules().block_out_from);
let title_texture = title_texture.filter(|_| !should_block_out); let title_texture = title_texture.filter(|_| !should_block_out);
let title_elems = title_texture.map(|(texture, size)| { if let Some((texture, size)) = title_texture {
// Clip from the right if it doesn't fit. // Clip from the right if it doesn't fit.
let src = Rectangle::from_size(size); let src = Rectangle::from_size(size);
@@ -484,15 +494,15 @@ impl Thumbnail {
let renderer = renderer.as_gles_renderer(); let renderer = renderer.as_gles_renderer();
if let Some(program) = GradientFadeTextureRenderElement::shader(renderer) { if let Some(program) = GradientFadeTextureRenderElement::shader(renderer) {
let elem = GradientFadeTextureRenderElement::new(texture, program); let elem = GradientFadeTextureRenderElement::new(texture, program);
WindowMruUiRenderElement::GradientFadeElem(elem) push(WindowMruUiRenderElement::GradientFadeElem(elem));
} else { } else {
let elem = PrimaryGpuTextureRenderElement(texture); let elem = PrimaryGpuTextureRenderElement(texture);
WindowMruUiRenderElement::TextureElement(elem) push(WindowMruUiRenderElement::TextureElement(elem));
}
} }
});
let is_urgent = mapped.is_urgent(); let is_urgent = mapped.is_urgent();
let background_elems = (is_active || is_urgent).then(|| { if is_active || is_urgent {
let padding = Point::new(padding, padding); let padding = Point::new(padding, padding);
let mut size = preview_geo.size; let mut size = preview_geo.size;
@@ -532,9 +542,9 @@ impl Thumbnail {
scale, scale,
0.5, 0.5,
); );
let bg_elems = background background.render(renderer, loc, &mut |elem| {
.render(renderer, loc) push(WindowMruUiRenderElement::FocusRing(elem))
.map(WindowMruUiRenderElement::FocusRing); });
let mut border = self.border.borrow_mut(); let mut border = self.border.borrow_mut();
let mut config = *border.config(); let mut config = *border.config();
@@ -554,15 +564,10 @@ impl Thumbnail {
1., 1.,
); );
let border_elems = border border.render(renderer, loc, &mut |elem| {
.render(renderer, loc) push(WindowMruUiRenderElement::FocusRing(elem))
.map(WindowMruUiRenderElement::FocusRing);
bg_elems.chain(border_elems)
}); });
let background_elems = background_elems.into_iter().flatten(); }
elems.chain(title_elems).chain(background_elems)
} }
} }
@@ -1091,26 +1096,27 @@ impl WindowMruUi {
} }
} }
pub fn render_output<'a, R: NiriRenderer>( pub fn render_output<R: NiriRenderer>(
&'a self, &self,
niri: &'a Niri, niri: &Niri,
output: &Output, output: &Output,
renderer: &'a mut R, renderer: &mut R,
target: RenderTarget, target: RenderTarget,
) -> Option<impl Iterator<Item = WindowMruUiRenderElement<R>> + 'a> { push: &mut dyn FnMut(WindowMruUiRenderElement<R>),
) {
let (inner, progress) = match &self.state { let (inner, progress) = match &self.state {
UiState::Closed { .. } => return None, UiState::Closed { .. } => return,
UiState::Closing { inner, anim } => (inner, anim.clamped_value()), UiState::Closing { inner, anim } => (inner, anim.clamped_value()),
UiState::Open(inner) => { UiState::Open(inner) => {
if inner.is_fully_open() { if inner.is_fully_open() {
(inner, 1.) (inner, 1.)
} else { } else {
return None; return;
} }
} }
}; };
let span = tracy_client::span!("mru render"); let _span = tracy_client::span!("WindowMruUi::render_output");
let alpha = progress.clamp(0., 1.) as f32; let alpha = progress.clamp(0., 1.) as f32;
@@ -1131,9 +1137,12 @@ impl WindowMruUi {
}; };
// During the closing fade, use an offscreen to avoid transparent compositing artifacts. // During the closing fade, use an offscreen to avoid transparent compositing artifacts.
let offscreen_elem = if *output == inner.output && alpha < 1. { let mut pushed_offscreen = false;
if *output == inner.output && alpha < 1. {
let renderer = renderer.as_gles_renderer(); let renderer = renderer.as_gles_renderer();
let mut elems = Vec::from_iter(inner.render(niri, renderer, target));
let mut elems = Vec::new();
inner.render(niri, renderer, target, &mut |elem| elems.push(elem));
elems.push(WindowMruUiRenderElement::SolidColor(render_backdrop(1.))); elems.push(WindowMruUiRenderElement::SolidColor(render_backdrop(1.)));
let scale = output.current_scale().fractional_scale(); let scale = output.current_scale().fractional_scale();
@@ -1149,39 +1158,27 @@ impl WindowMruUi {
// itself). // itself).
// //
// Anyhow, this is not very noticeable since Alt-Tab closing happens quickly. // Anyhow, this is not very noticeable since Alt-Tab closing happens quickly.
Some(WindowMruUiRenderElement::Offscreen(elem.with_alpha(alpha))) push(WindowMruUiRenderElement::Offscreen(elem.with_alpha(alpha)));
pushed_offscreen = true;
} }
Err(err) => { Err(err) => {
warn!("error rendering MRU to offscreen for fade-out: {err:?}"); warn!("error rendering MRU to offscreen for fade-out: {err:?}");
None
} }
} }
} else { }
None
};
// When alpha is 1., render everything directly, without an offscreen. // When alpha is 1., render everything directly, without an offscreen.
// //
// This is not used as fallback when offscreen fails to render because it looks better to // This is not used as fallback when offscreen fails to render because it looks better to
// hide the previews immediately than to render them with alpha = 1. during a fade-out. // hide the previews immediately than to render them with alpha = 1. during a fade-out.
let normal_elems = if *output == inner.output && alpha == 1. {
(*output == inner.output && alpha == 1.).then(|| inner.render(niri, renderer, target)); inner.render(niri, renderer, target, &mut |elem| push(elem));
let normal_elems = normal_elems.into_iter().flatten(); }
// This is used for both normal elems and for other outputs. // This is used for both normal elems and for other outputs.
let backdrop_elem = (offscreen_elem.is_none()) if !pushed_offscreen {
.then(|| WindowMruUiRenderElement::SolidColor(render_backdrop(alpha))); push(WindowMruUiRenderElement::SolidColor(render_backdrop(alpha)));
}
// Make sure the span includes consuming the iterator.
let drop_span = std::iter::once(span).filter_map(|_| None);
Some(
offscreen_elem
.into_iter()
.chain(normal_elems)
.chain(backdrop_elem)
.chain(drop_span),
)
} }
pub fn are_animations_ongoing(&self) -> bool { pub fn are_animations_ongoing(&self) -> bool {
@@ -1554,12 +1551,13 @@ impl Inner {
}) })
} }
fn render<'a, R: NiriRenderer>( fn render<R: NiriRenderer>(
&'a self, &self,
niri: &'a Niri, niri: &Niri,
renderer: &'a mut R, renderer: &mut R,
target: RenderTarget, target: RenderTarget,
) -> impl Iterator<Item = WindowMruUiRenderElement<R>> + 'a { push: &mut dyn FnMut(WindowMruUiRenderElement<R>),
) {
let output_size = output_size(&self.output); let output_size = output_size(&self.output);
let scale = self.output.current_scale().fractional_scale(); let scale = self.output.current_scale().fractional_scale();
@@ -1567,7 +1565,7 @@ impl Inner {
self.scope_panel self.scope_panel
.borrow_mut() .borrow_mut()
.get(renderer.as_gles_renderer(), scale, self.wmru.scope); .get(renderer.as_gles_renderer(), scale, self.wmru.scope);
let panel = panel_texture.map(move |texture| { if let Some(texture) = panel_texture {
let padding = round_logical_in_physical(scale, f64::from(PANEL_PADDING)); let padding = round_logical_in_physical(scale, f64::from(PANEL_PADDING));
let size = texture.logical_size(); let size = texture.logical_size();
@@ -1580,9 +1578,8 @@ impl Inner {
None, None,
Kind::Unspecified, Kind::Unspecified,
)); ));
WindowMruUiRenderElement::TextureElement(elem) push(WindowMruUiRenderElement::TextureElement(elem));
}); }
let panel = panel.into_iter();
let current_id = self.wmru.current_id; let current_id = self.wmru.current_id;
@@ -1591,26 +1588,20 @@ impl Inner {
let config = self.config.borrow(); let config = self.config.borrow();
let thumbnails = self for (thumbnail, geo) in self.thumbnails_in_view_render() {
.thumbnails_in_view_render()
.filter_map(move |(thumbnail, geo)| {
let id = thumbnail.id; let id = thumbnail.id;
let Some((_, mapped)) = niri.layout.windows().find(|(_, m)| m.id() == id) else { let Some((_, mapped)) = niri.layout.windows().find(|(_, m)| m.id() == id) else {
error!("window in the MRU must be present in the layout"); error!("window in the MRU must be present in the layout");
return None; continue;
}; };
let config = &config.recent_windows; let config = &config.recent_windows;
let is_active = Some(id) == current_id; let is_active = Some(id) == current_id;
let elems = thumbnail.render( thumbnail.render(
renderer, config, mapped, geo, scale, is_active, bob_y, target, renderer, config, mapped, geo, scale, is_active, bob_y, target, push,
); );
Some(elems) }
});
let thumbnails = thumbnails.flatten();
panel.chain(thumbnails)
} }
fn thumbnail_under(&self, pos: Point<f64, Logical>) -> Option<MappedId> { fn thumbnail_under(&self, pos: Point<f64, Logical>) -> Option<MappedId> {
+11 -15
View File
@@ -623,7 +623,8 @@ impl ScreenshotUi {
&self, &self,
output: &Output, output: &Output,
target: RenderTarget, target: RenderTarget,
) -> ArrayVec<ScreenshotUiRenderElement, 11> { push: &mut dyn FnMut(ScreenshotUiRenderElement),
) {
let _span = tracy_client::span!("ScreenshotUi::render_output"); let _span = tracy_client::span!("ScreenshotUi::render_output");
let Self::Open { let Self::Open {
@@ -637,10 +638,8 @@ impl ScreenshotUi {
panic!("screenshot UI must be open to render it"); panic!("screenshot UI must be open to render it");
}; };
let mut elements = ArrayVec::new();
let Some(output_data) = output_data.get(output) else { let Some(output_data) = output_data.get(output) else {
return elements; return;
}; };
let scale = output_data.scale; let scale = output_data.scale;
@@ -666,19 +665,18 @@ impl ScreenshotUi {
None, None,
Kind::Unspecified, Kind::Unspecified,
)); ));
elements.push(elem.into()); push(elem.into());
} }
let buf_loc = zip(&output_data.buffers, &output_data.locations); for (buffer, loc) in zip(&output_data.buffers, &output_data.locations) {
elements.extend(buf_loc.map(|(buffer, loc)| { let elem = SolidColorRenderElement::from_buffer(
SolidColorRenderElement::from_buffer(
buffer, buffer,
loc.to_f64().to_logical(scale), loc.to_f64().to_logical(scale),
progress, progress,
Kind::Unspecified, Kind::Unspecified,
) );
.into() push(elem.into());
})); }
// The screenshot itself goes last. // The screenshot itself goes last.
let index = match target { let index = match target {
@@ -690,12 +688,10 @@ impl ScreenshotUi {
if *show_pointer { if *show_pointer {
if let Some(pointer) = screenshot.pointer.clone() { if let Some(pointer) = screenshot.pointer.clone() {
elements.push(pointer.into()); push(pointer.into());
} }
} }
elements.push(screenshot.buffer.clone().into()); push(screenshot.buffer.clone().into());
elements
} }
pub fn capture( pub fn capture(
+32 -66
View File
@@ -2,7 +2,7 @@ use std::cell::{Cell, Ref, RefCell};
use std::time::Duration; use std::time::Duration;
use niri_config::{Color, CornerRadius, GradientInterpolation, WindowRule}; use niri_config::{Color, CornerRadius, GradientInterpolation, WindowRule};
use smithay::backend::renderer::element::surface::render_elements_from_surface_tree; use smithay::backend::renderer::element::surface::WaylandSurfaceRenderElement;
use smithay::backend::renderer::element::Kind; use smithay::backend::renderer::element::Kind;
use smithay::backend::renderer::gles::GlesRenderer; use smithay::backend::renderer::gles::GlesRenderer;
use smithay::desktop::space::SpaceElement as _; use smithay::desktop::space::SpaceElement as _;
@@ -33,8 +33,10 @@ use crate::render_helpers::offscreen::OffscreenData;
use crate::render_helpers::renderer::NiriRenderer; use crate::render_helpers::renderer::NiriRenderer;
use crate::render_helpers::snapshot::RenderSnapshot; use crate::render_helpers::snapshot::RenderSnapshot;
use crate::render_helpers::solid_color::{SolidColorBuffer, SolidColorRenderElement}; use crate::render_helpers::solid_color::{SolidColorBuffer, SolidColorRenderElement};
use crate::render_helpers::surface::render_snapshot_from_surface_tree; use crate::render_helpers::surface::{
use crate::render_helpers::{BakedBuffer, RenderTarget, SplitElements}; push_elements_from_surface_tree, render_snapshot_from_surface_tree,
};
use crate::render_helpers::{BakedBuffer, RenderTarget};
use crate::utils::id::IdCounter; use crate::utils::id::IdCounter;
use crate::utils::transaction::Transaction; use crate::utils::transaction::Transaction;
use crate::utils::{ use crate::utils::{
@@ -472,7 +474,8 @@ impl Mapped {
&self, &self,
renderer: &mut R, renderer: &mut R,
scale: Scale<f64>, scale: Scale<f64>,
) -> impl DoubleEndedIterator<Item = WindowCastRenderElements<R>> { push: &mut dyn FnMut(WindowCastRenderElements<R>),
) {
let bbox = self.window.bbox_with_popups().to_physical_precise_up(scale); let bbox = self.window.bbox_with_popups().to_physical_precise_up(scale);
let has_border_shader = BorderRenderElement::has_shader(renderer); let has_border_shader = BorderRenderElement::has_shader(renderer);
@@ -484,11 +487,9 @@ impl Mapped {
.to_physical_precise_round(scale) .to_physical_precise_round(scale)
.to_logical(scale); .to_logical(scale);
let radius = radius.fit_to(window_size.w as f32, window_size.h as f32); let radius = radius.fit_to(window_size.w as f32, window_size.h as f32);
let location = self.window.geometry().loc.to_f64() - bbox.loc.to_logical(scale); let location = self.window.geometry().loc.to_f64() - bbox.loc.to_logical(scale);
let elements = self.render(renderer, location, scale, 1., RenderTarget::Screencast);
elements.into_iter().map(move |elem| { let use_border = |elem| {
if let LayoutElementRenderElement::SolidColor(elem) = &elem { if let LayoutElementRenderElement::SolidColor(elem) = &elem {
// In this branch we're rendering a blocked-out window with a solid color. We need // In this branch we're rendering a blocked-out window with a solid color. We need
// to render it with a rounded corner shader even if clip_to_geometry is false, // to render it with a rounded corner shader even if clip_to_geometry is false,
@@ -516,7 +517,16 @@ impl Mapped {
} }
WindowCastRenderElements::from(elem) WindowCastRenderElements::from(elem)
}) };
self.render(
renderer,
location,
scale,
1.,
RenderTarget::Screencast,
&mut |elem| push(use_border(elem)),
);
} }
pub fn get_focus_timestamp(&self) -> Option<Duration> { pub fn get_focus_timestamp(&self) -> Option<Duration> {
@@ -601,52 +611,6 @@ impl LayoutElement for Mapped {
self.window.is_in_input_region(&surface_local) self.window.is_in_input_region(&surface_local)
} }
fn render<R: NiriRenderer>(
&self,
renderer: &mut R,
location: Point<f64, Logical>,
scale: Scale<f64>,
alpha: f32,
target: RenderTarget,
) -> SplitElements<LayoutElementRenderElement<R>> {
let mut rv = SplitElements::default();
if target.should_block_out(self.rules.block_out_from) {
let mut buffer = self.block_out_buffer.borrow_mut();
buffer.resize(self.window.geometry().size.to_f64());
let elem =
SolidColorRenderElement::from_buffer(&buffer, location, alpha, Kind::Unspecified);
rv.normal.push(elem.into());
} else {
let buf_pos = location - self.window.geometry().loc.to_f64();
let surface = self.toplevel().wl_surface();
for (popup, popup_offset) in PopupManager::popups_for_surface(surface) {
let offset = self.window.geometry().loc + popup_offset - popup.geometry().loc;
rv.popups.extend(render_elements_from_surface_tree(
renderer,
popup.wl_surface(),
(buf_pos + offset.to_f64()).to_physical_precise_round(scale),
scale,
alpha,
Kind::ScanoutCandidate,
));
}
rv.normal = render_elements_from_surface_tree(
renderer,
surface,
buf_pos.to_physical_precise_round(scale),
scale,
alpha,
Kind::ScanoutCandidate,
);
}
rv
}
fn render_normal<R: NiriRenderer>( fn render_normal<R: NiriRenderer>(
&self, &self,
renderer: &mut R, renderer: &mut R,
@@ -654,23 +618,26 @@ impl LayoutElement for Mapped {
scale: Scale<f64>, scale: Scale<f64>,
alpha: f32, alpha: f32,
target: RenderTarget, target: RenderTarget,
) -> Vec<LayoutElementRenderElement<R>> { push: &mut dyn FnMut(LayoutElementRenderElement<R>),
) {
if target.should_block_out(self.rules.block_out_from) { if target.should_block_out(self.rules.block_out_from) {
let mut buffer = self.block_out_buffer.borrow_mut(); let mut buffer = self.block_out_buffer.borrow_mut();
buffer.resize(self.window.geometry().size.to_f64()); buffer.resize(self.window.geometry().size.to_f64());
let elem = let elem =
SolidColorRenderElement::from_buffer(&buffer, location, alpha, Kind::Unspecified); SolidColorRenderElement::from_buffer(&buffer, location, alpha, Kind::Unspecified);
vec![elem.into()] push(elem.into());
} else { } else {
let buf_pos = location - self.window.geometry().loc.to_f64(); let buf_pos = location - self.window.geometry().loc.to_f64();
let surface = self.toplevel().wl_surface(); let surface = self.toplevel().wl_surface();
render_elements_from_surface_tree( let mut push = |elem: WaylandSurfaceRenderElement<R>| push(elem.into());
push_elements_from_surface_tree(
renderer, renderer,
surface, surface,
buf_pos.to_physical_precise_round(scale), buf_pos.to_physical_precise_round(scale),
scale, scale,
alpha, alpha,
Kind::ScanoutCandidate, Kind::ScanoutCandidate,
&mut push,
) )
} }
} }
@@ -682,28 +649,27 @@ impl LayoutElement for Mapped {
scale: Scale<f64>, scale: Scale<f64>,
alpha: f32, alpha: f32,
target: RenderTarget, target: RenderTarget,
) -> Vec<LayoutElementRenderElement<R>> { push: &mut dyn FnMut(LayoutElementRenderElement<R>),
) {
if target.should_block_out(self.rules.block_out_from) { if target.should_block_out(self.rules.block_out_from) {
vec![] return;
} else { }
let mut rv = vec![];
let buf_pos = location - self.window.geometry().loc.to_f64(); let buf_pos = location - self.window.geometry().loc.to_f64();
let surface = self.toplevel().wl_surface(); let surface = self.toplevel().wl_surface();
let mut push = |elem: WaylandSurfaceRenderElement<R>| push(elem.into());
for (popup, popup_offset) in PopupManager::popups_for_surface(surface) { for (popup, popup_offset) in PopupManager::popups_for_surface(surface) {
let offset = self.window.geometry().loc + popup_offset - popup.geometry().loc; let offset = self.window.geometry().loc + popup_offset - popup.geometry().loc;
rv.extend(render_elements_from_surface_tree( push_elements_from_surface_tree(
renderer, renderer,
popup.wl_surface(), popup.wl_surface(),
(buf_pos + offset.to_f64()).to_physical_precise_round(scale), (buf_pos + offset.to_f64()).to_physical_precise_round(scale),
scale, scale,
alpha, alpha,
Kind::ScanoutCandidate, Kind::ScanoutCandidate,
)); &mut push,
} );
rv
} }
} }