#!/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)"