Three root causes, all fixed and verified on the Pixel 9 Pro: 1. Present phase (backend + renderer): VSYNC_LOCKED_RENDER defers the render to the vsync wake; RENDER_AHEAD presents the previous frame at the start of render() (phase = wake + ~1.1 ms) and holds the new frame one slot. Crash fix: a held wgpu frame must be PRESENTED, never merely dropped, before the next acquire (AlreadyAcquired panic at launch otherwise); discard_pending_frame presents too. 2. Full-res prefetch churn (src/lib.rs): the 8-slot full cache cycled through a ~20-cell viewport forever (137 decodes/20 s) and each eviction free()'d a 50 MB Vec on the UI thread (20-52 ms CPU spikes, ~7/s = the residual SLOWEV NonMain jank). Fix: 60 s tombstone cooldown on evicted paths (idle prefetch skips, urgent tap bypasses) + off-thread reaper for the freed blobs (insert: 21 ms -> 4 us). 3. (earlier) Skia final flush + Mailbox present mode (vendor, M3). Instrumentation (all behind --features perf): OTHR/INVK/INVK_TAG/FLCK lines for cross-thread closure timing and push-site tagging. Warm measured swipe, 8580-file grid: cadence median 8.33 ms (exact 120 Hz), p99 15.5 ms, 5/552 gaps >16.7 ms (0.9%, max 28.3 ms, zero >33 ms); PRES phase median ~994 us, zero late frames. Docs updated (docs/BUGS/scroll-jank-120hz.md, full scorecard + line reference). Also: vendored i-slint-core + wgpu-hal (vkshim interposition patch), vkshim profiling tooling, backend made a non-optional dep, debuggable flag for simpleperf (REMOVE before shipping).
69 lines
2.5 KiB
Rust
69 lines
2.5 KiB
Rust
// Jank-hunt: per-frame draw-op census. Answers "is the scene we hand to
|
|
// Skia/Mali unnecessarily big or expensive?" A scrolling 3-column grid of
|
|
// thumbnails should be a few dozen cheap ops per frame; if the counts (or
|
|
// the total drawn area, or the image upscale ratio) are large or growing,
|
|
// that explains the Mali GPU backpressure. See docs/BUGS/scroll-jank-120hz.md.
|
|
use std::sync::atomic::{AtomicU64, Ordering};
|
|
|
|
pub static SCN_RECT: AtomicU64 = AtomicU64::new(0);
|
|
pub static SCN_RECT_AREA: AtomicU64 = AtomicU64::new(0);
|
|
pub static SCN_IMG: AtomicU64 = AtomicU64::new(0);
|
|
pub static SCN_IMG_AREA: AtomicU64 = AtomicU64::new(0);
|
|
/// Max (drawn area / source area) x 100, i.e. a value of 200 = drawn at 2x
|
|
/// the source area (a 1.41x linear upscale). High values = the GPU is
|
|
/// upscaling small textures a lot (or the "source" is a tiny fallback).
|
|
pub static SCN_IMG_UP: AtomicU64 = AtomicU64::new(0);
|
|
pub static SCN_TEXT: AtomicU64 = AtomicU64::new(0);
|
|
pub static SCN_BORDER: AtomicU64 = AtomicU64::new(0);
|
|
pub static SCN_SHADOW: AtomicU64 = AtomicU64::new(0);
|
|
pub static SCN_CLIP: AtomicU64 = AtomicU64::new(0);
|
|
|
|
pub fn reset() {
|
|
for s in [
|
|
&SCN_RECT,
|
|
&SCN_RECT_AREA,
|
|
&SCN_IMG,
|
|
&SCN_IMG_AREA,
|
|
&SCN_IMG_UP,
|
|
&SCN_TEXT,
|
|
&SCN_BORDER,
|
|
&SCN_SHADOW,
|
|
&SCN_CLIP,
|
|
] {
|
|
s.store(0, Ordering::Relaxed);
|
|
}
|
|
}
|
|
|
|
/// Add a value to an atomic (relaxed), used by the item renderer.
|
|
#[inline]
|
|
pub fn add(slot: &AtomicU64, v: u64) {
|
|
slot.fetch_add(v, Ordering::Relaxed);
|
|
}
|
|
|
|
#[inline]
|
|
pub fn inc(slot: &AtomicU64) {
|
|
slot.fetch_add(1, Ordering::Relaxed);
|
|
}
|
|
|
|
#[inline]
|
|
pub fn max(slot: &AtomicU64, v: u64) {
|
|
let _ = slot.fetch_max(v, Ordering::Relaxed);
|
|
}
|
|
|
|
pub fn report(frame: u64) {
|
|
let rect = SCN_RECT.load(Ordering::Relaxed);
|
|
let img = SCN_IMG.load(Ordering::Relaxed);
|
|
let text = SCN_TEXT.load(Ordering::Relaxed);
|
|
let border = SCN_BORDER.load(Ordering::Relaxed);
|
|
let shadow = SCN_SHADOW.load(Ordering::Relaxed);
|
|
let clip = SCN_CLIP.load(Ordering::Relaxed);
|
|
let rect_area = SCN_RECT_AREA.load(Ordering::Relaxed);
|
|
let img_area = SCN_IMG_AREA.load(Ordering::Relaxed);
|
|
let img_up = SCN_IMG_UP.load(Ordering::Relaxed);
|
|
let total = rect + img + text + border + shadow;
|
|
log::info!(
|
|
"SCN f={} rect={} rarea={} img={} iarea={} iup={} text={} border={} shadow={} clip={} total={}ops",
|
|
frame, rect, rect_area, img, img_area, img_up, text, border, shadow, clip, total
|
|
);
|
|
}
|