HANDOFF: M3 real-device scroll-FPS result + SurfaceFlinger latency method

Pixel 9 Pro (release aarch64, 8,577 photos): sustained 60 fps, 0 display
hitches while scrolling, cold and warm cache. dumpsys gfxinfo reports 0
frames for Slint's NativeActivity window on Android 15 — document the
dumpsys SurfaceFlinger --latency <layer> method instead.
This commit is contained in:
Greg Pomerantz 2026-10-27 18:45:46 -04:00
parent 59787f1d3f
commit c9a6066ace

View File

@ -10,10 +10,10 @@ rediscovered.
instant list paint, M2 thumbnail engine (turbojpeg DCT-shrink decode, worker instant list paint, M2 thumbnail engine (turbojpeg DCT-shrink decode, worker
pool, disk cache, cancellation), M3 grid UI (3-column ListView grid, shimmer pool, disk cache, cancellation), M3 grid UI (3-column ListView grid, shimmer
placeholders, scroll-driven prefetch, memory-bound cell retention) all placeholders, scroll-driven prefetch, memory-bound cell retention) all
verified: 13 unit tests, 5k synthetic engine test, and visually on the verified: 13 unit tests, 5k synthetic engine test, on the emulator (150
emulator (150 real JPEGs: first paint 0.6 s cold, warm relaunch from disk real JPEGs: first paint 0.6 s cold, warm relaunch from disk cache, 0 ANRs
cache, 0 ANRs through 16 fast swipes). M2 was also visually verified on a through 16 fast swipes), and on a real Pixel 9 Pro (8,577 photos: grid
real Pixel 9 Pro (8,577 photos). Next: M4 viewer. renders, scroll = 60 fps with 0 hitches). Next: M4 viewer.
--- ---
@ -289,6 +289,31 @@ on a real device with the aarch64 build):
- Cold start (emulator, release): compare against the eval baseline - Cold start (emulator, release): compare against the eval baseline
(Slint sample ≈ 1–2 s; gallery must not regress > 0.5 s). (Slint sample ≈ 1–2 s; gallery must not regress > 0.5 s).
**Scroll-FPS measurement (real device, verified 2026-10-27):**
`dumpsys gfxinfo wow.gmp.gallery` reports **0 frames** for Slint — the
NativeActivity window renders via its own EGL surface and is not tracked by
gfxinfo on Android 15 (works for normal View apps). Use SurfaceFlinger's
per-layer latency instead:
```bash
# find the layer name:
adb shell dumpsys SurfaceFlinger | grep "VRI-wow.gmp.gallery"
# while scrolling (swipes via `input swipe`): capture 1 s of frame history
adb shell "dumpsys SurfaceFlinger --latency 'VRI-wow.gmp.gallery/android.app.NativeActivity#<id>'"
# each line: latch_time display_time … — display_time is the vsync grid.
# fps = 1e6 / avg(display gap); jank = display gaps > ~25 ms; the latch
# column shows frame PRODUCTION timing (p95 ~17 ms = on time at 60 Hz).
```
Result (Pixel 9 Pro, release aarch64, 8,577 photos, 500 px thumbs):
sustained **60.0 fps, 0 display hitches** while scrolling, both cold cache
(production p95 21–25 ms) and warm cache (production avg 16.6 ms). The
display presents at a 60 Hz vsync grid (Slint requests Default framerate;
120 Hz is a later polish item, not an M3 acceptance criterion).
Note: the shimmer `animation-tick()` keeps frames flowing even at rest,
so latency history is continuous — filter by swiping if you need
scroll-only frames.
Test data (M2): generate a synthetic set on the device — a small Rust `bin` Test data (M2): generate a synthetic set on the device — a small Rust `bin`
or Python script that writes N=5,000 JPEGs of varied sizes/orientations into or Python script that writes N=5,000 JPEGs of varied sizes/orientations into
`/storage/emulated/0/Pictures/gallery-test/` (include a few HEICs if `/storage/emulated/0/Pictures/gallery-test/` (include a few HEICs if
@ -384,8 +409,10 @@ pool threads), and `logcat` pool timing logs behind a `debug_timing` flag.
(no model mutation), all mutation is in the 10 Hz drain timer. (no model mutation), all mutation is in the 10 Hz drain timer.
*Verified (emulator, 150 real JPEGs + 5k stubs): first paint 0.6 s cold; *Verified (emulator, 150 real JPEGs + 5k stubs): first paint 0.6 s cold;
warm relaunch from disk cache; 0 ANRs through 16 fast swipes; backlog warm relaunch from disk cache; 0 ANRs through 16 fast swipes; backlog
drains to 0 when idle. Still to do on a real device: 60 fps scroll check drains to 0 when idle. Verified on Pixel 9 Pro (release aarch64, 8,577
(gfxinfo) — emulator is software-GL so it's not a valid fps target.* photos): first screen ~0.7 s cold at 500 px; scroll = sustained 60 fps
with 0 display hitches (cold and warm cache) — see §8 for the
measurement method (gfxinfo does NOT track Slint's window).*
- **M4 — viewer**: tap cell → full decode + overlay; pinch/pan (manual - **M4 — viewer**: tap cell → full decode + overlay; pinch/pan (manual
math), double-tap zoom; back gesture/button; EXIF orientation correct. math), double-tap zoom; back gesture/button; EXIF orientation correct.
- **M5 — permissions**: implement `perm.rs` per §7; launch flow when - **M5 — permissions**: implement `perm.rs` per §7; launch flow when