Skip to content

fix(ml): retained paint compatible with C2/C4 subtree walk - #231

Merged
tiensonqin merged 2 commits into
feat/ml-backendfrom
fix/ml-retained-after-c4
Oct 13, 2026
Merged

tiensonqin merged 2 commits into
feat/ml-backendfrom
fix/ml-retained-after-c4

Conversation

@tiensonqin

Copy link
Copy Markdown
Collaborator

Fixes lui_retained after #224 landed on top of C2/C4 (walk incompatibility / diff loop).

tiensonqin and others added 2 commits October 13, 2026 08:48
lui_retained was written against the pre-C2 paint API (resolve_in,
paint_node_body, ordered_children, node_shown) that #207 C2 and #217
C4 replaced with the shared Lui_paint.walk. Re-plumb the fragment
cache onto the same decisions the walk makes — geometry/clip
threading via a new Lui_paint.frame_of, dead-clip culling via
Lui_paint.clip_dead, chrome and clip brackets via
paint_enter/paint_leave, ghost traversal via paint_ghost_enter — so
retained and full paint produce identical scenes by construction.

Popup handling now mirrors the deferred drain: popup-kind roots
promote too, and each subtree's sub_pops lists only the next drain
level so popups nested inside popups surface level-by-level in
forest order.

Invalidation gaps the journal replay missed:

- seg_dirty flags outlive the per-frame worklist on culled branches;
  a fresh mark on one never re-entered pend_segs, so stale pieces
  kept emitting through a parent's memoized Sub.
- Reparenting changes every descendant's inherited context
  (foreground, inverse-surface background), but only the moved child
  was repainted; InsertChild/RemoveChild now repaint the subtree, and
  foreground / tooltip-background prop changes mark their readers.
- Child-list mutations can flip a sibling's last-sibling chrome
  without touching the node itself; was_last tracking repaints both
  sides of the flip.

Correctness fixes outside the cache:

- The geometry-translation fast path assumed every absolute
  coordinate in cached ops tracks the node box; the modal scrim
  fills the viewport and must never translate. New
  Lui_paint.viewport_anchored gates the path.
- Lui_store RemoveChild's defensive scan cleared c.parent even when
  the child was not in the named list, leaving it dual-membershipped
  (root + live list) — the seed of sib-list cycles and hangs.

The random differential test now mutates across 14 op kinds —
z-index, nested popups, scroll/overflow clips, offscreen placement,
display:contents, reparents, prop removal — and asserts scene
equality with a full paint every frame.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
lui_paint walk: keep clip_dead helper and frame_of, plus the M3
mounted-window kids; retained vlist rekids passes store.
@tiensonqin
tiensonqin merged commit 71ac999 into feat/ml-backend Oct 13, 2026
8 checks passed
tiensonqin added a commit that referenced this pull request Oct 13, 2026
body: adopt frame_of; keep mounted/spatial child dispatch.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant