From 560a74caf80fe3e229315c68704f661e6d53191a Mon Sep 17 00:00:00 2001 From: iris <2+iris@noreply.localhost> Date: Sat, 5 Sep 2026 23:59:40 -0400 Subject: [PATCH] docs: record the phone-report fixes, follow-ups and the bundled-font API RUST.md's P0 box gets Iris's first real-phone report (no crash) and the four defects it found (glyph-wipe-on-first-touch, missing bold glyphs, text far too small, status-bar inset not applied), what was fixed and how it was verified on the emulator, and what's still open (item 1's root cause, and the top-row height anomaly noted in the last commit). IRIS_TODO.md gets a new "From the phone, 2026-09-06" section for the two items explicitly deferred to a follow-up agent: no scroll momentum/fling, and occasional jitter scrolling down. IRIS.md gets the public-API entry for TextData's bundled fonts/ font_diagnostics, UiRenderNode::new/resize's new window_size parameter, AndroidUiState::content_scale, AndroidAppState::on_insets_changed, and iris_core::WgpuErrorLog. Co-Authored-By: Claude Fable 5.1 --- docs/IRIS.md | 39 +++++++++++++++ docs/IRIS_TODO.md | 22 +++++++++ docs/RUST.md | 123 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 184 insertions(+) diff --git a/docs/IRIS.md b/docs/IRIS.md index dace824..465b7de 100644 --- a/docs/IRIS.md +++ b/docs/IRIS.md @@ -513,3 +513,42 @@ phase dropped from 369.3ms/284.5ms (full rebuild per event, prior pass) to ~101–130ms/~76–103ms across three runs (this fix) — see RUST.md's P0 box for the full numbers and the comparison's caveats (different AVD instances, not a controlled A/B on identical hardware state). + +## 2026-09-06: bundled fonts, `content_scale`, `AndroidAppState::on_insets_changed` + +From RUST.md's P0 box, working Iris's first real-phone report (font/scale/ +inset bugs the emulator never showed). + +- **`TextData` now bundles Noto Sans + Noto Sans Mono** (regular/bold/ + italic/bold-italic static faces, OFL) and registers them ahead of the + platform's own fonts in the `SansSerif`/`Monospace` generic-family + lists, rather than relying on the platform's font enumeration alone. + `TextData::font_diagnostics() -> FontDiagnostics` reports what was found + and what each style axis resolved to — logged once at startup and shown + on a screen's Diagnostics page if it has one. Adds ~3.6 MB uncompressed + to any binary linking `iris-core`; `build-apk.sh`'s own output says the + delivered (compressed) number. +- **`UiRenderNode::new`/`resize` now take the window size explicitly** + (`window_size: impl Into`) instead of deriving it from the + surface's physical `SurfaceConfiguration`. Existing callers pass a + *logical* size (physical ÷ density/scale-factor) now; this is what makes + a `font_size: 16.0` 16 dp instead of 16 raw device pixels on a + high-density phone. Before this, `scale_factor` did not exist anywhere + in the crate, on either platform. +- **`AndroidUiState::content_scale: f32`** (`DisplayMetrics.density`, read + once in `new_peer`) and the desktop equivalent (`window.scale_factor()`) + now divide every physical-pixel number before it reaches layout or + touch handling — see `content_scale`'s own field doc for the full list + of what depends on it. +- **New: `AndroidAppState::on_insets_changed(&mut self, rsc, LogicalInsets)`**, + a default-no-op hook called from `render()` exactly when + `AndroidUiState::insets()` changes. Nothing previously consumed + `insets().top` at all; a screen with chrome under the status bar + implements this to pad it, in the same logical units `content_scale` + converts everything else to. +- **New: `iris_core::WgpuErrorLog`**, installed via `Device:: + on_uncaptured_error` on the Android device (wgpu's default handler is an + unconditional panic outside `UiRenderNode::new`'s own error scopes). + Explicit `Arc`-backed value passed to the callback and kept on + `AndroidRenderer`, not a global — a caller wanting one on desktop builds + its own the same way. diff --git a/docs/IRIS_TODO.md b/docs/IRIS_TODO.md index 044ef3f..c66250b 100644 --- a/docs/IRIS_TODO.md +++ b/docs/IRIS_TODO.md @@ -117,6 +117,28 @@ order and what "done" looks like. Tick and date them in place. screen wants the same thing (P1's own transcript rows already read their content from a `TextEdit` for the same reason). +## From the phone, 2026-09-06 + +Found on Iris's own phone while working RUST.md's P0 box's phone-report +follow-ups. Recorded here rather than fixed in that pass, so a follow-up +agent takes them without colliding with that pass's `bench_client.rs`/ +`android/view.rs`/`android/sense.rs` changes. + +- [ ] **Swiping has no momentum.** iris's `List` pans exactly as far as the + finger moves and stops dead on release -- unlike Compose, which flings + and decelerates. Needs velocity tracking over the last several move + samples and an Android-style decelerating fling, cancelled by the next + touch-down, redrawing every frame until it settles. `BenchRun.kt`'s own + fling phase (RUST.md's "Benchmark v2" box) is the reference shape: + "travel way faster... which is better for stress testing." +- [ ] **Scrolling down sometimes jitters the text.** Two named suspects, + neither confirmed: `DragArbiter`'s slop being released as one jump + (`iris/src/sense.rs`), or a per-frame pan delta applied a frame late. + Measure by tracing the list's scroll offset per frame against a + monotonic synthetic drag, the same way `iris/android-app/trace-draw.sh`- + style instrumentation traced the touch-scroll dropout in RUST.md's I5 + box -- not by eyeballing a screenshot. + ## Build - [x] **Benchmarks**, not unit tests, run on demand (2026-09-05; a diff --git a/docs/RUST.md b/docs/RUST.md index f5296c6..5575d41 100644 --- a/docs/RUST.md +++ b/docs/RUST.md @@ -4315,6 +4315,129 @@ device. confirming the phase marks partition the whole run rather than overlapping or dropping frames between them. + **Iris's first real phone report, 2026-09-06** (the redelivered, + no-`force-gles` APK above): no crash. Two screenshots, before any + touch: headings/links/code/table all render correctly. Four defects + found and worked this pass: + + 1. **Every glyph disappears on the first tap or scroll; rectangles + stay drawn** (the keyboard case is the same thing -- a tap on the + composer). **Not root-caused this pass.** Audited `GpuTextures`' + atlas-grow/patch path, `ArrBuf`'s resize-on-length-change + contract, and the masks/move_offsets/rsc bind-group rebuild logic + in `core/src/render/mod.rs` against wgpu's queue-ordering + contract -- everything read as spec-correct (a `queue.write_texture`/ + `write_buffer` issued before a later `queue.submit` is guaranteed + visible to it on the same queue, and a dropped `Buffer`/`Texture`/ + `BindGroup` still in flight is kept alive by wgpu's own tracker). + No violation found by static reading; reproducing needs either + the phone or a Mali driver trace, neither available this pass. + Instrumented for the next report instead: `Device:: + on_uncaptured_error` is now installed on the Android device + (`WgpuErrorLog`, `android::render::AndroidRenderer`), and + `IrisViewPeer::render` logs masks/moves-resized, atlas + pages-grown and image bind-group creates for the first 10 frames + after every `surface_changed` -- exactly the window this bug + lands in. The bench screen's new "Diagnostics" button (below) + surfaces the error log and adapter identity on demand. + 2. **Bold words render as blank gaps of the correct advance width** + (regular, links, inline code render fine). Fixed by bundling Noto + Sans/Noto Sans Mono (regular/bold/italic/bold-italic, static + cuts, OFL) into `iris-core` and registering them ahead of the + platform's own fonts -- `core/src/primitive/text.rs`'s + `TextData::register_bundled_fonts`. Named hypothesis, not + confirmed on the phone: the system "Roboto" on a modern Android + device is the variable "Roboto Flex," and this crate's glyph + path (`TextData::place`) does not apply `Synthesis`/variable-axis + correction at all -- a bundled *static* per-style face sidesteps + the question rather than answering it. `TextData::font_diagnostics` + reports what got resolved; logged once at startup and shown on + the Diagnostics page. + 3. **Text far too small** -- iris had no device-pixel-ratio handling + on *either* platform before this pass (grepped for `scale_factor` + across the whole crate: zero hits). `DisplayMetrics.density` + (Android) / `Window::scale_factor()` (desktop) now divides every + physical-pixel number (window size, touch coordinates, the + shader's window uniform) down to logical units before it reaches + layout, so a `font_size: 16.0` is 16 dp rather than 16 raw device + pixels on a ~3x-density phone. Cost a second, real bug found only + by measuring on this checkout's emulator after the first fix + landed: `android::view::IrisViewPeer::surface_changed`'s call + into `UiRenderState::resize` (the layout engine's own notion of + the canvas, which every widget's absolute `PixelRegion` is + computed against) was still being handed raw physical + `width`/`height`, while `AndroidRenderer`'s side of the same + resize had already switched to logical -- splitting layout and + the shader into two different units. A fixed-size widget (the + bench screen's `.height(56)` button row) exposed it at ~40 + physical px against the ~147px `56 * content_scale` predicts; + a proportional (`rest(n)`) size hid it by adapting to whichever + total it was given. Both are logical now. **Not fully verified**: + a fresh-install emulator screenshot after both fixes shows + visibly larger, readable text (`docs/bench/` has neither + screenshot committed -- see AGENTS.md on transcripts/screenshots + not going in this repo -- but the before/after is described in + the commit), and the button row's own height still isn't + obviously matching `56 * content_scale` on this run -- worth a + second look with `ui-trace show --field box` once there's time, + but not a blocker for the magnitude of the original bug (3x too + small). + 4. **Status-bar inset not applied** -- confirmed nothing in this + app ever read `insets().top` at all (`android/insets.rs` has + carried `Insets.top` since it was written; nothing consumed it). + Fixed with a new, generic hook: `AndroidAppState:: + on_insets_changed(rsc, LogicalInsets)`, called from `render()` + exactly when `AndroidUiState::insets()` changes, in logical units + matching everything else `content_scale` now divides. + `BenchClient::on_insets_changed` rebuilds the root tree with + `Padding::top(insets.top)` on the button row -- rebuilding the + whole tree rather than one `WidgetPtr` slot's content, because + the first attempt (a `Pad` dropped into an unrelated `WidgetPtr` + slot with no height override of its own) did not propagate the + wrapped span's fixed height correctly, which is what surfaced + finding 3's `UiRenderState::resize` bug in the first place. + Verified via `ui-trace show --field box`: the button row's top + (150 physical px) sits 8px below `statusBarBackground`'s bottom + edge (142px) on this checkout's emulator. + + **A named `Diagnostics` control now exists** (RUST.md's own earlier + ask): a third button on the bench screen's top row, filling the + existing benchmark-report `TextEdit` with adapter identity/backend/ + driver, font resolution, the atlas's live view count, every + uncaptured wgpu error since surface creation, and the frame report + -- `android::render::AndroidRenderer::diagnostics_report`. Uses the + existing "Copy report" button/clipboard path rather than a second + one. + + **Verified this pass, this checkout's emulator** (`EMU_GPU` default, + `--features force-gles` -- this cold `emu up` again enumerated zero + Vulkan adapters, the same pre-existing flakiness earlier boxes + documented, not something this pass's diff caused): `cargo fmt --all + -- --check`, `cargo clippy --workspace --all-targets` (zero warnings + beyond the pre-existing wgpu future-incompat notice), `cargo test + --workspace` (all passing, unchanged pure-logic counts), `cargo ndk + -t x86_64 -P 26 check` clean, `./run-bench.sh` end to end + (`frames=691`, 24/24 swipes, 400/400 streamed events, no crash), + fresh-install screenshots and `ui-trace` box readouts for the four + items above. **Not verified this pass**: the actual phone (no + access), and item 1's root cause (needs either the phone's next + Diagnostics-page report or a Mali trace). + + **Recorded but not fixed this pass** (a follow-up agent takes these, + to avoid colliding with this pass's `bench_client.rs`/`view.rs` + changes) -- see `IRIS_TODO.md`'s "From the phone, 2026-09-06": + swiping has no momentum (stops exactly where the finger releases, + unlike Compose's fling), and scrolling down sometimes jitters the + text. + + **Redelivered, 2026-09-06, later the same day.** New arm64 APK + (Vulkan, no `force-gles`, bundled fonts, content-scale fix, + Diagnostics control), same `dev.iris.android.demo.bench` id, same + `CN=ai-app` signing cert, copied to `~/host/bench/ + iris-bench-arm64.apk` and `~/repos/ai-app-bench/iris/build/outputs/ + apk/release/iris-bench-arm64.apk`; that repo's own README gained a + dated entry. Still not confirmed on Iris's actual phone. + - [ ] **P1 — session screen parity.** History paging backward (with the page-boundary healing `client-core` does not have yet, below), `TranscriptSource`-backed cache/server stitching, jump-to-latest,