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>
118 lines
5.4 KiB
Bash
Executable File
118 lines
5.4 KiB
Bash
Executable File
#!/bin/sh
|
|
# Builds iris-android-app end to end: the cdylib (cargo ndk, straight into
|
|
# app/src/main/jniLibs/) then the APK (Gradle). Written to stop re-typing
|
|
# the same incantation by hand every time (ANDROID_HOME/NDK exports, the
|
|
# cargo ndk invocation, the keystore env for a release build, apksigner/
|
|
# aapt2 verification) -- see docs/RUST.md's P0 box. Same shape as `app/
|
|
# build-apk.sh` (the Compose app's own build script) and `app/
|
|
# iris-scroll.sh` (no coordinates, set -eu, exit 0 on success).
|
|
#
|
|
# Usage: ./build-apk.sh [debug|release] [--abi arm64-v8a|x86_64] [--features "a b c"]
|
|
# debug/release default to debug (matches this-machine-android's "the
|
|
# 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`, 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")"
|
|
|
|
BUILD_TYPE="debug"
|
|
ABI="arm64-v8a"
|
|
FEATURES="transcript-screen bench"
|
|
case "${1:-}" in
|
|
debug|release) BUILD_TYPE="$1"; shift ;;
|
|
esac
|
|
while [ $# -gt 0 ]; do
|
|
case "$1" in
|
|
--abi) ABI="$2"; shift 2 ;;
|
|
--features) FEATURES="$2"; shift 2 ;;
|
|
*) echo "build-apk.sh: unknown argument: $1" >&2; exit 1 ;;
|
|
esac
|
|
done
|
|
|
|
SDK_ROOT="$HOME/Android/Sdk"
|
|
export ANDROID_HOME="$SDK_ROOT"
|
|
export ANDROID_SDK_ROOT="$SDK_ROOT"
|
|
NDK_DIR=$(ls -d "$SDK_ROOT"/ndk/*/ 2>/dev/null | sort -V | tail -1)
|
|
if [ -z "$NDK_DIR" ]; then
|
|
echo "build-apk.sh: no NDK found under $SDK_ROOT/ndk" >&2
|
|
exit 1
|
|
fi
|
|
export ANDROID_NDK_HOME="$NDK_DIR"
|
|
|
|
# Only the ABI asked for goes into the APK. cargo ndk adds its output beside
|
|
# whatever earlier builds left here, and Gradle packages every directory it
|
|
# finds -- a debug x86_64 emulator build left behind made an arm64 "release"
|
|
# 339 MB on 2026-09-06.
|
|
rm -rf app/src/main/jniLibs
|
|
# ...and Gradle's own copy of them, which `rm -rf jniLibs` does not reach.
|
|
# `mergeReleaseNativeLibs` is *up to date* against its cached inputs, so a
|
|
# build that switches ABI packages the previous ABI: an `--abi x86_64`
|
|
# release APK containing `lib/arm64-v8a/libmain.so` installed fine and
|
|
# aborted at startup with `Could not get adapter!: NotFound {
|
|
# active_backends: VULKAN }` under libndk_translation -- which reads
|
|
# exactly like the phone's own Vulkan problem and is nothing of the kind.
|
|
# Scoped to the merge task's directory rather than all of `app/build`, so
|
|
# an ABI change costs the native merge and not the whole Gradle build.
|
|
rm -rf app/build/intermediates/merged_native_libs \
|
|
app/build/intermediates/stripped_native_libs \
|
|
app/build/intermediates/merged_jni_libs
|
|
echo "build-apk.sh: cargo ndk -t $ABI build ${BUILD_TYPE:+(${BUILD_TYPE})} --features \"$FEATURES\""
|
|
if [ "$BUILD_TYPE" = "release" ]; then
|
|
cargo ndk -t "$ABI" -P 29 -o app/src/main/jniLibs/ build --release --features "$FEATURES"
|
|
else
|
|
cargo ndk -t "$ABI" -P 29 -o app/src/main/jniLibs/ build --features "$FEATURES"
|
|
fi
|
|
|
|
GRADLE_TASK="assembleDebug"
|
|
APK_DIR="app/build/outputs/apk/debug"
|
|
APK_NAME="app-debug.apk"
|
|
if [ "$BUILD_TYPE" = "release" ]; then
|
|
GRADLE_TASK="assembleRelease"
|
|
APK_DIR="app/build/outputs/apk/release"
|
|
APK_NAME="app-release.apk"
|
|
# Same key `app/build-apk.sh` (the Compose app) generates once under
|
|
# ~/.config/ai-app/release.jks -- see AGENTS.md's "Checking your work".
|
|
export AI_APP_KEYSTORE="$HOME/.config/ai-app/release.jks"
|
|
if [ ! -f "$AI_APP_KEYSTORE" ]; then
|
|
echo "build-apk.sh: no release key at $AI_APP_KEYSTORE -- run app/build-apk.sh once first" >&2
|
|
exit 1
|
|
fi
|
|
export AI_APP_KEYSTORE_PASSWORD
|
|
AI_APP_KEYSTORE_PASSWORD=$(cat "$AI_APP_KEYSTORE.password")
|
|
fi
|
|
|
|
gradle ":app:$GRADLE_TASK" --console=plain
|
|
|
|
APK_PATH="$(pwd)/$APK_DIR/$APK_NAME"
|
|
BUILD_TOOLS=$(ls -d "$SDK_ROOT"/build-tools/*/ | sort -V | tail -1)
|
|
echo "--- aapt2 dump badging ---"
|
|
"${BUILD_TOOLS}aapt2" dump badging "$APK_PATH" | head -5
|
|
if [ "$BUILD_TYPE" = "release" ]; then
|
|
echo "--- apksigner verify ---"
|
|
"${BUILD_TOOLS}apksigner" verify --print-certs "$APK_PATH"
|
|
fi
|
|
echo "$APK_PATH"
|