diff --git a/RUST.md b/RUST.md index 202bd03..0ccc49d 100644 --- a/RUST.md +++ b/RUST.md @@ -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 accepted. -- [ ] **E0 — toolchain.** No NDK is installed (`~/Android/Sdk/ndk` is - empty; the Android Rust targets are). Install the NDK under the - user-owned SDK, `cargo-ndk`, and record the versions here. +- [x] **E0 — toolchain (done 2026-09-04).** Installed under the + user-owned SDK: **NDK r29 (`29.0.14206865`)**, 2.4 GB at + `~/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 builds with `cargo-ndk` + Gradle, renders on the GPU, and the phone's 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 GitHub copy, so a later reconciliation has a known base. A crate that uses it says `iris = { path = "../iris" }`. -- [ ] **I0b — make it build here.** Pin a dated nightly in - `iris/rust-toolchain.toml` (the machine has - `nightly-x86_64-unknown-linux-gnu`, rustc 1.100.0-nightly - 2026-08-25); list every `#![feature]` gate with what it is for, so - each can be retired when it stabilises. Get `cargo build`, `clippy` - and `fmt` clean at the defaults and the `tabs` example running in a - window (this VM has no display: a headless compositor is what - `emu` uses for the emulator, and the same trick serves here). - Pass: a fresh clone builds unattended from the pinned toolchain. - **Measured 2026-09-04**: `cargo +nightly check` on that nightly - fails in `iris-core` with 36 errors, all one cause — the - `const_trait_impl` feature changed shape between January and - August: `impl const Trait for T` is now rejected with "expected a - trait, found type" unless the trait itself is declared - `const trait`, at seven sites (`num.rs`, `align.rs`, `axis.rs`, - `pos.rs`, `color.rs`, `math.rs`, `vec2.rs`), and the unresolved - `UiVec2`/`Vec2`/`impl_op` imports cascade from those. That is the - nightly-drift risk in one build; the fix is mechanical (declare - the traits `const trait`), and it is the first item of I0b rather - than pinning back to a January nightly, which would only defer it. +- [x] **I0b — make it build here (done 2026-09-04).** iris now builds, + clippy-clean and rustfmt-clean at the defaults, on a pinned dated + nightly, and the `tabs` example draws on this VM's GPU. + + **The pin** is `nightly-2026-09-03` (rustc 1.100.0-nightly, + `2e2b193f8`), declared in `iris/rust-toolchain.toml` along with the + `clippy`/`rustfmt` components and the two Android targets, so a + fresh clone provisions itself. It is dated rather than `nightly` + because the whole failure below was a rolling channel moving under + an unattended build. Installed with `--profile minimal`: 912 MB. + + **The 36 errors were one syntax change, and the earlier diagnosis in + this file was wrong.** It is not that a trait must now be declared + `const trait` — the vendored tree already declares them that way, + which is how it was written in January. What changed is the *impl* + keyword order: `impl const Trait for T` is now + `const impl Trait for T`, and generics go on the `impl` + (`const impl Bar for T`). Bounds are unaffected; + `T: const Foo`, `T: [const] Foo` and `impl const Foo` in argument + position all still compile. Everything else — the unresolved + `UiVec2`/`Vec2`/`impl_op` imports, and a `Color` 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` + 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` 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 transcript needs rich inline spans (links, code chips, colour), selection across many widgets with the platform's handles on the