Record that picking any dirty widget does not work, and what it costs
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
592cc808ca
commit
d0c80a2037
1 file changed
+36
@@ -373,6 +373,42 @@ holds a *drawing* keyed on what produced it, and the widget still draws. The
|
|||||||
five reference renders and the resize render are byte-identical, and the
|
five reference renders and the resize render are byte-identical, and the
|
||||||
sweep passes.
|
sweep passes.
|
||||||
|
|
||||||
|
**Picking any dirty widget instead of the deepest does not work, and the
|
||||||
|
generated cases say so.** Tried on 2026-09-14: take whatever the dirty set
|
||||||
|
yields first, and let `redraw`'s existing climb to the highest size reader do
|
||||||
|
the ordering -- then also climbing to an ancestor that is itself dirty, since
|
||||||
|
drawing that redraws this one anyway. Both fail
|
||||||
|
`a_changed_size_lands_where_growing_it_that_way_would` at seed 1, 24 widgets
|
||||||
|
wrong, a subtree keeping a 317 px width where a cold tree has 147.
|
||||||
|
|
||||||
|
What the trace says, and where it stops: with the deepest first, seed 1 settles
|
||||||
|
in 50 draws and the same `SetSize` is drawn three times as its box is decided
|
||||||
|
and redecided above it. Picking any settles in 12, and two of the three edited
|
||||||
|
`SetSize` widgets are never popped at all -- they are drawn inside a `Span`'s
|
||||||
|
pass, which consumes their marks. Nothing in the climb misfires; no escalation
|
||||||
|
happens in this scenario at all, so the difference is only which widgets are
|
||||||
|
drawn as the root of a pass and which inside someone else's. **Why that changes
|
||||||
|
the answer is not yet pinned down**, and it is the thing to find out before
|
||||||
|
changing the order again.
|
||||||
|
|
||||||
|
So the order is load-bearing and the cost is the scan rather than the order.
|
||||||
|
It splits: of the 24.5%, about 15 points are the depth walk (removing it
|
||||||
|
entirely takes 16.5B instructions to 14.0B) and about 9 are iterating the
|
||||||
|
`HashSet` itself, which walks by capacity rather than by length. Three ways
|
||||||
|
out, none of them tried yet:
|
||||||
|
|
||||||
|
- **Keep the depth on `ActiveData`.** `draw_inner` knows the parent, whose
|
||||||
|
depth is in its own entry, so this is O(1) to read and the order is
|
||||||
|
unchanged. The risk is a second source of truth: a widget whose ancestor is
|
||||||
|
re-placed under a different parent without the widget itself being redrawn
|
||||||
|
would hold a stale depth.
|
||||||
|
- **Bucket the dirty set by depth.** Exact, and the depth is only needed when
|
||||||
|
an id is inserted -- which inside the loop is always the parent of a widget
|
||||||
|
whose depth is already known, so no walk is needed there.
|
||||||
|
- **Process in rounds**: sort once, drain, re-sort what is left. Cheapest to
|
||||||
|
write and *not* the same order -- a widget marked during a round can be
|
||||||
|
deeper than what remains of it.
|
||||||
|
|
||||||
**A frame that dirties many widgets at once was not being checked, and it is
|
**A frame that dirties many widgets at once was not being checked, and it is
|
||||||
where the settle order shows.** `77bb75e` adds two generated cases -- every
|
where the settle order shows.** `77bb75e` adds two generated cases -- every
|
||||||
declared size changing together, and a spread of widgets marked for redraw
|
declared size changing together, and a spread of widgets marked for redraw
|
||||||
|
|||||||
Reference in new issue
Block a user