Skip to main content

incular_rendering/
compositor.rs

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