docs: I5's clean scroll comparison between Compose and iris, one session

Same sandbox session content, same emulator, EMU_GPU=software: Compose
(debug, in-app report) 1102 frames/99.0% late/p50 33.8ms/p99 79.5ms vs
iris (release -- debug SIGSEGVs on this emulator) FrameReport 299
frames/94.65% janky/p50 79.1ms/p99 117.8ms (repeat: 233/94.42%/p50
109.3ms). Ticks I5 [x]; states plainly what's not comparable (build
profile forced asymmetric, three different jank definitions, both are
software-rasterised emulator numbers). The two "zero frames" attempts
that preceded the clean runs traced to this session's own script bug
(a cd into /tmp changed which emulator ui-trace targeted), not a
reproduction of the previously-suspected touch-delivery dropout; a
sampler ran the whole session and saw load rise during the gesture
without correlating to any failure. DECISIONS.md's DEFERRED item gets
the same table so Iris can decide iris-vs-Masonry from it -- that
choice is left to her.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
irisandClaude Fable 5.1 committed 2026-09-05 15:21:45 -04:00
1 parent 1e7b1cddb7
commit 0e4629361b
2 files changed
+222 -56

No files matched your search

+36 -21
View File
@@ -66,26 +66,41 @@ marked **DEFERRED** are ones the agent chose not to decide alone.
pieces and I5's Android integration are all done; next is giving iris pieces and I5's Android integration are all done; next is giving iris
its own frame-timing report so item 3 below can be decided by a number. its own frame-timing report so item 3 below can be decided by a number.
- **DEFERRED — whether to commit to iris over Masonry for `ai-app`.** - **DEFERRED — whether to commit to iris over Masonry for `ai-app`.**
Updated 2026-09-05 (later the same day) with what's now known: iris has Updated 2026-09-05 with the clean comparison the recommendation wanted:
its own frame-timing report now (`FrameReport`) and a real number from same sandbox session content, same emulator, `EMU_GPU=software`, one
it, captured from a genuine on-device touch-drag — session. Headline numbers (RUST.md's I5 box, "Clean scroll comparison,
`frames=34 janky%=61.76 p50=26.5ms p90=48.0ms p99=98.1ms worst=98.1ms` 2026-09-05," has the full table and every caveat):
— plus a confirmed on-device long-press-then-drag-to-select (logcat and
a screenshot of the highlighted selection). It is **not yet the clean, | app | build | frames | janky % | p50 | p90 | p99 | worst |
comparable number** the recommendation wants: gestures against this |---|---|---|---|---|---|---|---|
checkout's `EMU_GPU=software` emulator intermittently delivered zero | Compose (in-app report) | debug | 1102 | 99.0% late | 33.8ms | 50.6ms | 79.5ms | -- |
touch input during this session, reproducibly, for a reason named but | Compose (`dumpsys gfxinfo`) | debug | 1499 | 21.15% (95.66% legacy) | 32ms | 48ms | 150ms (p99) | -- |
not yet confirmed (the emulator's own software rasterisation measured | iris (`FrameReport`) | **release** | 299 | 94.65% | 79.1ms | 98.6ms | 117.8ms | 212.6ms |
at ~78% of a CPU core continuously — a plausible source of input | iris (`FrameReport`, repeat) | **release** | 233 | 94.42% | 109.3ms | 130.8ms | 147.1ms | 150.5ms |
backlog, not yet isolated with a sampler running during a failing
gesture). The Compose figure quoted for comparison (8.96% janky, 99th **Not a clean apples-to-apples reading, stated plainly rather than
percentile 150ms) is the same one already recorded earlier 2026-09-05, smoothed over**: iris had to be built **release** (debug `SIGSEGV`s on
not re-taken this pass, and against a different sandbox session's this emulator's Vulkan loader, I4's finding) against Compose's mandated
content — so the two numbers share configuration, not identical data. **debug** build, so this asymmetry likely *understates* iris's gap
rather than the reverse; the three frame-time sources measure different
things (Compose's own phase accounting vs. Android's HWUI deadline-miss
definition vs. iris's redraw-start-to-present window, the last of which
`dumpsys gfxinfo` cannot see at all for iris's `SurfaceView`); and both
figures are emulator numbers under software rasterisation, which
Compose's *own* in-app report shows already costs 20-34ms/frame in
`swap`+`gpu` alone under this GPU mode, so a same-mode iris number well
above 16.7ms was expected going in for either app. A second pair under
`-gpu host` was not taken this pass. The earlier session's suspected
intermittent touch-delivery dropout was **not reproduced** this pass —
the zero-frame results this time traced to this pass's own script bug
(a `cd` that changed which emulator `ui-trace` targeted), not the
emulator; a CPU-load rise during the gesture was observed by a sampler
running throughout, but did not correlate with any failure, so the
original candidate is neither confirmed nor ruled out.
The choice in front of Iris, updated: decide now on the The choice in front of Iris, updated: decide now on the
structural-plus-functional case already made (iris works end-to-end structural-plus-functional case already made (iris works end-to-end
where Masonry's scroll gesture doesn't exist at all on Android, plus a where Masonry's scroll gesture doesn't exist at all on Android) plus
real if narrowly-scoped frame-timing number), or wait for the this table — reading the two build profiles and three jank definitions
touch-delivery investigation above so the comparison can be the single with the caveats above rather than as a single number — or ask for a
clean 24-swipe loop rather than a number gathered around an unresolved same-profile, same-GPU-mode rerun first. RUST.md's I5 box has the full
rig flakiness. RUST.md's "Recommendation" item 3 has the full account. account.
+186 -35
View File
@@ -36,24 +36,26 @@ session spending an afternoon on them again.
## Where things stand (2026-09-05) ## Where things stand (2026-09-05)
- **iris now has its own frame-timing report and a confirmed on-device - **I5 is now `[x]`: a clean, single-session, like-for-like 24-swipe
long-press-drag, 2026-09-05, later the same day (I5's box still `[~]`, scroll comparison between Compose and iris exists, 2026-09-05.** Same
not `[x]` -- see I5's own box for exactly why).** `iris_core::FrameReport` sandbox session content for both apps, same emulator, `EMU_GPU=software`
(new) times every frame from `render()`'s redraw start to after (a second pair under `-gpu host` not yet taken). Headline: Compose
`queue.submit`+`present()`, exposed as two named on-screen controls, and (debug build) 1102 in-app-reported frames, 99.0% late, p50 33.8ms/p90
a new `ui-trace holddrag` action (in `emulator-tools`) can finally 50.6ms/p99 79.5ms; iris (**release** build -- debug `SIGSEGV`s on this
produce a real hold-then-drag touch sequence. Both were driven for emulator, I4's finding) `FrameReport` 299 frames, 94.65% janky, p50
real: long-press-then-drag-to-select is confirmed on-device (logcat plus 79.1ms/p90 98.6ms/p99 117.8ms/worst 212.6ms (repeat run: 233 frames,
a screenshot of the highlighted selection), and `FrameReport` captured a 94.42%, p50 109.3ms). **Not a clean apples-to-apples number**: different
real number from a real touch-drag (`frames=34 janky%=61.76 p50=26.5ms build profiles (forced, not chosen), different jank definitions/frame
p90=48.0ms p99=98.1ms worst=98.1ms`). **What's still missing**: a clean, populations across the three measurement sources, and both are emulator
single 24-swipe loop comparable to Compose's own number -- gestures numbers under software rasterisation -- all stated plainly in I5's own
against this checkout's `EMU_GPU=software` emulator intermittently box, "Clean scroll comparison, 2026-09-05," which also has the
delivered zero touch input during this pass, for a reason named but not sampler timeline (load rose during the gesture but did not correlate
yet confirmed (candidate: the emulator's own software rasterisation, with a failure this pass) and the dropout finding (this pass's own
measured at ~78% of a CPU core continuously, starving synthetic touch script bug -- `cd`ing into `/tmp` changed which emulator `ui-trace`
delivery under load). Next: a sampler running *during* a failing gesture targeted -- not a reproduction of the previously-suspected touch-
to confirm or rule out that candidate, then the clean comparable loop. delivery starvation). `DECISIONS.md`'s DEFERRED item has this table's
numbers for Iris to decide from; the iris-vs-Masonry choice itself is
still hers to make, not decided here.
I5's own box, "Update, 2026-09-05, later the same day" has the full I5's own box, "Update, 2026-09-05, later the same day" has the full
account. account.
- **I5's Android integration is done and measured, 2026-09-05.** The - **I5's Android integration is done and measured, 2026-09-05.** The
@@ -2344,23 +2346,19 @@ silently on real hardware.
time it was hit (mid-session, after adding an unrelated temporary time it was hit (mid-session, after adding an unrelated temporary
log line forced a dev rebuild) and cost real time to separate from log line forced a dev rebuild) and cost real time to separate from
the actual touch-dispatch question being chased at the time. the actual touch-dispatch question being chased at the time.
- [~] **I5 — the transcript screen in iris (2026-09-05, updated later the - [x] **I5 — the transcript screen in iris (2026-09-05, updated later the
same day). The widget-tree half and the Android integration are same day, and again 2026-09-05 with the clean scroll comparison).
both built and confirmed working on-device (real server, real The widget-tree half and the Android integration are both built and
scrolling, real touch-drag pan, tap-by-name), iris now has its own confirmed working on-device (real server, real scrolling, real
frame-timing instrumentation (`FrameReport`) and produced a real, touch-drag pan, tap-by-name), iris has its own frame-timing
measured number from a real on-device touch-drag, and long-press- instrumentation (`FrameReport`), and long-press-then-drag-to-select
then-drag-to-select is now confirmed on-device too (both by logcat is confirmed on-device (both by logcat and by a screenshot showing
and by a screenshot showing the highlighted selection); still `[~]` the highlighted selection). Ticked `[x]` now that a clean,
rather than `[x]` because the frame-time number obtained is not the single-session, like-for-like 24-swipe comparison against Compose
clean, single 24-swipe loop comparable to Compose's -- gestures exists -- see "Clean scroll comparison, 2026-09-05" near the end of
against this checkout's `EMU_GPU=software` emulator intermittently this box for the numbers, what is and is not comparable between the
produced zero frames for reasons not yet isolated (candidate: the two, and the dropout finding (this pass's own script bug, not a
emulator's own software rasterisation measured at ~78% of a CPU reproduction of the emulator touch-delivery candidate below).**
core, continuously, which may be starving synthetic touch delivery
under load -- not yet confirmed with a sampler running during the
failing gesture). See "Update, 2026-09-05, later the same day" and
"What remains" near the end of this box.**
**Where it lives.** `iris/transcript-ui/` (new workspace member, **Where it lives.** `iris/transcript-ui/` (new workspace member,
`[lib]`), the same shape as `iris/tabs-ui`: generic over `Rsc: `[lib]`), the same shape as `iris/tabs-ui`: generic over `Rsc:
@@ -2861,6 +2859,159 @@ silently on real hardware.
DEFERRED item is updated with this session's numbers and the DEFERRED item is updated with this session's numbers and the
touch-delivery caveat rather than a decision made here. touch-delivery caveat rather than a decision made here.
**Clean scroll comparison, 2026-09-05, one session, this checkout's
emulator, `EMU_GPU=software` only (the mode the existing Compose
figure above was taken under; a second pair under `-gpu host` was
not reached this pass -- see "Not attempted" below).** New content
for a fair pairing: a fresh sandbox session (`app/ui-sandbox.sh
spawn benchsession`, id `4d21d4a0d38f79fd`) with 30 identical sent
messages, each one heading/bold/italic/inline-code/link/list/fenced-
code paragraph, so both apps scroll the exact same bytes -- neither
of the two sessions quoted in this box's earlier passes (`8920378e
7167ebcd`, 40 msgs; `c76b71d017a54589`, 120 msgs) was reused, since
neither app had touched it. A sampler
(`date`/`/proc/loadavg`/`/proc/pressure/{cpu,io}`/top-5-by-CPU every
2s to `/tmp/iris-bench-sampler.log`) ran for the whole session,
started before either app was built, per the standing rule against
diagnosing a timing question from measurements taken after the
fact.
| app | build | GPU mode | frames | janky % | p50 | p90 | p99 | worst |
|---|---|---|---|---|---|---|---|---|
| Compose (in-app report) | debug | software | 1102 | 99.0% late | 33.8ms | 50.6ms | 79.5ms | -- |
| Compose (`dumpsys gfxinfo`) | debug | software | 1499 | 21.15% (95.66% legacy) | 32ms | 48ms | 150ms (99th) | -- |
| iris (`FrameReport`, run A) | **release** | software | 299 | 94.65% | 79.1ms | 98.6ms | 117.8ms | 212.6ms |
| iris (`FrameReport`, run B, repeat) | **release** | software | 233 | 94.42% | 109.3ms | 130.8ms | 147.1ms | 150.5ms |
Exact commands: Compose via `app/transcript-bench.sh` unmodified
(`open_session` then `copy_render_report` bracketing the standard
24-swipe/6-cycle loop, `dumpsys gfxinfo com.example.aiapp reset`
taken immediately before for the second row). iris via the same
24-swipe loop code -- extracted verbatim from
`transcript-bench.sh`'s `DO=""` .. `eval ui-trace record` block with
`sed`, not retyped, since `transcript-bench.sh` itself is Compose-
specific (opens by session title through the Compose app's own UI)
and could not be called directly -- bracketed by `ui-trace record
--do "tap 'Reset frame report'"` and `--do "tap 'Frame report'"`
(iris's own two named controls, I5's earlier "Update" section),
reading the result from `logcat`'s `iris frame report:` line.
**What each number counts, stated because the three are not the
same measurement.** Compose's in-app report times its own
Compose-internal phases (`total` = the full frame from Choreographer
callback to submit) and calls a frame "late" past a 16.7ms budget --
a stricter, self-reported definition. `dumpsys gfxinfo`'s "janky"
is Android's own HWUI/BLAST deadline-miss accounting, a different
threshold and a different frame population (it free-runs over
`Total frames rendered`, which includes frames from opening the
session and the report dialog, not only the swipe window -- hence
1499 vs. the in-app number's 1102). iris's `FrameReport` times
wall-clock from `render()`'s redraw start to after `queue.submit`+
`present()` -- i.e. iris's own render path only, nothing above the
GPU submit and nothing from Android's compositor -- confirmed
independently useless for iris via `dumpsys gfxinfo
dev.iris.android.demo`, which reported 1 total frame for the whole
run (unchanged from the earlier pass's finding: HWUI has no
visibility into a `wgpu`-drawn `SurfaceView`).
**Not comparable, stated plainly:**
- **Build profile differs by necessity, not choice.** Compose is
the **debug** variant (AGENTS.md's own bench-script requirement,
"the emulator scripts stay on the debug build"). iris is
**release** because I4's box already found the debug/dev profile
`SIGSEGV`s in this emulator's Vulkan loader the moment `wgpu`
creates a bind-group layout (`InstanceFlags::from_build_config()`
turns on debug object-labelling, which crashes against
`vulkan.ranchu.so`) -- there is no debug iris number to quote on
this rig. A release build is typically *faster* than debug, so
this asymmetry very likely understates how much worse than
Compose iris's own number would look built the same way Compose's
is, not the reverse.
- **The jank definitions and frame populations differ**, per the
paragraph above -- none of the three numbers is measuring the
same thing, so reading "94.65% > 21.15%" as "4x worse" is not
sound; only the general shape (iris's frames take longer, both by
its own accounting and by eye in the screenshots) transfers.
- **Both figures are emulator numbers under software rasterisation
(`EMU_GPU=software`/SwiftShader), not phone numbers**, per
AGENTS.md's and `this-machine-android`'s standing rule -- restated
because it applies doubly to iris's own number here: SwiftShader
is CPU-bound by design, and Compose's *own* in-app report shows a
14.6-22.2ms `swap` phase and a 20.8-33.9ms `gpu` phase alone (more
than the entire 16.7ms budget) under the same GPU mode, so a
software-rendering iris number well above 16.7ms is expected
going in and should not be read as an iris-specific defect
without a `-gpu host` pair to compare against.
**Where iris's time goes, from `FrameReport`/logcat -- not
optimised, per this task's own instruction, only described.** Two
things were checked because they were checkable without new
instrumentation: (1) **iris does not redraw while idle** --
`adb logcat -c` followed by a 3s settled wait produced *zero*
`iris::android::view: render()` lines, both before and after a
swipe; the render-per-frame spam only appears during and briefly
after a gesture (visible inertial settle), so there is no idle-
redraw tax to find here, unlike the composer-inset bug AGENTS.md
records for the Compose app. (2) **a swipe frame does not appear to
relayout the whole list** -- consecutive `render()` log lines during
a swipe show `active=88` falling to `83`, `78`, `73`, ... one small
step per frame, consistent with I3's virtualised list culling
widgets that scrolled out of the viewport rather than re-measuring
everything each frame (a full-list relayout would show `active`
constant at the total row count, not shrinking through it). Neither
observation isolates *where* the remaining ~80-150ms/frame actually
goes past those two rule-outs -- the leading remaining candidate is
the swapchain present/GPU path itself under SwiftShader's CPU
rasterisation, per the "not comparable" point above, but that was
not measured directly this pass (no per-phase breakdown inside
`FrameReport` the way Compose's report has `measure`/`place`/
`record`/`swap`/`gpu`).
**The dropout finding, corrected from earlier in this box: this
pass's own script bug, not a reproduction of the touch-delivery
candidate.** Of 3 planned attempts, the first 2 produced `iris frame
report: no frames recorded` -- but tracing it down found the cause
in this session's own tooling, not the emulator: the swipe loop was
extracted from `transcript-bench.sh` into a temporary wrapper script
that `cd`'d into `/tmp` before invoking `ui-trace`, and `ui-trace`/
`adb` here derive *which emulator to target* from the current
directory's basename (the per-checkout-AVD rule) -- from `/tmp` that
resolved to a nonexistent checkout named "tmp", `ui-trace` refused
immediately, and the wrapper's `set -eu` aborted the whole loop
before a single swipe was sent. Once the wrapper was fixed to run
from inside this checkout, the next **two** attempts (runs A and B
in the table above) both succeeded on the first try, each with a
confirmed screenshot-hash difference showing real scrolled content.
So this session did not reproduce the previously-documented
intermittent zero-touch phenomenon -- but two successes out of two
*valid* attempts is also too little evidence to say it is gone;
the earlier session's drops happened with a correctly-targeted
device, which is a different failure than the one found here.
**Sampler timeline**: `/tmp/iris-bench-sampler.log` shows
`/proc/loadavg` and the runnable-process count rising from an idle
baseline (~0.3-1.7 load, 0-4 running) to ~2.5-2.9 load and 11-23
running during the swipe window that produced run A -- consistent
with the standing candidate (SwiftShader's software rasterisation
loading the CPU during a gesture) but **not a confirmed cause**,
since both valid attempts succeeded despite the rise. Whether load
of that shape is what caused the *earlier* session's drops remains
unknown; this pass's sampler evidence neither confirms nor refutes
it, only shows the correlate is present under load without a
failure to correlate it to this time.
**Not attempted this pass**: the second `-gpu host` pair (time went
to the software-mode pair, the sampler, and diagnosing the dropout
above); a per-phase breakdown inside iris's own `FrameReport` the
way Compose's report has one; and syntax-highlighting/tappable-link/
accessibility-name gaps already named in "What remains" above,
unchanged.
**Verification for this update**: no Rust or Kotlin code changed
this pass (build/measurement only), so `cargo fmt`/`clippy`/`test`
were not re-run; `docs/DECISIONS.md`'s DEFERRED item is updated with
this table's headline numbers below rather than a decision made
here.
## For the next agent ## For the next agent
What to do when you pick this up, in order, so nothing here has to be What to do when you pick this up, in order, so nothing here has to be