RUST.md: record E0 and I0b, with what they measured

E0 is done (NDK r29, cargo-ndk 4.1.2, verified by cross-compiling to both
ABIs) and I0b is done. Corrects this file's guess at why iris would not
build, notes the const-traits family as the gates to re-read whenever the
pin is advanced, and records the cold build weight against the "slow in
debug" worry: 43s and 2.1 GB plain, 1m46s and 1.5 GB with dependencies at
opt-level 2.

Also records an open defect found on the way -- iris sometimes keeps its
pre-configure window size for good -- with what was ruled out, since it
is timing-sensitive enough that any added print hides it, and I2 will
meet it on every rotation.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
irisandClaude Opus 5 committed 2026-09-04 17:47:44 -04:00
1 parent b6b0928087
commit e0ee7d6e94
1 file changed
+119 -23
+119 -23
View File
@@ -418,9 +418,20 @@ Android backend wants API 26 (a libc symbol). The real phone has Vulkan.
Per the standing rule, a rig limit is something to fix before it is Per the standing rule, a rig limit is something to fix before it is
accepted. accepted.
- [ ] **E0 — toolchain.** No NDK is installed (`~/Android/Sdk/ndk` is - [x] **E0 — toolchain (done 2026-09-04).** Installed under the
empty; the Android Rust targets are). Install the NDK under the user-owned SDK: **NDK r29 (`29.0.14206865`)**, 2.4 GB at
user-owned SDK, `cargo-ndk`, and record the versions here. `~/Android/Sdk/ndk/29.0.14206865`, the newest stable — r30 is still
at rc.3. **cargo-ndk 4.1.2**. Verified by cross-compiling a scratch
`cdylib` to both ABIs: `file` reports "for Android 26, built by NDK
r29 (14206865)" for `aarch64-linux-android` and
`x86_64-linux-android`. Two things to know at the call site.
**cargo-ndk 4's API-level flag is `-P`, not `-p`**`-p` is now
passed through to cargo as `--package`, so the old
`cargo ndk -t arm64-v8a -p 26` panics with `unknown package: 26`
*and dumps the whole environment to stdout* as a bug report, which is
worth not doing in a log somebody might paste. And the Android
targets were installed for **stable** only; the pinned nightly needs
its own, which `iris/rust-toolchain.toml` now declares.
- [ ] **E1 — android-view's Masonry demo on this emulator.** Pass: it - [ ] **E1 — android-view's Masonry demo on this emulator.** Pass: it
builds with `cargo-ndk` + Gradle, renders on the GPU, and the phone's builds with `cargo-ndk` + Gradle, renders on the GPU, and the phone's
own keyboard types into its editor with autocorrect and suggestions. own keyboard types into its editor with autocorrect and suggestions.
@@ -461,26 +472,111 @@ step measured.
`7b54aaf` ("readme", 2026-01-29), byte-identical to the public `7b54aaf` ("readme", 2026-01-29), byte-identical to the public
GitHub copy, so a later reconciliation has a known base. A crate GitHub copy, so a later reconciliation has a known base. A crate
that uses it says `iris = { path = "../iris" }`. that uses it says `iris = { path = "../iris" }`.
- [ ] **I0b — make it build here.** Pin a dated nightly in - [x] **I0b — make it build here (done 2026-09-04).** iris now builds,
`iris/rust-toolchain.toml` (the machine has clippy-clean and rustfmt-clean at the defaults, on a pinned dated
`nightly-x86_64-unknown-linux-gnu`, rustc 1.100.0-nightly nightly, and the `tabs` example draws on this VM's GPU.
2026-08-25); list every `#![feature]` gate with what it is for, so
each can be retired when it stabilises. Get `cargo build`, `clippy` **The pin** is `nightly-2026-09-03` (rustc 1.100.0-nightly,
and `fmt` clean at the defaults and the `tabs` example running in a `2e2b193f8`), declared in `iris/rust-toolchain.toml` along with the
window (this VM has no display: a headless compositor is what `clippy`/`rustfmt` components and the two Android targets, so a
`emu` uses for the emulator, and the same trick serves here). fresh clone provisions itself. It is dated rather than `nightly`
Pass: a fresh clone builds unattended from the pinned toolchain. because the whole failure below was a rolling channel moving under
**Measured 2026-09-04**: `cargo +nightly check` on that nightly an unattended build. Installed with `--profile minimal`: 912 MB.
fails in `iris-core` with 36 errors, all one cause — the
`const_trait_impl` feature changed shape between January and **The 36 errors were one syntax change, and the earlier diagnosis in
August: `impl const Trait for T` is now rejected with "expected a this file was wrong.** It is not that a trait must now be declared
trait, found type" unless the trait itself is declared `const trait` — the vendored tree already declares them that way,
`const trait`, at seven sites (`num.rs`, `align.rs`, `axis.rs`, which is how it was written in January. What changed is the *impl*
`pos.rs`, `color.rs`, `math.rs`, `vec2.rs`), and the unresolved keyword order: `impl const Trait for T` is now
`UiVec2`/`Vec2`/`impl_op` imports cascade from those. That is the `const impl Trait for T`, and generics go on the `impl`
nightly-drift risk in one build; the fix is mechanical (declare (`const impl<T: [const] Foo> Bar for T`). Bounds are unaffected;
the traits `const trait`), and it is the first item of I0b rather `T: const Foo`, `T: [const] Foo` and `impl const Foo` in argument
than pinning back to a January nightly, which would only defer it. position all still compile. Everything else — the unresolved
`UiVec2`/`Vec2`/`impl_op` imports, and a `Color<u8>` that resolved
to `wgpu_types::Color` — cascaded from the seven files that failed
to parse. The rewrite was mechanical across 20 sites and took the
workspace from 36 errors to 0.
**`#![feature]` gates, 12 after this step** (two were declared and
unused, and were removed: `map_try_insert`, `const_cmp`).
Load-bearing and worth watching: `const_trait_impl`, `const_ops`,
`const_convert`, `const_destruct` are the const-traits family and
the one that has already broken once — they move together, so
advancing the pin means re-reading this section. `unboxed_closures`
+ `fn_traits` (postfix builder API) and `unsize` +
`coerce_unsized` (widget handles) are pairs. The rest are
individually small: `macro_metavar_expr_concat`, `portable_simd`,
`associated_type_defaults`, `option_into_flat_iter`, and `gen_blocks`
in the top crate.
**Running it headless.** `iris/run-headless.sh EXAMPLE [--shot PNG]`
with `iris/headless.conf`, the same trick `emu` uses: a headless
sway, and `grim` for the picture. It deliberately starts its *own*
compositor rather than joining `emu`'s — sway tiles, so adding a
window to the one an emulator sits in resizes that emulator.
Unlike `emu`'s it disables Xwayland, since winit speaks Wayland.
**This VM has a real GPU for this**: Vulkan 1.4 through Venus onto
the host's RX 7900 XT, and GL 4.6 through virgl — so desktop wgpu
work here is not software-rasterised, unlike inside the emulator.
**iris has no tests at all** (`cargo test --workspace`: 0 passed
across 6 targets). Nothing to keep passing, and nothing to catch a
regression — worth knowing before I1 changes the text stack.
**`iris-core` no longer depends on winit, and now cross-compiles to
Android.** It wanted exactly one thing from it — `PhysicalSize<u32>`
in `UiRenderNode::resize`'s signature, for two numbers it immediately
turned into floats — and that pulled a whole windowing backend into
the layer below it, the wrong direction. `resize` takes
`impl Into<Vec2>` now, like `UiRenderState::resize` beside it already
did. The consequence is the point: with winit in the graph an Android
build of the core failed in `android-activity` (which needs a backend
feature nothing here selects), and without it
`cargo ndk -t arm64-v8a -P 26 build -p iris-core` finishes in 30s and
produces an rlib, wgpu's Android backend included. So **iris's
widget, layout and render core already builds for the phone**, and
what I2 has to supply is the surface, the input and the IME — not a
port of the library.
**Build weight, cold, on this VM's 8 cores** (`rm -rf target`, then
`cargo build --example tabs`), since "the Linebender stack is slow in
debug" was the worry behind this question: plain debug **43s** and a
2.1 GB `target/`; with the `[profile.dev.package."*"] opt-level = 2`
knob, **1m46s** and 1.5 GB. So iris's own wgpu + winit + cosmic-text
graph is not the slow thing — which makes it a calibration for E1
rather than an answer about Masonry, whose graph adds Vello, Parley,
Fontique and Skrifa. Runtime cost of the knob was not measured here.
**Open defect found while doing this: iris sometimes never adopts
the window's real size.** Measured on the headless rig, ~3 starts in
15: the `tabs` example settles showing its 800×600 startup layout in
the top-left of a 1920×1200 surface, black around it, and stays that
way indefinitely — it is not a screenshot taken too early, since the
picture is byte-identical for the next four seconds. What is *not*
the cause, each checked: the winit event order is identical in good
and bad runs (`Resized(800×600)`, two redraws, `Resized(1920×1200)`,
one redraw), the swapchain reports `1920×1200` and
`suboptimal=false` on that last draw, and `output_size` is
`(1920, 1200)` going into it. It is timing-sensitive in the way that
makes it expensive: adding a single `eprintln!` anywhere in the draw
or event path hides it completely (0 in 16), which is why the
instrumentation above could not catch it in the act. A pointer move
does not repair it, because iris only redraws when something
changed; an output mode change does, because that is another resize.
Left open rather than guessed at. It matters most for **I2**, where
every rotation and every keyboard open is a resize, so a stale frame
would be the normal case rather than a rare one; a Wayland-level
trace of the xdg-surface configure/ack/commit sequence is the next
step, not more `eprintln`.
One thing was fixed on the way, and it is not that bug: `update`
redrew everything when `resized` was set, but `needs_redraw` — which
is what decides whether to *ask* for a frame — did not know about
`resized` at all. The two now share one `needs_redraw_all`, since a
condition in one and not the other is a frame nobody requests. It is
latent on Wayland only because winit asks for a redraw after a resize
by itself; on Android, where the surface work of I2 will not have
winit underneath it, nothing else here would have asked.
- [ ] **I1 — the text stack decision.** iris uses cosmic-text; the - [ ] **I1 — the text stack decision.** iris uses cosmic-text; the
transcript needs rich inline spans (links, code chips, colour), transcript needs rich inline spans (links, code chips, colour),
selection across many widgets with the platform's handles on the selection across many widgets with the platform's handles on the