//! STG (stage-timestamp) instrumentation for the jank hunt, 2026-10-28. //! //! The app's FR hook (rendering notifier) measures render cost and frame //! gaps; this measures what happens AROUND rendering inside the event //! loop: Choreographer wake latency, timer callbacks (the app's 8 ms //! drain tick runs in `update_timers_and_animations`), and event //! processing. Timestamps are plain atomics — the UI thread only does //! atomic loads/stores here; the APP formats the log line from its own //! tick (never log from the UI thread: logd backpressure). use std::sync::atomic::{AtomicU64, Ordering}; use std::time::Instant; static T0: std::sync::OnceLock = std::sync::OnceLock::new(); pub fn now_us() -> u64 { T0.get_or_init(Instant::now).elapsed().as_micros() as u64 } // Per-iteration stage marks (us since process start); reset at loop top. pub static S_START: AtomicU64 = AtomicU64::new(0); pub static S_FIRST_EV: AtomicU64 = AtomicU64::new(0); pub static S_POLL_END: AtomicU64 = AtomicU64::new(0); pub static S_RENDER_START: AtomicU64 = AtomicU64::new(0); pub static S_RENDER_END: AtomicU64 = AtomicU64::new(0); // Accumulated per-iteration costs. pub static S_TIMER_US: AtomicU64 = AtomicU64::new(0); pub static S_EVENT_US: AtomicU64 = AtomicU64::new(0); pub static S_N_EVENTS: AtomicU64 = AtomicU64::new(0); // Full do_render duration of the PREVIOUS iteration (the AfterRendering // hook fires mid-render, so the current iteration's render can't be read // yet): covers renderer.draw PLUS the wgpu surface tail (flush_and_submit // gr_context.submit + queue.present — the Mali driver calls). pub static S_LAST_RENDER_US: AtomicU64 = AtomicU64::new(0); /// One iteration of the event loop: start, first-event, poll-end, /// render-start, render-end, timer-us, event-us, n-events. pub fn snapshot() -> (u64, u64, u64, u64, u64, u64, u64, u64) { ( S_START.load(Ordering::Relaxed), S_FIRST_EV.load(Ordering::Relaxed), S_POLL_END.load(Ordering::Relaxed), S_RENDER_START.load(Ordering::Relaxed), S_RENDER_END.load(Ordering::Relaxed), S_TIMER_US.load(Ordering::Relaxed), S_EVENT_US.load(Ordering::Relaxed), S_N_EVENTS.load(Ordering::Relaxed), ) }