Reimplement window closing anim in an efficient way

- Keep a root surface cache to be accessible in surface destroyed()
- Only snapshot during / right before closing, rather than every frame
- Store textures rather than elements to handle scale and alpha properly
This commit is contained in:
Ivan Molodetskikh
2024-04-10 08:53:35 +04:00
parent b9e789619f
commit 1888696567
10 changed files with 286 additions and 209 deletions
+2 -4
View File
@@ -2,9 +2,7 @@ use std::cell::RefCell;
use std::cmp::{max, min};
use std::rc::Rc;
use niri::layout::{
LayoutElement, LayoutElementRenderElement, LayoutElementSnapshotRenderElements,
};
use niri::layout::{LayoutElement, LayoutElementRenderElement, LayoutElementRenderSnapshot};
use niri::render_helpers::renderer::NiriRenderer;
use niri::render_helpers::{RenderSnapshot, RenderTarget};
use niri::window::ResolvedWindowRules;
@@ -175,7 +173,7 @@ impl LayoutElement for TestWindow {
]
}
fn take_last_render(&self) -> RenderSnapshot<LayoutElementSnapshotRenderElements> {
fn take_last_render(&self) -> LayoutElementRenderSnapshot {
RenderSnapshot::default()
}
+51 -7
View File
@@ -16,6 +16,7 @@ use smithay::wayland::dmabuf::get_dmabuf;
use smithay::wayland::shm::{ShmHandler, ShmState};
use smithay::{delegate_compositor, delegate_shm};
use crate::layout::LayoutElement;
use crate::niri::{ClientState, State};
use crate::window::{InitialConfigureState, Mapped, ResolvedWindowRules, Unmapped};
@@ -80,6 +81,33 @@ impl CompositorHandler for State {
fn commit(&mut self, surface: &WlSurface) {
let _span = tracy_client::span!("CompositorHandler::commit");
let mut root_surface = surface.clone();
while let Some(parent) = get_parent(&root_surface) {
root_surface = parent;
}
// Update the cached root surface.
self.niri
.root_surface
.insert(surface.clone(), root_surface.clone());
// Check if this root surface got unmapped to snapshot it before on_commit_buffer_handler()
// overwrites the buffer.
if surface == &root_surface {
let got_unmapped = with_states(surface, |states| {
let attrs = states.cached_state.current::<SurfaceAttributes>();
matches!(attrs.buffer, Some(BufferAssignment::Removed))
});
if got_unmapped {
if let Some((mapped, _)) = self.niri.layout.find_window_and_output(surface) {
self.backend.with_primary_renderer(|renderer| {
mapped.render_and_store_snapshot(renderer);
});
}
}
}
on_commit_buffer_handler::<Self>(surface);
self.backend.early_import(surface);
@@ -87,11 +115,6 @@ impl CompositorHandler for State {
return;
}
let mut root_surface = surface.clone();
while let Some(parent) = get_parent(&root_surface) {
root_surface = parent;
}
if surface == &root_surface {
// This is a root surface commit. It might have mapped a previously-unmapped toplevel.
if let Entry::Occupied(entry) = self.niri.unmapped_windows.entry(surface.clone()) {
@@ -195,8 +218,11 @@ impl CompositorHandler for State {
false
});
// Must start the close animation before window.on_commit().
if !is_mapped {
if is_mapped {
// The surface remains mapped; clear any cached render snapshot.
let _ = mapped.take_last_render();
} else {
// Must start the close animation before window.on_commit().
self.backend.with_primary_renderer(|renderer| {
self.niri
.layout
@@ -288,6 +314,24 @@ impl CompositorHandler for State {
}
}
}
fn destroyed(&mut self, surface: &WlSurface) {
// Clients may destroy their subsurfaces before the main surface. Ensure we have a snapshot
// when that happens, so that the closing animation includes all these subsurfaces.
//
// Test client: alacritty with CSD.
if let Some(root) = self.niri.root_surface.get(surface) {
if let Some((mapped, _)) = self.niri.layout.find_window_and_output(root) {
self.backend.with_primary_renderer(|renderer| {
mapped.render_and_store_snapshot(renderer);
});
}
}
self.niri
.root_surface
.retain(|k, v| k != surface && v != surface);
}
}
impl BufferHandler for State {
+3
View File
@@ -382,6 +382,9 @@ impl XdgShellHandler for State {
let window = mapped.window.clone();
let output = output.clone();
self.backend.with_primary_renderer(|renderer| {
mapped.render_and_store_snapshot(renderer);
});
self.backend.with_primary_renderer(|renderer| {
self.niri
.layout
+1 -1
View File
@@ -59,7 +59,7 @@ impl ClosingWindow {
#[allow(clippy::too_many_arguments)]
pub fn new<E: RenderElement<GlesRenderer>>(
renderer: &mut GlesRenderer,
snapshot: RenderSnapshot<E>,
snapshot: RenderSnapshot<E, E>,
scale: i32,
center: Point<i32, Logical>,
pos: Point<i32, Logical>,
+8 -12
View File
@@ -36,10 +36,11 @@ use std::time::Duration;
use niri_config::{CenterFocusedColumn, Config, Struts};
use niri_ipc::SizeChange;
use smithay::backend::renderer::element::solid::SolidColorRenderElement;
use smithay::backend::renderer::element::solid::{SolidColorBuffer, SolidColorRenderElement};
use smithay::backend::renderer::element::surface::WaylandSurfaceRenderElement;
use smithay::backend::renderer::element::texture::TextureBuffer;
use smithay::backend::renderer::element::Id;
use smithay::backend::renderer::gles::GlesRenderer;
use smithay::backend::renderer::gles::{GlesRenderer, GlesTexture};
use smithay::output::Output;
use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface;
use smithay::utils::{Logical, Point, Scale, Size, Transform};
@@ -48,9 +49,8 @@ use self::monitor::Monitor;
pub use self::monitor::MonitorRenderElement;
use self::workspace::{compute_working_area, Column, ColumnWidth, OutputId, Workspace};
use crate::niri_render_elements;
use crate::render_helpers::primary_gpu_texture::PrimaryGpuTextureRenderElement;
use crate::render_helpers::renderer::NiriRenderer;
use crate::render_helpers::{RenderSnapshot, RenderTarget};
use crate::render_helpers::{BakedBuffer, RenderSnapshot, RenderTarget};
use crate::utils::output_size;
use crate::window::ResolvedWindowRules;
@@ -67,12 +67,8 @@ niri_render_elements! {
}
}
niri_render_elements! {
LayoutElementSnapshotRenderElements => {
Texture = PrimaryGpuTextureRenderElement,
SolidColor = SolidColorRenderElement,
}
}
pub type LayoutElementRenderSnapshot =
RenderSnapshot<BakedBuffer<TextureBuffer<GlesTexture>>, BakedBuffer<SolidColorBuffer>>;
pub trait LayoutElement {
/// Type that can be used as a unique ID of this element.
@@ -110,7 +106,7 @@ pub trait LayoutElement {
target: RenderTarget,
) -> Vec<LayoutElementRenderElement<R>>;
fn take_last_render(&self) -> RenderSnapshot<LayoutElementSnapshotRenderElements>;
fn take_last_render(&self) -> LayoutElementRenderSnapshot;
fn request_size(&self, size: Size<i32, Logical>);
fn request_fullscreen(&self, size: Size<i32, Logical>);
@@ -1930,7 +1926,7 @@ mod tests {
vec![]
}
fn take_last_render(&self) -> RenderSnapshot<LayoutElementSnapshotRenderElements> {
fn take_last_render(&self) -> LayoutElementRenderSnapshot {
RenderSnapshot::default()
}
+58 -43
View File
@@ -3,22 +3,19 @@ use std::rc::Rc;
use std::time::Duration;
use smithay::backend::renderer::element::solid::{SolidColorBuffer, SolidColorRenderElement};
use smithay::backend::renderer::element::utils::{
Relocate, RelocateRenderElement, RescaleRenderElement,
};
use smithay::backend::renderer::element::utils::RescaleRenderElement;
use smithay::backend::renderer::element::{Element, Kind};
use smithay::backend::renderer::gles::GlesRenderer;
use smithay::utils::{Logical, Point, Rectangle, Scale, Size};
use super::focus_ring::{FocusRing, FocusRingRenderElement};
use super::{
LayoutElement, LayoutElementRenderElement, LayoutElementSnapshotRenderElements, Options,
};
use super::{LayoutElement, LayoutElementRenderElement, Options};
use crate::animation::Animation;
use crate::niri_render_elements;
use crate::render_helpers::offscreen::OffscreenRenderElement;
use crate::render_helpers::primary_gpu_texture::PrimaryGpuTextureRenderElement;
use crate::render_helpers::renderer::NiriRenderer;
use crate::render_helpers::{RenderSnapshot, RenderTarget};
use crate::render_helpers::{RenderSnapshot, RenderTarget, ToRenderElement};
/// Toplevel window with decorations.
#[derive(Debug)]
@@ -65,7 +62,7 @@ niri_render_elements! {
niri_render_elements! {
TileSnapshotRenderElement => {
LayoutElement = RelocateRenderElement<LayoutElementSnapshotRenderElements>,
Texture = PrimaryGpuTextureRenderElement,
FocusRing = FocusRingRenderElement,
SolidColor = SolidColorRenderElement,
}
@@ -417,52 +414,70 @@ impl<W: LayoutElement> Tile<W> {
}
}
fn render_snapshot<C>(
&self,
renderer: &mut GlesRenderer,
scale: Scale<f64>,
view_size: Size<i32, Logical>,
contents: Vec<C>,
) -> Vec<TileSnapshotRenderElement>
where
C: ToRenderElement<RenderElement: Into<TileSnapshotRenderElement>>,
{
let alpha = if self.is_fullscreen {
1.
} else {
self.window.rules().opacity.unwrap_or(1.).clamp(0., 1.)
};
let mut rv = vec![];
for baked in contents {
let elem = baked.to_render_element(self.window_loc(), scale, alpha, Kind::Unspecified);
rv.push(elem.into());
}
if let Some(width) = self.effective_border_width() {
rv.extend(
self.border
.render(renderer, Point::from((width, width)), scale, view_size)
.map(Into::into),
);
}
if self.is_fullscreen {
let elem = SolidColorRenderElement::from_buffer(
&self.fullscreen_backdrop,
Point::from((0, 0)),
scale,
1.,
Kind::Unspecified,
);
rv.push(elem.into());
}
rv
}
pub fn take_snapshot_for_close_anim(
&self,
renderer: &mut GlesRenderer,
scale: Scale<f64>,
view_size: Size<i32, Logical>,
) -> RenderSnapshot<TileSnapshotRenderElement> {
) -> RenderSnapshot<TileSnapshotRenderElement, TileSnapshotRenderElement> {
let snapshot = self.window.take_last_render();
if snapshot.contents.is_empty() {
return RenderSnapshot::default();
}
let mut process = |contents| {
let mut rv = vec![];
let buf_pos =
(self.window_loc() + self.window.buf_loc()).to_physical_precise_round(scale);
for elem in contents {
let elem = RelocateRenderElement::from_element(elem, buf_pos, Relocate::Relative);
rv.push(elem.into());
}
if let Some(width) = self.effective_border_width() {
rv.extend(
self.border
.render(renderer, Point::from((width, width)), scale, view_size)
.map(Into::into),
);
}
if self.is_fullscreen {
let elem = SolidColorRenderElement::from_buffer(
&self.fullscreen_backdrop,
Point::from((0, 0)),
scale,
1.,
Kind::Unspecified,
);
rv.push(elem.into());
}
rv
};
RenderSnapshot {
contents: process(snapshot.contents),
blocked_out_contents: process(snapshot.blocked_out_contents),
contents: self.render_snapshot(renderer, scale, view_size, snapshot.contents),
blocked_out_contents: self.render_snapshot(
renderer,
scale,
view_size,
snapshot.blocked_out_contents,
),
block_out_from: snapshot.block_out_from,
}
}
+5
View File
@@ -151,6 +151,10 @@ pub struct Niri {
// Windows which don't have a buffer attached yet.
pub unmapped_windows: HashMap<WlSurface, Unmapped>,
// Cached root surface for every surface, so that we can access it in destroyed() where the
// normal get_parent() is cleared out.
pub root_surface: HashMap<WlSurface, WlSurface>,
pub output_state: HashMap<Output, OutputState>,
pub output_by_name: HashMap<String, Output>,
@@ -1318,6 +1322,7 @@ impl Niri {
output_state: HashMap::new(),
output_by_name: HashMap::new(),
unmapped_windows: HashMap::new(),
root_surface: HashMap::new(),
monitors_active: true,
devices: HashSet::new(),
+75 -6
View File
@@ -3,15 +3,19 @@ use std::ptr;
use anyhow::{ensure, Context};
use niri_config::BlockOutFrom;
use smithay::backend::allocator::Fourcc;
use smithay::backend::renderer::element::RenderElement;
use smithay::backend::renderer::element::solid::{SolidColorBuffer, SolidColorRenderElement};
use smithay::backend::renderer::element::texture::{TextureBuffer, TextureRenderElement};
use smithay::backend::renderer::element::{Kind, RenderElement};
use smithay::backend::renderer::gles::{GlesMapping, GlesRenderer, GlesTexture};
use smithay::backend::renderer::sync::SyncPoint;
use smithay::backend::renderer::{buffer_dimensions, Bind, ExportMem, Frame, Offscreen, Renderer};
use smithay::reexports::wayland_server::protocol::wl_buffer::WlBuffer;
use smithay::reexports::wayland_server::protocol::wl_shm;
use smithay::utils::{Physical, Rectangle, Scale, Size, Transform};
use smithay::utils::{Logical, Physical, Point, Rectangle, Scale, Size, Transform};
use smithay::wayland::shm;
use self::primary_gpu_texture::PrimaryGpuTextureRenderElement;
pub mod gradient;
pub mod offscreen;
pub mod primary_gpu_pixel_shader;
@@ -32,20 +36,85 @@ pub enum RenderTarget {
ScreenCapture,
}
/// Buffer with location, src and dst.
#[derive(Debug)]
pub struct BakedBuffer<B> {
pub buffer: B,
pub location: Point<i32, Logical>,
pub src: Option<Rectangle<f64, Logical>>,
pub dst: Option<Size<i32, Logical>>,
}
/// Snapshot of a render.
#[derive(Debug)]
pub struct RenderSnapshot<E> {
pub struct RenderSnapshot<C, B> {
/// Contents for a normal render.
pub contents: Vec<E>,
pub contents: Vec<C>,
/// Blocked-out contents.
pub blocked_out_contents: Vec<E>,
pub blocked_out_contents: Vec<B>,
/// Where the contents were blocked out from at the time of the snapshot.
pub block_out_from: Option<BlockOutFrom>,
}
impl<E> Default for RenderSnapshot<E> {
pub trait ToRenderElement {
type RenderElement;
fn to_render_element(
&self,
location: Point<i32, Logical>,
scale: Scale<f64>,
alpha: f32,
kind: Kind,
) -> Self::RenderElement;
}
impl ToRenderElement for BakedBuffer<TextureBuffer<GlesTexture>> {
type RenderElement = PrimaryGpuTextureRenderElement;
fn to_render_element(
&self,
location: Point<i32, Logical>,
scale: Scale<f64>,
alpha: f32,
kind: Kind,
) -> Self::RenderElement {
let elem = TextureRenderElement::from_texture_buffer(
(location + self.location).to_physical_precise_round(scale),
&self.buffer,
Some(alpha),
self.src,
self.dst,
kind,
);
PrimaryGpuTextureRenderElement(elem)
}
}
impl ToRenderElement for BakedBuffer<SolidColorBuffer> {
type RenderElement = SolidColorRenderElement;
fn to_render_element(
&self,
location: Point<i32, Logical>,
scale: Scale<f64>,
alpha: f32,
kind: Kind,
) -> Self::RenderElement {
SolidColorRenderElement::from_buffer(
&self.buffer,
(location + self.location)
.to_physical_precise_round(scale)
.to_i32_round(),
scale,
alpha,
kind,
)
}
}
impl<C, B> Default for RenderSnapshot<C, B> {
fn default() -> Self {
Self {
contents: Default::default(),
+37 -71
View File
@@ -1,41 +1,25 @@
use smithay::backend::renderer::element::surface::WaylandSurfaceRenderElement;
use smithay::backend::renderer::element::texture::{TextureBuffer, TextureRenderElement};
use smithay::backend::renderer::element::Kind;
use smithay::backend::renderer::gles::GlesRenderer;
use smithay::backend::renderer::element::texture::TextureBuffer;
use smithay::backend::renderer::gles::{GlesRenderer, GlesTexture};
use smithay::backend::renderer::utils::{import_surface, RendererSurfaceStateUserData};
use smithay::backend::renderer::Renderer as _;
use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface;
use smithay::utils::{Physical, Point, Scale};
use smithay::utils::{Logical, Point};
use smithay::wayland::compositor::{with_surface_tree_downward, TraversalAction};
use super::primary_gpu_texture::PrimaryGpuTextureRenderElement;
use super::renderer::NiriRenderer;
use crate::layout::{LayoutElementRenderElement, LayoutElementSnapshotRenderElements};
use super::BakedBuffer;
/// Renders elements from a surface tree, as well as saves them as textures into `storage`.
///
/// Saved textures are based at (0, 0) to facilitate later offscreening. This is why the location
/// argument is split into `location` and `offset`: the former is ignored for saved textures, but
/// the latter isn't (for things like popups).
#[allow(clippy::too_many_arguments)]
pub fn render_and_save_from_surface_tree<R: NiriRenderer>(
renderer: &mut R,
/// Renders elements from a surface tree as textures into `storage`.
pub fn render_snapshot_from_surface_tree(
renderer: &mut GlesRenderer,
surface: &WlSurface,
location: Point<f64, Physical>,
offset: Point<f64, Physical>,
scale: Scale<f64>,
alpha: f32,
kind: Kind,
elements: &mut Vec<LayoutElementRenderElement<R>>,
storage: &mut Option<&mut Vec<LayoutElementSnapshotRenderElements>>,
location: Point<i32, Logical>,
storage: &mut Vec<BakedBuffer<TextureBuffer<GlesTexture>>>,
) {
let _span = tracy_client::span!("render_and_save_from_surface_tree");
let base_pos = location;
let _span = tracy_client::span!("render_snapshot_from_surface_tree");
with_surface_tree_downward(
surface,
location + offset,
location,
|_, states, location| {
let mut location = *location;
let data = states.data_map.get::<RendererSurfaceStateUserData>();
@@ -44,7 +28,7 @@ pub fn render_and_save_from_surface_tree<R: NiriRenderer>(
let data = &*data.borrow();
if let Some(view) = data.view() {
location += view.offset.to_f64().to_physical(scale);
location += view.offset;
TraversalAction::DoChildren(location)
} else {
TraversalAction::SkipChildren
@@ -53,62 +37,44 @@ pub fn render_and_save_from_surface_tree<R: NiriRenderer>(
TraversalAction::SkipChildren
}
},
|surface, states, location| {
|_, states, location| {
let mut location = *location;
let data = states.data_map.get::<RendererSurfaceStateUserData>();
if let Some(data) = data {
if let Some(view) = data.borrow().view() {
location += view.offset.to_f64().to_physical(scale);
location += view.offset;
} else {
return;
}
let elem = match WaylandSurfaceRenderElement::from_surface(
renderer, surface, states, location, alpha, kind,
) {
Ok(elem) => elem,
Err(err) => {
warn!("failed to import surface: {err:?}");
return;
}
if let Err(err) = import_surface(renderer, states) {
warn!("failed to import surface: {err:?}");
return;
}
let data = data.borrow();
let view = data.view().unwrap();
let Some(texture) = data.texture::<GlesRenderer>(renderer.id()) else {
return;
};
elements.push(elem.into());
let buffer = TextureBuffer::from_texture(
renderer,
texture.clone(),
data.buffer_scale(),
data.buffer_transform(),
None,
);
if let Some(storage) = storage {
let renderer = renderer.as_gles_renderer();
// FIXME (possibly in Smithay): this causes a re-upload for shm textures.
if let Err(err) = import_surface(renderer, states) {
warn!("failed to import surface: {err:?}");
return;
}
let baked = BakedBuffer {
buffer,
location,
src: Some(view.src),
dst: Some(view.dst),
};
let data = data.borrow();
let view = data.view().unwrap();
let Some(texture) = data.texture::<GlesRenderer>(renderer.id()) else {
return;
};
let buffer = TextureBuffer::from_texture(
renderer,
texture.clone(),
data.buffer_scale(),
data.buffer_transform(),
None,
);
let elem = TextureRenderElement::from_texture_buffer(
location - base_pos,
&buffer,
Some(alpha),
Some(view.src),
Some(view.dst),
kind,
);
storage.push(PrimaryGpuTextureRenderElement(elem).into());
}
storage.push(baked);
}
},
|_, _, _| true,
+46 -65
View File
@@ -3,7 +3,8 @@ use std::cmp::{max, min};
use niri_config::{BlockOutFrom, WindowRule};
use smithay::backend::renderer::element::solid::{SolidColorBuffer, SolidColorRenderElement};
use smithay::backend::renderer::element::{Id, Kind};
use smithay::backend::renderer::element::{AsRenderElements, Id, Kind};
use smithay::backend::renderer::gles::GlesRenderer;
use smithay::desktop::space::SpaceElement as _;
use smithay::desktop::{PopupManager, Window};
use smithay::output::Output;
@@ -15,13 +16,11 @@ use smithay::wayland::compositor::{send_surface_state, with_states};
use smithay::wayland::shell::xdg::{SurfaceCachedState, ToplevelSurface};
use super::{ResolvedWindowRules, WindowRef};
use crate::layout::{
LayoutElement, LayoutElementRenderElement, LayoutElementSnapshotRenderElements,
};
use crate::layout::{LayoutElement, LayoutElementRenderElement, LayoutElementRenderSnapshot};
use crate::niri::WindowOffscreenId;
use crate::render_helpers::renderer::NiriRenderer;
use crate::render_helpers::surface::render_and_save_from_surface_tree;
use crate::render_helpers::{RenderSnapshot, RenderTarget};
use crate::render_helpers::surface::render_snapshot_from_surface_tree;
use crate::render_helpers::{BakedBuffer, RenderSnapshot, RenderTarget};
#[derive(Debug)]
pub struct Mapped {
@@ -43,7 +42,7 @@ pub struct Mapped {
block_out_buffer: RefCell<SolidColorBuffer>,
/// Snapshot of the last render for use in the close animation.
last_render: RefCell<RenderSnapshot<LayoutElementSnapshotRenderElements>>,
last_render: RefCell<LayoutElementRenderSnapshot>,
}
impl Mapped {
@@ -95,6 +94,42 @@ impl Mapped {
self.is_focused = is_focused;
self.need_to_recompute_rules = true;
}
pub fn render_and_store_snapshot(&self, renderer: &mut GlesRenderer) {
let mut snapshot = self.last_render.borrow_mut();
if !snapshot.contents.is_empty() {
return;
}
snapshot.contents.clear();
snapshot.blocked_out_contents.clear();
snapshot.block_out_from = self.rules.block_out_from;
let mut buffer = self.block_out_buffer.borrow_mut();
buffer.resize(self.window.geometry().size);
snapshot.blocked_out_contents = vec![BakedBuffer {
buffer: buffer.clone(),
location: Point::from((0, 0)),
src: None,
dst: None,
}];
let buf_pos = self.window.geometry().loc.upscale(-1);
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;
render_snapshot_from_surface_tree(
renderer,
popup.wl_surface(),
buf_pos + offset,
&mut snapshot.contents,
);
}
render_snapshot_from_surface_tree(renderer, surface, buf_pos, &mut snapshot.contents);
}
}
impl LayoutElement for Mapped {
@@ -131,10 +166,9 @@ impl LayoutElement for Mapped {
Some(BlockOutFrom::ScreenCapture) => target != RenderTarget::Output,
};
let mut buffer = self.block_out_buffer.borrow_mut();
buffer.resize(self.window.geometry().size);
if block_out {
let mut buffer = self.block_out_buffer.borrow_mut();
buffer.resize(self.window.geometry().size);
let elem = SolidColorRenderElement::from_buffer(
&buffer,
location.to_physical_precise_round(scale),
@@ -146,64 +180,11 @@ impl LayoutElement for Mapped {
} else {
let buf_pos = location - self.window.geometry().loc;
let buf_pos = buf_pos.to_physical_precise_round(scale);
let mut elements = vec![];
// If we're rendering for output, save into last_render.
let mut last_render = self.last_render.borrow_mut();
// FIXME: when preview-render is active, last render contents will never update.
let mut storage = if target == RenderTarget::Output {
last_render.contents.clear();
last_render.block_out_from = self.rules.block_out_from;
last_render.blocked_out_contents = vec![SolidColorRenderElement::from_buffer(
&buffer,
(0, 0),
scale,
alpha,
Kind::Unspecified,
)
.into()];
Some(&mut last_render.contents)
} else {
None
};
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)
.to_physical_precise_round(scale);
render_and_save_from_surface_tree(
renderer,
popup.wl_surface(),
buf_pos,
offset,
scale,
alpha,
Kind::Unspecified,
&mut elements,
&mut storage,
);
}
render_and_save_from_surface_tree(
renderer,
surface,
buf_pos,
Point::from((0., 0.)),
scale,
alpha,
Kind::Unspecified,
&mut elements,
&mut storage,
);
elements
self.window.render_elements(renderer, buf_pos, scale, alpha)
}
}
fn take_last_render(&self) -> RenderSnapshot<LayoutElementSnapshotRenderElements> {
fn take_last_render(&self) -> LayoutElementRenderSnapshot {
self.last_render.take()
}