Coalesce resize layout diagnostics

This commit is contained in:
iris-ai committed 2026-09-14 17:05:11 -04:00
1 parent 480f0bc99f
commit 82fa6c1123
7 files changed
+316 -28

No files matched your search

+61 -24
View File
@@ -1,5 +1,5 @@
#[cfg(feature = "layout-diagnostics")]
use crate::layout_diagnostics::{self as diag, Counter, TimerKind};
use crate::layout_diagnostics::{self as diag, Counter, ReuseOutcome, TimerKind};
use crate::{
ActiveData, Axis, DrawLayers, IdLike, MaskIdx, MoveIdx, Moves, OnResize, Painter, PixelRegion,
Size, StrongWidget, UiRegion, UiRsc, WidgetId, Widgets,
@@ -37,8 +37,11 @@ impl UiRenderState {
}
pub fn resize(&mut self, size: impl Into<Vec2>) {
self.output_size = size.into();
self.resized = true;
let size = size.into();
if size != self.output_size {
self.output_size = size;
self.resized = true;
}
}
pub fn output_size(&self) -> Vec2 {
@@ -77,11 +80,17 @@ impl UiRenderState {
{
#[cfg(feature = "layout-diagnostics")]
let _marking = diag::timer(TimerKind::ResizeMarking);
for (&id, active) in &self.active {
if active.reads_output {
#[cfg(feature = "layout-diagnostics")]
diag::bump(Counter::ResizeDependents);
rsc.widgets_mut().needs_redraw.insert(id);
let dependents: Vec<_> = self
.active
.iter()
.filter_map(|(&id, active)| active.reads_output.then_some(id))
.collect();
for id in dependents {
#[cfg(feature = "layout-diagnostics")]
diag::bump(Counter::ResizeDependents);
rsc.widgets_mut().needs_redraw.insert(id);
if let Some(top) = self.mark_readers(id, rsc) {
rsc.widgets_mut().needs_redraw.insert(top);
}
}
}
@@ -127,7 +136,10 @@ impl UiRenderState {
rsc: &mut dyn UiRsc,
) -> Size {
#[cfg(feature = "layout-diagnostics")]
diag::bump(Counter::DrawRequests);
{
diag::bump(Counter::DrawRequests);
diag::draw_request(id, parent, region, self.px_of(parent_move, region), slotted);
}
let mut old_children = old_children.unwrap_or_default();
if self.active.contains_key(&id) {
if let Some(size) = self.try_reuse(id, region, parent_move, rsc) {
@@ -175,6 +187,8 @@ impl UiRenderState {
let mut widget = painter.rsc.widgets().get_dyn_dynamic(id);
let size = widget.draw(&mut painter);
drop(widget);
#[cfg(feature = "layout-diagnostics")]
diag::size_reported(id, size);
let Painter {
state: _,
@@ -271,7 +285,10 @@ impl UiRenderState {
diag::bump(Counter::ReuseAttempts);
if rsc.widgets().needs_redraw.contains(&id) {
#[cfg(feature = "layout-diagnostics")]
diag::bump(Counter::ReuseDirty);
{
diag::bump(Counter::ReuseDirty);
diag::reuse(id, ReuseOutcome::Dirty);
}
return None;
}
let active = self.active.get(&id)?;
@@ -279,7 +296,10 @@ impl UiRenderState {
// longer sits in, and its slot names the wrong parent.
if active.parent_move != parent_move {
#[cfg(feature = "layout-diagnostics")]
diag::bump(Counter::ReuseWrongParent);
{
diag::bump(Counter::ReuseWrongParent);
diag::reuse(id, ReuseOutcome::WrongParent);
}
return None;
}
let (size, old_region, slot, old_px) =
@@ -294,7 +314,10 @@ impl UiRenderState {
}
if !changed.iter().any(|&c| c) && old_region == region {
#[cfg(feature = "layout-diagnostics")]
diag::bump(Counter::ReuseExact);
{
diag::bump(Counter::ReuseExact);
diag::reuse(id, ReuseOutcome::Exact);
}
return Some(size);
}
// Only a placed widget can be given a different box without drawing
@@ -302,7 +325,10 @@ impl UiRenderState {
// entry says where all of it went.
if slot == parent_move {
#[cfg(feature = "layout-diagnostics")]
diag::bump(Counter::ReuseUnslotted);
{
diag::bump(Counter::ReuseUnslotted);
diag::reuse(id, ReuseOutcome::Unslotted);
}
return None;
}
if changed.iter().any(|&c| c) {
@@ -316,12 +342,18 @@ impl UiRenderState {
// and has to lay out around what it comes to.
if redraws {
#[cfg(feature = "layout-diagnostics")]
diag::bump(Counter::ReuseOwnResize);
{
diag::bump(Counter::ReuseOwnResize);
diag::reuse(id, ReuseOutcome::OwnResize);
}
return None;
}
if self.redraws_under(id, changed, rsc) {
#[cfg(feature = "layout-diagnostics")]
diag::bump(Counter::ReuseDescendantResize);
{
diag::bump(Counter::ReuseDescendantResize);
diag::reuse(id, ReuseOutcome::DescendantResize);
}
return None;
}
}
@@ -330,7 +362,10 @@ impl UiRenderState {
active.region = region;
active.px = px;
#[cfg(feature = "layout-diagnostics")]
diag::bump(Counter::ReuseMoved);
{
diag::bump(Counter::ReuseMoved);
diag::reuse(id, ReuseOutcome::Moved);
}
Some(size)
}
@@ -446,14 +481,16 @@ impl UiRenderState {
let _layout = diag::timer(TimerKind::IncrementalLayout);
// A reader's answer is only valid after every dirty size it reads has
// settled. Equal-depth widgets are independent, so their order does
// not matter.
while let Some(id) = rsc
.widgets()
.needs_redraw
.iter()
.copied()
.max_by_key(|&id| self.depth(id))
{
// not matter. Resize dirtiness already marks whole reader chains, so
// choosing their shallowest roots coalesces descendants that share a
// reader and gives each changing box its final constraints first.
while let Some(id) = {
let dirty = rsc.widgets().needs_redraw.iter().copied();
match self.resized {
true => dirty.min_by_key(|&id| self.depth(id)),
false => dirty.max_by_key(|&id| self.depth(id)),
}
} {
#[cfg(feature = "layout-diagnostics")]
diag::bump(Counter::QueuePops);
self.redraw(id, rsc);