538 lines
34 KiB
Markdown
538 lines
34 KiB
Markdown
# ai-app bench
|
|
|
|
The two APKs for the phone benchmark gate (ai-app's `docs/RUST.md`, P0),
|
|
packaged for Dev Updater so they can be installed on a phone without a
|
|
build machine. Add this checkout as a project in Dev Updater (its
|
|
`.dev-updater.ron` names two APK components, `compose-bench` and
|
|
`iris-bench`, with no build step -- the APKs are committed under
|
|
`compose/` and `iris/`), then install each from its card. Both install
|
|
beside the real app under their own application ids and need no server,
|
|
no enrolment and no permissions.
|
|
|
|
The rest of this file is the runbook that was delivered with the APKs.
|
|
|
|
**2026-09-08 (evening): the crash on scrolling up, a devlog that can
|
|
show a panic, and icons from a font iris ships.** `iris-bench-arm64.apk`
|
|
rebuilt from ai-app-2 `9e301f3`.
|
|
|
|
- **The crash.** The bench built a texture per disclosure triangle -- one
|
|
per folded tool card, hundreds of copies of three pictures -- and the
|
|
path that rebuilds the renderer for a new GPU device forgot which slot
|
|
each was in, so a frame could ask for a texture that no longer existed
|
|
and the app aborted. It is one texture per shape now (three for the
|
|
whole screen), and a new device re-uploads what it had instead of
|
|
forgetting it, so the handles stay valid. On the emulator this was
|
|
reproducible on the *first frame*; there it now opens clean and takes
|
|
33 full-screen scrolls back through the fixture without complaint.
|
|
**Whether it was your crash is not confirmed** -- nothing recorded what
|
|
yours was, which is the other half of this build.
|
|
- **The Runtime tab will show a panic now.** It only ever had one line
|
|
because, after a crash, Dev Updater's query starts the app for the
|
|
*provider alone* -- no screen, so the code that replays the previous
|
|
run's crash file never ran. The provider does that itself now, and the
|
|
crash file carries the last 80 log lines from the run that died as well
|
|
as the panic, replayed in order: what the app was doing, then what
|
|
killed it, then this run. So if it still crashes, open Dev Updater's
|
|
Runtime tab afterwards and send what is there.
|
|
- **The disclosure triangles come from a font now.** You were right that
|
|
this should have been the icon font: the Compose app ships its own Nerd
|
|
Fonts subset and iris shipped none, so its mark was a bare Unicode
|
|
codepoint your phone had no face for. iris has its own subset now (992
|
|
bytes, three Material Design glyphs, same Mono face as the Compose
|
|
app's), the drawn triangle is deleted, and a missing codepoint now
|
|
fails a test rather than drawing nothing. If a mark is the wrong size
|
|
or sits wrong beside the tool name, say so -- it is a font size now, so
|
|
it is one number.
|
|
- Side effect worth knowing: switching away from the app and back no
|
|
longer re-rasterises every glyph on screen.
|
|
|
|
**2026-09-08: catching a moving list, and rounded code fences that clip
|
|
on the curve.** `iris-bench-arm64.apk` rebuilt from ai-app-2 `38bf630`.
|
|
Two things from your last report, and both want checking on the phone
|
|
because neither is confirmed there yet:
|
|
|
|
- **"Sometimes when I try to catch it while it's still moving
|
|
(particularly if I drag) then it fails to stop & snap to where finger
|
|
is."** A finger put down on a moving list now stops the fling *at that
|
|
sample* and drags from it with no slop wait, which is what Compose
|
|
does. A catch you put down and lift without moving is no longer
|
|
treated as a tap. What to try: flick hard, then put a finger down
|
|
mid-flight and drag slowly — the content should be pinned to your
|
|
finger from the first movement, not after a jump.
|
|
- **Code blocks clip on their rounded corners.** A mask carries a shape
|
|
now rather than a rectangle, so a code fence's content is cut on the
|
|
panel's own curve instead of an inner square, and the corners of
|
|
nested clips multiply their alpha rather than fighting. Touch obeys
|
|
the same shape: a corner rounded away is not pressable. What to try:
|
|
scroll a code block sideways and look at its four corners, and try
|
|
pressing just outside one.
|
|
|
|
Also in this build: the app no longer refuses to start on a device whose
|
|
only usable adapter is a GLES one (it falls back and says so in the log
|
|
rather than aborting), and `primitives=` in a frame report no longer
|
|
counts clip shapes that are never drawn.
|
|
|
|
If something is wrong, the **Copy report** button always copies now —
|
|
the log tail comes with it, and Dev Updater's **Runtime** tab on this
|
|
component reads the app's log directly off the phone.
|
|
|
|
|
|
**2026-09-06: composing text + caret, composer layout, text survives app
|
|
switch.** `iris-bench-arm64.apk` rebuilt on top of the fling/stream/type/
|
|
keyboard bench v2 above (same commit range, merged). Fixes from Iris's
|
|
phone report on the previous build: Gboard's composing keystrokes now sync
|
|
back to the app (`InputMethodManager.updateSelection`, previously never
|
|
called, which is why typing looked stuck until a space); swiping over the
|
|
composer bar no longer opens the keyboard (only a completed tap does,
|
|
confirmed on the emulator); and text no longer disappears after leaving
|
|
and returning to the app (a genuinely new renderer now clears the CPU-side
|
|
glyph cache instead of leaving it pointed at a destroyed GPU device). The
|
|
composer itself was also rebuilt as one widget with a height cap and
|
|
insets-aware bottom padding. **Not fixed**: composed/typed text still does
|
|
not become visible on screen at all, a separate, deeper bug found while
|
|
verifying the above -- ruled out as a widget-layout bug (a new unit test
|
|
proves the region math is correct), still open. See ai-app-2's
|
|
`docs/RUST.md`, P0 box, for the full account, what was verified on the
|
|
emulator, and what still needs a real device or Vulkan to isolate.
|
|
|
|
**2026-09-06: bench v2 (fling/stream/type/keyboard).** `compose-bench-arm64.apk`
|
|
rebuilt with the four-phase benchmark Iris asked for after using v1 on her
|
|
phone -- a real fling (12,000px/s through the list's own `FlingBehavior`,
|
|
not the old tween-based swipe, so it travels much further per gesture),
|
|
the existing 400-event/20s stream, a type phase (600 fixed characters
|
|
typed into the real composer then deleted, to exercise wrapping and the
|
|
transcript being pushed upward), and a keyboard phase (five show/hide
|
|
cycles). The report gains a `per phase:` block (frames/late/percentiles/
|
|
worst/duration per phase) ahead of the existing whole-run sections, and
|
|
`bench:` gains the fling phase's travel, the typed character count, and
|
|
how many of the ten keyboard toggles were confirmed by the platform. Full
|
|
spec and the emulator smoke run are in ai-app-2's `docs/RUST.md`, P0 box,
|
|
"Benchmark v2 (2026-09-06)". Same steps below still apply -- "Run
|
|
benchmark" is unchanged in name and location.
|
|
|
|
---
|
|
|
|
# P0 benchmark APKs
|
|
|
|
Two arm64 builds, asked for in docs/RUST.md's P0 box (the phone benchmark gate Iris asked for
|
|
2026-09-05, "before P1 I'd like to see benchmarks & also maybe stress test on my own phone"). Each
|
|
opens straight onto the same synthetic fixture transcript with no server, runs the same scripted
|
|
scroll-and-stream benchmark, and produces a text report to paste back. **Install both, run both,
|
|
paste both reports back** -- the gate is a comparison, not either number alone.
|
|
|
|
## Compose (`compose-bench-arm64.apk`)
|
|
|
|
1. Install it (it has its own application id, `com.example.aiapp.bench`, and its own label "AI
|
|
Sessions bench" -- it installs beside a real enrolled "AI Sessions" without touching it).
|
|
2. Open it. It goes straight to a session screen holding the fixture transcript -- no enrollment,
|
|
no permission prompts.
|
|
3. Tap the gear icon top-right ("Session settings").
|
|
4. Tap "Run benchmark". It scrolls the transcript (the same 24-swipe loop
|
|
`app/transcript-bench.sh` drives on a phone from a computer), then streams 400 more fixture
|
|
events in over 20 seconds pinned to the newest end (the same shape `app/stream-bench.sh`
|
|
measures), then copies a report to the clipboard and shows a toast.
|
|
5. Paste the clipboard back here (or in whatever you're pasting reports into). It is plain text
|
|
with units on every number: frame counts and percentiles, the draw-phase accounting, and a
|
|
`bench:` section this build added -- process CPU time, peak RSS, and battery current sampled
|
|
once a second (says "unavailable" rather than a fabricated number where the phone can't answer,
|
|
per this project's rule against showing an inferred value as a measured one).
|
|
6. If you want to run it again, "Run benchmark" can be pressed more than once -- each press resets
|
|
the counters first, so the report only covers that run's own scroll and stream.
|
|
|
|
Built from `app/androidApp`'s `bench` build type; see `app/build-apk.sh bench` and
|
|
`app/bench-fixture/README.md` for how the fixture it opens was generated (deterministic, checked
|
|
into the repo, never a real transcript).
|
|
|
|
## iris (`iris-bench-arm64.apk`)
|
|
|
|
1. Install it -- own application id, `dev.iris.android.demo.bench` (an `applicationIdSuffix` on
|
|
the same `dev.iris.android.demo` the plain iris tabs demo uses, so it installs beside that one
|
|
too), needs no permissions beyond `INTERNET` (unused here -- inherited from the same manifest
|
|
`transcript-screen` needs, harmless for a build that talks to nothing).
|
|
2. Open it. Same fixture as the Compose build (the identical checked-in
|
|
`app/bench-fixture/assets/transcript.jsonl`), opened with no server through `client-core`'s real
|
|
`fold_page` -- there is no enrollment step here at all, iris's shell has none yet.
|
|
3. Tap "Run benchmark" (the whole screen is just two buttons above the transcript and a report
|
|
area below it -- no settings dialog to find it in). It runs the identical scripted loop: the
|
|
same 24-swipe/6-cycle scroll (900px over 200ms, 500ms apart, animated in ~60Hz steps rather than
|
|
jumped so real frames render along the way), then the same 400-event/20s streaming phase pinned
|
|
to the newest end, through `fold_event` -- the same fold path a live SSE reply uses.
|
|
4. The report appears in the text area under the buttons (it is a selectable field, so it can be
|
|
copied by hand too) and is logged under logcat's `iris-android-app` tag on the line starting
|
|
`iris bench report:`. Tap "Copy report" to put it on the clipboard through the shell's own
|
|
`ClipboardManager` (no toast -- watch logcat or the on-screen text for confirmation it worked).
|
|
5. Paste it back. Same units, most lines the same shape as Compose's -- frame count, janky
|
|
percentage, p50/p90/p99/worst -- plus iris's own CPU/GPU split (`cpu_p50`/`gpu_wait_p50`, from
|
|
`FrameReport::record_split`) where Compose's report has its draw-phase accounting instead, and a
|
|
`bench:`-shaped tail with the same three added fields: process CPU time over the run
|
|
(`getrusage(RUSAGE_SELF)`), peak RSS (`/proc/self/status`'s `VmHWM`), and battery current sampled
|
|
once a second through `BatteryManager.getIntProperty(BATTERY_PROPERTY_CURRENT_NOW)` -- "no
|
|
frames recorded" / "unavailable" rather than a fabricated number wherever the phone can't answer
|
|
one, same rule Compose's report follows.
|
|
6. "Run benchmark" can be pressed again; it resets `FrameReport` first, so a second run's report
|
|
covers only that run.
|
|
|
|
Built with `iris/android-app/build-apk.sh release --abi arm64-v8a` (wraps `cargo ndk -t arm64-v8a
|
|
-P 26 -o app/src/main/jniLibs/ build --release --features "transcript-screen bench"` plus `gradle
|
|
:app:assembleRelease`, signed with the same `~/.config/ai-app/release.jks` `app/build-apk.sh`
|
|
generates, and verifies the result with `aapt2`/`apksigner`). **No longer built with `force-gles`
|
|
as of the 2026-09-06 rebuild below** -- see that entry for why it was there and why it had to come
|
|
out for a phone build specifically.
|
|
|
|
**2026-09-06, commit `46d3a6f`**: rebuilt after `transcript_ui::TranscriptScreen::apply` replaced
|
|
the full-rebuild-per-event streaming path in all three iris clients (docs/RUST.md's P0 box has the
|
|
before/after numbers) -- this is the build to use if comparing against an iris APK from before that
|
|
commit.
|
|
|
|
**2026-09-06, second rebuild, ai-app-2 commit `46246ea`**: this build aborted on Iris's
|
|
phone (a Pixel, GrapheneOS) in `AndroidRenderer::new`, on the very first `surface_changed`, with
|
|
only `wgpu error: Validation Error` surviving into the crash report before Android truncated it.
|
|
Two things changed:
|
|
|
|
1. **A renderer-creation failure no longer aborts the process.** `iris_core::UiRenderNode::new`
|
|
now runs its bind-group-layout/pipeline creation inside wgpu error scopes and returns
|
|
`Result<Self, String>` instead of letting wgpu's default handler panic; the Android backend
|
|
turns a failure into the adapter's identity, the exact limits and downlevel flags bind-group
|
|
layouts validate against, and wgpu's own "Caused by" chain, logged as one line under the
|
|
`iris-android-app` logcat tag and shown on screen as plain, selectable, scrollable text (a
|
|
`TextView` swapped in for the whole activity) saying to copy it and send it back. If this build
|
|
still fails on the phone, **that screen is what to screenshot or copy** -- there is no more
|
|
silent abort to chase through a truncated crash report.
|
|
2. **This build no longer forces the GLES backend.** Every earlier `iris-bench-arm64.apk`
|
|
(including the one above) was built with `--features "... force-gles ..."`, a flag that exists
|
|
only to force the *emulator* off its default software Vulkan and onto GLES for one specific
|
|
frame-time measurement (RUST.md's I5) -- its own doc in `iris/Cargo.toml` never mentions real
|
|
hardware. `build-apk.sh`'s default feature list carried it into every arm64 build regardless,
|
|
so the APK actually delivered to a phone was locked to GLES rather than the phone's own Vulkan
|
|
driver. That is the named hypothesis for the crash (docs/RUST.md's P0 box has the full audit):
|
|
GLES support for a storage buffer bound in the *vertex* stage (`move_offsets`, `masks_layout`)
|
|
depends on the driver reporting `GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS > 0`, which real Vulkan
|
|
grants unconditionally but a phone's GLES path is not guaranteed to -- and RUST.md's own
|
|
SwiftShader finding already flagged GLES-on-Android as the fragile backend for this exact
|
|
shader. This build uses the default backend (Vulkan on a real device) instead.
|
|
|
|
If this rebuild still crashes, the on-screen report from point 1 names the real cause directly;
|
|
paste it back rather than guessing further from a truncated log.
|
|
|
|
## 2026-09-06, later: bundled fonts, content-scale fix, Diagnostics control
|
|
|
|
Iris's first real-phone report on the previous rebuild (no crash) found four
|
|
defects the emulator never showed: every glyph disappearing on the first tap
|
|
or scroll (rectangles stay drawn -- not root-caused this pass, but a phone
|
|
report from this build's new Diagnostics page would help), bold words
|
|
rendering as blank gaps (fixed -- bundled Noto Sans/Noto Sans Mono, static
|
|
per-style faces, ahead of the platform's own fonts), text far too small
|
|
(fixed -- iris had no device-pixel-ratio handling on either platform before
|
|
this; density now divides window size, touch coordinates and the shader's
|
|
window uniform down to logical units), and the top buttons sitting under the
|
|
status bar (fixed -- a new `AndroidAppState::on_insets_changed` hook, unused
|
|
until now).
|
|
|
|
This build also adds a third **Diagnostics** button next to Run/Copy,
|
|
filling the report area with adapter identity, font resolution, the atlas's
|
|
view count, every wgpu error seen since surface creation, and the frame
|
|
report -- "copy this and send it to Iris" via the existing Copy button.
|
|
|
|
Full account, including what's still unverified on a real phone, in
|
|
`docs/RUST.md`'s P0 box in the `ai-app-2` repo (`rustify` branch).
|
|
|
|
## 2026-09-06, later still: keyboard-wipe fix, a density unit for crisp text, header background
|
|
|
|
Iris's report on the previous rebuild, with screenshots: text now the
|
|
right size but **blurry**; opening the keyboard still **wipes every
|
|
glyph** (rectangles stay, only text disappears); the **header buttons
|
|
have nothing behind them and overlap the transcript text**.
|
|
|
|
- **Keyboard wipe**: `surface_changed` fires on every surface resize, not
|
|
only a genuinely new surface -- showing the keyboard under
|
|
`adjustResize` is exactly such a resize. It was rebuilding the whole
|
|
renderer (a fresh, empty glyph atlas) every time, while the CPU-side
|
|
glyph cache kept coordinates into the *old* atlas. Fixed: reuse the
|
|
existing renderer's device/atlas/buffers and only reconfigure the
|
|
surface when one is already live. Confirmed on this checkout's own
|
|
emulator two ways (a forced display resize and an actual soft-keyboard
|
|
open) -- text stayed on screen both times.
|
|
- **Blur**: the previous fix's global "logical size" stretch (window
|
|
divided by density, then displayed stretched back up) rasterised each
|
|
glyph small and displayed it bigger than it had texels for. Fixed with
|
|
the density-independent length unit Iris asked for the same day (a new
|
|
`dp` alongside the existing physical-pixel and relative lengths,
|
|
resolved against the display's density at layout time) -- text now
|
|
rasterises at the display's real physical resolution.
|
|
- **Header**: only each button's own coloured rect painted anything, so
|
|
gaps between/around them showed the black clear colour one layer back,
|
|
and the row's reserved height did not match the (now density-correct)
|
|
transcript below it. Fixed with an opaque panel behind the whole row
|
|
and every header size moved onto the new `dp` unit.
|
|
- **New**: the bench build now auto-captures a diagnostics report ~500ms
|
|
after the keyboard opens (logged, copied to the clipboard, and shown in
|
|
a plain overlay with Copy/Close over the running app) -- so a report
|
|
can come back even if a keyboard-triggered regression is still
|
|
happening.
|
|
|
|
**Not fully verified.** This build has not been run on Iris's actual
|
|
phone. It was verified end to end on this checkout's own emulator
|
|
(`run-bench.sh`, no crash, 24/24 swipes, 400/400 streamed events;
|
|
before/after screenshots for the two keyboard-resize tests above). While
|
|
verifying, a **separate, not-yet-fixed issue turned up**: after a
|
|
keyboard-triggered resize, the header row appeared to render a second
|
|
time, out of place, inside the transcript area, and a tap meant for the
|
|
message field below landed on a header button instead (a benchmark run
|
|
started unexpectedly). Not root-caused, and not confirmed to be caused by
|
|
this pass's changes rather than something pre-existing -- worth watching
|
|
for on the phone and reporting back if seen. Full account in
|
|
`docs/RUST.md`'s P0 box in the `ai-app-2` repo (`rustify` branch).
|
|
|
|
## 2026-09-06, later still: Benchmark v2 (fling/type/keyboard), the header-duplicate finding, the ime-inset finding
|
|
|
|
**New**: "Run benchmark" now runs the same four-phase script the Compose
|
|
build's v2 does -- fling (8 flings out + 8 back at 12,000px/s through
|
|
`List`'s own new fling, matching Android's real deceleration curve),
|
|
stream (unchanged), type (the same 600-character string, one character
|
|
per 50ms into the real composer, then deleted), and keyboard (five
|
|
show/hide cycles). The report gained a `per phase:` block (frames, late
|
|
%, p50/p90/p99, worst per phase) and the whole-run `frames:` line now
|
|
reads the display's real refresh rate instead of assuming 60Hz.
|
|
|
|
**The header-duplicate bug from the previous entry: still not fixed.**
|
|
Reproduced reliably this pass (tap the composer field, screenshot after
|
|
the keyboard opens -- the three-button row appears twice, once at its
|
|
real position and once inside the transcript). One real, targeted fix
|
|
was tried and **ruled out**: skipping the header rebuild when the
|
|
status-bar inset hasn't actually changed (since a keyboard toggle also
|
|
re-fires this callback for an unrelated reason) -- the duplicate still
|
|
reproduced identically after that fix, so repeated rebuilding is not the
|
|
cause. Still unexplained; needs either live instrumentation of the
|
|
layout engine's two-phase span placement or a person with the phone.
|
|
|
|
**Found, and partly explains why the keyboard phase and the
|
|
keyboard-open auto-diagnostics both read "not confirmed" on every
|
|
run so far**: this app's `AndroidManifest.xml` uses
|
|
`windowSoftInputMode="adjustResize"` (needed so the keyboard resizes the
|
|
window instead of panning it off screen), and under `adjustResize`
|
|
`WindowInsets.Type.ime()`'s own inset *amount* is defined to read zero --
|
|
the window already shrank to avoid the overlap that inset would
|
|
otherwise report. Fixed the app to read `WindowInsets.isVisible(ime())`
|
|
(a boolean, unaffected by resize-vs-pan) instead of the numeric inset --
|
|
correct on its own terms, but on this emulator the insets listener still
|
|
never re-fires at all once the keyboard opens (a separate, deeper
|
|
platform behavior tied to this activity not being edge-to-edge, not
|
|
fixed this pass -- changing that is a bigger, riskier window-behavior
|
|
change than the time available justified). So the keyboard phase's "0
|
|
confirmed" and the auto-diagnostics not firing are both still open,
|
|
now for a specific, named reason rather than an unknown one.
|
|
|
|
Full account, including what was tried and ruled out, in
|
|
`docs/RUST.md`'s P0 box in the `ai-app-2` repo (`rustify` branch).
|
|
|
|
## 2026-09-06, evening: typing, launch layout, fling, per-block streaming, doubled rows
|
|
|
|
`iris-bench-arm64.apk` rebuilt from ai-app-2 commit `20303e0` (arm64 Vulkan release,
|
|
`iris/android-app/build-apk.sh release --abi arm64-v8a --features "transcript-screen bench"`).
|
|
Everything since the 02:07 build, in one entry -- two intermediate builds went to the wrong
|
|
folder and never reached this repo:
|
|
|
|
- **Launch layout**: the black third below the composer was the bench shell's own empty
|
|
report pane taking a third of the screen, not an inset bug. Sized to its content now.
|
|
- **Typing**: tapping the empty composer focused it with no caret, so every keystroke was
|
|
dropped. Fixed, with a regression test. Text should appear as you type.
|
|
- **Fling and swipe from the composer**: dragging is now iris's own `DragGesture` with pointer
|
|
capture. A fast swipe keeps moving after release; a pan starting on the grey bar is taken by
|
|
the list from the frame it commits.
|
|
- **Doubled rows** (the `Compacted:` row, and the earlier doubled header): a widget redrawn
|
|
through its ancestor while marked dirty never freed its previous primitives, so they were
|
|
drawn every frame for the life of the process. Fixed; a debug-build assertion now checks
|
|
every frame that no primitive outlives its widget.
|
|
- **Streaming re-lays out only the block the delta lands in.** A message row is a column of
|
|
one text widget per markdown block; a streamed delta replaces the last block only (one text
|
|
shape per delta into a 100-paragraph reply, by counter). Emulator stream-phase p99 went
|
|
343ms -> 137ms. **The stream phase on your phone is the number to look at** (it was p50
|
|
18.2ms vs Compose's 13.4ms before).
|
|
- **Selection is per block**: a drag selects within and across blocks; copy joins them. Whether
|
|
a block should be the selection unit is yours to judge (`docs/DECISIONS.md`, 2026-09-06).
|
|
- **The composer scrolls under a finger** when it overflows its six-line cap.
|
|
- Also fixed: every widget's tap target sat short of its drawing by its last move (from
|
|
launch); stale mask slots after a keyboard resize; a targeted-redraw abort on masked
|
|
widgets; the full-screen diagnostics overlay on every keyboard open is gone (logged only).
|
|
- **App-switch text loss** did not reproduce on the emulator, so this build logs one line per
|
|
surface event with glyph counts. If text still vanishes on returning to the app, please send
|
|
`adb logcat | grep -i iris` from around the return; `already_live=true` on the way back in
|
|
names the cause outright.
|
|
|
|
Not in this build: markdown block styling (headings, highlighted fences, lists, tables, links)
|
|
and tool-call cards, which landed on `rustify` after it -- next build.
|
|
|
|
## 2026-09-07: the keyboard reopens, the composer moves for the IME, and a fling actually flings
|
|
|
|
From `ai-app-2` commit `ba0f2ea` on `rustify` (`iris-bench-arm64.apk`, arm64 release,
|
|
minSdk is 29 now). This build answers all four items of your 22:16 report.
|
|
|
|
- **"I can't reopen keyboard by tapping on message box."** Fixed. Dismissing the keyboard
|
|
leaves the field focused, and the only code path that asked for the keyboard was the one
|
|
that ran when a field *became* focused, so it never ran again. A tap on an already-focused
|
|
field asks for it now, exactly as Android's own `EditText` does. Checked on the emulator
|
|
both ways: with the fix the second tap opens it, without it the second tap does nothing.
|
|
Swiping over the composer still summons nothing, which is the thing this could have broken.
|
|
- **"Message box does not push up the scroll area."** Fixed. The keyboard's *height* was being
|
|
sent across to Rust as the boolean 1, so nothing had a number to pad by. Height and
|
|
visibility are two separate values now. On the emulator the composer rises by exactly the
|
|
keyboard's height and the transcript shrinks to match.
|
|
- **"Fling still doesn't work."** Three separate faults, all fixed, and **this is the one to
|
|
test hardest** because the emulator cannot reproduce your phone's input timing.
|
|
1. A fast flick at 120Hz arrives as one or two events with the in-between positions batched
|
|
inside them, and we were reading only the last position -- so the speed came out as zero.
|
|
Every batched sample is replayed now, each with its own timestamp.
|
|
2. Nothing in iris advanced a fling between input events at all. The benchmark drove its own
|
|
fling loop, which is why the bench "flung" and a finger never did. Widgets can animate now.
|
|
3. Once flings finally moved, one lasted 45 seconds: two constants were wrong (a hardcoded
|
|
display density, and the scroll friction used where Android uses its own 0.84 tuning
|
|
number -- a factor of 56 inside an exponential). A flick now coasts for the time Android's
|
|
own formula says: 1.6s for a hard flick, 0.6s for a gentle one.
|
|
|
|
**If it still does not fling on your phone**, the one line that settles it is
|
|
`adb logcat | grep "iris drag release"` -- please send it. `samples=1` or `span=0.0ms` means
|
|
the batched samples are not reaching us on your device; a sensible span with `v=` in the
|
|
thousands and `outcome=Released(Some(...))` means the gesture is measured correctly and
|
|
whatever is wrong is after that point. `outcome=Tapped` means the flick never counted as a
|
|
drag at all.
|
|
- **"The textures are getting cooked after leaving the app and resuming."** Fixed in
|
|
`ba2afba`, with a headless test, but **not confirmed on a device**: no emulator here has a
|
|
Vulkan adapter and the GLES path may not destroy the surface at all, so this one needs your
|
|
phone. The cause was one cache deeper than the last build's fix reached -- each text widget
|
|
kept its own laid-out glyphs with the *old* atlas's coordinates and re-submitted them
|
|
verbatim, which is why only text drawn after the resume looked right. The atlas carries a
|
|
generation now and a cleared atlas invalidates every cached layout at once.
|
|
|
|
Also in this build, both found while checking the above: a tap on the composer aborted every
|
|
debug build (a fixed bound on how deep a widget can nest was 16 and the composer is 17 deep --
|
|
in a release build like this one it did not crash, it silently drew and hit-tested that part of
|
|
the tree in the wrong place), and the app now requires Android 10 or newer, because the
|
|
per-sample touch timestamps the fling fix needs are API 29.
|
|
|
|
Still not fixed, and unrelated to any of the above: the composer bar's grey background is not
|
|
drawn on this build, so the transcript shows through behind the message field.
|
|
|
|
## 2026-09-07, later: the fling curve was a straight line, and the keyboard was one build setting
|
|
|
|
`iris-bench-arm64.apk` rebuilt from ai-app-2 `rustify` commit `4274b8b`.
|
|
This is the build for your three lines on the last one.
|
|
|
|
**"It seems to just be linear velocity with an abrupt stop."** It was
|
|
exactly that, and not approximately -- the curve iris used to shape a
|
|
fling returned a straight line for every input. Android's own deceleration
|
|
curve is built by a loop that solves one cubic and samples a *different*
|
|
one; iris had both halves solving and sampling the same one, which made the
|
|
two tables it builds identical, and the lookup that reads one against the
|
|
other then cancels out to "distance is proportional to time". So a flick
|
|
coasted at its release speed for the whole (correctly computed) duration
|
|
and then stopped dead at the end of it.
|
|
|
|
It is ported exactly now, from Android's `OverScroller.java` and from
|
|
Compose's own copy of the same curve, which agree line for line -- so a
|
|
fling here should travel and slow the way every other Android scroll view
|
|
does. On the emulator a released 3750px/s now decelerates
|
|
3746 -> 2624 -> 1834 -> 1144 -> 752 -> 449 -> 243 -> 83px/s across 32
|
|
frames, where before it would have held 3750 for all of them. **What to
|
|
look for**: a flick should visibly slow before it stops, and a longer flick
|
|
should slow over a longer distance. Two things that also should hold, both
|
|
checked here: a flick into the top or bottom of the transcript stops there
|
|
without bouncing, and putting a finger down during a fling stops it dead.
|
|
|
|
Worth saying because it is the reason this took two builds: every test the
|
|
fling code had compared it against itself -- is it monotonic, is the sign
|
|
right, does it add up to the total -- and **all of them pass on a straight
|
|
line**. The expected numbers are now generated by a separate transcription
|
|
of Android's source that does not share a line of code with iris.
|
|
|
|
**"The keyboard raising up does not push things upwards."** This one was a
|
|
single line of the bench app's build file: it declared it targeted Android
|
|
14, while the real app (the Compose one, whose keyboard does push its
|
|
transcript up on your phone) declares 17. Below the Android 15 target line
|
|
a window keeps the old behaviour, where the system shrinks the window for
|
|
the keyboard and therefore reports the keyboard's height to the app as
|
|
zero -- nothing left to push up. The emulator here is a version where the
|
|
old opt-out still works, which is why it pushed up in every test run and
|
|
not on your phone.
|
|
|
|
**This one is a reading, not a measurement** -- there is no Android 17
|
|
device I can reach. So the other half of the change is making your phone
|
|
able to answer it without a log. **Press `Diagnostics` with the keyboard
|
|
open and screenshot the line beginning `insets:`.** It looks like
|
|
|
|
insets: dispatches=27 left=0 top=142 right=0 bottom=63 ime_bottom=0 ime_visible=false
|
|
|
|
- `ime_bottom` in the hundreds, and the message box has risen: fixed.
|
|
- `dispatches` counting up but `ime_bottom=0`: my reading was wrong and the
|
|
window is still being resized. That is the useful failure.
|
|
- `dispatches=0`: the system is not telling the app about insets at all,
|
|
which is a third thing again and would say so in words instead of
|
|
numbers.
|
|
|
|
The keyboard's height also arrives over a second path now (the animation
|
|
callback), so the push-up follows the keyboard up rather than jumping when
|
|
it lands.
|
|
|
|
**"The resume glyph corruption is fixed."** Thank you -- closed on your
|
|
evidence. That was the one nothing here could ever have confirmed: no
|
|
emulator on this machine has a Vulkan adapter, and the software path may
|
|
not destroy the surface at all, so the state you photographed was not
|
|
reproducible here.
|
|
|
|
Still not fixed, and still unrelated: the composer bar's grey background is
|
|
not drawn on this build, so the transcript shows through behind the message
|
|
field.
|
|
|
|
## 2026-09-07, evening: clamped scrolling, a faster flick start, platform fonts, enrollment, and a runtime log Dev Updater can read
|
|
|
|
`iris-bench-arm64.apk` rebuilt from ai-app-2 `rustify` commit `92985ba`,
|
|
against the last delivery (`b47eb73`, the fling-curve + keyboard-targetSdk
|
|
build). APK **9,639,224 bytes**, `libmain.so` **7,506,448 bytes** (arm64-v8a
|
|
only) -- both smaller than that build, mostly from replacing the bundled
|
|
Noto fonts with the platform's own (Roboto for body text, Droid Sans Mono
|
|
for code) and a further release-profile tuning pass; docs/RUST.md has the
|
|
full size table.
|
|
|
|
What changed:
|
|
|
|
- The list is now clipped to its own box and rows are drawn at both edges --
|
|
nothing scrolls up behind the header, and there is no blank band at
|
|
either end.
|
|
- Scrolling is clamped at both ends of the transcript, matching the fling
|
|
fix from the last build: you cannot drag or fling past the top or the
|
|
bottom.
|
|
- A released flick now starts at the speed your finger was actually moving
|
|
at, using the same least-squares velocity estimator Compose itself uses,
|
|
rather than a slower approximation -- a flick should feel like it starts
|
|
fast and decelerates, not like it ramps up.
|
|
- The clock that timestamps each touch sample was misdating batched input,
|
|
which fed wrong numbers into that velocity estimate; it now dates them
|
|
correctly.
|
|
- Code blocks render in a real monospace font and body text in Roboto --
|
|
both platform fonts now, not the bundled Noto this build removes.
|
|
- The app enrols itself from the `aiapp://enroll` link -- Dev Updater's
|
|
Enroll button on this APK's component opens it directly. Diagnostics
|
|
shows either `enrolled: host:port` or `not enrolled`.
|
|
- The app exposes its own log to Dev Updater: APK component -> log button
|
|
-> Runtime tab. This needs the updated Dev Updater build on the host to
|
|
show anything.
|
|
- A "Trace" switch in the bench header turns on a line per input and per
|
|
frame in that log, for when a report alone isn't enough to say what
|
|
happened.
|
|
- Several of the invariants this code relies on internally are now hard
|
|
assertions in this release build, rather than silently-wrong behaviour if
|
|
one is ever violated.
|
|
|
|
What to look for:
|
|
|
|
- The header shows nothing behind it, at either edge of a scroll.
|
|
- You cannot scroll past the top or the bottom of the transcript.
|
|
- A flick starts at roughly the speed of your finger and visibly
|
|
decelerates, rather than ramping up or holding a constant speed.
|
|
- Code blocks are monospace; body text is not.
|
|
- Opening the keyboard pushes the composer up. If it doesn't, screenshot
|
|
Diagnostics' `insets:` line as in the last build's report.
|
|
- Dev Updater's Runtime tab (APK component -> log button) shows this app's
|
|
own lines, once the host's Dev Updater is updated.
|
|
- With Trace on, do a few flicks, then press Copy in the render report --
|
|
the input log that comes back is what to send back here.
|