Skip to content

perf(ml): C8 interaction tests + shared renderer ladder (recreated #219) - #229

Merged
tiensonqin merged 24 commits into
feat/ml-backendfrom
perf/ml-c8-interaction-tests
Oct 13, 2026
Merged

tiensonqin merged 24 commits into
feat/ml-backendfrom
perf/ml-c8-interaction-tests

Conversation

@tiensonqin

Copy link
Copy Markdown
Collaborator

Recreates #219, which GitHub auto-closed (base_ref_deleted) when its base #217 merged with branch deletion. Same C8 changes: headless harness asserts Ui.handle actions + store state (Linux/Windows), shared Lui_window.Renderer_ladder with demote tests. Merged feat/ml-backend in (replayed only C8 delta since #217 was squashed).

tiensonqin and others added 24 commits October 13, 2026 04:30
A secure-field's real text went straight into the text hook and
painted as plaintext; the a11y password flag only marks the tree.
The paint pass now substitutes one bullet per grapheme before the
string reaches the text engine, and the window text engines apply the
same mask to any string that bypasses the paint resolver (the IME
composition overlay's marked text) and to every geometry answer —
measure, caret prefix, pointer-to-offset — so caret/selection/IME
geometry is computed on the masked text. Masked hit offsets map back
to real byte offsets through the same grapheme boundaries.

The grapheme segmenter is a compact UAX #29 subset kept engine-free
in Lui_paint so paint and all three window engines share one mask
shape. The gallery cell no longer hides the bug behind a literal
"********" — it passes a real value and relies on the mask.

Tests: lui_paint_test asserts the text hook only ever receives the
mask (fails on the old code, which handed it the plaintext), plus
grapheme/mask/offset-mapping unit cases.

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

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Paint and hit-test resolved stacking independently and disagreed:
paint sorted siblings by z-index and promoted popup kinds out of
ancestor clips into a deferred overlay pass, while hit_path walked
raw child order and the drivers' hit mirrors baked an approximate
scroll-ancestor clip into stored rects. A z-ordered node could paint
on top yet lose the click to a DOM-later sibling, and a popover
inside a scroll container could paint outside the clip yet never be
hit.

Lui_paint gains walk — the traversal producing each node's resolved
stacking/clip frame (z-ordered siblings, popup promotion with a fresh
clip context, display:contents pass-through, accumulated ancestor
clip). paint now runs as enter/leave callbacks on the walk emitting
the same op stream, and Ui.hit_path runs the same walk over the
driver's resolved rects (fed back as placement overrides) with only
the per-node contains check on top.

Driver hit mirrors in all three main.ml files stop baking
ancestor-clip intersections into stored rects — each node keeps its
full resolved box and the walk recomputes the clip with the exact
clips rule.

Tests: three new hit cases in lui_window_test — z-index order wins
the click, a popover inside a clipped scroll container is hittable
outside the clip, and a mid-tree popup wins over a later sibling
covering the same spot. All fail on the old code.

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

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Keep both: masked hit_text from C1 and caret_ops from C7 (#201).
caret_ops lays out the bullet mask for secure fields and maps offsets
through secure_masked_of_byte / secure_byte_of_masked.
…ling

The viewport cull dropped a node's whole subtree when its own box
missed the frame, losing overflow:visible children, position:fixed
descendants (which resolve against the frame) and promoted popups.

paint_enter now skips only the node's own chrome and keeps walking;
the true subtree cull lives in the shared walk as a dead-clip path —
a clip that misses the viewport can contain no output, so the walk
switches to a ghost pass that skips enter/chrome cost and only hunts
promoted popups, still threading alpha and hit flags down the path.

hit_path gives the walk the pointer point as its viewport so clips
that miss it prune; popup escapes keep their ancestors' flags.

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

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
… walk

C8: the Linux/Windows "interaction" tests drove Ui.handle but dropped
the returned actions and only compared two runs' scene checksums —
inputs whose dispatches never reached the model could still "pass".
Likewise the renderer-fallback coverage only asserted the ladder
ordering and the no-op renderer's frame size, a CPU→CPU comparison
that never touched the driver's real step-down path.

- Lui_window.Renderer_ladder now owns the walk/demote/renderer_live
  sequencing both drivers wrote by hand: try_rung init failures step
  down, the untried tail is kept for runtime demotion, release and a
  per-demote window re-fit hook come from the platform. The SDL and
  Win32 mains wire their rung inits into it unchanged.
- The headless harness primes the hit-test mirror before the first
  scripted event (previously frame 1's click tested against empty
  rects and always missed), records every action list, and feeds
  Dispatch events back into the app the way apply_actions does.
- test_headless_actions asserts the actions themselves (Focus_changed,
  Press, TextChanged, Submit) and the resulting store mutations:
  on Linux the submitted draft mounts as a new item and clears the
  field; on Windows the Name field's text and the last-event line
  update. The Windows variant addresses the autofocused field by
  prop and its laid-out rect rather than guessed coordinates.
- test_ladder_demote injects init failures on the GPU rungs and a
  mid-render device loss on the CPU rung through the same
  Renderer_ladder the drivers use, asserting walk order, release,
  and that renderer_live retries on the next rung inside the same
  render call.

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

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
C6 moved the hit-mirror populate into the shared Assembly; carry C2's
'no clip baked into the mirror' rule there and update the assembly
scroll test to assert the clip via hits instead of mirror heights.
# Conflicts:
#	platform/native/lui_window_linux/main.ml
#	platform/native/lui_window_linux/test/lui_window_linux_test.ml
#	platform/native/lui_window_windows/test/lui_window_windows_test.ml
P1-1 moved hit testing into Lui_input.Lui_query: port the shared
stacking/clip hit walk there (lui_input now depends on lui_paint) and
keep the unclipped Assembly hit mirror.
Hit walk now lives in Lui_query: port C4's point-viewport pruning and
ghost_enter flag threading there.
#207 (old base) landed as a squash; resolved by replaying only C4's own
delta onto feat/ml-backend.
#217 (old base) landed as a squash: replay only C8's delta. Keeps both
the Frame scheduler (#209) and Renderer_ladder. Includes the
resolve_in restore from #228 so this builds on today's base.
@tiensonqin
tiensonqin merged commit 2ba097a into feat/ml-backend Oct 13, 2026
8 checks passed
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