aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIvan Molodetskikh <yalterz@gmail.com>2025-03-01 09:45:57 +0300
committerIvan Molodetskikh <yalterz@gmail.com>2025-03-10 07:59:14 +0300
commit4f5c8e745bec6395105138c83468bccb4ab27ea9 (patch)
treedc9fec8fa7ba6019316fff22f22c1544f81fc7ad
parentf30413a744742b013c3f215369fe28780d6ba392 (diff)
downloadniri-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.rs5
-rw-r--r--src/layout/tile.rs77
-rw-r--r--src/layout/workspace.rs3
-rw-r--r--src/render_helpers/offscreen.rs5
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()