Read the marks rather than the queue to decide the walk is done
Review of the two commits above. The queue was the walk's only record of what was left, so a mark that reached `needs_redraw` without going through `mark` -- an `on_undraw` handler is the reachable one -- would have waited for the next frame. The set is read again once the queue drains, which is what the scan it replaced did for free. `pop_last` takes the deepest entry in one step rather than reading and then removing it. The rest is comments: nine lines shorter, and the arm that takes an ordinary ask said only what it does for a declared length. Unchanged by all of it: 109 suite and 20 core tests, the four fuzzer runs (100 seeds, 400 trees at depth 5, 1000 at depth 6, 2000 at depth 4), the five reference renders and the resized `tabs`, and every counter on the diagnostics rig. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
3bf22935ce
commit
34cafb6edc
3 files changed
+50
-49
No files matched your search
@@ -43,8 +43,8 @@ pub struct ActiveData {
|
|||||||
pub mask_region: Option<DrawRegion>,
|
pub mask_region: Option<DrawRegion>,
|
||||||
/// The children whose box is a part of this widget's extent rather than
|
/// The children whose box is a part of this widget's extent rather than
|
||||||
/// of its frame, and which part each was given. Moving the extent
|
/// of its frame, and which part each was given. Moving the extent
|
||||||
/// re-places them, so this widget's drawing does not have to depend on
|
/// re-places them through that part, so the drawing need not depend on
|
||||||
/// where its own drawing sits.
|
/// where it sits.
|
||||||
pub(crate) extent_children: Vec<(WidgetId, ExtentPlacement)>,
|
pub(crate) extent_children: Vec<(WidgetId, ExtentPlacement)>,
|
||||||
pub children: Vec<WidgetId>,
|
pub children: Vec<WidgetId>,
|
||||||
/// The children whose size this widget read while drawing.
|
/// The children whose size this widget read while drawing.
|
||||||
|
|||||||
+15
-21
@@ -185,12 +185,10 @@ impl<'a> Painter<'a> {
|
|||||||
/// resolves declared lengths and reports against that frame, then places
|
/// resolves declared lengths and reports against that frame, then places
|
||||||
/// its drawing by its own alignment.
|
/// its drawing by its own alignment.
|
||||||
///
|
///
|
||||||
/// `DrawRegion::Extent` gives a part of where this widget's own drawing
|
/// `DrawRegion::Extent` gives a part of where this widget's drawing sits
|
||||||
/// sits instead, which is what a container whose children belong inside
|
/// instead, for a container whose children belong inside that rather than
|
||||||
/// its drawing rather than inside the box it was offered wants. The
|
/// inside the box it was offered. The part is what is kept, so moving the
|
||||||
/// child's box then follows the extent without this widget's drawing
|
/// extent re-places the child rather than drawing this widget again.
|
||||||
/// depending on where that extent is, so moving it re-places the child
|
|
||||||
/// rather than drawing this widget again.
|
|
||||||
pub fn widget_within<'s, W: ?Sized>(
|
pub fn widget_within<'s, W: ?Sized>(
|
||||||
&'s mut self,
|
&'s mut self,
|
||||||
id: &'s StrongWidget<W>,
|
id: &'s StrongWidget<W>,
|
||||||
@@ -322,20 +320,18 @@ impl<'a> Painter<'a> {
|
|||||||
result.placement = Some(self.placement);
|
result.placement = Some(self.placement);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Its box is a part of this widget's extent, so both what
|
// Its box is a part of this widget's extent, so what it
|
||||||
// it was given and what it took of that are ranges on the
|
// holds for is a range on that extent and none of it a
|
||||||
// extent and none of them a range on the frame. That is
|
// range on the frame. Only the part's length reaches it,
|
||||||
// what lets this widget's drawing move without being made
|
// which is what lets the extent move without a redraw.
|
||||||
// again: only the part's length reaches the child, and
|
|
||||||
// where the part sits is re-placed rather than redrawn.
|
|
||||||
Some(ExtentPlacement::Within(part)) if declared[n].is_none() => {
|
Some(ExtentPlacement::Within(part)) if declared[n].is_none() => {
|
||||||
result.extent[n] = holds.frame[n]
|
result.extent[n] = holds.frame[n]
|
||||||
.and(holds.extent[n].through(chosen))
|
.and(holds.extent[n].through(chosen))
|
||||||
.through(part.axis(axis).len());
|
.through(part.axis(axis).len());
|
||||||
}
|
}
|
||||||
// A declared length is a length of this widget's frame
|
// Its box is a length of this widget's frame: an
|
||||||
// wherever the box it sits in came from, so what the child
|
// ordinary ask, or a declared length, which is that
|
||||||
// holds for is a range on the frame either way.
|
// length wherever the box it sits in came from.
|
||||||
_ => {
|
_ => {
|
||||||
result.frame[n] = holds.frame[n].through(local.axis(axis).len()).and(
|
result.frame[n] = holds.frame[n].through(local.axis(axis).len()).and(
|
||||||
holds.extent[n]
|
holds.extent[n]
|
||||||
@@ -349,12 +345,10 @@ impl<'a> Painter<'a> {
|
|||||||
};
|
};
|
||||||
self.under = self.under.and(in_parent(holds));
|
self.under = self.under.and(in_parent(holds));
|
||||||
let mut answer_holds = in_parent(answer_holds);
|
let mut answer_holds = in_parent(answer_holds);
|
||||||
// What it reports is a fraction of the box it was given, and that box
|
// What it reports is a fraction of the box it was given, which is a
|
||||||
// is a part of this widget's extent -- so the same fraction is a
|
// part of this widget's extent -- so the same fraction is a different
|
||||||
// different length once the extent is. Only the report: where the
|
// length once that extent is, and pixels are not. The answer only:
|
||||||
// extent moved without changing what it holds, the drawing under it
|
// the drawing this holds is re-placed rather than made again.
|
||||||
// is re-placed rather than made again, which is what the extent ask
|
|
||||||
// is for. Pixels come up unchanged and say nothing.
|
|
||||||
if matches!(extent, Some(ExtentPlacement::Within(_)))
|
if matches!(extent, Some(ExtentPlacement::Within(_)))
|
||||||
&& AXES.into_iter().any(|axis| {
|
&& AXES.into_iter().any(|axis| {
|
||||||
declared[axis as usize].is_none() && size.axis(axis).rel != crate::Rel::ZERO
|
declared[axis as usize].is_none() && size.axis(axis).rel != crate::Rel::ZERO
|
||||||
|
|||||||
+33
-26
@@ -70,8 +70,7 @@ pub struct UiRenderState {
|
|||||||
/// depths does not pick one up again at its own depth.
|
/// depths does not pick one up again at its own depth.
|
||||||
deferred: crate::util::HashSet<WidgetId>,
|
deferred: crate::util::HashSet<WidgetId>,
|
||||||
/// What the walk has left to settle, deepest last. Ordered rather than
|
/// What the walk has left to settle, deepest last. Ordered rather than
|
||||||
/// searched: the set is scanned once a frame, and every mark made while
|
/// searched for, so finding the next one is not a pass over the marks.
|
||||||
/// the walk runs puts itself in place.
|
|
||||||
pending: std::collections::BTreeSet<(usize, WidgetId)>,
|
pending: std::collections::BTreeSet<(usize, WidgetId)>,
|
||||||
pub moves: Moves,
|
pub moves: Moves,
|
||||||
}
|
}
|
||||||
@@ -999,37 +998,45 @@ impl UiRenderState {
|
|||||||
// something below is about to change it -- which is the whole class
|
// something below is about to change it -- which is the whole class
|
||||||
// of defect where a widget settles inside its parent's draw, clears
|
// of defect where a widget settles inside its parent's draw, clears
|
||||||
// its mark there, and tells nobody its answer moved.
|
// its mark there, and tells nobody its answer moved.
|
||||||
let marked: Vec<WidgetId> = rsc.widgets().needs_redraw.iter().copied().collect();
|
// The queue is that set, ordered: a mark made while the walk runs
|
||||||
for id in marked {
|
// queues itself through `mark`. What ends the walk is still the set
|
||||||
let depth = self.depth(id);
|
// being spent, not the queue, so a mark that reached it another way
|
||||||
self.pending.insert((depth, id));
|
// cannot be left for the next frame.
|
||||||
}
|
loop {
|
||||||
while let Some(&(depth, id)) = self.pending.last() {
|
for &id in rsc.widgets().needs_redraw.iter() {
|
||||||
self.pending.remove(&(depth, id));
|
if !self.deferred.contains(&id) {
|
||||||
// A widget settled inside an ancestor's draw, or deferred to one,
|
let depth = self.depth(id);
|
||||||
// is left here by the mark that queued it.
|
self.pending.insert((depth, id));
|
||||||
if self.deferred.contains(&id) || !rsc.widgets().needs_redraw.contains(&id) {
|
}
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
// A subtree that changed hands takes its descendants' depths with
|
if self.pending.is_empty() {
|
||||||
// it, so an entry queued before that move names the wrong one.
|
break;
|
||||||
let now = self.depth(id);
|
|
||||||
if now != depth {
|
|
||||||
self.pending.insert((now, id));
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
#[cfg(feature = "layout-diagnostics")]
|
while let Some((depth, id)) = self.pending.pop_last() {
|
||||||
diag::bump(Counter::QueuePops);
|
// Settled inside an ancestor's draw, or deferred to one,
|
||||||
if !self.redraw(id, rsc) {
|
// since the mark that queued it.
|
||||||
self.deferred.insert(id);
|
if self.deferred.contains(&id) || !rsc.widgets().needs_redraw.contains(&id) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// A subtree that changed hands takes its descendants' depths
|
||||||
|
// with it, so an entry queued before that move names the
|
||||||
|
// depth it had under the parent it left.
|
||||||
|
let now = self.depth(id);
|
||||||
|
if now != depth {
|
||||||
|
self.pending.insert((now, id));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
#[cfg(feature = "layout-diagnostics")]
|
||||||
|
diag::bump(Counter::QueuePops);
|
||||||
|
if !self.redraw(id, rsc) {
|
||||||
|
self.deferred.insert(id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.deferred.clear();
|
self.deferred.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Marks a widget for the walk to settle. Every mark made while a frame
|
/// Marks a widget for the walk to settle, and queues it at its depth.
|
||||||
/// is being laid out goes through here, so the queue holds what the set
|
|
||||||
/// holds without being searched again.
|
|
||||||
fn mark(&mut self, id: WidgetId, widgets: &mut Widgets) {
|
fn mark(&mut self, id: WidgetId, widgets: &mut Widgets) {
|
||||||
if widgets.needs_redraw.insert(id) && !self.deferred.contains(&id) {
|
if widgets.needs_redraw.insert(id) && !self.deferred.contains(&id) {
|
||||||
let depth = self.depth(id);
|
let depth = self.depth(id);
|
||||||
|
|||||||
Reference in new issue
Block a user