IRIS_TODO.md's "the composer has no touch-drag scroll". `Scroll::drag`
takes its pan from the same `sense::DragGesture` `List` is driven by --
arbitration, DRAG_SLOP, velocity and pointer capture all stay in sense.rs
and only what a committed pan *means* is decided per caller -- and
`WidgetLike::scrollable()` registers it beside the wheel handler it already
registered, so every scroll area pans on a finger with nothing added at the
call site. No fling: `Scroll` has no per-frame tick to animate one and the
areas it wraps are at most a screenful. `Scroll::amt()` exposes the pan
position.
`attr.rs`'s `on_press` treated an already-focused field as the plain
click_or_drag case, so every Pressing frame extended a selection. It now
applies the same DRAG_SLOP rule its unfocused branch already did: a press
past the slop vertically abandons its pending selection for the rest of the
gesture, so the scroll area around the field wins it. That is Android
EditText's own behaviour and it is what lets a swipe up over the composer
scroll instead of dragging a highlight through what you typed.
Also fixed, found doing it: `ActiveData::mask` stored the mask a widget
*set* rather than the one it was drawn *under*, and `redraw` feeds that
field back in as the inherited mask -- so a targeted redraw of any `Masked`
handed it its own mask and aborted on `set_mask`'s nested-mask assert. A
real abort on the emulator, `assertion failed: self.mask == MaskIdx::NONE`.
And the per-frame orphan guard from 76b1f99 is now a count comparison
(O(active widgets)); the O(primitives) walk only runs to build the failure
message, because running it per frame made a debug build on the emulator too
slow to finish a bench run at all.
Tests: four in scroll.rs (pan past the slop, a tap inside it, a horizontal
drag, the end clamp), `a_finger_drag_over_a_scroll_area_pans_it` in
sense_tests.rs driving the whole registration/dispatch/capture path (fails
with "got 0" without the new registration), and
`redrawing_a_masked_widget_does_not_nest_its_own_mask` in layout_tests.rs
(aborts on the pre-fix code).
The composer itself is deliberately still not `.scrollable()`: `Scroll`
measures against the window rather than its own offered box, so inside the
`MaxSize` capping it at six lines it pans the field out of the bar --
measured, reverted and written down in RUST.md and DECISIONS.md.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
67 lines
2.8 KiB
Markdown
67 lines
2.8 KiB
Markdown
# iris bench v2 report from Iris's phone, 2026-09-06
|
|
|
|
Build 2e3f4ad (bench v2, fling physics, keyboard-wipe fix, dp unit), run
|
|
by Iris on her Pixel 9 Pro XL, verbatim. The display was at **120 Hz**
|
|
(8.3 ms budget) where `compose-phone-v2-2026-09-06.md` ran at 60 Hz, so
|
|
compare the millisecond percentiles, not `late`.
|
|
|
|
Side by side (Compose 60 Hz / iris 120 Hz, p50 / p90 / p99 ms): fling
|
|
5.5/8.7/11.6 vs 3.8/6.9/12.6; stream 13.4/31.7/42.5 vs 18.2/35.8/43.1;
|
|
type 7.3/13.2/16.5 vs 7.2/9.2/11.2; keyboard: iris could not show the IME
|
|
(phase invalid). Process CPU 69.6 s over 125 s vs 40.6 s over 150 s; peak
|
|
RSS 577 MB vs 379 MB; battery current mean 571 mA vs 452 mA.
|
|
|
|
Iris's observations on the same run: "the scrolling is not similar at
|
|
all. It does not fling for me yet [with a finger], and the test also seems
|
|
to give it a constant velocity and abruptly stop it at some point. Also
|
|
unsure what's going on in that image with the compaction" -- her
|
|
screenshot shows the `Compacted: 180000 -> 20000 tokens.` row drawn twice
|
|
overlapping, and once more below the composer bar: primitives of a
|
|
replaced/removed row surviving in the GPU buffers, the same shape as the
|
|
header drawn twice after a keyboard resize.
|
|
|
|
**Root-caused and fixed 2026-09-06** (commit `76b1f99`): the diagnosis in
|
|
that sentence was right and the location was not -- `UiRenderState::
|
|
draw_inner` read the `needs_redraw` mark without consuming it and skipped
|
|
the branch that frees a redrawn widget's old primitives. docs/RUST.md's
|
|
"Stale primitives, the phone's half" box has the full account, the guard
|
|
(`orphaned_primitives`, `debug_assert`ed every frame) and the emulator run
|
|
that exercises it.
|
|
|
|
```
|
|
iris bench report
|
|
per phase:
|
|
fling: 1783 frames over 53.2s
|
|
late: 104 (5.8%)
|
|
total p50 3.8ms p90 6.9ms p99 12.6ms
|
|
worst 29.1ms
|
|
stream: 401 frames over 21.3s
|
|
late: 306 (76.3%)
|
|
total p50 18.2ms p90 35.8ms p99 43.1ms
|
|
worst 43.8ms
|
|
type: 1202 frames over 65.7s
|
|
late: 309 (25.7%)
|
|
total p50 7.2ms p90 9.2ms p99 11.2ms
|
|
worst 15.3ms
|
|
keyboard: 9 frames over 9.7s
|
|
late: 9 (100.0%)
|
|
total p50 12.0ms p90 12.9ms p99 12.9ms
|
|
worst 12.9ms
|
|
|
|
frames:
|
|
3395 frames over 149.9s at 120Hz (8.3ms budget)
|
|
late: 728 (21.4%)
|
|
total p50 5.0ms p90 10.9ms p99 36.6ms
|
|
worst 43.8ms
|
|
cpu_p50 2.0ms gpu_wait_p50 2.6ms
|
|
|
|
bench:
|
|
fling: 8 flings out + 8 back at 12000px/s, travel start=idx=651/off=1217px outward=idx=651/off=101536px end=idx=651/off=1022px
|
|
scroll: 6 cycles (24 swipes, legacy tween), streamed 400/400 fixture events
|
|
type: 600 characters inserted then deleted, one per 50ms
|
|
keyboard: could not be shown (5 attempts, 0 confirmed visible)
|
|
process CPU time over this run: 40603ms
|
|
peak RSS: 379156kB
|
|
battery current: mean -452353µA over 149 samples (min -1753125, max -204687)
|
|
```
|