gallery/vendor/i-slint-backend-android-activity-1.18.1
Greg Pomerantz 0a50f9b832 120Hz: fix the ALooper wake-fd race (root cause of slot drops) + phase-slip-robust referee
ROOT CAUSE of the 8-12% slot drops (uniform dec=1/0/1/0: locked, no wake
seen, dirty, no render): AOSP Looper::pollInner (system/core/libutils/
Looper.cpp) drains the wake fd in the SAME epoll batch (up to 16 fds) as
any ready input fd, and pollOnce returns the input's ident — the POLL_WAKE
result is a local that gets dropped. ALooper_wake's byte is consumed
WITHOUT PollEvent::Wake ever reaching Slint, unrecoverable. With motion
events arriving 1-4 ms after the edge (always for a real 120-240 Hz
finger; ~1 in 5 frames for 20 Hz adb input) the wake is lost and the
locked decision (saw_wake && slot-guard) drops the dirty frame for that
slot.

Fix (vendor backend): a dedicated eventfd registered with the MAIN looper
under ALOOPER_POLL_CALLBACK. The Looper invokes the C trampoline INSIDE
pollInner (before pollOnce returns, regardless of which ident is handed
back), so delivery can never be swallowed by a co-batched input response.
- frame_callback: note the edge, write the eventfd, then ALooper_wake
  (secondary). The fd is leaked: it belongs to the main looper for the
  process lifetime.
- trampoline: DRAINS the eventfd (level-triggered — without the drain the
  counter stays >0, the fd stays readable and the loop busy-spins on
  POLL_CALLBACK at ~10 kHz: 1812 spin iterations measured) and publishes
  the edge into delivered_edge.
- main loop: saw_wake |= (delivered_edge newer than last seen).
- DIRTY BACKSTOP: the 6 ms poll cap was never a backstop — timeout
  iterations have saw_wake=false so the locked decision never rendered
  (the 20-40 ms silences). Now: dirty && last render >= 2 slots old
  renders regardless of wake state.

Measured on the phone (Pixel 9 Pro, 120 Hz, sltest, identical capture):
  buildC (before): 127 drops + 12 stalls in 1142 frames
  buildE (after):    8 drops +  0 stalls in 1266 frames
Phase (p10-p90): 0.72-1.00 ms in all windows. Remaining 8/1266 (0.63%)
are slip-and-recover pairs where the UI thread is DESCHEDULED 6-8 ms
while sleeping in epoll (wakes arrive on time, cb-lat 0.5 ms) — EAS
scheduling variance, no app-side contention in sltest.

Referee fixes found while judging this data (tools/jank_eval.py):
- NEAREST-OBSERVED-EDGE slot grid: the 120 Hz panel clock phase-slips
  after idle blanking (re-locks at arbitrary phase), invalidating any
  grid anchored at ftns[0]. An entire drag window read late=230/
  dropped=24 while per-cycle tracing showed presents at edge+2.0 ms.
  slot_of_ftn assigns each present to the nearest observed hardware
  edge in a time-continuous slot domain (slip-immune: a constant delta
  shifts all slots, differences preserved; stall-accurate: ftn times
  are CLOCK_MONOTONIC).
- tools/jank_capture.py: capture is INVALID unless the screen is Awake
  AND the keyguard is down (a run on a sleeping phone silently injects
  no input: 49 IT lines, 1 present).
2026-10-30 16:08:48 -04:00
..
java Jank-hunt perf feature: FR/STG/DECODE instrumentation, off by default 2026-10-29 08:11:51 -04:00
LICENSES Jank-hunt perf feature: FR/STG/DECODE instrumentation, off by default 2026-10-29 08:11:51 -04:00
.cargo_vcs_info.json Jank-hunt perf feature: FR/STG/DECODE instrumentation, off by default 2026-10-29 08:11:51 -04:00
.cargo-ok Jank-hunt perf feature: FR/STG/DECODE instrumentation, off by default 2026-10-29 08:11:51 -04:00
androidwindowadapter.rs 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 Jank-hunt perf feature: FR/STG/DECODE instrumentation, off by default 2026-10-29 08:11:51 -04:00
Cargo.lock Jank-hunt perf feature: FR/STG/DECODE instrumentation, off by default 2026-10-29 08:11:51 -04:00
Cargo.toml 120Hz: fix the ALooper wake-fd race (root cause of slot drops) + phase-slip-robust referee 2026-10-30 16:08:48 -04:00
Cargo.toml.orig Jank-hunt perf feature: FR/STG/DECODE instrumentation, off by default 2026-10-29 08:11:51 -04:00
javahelper.rs Jank-hunt perf feature: FR/STG/DECODE instrumentation, off by default 2026-10-29 08:11:51 -04:00
lib.rs 120Hz: fix the ALooper wake-fd race (root cause of slot drops) + phase-slip-robust referee 2026-10-30 16:08:48 -04:00
README.md Jank-hunt perf feature: FR/STG/DECODE instrumentation, off by default 2026-10-29 08:11:51 -04:00
stg.rs 120Hz: Choreographer chain self-sustains on (animating||dirty); EW gates the wake; MISS v2 forensics; ctxsw discriminator + present CPU split 2026-10-30 14:51:06 -04:00
vsync.rs 120Hz: fix the ALooper wake-fd race (root cause of slot drops) + phase-slip-robust referee 2026-10-30 16:08:48 -04:00

NOTE: This library is an internal crate of the Slint project.

WARNING: This crate does not follow the semver convention for versioning and can only be used with version = "=x.y.z" in Cargo.toml.

Slint Android Activity Backend

This crate implements the Android backend/platform for Slint.

It uses the android-activity crate to initialize the app and provide events handling.

It can be used by using functions from the slint::android module