Skip to main content

Module clip

Module clip 

Source
Expand description

Interior clips — the main-world half of clip-independent layer capture.

A promoted layer’s capture must not bake ancestor clipping (scroll containers / viewport) into its cached pixels: the cache treats scroll as pure translation (see super::fold_member_geometry), so pixels captured under an ancestor clip would be stale the moment the layer scrolls. Web semantics agree: filter applies to the element’s full painted content, and ancestor overflow clips the result at composite time.

sync_layer_clips therefore re-runs bevy_ui‘s clip cascade (update_clipping semantics: a node’s inherited clip is its ancestors’ overflow intersection; its own overflow only clips its children) — but restarted at each layer root with no inherited clip. The result is two per-frame maps in LayerClips:

  • interior: per subtree member, the clip its extracted items should use inside the capture — clips originating inside the subtree still apply (they move with the layer, so cached pixels stay valid); everything above the root is stripped. Consumed by the render world’s extract-window swap (layer::render::clip), which substitutes these into the members’ CalculatedClip for exactly the duration of ExtractSchedule — the main world is exclusively borrowed there, so no main-world system (picking, focus) can ever observe the swap, and every stock extractor (backgrounds, text, gradients, box shadows, texture slices) picks the interior clips up uniformly. Text self-clips (TextScroll content boxes) recompose automatically in the stock text extractors from the swapped value.
  • quads: per layer root, the screen-space rect its composite quad clamps to instead — a top-level root’s inherited CalculatedClip, or, for a nested root, the enclosing layer’s interior cascade value at the root (its quad draws inside the enclosing capture).

Outside the extract window the real CalculatedClip values are always in place: picking (crate::pick_clip) and bevy_ui’s own consumers keep seeing true inherited clips.

Known divergence (deliberate, rare): an OverrideClip escapee inside a subtree is still clamped by the layer’s quad clip at composite time (stock lets it escape every ancestor clip).

Offscreen layers still capture (when dirty) like any other — a fully clipped-away quad simply draws nothing. The cost is invisible re-captures for continuously-animated offscreen layers; the win is that scrolling never re-captures and a layer scrolled into view is correct by construction (the bug this module fixes: captures taken under clipping were cached and served stale after scrolling).

Structs§

LayerClips
Per-frame clip maps for promoted layers, rebuilt from scratch by sync_layer_clips (like LayerMembership — nothing persists).

Functions§

sync_layer_clips
Rebuilds LayerClips for this frame. Runs in PostUpdate after super::sync_layer_geometry (fresh LayerMembership) and after bevy_ui’s UiSystems::PostLayout (final CalculatedClip values for the top-level quad clips). Must NOT feed super::resolve_layer_repaints: clip changes never dirty a capture — that is the point.