diff --git a/core/src/layout_diagnostics.rs b/core/src/layout_diagnostics.rs index f986803..bd52660 100644 --- a/core/src/layout_diagnostics.rs +++ b/core/src/layout_diagnostics.rs @@ -45,7 +45,7 @@ pub(crate) enum Counter { ResizeChecks, ResizeCheckChildren, QueuePops, - DepthSteps, + DepthReads, EagerReaderRedraws, LocalRedraws, SizeChanges, @@ -82,7 +82,7 @@ impl Counter { "resize checks", "resize children checked", "redraw queue pops", - "depth steps", + "depth reads", "eager reader redraws", "local redraws", "size changes", diff --git a/core/src/ui/active.rs b/core/src/ui/active.rs index df61ef0..a984207 100644 --- a/core/src/ui/active.rs +++ b/core/src/ui/active.rs @@ -14,6 +14,10 @@ pub struct ActiveData { /// has since changed is a different number of pixels. pub px: Vec2, pub parent: Option, + /// How far down the tree it was drawn, the root being 1. Carried down a + /// draw rather than worked out by walking up, so it is right for every + /// widget a frame visits and cannot drift while one is being drawn. + pub depth: usize, pub textures: Vec, pub primitives: Vec, pub children: Vec, diff --git a/core/src/ui/painter.rs b/core/src/ui/painter.rs index 24c5a00..5a403ff 100644 --- a/core/src/ui/painter.rs +++ b/core/src/ui/painter.rs @@ -31,6 +31,7 @@ pub struct Painter<'a> { /// its parent placed it, otherwise the nearest ancestor that has one. pub(super) move_idx: MoveIdx, pub layer: usize, + pub(super) depth: usize, pub(super) id: WidgetId, } @@ -134,6 +135,7 @@ impl<'a> Painter<'a> { id.id(), region, Some(self.id), + self.depth + 1, self.move_idx, slotted, self.mask, diff --git a/core/src/ui/render_state.rs b/core/src/ui/render_state.rs index 376216c..1dc40b7 100644 --- a/core/src/ui/render_state.rs +++ b/core/src/ui/render_state.rs @@ -155,6 +155,7 @@ impl UiRenderState { id.id(), UiRegion::FULL, None, + 1, MoveIdx::NONE, false, MaskIdx::NONE, @@ -172,6 +173,7 @@ impl UiRenderState { id: WidgetId, region: UiRegion, parent: Option, + depth: usize, parent_move: MoveIdx, slotted: bool, mask: MaskIdx, @@ -185,7 +187,7 @@ impl UiRenderState { } 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) { + if let Some(size) = self.try_reuse(id, region, depth, parent_move, rsc) { return size; } // if not, then maintain resize and track old children to remove unneeded @@ -217,6 +219,7 @@ impl UiRenderState { primitives: Vec::new(), children: Vec::new(), size_deps: Vec::new(), + depth, size_box_inputs: [false; 2], size_output_inputs: [false; 2], reads_output: [false; 2], @@ -249,6 +252,7 @@ impl UiRenderState { reads_output, move_idx, layer, + depth: _, id, } = painter; @@ -265,6 +269,7 @@ impl UiRenderState { size, px, parent, + depth, textures, primitives, children, @@ -383,6 +388,7 @@ impl UiRenderState { &mut self, id: WidgetId, region: UiRegion, + depth: usize, parent_move: MoveIdx, rsc: &mut dyn UiRsc, ) -> Option { @@ -429,6 +435,7 @@ impl UiRenderState { diag::bump(Counter::ReuseExact); diag::reuse(id, ReuseOutcome::Exact); } + self.keep_depth(id, depth); return Some(size); } // Only a placed widget can be given a different box without drawing @@ -469,6 +476,7 @@ impl UiRenderState { } } self.moves.set(slot, region); + self.keep_depth(id, depth); let active = self.active.get_mut(&id).unwrap(); active.region = region; #[cfg(feature = "layout-diagnostics")] @@ -614,12 +622,32 @@ impl UiRenderState { rsc.free(); } + /// Keeps a reused widget's depth current, since being reused is being + /// visited: only a subtree nobody looked at can hold a stale one. + fn keep_depth(&mut self, id: WidgetId, depth: usize) { + if let Some(active) = self.active.get_mut(&id) { + active.depth = depth; + } + } + fn depth(&self, id: WidgetId) -> usize { + #[cfg(feature = "layout-diagnostics")] + diag::bump(Counter::DepthReads); + let depth = self.active.get(&id).map_or(1, |active| active.depth); + debug_assert_eq!( + depth, + self.walked_depth(id), + "a widget's kept depth is not the one its ancestry says" + ); + depth + } + + /// What the kept depth is checked against, and the only thing that reads + /// the ancestry to find one. + fn walked_depth(&self, id: WidgetId) -> usize { let mut depth = 0; let mut at = Some(id); while let Some(id) = at { - #[cfg(feature = "layout-diagnostics")] - diag::bump(Counter::DepthSteps); at = self.active.get(&id).and_then(|active| active.parent); depth += 1; } @@ -714,6 +742,7 @@ impl UiRenderState { id, active.region, active.parent, + active.depth, active.parent_move, active.move_idx != active.parent_move, active.mask,