Retire Remap: a translation shifts, and only a stretch needs a fraction
`Remap` existed to invert a composition, and a translation never needed one: shifting a box shifts everything composed into it by the same amount, because `lerp(s + d, e + d, t) == lerp(s, e, t) + d` on both channels. That holds whether or not the box has a relative extent, so the carry branch was answering a question it did not have to ask. So the decision is made once, before the walk, and neither relocation method branches. A translation is already one slot write. A change of length calls `UiRegion::stretch`, which re-expresses each part at its own fraction of the new box and needs `stretchable` -- a fixed length holds its parts as offsets from its start and keeps no fraction to stretch by. `Remap`, `UiScalar::outside`, `UiSpan::outside` and `LerpUtil::lerp_inv` are all gone with it. Nothing inverts a lerp any more: the one division is done against a denominator `stretchable` already established is not zero. What it gives up is the per-axis carry, so a box that changed length on one axis and not the other is redrawn where it used to be remapped. Counted: six of `tabs`'s fourteen relocations and five of `text`'s sixteen, and one extra redraw per frame on `replace_cost`'s 200 rows -- 354,310,889 instructions against 354,272,387, which is noise. Checked: fmt, clippy and 42 tests. `tabs` (with the image replay), `view`, `minimal` and `text` all still render byte-identical to `upstream/main`.
This commit is contained in:
1 parent
78a53b6bf6
commit
db1751fdfd
5 files changed
+85
-99
No files matched your search
+11
-11
@@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
ActiveData, Axis, DrawLayers, IdLike, MaskIdx, MoveIdx, Moves, OnResize, Painter, PixelRegion,
|
||||
Remap, Size, StrongWidget, UiRegion, UiRsc, WidgetId, Widgets,
|
||||
Size, StrongWidget, UiRegion, UiRsc, WidgetId, Widgets,
|
||||
util::{HashMap, HashSet, Vec2, forget_ref},
|
||||
};
|
||||
|
||||
@@ -214,11 +214,11 @@ impl UiRenderState {
|
||||
self.moves.set(slot, moved);
|
||||
return Some(size);
|
||||
}
|
||||
if !self.reusable(id, region, rsc) {
|
||||
if !self.reusable(id, region, rsc) || !old.stretchable() {
|
||||
return None;
|
||||
}
|
||||
// Its drawing stands, if the new box can be reached from the old one.
|
||||
self.mov(id, &Remap::new(old, region)?);
|
||||
// Its drawing stands, re-expressed as the same fractions of the box.
|
||||
self.stretch(id, old, region);
|
||||
Some(size)
|
||||
}
|
||||
|
||||
@@ -259,21 +259,21 @@ impl UiRenderState {
|
||||
})
|
||||
}
|
||||
|
||||
/// Rewrites a subtree's regions into a new box, for a change a slot
|
||||
/// cannot express. Every region it rewrites is a region some slot was a
|
||||
/// delta from, so those go back to zero.
|
||||
fn mov(&mut self, id: WidgetId, remap: &Remap) {
|
||||
/// Rewrites a subtree's regions as the same fractions of a new box, for a
|
||||
/// change of length that a slot cannot express. Every region it rewrites
|
||||
/// is a region some slot was a delta from, so those go back to zero.
|
||||
fn stretch(&mut self, id: WidgetId, from: UiRegion, to: UiRegion) {
|
||||
let active = self.active.get_mut(&id).unwrap();
|
||||
for h in &active.primitives {
|
||||
let region = self.layers[h.layer].region_mut(h);
|
||||
*region = remap.apply(*region);
|
||||
*region = region.stretch(&from, &to);
|
||||
}
|
||||
active.region = remap.apply(active.region);
|
||||
active.region = active.region.stretch(&from, &to);
|
||||
self.moves.set(active.move_idx, Vec2::ZERO);
|
||||
// SAFETY: children cannot be recursive
|
||||
let children = unsafe { forget_ref(&active.children) };
|
||||
for child in children {
|
||||
self.mov(*child, remap);
|
||||
self.stretch(*child, from, to);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user