chore: refresco desde el monorepo (pin tawasuyu.git v0.2.0)
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}