d990627200
3 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
| df4e1bd6f8 |
Baseline-60 control build: stock crates.io Slint, no vendored fixes
shim: 512 for aarch64-linux-android
Using package `gallery` in `/mnt/Backup/prog/android/gallery/Cargo.toml`
Compiling apk resources...
Linking apk resources...
Optimizing apk resources...
Compiling Java sources...
Creating classes.jar from compiled Java sources...
'classes.dex'...
'lib/arm64-v8a/libgallery.so'...
Verifying alignment of /mnt/Backup/prog/android/gallery/target/release/apk/gallery.apk (4)...
49 AndroidManifest.xml (OK - compressed)
1072 resources.arsc (OK)
1153 classes.dex (OK - compressed)
1635 lib/arm64-v8a/libgallery.so (OK - compressed)
Verification succesful
Signing `/mnt/Backup/prog/android/gallery/target/release/apk/gallery.apk` with keystore `/home/gmp/.android/debug.keystore`
APK: target/release/apk/gallery.apk (512) strips the [patch.crates-io] section so
the whole Slint stack (renderer, backend, core, wgpu-hal) resolves to
the stock 1.18.1 sources: no render-ahead, no vsync-locked render, no
app-owned frame driver, no wake-fd trampoline, no vkshim, no perf
instrumentation. All vendor-internal references in the app are now
#[cfg(feature = "perf")]-gated so the stock build is genuinely stock.
Purpose: the CONTROL experiment for the 120Hz zero-jank campaign.
If the grid still janks on the phone at 60 Hz with this build, the
jank lives in the app / GPU pipeline (drain tick, texture uploads,
model updates), not in the vendored fix stack.
60 Hz lock: NOT via JNI — the public API turned out to be
unavailable (Display.setRefreshRate is hidden-API-blocked;
WindowManagerImpl lacks setPreferredDisplayRefreshRate on both
tested devices). Instead the display is locked with the device's
own user setting:
(restored after the test). The baseline60 feature is a pure
marker; the build is otherwise identical to the app code.
Verified on emulator (x86_64, 60 Hz, SwiftShader): launch, grid
render, thumbnail fill, scroll + prefetch all work.
|
|||
| 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). |
|||
| a68c8996c6 |
perf tooling: jank_capture.py (end-to-end capture+report) + stress-test fixes in jank_eval
jank_capture.py: one command owns the whole pipeline so provenance can
never be wrong (the failure mode this replaces: append-only logs spanning
processes, hand byte-offsets, tail-guessing, wrong-build data):
1. record build identity (versionName + apk sha256 + tag)
2. force-stop + DELETE it.log (file can then contain exactly ONE process)
3. cold launch, verify ResumedActivity (before and after)
4. scripted drags ('4,2,4' = 4s drag, 2s settle, 4s drag)
5. pull the complete log
6. report: PROVENANCE + jank_eval verdict + MISS forensics
verdicts: PASS(0) / FAIL(1) / SUSPECT(fitted grid) / INVALID(multi-process)
MISS forensics: groups consecutive misses into episodes, classifies each
(CHAIN-STALL / LOOP-STUCK / GUARD-REJECT / NO-WAKE-ITER) with a +-33 ms
all-thread context window (IT poll/ev/rd/dec/pc/rc/vsw/nvol, VSYNC
latency, PRES, FR, SLOWEV, DECODE, OTHR, INVK).
self-test: synthetic 120Hz log with a 30 ms stuck-poll episode.
jank_eval.py fixes found under emulator stress:
- auto-windows active threshold was circular: 0.75x the refresh rate
meant a JANKY app (low present rate) is never detected as active.
Now 0.4x rate / 250 ms buckets. (Emulated 60 Hz scroll at 30-40 Hz
present was invisible to the old threshold.)
- BUCK 500->250 ms required the density multiplier *2 -> *4 (forgot,
caught by stress: only the fastest spans were windowed).
- fallback period (no VSYNC lines) was a telescoping mean (spans idle
gaps: 391 presents over 23 s fit '59.6 ms'). Now median-of-deltas
snapped to standard rates. A fitted grid cannot prove drops/doubles:
verdict on it excludes them (stalls+lates+spread remain) and the
verdict is marked SUSPECT (exit 3) instead of PASS/FAIL.
- run_file now returns (ok, suspect); small-log early exit fixed.
Stress matrix on emulator (x86_64, 60 Hz SF) — all pass:
capture pipeline x3 (standard 4,2,4 / fast 1,1,1 / slow 6,2,6)
pause/resume mid-drag (teardown -> occluded -> resume burst)
determinism (identical report twice), multi-process -> INVALID,
explicit --windows, --period-ms override, no-VSYNC -> SUSPECT path,
small log -> exit 1, synthetic perfect 120 Hz -> PASS exit 0.
|