Compare commits
3
Commits
b87f5a597e
...
3eb0e033d5
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3eb0e033d5 | ||
|
|
b8ea723718 | ||
|
|
7485d78d50 |
No files matched your search
+220
-12
@@ -11,7 +11,7 @@
|
||||
//!
|
||||
//! Two consumers, both reading the same ring rather than each keeping
|
||||
//! their own: the bench app's `Copy report`/`Diagnostics` (which reads
|
||||
//! [`LogRing::to_text`] and [`LogRing::summary`]) and whatever hands the
|
||||
//! [`LogRing::tail_text`] and [`LogRing::summary`]) and whatever hands the
|
||||
//! log out of the process -- on Android, the `DevLogProvider` Dev Updater
|
||||
//! queries, which reads [`LogRing::since`] and [`LogRing::newest_seq`].
|
||||
//! That is why reading does not consume: a line already handed over must
|
||||
@@ -32,6 +32,15 @@ use std::time::{SystemTime, UNIX_EPOCH};
|
||||
pub const DEFAULT_MAX_LINES: usize = 2000;
|
||||
pub const DEFAULT_MAX_BYTES: usize = 256 * 1024;
|
||||
|
||||
/// How many of the ring's newest lines [`LogRing::tail_text`] includes.
|
||||
/// Sized for a phone's share sheet rather than for the ring itself: 150
|
||||
/// lines of `HH:MM:SS.mmm LEVEL target: message` is a few KiB, comfortably
|
||||
/// short of whatever made pasting the full (up to 2000-line) ring into a
|
||||
/// chat's message box laggy on Iris's phone. The full ring is still
|
||||
/// reachable through `devlog`'s provider, so this only bounds what a
|
||||
/// report inlines.
|
||||
pub const COPY_REPORT_TAIL_LINES: usize = 150;
|
||||
|
||||
/// One recorded line. `seq` is assigned by the ring and only ever
|
||||
/// increases, so a reader that remembers where it got to can ask for what
|
||||
/// came after -- and a gap in the sequence is exactly the lines the bound
|
||||
@@ -240,6 +249,35 @@ impl LogRing {
|
||||
.join("\n")
|
||||
}
|
||||
|
||||
/// The newest `max_lines` lines, formatted, with a first line naming
|
||||
/// how many older ones were left out of *this* text when the ring held
|
||||
/// more than that -- what `Copy report` appends instead of
|
||||
/// [`Self::to_text`].
|
||||
///
|
||||
/// Iris's own report: pasting the full ring (over a thousand lines on
|
||||
/// a session that ran with tracing on) into a phone's message box was
|
||||
/// what "causes a lot of lag" meant (docs/IRIS_TODO.md, 2026-09-07
|
||||
/// night) -- nothing is actually lost, since `devlog`'s provider still
|
||||
/// hands Dev Updater's Runtime tab the whole ring; this only caps what
|
||||
/// gets inlined into a share.
|
||||
pub fn tail_text(&self, max_lines: usize) -> String {
|
||||
let lines = self.snapshot();
|
||||
if lines.len() <= max_lines {
|
||||
return lines
|
||||
.iter()
|
||||
.map(LogLine::format)
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n");
|
||||
}
|
||||
let omitted = lines.len() - max_lines;
|
||||
let tail = lines[lines.len() - max_lines..]
|
||||
.iter()
|
||||
.map(LogLine::format)
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n");
|
||||
format!("{omitted} earlier lines omitted; full log in Dev Updater's Runtime tab\n{tail}")
|
||||
}
|
||||
|
||||
/// One line for a diagnostics pane: how much is held, how much was
|
||||
/// dropped, and when the last line arrived. "no lines yet" is its own
|
||||
/// wording rather than a count of zero with a made-up time, because
|
||||
@@ -270,6 +308,37 @@ impl LogRing {
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether a target belongs to this app's own crates (`iris` or
|
||||
/// `client_core`) rather than a dependency's -- `starts_with` guarded by an
|
||||
/// exact match or a `::` so an unrelated crate that merely begins with the
|
||||
/// same letters (there is no such crate today, but the check should not
|
||||
/// rely on that) is never mistaken for one of ours.
|
||||
fn is_own_target(target: &str) -> bool {
|
||||
target == "iris"
|
||||
|| target.starts_with("iris::")
|
||||
|| target == "client_core"
|
||||
|| target.starts_with("client_core::")
|
||||
}
|
||||
|
||||
/// Whether a line at `level` from `target` belongs in the ring, given
|
||||
/// whether tracing is on right now.
|
||||
///
|
||||
/// This is the one filter docs/IRIS_TODO.md's "logs way too big" entry
|
||||
/// asked for, applied once here rather than at each `debug!` call site:
|
||||
/// Info and above always ring, from anything, because a real warning or
|
||||
/// error from a dependency is worth keeping. Debug and Trace ring only
|
||||
/// from this app's own targets, and only while tracing is switched on --
|
||||
/// otherwise `naga::front`/`wgpu_core`/`jni` log at Debug unconditionally
|
||||
/// (the process logger's own level, set once at install and unrelated to
|
||||
/// tracing), which is what filled the ring with 1339 lines of it and
|
||||
/// dropped 4050 more before this existed. `iris`'s own Debug lines already
|
||||
/// self-gate on `iris::diagnostics::trace_enabled` at their call sites
|
||||
/// (commit 992c472); this is the backstop for lines this crate does not
|
||||
/// control.
|
||||
fn ring_accepts(level: log::Level, target: &str, trace_enabled: bool) -> bool {
|
||||
level <= log::Level::Info || (trace_enabled && is_own_target(target))
|
||||
}
|
||||
|
||||
/// A `log` backend that records into a [`LogRing`] **and** forwards to the
|
||||
/// logger the platform already installs, so nothing that reads the
|
||||
/// platform's log (`logcat`, a terminal) changes.
|
||||
@@ -281,25 +350,40 @@ impl LogRing {
|
||||
pub struct RingLogger {
|
||||
ring: LogRing,
|
||||
inner: Box<dyn log::Log>,
|
||||
/// Whether `iris::input`/`iris::frame`-style tracing is switched on
|
||||
/// right now, consulted by [`ring_accepts`]. A plain fn pointer rather
|
||||
/// than a dependency on `iris::diagnostics::trace_enabled` directly:
|
||||
/// `client-core` sits below `iris` (AGENTS.md's "dependencies flow one
|
||||
/// direction"), so the platform crate that depends on both is the one
|
||||
/// that wires this closure through, the same way it already supplies
|
||||
/// `inner`.
|
||||
trace_enabled: fn() -> bool,
|
||||
}
|
||||
|
||||
impl RingLogger {
|
||||
pub fn new(ring: LogRing, inner: Box<dyn log::Log>) -> Self {
|
||||
Self { ring, inner }
|
||||
pub fn new(ring: LogRing, inner: Box<dyn log::Log>, trace_enabled: fn() -> bool) -> Self {
|
||||
Self {
|
||||
ring,
|
||||
inner,
|
||||
trace_enabled,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl log::Log for RingLogger {
|
||||
/// True for anything `log`'s own max level lets through: the ring
|
||||
/// wants everything, even where the platform logger would filter it
|
||||
/// out. The filter is applied per-logger in [`Self::log`] instead.
|
||||
/// wants everything the *inner* logger might also want, even where the
|
||||
/// platform logger would filter it out. Which lines the ring itself
|
||||
/// keeps is decided in [`Self::log`] by [`ring_accepts`].
|
||||
fn enabled(&self, _metadata: &log::Metadata) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn log(&self, record: &log::Record) {
|
||||
if ring_accepts(record.level(), record.target(), (self.trace_enabled)()) {
|
||||
self.ring
|
||||
.push(record.level(), record.target(), record.args().to_string());
|
||||
}
|
||||
if self.inner.enabled(record.metadata()) {
|
||||
self.inner.log(record);
|
||||
}
|
||||
@@ -320,8 +404,9 @@ pub fn install(
|
||||
ring: LogRing,
|
||||
inner: Box<dyn log::Log>,
|
||||
max_level: log::LevelFilter,
|
||||
trace_enabled: fn() -> bool,
|
||||
) -> Result<(), log::SetLoggerError> {
|
||||
log::set_boxed_logger(Box::new(RingLogger::new(ring, inner)))?;
|
||||
log::set_boxed_logger(Box::new(RingLogger::new(ring, inner, trace_enabled)))?;
|
||||
log::set_max_level(max_level);
|
||||
Ok(())
|
||||
}
|
||||
@@ -349,12 +434,16 @@ pub fn process_ring() -> &'static LogRing {
|
||||
/// Installs [`process_ring`] as the recording half of the process logger,
|
||||
/// forwarding to `inner` (the platform's own logger, already configured).
|
||||
/// The platform half of AGENTS.md's sharing rule is `inner`; everything
|
||||
/// else is shared.
|
||||
/// else is shared. `trace_enabled` is the platform's own trace toggle
|
||||
/// (`iris::diagnostics::trace_enabled` on Android) -- see
|
||||
/// [`ring_accepts`] and the field doc on `RingLogger` for why it is
|
||||
/// passed in rather than called directly.
|
||||
pub fn install_process_logger(
|
||||
inner: Box<dyn log::Log>,
|
||||
max_level: log::LevelFilter,
|
||||
trace_enabled: fn() -> bool,
|
||||
) -> Result<(), log::SetLoggerError> {
|
||||
install(process_ring().clone(), inner, max_level)
|
||||
install(process_ring().clone(), inner, max_level, trace_enabled)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -471,6 +560,33 @@ mod tests {
|
||||
assert_eq!(ring.to_text().lines().count(), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tail_text_is_the_whole_ring_untouched_when_under_the_cap() {
|
||||
let ring = LogRing::new(100, 1 << 20);
|
||||
fill(&ring, 5);
|
||||
assert_eq!(ring.tail_text(150), ring.to_text());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tail_text_trims_to_the_newest_lines_and_says_how_many_were_left_out() {
|
||||
let ring = LogRing::new(1000, 1 << 20);
|
||||
fill(&ring, 200);
|
||||
let tail = ring.tail_text(150);
|
||||
let mut lines = tail.lines();
|
||||
assert_eq!(
|
||||
lines.next().unwrap(),
|
||||
"50 earlier lines omitted; full log in Dev Updater's Runtime tab"
|
||||
);
|
||||
let rest: Vec<&str> = lines.collect();
|
||||
assert_eq!(rest.len(), 150, "exactly the cap, after the header line");
|
||||
assert!(
|
||||
rest[0].ends_with("line 50"),
|
||||
"the oldest line kept is the 50th, not line 0: {}",
|
||||
rest[0]
|
||||
);
|
||||
assert!(rest.last().unwrap().ends_with("line 199"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn an_empty_ring_says_so_rather_than_reporting_a_time() {
|
||||
let ring = LogRing::with_defaults();
|
||||
@@ -522,19 +638,26 @@ mod tests {
|
||||
|
||||
let seen = Arc::new(Mutex::new(Vec::new()));
|
||||
let ring = LogRing::with_defaults();
|
||||
let logger = RingLogger::new(ring.clone(), Box::new(Collect(seen.clone(), Level::Info)));
|
||||
// Own target, tracing on: this is the case where the ring and the
|
||||
// inner logger disagree, which is the thing under test -- a
|
||||
// foreign target is covered separately below.
|
||||
let logger = RingLogger::new(
|
||||
ring.clone(),
|
||||
Box::new(Collect(seen.clone(), Level::Info)),
|
||||
|| true,
|
||||
);
|
||||
logger.log(
|
||||
&log::Record::builder()
|
||||
.args(format_args!("kept"))
|
||||
.level(Level::Info)
|
||||
.target("t")
|
||||
.target("iris::test")
|
||||
.build(),
|
||||
);
|
||||
logger.log(
|
||||
&log::Record::builder()
|
||||
.args(format_args!("filtered"))
|
||||
.level(Level::Debug)
|
||||
.target("t")
|
||||
.target("iris::test")
|
||||
.build(),
|
||||
);
|
||||
|
||||
@@ -544,6 +667,91 @@ mod tests {
|
||||
"the inner logger's own filter still applies"
|
||||
);
|
||||
let held: Vec<String> = ring.snapshot().into_iter().map(|l| l.message).collect();
|
||||
assert_eq!(held, ["kept", "filtered"], "the ring keeps both");
|
||||
assert_eq!(
|
||||
held,
|
||||
["kept", "filtered"],
|
||||
"own-target debug still rings while tracing is on"
|
||||
);
|
||||
}
|
||||
|
||||
/// The bug this filter fixes: `naga`/`wgpu_core`/`jni` log at Debug
|
||||
/// unconditionally, and used to flood the ring even though nothing in
|
||||
/// this app asked for their Debug output. A foreign target's Debug
|
||||
/// line must not ring even while tracing is on -- tracing controls
|
||||
/// this app's own diagnostics, not a dependency's chatter.
|
||||
#[test]
|
||||
fn a_foreign_targets_debug_line_never_rings_even_while_tracing_is_on() {
|
||||
use log::Log;
|
||||
struct Discard;
|
||||
impl Log for Discard {
|
||||
fn enabled(&self, _: &log::Metadata) -> bool {
|
||||
true
|
||||
}
|
||||
fn log(&self, _: &log::Record) {}
|
||||
fn flush(&self) {}
|
||||
}
|
||||
|
||||
let ring = LogRing::with_defaults();
|
||||
let logger = RingLogger::new(ring.clone(), Box::new(Discard), || true);
|
||||
logger.log(
|
||||
&log::Record::builder()
|
||||
.args(format_args!("naga debug spam"))
|
||||
.level(Level::Debug)
|
||||
.target("naga::front")
|
||||
.build(),
|
||||
);
|
||||
logger.log(
|
||||
&log::Record::builder()
|
||||
.args(format_args!("naga warning"))
|
||||
.level(Level::Warn)
|
||||
.target("wgpu_core::device")
|
||||
.build(),
|
||||
);
|
||||
|
||||
let held: Vec<String> = ring.snapshot().into_iter().map(|l| l.message).collect();
|
||||
assert_eq!(
|
||||
held,
|
||||
["naga warning"],
|
||||
"Info-and-above always rings; foreign Debug never does"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ring_accepts_is_own_target_debug_only_while_tracing() {
|
||||
assert!(
|
||||
ring_accepts(Level::Info, "wgpu_core::device", false),
|
||||
"Info+ from anything, tracing off"
|
||||
);
|
||||
assert!(
|
||||
ring_accepts(Level::Warn, "jni", true),
|
||||
"Info+ from anything, tracing on"
|
||||
);
|
||||
assert!(
|
||||
!ring_accepts(Level::Debug, "jni", true),
|
||||
"foreign Debug, tracing on: still excluded"
|
||||
);
|
||||
assert!(
|
||||
!ring_accepts(Level::Debug, "iris::sense", false),
|
||||
"own Debug, tracing off: excluded"
|
||||
);
|
||||
assert!(
|
||||
ring_accepts(Level::Debug, "iris::sense", true),
|
||||
"own Debug, tracing on: included"
|
||||
);
|
||||
assert!(
|
||||
ring_accepts(Level::Trace, "client_core::api", true),
|
||||
"own Trace, tracing on: included"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_own_target_matches_the_crate_or_its_modules_only() {
|
||||
assert!(is_own_target("iris"));
|
||||
assert!(is_own_target("iris::sense"));
|
||||
assert!(is_own_target("client_core"));
|
||||
assert!(is_own_target("client_core::log_ring"));
|
||||
assert!(!is_own_target("iris_something_else"));
|
||||
assert!(!is_own_target("naga::front"));
|
||||
assert!(!is_own_target("jni"));
|
||||
}
|
||||
}
|
||||
+21
-18
@@ -1091,24 +1091,27 @@ showed, beyond her words:
|
||||
`Released(None)`, not a tap (Compose does not deliver a click either).
|
||||
Layer-1 test on a flick followed by a down + small drag 150 ms later:
|
||||
offset tracks the finger sample-for-sample from the down.
|
||||
- [ ] **"The copy report button seemed impossible to hit until I hit the
|
||||
diagnostics one."** Not hit-testing: the button logged `iris bench
|
||||
report: nothing to copy -- run the benchmark first` six times and did
|
||||
nothing on screen. A control that silently declines is the UI_RULES
|
||||
failure "a failure is reported where it happened": Copy report must
|
||||
always copy something -- the diagnostics and the log with a first line
|
||||
saying no benchmark has run -- and never depend on another button
|
||||
having been pressed first.
|
||||
- [ ] **"The logs seem way too big to send in this message box, causes a
|
||||
lot of lag."** Two causes. (1) The ring was 1339 lines of `naga::front`
|
||||
/ `wgpu_core` / `jni` DEBUG output with 4050 dropped: the ring logger
|
||||
accepts every crate at Debug, and the trace gate (992c472) only covers
|
||||
iris's own lines. The ring must take Debug only from `iris`/`client_core`
|
||||
targets when trace is on, and Info and above from everything else --
|
||||
one filter at the ring, not per callsite. (2) Copy report appends the
|
||||
whole ring. It should append the last ~150 lines and a first line
|
||||
saying "N earlier lines omitted; full log in Dev Updater's Runtime
|
||||
tab" -- the full ring is what the devlog provider is for.
|
||||
- [x] **"The copy report button seemed impossible to hit until I hit the
|
||||
diagnostics one." (done 2026-09-07, b8ea723).** Not hit-testing: the
|
||||
button logged `iris bench report: nothing to copy -- run the benchmark
|
||||
first` six times and did nothing on screen. A control that silently
|
||||
declines is the UI_RULES failure "a failure is reported where it
|
||||
happened": `copy_report` now always copies something -- the
|
||||
diagnostics pane's own text (with a first line saying no benchmark has
|
||||
run) when nothing has run yet, or the last report otherwise -- and
|
||||
never depends on another button having been pressed first.
|
||||
- [x] **"The logs seem way too big to send in this message box, causes a
|
||||
lot of lag." (done 2026-09-07, 7485d78 + b8ea723).** Two causes. (1)
|
||||
The ring was 1339 lines of `naga::front` / `wgpu_core` / `jni` DEBUG
|
||||
output with 4050 dropped: the ring logger accepted every crate at
|
||||
Debug, and the trace gate (992c472) only covered iris's own lines.
|
||||
`client_core::log_ring::ring_accepts` is the one filter now, applied at
|
||||
the ring rather than per callsite: Debug/Trace only from `iris`/
|
||||
`client_core` targets when tracing is on, Info and above from
|
||||
everything else. (2) Copy report appended the whole ring; it now
|
||||
appends `LogRing::tail_text(COPY_REPORT_TAIL_LINES)` (150, named at the
|
||||
constant) with a first line saying how many older lines were left out
|
||||
-- the full ring is still what the devlog provider hands Dev Updater.
|
||||
- [x] Keyboard: the report shows `ime_bottom=891 ime_visible=true` then
|
||||
back to 0 on the phone, so the insets now arrive with a height; the
|
||||
push-up was not reported broken this time.
|
||||
+27
-7
@@ -719,13 +719,33 @@ closes it.
|
||||
- [ ] Catch-a-fling: down during a fling stops it at the down and drags
|
||||
with no slop (docs/IRIS_TODO.md, night). Opus, next slot; uses the
|
||||
layer-1 harness.
|
||||
- [ ] Report hygiene: ring takes Debug only from iris targets, Copy
|
||||
report always copies and trims the log (docs/IRIS_TODO.md, night).
|
||||
Sonnet, with the header item below (same file, `bench_client.rs`).
|
||||
- [ ] Bench app header: four controls no longer fit at 1080px and the
|
||||
devlog agent shrank the label type 18 -> 13 to make room (UI_RULES:
|
||||
never shrink text to fit). Put the controls in two rows or make the
|
||||
header wrap; restore the size. Sonnet.
|
||||
- [x] **Report hygiene (done 2026-09-07).** Ring takes Debug only from
|
||||
`iris`/`client_core` targets, Copy report always copies and trims the
|
||||
log. `client_core::log_ring::ring_accepts` is the one filter (Info+
|
||||
from anywhere; Debug/Trace only from this app's own targets, and only
|
||||
while `iris::diagnostics::trace_enabled()` says tracing is on) --
|
||||
`RingLogger` takes that as a plain `fn() -> bool` rather than depending
|
||||
on `iris` directly, since `client-core` sits below it; `app_log.rs`
|
||||
wires `iris::diagnostics::trace_enabled` through at install. Fixed the
|
||||
1339-held/4050-dropped flood from `naga::front`/`wgpu_core`/`jni`
|
||||
logging at Debug unconditionally. `bench_client.rs`'s `copy_report` no
|
||||
longer declines when nothing has run: with no benchmark yet it copies
|
||||
the diagnostics pane's own text instead, with a first line saying so,
|
||||
and always appends `LogRing::tail_text(COPY_REPORT_TAIL_LINES = 150)`
|
||||
(a first line naming how many older lines were left out) rather than
|
||||
the whole ring. `client-core`'s tests cover the filter and the trim;
|
||||
the copy path was checked on the emulator (immediate `Copy report` tap
|
||||
with no prior button press now logs "copied to clipboard").
|
||||
- [x] **Bench app header (done 2026-09-07).** Four controls no longer fit
|
||||
at 1080px at `HEADER_TEXT = 18`, and a previous agent had shrunk it to
|
||||
13 to make room -- UI_RULES: never shrink text to fit. Restored to 18
|
||||
and split `bench_controls` into two rows (`Dir::DOWN` of two
|
||||
`Dir::RIGHT` pairs: run+copy, then diagnostics+trace), doubling the
|
||||
header's own height (`HEADER_ROW_HEIGHT_DP`) rather than the outer
|
||||
layout's reserved space, since `top_bar` sizes to its own content.
|
||||
Checked on the emulator: `ui-trace show ... --field box` confirms two
|
||||
clean rows with no overlap, and a screenshot shows the restored size
|
||||
reading clearly.
|
||||
- [x] **Bench app crash-loops on this checkout's emulator (done
|
||||
2026-09-07).** Not the surface lifecycle at all, and not "once
|
||||
backgrounded" -- a build with the **default features** (no
|
||||
|
||||
@@ -27,7 +27,13 @@ pub fn install(max_level: log::LevelFilter) {
|
||||
.with_max_level(max_level)
|
||||
.with_tag("iris-android-app"),
|
||||
);
|
||||
if log_ring::install_process_logger(Box::new(inner), max_level).is_err() {
|
||||
if log_ring::install_process_logger(
|
||||
Box::new(inner),
|
||||
max_level,
|
||||
iris::diagnostics::trace_enabled,
|
||||
)
|
||||
.is_err()
|
||||
{
|
||||
// Not a panic: a logger already installed means logging works,
|
||||
// just without the ring, and taking the app down over a
|
||||
// diagnostic would be worse than the diagnostic being missing.
|
||||
|
||||
@@ -468,14 +468,21 @@ const HEADER_SURFACE: UiColor = UiColor::new(28, 28, 34, 255);
|
||||
/// The size every label in the header row is drawn at.
|
||||
///
|
||||
/// One constant for all four rather than a number per button, because the
|
||||
/// whole row has to be sized together: it was 18 with three controls, and
|
||||
/// adding the trace switch made four labels overlap each other on a
|
||||
/// 1080px screen. Shrinking *one* label to fit is what the UI rules
|
||||
/// forbid -- a label a different size from its neighbours for a reason the
|
||||
/// reader cannot see; changing the row's own type size is a layout
|
||||
/// decision, and all four still match. Whoever adds a fifth control has
|
||||
/// one number to reconsider rather than four.
|
||||
const HEADER_TEXT: f32 = 13.0;
|
||||
/// whole row has to be sized together. Adding the trace switch made four
|
||||
/// controls too wide for one row at the size three had used (18), and an
|
||||
/// earlier pass shrank this constant to 13 to make them fit -- exactly
|
||||
/// what UI_RULES forbids ("never shrink text to make it fit": a label a
|
||||
/// different size from its neighbours elsewhere in the app for a reason
|
||||
/// the reader cannot see). The fix is [`bench_controls`]'s two rows
|
||||
/// instead, which leaves room to put this back. Whoever adds a fifth
|
||||
/// control reconsiders the row split, not this number.
|
||||
const HEADER_TEXT: f32 = 18.0;
|
||||
|
||||
/// The height of one row of header controls, in dp. `bench_controls` now
|
||||
/// stacks two of these, so this is the one number to change if a control's
|
||||
/// own padding ever changes instead of `dp(56)` and `dp(112)` needing to
|
||||
/// be kept in sync by hand.
|
||||
const HEADER_ROW_HEIGHT_DP: f32 = 56.0;
|
||||
|
||||
fn bench_controls(rsc: &mut Rsc, top_pad: f32) -> StrongWidget {
|
||||
let run_rect = rect(Color::rgb(40, 70, 40))
|
||||
@@ -499,8 +506,8 @@ fn bench_controls(rsc: &mut Rsc, top_pad: f32) -> StrongWidget {
|
||||
let copy_rect = rect(Color::rgb(50, 50, 60))
|
||||
.on(
|
||||
CursorSense::click(),
|
||||
|ctx: EventIdCtx<'_, Rsc, _, _>, _rsc: &mut Rsc| {
|
||||
ctx.state.copy_report();
|
||||
|ctx: EventIdCtx<'_, Rsc, _, _>, rsc: &mut Rsc| {
|
||||
ctx.state.copy_report(rsc);
|
||||
},
|
||||
)
|
||||
.label("Copy report");
|
||||
@@ -566,11 +573,19 @@ fn bench_controls(rsc: &mut Rsc, top_pad: f32) -> StrongWidget {
|
||||
.pad(dp(8))
|
||||
.add(rsc);
|
||||
|
||||
let buttons = (run, copy, diagnostics, trace).span(Dir::RIGHT).add(rsc);
|
||||
// Two rows rather than one: four controls at the restored `HEADER_TEXT`
|
||||
// no longer fit a 1080px-wide row (that was the shrink this replaces --
|
||||
// see the constant's own doc). Grouped by what they act on: the first
|
||||
// row starts a benchmark and copies its result; the second is the
|
||||
// diagnostics pane and the switch that decides what it will contain
|
||||
// next time.
|
||||
let row1 = (run, copy).span(Dir::RIGHT).add(rsc);
|
||||
let row2 = (diagnostics, trace).span(Dir::RIGHT).add(rsc);
|
||||
let buttons = (row1, row2).span(Dir::DOWN).add(rsc);
|
||||
|
||||
(rect(HEADER_SURFACE), buttons)
|
||||
.stack()
|
||||
.height(dp(56))
|
||||
.height(dp(2.0 * HEADER_ROW_HEIGHT_DP))
|
||||
.pad(Padding::top(top_pad))
|
||||
.add_strong(rsc)
|
||||
.any()
|
||||
@@ -672,23 +687,38 @@ impl BenchClient {
|
||||
log::info!("iris keyboard diagnostics:\n{report}");
|
||||
}
|
||||
|
||||
fn copy_report(&mut self) {
|
||||
let Some(report) = self.last_report.clone() else {
|
||||
log::info!("iris bench report: nothing to copy -- run the benchmark first");
|
||||
return;
|
||||
};
|
||||
/// Always copies something, and never depends on `Diagnostics` or
|
||||
/// `Run benchmark` having been pressed first (docs/IRIS_TODO.md,
|
||||
/// 2026-09-07 night: "the copy report button seemed impossible to hit
|
||||
/// until I hit the diagnostics one" -- it was silently declining
|
||||
/// instead of reporting where it had failed, the UI_RULES failure "a
|
||||
/// failure is reported where it happened"). With no benchmark run yet,
|
||||
/// it copies the diagnostics pane's own text instead, with a first
|
||||
/// line saying so -- `diagnostics_text` needs no prior button press
|
||||
/// either, so this is never actually empty-handed.
|
||||
fn copy_report(&mut self, rsc: &mut Rsc) {
|
||||
let Some(platform) = &self.platform else {
|
||||
log::info!("iris bench report: no platform handle, can't reach the clipboard");
|
||||
return;
|
||||
};
|
||||
// The ring goes on the clipboard, not into the pane: the pane is
|
||||
// on screen and a thousand log lines in it would bury the report
|
||||
// somebody pressed the button for, while the clipboard is going
|
||||
// straight into a message to be read elsewhere.
|
||||
let report = match self.last_report.clone() {
|
||||
Some(report) => report,
|
||||
None => format!(
|
||||
"no benchmark has run yet -- these are the diagnostics instead:\n\n{}",
|
||||
self.diagnostics_text(rsc)
|
||||
),
|
||||
};
|
||||
// The ring's tail goes on the clipboard, not the full ring, and
|
||||
// not into the on-screen pane either: the full ring can be over a
|
||||
// thousand lines with tracing on, and pasting that into a phone's
|
||||
// message box was Iris's own "causes a lot of lag" report. The
|
||||
// full ring is still reachable through Dev Updater's Runtime tab
|
||||
// (`devlog`'s provider reads the same ring) -- this only bounds
|
||||
// what gets inlined here.
|
||||
let report = format!(
|
||||
"{report}\n\n=== app log ({}) ===\n{}",
|
||||
crate::app_log::ring().summary(),
|
||||
crate::app_log::ring().to_text()
|
||||
crate::app_log::ring().tail_text(client_core::log_ring::COPY_REPORT_TAIL_LINES)
|
||||
);
|
||||
if platform.copy_to_clipboard("iris bench report", &report) {
|
||||
log::info!("iris bench report: copied to clipboard");
|
||||
|
||||
Reference in new issue
Block a user