Work a move out once for the subtree, not once for each part

`RegionRemap` re-derived the same things for every scalar of every part of a
moving subtree: the extent it divides by, whether the box only moved, whether
it spans the whole of its parent's, and the two ends of each `lerp`. All of
them are the same for the whole walk, because the walk is one box moving into
one other box. They are worked out once in `RegionRemap::new` now, as an
`AxisRemap` per axis that is either a translation or a scale.

Identical arithmetic in the same order, so the answers are unchanged: 500
frames of the `many` phase went from 1,886,328,855 instructions to
1,815,666,327, 3.8% fewer, and `tabs`, `text` and `random` are byte-identical
at 1920x1200.

Cycles moved 0.8%, which is the finding worth keeping: the surrounding
arithmetic was never the cost. The `i64` division is, and it is still there.

Checked: fmt, clippy, 105 tests, all five shrinker cases at 300 seeds.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
iris-aiandClaude Opus 5 committed 2026-09-16 13:46:23 -04:00
1 parent 490918b789
commit cb1bba4682
1 file changed
+85 -38
+85 -38
View File
@@ -3,7 +3,7 @@ use crate::layout_diagnostics::{self as diag, Counter, ReuseOutcome, TimerKind};
use crate::ui::painter::{declared_box, declared_lens, placed_box}; use crate::ui::painter::{declared_box, declared_lens, placed_box};
use crate::{ use crate::{
ActiveData, Axis, DrawLayers, Holds, IdLike, LayoutLen, Len, MaskIdx, MoveIdx, Moves, Painter, ActiveData, Axis, DrawLayers, Holds, IdLike, LayoutLen, Len, MaskIdx, MoveIdx, Moves, Painter,
PixelRegion, PxVec2, RegionAlign, Rel, Size, StrongWidget, UiRegion, UiRsc, UiSpan, Weight, PixelRegion, Px, PxVec2, RegionAlign, Rel, Size, StrongWidget, UiRegion, UiRsc, UiSpan, Weight,
WidgetId, Widgets, WidgetId, Widgets,
util::{HashMap, Vec2}, util::{HashMap, Vec2},
}; };
@@ -632,7 +632,7 @@ impl UiRenderState {
self.moves.set(slot, region); self.moves.set(slot, region);
} else { } else {
let remap = RegionRemap::new(old_region, region)?; let remap = RegionRemap::new(old_region, region)?;
self.remap_subtree(id, remap, info.parent_move, mask, rsc); self.remap_subtree(id, &remap, info.parent_move, mask, rsc);
} }
} }
let active = self.active.get_mut(&id).unwrap(); let active = self.active.get_mut(&id).unwrap();
@@ -668,7 +668,7 @@ impl UiRenderState {
fn remap_subtree( fn remap_subtree(
&mut self, &mut self,
id: WidgetId, id: WidgetId,
remap: RegionRemap, remap: &RegionRemap,
parent_move: MoveIdx, parent_move: MoveIdx,
inherited_mask: MaskIdx, inherited_mask: MaskIdx,
rsc: &mut dyn UiRsc, rsc: &mut dyn UiRsc,
@@ -1045,55 +1045,102 @@ fn same_pixel_region(a: PixelRegion, b: PixelRegion) -> bool {
/// source extent can be translated but cannot recover fractions for a resize. /// source extent can be translated but cannot recover fractions for a resize.
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
struct RegionRemap { struct RegionRemap {
from: UiRegion, axes: [AxisRemap; 2],
to: UiRegion, }
/// Moving one axis of a box into another, worked out once for the whole
/// subtree that moves with it. Every part of that subtree is divided by the
/// same extent and placed between the same two ends, so the ends and the
/// divisor belong here rather than in each part's arithmetic.
#[derive(Clone, Copy)]
enum AxisRemap {
/// A box that kept its length carries its parts by moving them, which is
/// exact. Dividing to find the fraction each sits at and multiplying to
/// place it again are two roundings, and they land a step from where
/// growing the tree that way does.
Translate(Len),
/// A box that changed length has to re-express each part as a fraction of
/// the new one, which is what a part of a box means.
Scale(AxisScale),
}
#[derive(Clone, Copy)]
struct AxisScale {
/// What the fraction is measured from, and what divides it. `whole` is
/// the common case of a box spanning the whole of its parent's, where
/// dividing by one is the expensive way to write a subtraction.
start_rel: Rel,
extent: Rel,
whole: bool,
/// `lerp` is `a + (b - a) * fraction`, and both ends are the same for
/// every part, so each is kept as its near end and its span.
from_px: Px,
from_px_span: Px,
to_rel: Rel,
to_rel_span: Rel,
to_px: Px,
to_px_span: Px,
} }
impl RegionRemap { impl RegionRemap {
fn new(from: UiRegion, to: UiRegion) -> Option<Self> { fn new(from: UiRegion, to: UiRegion) -> Option<Self> {
AXES.into_iter() Some(Self {
.all(|axis| { axes: [AxisRemap::new(from.x, to.x)?, AxisRemap::new(from.y, to.y)?],
let from = from.axis(axis); })
from.start.rel != from.end.rel || from.len() == to.axis(axis).len()
})
.then_some(Self { from, to })
} }
fn apply(self, region: UiRegion) -> UiRegion { fn apply(&self, region: UiRegion) -> UiRegion {
UiRegion { UiRegion {
x: self.apply_span(region.x, self.from.x, self.to.x), x: self.axes[0].apply_span(region.x),
y: self.apply_span(region.y, self.from.y, self.to.y), y: self.axes[1].apply_span(region.y),
} }
} }
}
fn apply_span(self, span: UiSpan, from: UiSpan, to: UiSpan) -> UiSpan { impl AxisRemap {
UiSpan { fn new(from: UiSpan, to: UiSpan) -> Option<Self> {
start: self.apply_scalar(span.start, from, to), if from.len() == to.len() {
end: self.apply_scalar(span.end, from, to), return Some(Self::Translate(to.start - from.start));
} }
}
fn apply_scalar(self, scalar: Len, from: UiSpan, to: UiSpan) -> Len {
let extent = from.end.rel - from.start.rel; let extent = from.end.rel - from.start.rel;
// A box that only moved, or that has no relative extent to divide, // Without a relative extent there is no fraction to re-express: a box
// carries its parts by moving them, which is exact. Dividing to find // of fixed length cannot say where its parts sit in a different one.
// the fraction each sits at and multiplying to place it again are two if extent == Rel::ZERO {
// roundings, and they land a step from where growing the tree that return None;
// way does. Where the box changed length there is nothing else to do,
// and the fraction is what a part means.
if from.len() == to.len() || extent == Rel::ZERO {
return scalar + to.start - from.start;
} }
// A box that spans the whole of its parent's is the common one, and Some(Self::Scale(AxisScale {
// dividing by one is the expensive way to write a subtraction. start_rel: from.start.rel,
let offset = scalar.rel - from.start.rel; extent,
let fraction = match extent == Rel::ONE { whole: extent == Rel::ONE,
true => offset, from_px: from.start.px,
false => offset / extent, from_px_span: from.end.px - from.start.px,
to_rel: to.start.rel,
to_rel_span: to.end.rel - to.start.rel,
to_px: to.start.px,
to_px_span: to.end.px - to.start.px,
}))
}
fn apply_span(&self, span: UiSpan) -> UiSpan {
UiSpan {
start: self.apply_scalar(span.start),
end: self.apply_scalar(span.end),
}
}
fn apply_scalar(&self, scalar: Len) -> Len {
let scale = match self {
Self::Translate(by) => return scalar + *by,
Self::Scale(scale) => scale,
}; };
let from_px = fraction.lerp(from.start.px, from.end.px); let offset = scalar.rel - scale.start_rel;
let to_rel = fraction.lerp(to.start.rel, to.end.rel); let fraction = match scale.whole {
let to_px = fraction.lerp(to.start.px, to.end.px); true => offset,
false => offset / scale.extent,
};
let from_px = scale.from_px + scale.from_px_span.mul(fraction);
let to_rel = scale.to_rel + scale.to_rel_span.mul(fraction);
let to_px = scale.to_px + scale.to_px_span.mul(fraction);
Len::from_parts(to_rel, scalar.px - from_px + to_px) Len::from_parts(to_rel, scalar.px - from_px + to_px)
} }
} }