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). |
||
|---|---|---|
| docs/BUGS | ||
| scripts | ||
| shim | ||
| src | ||
| vendor | ||
| .gitignore | ||
| build.rs | ||
| Cargo.lock | ||
| Cargo.toml | ||
| HANDOFF.md | ||
| LICENSE | ||
| README.md | ||
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).