emulator: settle on GLES, and make every run say which adapter drew it
Iris's call, after the guest measurement: the emulator is a GLES rig and nothing chases hardware Vulkan in it; the Vulkan path is covered by the desktop build and by her phone. Nothing had to be forced. 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, so iris's existing runtime fallback lands there by itself. Verified end to end with an ordinary debug APK: "no Backends(VULKAN|...) adapter on this device, falling back to GLES", then "Android Emulator OpenGL ES Translator (virgl (AMD Radeon RX 7900 XT ...)) (Gl, OpenGL ES 3.1 ...) on Backends(GL)". So the emulator and the phone run the same binary, differing only in what it finds -- which is the point, and `force-gles` must not be reintroduced to arrange the emulator's backend. What changed: - The Android renderer logs the full adapter line at startup, as the desktop already did. Only the backend enum was logged, which cannot tell `Gl` on the host's GPU from `Gl` on SwiftShader; the same rule was written on one member of the pair and not the other. - `run-bench.sh` prints that line before any number. - build-apk.sh, Cargo.toml and RUST.md's "Vulkan in the emulator" carried the stale premise that the emulator defaults to software Vulkan and has to be steered off it. The recipes are marked superseded rather than deleted, since the record of why host Vulkan is unavailable is still worth having. - Drive-by: an `#[allow]`-free clippy warning in android/platform.rs (useless JObject conversion) that only appears on the android target. No Vulkan requirement was found in iris itself to remove: neither backend asks for a feature, `device_limits()` stays at wgpu's defaults with the compute fields zeroed, and both probe rather than expect an adapter. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
af7d5f3782
commit
2756087e1c
9 files changed
+134
-36
No files matched your search
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
+14
-2
@@ -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
|
||||
|
||||
+14
-9
@@ -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 = []
|
||||
|
||||
@@ -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")"
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
|
||||
@@ -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::<Vec<_>>()
|
||||
.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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in new issue
Block a user