Go to file
Greg Pomerantz 7c2d0f354e 120Hz: early waker thread + render freshness guard (vsync-edge pacing)
The Choreographer vsync callback reaches the UI thread only 2.5-6 ms
AFTER the vsync edge (measured on the phone). With a 2.5-5 ms Skia
render plus present, that left no phase margin inside the 8.3 ms slot:
51% of frames landed one slot late (judder) and ~4% two slots late
(hitches), on every build tested so far.

Attempted fixes, all measured and retired (see commit history):
- render-ahead hold (present previous frame at next render start):
  the held present lands after the NEXT wake's 3.2-6 ms Choreographer
  latency -> 52% one-slot late.
- present-at-end + present GATE (sleep on the UI thread to quantize
  the present to the vsync grid): the gate sleep (2.5-4 ms) added to
  the slot budget (wake 5.3 + core 3 + gate 3 + present 1 = 12.3 ms >
  8.3 ms) -> cadence collapsed to 60 Hz.

Working fix (this commit):
- vsync.rs: an 'early waker' thread learns the vsync grid from the
  callback's frame_time_nanos (vkprobe::note_vsync_edge) and wakes the
  main looper ~0.8 ms BEFORE each edge while animating. Render start
  moves from edge+2.5..6 to edge-0.8..+0.5, so the present lands at a
  natural ~edge+3..5 ms phase with no UI-thread sleep.
- lib.rs: a render freshness guard (min gap = period - 1.5 ms) makes
  the FIRST wake of each slot (early or the late Choreographer
  fallback) render and rejects the second (double render = 60 Hz
  collapse). Threshold sits between late-after-early (5.8 ms, reject)
  and early-after-late (7.5 ms, accept).
- lib.rs: locked renders no longer consume pending_redraw (it carried
  the motion invalidations; consuming it starved the follow-up
  unlocked iteration -> 60 Hz collapse).
- stg.rs: IT lines now log the render decision (locked/saw_wake/
  pr_before/do_render); vkprobe logs gate sleep (GATE_US, now 0).

Measured (phone, 8580-file grid, slow 4 s drags):
  in-burst cadence: 8.29-8.44 ms median, p90 8.6-10.2 ms
  (was 8.36 median / 10.2 p90 with 3.7-20% two-slot gaps)
  in-burst >16.7 ms gaps: 0-2 per burst (was 3-13)
  present-to-present: 27% exact one slot, 33% one-slot-late steady
  state (imvisible), 3% f-jumps (Skia core 2.5-5 ms variance).
Residual judder lever: Skia core cost variance (next step).

Test hygiene reminders (HANDOFF 8.10): verify ResumedActivity, keep
the task clean (sz=1), record the perf-log byte offset before each
run (the file accumulates across process launches).
2026-10-30 11:06:58 -04:00
docs/BUGS Fix 120Hz scroll jank end-to-end: render-ahead present + full-cache eviction off the UI thread 2026-10-30 02:49:23 -04:00
scripts Fix 120Hz scroll jank end-to-end: render-ahead present + full-cache eviction off the UI thread 2026-10-30 02:49:23 -04:00
shim Fix 120Hz scroll jank end-to-end: render-ahead present + full-cache eviction off the UI thread 2026-10-30 02:49:23 -04:00
src Fix thumbnail pop-in: hold-queue for paint-cap overflow + append-only scan streaming + window-liveness watchdog 2026-10-30 09:04:01 -04:00
vendor 120Hz: early waker thread + render freshness guard (vsync-edge pacing) 2026-10-30 11:06:58 -04:00
.gitignore Fix 120Hz scroll jank end-to-end: render-ahead present + full-cache eviction off the UI thread 2026-10-30 02:49:23 -04:00
build.rs M0: Slint gallery scaffold + development hand-off 2026-10-27 12:22:59 -04:00
Cargo.lock Fix 120Hz scroll jank end-to-end: render-ahead present + full-cache eviction off the UI thread 2026-10-30 02:49:23 -04:00
Cargo.toml Fix thumbnail pop-in: hold-queue for paint-cap overflow + append-only scan streaming + window-liveness watchdog 2026-10-30 09:04:01 -04:00
HANDOFF.md HANDOFF §8.10: pop-in root causes (paint-cap drop, scan storm, black window) + test hygiene 2026-10-30 09:05:08 -04:00
LICENSE Initial commit 2026-10-27 12:13:22 -04:00
README.md M1: metadata scan + sqlite DB + placeholder list 2026-10-27 13:28:33 -04:00

gallery

A high-performance image gallery for Android (primary), Linux and macOS — Rust + Slint 1.18.

Status: M1 (metadata scan + DB) done. See HANDOFF.md — the development hand-off with the full plan: architecture, thumbnail pipeline, milestones M1–M7, Slint gotchas, permission flow, and performance targets. Next: M2 (thumbnail engine).

Build

./scripts/build.sh debug     # debug x86_64 APK + install on emulator
./scripts/build.sh           # release x86_64 (emulator)
./scripts/build.sh aarch64   # release aarch64 (device)
cargo run                    # desktop (Linux needs libfontconfig1 + pkg-config)

Requires: Rust (rustup), cargo-apk2, Android SDK + NDK 27 (see HANDOFF.md §3 for exact paths/env).

License

GPL-3.0 (see LICENSE). Built on Slint, used under the Slint royalty-free license for proprietary apps (see HANDOFF.md §10.4).