Pin that a moved subtree does not drift from a cold layout
A move rewrites a retained subtree's stored regions, and those stores are the only record of where it is. So a move that works from the last answer integrates its own rounding with nothing to correct it, while one that re-expresses each part as the same fraction of the new box is anchored to that box and cannot. Nothing was checking which of those `try_reuse` does. Replacing the fraction with an offset added to both endpoints -- which is cheaper, and looks like it should be exact for a translation -- shortens this fixture's row by 0.071 over 20,000 moves and by 0.712 over 200,000, growing with the count rather than settling. That is five minutes of scrolling at 60Hz to pass the 0.05 physical pixels layout treats as the same place, and it keeps going. Placing the far end from the near one instead of offsetting both leaves 0.069, since the length is re-derived from the endpoints either way. The existing warm-against-cold checks did not reach it: the generated oracle compares within 0.05, and `unsettled.rs` compares exactly but only over a handful of frames, where the drift is still 6e-5.
This commit is contained in:
1 parent
71c9c39523
commit
0283c9d6c7
1 file changed
+45
@@ -0,0 +1,45 @@
|
||||
//! What a retained drawing costs in accuracy when it is moved instead of made
|
||||
//! again. A subtree's stored regions are the only record of where it is, so a
|
||||
//! move that works from the last answer rather than from the box it is now in
|
||||
//! integrates its own rounding, and nothing later recomputes it. Re-expressing
|
||||
//! each part as the same fraction of the new box is what keeps a long-lived
|
||||
//! layout on the one a cold start produces.
|
||||
|
||||
use iris::harness::Harness;
|
||||
use iris::prelude::*;
|
||||
|
||||
/// A row of a fixed height under a bar, so changing the bar's height moves the
|
||||
/// row without changing the box it is given: the move path, repeatedly.
|
||||
fn plant(h: &mut Harness, bar_height: f32) -> (WeakWidget<SetSize>, WeakWidget<Rect>) {
|
||||
let bar = rect(Color::RED).height(bar_height).add(&mut h.rsc);
|
||||
let inner = rect(Color::BLUE).add(&mut h.rsc);
|
||||
let row = (inner, rect(Color::GREEN)).span(Dir::RIGHT).height(100);
|
||||
h.set_root((bar, row).span(Dir::DOWN));
|
||||
(bar, inner)
|
||||
}
|
||||
|
||||
/// Enough moves to pass the 0.05 physical pixels layout treats as the same
|
||||
/// place, for a move that adds an offset to the last answer. Measured on this
|
||||
/// fixture on 2026-09-15: adding the offset to both ends of a span shortened
|
||||
/// the row by 0.071 over this many moves and by 0.712 over ten times as many,
|
||||
/// growing with the count rather than settling. Placing the far end from the
|
||||
/// near one instead left 0.069, because the length is re-derived either way.
|
||||
const MOVES: usize = 20_000;
|
||||
|
||||
#[test]
|
||||
fn a_subtree_moved_many_times_stays_where_a_cold_layout_puts_it() {
|
||||
let mut warm = Harness::new((640, 900));
|
||||
let (bar, inner) = plant(&mut warm, 40.0);
|
||||
let mut height = 40.0;
|
||||
for step in 0..MOVES {
|
||||
height = 40.0 + (step % 300) as f32 * 0.37;
|
||||
warm.rsc[bar].y = Some(Len::px(height));
|
||||
warm.frame();
|
||||
}
|
||||
|
||||
let mut cold = Harness::new((640, 900));
|
||||
let (_, cold_inner) = plant(&mut cold, height);
|
||||
cold.frame();
|
||||
|
||||
assert_eq!(warm.region(&inner), cold.region(&cold_inner));
|
||||
}
|
||||
Reference in new issue
Block a user