From 8088a1fa5919f502a728d073fb43488d850cb789 Mon Sep 17 00:00:00 2001 From: iris-ai <4+iris-ai@noreply.localhost> Date: Sun, 20 Sep 2026 00:20:23 -0400 Subject: [PATCH] Say why a reuse was refused, on every path that refuses one Three of the eight rejections in `try_reuse` were invisible or half-visible to the diagnostics: a changed inherited mask counted nothing and traced nothing, an undrawn record traced without counting, and a changed region-node choice counted without tracing. The mask one is the rejection this branch's repair was about, so "why did that redraw?" was exactly the question the rig could not answer. Adding a counter meant editing a variant list and a name list at the same index, which renames every total after a slip and says nothing. The two lists are one declaration now. Co-Authored-By: Claude Opus 5 --- core/src/layout_diagnostics.rs | 152 ++++++++++++++------------------- core/src/ui/render_state.rs | 18 +++- 2 files changed, 78 insertions(+), 92 deletions(-) diff --git a/core/src/layout_diagnostics.rs b/core/src/layout_diagnostics.rs index 71894c3..6950aa1 100644 --- a/core/src/layout_diagnostics.rs +++ b/core/src/layout_diagnostics.rs @@ -23,100 +23,70 @@ use std::{ time::Instant, }; -#[derive(Clone, Copy)] -pub(crate) enum Counter { - Updates, - DrawRequests, - WidgetDraws, - RegionNodeDraws, - SizeReads, - HintHits, - HintMisses, - ReuseAttempts, - ReuseExact, - ReuseMoved, - ReuseDirty, - ReuseWrongParent, - ReuseRemapped, - ReuseOutside, - ReuseWrongLayer, - ReuseWrongNode, - QueuePops, - DepthReads, - LocalRedraws, - SizeChanges, - ReaderEdges, - PrimitiveWrites, - TextRenders, - TextShapeHits, - TextShapes, - TextBreaks, - GlyphPlacements, - OutsidePinnedLen, - OutsideRelBase, - OutsideRegion, +/// Declares a counter or timer kind beside the name its report prints. Two +/// lists in the same order was one list too many: a variant inserted without +/// its label moving with it renames every total after it, and nothing says +/// so. +macro_rules! labelled { + ($(#[$meta:meta])* $vis:vis enum $Name:ident { $($variant:ident = $label:literal,)* }) => { + $(#[$meta])* + #[derive(Clone, Copy)] + $vis enum $Name { $($variant,)* } + + impl $Name { + const COUNT: usize = [$($label,)*].len(); + const NAMES: [&'static str; Self::COUNT] = [$($label,)*]; + } + }; } -impl Counter { - const COUNT: usize = Self::OutsideRegion as usize + 1; - - const NAMES: [&'static str; Self::COUNT] = [ - "updates", - "draw requests", - "widget draws", - "region-node draws", - "draw-result size reads", - "hint hits", - "hint misses", - "reuse attempts", - "reuse exact", - "reuse moved", - "reuse: dirty", - "reuse: wrong parent", - "reuse remapped", - "reuse: outside what it holds for", - "reuse: another layer", - "reuse: region-node choice changed", - "redraw queue pops", - "depth reads", - "local redraws", - "size changes", - "reader edges", - "primitive writes", - "text renders", - "text shape hits", - "text shapes", - "text line breaks", - "glyph placements", - "reuse outside: the length it was pinned to", - "reuse outside: a rel base", - "reuse outside: a region length", - ]; +labelled! { + pub(crate) enum Counter { + Updates = "updates", + DrawRequests = "draw requests", + WidgetDraws = "widget draws", + RegionNodeDraws = "region-node draws", + SizeReads = "draw-result size reads", + HintHits = "hint hits", + HintMisses = "hint misses", + ReuseAttempts = "reuse attempts", + ReuseExact = "reuse exact", + ReuseMoved = "reuse moved", + ReuseDirty = "reuse: dirty", + ReuseUndrawn = "reuse: nothing drawn to keep", + ReuseWrongParent = "reuse: wrong parent", + ReuseRemapped = "reuse remapped", + ReuseOutside = "reuse: outside what it holds for", + ReuseWrongLayer = "reuse: another layer", + ReuseWrongNode = "reuse: region-node choice changed", + ReuseWrongMask = "reuse: a different inherited mask", + QueuePops = "redraw queue pops", + DepthReads = "depth reads", + LocalRedraws = "local redraws", + SizeChanges = "size changes", + ReaderEdges = "reader edges", + PrimitiveWrites = "primitive writes", + TextRenders = "text renders", + TextShapeHits = "text shape hits", + TextShapes = "text shapes", + TextBreaks = "text line breaks", + GlyphPlacements = "glyph placements", + OutsidePinnedLen = "reuse outside: the length it was pinned to", + OutsideRelBase = "reuse outside: a rel base", + OutsideRegion = "reuse outside: a region length", + } } -#[derive(Clone, Copy)] -pub(crate) enum TimerKind { - Update, - FullLayout, - IncrementalLayout, - TextRender, - TextShape, - TextBreak, - GlyphPlacement, -} - -impl TimerKind { - const COUNT: usize = Self::GlyphPlacement as usize + 1; - - const NAMES: [&'static str; Self::COUNT] = [ - "update total", - "full layout", - "incremental layout", - "text render", - "text shape", - "text line break", - "glyph placement", - ]; +labelled! { + pub(crate) enum TimerKind { + Update = "update total", + FullLayout = "full layout", + IncrementalLayout = "incremental layout", + TextRender = "text render", + TextShape = "text shape", + TextBreak = "text line break", + GlyphPlacement = "glyph placement", + } } #[derive(Clone)] @@ -251,6 +221,8 @@ pub enum ReuseOutcome { Dirty, WrongParent, WrongLayer, + WrongMask, + WrongNode, Remapped, Outside, Undrawn, diff --git a/core/src/ui/render_state.rs b/core/src/ui/render_state.rs index c235836..bf57bf9 100644 --- a/core/src/ui/render_state.rs +++ b/core/src/ui/render_state.rs @@ -569,12 +569,18 @@ impl UiRenderState { }; if !active.drawn { #[cfg(feature = "layout-diagnostics")] - diag::reuse(id, ReuseOutcome::Undrawn); + { + diag::bump(Counter::ReuseUndrawn); + diag::reuse(id, ReuseOutcome::Undrawn); + } return false; } if active.is_region_node() != info.region_node { #[cfg(feature = "layout-diagnostics")] - diag::bump(Counter::ReuseWrongNode); + { + diag::bump(Counter::ReuseWrongNode); + diag::reuse(id, ReuseOutcome::WrongNode); + } return false; } // Drawn on another layer: the drawing sits in that layer's list and @@ -590,7 +596,15 @@ impl UiRenderState { } return false; } + // Its primitives name the mask it inherited, and a masking parent + // that redrew pushed another: keeping them would clip them by one + // nothing updates again. if active.parent_mask != info.mask { + #[cfg(feature = "layout-diagnostics")] + { + diag::bump(Counter::ReuseWrongMask); + diag::reuse(id, ReuseOutcome::WrongMask); + } return false; } // Drawn somewhere else in the tree: its box is in coordinates it no