13d1aa5738
The long-deferred Android display stage (design/stats-unification.md; plan 4.1 of design/client-parity-and-network-resilience.md): AMediaCodec_setOnFrameRenderedCallback (API 26, under the minSdk-28 floor ⇒ hard-linked via ndk-sys) reports SurfaceFlinger's per-frame render timestamp, giving the HUD the spec's `display` = decoded→displayed term and the directly-measured capture→displayed end-to-end headline on both decode loops. Falls back per spec to the v1 capture→decoded endpoint on any window without render callbacks (the platform may drop them under load), and to it permanently if registration is refused. - The render timestamp arrives on CLOCK_MONOTONIC; it's re-based onto CLOCK_REALTIME against monotonic-now at callback time, which also cancels the (batchable) callback delivery lag. - The `ndk` crate exposes neither the callback nor the codec pointer needed to bind it raw, so the workspace pins `ndk` 0.9.0 to a vendored copy (clients/android/native/ vendor/ndk) whose ONLY change makes MediaCodec::as_ptr public — the "as_ptr patch". Workspace-excluded so host builds never compile it; drop when upstream exposes either. - nativeVideoStats grows to 26 doubles (22–25: dispValid, displayP50, e2eDispP50/P95; 0–21 unchanged for older readers); StatsOverlay moves headline endpoint + equation together so the equation always tiles the headline interval. Verified: host cargo check/test/clippy, aarch64-linux-android check/clippy, Kotlin app+kit+tests compile, roborazzi HUD render shows the full 4-term equation. Device verification rides plan 4.2's phone A/B. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
93 lines
2.1 KiB
Rust
93 lines
2.1 KiB
Rust
//! Bindings for the NDK tracing API.
|
|
//!
|
|
//! See also [the NDK docs](https://developer.android.com/ndk/reference/group/tracing)
|
|
#![cfg(feature = "api-level-23")]
|
|
use std::ffi::{CString, NulError};
|
|
use std::marker::PhantomData;
|
|
|
|
pub fn is_trace_enabled() -> bool {
|
|
unsafe { ffi::ATrace_isEnabled() }
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct Section {
|
|
// Section is !Sync and !Send
|
|
_pd: PhantomData<*mut ()>,
|
|
}
|
|
|
|
impl Section {
|
|
pub fn new(name: &str) -> Result<Self, NulError> {
|
|
let section_name = CString::new(name)?;
|
|
unsafe { ffi::ATrace_beginSection(section_name.as_ptr()) };
|
|
|
|
Ok(Self { _pd: PhantomData })
|
|
}
|
|
|
|
pub fn end(self) {
|
|
drop(self)
|
|
}
|
|
}
|
|
|
|
impl Drop for Section {
|
|
fn drop(&mut self) {
|
|
unsafe { ffi::ATrace_endSection() };
|
|
}
|
|
}
|
|
|
|
/// Unique identifier for distinguishing simultaneous events
|
|
#[derive(Debug)]
|
|
#[cfg(feature = "api-level-29")]
|
|
pub struct Cookie(pub i32);
|
|
|
|
#[derive(Debug)]
|
|
#[cfg(feature = "api-level-29")]
|
|
pub struct AsyncSection {
|
|
section_name: CString,
|
|
cookie: Cookie,
|
|
// AsyncSection is !Sync
|
|
_pd: PhantomData<&'static ()>,
|
|
}
|
|
|
|
#[cfg(feature = "api-level-29")]
|
|
impl AsyncSection {
|
|
pub fn new(name: &str, cookie: Cookie) -> Result<Self, NulError> {
|
|
let section_name = CString::new(name)?;
|
|
unsafe { ffi::ATrace_beginAsyncSection(section_name.as_ptr(), cookie.0) };
|
|
|
|
Ok(Self {
|
|
section_name,
|
|
cookie,
|
|
_pd: PhantomData,
|
|
})
|
|
}
|
|
|
|
pub fn end(self) {
|
|
drop(self)
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "api-level-29")]
|
|
impl Drop for AsyncSection {
|
|
fn drop(&mut self) {
|
|
unsafe { ffi::ATrace_endAsyncSection(self.section_name.as_ptr(), self.cookie.0) };
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "api-level-29")]
|
|
#[derive(Debug)]
|
|
pub struct Counter {
|
|
name: CString,
|
|
}
|
|
|
|
#[cfg(feature = "api-level-29")]
|
|
impl Counter {
|
|
pub fn new(name: &str) -> Result<Self, NulError> {
|
|
let name = CString::new(name)?;
|
|
Ok(Self { name })
|
|
}
|
|
|
|
pub fn set_value(&self, value: i64) {
|
|
unsafe { ffi::ATrace_setCounter(self.name.as_ptr(), value) }
|
|
}
|
|
}
|