gioser

tawasuyu/mirada

Sovereign tiling Wayland compositor + window manager in Rust, on Llimphi.

Rust 50Markdown 30TOML 211282 Definiciones

⎇ main vendor/smithay/src/backend/renderer/damage/mod.rs Rust · 788 líneas · 29.3 KB Historia Quién tocó qué crudo

chore: refresco desde el monorepo (pin tawasuyu.git v0.2.0) a91da3fea · Sergio ·

1//! Helper for effective output damage tracking
2//!
3//! # Why use this implementation
4//!
5//! The [`OutputDamageTracker`] in combination with the [`RenderElement`] trait
6//! can help you to reduce resource consumption by tracking what elements have
7//! been damaged and only redraw the damaged parts on an output.
8//!
9//! It does so by keeping track of the last used [`CommitCounter`] for all provided
10//! [`RenderElement`]s and queries the element for new damage on each call to [`render_output`](OutputDamageTracker::render_output) or [`damage_output`](OutputDamageTracker::damage_output).
11//!
12//! Additionally the damage tracker will automatically generate damage in the following situations:
13//! - Current geometry for elements entering the output
14//! - Current and last known geometry for moved elements (includes z-index changes)
15//! - Last known geometry for elements no longer present
16//!
17//! Elements fully occluded by opaque regions as defined by elements higher in the stack are skipped.
18//! The actual action taken by the damage tracker can be inspected from the returned [`RenderElementStates`].
19//!
20//! You can initialize it with a static output by using [`OutputDamageTracker::new`] or
21//! allow it to track a specific [`Output`] with [`OutputDamageTracker::from_output`].
22//!
23//! See the [`renderer::element`](crate::backend::renderer::element) module for more information
24//! about how to use [`RenderElement`].
25//!
26//! # How to use it
27//!
28//! ```no_run
29//! # use smithay::{
30//! #     backend::renderer::{Color32F, DebugFlags, Frame, ImportMem, Renderer, Texture, TextureFilter, sync::SyncPoint, test::{DummyRenderer, DummyFramebuffer}},
31//! #     utils::{Buffer, Physical, Rectangle, Size},
32//! # };
33//! use smithay::{
34//!     backend::{
35//!         allocator::Fourcc,
36//!         renderer::{
37//!             damage::OutputDamageTracker,
38//!             element::{
39//!                 Kind,
40//!                 memory::{MemoryRenderBuffer, MemoryRenderBufferRenderElement},
41//!             }
42//!         },
43//!     },
44//!     utils::{Point, Transform},
45//! };
46//! use std::time::{Duration, Instant};
47//!
48//! const WIDTH: i32 = 10;
49//! const HEIGHT: i32 = 10;
50//! # let mut renderer = DummyRenderer::default();
51//! # let mut framebuffer = DummyFramebuffer;
52//! # let buffer_age = 0;
53//!
54//! // Initialize a new damage tracker for a static output
55//! let mut damage_tracker = OutputDamageTracker::new((800, 600), 1.0, Transform::Normal);
56//!
57//! // Initialize a buffer to render
58//! let mut memory_buffer = MemoryRenderBuffer::new(Fourcc::Argb8888, (WIDTH, HEIGHT), 1, Transform::Normal, None);
59//!
60//! let mut last_update = Instant::now();
61//!
62//! loop {
63//!     let now = Instant::now();
64//!     if now.duration_since(last_update) >= Duration::from_secs(3) {
65//!         let mut render_context = memory_buffer.render();
66//!
67//!         render_context.draw(|_buffer| {
68//!             // Update the changed parts of the buffer
69//!
70//!             // Return the updated parts
71//!             Result::<_, ()>::Ok(vec![Rectangle::from_size((WIDTH, HEIGHT).into())])
72//!         });
73//!
74//!         last_update = now;
75//!     }
76//!
77//!     // Create a render element from the buffer
78//!     let location = Point::from((100.0, 100.0));
79//!     let render_element =
80//!         MemoryRenderBufferRenderElement::from_buffer(&mut renderer, location, &memory_buffer, None, None, None, Kind::Unspecified)
81//!         .expect("Failed to upload memory to gpu");
82//!
83//!     // Render the output
84//!     damage_tracker
85//!         .render_output(
86//!             &mut renderer,
87//!             &mut framebuffer,
88//!             buffer_age,
89//!             &[render_element],
90//!             [0.8, 0.8, 0.9, 1.0],
91//!         )
92//!         .expect("failed to render the output");
93//! }
94//! ```
95
96use std::{
97    collections::{HashMap, VecDeque},
98    ops::Range,
99};
100
101use indexmap::IndexMap;
102use smallvec::{smallvec, SmallVec};
103use tracing::{info_span, instrument, trace};
104
105use crate::{
106    backend::renderer::{element::RenderElementPresentationState, Frame},
107    output::{Output, OutputModeSource, OutputNoMode},
108    utils::{Buffer as BufferCoords, Physical, Rectangle, Scale, Size, Transform},
109};
110
111use super::{
112    element::{Element, Id, RenderElement, RenderElementState, RenderElementStates},
113    sync::SyncPoint,
114    utils::CommitCounter,
115    Color32F,
116};
117
118use super::{Renderer, Texture};
119
120mod shaper;
121
122use shaper::DamageShaper;
123
124const MAX_AGE: usize = 4;
125
126#[derive(Debug, Clone, Copy)]
127struct ElementInstanceState {
128    last_src: Rectangle<f64, BufferCoords>,
129    last_geometry: Rectangle<i32, Physical>,
130    last_transform: Transform,
131    last_alpha: f32,
132    last_z_index: usize,
133}
134
135impl ElementInstanceState {
136    #[inline]
137    fn matches(
138        &self,
139        src: Rectangle<f64, BufferCoords>,
140        geometry: Rectangle<i32, Physical>,
141        transform: Transform,
142        alpha: f32,
143        z_index: usize,
144    ) -> bool {
145        self.last_src == src
146            && self.last_geometry == geometry
147            && self.last_transform == transform
148            && self.last_alpha == alpha
149            && self.last_z_index == z_index
150    }
151}
152
153#[derive(Debug, Clone)]
154struct ElementState {
155    last_commit: CommitCounter,
156    last_instances: SmallVec<[ElementInstanceState; 1]>,
157}
158
159impl ElementState {
160    #[inline]
161    fn instance_matches(
162        &self,
163        src: Rectangle<f64, BufferCoords>,
164        geometry: Rectangle<i32, Physical>,
165        transform: Transform,
166        alpha: f32,
167        z_index: usize,
168    ) -> bool {
169        self.last_instances
170            .iter()
171            .any(|instance| instance.matches(src, geometry, transform, alpha, z_index))
172    }
173}
174
175#[derive(Debug, Default)]
176struct RendererState {
177    transform: Option<Transform>,
178    size: Option<Size<i32, Physical>>,
179    elements: IndexMap<Id, ElementState>,
180    old_damage: VecDeque<Vec<Rectangle<i32, Physical>>>,
181    opaque_regions: Vec<Rectangle<i32, Physical>>,
182    clear_color: Option<Color32F>,
183}
184
185/// Damage tracker for a single output
186#[derive(Debug)]
187pub struct OutputDamageTracker {
188    mode: OutputModeSource,
189    last_state: RendererState,
190    damage_shaper: DamageShaper,
191    damage: Vec<Rectangle<i32, Physical>>,
192    element_damage: Vec<Rectangle<i32, Physical>>,
193    opaque_regions: Vec<Rectangle<i32, Physical>>,
194    opaque_regions_index: Vec<Range<usize>>,
195    element_opaque_regions: Vec<Rectangle<i32, Physical>>,
196    element_visible_area_workhouse: Vec<Rectangle<i32, Physical>>,
197    span: tracing::Span,
198}
199
200/// Errors thrown by [`OutputDamageTracker::render_output`]
201#[derive(thiserror::Error)]
202pub enum Error<E: std::error::Error> {
203    /// The provided [`Renderer`] returned an error
204    #[error(transparent)]
205    Rendering(E),
206    /// The given [`Output`] has no mode set
207    #[error(transparent)]
208    OutputNoMode(#[from] OutputNoMode),
209}
210
211/// Represents the result from rendering the output
212#[derive(Debug)]
213pub struct RenderOutputResult<'a> {
214    /// Holds the sync point of the rendering operation
215    pub sync: SyncPoint,
216    /// Holds the damage from the rendering operation
217    pub damage: Option<&'a Vec<Rectangle<i32, Physical>>>,
218    /// Holds the render element states
219    pub states: RenderElementStates,
220}
221
222impl RenderOutputResult<'_> {
223    fn skipped(states: RenderElementStates) -> Self {
224        Self {
225            sync: SyncPoint::signaled(),
226            damage: None,
227            states,
228        }
229    }
230}
231
232impl<E: std::error::Error> std::fmt::Debug for Error<E> {
233    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
234        match self {
235            Error::Rendering(err) => std::fmt::Debug::fmt(err, f),
236            Error::OutputNoMode(err) => std::fmt::Debug::fmt(err, f),
237        }
238    }
239}
240
241impl OutputDamageTracker {
242    /// Initialize a static [`OutputDamageTracker`]
243    pub fn new(
244        size: impl Into<Size<i32, Physical>>,
245        scale: impl Into<Scale<f64>>,
246        transform: Transform,
247    ) -> Self {
248        Self {
249            mode: OutputModeSource::Static {
250                size: size.into(),
251                scale: scale.into(),
252                transform,
253            },
254            last_state: Default::default(),
255            damage_shaper: Default::default(),
256            damage: Default::default(),
257            element_damage: Default::default(),
258            opaque_regions: Default::default(),
259            opaque_regions_index: Default::default(),
260            element_opaque_regions: Default::default(),
261            element_visible_area_workhouse: Default::default(),
262            span: info_span!("renderer_damage"),
263        }
264    }
265
266    /// Initialize a new [`OutputDamageTracker`] from an [`Output`]
267    ///
268    /// The renderer will keep track of changes to the [`Output`]
269    /// and handle size and scaling changes automatically on the
270    /// next call to [`render_output`](OutputDamageTracker::render_output)
271    pub fn from_output(output: &Output) -> Self {
272        Self {
273            mode: OutputModeSource::Auto(output.clone()),
274            damage_shaper: Default::default(),
275            damage: Default::default(),
276            element_damage: Default::default(),
277            opaque_regions: Default::default(),
278            opaque_regions_index: Default::default(),
279            element_opaque_regions: Default::default(),
280            element_visible_area_workhouse: Default::default(),
281            last_state: Default::default(),
282            span: info_span!("renderer_damage", output = output.name()),
283        }
284    }
285
286    /// Initialize a new [`OutputDamageTracker`] from an [`OutputModeSource`].
287    ///
288    /// This should only be used when trying to support both static and automatic output mode
289    /// sources. For known modes use [`OutputDamageTracker::new`] or
290    /// [`OutputDamageTracker::from_output`] instead.
291    pub fn from_mode_source(output_mode_source: impl Into<OutputModeSource>) -> Self {
292        Self {
293            mode: output_mode_source.into(),
294            span: info_span!("render_damage"),
295            damage_shaper: Default::default(),
296            damage: Default::default(),
297            element_damage: Default::default(),
298            element_opaque_regions: Default::default(),
299            opaque_regions: Default::default(),
300            opaque_regions_index: Default::default(),
301            element_visible_area_workhouse: Default::default(),
302            last_state: Default::default(),
303        }
304    }
305
306    /// Get the [`OutputModeSource`] of the [`OutputDamageTracker`]
307    pub fn mode(&self) -> &OutputModeSource {
308        &self.mode
309    }
310
311    /// Render this output with the provided [`Renderer`]
312    ///
313    /// - `elements` for this output in front-to-back order
314    #[instrument(level = "trace", parent = &self.span, skip(renderer, framebuffer, elements, clear_color))]
315    #[profiling::function]
316    pub fn render_output<E, R>(
317        &mut self,
318        renderer: &mut R,
319        framebuffer: &mut R::Framebuffer<'_>,
320        age: usize,
321        elements: &[E],
322        clear_color: impl Into<Color32F>,
323    ) -> Result<RenderOutputResult<'_>, Error<R::Error>>
324    where
325        E: RenderElement<R>,
326        R: Renderer,
327        R::TextureId: Texture,
328    {
329        let clear_color = clear_color.into();
330        let (output_size, output_scale, output_transform) =
331            std::convert::TryInto::<(Size<i32, Physical>, Scale<f64>, Transform)>::try_into(&self.mode)?;
332
333        // Output transform is specified in surface-rotation, so inversion gives us the
334        // render transform for the output itself.
335        let output_transform = output_transform.invert();
336
337        // We have to apply to output transform to the output size so that the intersection
338        // tests in damage_output_internal produces the correct results and do not crop
339        // damage with the wrong size
340        let output_geo = Rectangle::from_size(output_transform.transform_size(output_size));
341
342        // This will hold all the damage we need for this rendering step
343        let mut render_elements: Vec<&E> = Vec::with_capacity(elements.len());
344        let states = self.damage_output_internal(
345            age,
346            elements,
347            output_scale,
348            output_transform,
349            output_geo,
350            Some(clear_color),
351            &mut render_elements,
352        );
353
354        if self.damage.is_empty() {
355            trace!("no damage, skipping rendering");
356            return Ok(RenderOutputResult::skipped(states));
357        }
358
359        trace!(
360            "rendering with damage {:?} and opaque regions {:?}",
361            self.damage,
362            self.opaque_regions
363        );
364
365        let render_res = (|| {
366            // we have to take the element damage to be able to move it around
367            let mut element_damage = std::mem::take(&mut self.element_damage);
368            let mut element_opaque_regions = std::mem::take(&mut self.element_opaque_regions);
369            let mut frame = renderer.render(framebuffer, output_size, output_transform)?;
370
371            element_damage.clear();
372            element_damage.extend_from_slice(&self.damage);
373            element_damage =
374                Rectangle::subtract_rects_many_in_place(element_damage, self.opaque_regions.iter().copied());
375
376            trace!("clearing damage {:?}", element_damage);
377            frame.clear(clear_color, &element_damage)?;
378
379            for (z_index, element) in render_elements.iter().rev().enumerate() {
380                let element_id = element.id();
381                let element_geometry = element.geometry(output_scale);
382
383                element_damage.clear();
384                element_damage.extend(
385                    self.damage
386                        .iter()
387                        .filter_map(|d| d.intersection(element_geometry)),
388                );
389
390                let element_opaque_regions_range =
391                    self.opaque_regions_index.iter().rev().nth(z_index).unwrap();
392                element_damage = Rectangle::subtract_rects_many_in_place(
393                    element_damage,
394                    self.opaque_regions[..element_opaque_regions_range.start]
395                        .iter()
396                        .copied(),
397                );
398                element_damage.iter_mut().for_each(|d| {
399                    d.loc -= element_geometry.loc;
400                });
401
402                if element_damage.is_empty() {
403                    trace!(
404                        "skipping rendering element {:?} with geometry {:?}, no damage",
405                        element_id,
406                        element_geometry
407                    );
408                    continue;
409                }
410
411                element_opaque_regions.clear();
412                element_opaque_regions.extend(
413                    self.opaque_regions[element_opaque_regions_range.start..element_opaque_regions_range.end]
414                        .iter()
415                        .copied()
416                        .map(|mut rect| {
417                            rect.loc -= element_geometry.loc;
418                            rect
419                        }),
420                );
421
422                trace!(
423                    "rendering element {:?} with geometry {:?} and damage {:?}",
424                    element_id,
425                    element_geometry,
426                    element_damage,
427                );
428
429                element.draw(
430                    &mut frame,
431                    element.src(),
432                    element_geometry,
433                    &element_damage,
434                    &element_opaque_regions,
435                )?;
436            }
437
438            // return the element damage so that we can re-use the allocation
439            std::mem::swap(&mut self.element_damage, &mut element_damage);
440            std::mem::swap(&mut self.element_opaque_regions, &mut element_opaque_regions);
441            frame.finish()
442        })();
443
444        match render_res {
445            Ok(sync) => Ok(RenderOutputResult {
446                sync,
447                damage: Some(&self.damage),
448                states,
449            }),
450            Err(err) => {
451                // if the rendering errors on us, we need to be prepared, that this whole buffer was partially updated and thus now unusable.
452                // thus clean our old states before returning
453                self.last_state = Default::default();
454                Err(Error::Rendering(err))
455            }
456        }
457    }
458
459    /// Damage this output and return the damage without actually rendering the difference
460    ///
461    /// - `elements` for this output in front-to-back order
462    #[instrument(level = "trace", parent = &self.span, skip(elements))]
463    #[profiling::function]
464    pub fn damage_output<'a, 'e, E>(
465        &'a mut self,
466        age: usize,
467        elements: &'e [E],
468    ) -> Result<(Option<&'a Vec<Rectangle<i32, Physical>>>, RenderElementStates), OutputNoMode>
469    where
470        E: Element,
471    {
472        let (output_size, output_scale, output_transform) = self.mode.clone().try_into()?;
473
474        // Output transform is specified in surface-rotation, so inversion gives us the
475        // render transform for the output itself.
476        let output_transform = output_transform.invert();
477
478        // We have to apply to output transform to the output size so that the intersection
479        // tests in damage_output_internal produces the correct results and do not crop
480        // damage with the wrong size
481        let output_geo = Rectangle::from_size(output_transform.transform_size(output_size));
482
483        let mut render_elements: Vec<&E> = Vec::with_capacity(elements.len());
484        let states = self.damage_output_internal(
485            age,
486            elements,
487            output_scale,
488            output_transform,
489            output_geo,
490            self.last_state.clear_color,
491            &mut render_elements,
492        );
493
494        if self.damage.is_empty() {
495            Ok((None, states))
496        } else {
497            Ok((Some(&self.damage), states))
498        }
499    }
500
501    #[allow(clippy::too_many_arguments)]
502    #[profiling::function]
503    fn damage_output_internal<'a, E>(
504        &mut self,
505        age: usize,
506        elements: &'a [E],
507        output_scale: Scale<f64>,
508        output_transform: Transform,
509        output_geo: Rectangle<i32, Physical>,
510        clear_color: Option<Color32F>,
511        render_elements: &mut Vec<&'a E>,
512    ) -> RenderElementStates
513    where
514        E: Element,
515    {
516        self.damage.clear();
517        self.opaque_regions.clear();
518        self.opaque_regions_index.clear();
519
520        let mut element_render_states = RenderElementStates {
521            states: HashMap::with_capacity(elements.len()),
522        };
523
524        // we have to take the element damage to be able to move it around
525        let mut element_damage = std::mem::take(&mut self.element_damage);
526
527        let mut element_visible_area_workhouse = std::mem::take(&mut self.element_visible_area_workhouse);
528        for element in elements.iter() {
529            let element_id = element.id();
530            let element_loc = element.geometry(output_scale).loc;
531
532            // First test if the element overlaps with the output
533            // if not we can skip it
534            let element_output_geometry = match element.geometry(output_scale).intersection(output_geo) {
535                Some(geo) => geo,
536                None => continue,
537            };
538
539            // Then test if the element is completely hidden behind opaque regions
540            element_visible_area_workhouse.clear();
541            element_visible_area_workhouse.push(element_output_geometry);
542            element_visible_area_workhouse = Rectangle::subtract_rects_many_in_place(
543                element_visible_area_workhouse,
544                self.opaque_regions.iter().copied(),
545            );
546            let element_visible_area = element_visible_area_workhouse
547                .iter()
548                .fold(0usize, |acc, item| acc + (item.size.w * item.size.h) as usize);
549
550            // No need to draw a completely hidden element
551            if element_visible_area == 0 {
552                // We allow multiple instance of a single element, so do not
553                // override the state if we already have one
554                if !element_render_states.states.contains_key(element_id) {
555                    element_render_states
556                        .states
557                        .insert(element_id.clone(), RenderElementState::skipped());
558                }
559                continue;
560            }
561
562            let element_output_damage = element
563                .damage_since(
564                    output_scale,
565                    self.last_state.elements.get(element_id).map(|s| s.last_commit),
566                )
567                .into_iter()
568                .map(|mut d| {
569                    d.loc += element_loc;
570                    d
571                })
572                .filter_map(|geo| geo.intersection(output_geo));
573            self.damage.extend(element_output_damage);
574
575            let element_opaque_regions_start_index = self.opaque_regions.len();
576            let element_opaque_regions = element
577                .opaque_regions(output_scale)
578                .into_iter()
579                .map(|mut region| {
580                    region.loc += element_loc;
581                    region
582                })
583                .filter_map(|geo| geo.intersection(output_geo));
584            self.opaque_regions.extend(element_opaque_regions);
585            let element_opaque_regions_end_index = self.opaque_regions.len();
586            self.opaque_regions_index
587                .push(element_opaque_regions_start_index..element_opaque_regions_end_index);
588            render_elements.push(element);
589
590            if let Some(state) = element_render_states.states.get_mut(element_id) {
591                if matches!(state.presentation_state, RenderElementPresentationState::Skipped) {
592                    *state = RenderElementState::rendered(element_visible_area);
593                } else {
594                    state.visible_area += element_visible_area;
595                }
596            } else {
597                element_render_states.states.insert(
598                    element_id.clone(),
599                    RenderElementState::rendered(element_visible_area),
600                );
601            }
602        }
603        std::mem::swap(
604            &mut self.element_visible_area_workhouse,
605            &mut element_visible_area_workhouse,
606        );
607
608        // add the damage for elements gone that are not covered an opaque region
609        let elements_gone = self.last_state.elements.iter().filter(|(id, _)| {
610            element_render_states
611                .states
612                .get(id)
613                .map(|state| state.presentation_state == RenderElementPresentationState::Skipped)
614                .unwrap_or(true)
615        });
616
617        for (_, state) in elements_gone {
618            self.damage.extend(
619                state
620                    .last_instances
621                    .iter()
622                    .filter_map(|i| i.last_geometry.intersection(output_geo)),
623            );
624        }
625
626        // if the element has been moved or it's alpha or z index changed, damage it
627        for (z_index, element) in render_elements.iter().enumerate() {
628            let element_src = element.src();
629            let element_geometry = element.geometry(output_scale);
630            let element_transform = element.transform();
631            let element_alpha = element.alpha();
632            let element_last_state = self.last_state.elements.get(element.id());
633
634            if element_last_state
635                .map(|s| {
636                    !s.instance_matches(
637                        element_src,
638                        element_geometry,
639                        element_transform,
640                        element_alpha,
641                        z_index,
642                    )
643                })
644                .unwrap_or(true)
645            {
646                if let Some(intersection) = element_geometry.intersection(output_geo) {
647                    self.damage.push(intersection);
648                }
649                if let Some(state) = element_last_state {
650                    self.damage.extend(
651                        state
652                            .last_instances
653                            .iter()
654                            .filter_map(|i| i.last_geometry.intersection(output_geo)),
655                    );
656                }
657            }
658        }
659
660        // damage regions no longer covered by opaque regions
661        element_damage.clear();
662        element_damage.extend_from_slice(&self.last_state.opaque_regions);
663        element_damage =
664            Rectangle::subtract_rects_many_in_place(element_damage, self.opaque_regions.iter().copied());
665        self.damage.extend_from_slice(&element_damage);
666
667        // we no longer need the element damage, return it so that we can
668        // re-use its allocation next time
669        std::mem::swap(&mut self.element_damage, &mut element_damage);
670
671        if self.last_state.size != Some(output_geo.size)
672            || self.last_state.transform != Some(output_transform)
673            || self.last_state.clear_color != clear_color
674        {
675            // The output geometry or transform changed, so just damage everything
676            trace!(
677                previous_geometry = ?self.last_state.size,
678                current_geometry = ?output_geo.size,
679                previous_transform = ?self.last_state.transform,
680                current_transform = ?output_transform,
681                previous_clear_color = ?self.last_state.clear_color,
682                current_clear_color = ?clear_color,
683                "Output geometry, transform or clear color changed, damaging whole output geometry");
684            self.damage.clear();
685            self.damage.push(output_geo);
686        }
687
688        // That is all completely new damage, which we need to store for subsequent renders
689        let mut new_damage = self.damage.clone();
690        new_damage.shrink_to_fit();
691
692        // We now add old damage states, if we have an age value
693        if age > 0 && self.last_state.old_damage.len() >= age {
694            trace!("age of {} recent enough, using old damage", age);
695            // We do not need even older states anymore
696            self.last_state.old_damage.truncate(age);
697            self.damage
698                .extend(self.last_state.old_damage.iter().take(age - 1).flatten().copied());
699        } else {
700            trace!(
701                "no old damage available, re-render everything. age: {} old_damage len: {}",
702                age,
703                self.last_state.old_damage.len(),
704            );
705            // we still truncate the old damage to prevent growing
706            // indefinitely in case we are continuously called with
707            // an age of 0
708            self.last_state.old_damage.truncate(MAX_AGE);
709            // just damage everything, if we have no damage
710            self.damage.clear();
711            self.damage.push(output_geo);
712        };
713
714        // Optimize the damage for rendering
715
716        // Clamp all rectangles to the bounds removing the ones without intersection.
717        self.damage.retain_mut(|rect| {
718            if let Some(intersected) = rect.intersection(output_geo) {
719                *rect = intersected;
720                true
721            } else {
722                false
723            }
724        });
725
726        self.damage_shaper.shape_damage(&mut self.damage);
727
728        if self.damage.is_empty() {
729            trace!("nothing damaged, exiting early");
730            return element_render_states;
731        }
732
733        let mut new_elements_state = std::mem::take(&mut self.last_state.elements);
734        new_elements_state.clear();
735        new_elements_state.reserve(render_elements.len());
736        let new_elements_state =
737            render_elements
738                .iter()
739                .enumerate()
740                .fold(new_elements_state, |mut map, (z_index, elem)| {
741                    let id = elem.id();
742                    let elem_src = elem.src();
743                    let elem_alpha = elem.alpha();
744                    let elem_geometry = elem.geometry(output_scale);
745                    let elem_transform = elem.transform();
746
747                    if let Some(state) = map.get_mut(id) {
748                        state.last_instances.push(ElementInstanceState {
749                            last_src: elem_src,
750                            last_geometry: elem_geometry,
751                            last_transform: elem_transform,
752                            last_alpha: elem_alpha,
753                            last_z_index: z_index,
754                        });
755                    } else {
756                        let current_commit = elem.current_commit();
757                        map.insert(
758                            id.clone(),
759                            ElementState {
760                                last_commit: current_commit,
761                                last_instances: smallvec![ElementInstanceState {
762                                    last_src: elem_src,
763                                    last_geometry: elem_geometry,
764                                    last_transform: elem_transform,
765                                    last_alpha: elem_alpha,
766                                    last_z_index: z_index,
767                                }],
768                            },
769                        );
770                    }
771
772                    map
773                });
774
775        self.last_state.size = Some(output_geo.size);
776        self.last_state.transform = Some(output_transform);
777        self.last_state.elements = new_elements_state;
778        self.last_state.old_damage.push_front(new_damage);
779        self.last_state.opaque_regions.clear();
780        self.last_state
781            .opaque_regions
782            .extend(self.opaque_regions.iter().copied());
783        self.last_state.opaque_regions.shrink_to_fit();
784        self.last_state.clear_color = clear_color;
785
786        element_render_states
787    }
788}