diff --git a/HANDOFF.md b/HANDOFF.md index d9be850..c203537 100644 --- a/HANDOFF.md +++ b/HANDOFF.md @@ -10,10 +10,10 @@ rediscovered. instant list paint, M2 thumbnail engine (turbojpeg DCT-shrink decode, worker pool, disk cache, cancellation), M3 grid UI (3-column ListView grid, shimmer placeholders, scroll-driven prefetch, memory-bound cell retention) all -verified: 13 unit tests, 5k synthetic engine test, and visually on the -emulator (150 real JPEGs: first paint 0.6 s cold, warm relaunch from disk -cache, 0 ANRs through 16 fast swipes). M2 was also visually verified on a -real Pixel 9 Pro (8,577 photos). Next: M4 viewer. +verified: 13 unit tests, 5k synthetic engine test, on the emulator (150 +real JPEGs: first paint 0.6 s cold, warm relaunch from disk cache, 0 ANRs +through 16 fast swipes), and on a real Pixel 9 Pro (8,577 photos: grid +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 (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#'" +# 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` 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 @@ -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. *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 - drains to 0 when idle. Still to do on a real device: 60 fps scroll check - (gfxinfo) — emulator is software-GL so it's not a valid fps target.* + drains to 0 when idle. Verified on Pixel 9 Pro (release aarch64, 8,577 + 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 math), double-tap zoom; back gesture/button; EXIF orientation correct. - **M5 — permissions**: implement `perm.rs` per §7; launch flow when