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 <noreply@anthropic.com>
This commit is contained in:
1 parent
fd7e17523d
commit
560a74caf8
3 files changed
+184
No files matched your search
@@ -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
|
~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
|
for the full numbers and the comparison's caveats (different AVD
|
||||||
instances, not a controlled A/B on identical hardware state).
|
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<Vec2>`) 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.
|
||||||
@@ -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
|
screen wants the same thing (P1's own transcript rows already read
|
||||||
their content from a `TextEdit` for the same reason).
|
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
|
## Build
|
||||||
|
|
||||||
- [x] **Benchmarks**, not unit tests, run on demand (2026-09-05; a
|
- [x] **Benchmarks**, not unit tests, run on demand (2026-09-05; a
|
||||||
|
|||||||
+123
@@ -4315,6 +4315,129 @@ device.
|
|||||||
confirming the phase marks partition the whole run rather than
|
confirming the phase marks partition the whole run rather than
|
||||||
overlapping or dropping frames between them.
|
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
|
- [ ] **P1 — session screen parity.** History paging backward (with the
|
||||||
page-boundary healing `client-core` does not have yet, below),
|
page-boundary healing `client-core` does not have yet, below),
|
||||||
`TranscriptSource`-backed cache/server stitching, jump-to-latest,
|
`TranscriptSource`-backed cache/server stitching, jump-to-latest,
|
||||||
|
|||||||
Reference in new issue
Block a user