Skip to main content

incular_rendering/
compositor.rs

1use crate::display_list::{BackdropKey, DisplayList, PaintCommand, SurfacePartitionId};
2use crate::effects::{
3    BlendMode, ColorFilter, DropShadowEffect, GaussianBlur, blur_bounds, drop_shadow_bounds,
4};
5use crate::geometry::{RRect, union_rect};
6use crate::gradients::Brush;
7use crate::paint::FillRule;
8use crate::paths::{Path, ellipse_as_path, fallback_tolerance, rect_as_path, rrect_as_path};
9use incular_core::finite_or_zero;
10use incular_core::{Arena, ArenaId, Offset, Rect, Size, Transform};
11use std::{
12    any::{Any, TypeId},
13    cell::RefCell,
14    collections::HashMap,
15    rc::Rc,
16    sync::Arc,
17};
18
19/// Opaque retained compositor identity. It uses the core generational arena so
20/// an ID for a removed layer can never select a later, unrelated layer.
21#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
22pub struct LayerId(ArenaId);
23
24impl LayerId {
25    /// Stable numeric form (`generation << 32 | index`) for diagnostics that
26    /// must name a layer without exposing the arena type.
27    #[must_use]
28    pub const fn raw(self) -> u64 {
29        ((self.0.generation() as u64) << 32) | self.0.index() as u64
30    }
31}
32
33/// Canonical opacity representation shared by widgets, retained layers, and
34/// GPU lowering. Non-finite values become transparent and finite values are
35/// clamped to the physically meaningful range.
36#[must_use]
37pub fn normalize_opacity(alpha: f32) -> f32 {
38    if alpha.is_finite() {
39        alpha.clamp(0., 1.)
40    } else {
41        0.
42    }
43}
44
45#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
46pub struct CompositorDiagnostics {
47    pub layers: u64,
48    pub picture_layers_repainted: u64,
49    pub transform_updates: u64,
50    pub clip_updates: u64,
51    pub layers_culled: u64,
52    pub opacity_updates: u64,
53    /// Leader-publication passes executed (every `flatten` and every
54    /// pre-semantics republish runs one, unconditionally).
55    pub leader_publish_passes: u64,
56    /// Leaders successfully published across those passes (cumulative, so
57    /// per-pass work is the delta over a known frame window).
58    pub leaders_published: u64,
59}
60
61/// A picture placement observed while flattening. All rectangles here are in
62/// logical coordinates; this is intentionally useful for CPU-side scene tests
63/// and diagnostics without involving a surface or DPI scale factor.
64#[derive(Clone, Copy, Debug, PartialEq)]
65pub struct FlattenedPicture {
66    pub layer: LayerId,
67    pub local_bounds: Rect,
68    pub world_bounds: Rect,
69    pub active_clip: Option<Rect>,
70}
71
72/// A type-erased application annotation carried by an annotated compositing
73/// region. The value remains strongly typed at the API boundary: consumers
74/// query it with [`LayerTree::find_annotation`] and receive only values of the
75/// requested concrete type. Native platform adapters can therefore project
76/// their own typed metadata without making the renderer depend on a platform
77/// crate.
78#[derive(Clone)]
79pub struct Annotation {
80    value: Rc<dyn Any>,
81    type_id: TypeId,
82}
83impl Annotation {
84    #[must_use]
85    pub fn new<T: Any>(value: T) -> Self {
86        Self {
87            value: Rc::new(value),
88            type_id: TypeId::of::<T>(),
89        }
90    }
91    #[must_use]
92    pub const fn value_type_id(&self) -> TypeId {
93        self.type_id
94    }
95    #[must_use]
96    pub fn downcast_ref<T: Any>(&self) -> Option<&T> {
97        self.value.downcast_ref::<T>()
98    }
99    #[must_use]
100    pub fn cloned<T: Any + Clone>(&self) -> Option<T> {
101        self.downcast_ref::<T>().cloned()
102    }
103}
104impl std::fmt::Debug for Annotation {
105    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
106        formatter
107            .debug_struct("Annotation")
108            .field("type_id", &self.type_id)
109            .finish_non_exhaustive()
110    }
111}
112impl PartialEq for Annotation {
113    fn eq(&self, other: &Self) -> bool {
114        self.type_id == other.type_id && Rc::ptr_eq(&self.value, &other.value)
115    }
116}
117
118/// Normalized anchor used by leader/follower layers. `(-1, -1)` is the
119/// top-left corner, `(0, 0)` the center, and `(1, 1)` the bottom-right.
120#[derive(Clone, Copy, Debug, Default, PartialEq)]
121pub struct LayerAnchor {
122    pub x: f32,
123    pub y: f32,
124}
125impl LayerAnchor {
126    pub const TOP_LEFT: Self = Self { x: -1., y: -1. };
127
128    #[must_use]
129    pub fn new(x: f32, y: f32) -> Self {
130        Self {
131            x: finite_or_zero(x),
132            y: finite_or_zero(y),
133        }
134    }
135    #[must_use]
136    pub const fn along_size(self, size: Size) -> Offset {
137        Offset::new(
138            size.width * (self.x + 1.) * 0.5,
139            size.height * (self.y + 1.) * 0.5,
140        )
141    }
142}
143
144/// World-space bounding box of a clip layer's shape, or `None` when the
145/// shape is empty and clips everything away. Conservative for rounded
146/// corners: the box covers the full rect. Pass only clip layers; any
147/// other kind also reports `None` so a misuse culls rather than leaks.
148fn clip_world_bounds(kind: &LayerKind, world_transform: Transform) -> Option<Rect> {
149    match kind {
150        LayerKind::ClipRect { rect, .. } => Some(world_transform.transform_rect_bbox(*rect)),
151        LayerKind::ClipRRect { rrect, .. } => Some(world_transform.transform_rect_bbox(rrect.rect)),
152        LayerKind::ClipOval { rect, .. } => Some(world_transform.transform_rect_bbox(*rect)),
153        LayerKind::ClipPath { path, .. } => path
154            .bounds()
155            .map(|bounds| world_transform.transform_rect_bbox(bounds)),
156        _ => None,
157    }
158}
159
160/// What shape representations stay exact under a world transform.
161/// Misclassification only costs a path fallback, never correctness: the
162/// fallback path is exact for every affine map.
163#[derive(Clone, Copy, Debug, PartialEq)]
164enum ClipSpace {
165    Translation,
166    Scale { x: f32, y: f32 },
167    General,
168}
169
170fn clip_space(world: Transform) -> ClipSpace {
171    let [a, b, c, d, _, _] = world.to_kurbo().as_coeffs();
172    if b == 0. && c == 0. {
173        if a == 1. && d == 1. {
174            ClipSpace::Translation
175        } else {
176            ClipSpace::Scale {
177                x: a as f32,
178                y: d as f32,
179            }
180        }
181    } else {
182        ClipSpace::General
183    }
184}
185
186/// World-space point where a follower's anchor must land, resolved entirely
187/// in leader space. The compositor and the widget tree share this half of
188/// the projection; each side then inverts it into its own retained frame
189/// (layer world vs. render parent), which is why the callers still differ.
190#[must_use]
191pub fn resolve_follower_target(
192    leader_transform: Transform,
193    leader_size: Size,
194    target_anchor: LayerAnchor,
195    offset: Offset,
196) -> Offset {
197    let target_point = leader_transform.transform_point(target_anchor.along_size(leader_size));
198    let leader_origin = leader_transform.transform_point(Offset::ZERO);
199    target_point + (leader_transform.transform_point(offset) - leader_origin)
200}
201
202/// World-resolved fallback path retained per clip layer. At most one
203/// lives on a layer: geometry updates replace it through their own
204/// update path, removal drops it with the layer, and no global map or
205/// transform history exists. Tolerance is a pure function of the world,
206/// so world equality implies tolerance equality.
207#[derive(Clone, Debug)]
208struct ResolvedClipPath {
209    world: Transform,
210    path: Arc<Path>,
211}
212
213#[derive(Clone, Debug)]
214struct LeaderData {
215    owner: PublisherIdentity,
216    transform: Transform,
217    size: Size,
218    generation: u64,
219}
220
221/// One reachable leader layer snapshotted for the resolution pass, in
222/// paint order. The snapshot carries everything publication needs so the
223/// pass never holds layer borrows across recursive resolution.
224struct LeaderRecord {
225    id: LayerId,
226    link: LayerLink,
227    size: Size,
228    publisher: PublisherIdentity,
229    generation: u64,
230}
231
232#[derive(Clone, Copy, PartialEq, Eq)]
233enum PublishState {
234    InProgress,
235    Published,
236    Unresolvable,
237}
238
239/// Opaque publication owner minted per leader layer. A link may be shared
240/// across layer trees whose arena indices overlap, so a tree-local
241/// `LayerId` cannot name the publisher; instead each leader layer owns one
242/// allocation whose identity is inseparable from the owner's lifetime.
243/// Compare only with `same_owner`, never by detached address.
244#[derive(Clone, Debug)]
245pub struct PublisherIdentity(Rc<()>);
246impl PublisherIdentity {
247    pub(crate) fn new() -> Self {
248        Self(Rc::new(()))
249    }
250    /// Whether both tokens name the same owning leader layer. Clones of
251    /// one token always agree, including across layer trees.
252    #[must_use]
253    pub fn same_owner(&self, other: &Self) -> bool {
254        Rc::ptr_eq(&self.0, &other.0)
255    }
256}
257
258/// Shared identity used to resolve a composited target and any number of
259/// followers across otherwise unrelated retained subtrees.
260#[derive(Clone)]
261pub struct LayerLink(Rc<RefCell<Option<LeaderData>>>);
262impl Default for LayerLink {
263    fn default() -> Self {
264        Self::new()
265    }
266}
267impl std::fmt::Debug for LayerLink {
268    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
269        formatter
270            .debug_struct("LayerLink")
271            .field("linked", &self.is_linked())
272            .finish()
273    }
274}
275impl PartialEq for LayerLink {
276    fn eq(&self, other: &Self) -> bool {
277        Rc::ptr_eq(&self.0, &other.0)
278    }
279}
280impl LayerLink {
281    #[must_use]
282    pub fn new() -> Self {
283        Self(Rc::new(RefCell::new(None)))
284    }
285    #[must_use]
286    pub fn is_linked(&self) -> bool {
287        self.0.borrow().is_some()
288    }
289    #[must_use]
290    pub fn leader_size(&self) -> Option<Size> {
291        self.0.borrow().as_ref().map(|leader| leader.size)
292    }
293    #[must_use]
294    pub fn leader_transform(&self) -> Option<Transform> {
295        self.0.borrow().as_ref().map(|leader| leader.transform)
296    }
297    #[must_use]
298    pub fn leader_generation(&self) -> u64 {
299        self.0
300            .borrow()
301            .as_ref()
302            .map_or(0, |leader| leader.generation)
303    }
304    /// Releases the publication only when `owner` made it. Removing or
305    /// rebinding any other leader layer must not disturb the winner.
306    fn clear_if_owned_by(&self, owner: &PublisherIdentity) {
307        let mut state = self.0.borrow_mut();
308        if state
309            .as_ref()
310            .is_some_and(|leader| leader.owner.same_owner(owner))
311        {
312            *state = None;
313        }
314    }
315    /// Unconditional frame reset. The resolution pass republishes every live
316    /// leader right after, so no reader observes the gap.
317    fn clear_publication(&self) {
318        *self.0.borrow_mut() = None;
319    }
320    fn publish(&self, owner: PublisherIdentity, transform: Transform, size: Size, generation: u64) {
321        // One target per link: the first resolvable leader in paint order
322        // wins. Keeping that rule deterministic is preferable to allowing
323        // a later subtree to move an already published follower.
324        let mut state = self.0.borrow_mut();
325        if state.is_none() {
326            *state = Some(LeaderData {
327                owner,
328                transform,
329                size,
330                generation,
331            });
332        }
333    }
334}
335
336/// Annotation placement captured by the most recent layer flatten. A `None`
337/// bounds value represents `sized: false`, which participates at every point
338/// in the scene subject to ancestor clipping.
339#[derive(Clone, Debug, PartialEq)]
340pub struct FlattenedAnnotation {
341    pub annotation: Annotation,
342    pub world_bounds: Option<Rect>,
343}
344
345#[derive(Clone, Debug)]
346pub enum LayerKind {
347    Picture {
348        display_list: Arc<DisplayList>,
349        bounds: Rect,
350    },
351    Transform {
352        transform: Transform,
353    },
354    ClipRect {
355        rect: Rect,
356    },
357    ClipRRect {
358        rrect: RRect,
359    },
360    ClipOval {
361        rect: Rect,
362    },
363    ClipPath {
364        path: Arc<Path>,
365        fill_rule: FillRule,
366    },
367    Opacity {
368        alpha: f32,
369    },
370    Blur {
371        blur: GaussianBlur,
372    },
373    DropShadow {
374        shadow: DropShadowEffect,
375    },
376    // Rare large payloads are boxed so every layer slot stays small.
377    ColorFilter {
378        filter: Box<ColorFilter>,
379    },
380    Blend {
381        mode: BlendMode,
382    },
383    ShaderMask {
384        shader: Box<Brush>,
385        blend_mode: BlendMode,
386        mask_size: Size,
387        mask_transform: Box<Transform>,
388    },
389    BackdropFilter {
390        blur: GaussianBlur,
391        blend_mode: BlendMode,
392        enabled: bool,
393        /// Flutter's `ImageFilter.matrix`: when set, the backdrop is
394        /// transformed by it instead of blurred.
395        matrix: Option<Transform>,
396        /// Flutter's `BackdropKey`: the backdrop shared with other stages.
397        backdrop_key: Option<BackdropKey>,
398    },
399    AnnotatedRegion {
400        annotation: Annotation,
401        sized: bool,
402        size: Size,
403    },
404    Leader {
405        link: LayerLink,
406        size: Size,
407        publisher: PublisherIdentity,
408    },
409    Follower(FollowerLayer),
410}
411
412/// Retained configuration of a layer that tracks a [`LayerLink`] leader.
413#[derive(Clone, Debug, PartialEq)]
414pub struct FollowerLayer {
415    pub link: LayerLink,
416    pub show_when_unlinked: bool,
417    pub offset: Offset,
418    pub target_anchor: LayerAnchor,
419    pub follower_anchor: LayerAnchor,
420    pub size: Size,
421}
422
423#[derive(Clone, Debug)]
424struct Layer {
425    kind: LayerKind,
426    children: Vec<LayerId>,
427    surface_partition: Option<SurfacePartitionId>,
428    generation: u64,
429    /// Owned fallback-path cache for clip layers. `LayerKind` describes
430    /// requested behavior only; resolved state lives here so removal
431    /// releases it and geometry updates invalidate it in one place.
432    clip_cache: Option<Box<ResolvedClipPath>>,
433    /// The container listing this layer, so a change can reset the cull
434    /// bounds of every layer above it.
435    parent: Option<LayerId>,
436    /// Memoized local bounds of every picture in this subtree, used to cull
437    /// whole subtrees during flattening.
438    cull: CullBounds,
439}
440
441/// Local-space bounds of the pictures under one layer. A layer with
442/// [`CullBounds::Known`] has known bounds on all of its descendants, so a
443/// reset walks up only until it reaches a layer that is already unknown.
444#[derive(Clone, Copy, Debug)]
445enum CullBounds {
446    Unknown,
447    Known {
448        bounds: Option<Rect>,
449        /// False when skipping the subtree would change more than its
450        /// pictures: a surface-partition marker or a backdrop sample must
451        /// still reach the output, and follower placement depends on a
452        /// leader outside the subtree.
453        cullable: bool,
454    },
455}
456
457/// Returns the current layer generation and advances the counter.
458fn next_generation(counter: &mut u64) -> u64 {
459    let generation = *counter;
460    *counter = generation
461        .checked_add(1)
462        .expect("layer generation exhausted");
463    generation
464}
465
466/// Renderer-independent retained layer arena. Picture display lists are owned
467/// once and scene flattening only emits a transient linear command stream.
468/// This deliberately has no GPU resources or offscreen textures.
469pub struct LayerTree {
470    layers: Arena<Layer>,
471    root: Option<LayerId>,
472    diagnostics: CompositorDiagnostics,
473    flattened_pictures: Vec<FlattenedPicture>,
474    flattened_annotations: Vec<FlattenedAnnotation>,
475    next_generation: u64,
476    /// Live annotated-region layers; the per-frame annotation pass is
477    /// skipped entirely while there are none.
478    annotation_layers: usize,
479    /// Live leader layers; leader publication and its two layer walks are
480    /// skipped entirely while there are none.
481    leader_layers: usize,
482    /// Live layers carrying a surface partition; clearing them is free
483    /// while there are none.
484    partition_layers: usize,
485    /// Reusable child-id stack for recursive passes, so traversals borrow
486    /// the tree mutably without cloning each layer's child list.
487    child_stack: Vec<LayerId>,
488    /// Retained flatten target, cleared and refilled each frame so its
489    /// capacity is reused. Never shared, so recording never copies it.
490    flatten_scratch: DisplayList,
491    /// Last list handed out by [`Self::flatten`]. An unchanged scene returns
492    /// a cheap shared clone of it instead of a fresh command buffer.
493    flatten_published: DisplayList,
494}
495impl Default for LayerTree {
496    fn default() -> Self {
497        Self::new()
498    }
499}
500impl LayerTree {
501    #[must_use]
502    pub fn new() -> Self {
503        Self {
504            layers: Arena::new(),
505            root: None,
506            diagnostics: CompositorDiagnostics::default(),
507            flattened_pictures: Vec::new(),
508            flattened_annotations: Vec::new(),
509            next_generation: 1,
510            annotation_layers: 0,
511            leader_layers: 0,
512            partition_layers: 0,
513            child_stack: Vec::new(),
514            flatten_scratch: DisplayList::new(),
515            flatten_published: DisplayList::new(),
516        }
517    }
518    #[must_use]
519    pub fn diagnostics(&self) -> CompositorDiagnostics {
520        self.diagnostics
521    }
522    #[must_use]
523    pub fn root(&self) -> Option<LayerId> {
524        self.root
525    }
526    /// Read-only view of one layer's kind and ordered children, for
527    /// diagnostics and inspection tooling.
528    #[must_use]
529    pub fn layer(&self, id: LayerId) -> Option<(&LayerKind, &[LayerId])> {
530        self.layers
531            .get(id.0)
532            .map(|layer| (&layer.kind, layer.children.as_slice()))
533    }
534    #[doc(hidden)]
535    #[must_use]
536    pub fn contains(&self, id: LayerId) -> bool {
537        self.layers.contains(id.0)
538    }
539    pub fn set_root(&mut self, root: LayerId) {
540        self.root = Some(root);
541    }
542
543    /// Returns the retained scene's conservative world-space visual bounds
544    /// after compositor transforms, rectangular clipping, blur support, and
545    /// drop-shadow support have been applied.
546    ///
547    /// This is renderer-independent geometry. It is useful to embedders that
548    /// need to size a native host around retained content without inspecting
549    /// widget types or GPU commands.
550    #[must_use]
551    pub fn scene_bounds(&self) -> Option<Rect> {
552        self.root
553            .and_then(|root| self.visual_subtree_bounds(root, Transform::IDENTITY))
554    }
555    pub fn create_picture(&mut self, display_list: DisplayList, bounds: Rect) -> LayerId {
556        self.insert(LayerKind::Picture {
557            display_list: Arc::new(display_list),
558            bounds,
559        })
560    }
561    pub fn create_transform(&mut self, transform: Transform) -> LayerId {
562        self.insert(LayerKind::Transform { transform })
563    }
564    pub fn create_clip_rect(&mut self, rect: Rect) -> LayerId {
565        self.insert(LayerKind::ClipRect { rect })
566    }
567    /// Creates a rounded-rectangle clip stage in local space; the world
568    /// shape resolves at flatten time like [`Self::create_clip_rect`].
569    pub fn create_clip_rrect(&mut self, rrect: RRect) -> LayerId {
570        self.insert(LayerKind::ClipRRect { rrect })
571    }
572    /// Creates an oval clip stage in local space; the world shape resolves
573    /// at flatten time like [`Self::create_clip_rect`].
574    pub fn create_clip_oval(&mut self, rect: Rect) -> LayerId {
575        self.insert(LayerKind::ClipOval { rect })
576    }
577    /// Creates a path clip stage in local space; the world shape resolves
578    /// at flatten time like [`Self::create_clip_rect`].
579    pub fn create_clip_path(&mut self, path: Arc<Path>, fill_rule: FillRule) -> LayerId {
580        self.insert(LayerKind::ClipPath { path, fill_rule })
581    }
582    /// Creates an isolated compositor group. Alpha is normalized at the
583    /// renderer-neutral boundary so every backend observes identical values.
584    pub fn create_opacity(&mut self, alpha: f32) -> LayerId {
585        self.insert(LayerKind::Opacity {
586            alpha: normalize_opacity(alpha),
587        })
588    }
589    /// Creates an isolated Gaussian-filtered compositor group.
590    pub fn create_blur(&mut self, blur: GaussianBlur) -> LayerId {
591        self.insert(LayerKind::Blur {
592            blur: GaussianBlur::new(blur.sigma_x, blur.sigma_y),
593        })
594    }
595    /// Creates an isolated arbitrary-subtree drop-shadow compositor group.
596    pub fn create_drop_shadow(&mut self, shadow: DropShadowEffect) -> LayerId {
597        self.insert(LayerKind::DropShadow {
598            shadow: DropShadowEffect::asymmetric(
599                shadow.offset,
600                shadow.sigma_x,
601                shadow.sigma_y,
602                shadow.color,
603            ),
604        })
605    }
606    /// Creates an isolated color-matrix stage. Matrix changes are compositor
607    /// updates and deliberately do not change the source generation.
608    pub fn create_color_filter(&mut self, filter: ColorFilter) -> LayerId {
609        self.insert(LayerKind::ColorFilter {
610            filter: Box::new(filter),
611        })
612    }
613    /// Creates a retained blend group whose source remains cached when only
614    /// the discrete blend mode changes.
615    pub fn create_blend(&mut self, mode: BlendMode) -> LayerId {
616        self.insert(LayerKind::Blend { mode })
617    }
618    /// Creates an isolated shader-mask stage. The callback is evaluated by
619    /// the retained widget during paint; this layer only owns the resolved,
620    /// renderer-neutral shader description and its local coordinate space.
621    pub fn create_shader_mask(
622        &mut self,
623        shader: Brush,
624        blend_mode: BlendMode,
625        mask_size: Size,
626        mask_transform: Transform,
627    ) -> LayerId {
628        self.insert(LayerKind::ShaderMask {
629            shader: Box::new(shader),
630            blend_mode,
631            mask_size,
632            mask_transform: Box::new(mask_transform),
633        })
634    }
635    /// Creates a backdrop-filter stage. Unlike [`Self::create_blur`], this
636    /// stage samples the already painted destination before its child is
637    /// composited.
638    pub fn create_backdrop_filter(
639        &mut self,
640        blur: GaussianBlur,
641        blend_mode: BlendMode,
642        enabled: bool,
643    ) -> LayerId {
644        self.insert(LayerKind::BackdropFilter {
645            blur: GaussianBlur::new(blur.sigma_x, blur.sigma_y),
646            blend_mode,
647            enabled,
648            matrix: None,
649            backdrop_key: None,
650        })
651    }
652    /// Sets the matrix a backdrop-filter stage transforms its backdrop by
653    /// (Flutter `ImageFilter.matrix`), or clears it for the blur.
654    pub fn update_backdrop_matrix(&mut self, id: LayerId, matrix: Option<Transform>) -> bool {
655        let Some(layer) = self.layers.get_mut(id.0) else {
656            return false;
657        };
658        let LayerKind::BackdropFilter {
659            matrix: current, ..
660        } = &mut layer.kind
661        else {
662            return false;
663        };
664        if *current == matrix {
665            return false;
666        }
667        *current = matrix;
668        layer.generation = next_generation(&mut self.next_generation);
669        true
670    }
671    /// Sets the backdrop a backdrop-filter stage shares with others
672    /// (Flutter `BackdropKey`), or clears it.
673    pub fn update_backdrop_key(&mut self, id: LayerId, key: Option<BackdropKey>) -> bool {
674        let Some(layer) = self.layers.get_mut(id.0) else {
675            return false;
676        };
677        let LayerKind::BackdropFilter {
678            backdrop_key: current,
679            ..
680        } = &mut layer.kind
681        else {
682            return false;
683        };
684        if *current == key {
685            return false;
686        }
687        *current = key;
688        layer.generation = next_generation(&mut self.next_generation);
689        true
690    }
691    pub fn create_annotated_region(
692        &mut self,
693        annotation: Annotation,
694        sized: bool,
695        size: Size,
696    ) -> LayerId {
697        self.insert(LayerKind::AnnotatedRegion {
698            annotation,
699            sized,
700            size,
701        })
702    }
703    pub fn create_leader(&mut self, link: LayerLink, size: Size) -> LayerId {
704        self.insert(LayerKind::Leader {
705            link,
706            size,
707            publisher: PublisherIdentity::new(),
708        })
709    }
710    pub fn create_follower(&mut self, follower: FollowerLayer) -> LayerId {
711        self.insert(LayerKind::Follower(follower))
712    }
713    fn insert(&mut self, kind: LayerKind) -> LayerId {
714        self.annotation_layers += usize::from(matches!(kind, LayerKind::AnnotatedRegion { .. }));
715        self.leader_layers += usize::from(matches!(kind, LayerKind::Leader { .. }));
716        let generation = next_generation(&mut self.next_generation);
717        let id = LayerId(self.layers.insert(Layer {
718            kind,
719            children: Vec::new(),
720            surface_partition: None,
721            generation,
722            clip_cache: None,
723            parent: None,
724            cull: CullBounds::Unknown,
725        }));
726        self.diagnostics.layers += 1;
727        id
728    }
729    pub fn remove(&mut self, id: LayerId) {
730        if let Some(layer) = self.layers.remove(id.0) {
731            // The retained flatten result may share resources owned by the
732            // removed layer; release them with it.
733            self.flatten_published = DisplayList::new();
734            self.annotation_layers -=
735                usize::from(matches!(layer.kind, LayerKind::AnnotatedRegion { .. }));
736            self.leader_layers -= usize::from(matches!(layer.kind, LayerKind::Leader { .. }));
737            // A removed leader stops resolving only when it owned the
738            // publication: its shared link state would otherwise outlive it
739            // and followers would track a ghost. A non-owner's removal must
740            // not disturb the winner.
741            if let LayerKind::Leader {
742                link, publisher, ..
743            } = &layer.kind
744            {
745                link.clear_if_owned_by(publisher);
746            }
747            self.diagnostics.layers -= 1;
748            self.partition_layers -= usize::from(layer.surface_partition.is_some());
749            if let Some(parent) = layer.parent {
750                self.reset_cull_bounds(parent);
751            }
752            if self.root == Some(id) {
753                self.root = None;
754            }
755            // Children are pruned by their owners before removal in normal
756            // widget unmounting; stale entries are ignored by flattening.
757        }
758    }
759    pub fn set_children(&mut self, id: LayerId, children: Vec<LayerId>) {
760        let Some(layer) = self.layers.get_mut(id.0) else {
761            return;
762        };
763        if layer.children == children {
764            return;
765        }
766        let previous = std::mem::replace(&mut layer.children, children);
767        layer.generation = next_generation(&mut self.next_generation);
768        for child in previous {
769            if let Some(child) = self.layers.get_mut(child.0)
770                && child.parent == Some(id)
771            {
772                child.parent = None;
773            }
774        }
775        for index in 0.. {
776            let Some(child) = self
777                .layers
778                .get(id.0)
779                .and_then(|layer| layer.children.get(index).copied())
780            else {
781                break;
782            };
783            let Some(child) = self.layers.get_mut(child.0) else {
784                continue;
785            };
786            // A layer adopted from another container leaves that one's
787            // bounds behind as well.
788            if let Some(old) = child.parent.replace(id).filter(|old| *old != id) {
789                self.reset_cull_bounds(old);
790            }
791        }
792        self.reset_cull_bounds(id);
793    }
794
795    /// Marks one retained layer subtree as detachable presentation content.
796    /// The marker is renderer-neutral and does not alter layout, painting, or
797    /// the retained layer topology.
798    pub fn set_surface_partition(&mut self, id: LayerId, partition: Option<SurfacePartitionId>) {
799        let Some(layer) = self.layers.get_mut(id.0) else {
800            return;
801        };
802        if layer.surface_partition == partition {
803            return;
804        }
805        let had_partition = layer.surface_partition.is_some();
806        layer.surface_partition = partition;
807        if had_partition != partition.is_some() {
808            if had_partition {
809                self.partition_layers -= 1;
810            } else {
811                self.partition_layers += 1;
812            }
813            self.reset_cull_bounds(id);
814        }
815    }
816
817    /// Clears every detachable-surface marker. WidgetTree reapplies markers for
818    /// the currently visible transient portals before flattening each frame.
819    pub fn clear_surface_partitions(&mut self) {
820        if self.partition_layers == 0 {
821            return;
822        }
823        let ids = self
824            .layers
825            .iter()
826            .filter_map(|(raw, layer)| layer.surface_partition.is_some().then_some(LayerId(raw)))
827            .collect::<Vec<_>>();
828        for id in ids {
829            self.set_surface_partition(id, None);
830        }
831    }
832    pub fn update_picture(&mut self, id: LayerId, display_list: DisplayList, bounds: Rect) {
833        if let Some(layer) = self.layers.get_mut(id.0)
834            && let LayerKind::Picture {
835                display_list: current,
836                bounds: current_bounds,
837            } = &mut layer.kind
838        {
839            *current = Arc::new(display_list);
840            let moved = *current_bounds != bounds;
841            *current_bounds = bounds;
842            layer.generation = next_generation(&mut self.next_generation);
843            self.diagnostics.picture_layers_repainted += 1;
844            if moved {
845                self.reset_cull_bounds(id);
846            }
847        }
848    }
849    /// Replaces a picture layer's contents only when `display_list` or
850    /// `bounds` differ from what it holds, copying the list in that case.
851    /// A repaint that records the same picture keeps the retained one and its
852    /// generation, so a caller can record into one reused buffer without
853    /// allocating per layer. Returns whether the layer changed.
854    pub fn update_picture_if_changed(
855        &mut self,
856        id: LayerId,
857        display_list: &DisplayList,
858        bounds: Rect,
859    ) -> bool {
860        let Some(layer) = self.layers.get_mut(id.0) else {
861            return false;
862        };
863        let LayerKind::Picture {
864            display_list: current,
865            bounds: current_bounds,
866        } = &mut layer.kind
867        else {
868            return false;
869        };
870        if **current == *display_list && *current_bounds == bounds {
871            return false;
872        }
873        self.update_picture(id, display_list.clone(), bounds);
874        true
875    }
876    pub fn update_transform(&mut self, id: LayerId, transform: Transform) -> bool {
877        let Some(layer) = self.layers.get_mut(id.0) else {
878            return false;
879        };
880        let LayerKind::Transform { transform: current } = &mut layer.kind else {
881            return false;
882        };
883        if *current == transform {
884            return false;
885        }
886        *current = transform;
887        layer.generation = next_generation(&mut self.next_generation);
888        self.diagnostics.transform_updates += 1;
889        self.reset_cull_bounds(id);
890        true
891    }
892    pub fn update_clip(&mut self, id: LayerId, rect: Rect) -> bool {
893        let Some(layer) = self.layers.get_mut(id.0) else {
894            return false;
895        };
896        let LayerKind::ClipRect { rect: current } = &mut layer.kind else {
897            return false;
898        };
899        if *current == rect {
900            return false;
901        }
902        *current = rect;
903        layer.clip_cache = None;
904        layer.generation = next_generation(&mut self.next_generation);
905        self.diagnostics.clip_updates += 1;
906        true
907    }
908    pub fn update_clip_rrect(&mut self, id: LayerId, rrect: RRect) -> bool {
909        let Some(layer) = self.layers.get_mut(id.0) else {
910            return false;
911        };
912        let LayerKind::ClipRRect { rrect: current } = &mut layer.kind else {
913            return false;
914        };
915        if *current == rrect {
916            return false;
917        }
918        *current = rrect;
919        layer.clip_cache = None;
920        layer.generation = next_generation(&mut self.next_generation);
921        self.diagnostics.clip_updates += 1;
922        true
923    }
924    pub fn update_clip_oval(&mut self, id: LayerId, rect: Rect) -> bool {
925        let Some(layer) = self.layers.get_mut(id.0) else {
926            return false;
927        };
928        let LayerKind::ClipOval { rect: current } = &mut layer.kind else {
929            return false;
930        };
931        if *current == rect {
932            return false;
933        }
934        *current = rect;
935        layer.clip_cache = None;
936        layer.generation = next_generation(&mut self.next_generation);
937        self.diagnostics.clip_updates += 1;
938        true
939    }
940    pub fn update_clip_path(&mut self, id: LayerId, path: Arc<Path>, fill_rule: FillRule) -> bool {
941        let Some(layer) = self.layers.get_mut(id.0) else {
942            return false;
943        };
944        let LayerKind::ClipPath {
945            path: current_path,
946            fill_rule: current_rule,
947        } = &mut layer.kind
948        else {
949            return false;
950        };
951        if **current_path == *path && *current_rule == fill_rule {
952            return false;
953        }
954        *current_path = path;
955        *current_rule = fill_rule;
956        layer.clip_cache = None;
957        layer.generation = next_generation(&mut self.next_generation);
958        self.diagnostics.clip_updates += 1;
959        true
960    }
961    /// Updates only the group alpha. This deliberately does not change the
962    /// content generation, allowing a cached offscreen texture to be reused
963    /// while an opacity animation is ticking.
964    pub fn update_opacity(&mut self, id: LayerId, alpha: f32) -> bool {
965        let Some(layer) = self.layers.get_mut(id.0) else {
966            return false;
967        };
968        let LayerKind::Opacity { alpha: current } = &mut layer.kind else {
969            return false;
970        };
971        let alpha = normalize_opacity(alpha);
972        if *current == alpha {
973            return false;
974        }
975        *current = alpha;
976        self.diagnostics.opacity_updates += 1;
977        true
978    }
979    /// Updates Gaussian parameters without changing the retained source
980    /// generation. A backend recomputes only the filtered result.
981    pub fn update_blur(&mut self, id: LayerId, blur: GaussianBlur) -> bool {
982        let Some(layer) = self.layers.get_mut(id.0) else {
983            return false;
984        };
985        let LayerKind::Blur { blur: current } = &mut layer.kind else {
986            return false;
987        };
988        let blur = GaussianBlur::new(blur.sigma_x, blur.sigma_y);
989        if *current == blur {
990            return false;
991        }
992        *current = blur;
993        true
994    }
995    /// Updates shadow presentation properties without changing source pixels.
996    /// Sigma invalidates the filtered mask; offset/color remain composite-only
997    /// in GPU backends.
998    pub fn update_drop_shadow(&mut self, id: LayerId, shadow: DropShadowEffect) -> bool {
999        let Some(layer) = self.layers.get_mut(id.0) else {
1000            return false;
1001        };
1002        let LayerKind::DropShadow { shadow: current } = &mut layer.kind else {
1003            return false;
1004        };
1005        let shadow = DropShadowEffect::asymmetric(
1006            shadow.offset,
1007            shadow.sigma_x,
1008            shadow.sigma_y,
1009            shadow.color,
1010        );
1011        if *current == shadow {
1012            return false;
1013        }
1014        *current = shadow;
1015        true
1016    }
1017    pub fn update_color_filter(&mut self, id: LayerId, filter: ColorFilter) -> bool {
1018        let Some(layer) = self.layers.get_mut(id.0) else {
1019            return false;
1020        };
1021        let LayerKind::ColorFilter { filter: current } = &mut layer.kind else {
1022            return false;
1023        };
1024        if **current == filter {
1025            return false;
1026        }
1027        **current = filter;
1028        true
1029    }
1030    pub fn update_blend(&mut self, id: LayerId, mode: BlendMode) -> bool {
1031        let Some(layer) = self.layers.get_mut(id.0) else {
1032            return false;
1033        };
1034        let LayerKind::Blend { mode: current } = &mut layer.kind else {
1035            return false;
1036        };
1037        if *current == mode {
1038            return false;
1039        }
1040        *current = mode;
1041        true
1042    }
1043    pub fn update_shader_mask(
1044        &mut self,
1045        id: LayerId,
1046        shader: Brush,
1047        blend_mode: BlendMode,
1048        mask_size: Size,
1049        mask_transform: Transform,
1050    ) -> bool {
1051        let Some(layer) = self.layers.get_mut(id.0) else {
1052            return false;
1053        };
1054        let LayerKind::ShaderMask {
1055            shader: current_shader,
1056            blend_mode: current_mode,
1057            mask_size: current_size,
1058            mask_transform: current_transform,
1059        } = &mut layer.kind
1060        else {
1061            return false;
1062        };
1063        if **current_shader == shader
1064            && *current_mode == blend_mode
1065            && *current_size == mask_size
1066            && **current_transform == mask_transform
1067        {
1068            return false;
1069        }
1070        **current_shader = shader;
1071        *current_mode = blend_mode;
1072        *current_size = mask_size;
1073        **current_transform = mask_transform;
1074        layer.generation = next_generation(&mut self.next_generation);
1075        true
1076    }
1077    pub fn update_shader_mask_blend(&mut self, id: LayerId, blend_mode: BlendMode) -> bool {
1078        let Some(layer) = self.layers.get_mut(id.0) else {
1079            return false;
1080        };
1081        let LayerKind::ShaderMask {
1082            blend_mode: current,
1083            ..
1084        } = &mut layer.kind
1085        else {
1086            return false;
1087        };
1088        if *current == blend_mode {
1089            return false;
1090        }
1091        *current = blend_mode;
1092        layer.generation = next_generation(&mut self.next_generation);
1093        true
1094    }
1095    pub fn update_backdrop_filter(
1096        &mut self,
1097        id: LayerId,
1098        blur: GaussianBlur,
1099        blend_mode: BlendMode,
1100        enabled: bool,
1101    ) -> bool {
1102        let Some(layer) = self.layers.get_mut(id.0) else {
1103            return false;
1104        };
1105        let LayerKind::BackdropFilter {
1106            blur: current_blur,
1107            blend_mode: current_mode,
1108            enabled: current_enabled,
1109            ..
1110        } = &mut layer.kind
1111        else {
1112            return false;
1113        };
1114        let blur = GaussianBlur::new(blur.sigma_x, blur.sigma_y);
1115        if *current_blur == blur && *current_mode == blend_mode && *current_enabled == enabled {
1116            return false;
1117        }
1118        *current_blur = blur;
1119        *current_mode = blend_mode;
1120        *current_enabled = enabled;
1121        layer.generation = next_generation(&mut self.next_generation);
1122        true
1123    }
1124    pub fn update_annotated_region(
1125        &mut self,
1126        id: LayerId,
1127        annotation: Annotation,
1128        sized: bool,
1129        size: Size,
1130    ) -> bool {
1131        let Some(layer) = self.layers.get_mut(id.0) else {
1132            return false;
1133        };
1134        let LayerKind::AnnotatedRegion {
1135            annotation: current_annotation,
1136            sized: current_sized,
1137            size: current_size,
1138        } = &mut layer.kind
1139        else {
1140            return false;
1141        };
1142        if *current_annotation == annotation && *current_sized == sized && *current_size == size {
1143            return false;
1144        }
1145        *current_annotation = annotation;
1146        *current_sized = sized;
1147        *current_size = size;
1148        layer.generation = next_generation(&mut self.next_generation);
1149        true
1150    }
1151    pub fn update_leader(&mut self, id: LayerId, link: LayerLink, size: Size) -> bool {
1152        let Some(layer) = self.layers.get_mut(id.0) else {
1153            return false;
1154        };
1155        let LayerKind::Leader {
1156            link: current_link,
1157            size: current_size,
1158            publisher,
1159        } = &mut layer.kind
1160        else {
1161            return false;
1162        };
1163        if *current_link == link && *current_size == size {
1164            return false;
1165        }
1166        // The rebound layer releases only its own publication; a non-owner
1167        // rebind must not clear the winner it never displaced.
1168        current_link.clear_if_owned_by(publisher);
1169        *current_link = link;
1170        *current_size = size;
1171        layer.generation = next_generation(&mut self.next_generation);
1172        true
1173    }
1174    pub fn update_leader_size(&mut self, id: LayerId, size: Size) -> bool {
1175        let Some(layer) = self.layers.get_mut(id.0) else {
1176            return false;
1177        };
1178        let LayerKind::Leader { size: current, .. } = &mut layer.kind else {
1179            return false;
1180        };
1181        if *current == size {
1182            return false;
1183        }
1184        *current = size;
1185        layer.generation = next_generation(&mut self.next_generation);
1186        true
1187    }
1188    pub fn update_follower(&mut self, id: LayerId, follower: FollowerLayer) -> bool {
1189        let Some(layer) = self.layers.get_mut(id.0) else {
1190            return false;
1191        };
1192        let LayerKind::Follower(current) = &mut layer.kind else {
1193            return false;
1194        };
1195        if *current == follower {
1196            return false;
1197        }
1198        *current = follower;
1199        layer.generation = next_generation(&mut self.next_generation);
1200        true
1201    }
1202    /// World-space logical bounds captured by the most recent [`Self::flatten`]
1203    /// call. This keeps coordinate diagnostics out of the renderer hot path.
1204    #[must_use]
1205    pub fn flattened_pictures(&self) -> &[FlattenedPicture] {
1206        &self.flattened_pictures
1207    }
1208    #[must_use]
1209    pub fn flattened_annotations(&self) -> &[FlattenedAnnotation] {
1210        &self.flattened_annotations
1211    }
1212    /// Returns the front-most annotation of type `T` at `point`. Annotation
1213    /// lookup follows layer paint order, honours ancestor clips, and treats
1214    /// `sized: false` regions as covering the entire clipped subtree.
1215    #[must_use]
1216    pub fn find_annotation<T: Any + Clone>(&self, point: Offset) -> Option<T> {
1217        self.flattened_annotations.iter().find_map(|entry| {
1218            let in_bounds = entry
1219                .world_bounds
1220                .is_none_or(|bounds| bounds.contains(point));
1221            in_bounds.then(|| entry.annotation.cloned::<T>()).flatten()
1222        })
1223    }
1224    #[must_use]
1225    pub fn find_annotations<T: Any + Clone>(&self, point: Offset) -> Vec<T> {
1226        self.flattened_annotations
1227            .iter()
1228            .filter_map(|entry| {
1229                let in_bounds = entry
1230                    .world_bounds
1231                    .is_none_or(|bounds| bounds.contains(point));
1232                in_bounds.then(|| entry.annotation.cloned::<T>()).flatten()
1233            })
1234            .collect()
1235    }
1236    #[must_use]
1237    pub fn flatten(&mut self) -> DisplayList {
1238        let mut out = std::mem::take(&mut self.flatten_scratch);
1239        out.clear();
1240        self.flattened_pictures.clear();
1241        self.flattened_annotations.clear();
1242        if let Some(root) = self.root {
1243            // Leader publication runs as its own pass before any follower
1244            // resolves, so flatten order cannot strand a follower behind
1245            // its leader: paint, hit testing, and semantics all read the
1246            // same post-publication state.
1247            self.publish_leader_links();
1248            self.flatten_layer(root, Transform::IDENTITY, None, &mut out);
1249            if self.annotation_layers > 0 {
1250                self.collect_annotations(root, Transform::IDENTITY, None);
1251            }
1252        }
1253        if out != self.flatten_published {
1254            self.flatten_published = out.deep_copy();
1255        }
1256        // Drop the scratch commands (and any resources they share) while
1257        // keeping the buffer's capacity.
1258        out.clear();
1259        self.flatten_scratch = out;
1260        self.flatten_published.clone()
1261    }
1262    /// Exact world-space clip command for one clip layer. Translation-only
1263    /// worlds keep every shape analytic; uniform scales additionally keep
1264    /// rounded corners analytic with scaled radii. Anything a shape cannot
1265    /// represent falls back to an equivalent tolerance-flattened path,
1266    /// which stays exact for every affine map. Culling still uses the
1267    /// bounding box from `clip_world_bounds`: conservative there, never
1268    /// the emitted shape.
1269    fn clip_command(&mut self, id: LayerId, kind: &LayerKind, world: Transform) -> PaintCommand {
1270        match kind {
1271            LayerKind::ClipRect { rect, .. } => match clip_space(world) {
1272                ClipSpace::Translation | ClipSpace::Scale { .. } => PaintCommand::PushClip {
1273                    rect: world.transform_rect_bbox(*rect),
1274                },
1275                ClipSpace::General => {
1276                    let rect = *rect;
1277                    PaintCommand::PushClipPath {
1278                        path: self.memoized_fallback_path(id, move || rect_as_path(rect), world),
1279                        fill_rule: FillRule::NonZero,
1280                    }
1281                }
1282            },
1283            LayerKind::ClipRRect { rrect, .. } => match clip_space(world) {
1284                ClipSpace::Translation => PaintCommand::PushClipRRect {
1285                    rrect: RRect::new(world.transform_rect_bbox(rrect.rect), rrect.radii),
1286                },
1287                // Positive uniform scales keep corners circular; anything
1288                // else (mirrors, nonuniform scales) would move corner
1289                // ownership, so those shapes fall back to a path.
1290                ClipSpace::Scale { x, y } if x > 0. && x == y => PaintCommand::PushClipRRect {
1291                    rrect: RRect::new(world.transform_rect_bbox(rrect.rect), rrect.radii.scaled(x)),
1292                },
1293                _ => {
1294                    let (rect, radii) = (rrect.rect, rrect.radii);
1295                    PaintCommand::PushClipPath {
1296                        path: self.memoized_fallback_path(
1297                            id,
1298                            move || rrect_as_path(rect, radii, fallback_tolerance(world)),
1299                            world,
1300                        ),
1301                        fill_rule: FillRule::NonZero,
1302                    }
1303                }
1304            },
1305            LayerKind::ClipOval { rect, .. } => match clip_space(world) {
1306                // Axis-aligned scales keep ovals exact through the box.
1307                ClipSpace::Translation | ClipSpace::Scale { .. } => PaintCommand::PushClipOval {
1308                    rect: world.transform_rect_bbox(*rect),
1309                },
1310                ClipSpace::General => {
1311                    let rect = *rect;
1312                    PaintCommand::PushClipPath {
1313                        path: self.memoized_fallback_path(
1314                            id,
1315                            move || ellipse_as_path(rect, fallback_tolerance(world)),
1316                            world,
1317                        ),
1318                        fill_rule: FillRule::NonZero,
1319                    }
1320                }
1321            },
1322            LayerKind::ClipPath {
1323                path, fill_rule, ..
1324            } => PaintCommand::PushClipPath {
1325                path: self.memoized_clip_path(id, path, world),
1326                fill_rule: *fill_rule,
1327            },
1328            _ => unreachable!("clip emitter received a non-clip layer"),
1329        }
1330    }
1331    /// World-resolved path for a ClipPath layer, reusing the memoized
1332    /// resolution while the world, path, and rule stand still. Identity
1333    /// worlds reuse the supplied arc directly so static clips never mint
1334    /// replacement identities (and backend re-tessellations) per flatten.
1335    fn memoized_clip_path(&mut self, id: LayerId, path: &Arc<Path>, world: Transform) -> Arc<Path> {
1336        if world == Transform::IDENTITY {
1337            return path.clone();
1338        }
1339        let owned = path.clone();
1340        self.memoized_fallback_path(id, move || owned.as_ref().clone(), world)
1341    }
1342    /// World-resolved fallback path shared by every clip variant that
1343    /// emits one. The builder runs only on a memo miss; hits reuse the
1344    /// retained arc, so unchanged geometry and transform keep one path
1345    /// identity (and one backend mesh) across flattens. Geometry updates
1346    /// reset the memo through their own update path, and removal drops
1347    /// it with the layer.
1348    fn memoized_fallback_path(
1349        &mut self,
1350        id: LayerId,
1351        build: impl FnOnce() -> Path,
1352        world: Transform,
1353    ) -> Arc<Path> {
1354        if let Some(layer) = self.layers.get(id.0)
1355            && let Some(memo) = layer.clip_cache.as_ref()
1356            && memo.world == world
1357        {
1358            return memo.path.clone();
1359        }
1360        let resolved = Arc::new(build().transformed(world));
1361        if let Some(layer) = self.layers.get_mut(id.0) {
1362            layer.clip_cache = Some(Box::new(ResolvedClipPath {
1363                world,
1364                path: resolved.clone(),
1365            }));
1366        }
1367        resolved
1368    }
1369    /// Forgets the cull bounds of `id` and of every container above it.
1370    fn reset_cull_bounds(&mut self, id: LayerId) {
1371        let mut current = Some(id);
1372        while let Some(layer) = current.and_then(|id| self.layers.get_mut(id.0)) {
1373            if matches!(layer.cull, CullBounds::Unknown) {
1374                break;
1375            }
1376            layer.cull = CullBounds::Unknown;
1377            current = layer.parent;
1378        }
1379    }
1380
1381    /// Local bounds of the pictures under `id`, and whether the subtree can
1382    /// be culled by them. Picture bounds and transforms are all that move
1383    /// them, so they are memoized until one of those changes below `id`.
1384    /// The bounds are conservative: clip shapes are not applied.
1385    fn cull_bounds(&mut self, id: LayerId) -> (Option<Rect>, bool) {
1386        let Some(layer) = self.layers.get(id.0) else {
1387            return (None, true);
1388        };
1389        if let CullBounds::Known { bounds, cullable } = layer.cull {
1390            return (bounds, cullable);
1391        }
1392        let own_cullable = layer.surface_partition.is_none()
1393            && !matches!(
1394                layer.kind,
1395                LayerKind::Follower(_) | LayerKind::BackdropFilter { .. }
1396            );
1397        let (local, own_bounds) = match &layer.kind {
1398            LayerKind::Picture { bounds, .. } => (Transform::IDENTITY, Some(*bounds)),
1399            LayerKind::Transform { transform } => (*transform, None),
1400            _ => (Transform::IDENTITY, None),
1401        };
1402        let mut bounds = own_bounds;
1403        let mut cullable = own_cullable;
1404        for index in 0.. {
1405            let Some(child) = self
1406                .layers
1407                .get(id.0)
1408                .and_then(|layer| layer.children.get(index).copied())
1409            else {
1410                break;
1411            };
1412            let (child_bounds, child_cullable) = self.cull_bounds(child);
1413            cullable &= child_cullable;
1414            if let Some(child_bounds) = child_bounds {
1415                let child_bounds = local.transform_rect_bbox(child_bounds);
1416                bounds =
1417                    Some(bounds.map_or(child_bounds, |bounds| union_rect(bounds, child_bounds)));
1418            }
1419        }
1420        if let Some(layer) = self.layers.get_mut(id.0) {
1421            layer.cull = CullBounds::Known { bounds, cullable };
1422        }
1423        (bounds, cullable)
1424    }
1425
1426    fn flatten_layer(
1427        &mut self,
1428        id: LayerId,
1429        world_transform: Transform,
1430        clip: Option<Rect>,
1431        out: &mut DisplayList,
1432    ) {
1433        // A subtree whose pictures all fall outside the active clip emits
1434        // nothing visible, so it is skipped without visiting its layers.
1435        if let Some(active) = clip {
1436            let (bounds, cullable) = self.cull_bounds(id);
1437            if cullable
1438                && bounds.is_none_or(|bounds| {
1439                    !active.intersects(world_transform.transform_rect_bbox(bounds))
1440                })
1441            {
1442                self.diagnostics.layers_culled += 1;
1443                return;
1444            }
1445        }
1446        let partition = self
1447            .layers
1448            .get(id.0)
1449            .and_then(|layer| layer.surface_partition);
1450        if let Some(id) = partition {
1451            out.push(PaintCommand::PushSurfacePartition { id });
1452        }
1453        self.flatten_layer_contents(id, world_transform, clip, out);
1454        if partition.is_some() {
1455            out.push(PaintCommand::PopSurfacePartition);
1456        }
1457    }
1458
1459    /// Visits `id`'s children (back to front when `reverse`) without cloning
1460    /// its child list: ids are staged on a reusable stack so `visit` may
1461    /// recurse with the tree borrowed mutably.
1462    fn visit_children(
1463        &mut self,
1464        id: LayerId,
1465        reverse: bool,
1466        mut visit: impl FnMut(&mut Self, LayerId),
1467    ) {
1468        let start = self.child_stack.len();
1469        if let Some(layer) = self.layers.get(id.0) {
1470            self.child_stack.extend_from_slice(&layer.children);
1471        }
1472        let end = self.child_stack.len();
1473        for index in start..end {
1474            let child = self.child_stack[if reverse {
1475                start + end - 1 - index
1476            } else {
1477                index
1478            }];
1479            visit(self, child);
1480        }
1481        self.child_stack.truncate(start);
1482    }
1483
1484    fn flatten_layer_contents(
1485        &mut self,
1486        id: LayerId,
1487        world_transform: Transform,
1488        clip: Option<Rect>,
1489        out: &mut DisplayList,
1490    ) {
1491        let Some(kind) = self.layers.get(id.0).map(|layer| layer.kind.clone()) else {
1492            return;
1493        };
1494        match kind {
1495            LayerKind::Picture {
1496                display_list,
1497                bounds,
1498            } => {
1499                let world = world_transform.transform_rect_bbox(bounds);
1500                if clip.is_some_and(|active| !active.intersects(world)) {
1501                    self.diagnostics.layers_culled += 1;
1502                    return;
1503                }
1504                self.flattened_pictures.push(FlattenedPicture {
1505                    layer: id,
1506                    local_bounds: bounds,
1507                    world_bounds: world,
1508                    active_clip: clip,
1509                });
1510                out.push(PaintCommand::PushTransform {
1511                    transform: world_transform,
1512                });
1513                out.extend_from(&display_list);
1514                out.push(PaintCommand::PopTransform);
1515            }
1516            LayerKind::Transform { transform: local } => {
1517                let next = world_transform.then(local);
1518                self.visit_children(id, false, |tree, child| {
1519                    tree.flatten_layer(child, next, clip, out)
1520                });
1521            }
1522            LayerKind::ClipRect { .. }
1523            | LayerKind::ClipRRect { .. }
1524            | LayerKind::ClipOval { .. }
1525            | LayerKind::ClipPath { .. } => {
1526                // Conservative world bounds drive culling; the emitted
1527                // command carries the exact shape (or its path fallback),
1528                // never the box.
1529                let Some(shape_bounds) = clip_world_bounds(&kind, world_transform) else {
1530                    self.diagnostics.layers_culled += 1;
1531                    return;
1532                };
1533                let next_clip = match clip {
1534                    Some(active) => active.intersection(shape_bounds),
1535                    None => Some(shape_bounds),
1536                };
1537                if next_clip.is_none() {
1538                    self.diagnostics.layers_culled += 1;
1539                    return;
1540                }
1541                out.push(self.clip_command(id, &kind, world_transform));
1542                self.visit_children(id, false, |tree, child| {
1543                    tree.flatten_layer(child, world_transform, next_clip, out)
1544                });
1545                out.push(PaintCommand::PopClip);
1546            }
1547            LayerKind::Opacity { alpha } => {
1548                let bounds = self
1549                    .subtree_bounds(id, world_transform)
1550                    .unwrap_or_else(|| Rect::from_origin_size(Offset::ZERO, Size::ZERO));
1551                out.push(PaintCommand::PushOpacity {
1552                    layer: id,
1553                    alpha,
1554                    generation: self.subtree_generation(id),
1555                    bounds,
1556                });
1557                self.visit_children(id, false, |tree, child| {
1558                    tree.flatten_layer(child, world_transform, clip, out)
1559                });
1560                out.push(PaintCommand::PopOpacity);
1561            }
1562            LayerKind::Blur { blur } => {
1563                let bounds = self
1564                    .subtree_bounds(id, world_transform)
1565                    .unwrap_or_else(|| Rect::from_origin_size(Offset::ZERO, Size::ZERO));
1566                out.push(PaintCommand::PushBlur {
1567                    layer: id,
1568                    blur,
1569                    generation: self.subtree_generation(id),
1570                    bounds,
1571                });
1572                self.visit_children(id, false, |tree, child| {
1573                    tree.flatten_layer(child, world_transform, clip, out)
1574                });
1575                out.push(PaintCommand::PopEffect);
1576            }
1577            LayerKind::DropShadow { shadow } => {
1578                let bounds = self
1579                    .subtree_bounds(id, world_transform)
1580                    .unwrap_or_else(|| Rect::from_origin_size(Offset::ZERO, Size::ZERO));
1581                out.push(PaintCommand::PushDropShadow {
1582                    layer: id,
1583                    shadow,
1584                    generation: self.subtree_generation(id),
1585                    bounds,
1586                });
1587                self.visit_children(id, false, |tree, child| {
1588                    tree.flatten_layer(child, world_transform, clip, out)
1589                });
1590                out.push(PaintCommand::PopEffect);
1591            }
1592            LayerKind::ColorFilter { filter } => {
1593                let bounds = self
1594                    .subtree_bounds(id, world_transform)
1595                    .unwrap_or_else(|| Rect::from_origin_size(Offset::ZERO, Size::ZERO));
1596                out.push(PaintCommand::PushColorFilter {
1597                    layer: id,
1598                    filter: *filter,
1599                    generation: self.subtree_generation(id),
1600                    bounds,
1601                });
1602                self.visit_children(id, false, |tree, child| {
1603                    tree.flatten_layer(child, world_transform, clip, out)
1604                });
1605                out.push(PaintCommand::PopEffect);
1606            }
1607            LayerKind::Blend { mode } => {
1608                let bounds = self
1609                    .subtree_bounds(id, world_transform)
1610                    .unwrap_or_else(|| Rect::from_origin_size(Offset::ZERO, Size::ZERO));
1611                out.push(PaintCommand::PushBlend {
1612                    layer: id,
1613                    mode,
1614                    generation: self.subtree_generation(id),
1615                    bounds,
1616                });
1617                self.visit_children(id, false, |tree, child| {
1618                    tree.flatten_layer(child, world_transform, clip, out)
1619                });
1620                out.push(PaintCommand::PopEffect);
1621            }
1622            LayerKind::ShaderMask {
1623                shader,
1624                blend_mode,
1625                mask_size,
1626                mask_transform,
1627            } => {
1628                let bounds = self
1629                    .subtree_bounds(id, world_transform)
1630                    .unwrap_or_else(|| Rect::from_origin_size(Offset::ZERO, Size::ZERO));
1631                if !bounds.size.width.is_finite()
1632                    || !bounds.size.height.is_finite()
1633                    || bounds.size.width <= 0.
1634                    || bounds.size.height <= 0.
1635                {
1636                    return;
1637                }
1638                out.push(PaintCommand::PushShaderMask {
1639                    layer: id,
1640                    shader: *shader,
1641                    blend_mode,
1642                    mask_size,
1643                    mask_transform: *mask_transform,
1644                    generation: self.subtree_generation(id),
1645                    bounds,
1646                });
1647                self.visit_children(id, false, |tree, child| {
1648                    tree.flatten_layer(child, world_transform, clip, out)
1649                });
1650                out.push(PaintCommand::PopEffect);
1651            }
1652            LayerKind::BackdropFilter {
1653                blur,
1654                blend_mode,
1655                enabled,
1656                matrix,
1657                backdrop_key,
1658            } => {
1659                if !crate::backdrop_filter_is_active(blur, enabled, matrix) {
1660                    self.visit_children(id, false, |tree, child| {
1661                        tree.flatten_layer(child, world_transform, clip, out)
1662                    });
1663                    return;
1664                }
1665                let bounds = self
1666                    .subtree_bounds(id, world_transform)
1667                    .unwrap_or_else(|| Rect::from_origin_size(Offset::ZERO, Size::ZERO));
1668                out.push(PaintCommand::PushBackdropFilter {
1669                    layer: id,
1670                    blur,
1671                    blend_mode,
1672                    enabled,
1673                    matrix,
1674                    backdrop_key,
1675                    generation: self.subtree_generation(id),
1676                    bounds,
1677                });
1678                self.visit_children(id, false, |tree, child| {
1679                    tree.flatten_layer(child, world_transform, clip, out)
1680                });
1681                out.push(PaintCommand::PopEffect);
1682            }
1683            LayerKind::AnnotatedRegion { .. } => {
1684                self.visit_children(id, false, |tree, child| {
1685                    tree.flatten_layer(child, world_transform, clip, out)
1686                });
1687            }
1688            LayerKind::Leader { .. } => {
1689                // Publication already ran in the dedicated pass above; the
1690                // flatten walk only positions children here.
1691                self.visit_children(id, false, |tree, child| {
1692                    tree.flatten_layer(child, world_transform, clip, out)
1693                });
1694            }
1695            LayerKind::Follower(follower) => {
1696                let Some(next_transform) = self.follower_transform(world_transform, &follower)
1697                else {
1698                    return;
1699                };
1700                self.visit_children(id, false, |tree, child| {
1701                    tree.flatten_layer(child, next_transform, clip, out)
1702                });
1703            }
1704        }
1705    }
1706    fn clear_link_states(&self, id: LayerId) {
1707        let Some(layer) = self.layers.get(id.0) else {
1708            return;
1709        };
1710        if let LayerKind::Leader { link, .. } = &layer.kind {
1711            link.clear_publication();
1712        }
1713        for child in layer.children.iter() {
1714            self.clear_link_states(*child);
1715        }
1716    }
1717    /// Republishes leader links from current layer state for readers that
1718    /// run before `flatten` (notably semantic collection, which the frame
1719    /// runs pre-paint). Same pass `flatten` runs, so follower visibility
1720    /// reflects this frame's layout instead of the previous paint;
1721    /// genuinely hidden leaders still stay unpublished. Idempotent.
1722    pub fn publish_leader_links(&mut self) {
1723        if self.leader_layers == 0 {
1724            return;
1725        }
1726        if let Some(root) = self.root {
1727            self.clear_link_states(root);
1728            self.publish_resolved_leaders(root);
1729        }
1730    }
1731    /// Publishes every reachable leader in dependency order: a leader
1732    /// nested under linked followers resolves through the same follower
1733    /// projection the flatten walk uses, so nested publications land in
1734    /// the resolved frame rather than the layout frame. Leaders are
1735    /// attempted once each in paint order with cycle-guarded recursion,
1736    /// so chains and cycles always terminate; the first resolvable
1737    /// leader per link wins and anything culled or cyclic stays
1738    /// unpublished for its followers to treat as unlinked.
1739    fn publish_resolved_leaders(&mut self, root: LayerId) {
1740        self.diagnostics.leader_publish_passes += 1;
1741        let mut parents = HashMap::new();
1742        let mut leaders = Vec::new();
1743        self.collect_publish_state(root, &mut parents, &mut leaders);
1744        if leaders.is_empty() {
1745            return;
1746        }
1747        let mut states = HashMap::new();
1748        let mut published = 0;
1749        for index in 0..leaders.len() {
1750            published += self.ensure_published(index, &leaders, &parents, &mut states) as u64;
1751        }
1752        self.diagnostics.leaders_published += published;
1753    }
1754    fn collect_publish_state(
1755        &self,
1756        id: LayerId,
1757        parents: &mut HashMap<LayerId, LayerId>,
1758        leaders: &mut Vec<LeaderRecord>,
1759    ) {
1760        let Some(layer) = self.layers.get(id.0) else {
1761            return;
1762        };
1763        if let LayerKind::Leader {
1764            link,
1765            size,
1766            publisher,
1767        } = &layer.kind
1768        {
1769            leaders.push(LeaderRecord {
1770                id,
1771                link: link.clone(),
1772                size: *size,
1773                publisher: publisher.clone(),
1774                generation: layer.generation,
1775            });
1776        }
1777        for child in layer.children.iter() {
1778            parents.entry(*child).or_insert(id);
1779            self.collect_publish_state(*child, parents, leaders);
1780        }
1781    }
1782    fn ensure_published(
1783        &self,
1784        index: usize,
1785        leaders: &[LeaderRecord],
1786        parents: &HashMap<LayerId, LayerId>,
1787        states: &mut HashMap<LayerId, PublishState>,
1788    ) -> bool {
1789        let id = leaders[index].id;
1790        match states.get(&id) {
1791            Some(PublishState::Published) => return true,
1792            Some(PublishState::Unresolvable) => return false,
1793            // A dependency cycle: the revisit reports failure so the
1794            // depender treats the link as currently unlinked. The outer
1795            // attempt still finishes and overwrites this marker with its
1796            // own outcome, so a leader whose only dependent is itself
1797            // resolves through the unlinked-follower rule below.
1798            Some(PublishState::InProgress) => {
1799                states.insert(id, PublishState::Unresolvable);
1800                return false;
1801            }
1802            None => {}
1803        }
1804        states.insert(id, PublishState::InProgress);
1805        let published = match self.leader_context(index, leaders, parents, states) {
1806            Some(world) => {
1807                let record = &leaders[index];
1808                record.link.publish(
1809                    record.publisher.clone(),
1810                    world,
1811                    record.size,
1812                    record.generation,
1813                );
1814                true
1815            }
1816            None => false,
1817        };
1818        states.insert(
1819            id,
1820            if published {
1821                PublishState::Published
1822            } else {
1823                PublishState::Unresolvable
1824            },
1825        );
1826        published
1827    }
1828    /// Resolves one leader's publication frame by folding its ancestor
1829    /// chain with the same rules the flatten walk applies: transforms
1830    /// compose, followers resolve through the shared projection (ensuring
1831    /// their links' winners first), showing-but-unlinked followers keep
1832    /// the parent frame, and anything the flatten walk would cull —
1833    /// hidden followers, emptied clips, singular inversions — resolves
1834    /// to nothing so the leader stays unpublished exactly where flatten
1835    /// would never visit it.
1836    fn leader_context(
1837        &self,
1838        index: usize,
1839        leaders: &[LeaderRecord],
1840        parents: &HashMap<LayerId, LayerId>,
1841        states: &mut HashMap<LayerId, PublishState>,
1842    ) -> Option<Transform> {
1843        let mut chain = vec![leaders[index].id];
1844        while let Some(parent) = chain.last().and_then(|id| parents.get(id)) {
1845            chain.push(*parent);
1846        }
1847        let mut world = Transform::IDENTITY;
1848        let mut clip: Option<Rect> = None;
1849        for node in chain.iter().rev() {
1850            let layer = self.layers.get(node.0)?;
1851            match &layer.kind {
1852                LayerKind::Transform { transform } => {
1853                    world = world.then(*transform);
1854                }
1855                LayerKind::Follower(follower) => {
1856                    let link = &follower.link;
1857                    if link.leader_transform().is_none() {
1858                        // Publish the dependency first: a nested leader
1859                        // waits for its ancestor follower's link, trying
1860                        // candidates in paint order until one publishes.
1861                        for dependency in 0..leaders.len() {
1862                            if leaders[dependency].link == *link {
1863                                self.ensure_published(dependency, leaders, parents, states);
1864                                if link.leader_transform().is_some() {
1865                                    break;
1866                                }
1867                            }
1868                        }
1869                    }
1870                    match link.leader_transform() {
1871                        Some(_) => {
1872                            world = self.follower_transform(world, follower)?;
1873                        }
1874                        None if follower.show_when_unlinked => {}
1875                        None => return None,
1876                    }
1877                }
1878                LayerKind::ClipRect { .. }
1879                | LayerKind::ClipRRect { .. }
1880                | LayerKind::ClipOval { .. }
1881                | LayerKind::ClipPath { .. } => {
1882                    let shape = clip_world_bounds(&layer.kind, world)?;
1883                    clip = match clip {
1884                        Some(active) => active.intersection(shape),
1885                        None => Some(shape),
1886                    };
1887                    // An emptied clip culls the subtree: flatten never
1888                    // visits leaders beneath it, so neither does this pass.
1889                    clip?;
1890                }
1891                _ => {}
1892            }
1893        }
1894        Some(world)
1895    }
1896    fn follower_transform(
1897        &self,
1898        world_transform: Transform,
1899        follower: &FollowerLayer,
1900    ) -> Option<Transform> {
1901        let FollowerLayer {
1902            link,
1903            show_when_unlinked,
1904            offset,
1905            target_anchor,
1906            follower_anchor,
1907            size,
1908        } = follower;
1909        let Some(leader_transform) = link.leader_transform() else {
1910            return show_when_unlinked.then_some(world_transform);
1911        };
1912        let leader_size = link.leader_size().unwrap_or(Size::ZERO);
1913        let desired_anchor =
1914            resolve_follower_target(leader_transform, leader_size, *target_anchor, *offset);
1915        let desired_local = world_transform.inverse_transform_point(desired_anchor)?;
1916        let local_delta = desired_local - follower_anchor.along_size(*size);
1917        Some(world_transform.then(Transform::translation(local_delta)))
1918    }
1919    fn collect_annotations(&mut self, id: LayerId, world_transform: Transform, clip: Option<Rect>) {
1920        let Some(kind) = self.layers.get(id.0).map(|layer| layer.kind.clone()) else {
1921            return;
1922        };
1923        match kind {
1924            LayerKind::Transform { transform } => {
1925                let next = world_transform.then(transform);
1926                self.visit_children(id, false, |tree, child| {
1927                    tree.collect_annotations(child, next, clip)
1928                });
1929            }
1930            LayerKind::ClipRect { rect, .. } => {
1931                let world = world_transform.transform_rect_bbox(rect);
1932                let next_clip = match clip {
1933                    Some(current) => current.intersection(world),
1934                    None => Some(world),
1935                };
1936                if next_clip.is_none() {
1937                    return;
1938                }
1939                self.visit_children(id, false, |tree, child| {
1940                    tree.collect_annotations(child, world_transform, next_clip)
1941                });
1942            }
1943            LayerKind::ClipRRect { .. }
1944            | LayerKind::ClipOval { .. }
1945            | LayerKind::ClipPath { .. } => {
1946                let Some(world) = clip_world_bounds(&kind, world_transform) else {
1947                    return;
1948                };
1949                let next_clip = match clip {
1950                    Some(current) => current.intersection(world),
1951                    None => Some(world),
1952                };
1953                if next_clip.is_none() {
1954                    return;
1955                }
1956                self.visit_children(id, false, |tree, child| {
1957                    tree.collect_annotations(child, world_transform, next_clip)
1958                });
1959            }
1960            LayerKind::Follower(follower) => {
1961                let Some(next) = self.follower_transform(world_transform, &follower) else {
1962                    return;
1963                };
1964                self.visit_children(id, false, |tree, child| {
1965                    tree.collect_annotations(child, next, clip)
1966                });
1967            }
1968            LayerKind::AnnotatedRegion {
1969                annotation,
1970                sized,
1971                size,
1972            } => {
1973                // Container layers are painted in child order, but annotation
1974                // lookup walks visually front-to-back. Visit children in
1975                // reverse order, then append this layer's own annotation.
1976                self.visit_children(id, true, |tree, child| {
1977                    tree.collect_annotations(child, world_transform, clip)
1978                });
1979                let region =
1980                    world_transform.transform_rect_bbox(Rect::from_origin_size(Offset::ZERO, size));
1981                let world_bounds = if sized {
1982                    match clip {
1983                        Some(active) => active.intersection(region),
1984                        None => Some(region),
1985                    }
1986                } else {
1987                    clip
1988                };
1989                if world_bounds
1990                    .is_none_or(|bounds| bounds.size.width > 0. && bounds.size.height > 0.)
1991                {
1992                    self.flattened_annotations.push(FlattenedAnnotation {
1993                        annotation,
1994                        world_bounds,
1995                    });
1996                }
1997            }
1998            LayerKind::Picture { .. }
1999            | LayerKind::Opacity { .. }
2000            | LayerKind::Blur { .. }
2001            | LayerKind::DropShadow { .. }
2002            | LayerKind::ColorFilter { .. }
2003            | LayerKind::Blend { .. }
2004            | LayerKind::ShaderMask { .. }
2005            | LayerKind::BackdropFilter { .. }
2006            | LayerKind::Leader { .. } => {
2007                self.visit_children(id, false, |tree, child| {
2008                    tree.collect_annotations(child, world_transform, clip)
2009                });
2010            }
2011        }
2012    }
2013    fn subtree_generation(&self, id: LayerId) -> u64 {
2014        let Some(layer) = self.layers.get(id.0) else {
2015            return 0;
2016        };
2017        let mut value = layer.generation.wrapping_mul(0x9e37_79b9_7f4a_7c15);
2018        for child in &layer.children {
2019            // An opacity layer's own alpha is intentionally excluded from
2020            // its cache key: changing it only changes the final composite.
2021            // When that layer is nested inside another isolated group,
2022            // however, its composite is part of the parent's pixels. Include
2023            // the child's alpha at this boundary so an inner fade invalidates
2024            // the outer group's cached texture without invalidating the
2025            // inner group's own content target.
2026            if let Some(child_layer) = self.layers.get(child.0)
2027                && let LayerKind::Opacity { alpha } = &child_layer.kind
2028            {
2029                value = value.rotate_left(11) ^ u64::from(alpha.to_bits());
2030            }
2031            if let Some(child_layer) = self.layers.get(child.0) {
2032                match &child_layer.kind {
2033                    LayerKind::Blur { blur } => {
2034                        value = value.rotate_left(13)
2035                            ^ u64::from(blur.sigma_x.to_bits())
2036                            ^ u64::from(blur.sigma_y.to_bits()).rotate_left(17);
2037                    }
2038                    LayerKind::DropShadow { shadow } => {
2039                        value = value.rotate_left(13)
2040                            ^ u64::from(shadow.offset.x.to_bits())
2041                            ^ u64::from(shadow.offset.y.to_bits()).rotate_left(7)
2042                            ^ u64::from(shadow.sigma_x.to_bits()).rotate_left(17)
2043                            ^ u64::from(shadow.sigma_y.to_bits()).rotate_left(23)
2044                            ^ u64::from(shadow.color.red)
2045                            ^ (u64::from(shadow.color.green) << 8)
2046                            ^ (u64::from(shadow.color.blue) << 16)
2047                            ^ (u64::from(shadow.color.alpha) << 24);
2048                    }
2049                    LayerKind::ColorFilter { filter } => {
2050                        for (index, bits) in filter.to_matrix().map(f32::to_bits).iter().enumerate()
2051                        {
2052                            value ^= u64::from(*bits).rotate_left((index as u32 % 31) + 1);
2053                        }
2054                    }
2055                    LayerKind::Blend { mode } => {
2056                        value = value.rotate_left(13) ^ u64::from(mode.code());
2057                    }
2058                    LayerKind::ShaderMask {
2059                        shader,
2060                        blend_mode,
2061                        mask_size,
2062                        mask_transform,
2063                    } => {
2064                        value = value.rotate_left(13)
2065                            ^ brush_generation(shader)
2066                            ^ u64::from(blend_mode.code())
2067                            ^ u64::from(mask_size.width.to_bits()).rotate_left(9)
2068                            ^ u64::from(mask_size.height.to_bits()).rotate_left(15)
2069                            ^ transform_generation(**mask_transform);
2070                    }
2071                    LayerKind::BackdropFilter {
2072                        blur,
2073                        blend_mode,
2074                        enabled,
2075                        matrix,
2076                        backdrop_key: _,
2077                    } => {
2078                        value = value.rotate_left(13)
2079                            ^ u64::from(blur.sigma_x.to_bits())
2080                            ^ u64::from(blur.sigma_y.to_bits()).rotate_left(7)
2081                            ^ u64::from(blend_mode.code())
2082                            ^ u64::from(*enabled as u8);
2083                        if let Some(matrix) = matrix {
2084                            value ^= transform_generation(*matrix).rotate_left(21);
2085                        }
2086                    }
2087                    LayerKind::AnnotatedRegion {
2088                        annotation: _,
2089                        sized,
2090                        size,
2091                    } => {
2092                        value = value.rotate_left(13)
2093                            ^ u64::from(*sized as u8)
2094                            ^ u64::from(size.width.to_bits()).rotate_left(9)
2095                            ^ u64::from(size.height.to_bits()).rotate_left(15);
2096                    }
2097                    LayerKind::Leader { link, size, .. } => {
2098                        value = value.rotate_left(13)
2099                            ^ link.leader_generation()
2100                            ^ u64::from(size.width.to_bits()).rotate_left(9)
2101                            ^ u64::from(size.height.to_bits()).rotate_left(15);
2102                    }
2103                    LayerKind::Follower(follower) => {
2104                        value = value.rotate_left(13)
2105                            ^ follower.link.leader_generation()
2106                            ^ u64::from(follower.show_when_unlinked as u8)
2107                            ^ u64::from(follower.offset.x.to_bits())
2108                            ^ u64::from(follower.offset.y.to_bits()).rotate_left(7)
2109                            ^ u64::from(follower.target_anchor.x.to_bits()).rotate_left(13)
2110                            ^ u64::from(follower.target_anchor.y.to_bits()).rotate_left(17)
2111                            ^ u64::from(follower.follower_anchor.x.to_bits()).rotate_left(21)
2112                            ^ u64::from(follower.follower_anchor.y.to_bits()).rotate_left(25)
2113                            ^ u64::from(follower.size.width.to_bits()).rotate_left(29)
2114                            ^ u64::from(follower.size.height.to_bits()).rotate_left(31);
2115                    }
2116                    _ => {}
2117                }
2118            }
2119            value = value.rotate_left(7) ^ self.subtree_generation(*child);
2120        }
2121        value
2122    }
2123    fn subtree_bounds(&self, id: LayerId, world_transform: Transform) -> Option<Rect> {
2124        let layer = self.layers.get(id.0)?;
2125        match &layer.kind {
2126            LayerKind::Picture { bounds, .. } => Some(world_transform.transform_rect_bbox(*bounds)),
2127            LayerKind::Transform { transform: local } => layer
2128                .children
2129                .iter()
2130                .filter_map(|child| self.subtree_bounds(*child, world_transform.then(*local)))
2131                .reduce(union_rect),
2132            LayerKind::ClipRect { rect, .. } => {
2133                let world = world_transform.transform_rect_bbox(*rect);
2134                layer
2135                    .children
2136                    .iter()
2137                    .filter_map(|child| self.subtree_bounds(*child, world_transform))
2138                    .reduce(union_rect)
2139                    .and_then(|bounds| bounds.intersection(world))
2140            }
2141            LayerKind::ClipRRect { .. }
2142            | LayerKind::ClipOval { .. }
2143            | LayerKind::ClipPath { .. } => {
2144                let world = clip_world_bounds(&layer.kind, world_transform)?;
2145                layer
2146                    .children
2147                    .iter()
2148                    .filter_map(|child| self.subtree_bounds(*child, world_transform))
2149                    .reduce(union_rect)
2150                    .and_then(|bounds| bounds.intersection(world))
2151            }
2152            LayerKind::Opacity { .. } => layer
2153                .children
2154                .iter()
2155                .filter_map(|child| self.subtree_bounds(*child, world_transform))
2156                .reduce(union_rect),
2157            LayerKind::Blur { .. } | LayerKind::DropShadow { .. } => layer
2158                .children
2159                .iter()
2160                .filter_map(|child| self.subtree_bounds(*child, world_transform))
2161                .reduce(union_rect),
2162            LayerKind::ColorFilter { .. } | LayerKind::Blend { .. } => layer
2163                .children
2164                .iter()
2165                .filter_map(|child| self.subtree_bounds(*child, world_transform))
2166                .reduce(union_rect),
2167            LayerKind::ShaderMask { .. }
2168            | LayerKind::BackdropFilter { .. }
2169            | LayerKind::AnnotatedRegion { .. }
2170            | LayerKind::Leader { .. } => layer
2171                .children
2172                .iter()
2173                .filter_map(|child| self.subtree_bounds(*child, world_transform))
2174                .reduce(union_rect),
2175            LayerKind::Follower(follower) => {
2176                let next = self.follower_transform(world_transform, follower)?;
2177                layer
2178                    .children
2179                    .iter()
2180                    .filter_map(|child| self.subtree_bounds(*child, next))
2181                    .reduce(union_rect)
2182            }
2183        }
2184    }
2185
2186    fn visual_subtree_bounds(&self, id: LayerId, world_transform: Transform) -> Option<Rect> {
2187        let layer = self.layers.get(id.0)?;
2188        let child_bounds = |tree: &Self, transform: Transform| {
2189            layer
2190                .children
2191                .iter()
2192                .filter_map(|child| tree.visual_subtree_bounds(*child, transform))
2193                .reduce(union_rect)
2194        };
2195        match &layer.kind {
2196            LayerKind::Picture { bounds, .. } => Some(world_transform.transform_rect_bbox(*bounds)),
2197            LayerKind::Transform { transform: local } => {
2198                child_bounds(self, world_transform.then(*local))
2199            }
2200            LayerKind::ClipRect { rect, .. } => {
2201                let world = world_transform.transform_rect_bbox(*rect);
2202                child_bounds(self, world_transform).and_then(|bounds| bounds.intersection(world))
2203            }
2204            LayerKind::ClipRRect { .. }
2205            | LayerKind::ClipOval { .. }
2206            | LayerKind::ClipPath { .. } => {
2207                let world = clip_world_bounds(&layer.kind, world_transform)?;
2208                child_bounds(self, world_transform).and_then(|bounds| bounds.intersection(world))
2209            }
2210            LayerKind::Opacity { .. }
2211            | LayerKind::ColorFilter { .. }
2212            | LayerKind::Blend { .. }
2213            | LayerKind::ShaderMask { .. }
2214            | LayerKind::BackdropFilter { .. }
2215            | LayerKind::AnnotatedRegion { .. }
2216            | LayerKind::Leader { .. } => child_bounds(self, world_transform),
2217            LayerKind::Blur { blur } => child_bounds(self, world_transform)
2218                .map(|bounds| blur_bounds(bounds, blur.sigma_x, blur.sigma_y)),
2219            LayerKind::DropShadow { shadow } => child_bounds(self, world_transform).map(|bounds| {
2220                drop_shadow_bounds(bounds, shadow.offset, shadow.sigma_x, shadow.sigma_y)
2221            }),
2222            LayerKind::Follower(follower) => {
2223                let next = self.follower_transform(world_transform, follower)?;
2224                child_bounds(self, next)
2225            }
2226        }
2227    }
2228    #[must_use]
2229    pub fn debug_tree(&self) -> String {
2230        let mut out = String::new();
2231        if let Some(root) = self.root {
2232            self.write_debug(root, 0, &mut out);
2233        }
2234        out
2235    }
2236    fn write_debug(&self, id: LayerId, depth: usize, out: &mut String) {
2237        self.write_debug_at(id, None, depth, Transform::IDENTITY, None, out);
2238    }
2239    fn write_debug_at(
2240        &self,
2241        id: LayerId,
2242        parent: Option<LayerId>,
2243        depth: usize,
2244        world_transform: Transform,
2245        clip: Option<Rect>,
2246        out: &mut String,
2247    ) {
2248        let Some(layer) = self.layers.get(id.0) else {
2249            return;
2250        };
2251        let indent = "  ".repeat(depth);
2252        let kind = match &layer.kind {
2253            LayerKind::Picture { bounds, .. } => format!(
2254                "Picture(local_bounds={bounds:?}, world_bounds={:?}, clip={clip:?})",
2255                world_transform.transform_rect_bbox(*bounds)
2256            ),
2257            LayerKind::Transform { transform } => {
2258                format!(
2259                    "Transform(local={:?}, world={:?})",
2260                    transform,
2261                    world_transform.then(*transform)
2262                )
2263            }
2264            LayerKind::ClipRect { rect, .. } => format!(
2265                "ClipRect(local={rect:?}, world={:?})",
2266                world_transform.transform_rect_bbox(*rect)
2267            ),
2268            LayerKind::ClipRRect { rrect, .. } => format!(
2269                "ClipRRect(local={rrect:?}, world={:?})",
2270                world_transform.transform_rect_bbox(rrect.rect)
2271            ),
2272            LayerKind::ClipOval { rect, .. } => format!(
2273                "ClipOval(local={rect:?}, world={:?})",
2274                world_transform.transform_rect_bbox(*rect)
2275            ),
2276            LayerKind::ClipPath { path, .. } => format!(
2277                "ClipPath(local_bounds={:?}, world={:?})",
2278                path.bounds(),
2279                clip_world_bounds(&layer.kind, world_transform)
2280            ),
2281            LayerKind::Opacity { alpha } => format!(
2282                "Opacity(alpha={alpha:.3}, bounds={:?}, generation={})",
2283                self.subtree_bounds(id, world_transform),
2284                self.subtree_generation(id)
2285            ),
2286            LayerKind::Blur { blur } => format!(
2287                "Blur(sigma=({:.3},{:.3}), bounds={:?}, generation={})",
2288                blur.sigma_x,
2289                blur.sigma_y,
2290                self.subtree_bounds(id, world_transform),
2291                self.subtree_generation(id)
2292            ),
2293            LayerKind::DropShadow { shadow } => format!(
2294                "DropShadow(sigma=({:.3},{:.3}), offset={:?}, bounds={:?}, generation={})",
2295                shadow.sigma_x,
2296                shadow.sigma_y,
2297                shadow.offset,
2298                self.subtree_bounds(id, world_transform),
2299                self.subtree_generation(id)
2300            ),
2301            LayerKind::ColorFilter { filter } => format!(
2302                "ColorFilter(matrix={:?}, bounds={:?}, generation={})",
2303                filter.to_matrix(),
2304                self.subtree_bounds(id, world_transform),
2305                self.subtree_generation(id)
2306            ),
2307            LayerKind::Blend { mode } => format!(
2308                "Blend(mode={mode:?}, bounds={:?}, generation={})",
2309                self.subtree_bounds(id, world_transform),
2310                self.subtree_generation(id)
2311            ),
2312            LayerKind::ShaderMask {
2313                blend_mode,
2314                mask_size,
2315                ..
2316            } => format!(
2317                "ShaderMask(blend_mode={blend_mode:?}, mask_size={mask_size:?}, bounds={:?}, generation={})",
2318                self.subtree_bounds(id, world_transform),
2319                self.subtree_generation(id)
2320            ),
2321            LayerKind::BackdropFilter {
2322                blur,
2323                blend_mode,
2324                enabled,
2325                matrix,
2326                backdrop_key,
2327            } => format!(
2328                "BackdropFilter({}{}, blend_mode={blend_mode:?}, enabled={enabled}, bounds={:?}, generation={})",
2329                match matrix {
2330                    Some(matrix) => format!("matrix={matrix:?}"),
2331                    None => format!("sigma=({:.3},{:.3})", blur.sigma_x, blur.sigma_y),
2332                },
2333                backdrop_key.map_or_else(String::new, |key| format!(", key={key:?}")),
2334                self.subtree_bounds(id, world_transform),
2335                self.subtree_generation(id)
2336            ),
2337            LayerKind::AnnotatedRegion { sized, size, .. } => format!(
2338                "AnnotatedRegion(sized={sized}, size={size:?}, generation={})",
2339                self.subtree_generation(id)
2340            ),
2341            LayerKind::Leader { link, size, .. } => format!(
2342                "CompositedTransformTarget(size={size:?}, linked={}, generation={})",
2343                link.is_linked(),
2344                self.subtree_generation(id)
2345            ),
2346            LayerKind::Follower(FollowerLayer {
2347                show_when_unlinked,
2348                offset,
2349                target_anchor,
2350                follower_anchor,
2351                ..
2352            }) => format!(
2353                "CompositedTransformFollower(show_when_unlinked={show_when_unlinked}, offset={offset:?}, target_anchor={target_anchor:?}, follower_anchor={follower_anchor:?}, bounds={:?}, generation={})",
2354                self.subtree_bounds(id, world_transform),
2355                self.subtree_generation(id)
2356            ),
2357        };
2358        out.push_str(&format!("{indent}{id:?} parent={parent:?} {kind}\n"));
2359        let (next_translation, next_clip) = match &layer.kind {
2360            LayerKind::Picture { .. } => (world_transform, clip),
2361            LayerKind::Transform { transform: local } => (world_transform.then(*local), clip),
2362            LayerKind::ClipRect { rect, .. } => {
2363                let world = world_transform.transform_rect_bbox(*rect);
2364                (
2365                    world_transform,
2366                    Some(clip.map_or(world, |old| old.intersection(world).unwrap_or(world))),
2367                )
2368            }
2369            LayerKind::ClipRRect { .. }
2370            | LayerKind::ClipOval { .. }
2371            | LayerKind::ClipPath { .. } => {
2372                // An empty path keeps the incoming clip: this dump never
2373                // culls, it only narrows what children report.
2374                let narrowed = clip_world_bounds(&layer.kind, world_transform).map(|world| {
2375                    clip.map_or(world, |old| old.intersection(world).unwrap_or(world))
2376                });
2377                (world_transform, narrowed.or(clip))
2378            }
2379            LayerKind::Opacity { .. } => (world_transform, clip),
2380            LayerKind::Blur { .. } | LayerKind::DropShadow { .. } => (world_transform, clip),
2381            LayerKind::ColorFilter { .. } | LayerKind::Blend { .. } => (world_transform, clip),
2382            LayerKind::ShaderMask { .. }
2383            | LayerKind::BackdropFilter { .. }
2384            | LayerKind::AnnotatedRegion { .. }
2385            | LayerKind::Leader { .. } => (world_transform, clip),
2386            LayerKind::Follower(follower) => (
2387                self.follower_transform(world_transform, follower)
2388                    .unwrap_or(world_transform),
2389                clip,
2390            ),
2391        };
2392        for child in &layer.children {
2393            self.write_debug_at(
2394                *child,
2395                Some(id),
2396                depth + 1,
2397                next_translation,
2398                next_clip,
2399                out,
2400            );
2401        }
2402    }
2403}
2404
2405fn brush_generation(brush: &Brush) -> u64 {
2406    match brush {
2407        Brush::Solid(color) => {
2408            u64::from(color.red)
2409                | (u64::from(color.green) << 8)
2410                | (u64::from(color.blue) << 16)
2411                | (u64::from(color.alpha) << 24)
2412        }
2413        Brush::LinearGradient(gradient) => {
2414            gradient.stops.id().get()
2415                ^ u64::from(gradient.start.x.to_bits()).rotate_left(7)
2416                ^ u64::from(gradient.start.y.to_bits()).rotate_left(13)
2417                ^ u64::from(gradient.end.x.to_bits()).rotate_left(19)
2418                ^ u64::from(gradient.end.y.to_bits()).rotate_left(23)
2419                ^ (gradient.tile_mode as u64).rotate_left(29)
2420        }
2421        Brush::RadialGradient(gradient) => {
2422            gradient.stops.id().get()
2423                ^ u64::from(gradient.center.x.to_bits()).rotate_left(7)
2424                ^ u64::from(gradient.center.y.to_bits()).rotate_left(13)
2425                ^ u64::from(gradient.radius.to_bits()).rotate_left(19)
2426                ^ (gradient.tile_mode as u64).rotate_left(29)
2427                ^ gradient.focal.map_or(0, |focal| {
2428                    u64::from(focal.x.to_bits()).rotate_left(31)
2429                        ^ u64::from(focal.y.to_bits()).rotate_left(37)
2430                })
2431                ^ u64::from(gradient.focal_radius.to_bits()).rotate_left(41)
2432        }
2433        Brush::SweepGradient(gradient) => {
2434            gradient.stops.id().get()
2435                ^ u64::from(gradient.center.x.to_bits()).rotate_left(7)
2436                ^ u64::from(gradient.center.y.to_bits()).rotate_left(13)
2437                ^ u64::from(gradient.start_angle.to_bits()).rotate_left(19)
2438                ^ (gradient.tile_mode as u64).rotate_left(29)
2439        }
2440    }
2441}
2442
2443fn transform_generation(transform: Transform) -> u64 {
2444    transform
2445        .to_kurbo()
2446        .as_coeffs()
2447        .iter()
2448        .enumerate()
2449        .fold(0, |value, (index, coefficient)| {
2450            value ^ coefficient.to_bits().rotate_left((index as u32 * 9) % 63)
2451        })
2452}