diff options
| author | Ivan Molodetskikh <yalterz@gmail.com> | 2025-03-01 09:45:57 +0300 |
|---|---|---|
| committer | Ivan Molodetskikh <yalterz@gmail.com> | 2025-03-10 07:59:14 +0300 |
| commit | 4f5c8e745bec6395105138c83468bccb4ab27ea9 (patch) | |
| tree | dc9fec8fa7ba6019316fff22f22c1544f81fc7ad | |
| parent | f30413a744742b013c3f215369fe28780d6ba392 (diff) | |
| download | niri-4f5c8e745bec6395105138c83468bccb4ab27ea9.tar.gz niri-4f5c8e745bec6395105138c83468bccb4ab27ea9.tar.bz2 niri-4f5c8e745bec6395105138c83468bccb4ab27ea9.zip | |
Offscreen semitransparent tiles
Now that offscreen does damage tracking, we can reasonably do this. Note this
only affects full-tile opacity, not window opacity.
| -rw-r--r-- | src/layout/opening_window.rs | 5 | ||||
| -rw-r--r-- | src/layout/tile.rs | 77 | ||||
| -rw-r--r-- | src/layout/workspace.rs | 3 | ||||
| -rw-r--r-- | src/render_helpers/offscreen.rs | 5 |
4 files changed, 64 insertions, 26 deletions
diff --git a/src/layout/opening_window.rs b/src/layout/opening_window.rs index 7e24b4cb..8a1db409 100644 --- a/src/layout/opening_window.rs +++ b/src/layout/opening_window.rs @@ -54,6 +54,7 @@ impl OpenAnimation { geo_size: Size<f64, Logical>, location: Point<f64, Logical>, scale: Scale<f64>, + alpha: f32, ) -> anyhow::Result<OpeningWindowRenderElement> { let progress = self.anim.value(); let clamped_progress = self.anim.clamped_value().clamp(0., 1.); @@ -102,7 +103,7 @@ impl OpenAnimation { area.size, None, scale.x as f32, - 1., + alpha, vec![ mat3_uniform("niri_input_to_geo", input_to_geo), Uniform::new("niri_geo_size", geo_size.to_array()), @@ -118,7 +119,7 @@ impl OpenAnimation { .into()); } - let elem = elem.with_alpha(clamped_progress as f32); + let elem = elem.with_alpha(clamped_progress as f32 * alpha); let center = geo_size.to_point().downscale(2.); let elem = RescaleRenderElement::from_element( diff --git a/src/layout/tile.rs b/src/layout/tile.rs index dbefd6e1..71aa33a1 100644 --- a/src/layout/tile.rs +++ b/src/layout/tile.rs @@ -18,6 +18,7 @@ use crate::niri_render_elements; use crate::render_helpers::border::BorderRenderElement; use crate::render_helpers::clipped_surface::{ClippedSurfaceRenderElement, RoundedCornerDamage}; use crate::render_helpers::damage::ExtraDamage; +use crate::render_helpers::offscreen::{OffscreenBuffer, OffscreenRenderElement}; use crate::render_helpers::renderer::NiriRenderer; use crate::render_helpers::resize::ResizeRenderElement; use crate::render_helpers::shadow::ShadowRenderElement; @@ -86,7 +87,7 @@ pub struct Tile<W: LayoutElement> { move_y_animation: Option<MoveAnimation>, /// The animation of the tile's opacity. - pub(super) alpha_animation: Option<Animation>, + pub(super) alpha_animation: Option<AlphaAnimation>, /// Offset during the initial interactive move rubberband. pub(super) interactive_move_offset: Point<f64, Logical>, @@ -122,6 +123,7 @@ niri_render_elements! { Border = BorderRenderElement, Shadow = ShadowRenderElement, ClippedSurface = ClippedSurfaceRenderElement<R>, + Offscreen = OffscreenRenderElement, ExtraDamage = ExtraDamage, } } @@ -143,6 +145,12 @@ struct MoveAnimation { from: f64, } +#[derive(Debug)] +pub(super) struct AlphaAnimation { + pub(super) anim: Animation, + offscreen: OffscreenBuffer, +} + impl<W: LayoutElement> Tile<W> { pub fn new( window: W, @@ -311,7 +319,7 @@ impl<W: LayoutElement> Tile<W> { } if let Some(alpha) = &mut self.alpha_animation { - if alpha.is_done() { + if alpha.anim.is_done() { self.alpha_animation = None; } } @@ -331,7 +339,6 @@ impl<W: LayoutElement> Tile<W> { pub fn update_render_elements(&mut self, is_active: bool, view_rect: Rectangle<f64, Logical>) { let rules = self.window.rules(); - let alpha = self.tile_alpha(); let draw_border_with_background = rules .draw_border_with_background @@ -356,7 +363,7 @@ impl<W: LayoutElement> Tile<W> { ), radius, self.scale, - alpha, + 1., ); let radius = if self.is_fullscreen { @@ -371,7 +378,7 @@ impl<W: LayoutElement> Tile<W> { is_active, radius, self.scale, - alpha, + 1., ); let draw_focus_ring_with_background = if self.effective_border_width().is_some() { @@ -387,7 +394,7 @@ impl<W: LayoutElement> Tile<W> { view_rect, radius, self.scale, - alpha, + 1., ); } @@ -475,14 +482,22 @@ impl<W: LayoutElement> Tile<W> { pub fn animate_alpha(&mut self, from: f64, to: f64, config: niri_config::Animation) { let from = from.clamp(0., 1.); let to = to.clamp(0., 1.); - let current = self.alpha_animation.take().map(|anim| anim.clamped_value()); - let current = current.unwrap_or(from); - self.alpha_animation = Some(Animation::new(self.clock.clone(), current, to, 0., config)); + + let (current, offscreen) = if let Some(alpha) = self.alpha_animation.take() { + (alpha.anim.clamped_value(), alpha.offscreen) + } else { + (from, OffscreenBuffer::default()) + }; + + self.alpha_animation = Some(AlphaAnimation { + anim: Animation::new(self.clock.clone(), current, to, 0., config), + offscreen, + }); } pub fn ensure_alpha_animates_to_1(&mut self) { - if let Some(anim) = &self.alpha_animation { - if anim.to() != 1. { + if let Some(alpha) = &self.alpha_animation { + if alpha.anim.to() != 1. { // Cancel animation instead of starting a new one because the user likely wants to // see the tile right away. self.alpha_animation = None; @@ -490,13 +505,6 @@ impl<W: LayoutElement> Tile<W> { } } - /// Opacity that applies to both window and decorations. - fn tile_alpha(&self) -> f32 { - self.alpha_animation - .as_ref() - .map_or(1., |anim| anim.clamped_value()) as f32 - } - pub fn window(&self) -> &W { &self.window } @@ -802,9 +810,6 @@ impl<W: LayoutElement> Tile<W> { self.window.rules().opacity.unwrap_or(1.).clamp(0., 1.) }; - let tile_alpha = self.tile_alpha(); - let win_alpha = win_alpha * tile_alpha; - // This is here rather than in render_offset() because render_offset() is currently assumed // by the code to be temporary. So, for example, interactive move will try to "grab" the // tile at its current render offset and reset the render offset to zero by cancelling the @@ -998,7 +1003,7 @@ impl<W: LayoutElement> Tile<W> { SolidColorRenderElement::from_buffer( &self.fullscreen_backdrop, location, - tile_alpha, + 1., Kind::Unspecified, ) .into() @@ -1028,7 +1033,13 @@ impl<W: LayoutElement> Tile<W> { ) -> impl Iterator<Item = TileRenderElement<R>> + 'a { let _span = tracy_client::span!("Tile::render"); + let tile_alpha = self + .alpha_animation + .as_ref() + .map_or(1., |alpha| alpha.anim.clamped_value()) as f32; + let mut open_anim_elem = None; + let mut alpha_anim_elem = None; let mut window_elems = None; if let Some(open) = &self.open_animation { @@ -1042,6 +1053,7 @@ impl<W: LayoutElement> Tile<W> { self.animated_tile_size(), location, scale, + tile_alpha, ) { Ok(elem) => { self.window() @@ -1052,15 +1064,34 @@ impl<W: LayoutElement> Tile<W> { warn!("error rendering window opening animation: {err:?}"); } } + } else if let Some(alpha) = &self.alpha_animation { + let renderer = renderer.as_gles_renderer(); + let elements = + self.render_inner(renderer, Point::from((0., 0.)), scale, focus_ring, target); + let elements = elements.collect::<Vec<TileRenderElement<_>>>(); + match alpha.offscreen.render(renderer, scale, &elements) { + Ok((elem, _sync)) => { + let offset = elem.offset(); + let elem = elem.with_alpha(tile_alpha).with_offset(location + offset); + + self.window() + .set_offscreen_element_id(Some(elem.id().clone())); + alpha_anim_elem = Some(elem.into()); + } + Err(err) => { + warn!("error rendering tile to offscreen for alpha animation: {err:?}"); + } + } } - if open_anim_elem.is_none() { + if open_anim_elem.is_none() && alpha_anim_elem.is_none() { self.window().set_offscreen_element_id(None); window_elems = Some(self.render_inner(renderer, location, scale, focus_ring, target)); } open_anim_elem .into_iter() + .chain(alpha_anim_elem) .chain(window_elems.into_iter().flatten()) } diff --git a/src/layout/workspace.rs b/src/layout/workspace.rs index 226c4411..0e4c1af5 100644 --- a/src/layout/workspace.rs +++ b/src/layout/workspace.rs @@ -1752,7 +1752,8 @@ impl<W: LayoutElement> Workspace<W> { assert_abs_diff_eq!(tile_pos.x, rounded_pos.x, epsilon = 1e-5); assert_abs_diff_eq!(tile_pos.y, rounded_pos.y, epsilon = 1e-5); - if let Some(anim) = &tile.alpha_animation { + if let Some(alpha) = &tile.alpha_animation { + let anim = &alpha.anim; if visible { assert_eq!(anim.to(), 1., "visible tiles can animate alpha only to 1"); } diff --git a/src/render_helpers/offscreen.rs b/src/render_helpers/offscreen.rs index 1a4cc2c8..62c123ea 100644 --- a/src/render_helpers/offscreen.rs +++ b/src/render_helpers/offscreen.rs @@ -206,6 +206,11 @@ impl OffscreenRenderElement { self } + pub fn with_offset(mut self, offset: Point<f64, Logical>) -> Self { + self.offset = offset; + self + } + pub fn logical_size(&self) -> Size<f64, Logical> { self.src_size .to_f64() |
