Three root causes, all fixed and verified on the Pixel 9 Pro: 1. Present phase (backend + renderer): VSYNC_LOCKED_RENDER defers the render to the vsync wake; RENDER_AHEAD presents the previous frame at the start of render() (phase = wake + ~1.1 ms) and holds the new frame one slot. Crash fix: a held wgpu frame must be PRESENTED, never merely dropped, before the next acquire (AlreadyAcquired panic at launch otherwise); discard_pending_frame presents too. 2. Full-res prefetch churn (src/lib.rs): the 8-slot full cache cycled through a ~20-cell viewport forever (137 decodes/20 s) and each eviction free()'d a 50 MB Vec on the UI thread (20-52 ms CPU spikes, ~7/s = the residual SLOWEV NonMain jank). Fix: 60 s tombstone cooldown on evicted paths (idle prefetch skips, urgent tap bypasses) + off-thread reaper for the freed blobs (insert: 21 ms -> 4 us). 3. (earlier) Skia final flush + Mailbox present mode (vendor, M3). Instrumentation (all behind --features perf): OTHR/INVK/INVK_TAG/FLCK lines for cross-thread closure timing and push-site tagging. Warm measured swipe, 8580-file grid: cadence median 8.33 ms (exact 120 Hz), p99 15.5 ms, 5/552 gaps >16.7 ms (0.9%, max 28.3 ms, zero >33 ms); PRES phase median ~994 us, zero late frames. Docs updated (docs/BUGS/scroll-jank-120hz.md, full scorecard + line reference). Also: vendored i-slint-core + wgpu-hal (vkshim interposition patch), vkshim profiling tooling, backend made a non-optional dep, debuggable flag for simpleperf (REMOVE before shipping).
86 lines
4.0 KiB
Bash
Executable File
86 lines
4.0 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Build the gallery Android APK (and optionally install it).
|
|
#
|
|
# ./scripts/build.sh # release x86_64 (emulator) + install
|
|
# ./scripts/build.sh aarch64 # release aarch64 (real device) + install
|
|
# ./scripts/build.sh debug # debug x86_64 + install (faster builds)
|
|
# ./scripts/build.sh <arch> no-install
|
|
#
|
|
# Desktop (Linux): cargo run (needs libfontconfig1 + pkg-config)
|
|
# Desktop (macOS): cargo run (pure Rust, nothing extra)
|
|
set -euo pipefail
|
|
cd "$(dirname "$0")/.."
|
|
|
|
export ANDROID_HOME=$HOME/android-sdk
|
|
export ANDROID_NDK_ROOT=$HOME/android-sdk/ndk/27.0.12077973
|
|
# ~/.local/bin: nasm (host build of libjpeg-turbo SIMD; aarch64 needs no nasm).
|
|
export PATH="$HOME/.local/bin:$HOME/.cargo/bin:$ANDROID_HOME/platform-tools:$PATH"
|
|
|
|
# turbojpeg (M2): build the vendored libjpeg-turbo with CMake, statically linked
|
|
# into the app (no extra .so in the APK). The NDK CMake toolchain + ANDROID_*
|
|
# vars are picked up by the `cmake` crate; verified 2026-10 (HANDOFF §10).
|
|
if [ -n "$ANDROID_NDK_ROOT" ]; then
|
|
export CMAKE_TOOLCHAIN_FILE=$ANDROID_NDK_ROOT/build/cmake/android.toolchain.cmake
|
|
export ANDROID_PLATFORM=android-26
|
|
export ANDROID_STL=c++_static
|
|
export TURBOJPEG_STATIC=1
|
|
fi
|
|
# SDK platform used to compile the Slint Java helper (target API, matches
|
|
# target_sdk_version in Cargo.toml). Must be an installed platform dir.
|
|
SDK_PLATFORM=android-35
|
|
|
|
# Slint's android-activity build reads ANDROID_JAR first; setting it explicitly
|
|
# lets us ALSO export ANDROID_PLATFORM=android-26 below (for the NDK CMake
|
|
# toolchain's native API level) without the two tools fighting over that
|
|
# variable (`android-build` would otherwise look up platforms/android-26).
|
|
export ANDROID_JAR=$ANDROID_HOME/platforms/$SDK_PLATFORM/android.jar
|
|
|
|
# C/C++ cross-compilers for bundled native deps (libsqlite3-sys via rusqlite).
|
|
# The `cc` crate picks these up per target triple (min sdk 26).
|
|
NDK_BIN=$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/linux-x86_64/bin
|
|
# CMake target ABI (x86_64 default; overridden below for aarch64).
|
|
export ANDROID_ABI=x86_64
|
|
export CC_x86_64_linux_android=$NDK_BIN/x86_64-linux-android26-clang
|
|
export CXX_x86_64_linux_android=$NDK_BIN/x86_64-linux-android26-clang++
|
|
export AR_x86_64_linux_android=$NDK_BIN/llvm-ar
|
|
export CC_aarch64_linux_android=$NDK_BIN/aarch64-linux-android26-clang
|
|
export CXX_aarch64_linux_android=$NDK_BIN/aarch64-linux-android26-clang++
|
|
export AR_aarch64_linux_android=$NDK_BIN/llvm-ar
|
|
# Debug-keystore signing is fine for personal use / Play internal testing.
|
|
export CARGO_APK_RELEASE_KEYSTORE=$HOME/.android/debug.keystore
|
|
export CARGO_APK_RELEASE_KEYSTORE_PASSWORD=android
|
|
|
|
arg1=${1:-x86_64}
|
|
arg2=${2:-install}
|
|
|
|
profile=--release
|
|
case $arg1 in
|
|
debug) target=x86_64-linux-android; profile="" ;;
|
|
debug-aarch64) target=aarch64-linux-android; profile=""; export ANDROID_ABI=arm64-v8a ;;
|
|
x86_64) target=x86_64-linux-android ;;
|
|
aarch64) target=aarch64-linux-android; export ANDROID_ABI=arm64-v8a ;;
|
|
*) echo "unknown arch: $arg1" >&2; exit 1 ;;
|
|
esac
|
|
|
|
profile_dir=release; [ -z "$profile" ] && profile_dir=debug
|
|
|
|
# Optional: ADB_SERIAL picks the device when several are attached (see AGENTS.md).
|
|
adb=(); [ -n "${ADB_SERIAL:-}" ] && adb=(adb -s "$ADB_SERIAL")
|
|
|
|
# Jank-hunt: rebuild the Vulkan interposer shim for THIS target ABI before
|
|
# cargo — src/vkshim.rs embeds shim/out/libvulkan.so via include_bytes!, so a
|
|
# stale/wrong-ABI shim would break the app at dlopen time. (Single-target
|
|
# builds only; a universal APK would need one shim per ABI — see vkshim.rs.)
|
|
mkdir -p shim/out
|
|
"$NDK_BIN/${target}26-clang" -shared -fPIC -O2 shim/vkshim.c -o shim/out/libvulkan.so -lunwind
|
|
echo "shim: $(du -h shim/out/libvulkan.so | cut -f1) for ${target}"
|
|
|
|
cargo apk2 build $profile --target "$target" --lib --features perf
|
|
apk=target/$profile_dir/apk/gallery.apk
|
|
echo "APK: $apk ($(du -h "$apk" | cut -f1))"
|
|
|
|
if [ "$arg2" = install ] && command -v adb >/dev/null; then
|
|
"${adb[@]}" install -r "$apk"
|
|
"${adb[@]}" shell am start -n wow.gmp.gallery/android.app.NativeActivity || true
|
|
fi
|