Merge remote-tracking branch 'origin/rustify' into worktree-agent-a673ba12761c025d9

This commit is contained in:
iris committed 2026-09-06 21:33:28 -04:00
commit b332873894
9 files changed
+298 -35

No files matched your search

+40 -22
View File
@@ -591,17 +591,23 @@ agent ticks it here with the evidence.
that. Fixed in `iris/src/widget/rect.rs`; the reason is written at the
definition. Suspect the same cause for anything else tinted with a
background rect.
- [ ] **A wrapped transcript row trips `reposition`'s debug assert.**
*"widget ... is both moved by its parent's own layout (`mov`) and
repositioned within it"*, raised from `List::place`. Repro: change
`.wrap(!verbatim)` to `.wrap(true)` in `transcript-ui/src/row.rs`'s
`build_block` and run `iris/run-headless.sh transcript --shot
/tmp/x.png -- -p transcript-ui`. Survives the `Rect` fix above and is
not specific to any block kind -- it appears once the row is tall
enough. The shipping configuration does not reach it (verbatim blocks
do not wrap) and the Android bench runs clean with assertions live,
but it is a real disagreement about who owns a widget's move slot and
should be settled before more of P1 leans on `List`.
- [x] **A wrapped transcript row tripped `reposition`'s debug assert.**
Settled 2026-09-06 by giving the move slot one owner instead of two.
`mov` accumulates a delta on it, `reposition` overwrote it, and both
legitimately land on one widget in one frame: `List::place`'s
Bottom-known branch offers a row a same-size box that has *moved*
(`mov`), then corrects the placement inside it when the row's cached
height no longer matches what the row reports (`reposition`). The
slot now always means `move_applied + repositioned`
(`ActiveData::repositioned`, `iris/core/src/ui/render_state.rs`), so
`reposition` adds the move rather than dropping it -- the assert is
gone and the arithmetic is right. Test:
`a_widget_moved_by_its_parent_and_then_placed_inside_it_lands_at_the_placement`
in `layout_tests.rs`, which lands the child at the *offered* position
(-100px) instead of the placement (100px) without the fix, and a
`debug_assert_eq!` in `reposition` that nothing but those two ever
writes the slot. Verified with the `.wrap(true)` repro (draws, no
panic) and an emulator bench run with assertions live.
- [ ] **Desktop colours are washed out: the winit surface is sRGB and
the shader writes the palette's bytes as linear.** Mocha Crust
(17,17,27) is drawn as (73,73,91), measured off
@@ -609,17 +615,29 @@ agent ticks it here with the evidence.
format rather than the palette -- but it makes the desktop build
useless as a colour reference, which is exactly what P1a needed it for
when the emulator could not draw glyphs.
- [ ] **The emulator cannot draw iris's glyphs.** Under `-gpu host` with
Vulkan disabled (Mesa 26.2.2 / virgl -- what `emu` does on this
machine) every character renders as a solid filled box: the atlas
sample's alpha reads 1, which is what an incomplete GL texture returns
(0,0,0,1). Not new (`20303e0` does it too) and not the platform's
(Compose draws text perfectly on the same AVD in the same minute).
Enabling host Vulkan still dies at boot in gfxstream, and
`EMU_GPU=software` gives SwiftShader Vulkan on which iris **SIGSEGVs
in `surface_changed`**. Either of the last two would restore
appearance testing on Android; today it has to be done on the desktop
backend or on Iris's phone.
- [x] **Every glyph was a solid box on the GLES backend -- iris's bug,
not the emulator's.** Fixed 2026-09-06. The atlas is one
`texture_2d_array` and `GpuTextures::new` created it with **one
layer**; wgpu-hal picks the GL target from the descriptor
(`(false, 1) => TEXTURE_2D`), so under GLES that array was a
`GL_TEXTURE_2D` bound to the shader's `sampler2DArray`, the unit was
incomplete, every `textureSample` returned (0,0,0,1), and
`draw_glyph`'s `color.a *= texel.a` filled the quad. `MIN_ARRAY_LAYERS
= 2` in `iris/core/src/render/texture.rs`, with a `debug_assert!` at
`create_array_texture`. Vulkan (the phone, the desktop's default
backend) was never affected. Reproduce the class in seconds without an
emulator: `iris`'s `force-gles` feature now switches the **desktop**
backend too -- `./run-headless.sh transcript --shot /tmp/x.png -- -p
transcript-ui --features iris/force-gles`.
- [ ] **The bench report pane draws over the transcript rows instead of
replacing them.** Visible on the emulator for the first time now that
glyphs render there (`/tmp/emu-final.png`, 2026-09-06): after a bench
run the report's lines and the transcript's occupy the same rows in the
top third of the screen, both legible, neither on top. Pre-existing --
the same overlap is in a screenshot taken before the move-slot fix -- so
it is its own item, most likely the report pane not masking or not
claiming its region.
## Build (for the port)
+90 -3
View File
@@ -559,9 +559,12 @@ column above.
reconnect/backoff and cross-thread stream cancellation, which are
runtime policy for whichever framework embeds this crate, not pure
logic -- see `CLIENT_CORE.md`'s new section for the full account.
- **Then**: redeliver `~/host/bench/iris-bench-arm64.apk` for Iris with
its README saying what changed, and record any choice she should see in
`DECISIONS.md`.
- **Then**: redeliver the APK for Iris. **Delivery is a push to the
`~/repos/ai-app-bench` repo** (`iris/build/outputs/apk/release/
iris-bench-arm64.apk` plus a dated README section), which Dev Updater
on the host pulls -- **not** `~/host/bench/`, which nothing reads; two
builds on 2026-09-06 went there and never reached her phone. Record any
choice she should see in `DECISIONS.md`.
## Where things stand (2026-09-05)
@@ -5600,6 +5603,90 @@ device.
transcript-ui -p desktop-app -p tabs-ui --all-targets`
warning-free; `cargo test` 85 (iris, +4) + 13 (iris-core) +
31 (transcript-ui, +11) + 123 (client-core).
**2026-09-06, after P1a: "the emulator cannot draw iris's glyphs"
was iris's bug, not the emulator's.** The finding recorded in the
box above -- that every glyph is a solid filled box under `-gpu
host` GLES and that this is what an incomplete GL texture returns
-- had the mechanism right and the attribution wrong. It is a real
defect on **any** adapter that is GL rather than Vulkan.
- **Reproduced off the emulator entirely**, which is what made it
cheap: `default/render.rs` now honours the same `force-gles`
feature `android/render.rs` did, so
`./run-headless.sh transcript --shot /tmp/x.png -- -p
transcript-ui --features iris/force-gles` draws the boxes on this
machine's own GPU in seconds. Two shader probes then said what
the sample was: `return vec4(texel.rgb, 1.0)` drew black boxes and
`return vec4(texel.a, texel.a, texel.a, 1.0)` drew white ones, so
the atlas sample was exactly (0, 0, 0, 1) -- GL's answer for an
**incomplete texture unit**, and not the null texture (which is
zeroed, alpha 0).
- **Root cause: the glyph atlas array was created with one layer.**
`GpuTextures::new` started `array_capacity` at 1 and `grow_array`
only doubles once a page needs a layer past it, so the ordinary
case -- one atlas page -- is a one-layer array. wgpu-hal picks the
GL texture target from the descriptor alone
(`gles::Texture::get_info_from_desc`: `(false, 1) => TEXTURE_2D`),
so that array is created as a `GL_TEXTURE_2D` and then bound to
the shader's `sampler2DArray`. wgpu has a name for this
(`log_failing_target_heuristics`, its issues #1614/#1574); the
result is an incomplete unit, `texel.a == 1`, and `draw_glyph`'s
`color.a *= texel.a` paints the whole glyph quad.
- **Fix**: `MIN_ARRAY_LAYERS = 2` in
`iris/core/src/render/texture.rs` -- the array is never created
with fewer, with the account at `create_array_texture` and a
`debug_assert!` there so a future capacity arithmetic change fails
at the mistake rather than as boxes on a screen. Cost: one page of
texture memory, which the next atlas page uses anyway.
- **Not a regression from `3e72a4e..20303e0`, and the bisect was not
run.** The defect is a function of the layer count, not of any
commit in that range: it has been there since the atlas became a
`texture_2d_array` (TEXTURES.md, 2026-09-04) and it reproduces at
HEAD and disappears at HEAD with the one-line capacity change. The
claimed "visible text at `3e72a4e`" is a misreading of its own
evidence -- `/tmp/final-typing.png`, the screenshot that entry
cites, is boxes; what the agent read was the `iris text render:
chars=5 glyphs=5` log line, which reports what **parley shaped**,
not what reached the screen. The earlier genuinely-good emulator
shots (`/tmp/after3.png`, 2026-09-05 19:56) predate the APK being
built with `force-gles` at all, so they were the Vulkan path.
- **The phone build is not affected and does not need withdrawing.**
`android-app/build-apk.sh`'s default features are deliberately
without `force-gles` (`d73db97`'s comment), so a phone build takes
`Backends::PRIMARY` -> Vulkan, where a one-layer array is an
ordinary one-layer array and glyphs draw correctly -- which is
also what Iris's phone reports have shown all along. The fix
matters for any device that falls back to GLES, which is why it is
not just an emulator convenience.
- **Appearance testing on Android is back.** `build-apk.sh debug
--abi x86_64 --features "transcript-screen bench force-gles"` on
this checkout's AVD draws the transcript legibly --
`docs/bench/p1a-2026-09-06/iris-emulator-gles-fixed.png` -- so
P1b onwards can be checked here rather than only on the desktop
backend or Iris's phone.
**2026-09-06: the move slot has one owner.** IRIS_TODO's open
"a wrapped transcript row trips `reposition`'s debug assert" is
fixed rather than suppressed. `mov` accumulates a delta on a
widget's move slot and `reposition` overwrote it, and both
legitimately land on one widget in one frame: `List::place`'s
Bottom-known branch offers a row a same-size box that has *moved*
(`mov`), then corrects the placement inside it when the row's
cached height no longer matches what the row reports
(`reposition`) -- measured with a probe on the `.wrap(true)` repro:
`h=1604.7 height=548.7`, the row's own draw having updated
`active.size` without the list's height cache. The slot now means
`move_applied + repositioned` (both on `ActiveData`), so
`reposition` adds the move instead of dropping it and stays
idempotent, and the old assert is replaced by a `debug_assert_eq!`
that nothing *but* those two ever wrote the slot. Test:
`a_widget_moved_by_its_parent_and_then_placed_inside_it_lands_at_the_placement`
(`layout_tests.rs`), which draws the child at the offered position
rather than the placement without the fix. Checks: `cargo fmt --all
--check` clean, `cargo clippy --workspace --all-targets`
warning-free, `cargo test --workspace` 86 (iris, +1) + 13
(iris-core) + 31 (transcript-ui), the `.wrap(true)` repro drawing
correctly, and an emulator bench run with assertions live and no
abort (`2438 frames over 147.7s, p50 27.2ms`).
- [ ] **P1b — tool-call cards and grouping.** `ToolRows.kt`/
`ToolInput.kt`'s cards: a collapsed row per call with name
and a one-line summary, expand to input and output, runs of
Binary file not shown.

After

Width:  |  Height:  |  Size: 294 KiB