Files
ai-app/docs/DECISIONS.md
T
irisandClaude Fable 5.1 e49d0e606f RUST.md, DECISIONS.md, IRIS.md: iris's host-GPU frame time, 2026-09-05
Takes the -gpu host pair the earlier software-mode comparison flagged as
missing. Under real GPU rendering (--features force-gles: the default
Vulkan backend has no adapter at all under plain host-GPU boot, confirmed
by the exact wgpu error), iris's median frame (15.0ms) is faster than
Compose's (20.0ms) on the same session content -- the opposite shape from
the software-mode table. The new redraw-to-submit/submit-to-present split
shows iris's own CPU work is a median 0.2ms per frame; almost the whole
frame is time handing off to the driver, consistent with (but not proof
of) the software-mode gap being mostly SwiftShader's CPU rasterisation
cost rather than iris-specific slowness.

A same-mode software force-gles run, meant to isolate the backend, hit a
third distinct crash instead (SwiftShader's GL path reports itself as
OpenGL ES 3.0, which has no compute shaders, and iris's device request
assumes them unconditionally) -- real scope to fix, not done here, so the
software-mode question stays open. A real intermittent touch-scroll
dropout was also reproduced (six consecutive swipes produced zero
redraws while taps kept working; an identical retry then succeeded) and
is not explained. The idle-redraw and virtualised-culling findings from
the software-mode pass were confirmed to hold under real GPU rendering
too.

DECISIONS.md's DEFERRED item carries the updated table; the iris-vs-
Masonry choice itself is still Iris's to make. IRIS.md records the
FrameReport::record_split/FrameStats::cpu_p50/gpu_wait_p50 API from the
prior commit (e2a1fad), which this pass's measurement used.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-05 20:07:20 -04:00

137 lines
8.7 KiB
Markdown

