Carry the composed box down the draw, rather than walking back up for it

Every widget that reads its box in pixels was making `Moves` compose its
slot's chain again, a mean of 2.8 levels, about eight hundred times a frame.
A draw already descends past every one of those entries on its way in, so
`DrawInfo` carries what the slot composes to and `draw_at` steps it one box
further -- which is a select where it was a walk. `Moves::size_of` and
`compose` are left for `redraw`, which starts mid-tree with nothing above it
in flight.

Measured on the fixed-shape fixture, seed 1 depth 8, 500 frames of `many`,
medians of 25 runs, twenty-five work counters identical throughout:

| | instructions | cycles |
| --- | ---: | ---: |
| `d21a215`, before exact composition | 1,908M | 760M |
| `45a7176`, composing on the fine grid | 1,880M | 755M |
| this | **1,840M** | **735M** |

So exact composition ends up 3.6% fewer instructions and 3.3% fewer cycles
than the rounding-per-level walk it replaced, and the widening it needed was
paid for twice over by not doing the walk.

`Holds::through`'s allowance does not move: two half steps is where shrinker
seed 220 pins it, not where the arithmetic does. `Painter` still composes a
child's region into its own on the grid before asking for it in pixels, which
is the last narrow step in that path; taking it out needs the child's region
as its parent stated it, which `draw_inner` is not handed.

Checked: fmt, clippy, 83 suite tests, 17 core unit tests, the release oracle
at 100 seeds and at 1000 seeds of depth 6, all fifteen shrinker cases at 400
seeds of depth 5, and `tabs`, `view`, `minimal`, `text`, `random` and the tab
replay byte-identical at 1920x1200 against `45a7176`.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
iris-aiandClaude Opus 5 committed 2026-09-16 21:49:25 -04:00
1 parent 45a717695b
commit 5f16617511
3 files changed
+92 -12

No files matched your search

+54
View File
@@ -109,6 +109,44 @@ impl WideSpan {
}
}
impl WideSpan {
/// A box given as a part of this one: the same composition carried one
/// level further, with the part on the ordinary grid and the frame it
/// lands in already on the fine one. That is the way round a draw
/// descends -- a widget states its child's box as a part of its own --
/// so composing this way never puts an intermediate back on the grid.
pub const fn select(self, part: &UiSpan) -> Self {
Self {
start: self.end_at(part.start),
end: self.end_at(part.end),
}
}
/// How long such a part is, in half the multiplies its two ends cost:
/// where this box sits falls out of the difference.
pub const fn select_len(self, part: &UiSpan) -> WideLen {
let len = part.len();
let rel_span = (self.end.rel - self.start.rel) as i128;
let px_span = (self.end.px - self.start.px) >> PX_GAIN;
WideLen {
rel: ((rel_span * len.rel.raw() as i128) >> REL_SHIFT) as i64,
px: ((len.px.raw() as i64) << PX_GAIN)
+ ((px_span * len.rel.raw() as i64) >> (REL_SHIFT - PX_GAIN)),
}
}
const fn end_at(self, at: Len) -> WideLen {
let rel_span = (self.end.rel - self.start.rel) as i128;
let px_span = (self.end.px - self.start.px) >> PX_GAIN;
WideLen {
rel: self.start.rel + ((rel_span * at.rel.raw() as i128) >> REL_SHIFT) as i64,
px: self.start.px
+ ((at.px.raw() as i64) << PX_GAIN)
+ ((px_span * at.rel.raw() as i64) >> (REL_SHIFT - PX_GAIN)),
}
}
}
/// How long a box is on each axis, part-way through a composition. What
/// reads a box in pixels almost always wants this and not where it sits.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
@@ -144,6 +182,22 @@ pub struct WideRegion {
}
impl WideRegion {
/// A box given as a part of this one, on both axes.
pub const fn select(self, part: &UiRegion) -> Self {
Self {
x: self.x.select(&part.x),
y: self.y.select(&part.y),
}
}
/// How big such a part is, which is what reads a box in pixels.
pub const fn select_size(self, part: &UiRegion) -> WideSize {
WideSize {
x: self.x.select_len(&part.x),
y: self.y.select_len(&part.y),
}
}
pub const fn of(region: UiRegion) -> Self {
Self {
x: WideSpan::of(region.x),