diff --git a/scripts/jank_ab.sh b/scripts/jank_ab.sh new file mode 100755 index 0000000..fb0ffa2 --- /dev/null +++ b/scripts/jank_ab.sh @@ -0,0 +1,85 @@ +#!/usr/bin/env bash +# One-shot jank A/B for the phone (usage: ADB_SERIAL=10.0.0.5:5555 ./scripts/jank_ab.sh) +# +# What it does: +# 1. Installs the release APK (build with ./scripts/build.sh aarch64 first). +# 2. Verifies thread priorities (thumb-* at nice 2, slint vsync at 0). +# 3. Runs TWO 30-second scroll windows — real textures, then grey 1x1 +# images — with logcat capture + cadence analysis in between. +# THE USER MUST SCROLL WITH A FINGER during each window +# (moderate drag, no fling — the reported repro). +# 4. Prints a comparison: % of scroll events on the 8.3 ms grid, +# stall count (gap > 16 ms), worst gaps. +# +# Interpretation: +# real janky + grey smooth -> texture/GPU path is the culprit +# both janky -> base render/present/event path +# both smooth -> the priority fix worked; ship it + +set -u +E="adb ${ADB_SERIAL:+-s $ADB_SERIAL}" +APK=target/release/apk/gallery.apk +PKG=wow.gmp.gallery +CMD=/sdcard/Download/.gallery_cmd +GREY=/sdcard/Download/.gallery_grey + +[ -f "$APK" ] || { echo "build first: ./scripts/build.sh aarch64"; exit 1; } + +scroll_window() { # $1 = label, $2 = tag, $3 = seconds + local out="/tmp/jank_$2.txt" + $E logcat -c + echo "### $1 — scroll now (moderate drag, no fling)..." + $E logcat 2>/dev/null | grep -m 2000 "SCRDBG\|TICK \|APLY \|raised\|not found\|panic" > "$out" & + local LP=$! + sleep "$3" + kill "$LP" 2>/dev/null + python3 - "$out" << 'EOF' +import re, sys +ev = [] +for line in open(sys.argv[1], errors="ignore"): + m = re.search(r"SCRDBG Instant \{ tv_sec: (\d+), tv_nsec: (\d+) \}", line) + if m: ev.append(int(m.group(1))*1000 + int(m.group(2))/1e6) +if len(ev) < 10: + print(f" {sys.argv[1]}: only {len(ev)} scroll events — not enough data"); sys.exit() +gaps = [ev[i+1]-ev[i] for i in range(len(ev)-1)] +on_grid = sum(1 for g in gaps if 7.5 <= g <= 10.5) +stalls = [g for g in gaps if g > 16] +print(f" events={len(ev)} on 8.3ms grid={100*on_grid/len(gaps):.0f}% " + f"stalls(>16ms)={len(stalls)} p95={sorted(gaps)[int(len(gaps)*0.95)]:.1f}ms " + f"worst={max(gaps):.1f}ms") +EOF +} + +check_prios() { + local pid; pid=$($E shell pidof $PKG) + echo "### thread priorities (pid $pid)" + $E shell "for t in /proc/$pid/task/*; do c=\$(cat \$t/comm 2>/dev/null); case \$c in slint*|thumb*) n=\$(awk -F')' '{print \$2}' \$t/stat | awk '{print \$17}'); echo \" \$c nice=\$n\";; esac; done" +} + +install_and_start() { # $1 = "grey" to enable the grey knob before launch + $E install -r "$APK" >/dev/null + if [ "$1" = grey ]; then $E shell "touch $GREY"; else $E shell "rm -f $GREY"; fi + $E shell am force-stop $PKG + $E shell am start -n $PKG/android.app.NativeActivity >/dev/null + sleep 8 +} + +echo "=== installing $APK ===" +install_and_start +check_prios +echo +echo "=== window 1: REAL textures (30 s) ===" +scroll_window "real" real 30 +echo +echo "=== restarting with grey 1x1 images ===" +install_and_start grey +check_prios +echo +echo "=== window 2: GREY images (30 s) ===" +$E logcat -d 2>/dev/null | grep "grey images ON" || echo " WARNING: grey knob not active!" +scroll_window "grey" grey 30 + +$E shell "rm -f $GREY $CMD" +echo +echo "done. Compare the two summaries above." +echo " (raw captures: /tmp/jank_real.txt /tmp/jank_grey.txt)"