# Decisions taken for Iris to review
Short list of design choices made by the design agent without asking, so
they can be judged and reversed later. Detail lives in RUST.md (and IRIS.md
for iris API changes); this file is only the summary. Newest first. Items
marked **DEFERRED** are ones the agent chose not to decide alone.
## 2026-09-05
- **iris gets its own measured frame report, rather than waiting on a
`dumpsys`/`gfxinfo` answer that cannot see a `SurfaceView`'s GPU-drawn
frames.** `iris_core::FrameReport` (`iris/core/src/render/frame_report.rs`)
times each frame's wall clock from the same point `render()`'s redraw
starts to just after `queue.submit` + `present()` — the span Compose's
own render report and `gfxinfo` both count — into a fixed 4096-entry
ring (no allocation per frame; `report()` is the only place that
allocates, and only on a button tap). The report gives total frames,
janky % over the same 16.7ms budget `gfxinfo` uses, P50/P90/P99 and the
worst, plus a reset. Exposed the way the Compose app's copy-button
report already is: two named controls ("Frame report", "Reset frame
report") on the transcript screen, tappable by accessibility name via
`ui-trace`, logging under this crate's fixed `android_logger` tag
(`iris-android-app`) so a script can grep `"iris frame report"` the way
`transcript-bench.sh` greps `"ai-app render report"`. The report's own
`Display` line says plainly that it measures up to the `present()` call
returning, not GPU/compositor completion — wgpu's `present()` is not
fenced against either, so presenting that span as "time to reach the
screen" would be a measured-looking number that is actually inferred,
which the standing UI rule forbids.
- **`ui-trace` gains a hold-then-drag gesture, additive, in
`emulator-tools`.** Neither of its two existing actions can produce
"hold stationary for `LONG_PRESS`, then move without lifting" — `tap`
has no hold and `swipe X1 Y1 X2 Y2 MS` interpolates motion across its
whole duration from t=0. A new action presses, waits, then moves to a
second point and releases as one continuous touch (raw
`sendevent`/`MotionEvent` injection, extending whatever mechanism the
existing `swipe` already uses), so `DragArbiter`'s pan-vs-select rule
(`iris/src/sense.rs`, already covered by 8 unit tests against a
synthetic clock) can finally be driven on a real device instead of only
in a test harness.
- **Touch drag on a transcript row follows Android's own rule**: a vertical
drag pans the list immediately; a stationary press held 500 ms starts a
text selection which further dragging extends; a horizontal drag while
something is already selected extends that selection without the wait.
One `DragArbiter` per list decides it (`iris/src/sense.rs`). Chosen over a
"text layer always wins" or "list always wins" rule because either loses
one of the two gestures a reader expects.
- **E4's desktop shape is a new `iris/desktop-app` crate**: a winit window
holding `transcript-ui`'s screen beside a session list, talking to a real
`ai-server` through `client-core`. It enrols by pasting the same
`aiapp://enroll?…` link a phone scans (`client-core::config::EnrolledServer`)
and keeps it owner-only under `$XDG_CONFIG_HOME/ai-app-desktop/`. The
pinned CA is a path given on the command line, not baked in. Chosen so
the phone and desktop share one enrolment format and no second one is
invented.
- **I5's Android integration extends `iris-android-app` (I2's shell)
behind a Cargo feature (`transcript-screen`), rather than a third
shell crate.** That project already has the Gradle module, the
`IrisView`/`MainActivity` Java, and the JNI registration; the only
thing a second screen needs on top is a different `AndroidAppState`,
the same axis `tabs_ui::build`/`transcript_ui::build` already vary
along on the winit side. `tabs-screen`/`transcript-screen` are
mutually exclusive and each pulls in only its own deps, so the plain
tabs build (I2/I4) is untouched.
- **Order of remaining work, updated 2026-09-05**: the two in-flight
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.
- **DEFERRED — whether to commit to iris over Masonry for `ai-app`.**
Updated 2026-09-05 with the clean comparison the recommendation wanted:
same sandbox session content, same emulator, `EMU_GPU=software`, one
session. Headline numbers (RUST.md's I5 box, "Clean scroll comparison,
2026-09-05," has the full table and every caveat):
| app | build | frames | janky % | p50 | p90 | p99 | worst |
|---|---|---|---|---|---|---|---|
| Compose (in-app report) | debug | 1102 | 99.0% late | 33.8ms | 50.6ms | 79.5ms | -- |
| Compose (`dumpsys gfxinfo`) | debug | 1499 | 21.15% (95.66% legacy) | 32ms | 48ms | 150ms (p99) | -- |
| iris (`FrameReport`) | **release** | 299 | 94.65% | 79.1ms | 98.6ms | 117.8ms | 212.6ms |
| iris (`FrameReport`, repeat) | **release** | 233 | 94.42% | 109.3ms | 130.8ms | 147.1ms | 150.5ms |
**Not a clean apples-to-apples reading, stated plainly rather than
smoothed over**: iris had to be built **release** (debug `SIGSEGV`s on
this emulator's Vulkan loader, I4's finding) against Compose's mandated
**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
structural-plus-functional case already made (iris works end-to-end
where Masonry's scroll gesture doesn't exist at all on Android) plus
this table — reading the two build profiles and three jank definitions
with the caveats above rather than as a single number — or ask for a
same-profile, same-GPU-mode rerun first. RUST.md's I5 box has the full
account.
**Updated 2026-09-05, the `-gpu host` pair taken.** Real GPU rendering
(`force-gles` -- the default Vulkan backend has no adapter at all under
plain host-GPU boot, confirmed by the exact `wgpu` error) reverses the
software-mode shape:
| app | build | GPU mode | frames | janky % | p50 | p90 | p99 | worst | cpu p50 | gpu-wait p50 |
|---|---|---|---|---|---|---|---|---|---|---|
| Compose (in-app report) | debug | host (virgl) | 1268 | 96.4% late | 20.0ms | 28.4ms | 37.7ms | -- | -- | -- |
| iris (`FrameReport`) | **release**, `force-gles` | host (virgl) | 62 | 41.94% | 15.0ms | 21.8ms | 37.1ms | 37.1ms | 0.2ms | 12.9ms |
Under real GPU rendering iris's median frame is *faster* than
Compose's, not the 2-3x-slower shape the software-mode table shows. A
new split inside `FrameReport` (redraw-to-submit vs. submit-to-present,
commit `e2a1fad`) says why: iris's own CPU work per frame is a median
0.2ms -- almost the entire frame is time spent handing the frame to the
driver, not in iris's layout/text/primitive code. This is consistent
with the earlier software-mode gap being mostly SwiftShader's CPU
rasterisation cost rather than an iris-specific slowness, but is not
proof of it: a same-mode software `force-gles` run to isolate the
backend crashed for an unrelated reason (SwiftShader's GL path reports
itself as OpenGL ES 3.0, which has no compute shaders, and iris's device
request assumes them unconditionally) — real scope to fix, not done
here — and the two apps' frame populations still differ in kind the same
way the software-mode caveats describe. A real intermittent touch-
scroll dropout was also reproduced this pass (six consecutive swipes
produced zero redraws while taps kept working; an identical retry then
succeeded) and is not explained. RUST.md's I5 box, "Where iris's frame
time goes, 2026-09-05, the `-gpu host` pass," has the full account. The
iris-vs-Masonry choice itself is still Iris's to make.