Coalesce resize layout diagnostics
This commit is contained in:
1 parent
480f0bc99f
commit
82fa6c1123
7 files changed
+316
-28
No files matched your search
@@ -10,8 +10,12 @@
|
||||
//! added together. Use an uninstrumented build under `perf` for final CPU
|
||||
//! totals; counting every primitive and distinct widget deliberately perturbs
|
||||
//! the instrumented run.
|
||||
//!
|
||||
//! Call [`trace_widget`] before a frame to retain the ordered constraint,
|
||||
//! reuse, size, placement, and text events for one suspicious widget. The
|
||||
//! selection is a set and survives [`take`] until cleared.
|
||||
|
||||
use crate::WidgetId;
|
||||
use crate::{Axis, Len, Size, UiRegion, WidgetId, util::Vec2};
|
||||
use std::{
|
||||
cell::RefCell,
|
||||
collections::{HashMap, HashSet},
|
||||
@@ -119,6 +123,7 @@ pub struct Report {
|
||||
distinct_text_widgets: usize,
|
||||
hot_widgets: Vec<Callsite>,
|
||||
hot_text: Vec<Callsite>,
|
||||
traces: Vec<TraceEvent>,
|
||||
}
|
||||
|
||||
impl Default for Report {
|
||||
@@ -130,6 +135,7 @@ impl Default for Report {
|
||||
distinct_text_widgets: 0,
|
||||
hot_widgets: Vec::new(),
|
||||
hot_text: Vec::new(),
|
||||
traces: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -160,6 +166,11 @@ impl Report {
|
||||
&self.hot_text
|
||||
}
|
||||
|
||||
/// Ordered layout events for widgets selected with [`trace_widget`].
|
||||
pub fn traces(&self) -> &[TraceEvent] {
|
||||
&self.traces
|
||||
}
|
||||
|
||||
/// Formats nonzero totals divided by `frames`.
|
||||
pub fn per_frame(&self, frames: usize) -> String {
|
||||
let divisor = frames.max(1) as f64;
|
||||
@@ -211,6 +222,12 @@ impl Report {
|
||||
);
|
||||
}
|
||||
}
|
||||
if !self.traces.is_empty() {
|
||||
let _ = writeln!(out, " targeted layout trace:");
|
||||
for event in &self.traces {
|
||||
let _ = writeln!(out, " {event:?}");
|
||||
}
|
||||
}
|
||||
out
|
||||
}
|
||||
}
|
||||
@@ -223,6 +240,58 @@ pub struct Callsite {
|
||||
pub distinct_widths: usize,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub enum ReuseOutcome {
|
||||
Exact,
|
||||
Moved,
|
||||
Dirty,
|
||||
WrongParent,
|
||||
Unslotted,
|
||||
OwnResize,
|
||||
DescendantResize,
|
||||
}
|
||||
|
||||
/// One targeted layout event. Events are retained in execution order, making
|
||||
/// repeated constraint paths visible without logging every widget globally.
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub enum TraceEvent {
|
||||
DrawRequest {
|
||||
id: WidgetId,
|
||||
parent: Option<WidgetId>,
|
||||
region: UiRegion,
|
||||
pixel_size: Vec2,
|
||||
slotted: bool,
|
||||
},
|
||||
Reuse {
|
||||
id: WidgetId,
|
||||
outcome: ReuseOutcome,
|
||||
},
|
||||
SizeReported {
|
||||
id: WidgetId,
|
||||
size: Size,
|
||||
},
|
||||
Placed {
|
||||
id: WidgetId,
|
||||
parent: WidgetId,
|
||||
region: UiRegion,
|
||||
},
|
||||
SizeRead {
|
||||
id: WidgetId,
|
||||
reader: WidgetId,
|
||||
size: Size,
|
||||
},
|
||||
HintRead {
|
||||
id: WidgetId,
|
||||
reader: WidgetId,
|
||||
axis: Axis,
|
||||
hint: Option<Len>,
|
||||
},
|
||||
TextRendered {
|
||||
id: WidgetId,
|
||||
width: Option<f32>,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct Calls {
|
||||
label: String,
|
||||
@@ -235,6 +304,7 @@ struct Current {
|
||||
report: Report,
|
||||
widgets: HashMap<WidgetId, Calls>,
|
||||
text_widgets: HashMap<WidgetId, Calls>,
|
||||
traced: HashSet<WidgetId>,
|
||||
}
|
||||
|
||||
thread_local! {
|
||||
@@ -255,6 +325,79 @@ pub(crate) fn draw_widget(id: WidgetId, label: &str) {
|
||||
});
|
||||
}
|
||||
|
||||
/// Adds a widget to the targeted trace set. Selection survives [`take`]
|
||||
/// until explicitly removed or cleared.
|
||||
pub fn trace_widget(id: impl Into<WidgetId>) {
|
||||
CURRENT.with_borrow_mut(|current| {
|
||||
current.traced.insert(id.into());
|
||||
});
|
||||
}
|
||||
|
||||
pub fn untrace_widget(id: impl Into<WidgetId>) {
|
||||
CURRENT.with_borrow_mut(|current| {
|
||||
current.traced.remove(&id.into());
|
||||
});
|
||||
}
|
||||
|
||||
pub fn clear_traced_widgets() {
|
||||
CURRENT.with_borrow_mut(|current| current.traced.clear());
|
||||
}
|
||||
|
||||
fn trace(id: WidgetId, event: TraceEvent) {
|
||||
CURRENT.with_borrow_mut(|current| {
|
||||
if current.traced.contains(&id) {
|
||||
current.report.traces.push(event);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub(crate) fn draw_request(
|
||||
id: WidgetId,
|
||||
parent: Option<WidgetId>,
|
||||
region: UiRegion,
|
||||
pixel_size: Vec2,
|
||||
slotted: bool,
|
||||
) {
|
||||
trace(
|
||||
id,
|
||||
TraceEvent::DrawRequest {
|
||||
id,
|
||||
parent,
|
||||
region,
|
||||
pixel_size,
|
||||
slotted,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
pub(crate) fn reuse(id: WidgetId, outcome: ReuseOutcome) {
|
||||
trace(id, TraceEvent::Reuse { id, outcome });
|
||||
}
|
||||
|
||||
pub(crate) fn size_reported(id: WidgetId, size: Size) {
|
||||
trace(id, TraceEvent::SizeReported { id, size });
|
||||
}
|
||||
|
||||
pub(crate) fn placed(id: WidgetId, parent: WidgetId, region: UiRegion) {
|
||||
trace(id, TraceEvent::Placed { id, parent, region });
|
||||
}
|
||||
|
||||
pub(crate) fn size_read(id: WidgetId, reader: WidgetId, size: Size) {
|
||||
trace(id, TraceEvent::SizeRead { id, reader, size });
|
||||
}
|
||||
|
||||
pub(crate) fn hint_read(id: WidgetId, reader: WidgetId, axis: Axis, hint: Option<Len>) {
|
||||
trace(
|
||||
id,
|
||||
TraceEvent::HintRead {
|
||||
id,
|
||||
reader,
|
||||
axis,
|
||||
hint,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
pub(crate) fn render_text(id: WidgetId, label: &str, width: Option<f32>) {
|
||||
CURRENT.with_borrow_mut(|current| {
|
||||
let calls = current.text_widgets.entry(id).or_default();
|
||||
@@ -263,6 +406,12 @@ pub(crate) fn render_text(id: WidgetId, label: &str, width: Option<f32>) {
|
||||
}
|
||||
calls.count += 1;
|
||||
calls.widths.insert(width.map(f32::to_bits));
|
||||
if current.traced.contains(&id) {
|
||||
current
|
||||
.report
|
||||
.traces
|
||||
.push(TraceEvent::TextRendered { id, width });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user