diff --git a/AGENTS.md b/AGENTS.md index 20786bf..08b18d9 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -297,6 +297,17 @@ Each exists because something was invisible without it. into it. The emulator is for JNI, the IME, insets, the surface lifecycle and one verification run before a build goes to the phone -- not for iterating on layout. +- **The emulator is a GLES rig, deliberately** (Iris, 2026-09-08; + docs/DECISIONS.md). Its guest has no hardware Vulkan -- only SwiftShader + in software -- while its GLES *is* the host's real GPU through virgl at + ES 3.1, so an ordinary build's runtime fallback lands there by itself + and nothing should pass `force-gles` to arrange it. The Vulkan path is + verified on the desktop build and on Iris's phone. Do not boot the + emulator with SwiftShader Vulkan to "test the Vulkan path": that + measures a software rasteriser and steers iris away from the one + hardware-accelerated backend it has there. Every run says which adapter + drew it (`iris renderer:` in logcat, printed by `run-bench.sh`); read + that line before reading a number. ### Driving the UI diff --git a/docs/DECISIONS.md b/docs/DECISIONS.md index cdc901c..3f4a3bc 100644 --- a/docs/DECISIONS.md +++ b/docs/DECISIONS.md @@ -5,6 +5,38 @@ they can be judged and reversed later. Detail lives in RUST.md (and IRIS.md for iris API changes); this file is only the summary. Newest first. Items marked **DEFERRED** are ones the agent chose not to decide alone. +## 2026-09-08 (the emulator is a GLES machine, and Vulkan is verified elsewhere) + +- **Directed by Iris, carried out here**: "make sure the setup uses GL for + the android emulator and remove any vulkan requirements. That'll be + tested through both the desktop version as well as my phone." So the + emulator is settled as a GLES rig and nothing chases hardware Vulkan in + it any more; the Vulkan path is covered by the desktop build and by her + phone. +- **Nothing had to be forced to make that true.** Measured in the guest + the same day: the emulator has no hardware Vulkan at all (its only + Vulkan is SwiftShader, in software) and its GLES is the host's real RX + 7900 XT through virgl at ES 3.1. iris's existing runtime fallback -- + `Backends::PRIMARY`, no adapter, rebuild on `Backends::GL` -- already + lands there, verified end to end with an ordinary (no `force-gles`) + debug APK. +- **The emulator and the phone therefore run the same binary**, differing + only in what that binary finds. That is deliberate and worth not + undoing: a build flag that changed the backend would mean the thing + measured on the emulator is not the thing shipped. `force-gles` stays, + but only for pinning the backend on a machine that *does* have Vulkan + (the desktop), and never for a phone build. +- **Every run now says which adapter drew it.** The Android renderer logs + the full adapter line at startup the way the desktop already did -- only + the backend enum was logged before, which cannot separate `Gl` on the + host's GPU from `Gl` on SwiftShader, or a phone's real Vulkan from a + software one. `run-bench.sh` prints that line before any number. +- **No Vulkan requirement was found in iris to remove.** `device_limits()` + asks for nothing beyond wgpu's defaults (and zeroes the compute fields), + neither backend requires a feature, and both probe rather than + `.expect()` an adapter. What was removed was the *documentation* telling + people to boot the emulator with SwiftShader Vulkan. + ## 2026-09-07 (platform fonts, not bundled ones) - **Iris's own decision, carried out as directed**: removed the 3.6 MB of diff --git a/docs/RUST.md b/docs/RUST.md index 6feaaff..3bc63c0 100644 --- a/docs/RUST.md +++ b/docs/RUST.md @@ -7706,9 +7706,21 @@ as external-memory modes, so there is nothing to switch to. The retry costs one boot; do it again when either the emulator package or Mesa moves. -### Vulkan in the emulator (measured 2026-09-04) +### Vulkan in the emulator (measured 2026-09-04, superseded 2026-09-08) -**Settled 2026-09-04: the guest gets Vulkan from SwiftShader, and the +**Do not follow the recipes below.** Iris settled the posture on +2026-09-08: the emulator is a **GLES** rig, tested on the host's real GPU +through virgl, and the Vulkan path is covered by the desktop build and by +her phone (docs/DECISIONS.md, 2026-09-08). Booting the emulator with +SwiftShader Vulkan gets a *software* Vulkan device, which is a slower and +less representative thing to measure than the GLES path that runs on real +hardware -- and it makes iris pick that software device over the fast +path, since the fallback only reaches for GLES when Vulkan has no adapter +at all. This section stays as the record of **why host Vulkan is not +available here** and what was ruled out, so the question is not +re-opened from scratch. + +**Measured 2026-09-04: the guest can get Vulkan from SwiftShader, and the missing step was a cold boot.** `-feature Vulkan` plus `VK_DRIVER_FILES=$HOME/Android/Sdk/emulator/lib64/vulkan/vk_swiftshader_icd.json` gets the *host* side to select SwiftShader, but the guest keeps reporting diff --git a/iris/Cargo.toml b/iris/Cargo.toml index 54286ea..8c4bc91 100644 --- a/iris/Cargo.toml +++ b/iris/Cargo.toml @@ -60,15 +60,20 @@ accesskit_android = "0.8.0" send_wrapper = "0.6.0" [features] -# RUST.md's I5 "Where iris's frame time goes" diagnosis: forces the Android -# `wgpu::Instance` to `Backends::GL` instead of `Backends::PRIMARY`, so the -# same build can be measured against SwiftShader's software Vulkan ICD (the -# default) or virgl's GLES path, without a second env-var plumbing path that -# nothing on this machine can hand to an already-launched Android process -# (there is no `am start` environment and no system-property reader here to -# add one). Read by `android/render.rs` and, so the GLES path can be -# reproduced on a machine with a real GPU rather than only in the emulator, -# by `default/render.rs`: +# RUST.md's I5 "Where iris's frame time goes" diagnosis: pins the +# `wgpu::Instance` to `Backends::GL` instead of `Backends::PRIMARY`, so one +# build can be measured on either backend. A compile-time feature rather +# than an env var because nothing on this machine can hand an env var to an +# already-launched Android process (there is no `am start` environment and +# no system-property reader here to add one). +# +# **Not needed to get GLES in the emulator**, whatever the history here +# says: the emulator's guest has no hardware Vulkan at all, so an ordinary +# build's runtime fallback lands on GLES by itself (docs/RUST.md, "What the +# emulator gives a GPU app"). Keeping the emulator on the same binary the +# phone runs is the point. What this feature is still for is forcing GLES +# on a machine that *does* have Vulkan -- the desktop -- which is why +# `default/render.rs` reads it too: # ./run-headless.sh transcript --shot /tmp/x.png -- -p transcript-ui \ # --features iris/force-gles force-gles = [] diff --git a/iris/android-app/build-apk.sh b/iris/android-app/build-apk.sh index 7d4b8f2..04cfeb7 100755 --- a/iris/android-app/build-apk.sh +++ b/iris/android-app/build-apk.sh @@ -12,19 +12,29 @@ # emulator stays on debug" rule -- pass `release` explicitly for a phone # build). --abi defaults to arm64-v8a (a phone/real device); pass # x86_64 for this checkout's own AVD. --features defaults to -# "transcript-screen bench" -- deliberately *without* `force-gles`, unlike -# an earlier version of this default. `force-gles` (`iris/Cargo.toml`'s -# own doc) exists only to force the emulator off its default software -# Vulkan and onto GLES for one specific measurement (RUST.md's I5, "Where -# iris's frame time goes") -- it was never meant to reach a real device, -# but this script's old default put it in every arm64 build regardless, -# so the P0 bench APK delivered to Iris's phone forced GLES there too. -# That is the named hypothesis in RUST.md's P0 box ("iris bench crash on -# the phone, 2026-09-06"): a real Vulkan driver is what a phone should -# run, and GLES is the backend the same box's own SwiftShader finding -# already flagged as the fragile one for this shader's storage buffers. -# Pass `--features "transcript-screen force-gles bench"` explicitly for -# an emulator backend-isolation run; never for a build meant for a phone. +# "transcript-screen bench" -- deliberately *without* `force-gles`, and +# nothing should add it back for the emulator's sake. +# +# **The emulator does not need a GLES build, because it has no hardware +# Vulkan to be steered away from** (docs/RUST.md, "What the emulator +# gives a GPU app", 2026-09-08): its guest's only Vulkan is SwiftShader +# in software, its GLES is the host's real GPU through virgl, and iris's +# own runtime fallback -- `Backends::PRIMARY`, no adapter, rebuild on +# `Backends::GL` -- takes an ordinary build there by itself. So the +# emulator and the phone run the *same binary* and differ only in what +# that binary finds, which is the whole point: a build flag that changed +# the backend would mean the thing measured here is not the thing +# shipped. +# +# `force-gles` (`iris/Cargo.toml`'s own doc) pins the backend at compile +# time for a backend-isolation measurement (RUST.md's I5, "Where iris's +# frame time goes"), and the desktop is the better place to run it now +# (`run-headless.sh ... --features iris/force-gles`). It was never meant +# to reach a real device, but this script's old default put it in every +# arm64 build regardless, so the P0 bench APK delivered to Iris's phone +# forced GLES there too -- the named hypothesis in RUST.md's P0 box +# ("iris bench crash on the phone, 2026-09-06"). Never pass it for a +# build meant for a phone. set -eu cd "$(dirname "$0")" diff --git a/iris/android-app/run-bench.sh b/iris/android-app/run-bench.sh index 3947e9a..e3bb22a 100755 --- a/iris/android-app/run-bench.sh +++ b/iris/android-app/run-bench.sh @@ -46,6 +46,21 @@ adb -s "$SERIAL" shell am start -n "$PKG/dev.iris.android.demo.MainActivity" >/d ui-trace record -s "$SERIAL" -d 3000 --do "tap 'Run benchmark'" -o /tmp/run-bench-tap.txt >/dev/null +# Which adapter drew, before any number is printed. The emulator is a GLES +# machine -- its guest has no hardware Vulkan (docs/RUST.md, "What the +# emulator gives a GPU app") -- so iris's runtime fallback lands on `Gl`, +# and `Gl (... virgl ...)` is the host's real GPU while `Gl (... +# SwiftShader ...)` is the CPU. Those two produce frame times an order of +# magnitude apart and are otherwise indistinguishable in this report, so +# the line is printed rather than left in logcat for somebody to think of. +ADAPTER=$(adb -s "$SERIAL" logcat -d -s iris-android-app:I 2>/dev/null \ + | sed -n 's/.*\(iris renderer: .*\)/\1/p' | tail -1) +if [ -n "$ADAPTER" ]; then + echo "run-bench.sh: $ADAPTER" +else + echo "run-bench.sh: no 'iris renderer:' line in logcat -- cannot say what drew this run" >&2 +fi + # Poll for the report line rather than a fixed sleep -- the run itself is # a fixed script (RUST.md's "Benchmark v2": 16 flings, a 20s streaming # phase, ~61s of typing, 10s of keyboard toggles, roughly 2.5 minutes end diff --git a/iris/src/android/platform.rs b/iris/src/android/platform.rs index 8f72fba..df5f867 100644 --- a/iris/src/android/platform.rs +++ b/iris/src/android/platform.rs @@ -1,10 +1,7 @@ use crate::platform::OpenUrl; use android_view::{ View, - jni::{ - JNIEnv, - objects::{JObject, JValue}, - }, + jni::{JNIEnv, objects::JValue}, }; use super::view::HasAndroidUiState; @@ -78,7 +75,7 @@ fn try_open_url<'local>( &context, "startActivity", "(Landroid/content/Intent;)V", - &[JValue::Object(&JObject::from(intent))], + &[JValue::Object(&intent)], )?; Ok(()) } diff --git a/iris/src/android/render.rs b/iris/src/android/render.rs index fcb1662..d790be2 100644 --- a/iris/src/android/render.rs +++ b/iris/src/android/render.rs @@ -214,11 +214,27 @@ impl AndroidRenderer { let info = adapter.get_info(); let adapter_name = info.name.clone(); let adapter_backend = info.backend; - let adapter_driver = if info.driver_info.is_empty() { - info.driver.clone() - } else { - format!("{} {}", info.driver, info.driver_info) - }; + // Either half can be empty -- the emulator's GLES adapter reports + // no `driver` and a long `driver_info`, so joining unconditionally + // left a leading space in every log line it appears in. + let adapter_driver = [info.driver.as_str(), info.driver_info.as_str()] + .into_iter() + .filter(|part| !part.is_empty()) + .collect::>() + .join(" "); + // Say which adapter won, in the same words `default::render` uses, + // and at startup rather than only on the Diagnostics page: the + // backend alone (logged by `view.rs` when a renderer is built) does + // not separate the cases that matter. In this checkout's emulator + // `Gl` is the host's real GPU through virgl, and `Gl` under + // `EMU_GPU=software` is SwiftShader on the CPU; on a phone `Vulkan` + // is the device's own driver. A frame time or a screenshot with no + // record of which of those produced it cannot be read, and the + // fallback above is silent by design. + log::info!( + "iris renderer: {adapter_name} ({adapter_backend:?}, {adapter_driver}) on \ + {backends:?}" + ); let surface_caps = surface.get_capabilities(&adapter); let surface_format = surface_caps diff --git a/iris/src/default/render.rs b/iris/src/default/render.rs index 1163027..abaada4 100644 --- a/iris/src/default/render.rs +++ b/iris/src/default/render.rs @@ -132,8 +132,8 @@ impl UiRenderer { panic!("No usable GPU adapter for backends {backends:?}: {error}") }); - // Say which adapter won, the way the Android backend's own report - // does. Without it a layer-2 screenshot or frame time from this + // Say which adapter won, in the same words the Android backend + // uses. Without it a layer-2 screenshot or frame time from this // window carries no record of what drew it, and the two cases that // matter look identical in the PNG: the host's real GPU, and // llvmpipe after this VM lost its virtio-gpu contexts. That