iris: FrameReport, a per-frame wall-time report of iris's own render path
dumpsys gfxinfo cannot see a SurfaceView's own GPU-drawn frames at all
(RUST.md's I5 box), so iris needs its own equivalent of Compose's
render-report button before item 3 of the recommendation can be decided
by a number. FrameReport (iris/core/src/render/frame_report.rs) records
each frame's wall time -- from render()'s redraw start to after
queue.submit + present() -- into a fixed 4096-entry ring, and reports
total frames, janky % (>16.7ms, gfxinfo's own budget), P50/P90/P99 and
the worst. Wired into AndroidUiState and android/view.rs's render(), and
exposed as two named controls ("Frame report", "Reset frame report") on
iris-android-app's transcript screen, logged under the crate's fixed tag
so a script can grep "iris frame report" the way transcript-bench.sh
greps "ai-app render report".
6 new unit tests for the ring/percentile math. cargo fmt/clippy/test
--workspace clean; cargo ndk (iris, transcript-ui, and
iris-android-app --features transcript-screen) all clean.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
1 parent
d17040b601
commit
7ae53ad797
5 files changed
+335
-3
No files matched your search
@@ -44,6 +44,12 @@ mod pinned {
|
||||
|
||||
pub struct TranscriptClient {
|
||||
ui_state: AndroidUiState,
|
||||
/// The screen's own content -- everything under the fixed
|
||||
/// [`frame_report_controls`] bar, which is built once (`new`, below)
|
||||
/// and never touched by `show_message`/`rebuild_transcript`'s own
|
||||
/// `set` calls the way `desktop-app`'s `transcript_ptr` isn't touched
|
||||
/// by rebuilding the session list beside it.
|
||||
content: WeakWidget<WidgetPtr>,
|
||||
screen: Option<transcript_ui::TranscriptScreen>,
|
||||
/// The folded transcript as of the last rebuild -- kept here (not
|
||||
/// re-derived) for the same reason `desktop-app`'s `Client::items`
|
||||
@@ -94,13 +100,76 @@ fn placeholder<Rsc: HasEvents>(rsc: &mut Rsc, message: &str) -> StrongWidget {
|
||||
.any()
|
||||
}
|
||||
|
||||
/// The two named controls RUST.md's I5 box ("Measurements taken" (b))
|
||||
/// drives by name over `ui-trace`, e.g. `ui-trace record --do "tap 'Frame
|
||||
/// report'"`. `dumpsys gfxinfo` cannot see this screen's own GPU-drawn
|
||||
/// frames at all -- this is the screen's own equivalent of the Compose
|
||||
/// app's "Copy render timings" control, logged rather than clipboarded
|
||||
/// (no clipboard wiring exists here) under this crate's own fixed
|
||||
/// `android_logger` tag (`iris-android-app`, `lib.rs`'s `JNI_OnLoad`),
|
||||
/// grep-able on the fixed string `"iris frame report"` the way
|
||||
/// `transcript-bench.sh` greps `"ai-app render report"`.
|
||||
fn frame_report_controls(rsc: &mut AndroidRsc<TranscriptClient>) -> WeakWidget {
|
||||
type Rsc = AndroidRsc<TranscriptClient>;
|
||||
let report_rect = rect(Color::rgb(50, 50, 60))
|
||||
.on(
|
||||
CursorSense::click(),
|
||||
|ctx: EventIdCtx<'_, Rsc, _, _>, _rsc: &mut Rsc| match ctx
|
||||
.state
|
||||
.android_state()
|
||||
.frame_report
|
||||
.report()
|
||||
{
|
||||
Some(stats) => log::info!("iris frame report: {stats}"),
|
||||
None => log::info!(
|
||||
"iris frame report: no frames recorded -- scroll first, then press this"
|
||||
),
|
||||
},
|
||||
)
|
||||
.label("Frame report");
|
||||
let report = (
|
||||
report_rect,
|
||||
wtext("Frame report").size(18).text_align(Align::CENTER),
|
||||
)
|
||||
.stack()
|
||||
.pad(8)
|
||||
.add(rsc);
|
||||
|
||||
let reset_rect = rect(Color::rgb(70, 40, 40))
|
||||
.on(
|
||||
CursorSense::click(),
|
||||
|ctx: EventIdCtx<'_, Rsc, _, _>, _rsc: &mut Rsc| {
|
||||
ctx.state.android_state_mut().frame_report.reset();
|
||||
log::info!("iris frame report: reset");
|
||||
},
|
||||
)
|
||||
.label("Reset frame report");
|
||||
let reset = (
|
||||
reset_rect,
|
||||
wtext("Reset").size(18).text_align(Align::CENTER),
|
||||
)
|
||||
.stack()
|
||||
.pad(8)
|
||||
.add(rsc);
|
||||
|
||||
(report, reset).span(Dir::RIGHT).height(56).add(rsc)
|
||||
}
|
||||
|
||||
impl AndroidAppState for TranscriptClient {
|
||||
fn new(mut ui_state: AndroidUiState, rsc: &mut AndroidRsc<Self>) -> Self {
|
||||
let content = WidgetPtr::new().add(rsc);
|
||||
let loading = placeholder(rsc, "Loading sessions...");
|
||||
ui_state.set_root(loading);
|
||||
content(rsc).set(loading);
|
||||
|
||||
let tree = (frame_report_controls(rsc), content.height(rest(1)))
|
||||
.span(Dir::DOWN)
|
||||
.add_strong(rsc)
|
||||
.any();
|
||||
ui_state.set_root(tree);
|
||||
|
||||
let mut client = Self {
|
||||
ui_state,
|
||||
content,
|
||||
screen: None,
|
||||
items: Vec::new(),
|
||||
session_id: None,
|
||||
@@ -120,7 +189,7 @@ impl AndroidAppState for TranscriptClient {
|
||||
impl TranscriptClient {
|
||||
fn show_message(&mut self, rsc: &mut AndroidRsc<Self>, message: &str) {
|
||||
let widget = placeholder(rsc, message);
|
||||
self.android_state_mut().set_root(widget);
|
||||
(self.content)(rsc).set(widget);
|
||||
self.screen = None;
|
||||
}
|
||||
|
||||
@@ -286,7 +355,7 @@ impl TranscriptClient {
|
||||
});
|
||||
}
|
||||
|
||||
self.android_state_mut().set_root(tree);
|
||||
(self.content)(rsc).set(tree);
|
||||
self.screen = Some(screen);
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user