Retain layout sizes by pixel axis
This commit is contained in:
1 parent
82fa6c1123
commit
b1b3eca1c0
11 files changed
+371
-69
No files matched your search
+14
-17
@@ -17,20 +17,11 @@ use iris::prelude::*;
|
||||
use iris::random::{Edits, Lens, Rng, SpanEdit, Tree, grow};
|
||||
|
||||
const DEPTH: usize = 4;
|
||||
const SEEDS: [u64; 6] = [1, 2, 3, 5, 8, 13];
|
||||
const REGION_ULPS: u32 = 4;
|
||||
|
||||
fn ordered_bits(value: f32) -> u32 {
|
||||
const SIGN: u32 = 1 << 31;
|
||||
let bits = value.to_bits();
|
||||
match bits & SIGN {
|
||||
0 => bits | SIGN,
|
||||
_ => !bits,
|
||||
}
|
||||
}
|
||||
const SEEDS: [u64; 7] = [1, 2, 3, 5, 8, 13, 98];
|
||||
const REGION_EPSILON_PX: f32 = 0.05;
|
||||
|
||||
fn same_coordinate(got: f32, want: f32) -> bool {
|
||||
got == want || ordered_bits(got).abs_diff(ordered_bits(want)) <= REGION_ULPS
|
||||
(got - want).abs() <= REGION_EPSILON_PX
|
||||
}
|
||||
|
||||
fn same_region(got: Option<PixelRegion>, want: Option<PixelRegion>) -> bool {
|
||||
@@ -165,9 +156,10 @@ fn assert_same(seed: u64, what: &str, warm: (&Harness, &Tree), cold: (&Harness,
|
||||
for (i, (&w, &c)) in wt.ids.iter().zip(&ct.ids).enumerate() {
|
||||
let (got, want) = (wh.region(&w), ch.region(&c));
|
||||
drawn += usize::from(got.is_some());
|
||||
// Equivalent composition orders can differ by a few f32 ULPs. Bound
|
||||
// that representation drift directly, while whether a widget drew
|
||||
// remains exact.
|
||||
// This oracle cares where rasterization lands, not whether equivalent
|
||||
// arithmetic produced the same f32. Keep the tolerance to one
|
||||
// twentieth of a physical pixel, while whether a widget drew remains
|
||||
// exact.
|
||||
if same_region(got, want) {
|
||||
continue;
|
||||
}
|
||||
@@ -327,9 +319,14 @@ fn adding_and_removing_span_children_lands_where_growing_it_that_way_would() {
|
||||
/// same defect, and it wants fixing where the two draws meet -- LAYOUT.md §4 --
|
||||
/// rather than anywhere in the chain.
|
||||
#[test]
|
||||
#[ignore = "a hundred seeds, rather than the six the others check"]
|
||||
#[ignore = "a hundred seeds, rather than the seven the others check"]
|
||||
fn a_long_run_of_seeds_agrees() {
|
||||
for seed in 1..=100 {
|
||||
let seeds = std::env::var("IRIS_GENERATED_SEED")
|
||||
.ok()
|
||||
.and_then(|seed| seed.parse().ok())
|
||||
.map(|seed| seed..=seed)
|
||||
.unwrap_or(1..=100);
|
||||
for seed in seeds {
|
||||
changed_size(seed);
|
||||
resized(seed);
|
||||
resized_then_changed(seed);
|
||||
|
||||
@@ -75,6 +75,22 @@ fn env<T: std::str::FromStr>(name: &str, fallback: T) -> T {
|
||||
.unwrap_or(fallback)
|
||||
}
|
||||
|
||||
#[cfg(feature = "layout-diagnostics")]
|
||||
fn trace_selected(tree: &Tree) {
|
||||
let Ok(value) = std::env::var("IRIS_TRACE_INDEX") else {
|
||||
return;
|
||||
};
|
||||
let index = value
|
||||
.parse::<usize>()
|
||||
.expect("IRIS_TRACE_INDEX must be a tree.ids index");
|
||||
let id = tree.ids[index];
|
||||
iris::core::layout_diagnostics::trace_widget(id);
|
||||
println!("tracing tree.ids[{index}] = {id:?}");
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "layout-diagnostics"))]
|
||||
fn trace_selected(_: &Tree) {}
|
||||
|
||||
fn warm(seed: u64, depth: usize) -> (Harness, Tree) {
|
||||
let mut harness = Harness::new(OUTPUT);
|
||||
let (root, tree) = grow(&mut harness.rsc, seed, depth, &Edits::default());
|
||||
@@ -157,6 +173,7 @@ fn layout_cost() {
|
||||
"fixture: seed {seed}, depth {depth}, {} widgets",
|
||||
tree.ids.len()
|
||||
);
|
||||
trace_selected(&tree);
|
||||
#[cfg(feature = "layout-diagnostics")]
|
||||
let _ = iris::core::layout_diagnostics::take();
|
||||
run("cold", 1, &mut harness, |_, _| {});
|
||||
@@ -165,6 +182,7 @@ fn layout_cost() {
|
||||
|
||||
if selected("repaint") {
|
||||
let (mut harness, tree) = warm(seed, depth);
|
||||
trace_selected(&tree);
|
||||
let leaf = tree.ids[0];
|
||||
run("repaint", frames, &mut harness, move |harness, _| {
|
||||
let _ = harness.rsc.widgets_mut().get_dyn_mut(leaf);
|
||||
@@ -173,6 +191,7 @@ fn layout_cost() {
|
||||
|
||||
if selected("size") {
|
||||
let (mut harness, tree) = warm(seed, depth);
|
||||
trace_selected(&tree);
|
||||
let sized = tree.sized[0];
|
||||
run("size", frames, &mut harness, move |harness, frame| {
|
||||
harness.rsc[sized].x = Some(Len::abs(100.0 + (frame % 2) as f32 * 40.0));
|
||||
@@ -181,6 +200,7 @@ fn layout_cost() {
|
||||
|
||||
if selected("scroll") {
|
||||
let (mut harness, tree) = warm(seed, depth);
|
||||
trace_selected(&tree);
|
||||
let scroll = tree.scrolls[0];
|
||||
run("scroll", frames, &mut harness, move |harness, frame| {
|
||||
harness.rsc[scroll].scroll(if frame % 2 == 0 { 12.0 } else { -12.0 });
|
||||
@@ -189,8 +209,9 @@ fn layout_cost() {
|
||||
|
||||
if selected("resize") {
|
||||
let (mut harness, tree) = warm(seed, depth);
|
||||
trace_selected(&tree);
|
||||
run("resize", frames, &mut harness, |harness, frame| {
|
||||
harness.resize((OUTPUT.0 - (frame % 2) as f32 * 8.0, OUTPUT.1));
|
||||
harness.resize((OUTPUT.0 - ((frame + 1) % 2) as f32 * 8.0, OUTPUT.1));
|
||||
});
|
||||
drop(tree);
|
||||
}
|
||||
|
||||
@@ -191,6 +191,17 @@ impl Widget for ReadsOutput {
|
||||
}
|
||||
}
|
||||
|
||||
struct ReadsWidth {
|
||||
draws: Rc<Cell<usize>>,
|
||||
}
|
||||
|
||||
impl Widget for ReadsWidth {
|
||||
fn draw(&mut self, painter: &mut Painter) -> Size {
|
||||
self.draws.set(self.draws.get() + 1);
|
||||
Size::abs((painter.output_len(Axis::X) / 4.0, 20.0).into())
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_resize_does_not_redraw_what_the_shader_can_move() {
|
||||
let mut h = Harness::new((400, 200));
|
||||
@@ -227,6 +238,65 @@ fn a_resize_redraws_what_read_the_output() {
|
||||
assert_eq!(draws.get(), settled + 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_resize_only_redraws_read_output_axes() {
|
||||
let mut h = Harness::new((400, 200));
|
||||
let draws = Rc::new(Cell::new(0));
|
||||
let leaf = ReadsWidth {
|
||||
draws: draws.clone(),
|
||||
}
|
||||
.add(&mut h.rsc);
|
||||
h.set_root(leaf);
|
||||
let settled = draws.get();
|
||||
|
||||
h.resize((400, 300));
|
||||
h.frame();
|
||||
assert_eq!(draws.get(), settled, "height was never read");
|
||||
|
||||
h.resize((800, 300));
|
||||
h.frame();
|
||||
assert_eq!(draws.get(), settled + 1, "width changes its answer");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn subpixel_resize_changes_accumulate_from_the_last_layout() {
|
||||
let mut h = Harness::new((400, 200));
|
||||
let draws = Rc::new(Cell::new(0));
|
||||
let leaf = ReadsWidth {
|
||||
draws: draws.clone(),
|
||||
}
|
||||
.add(&mut h.rsc);
|
||||
h.set_root(leaf);
|
||||
let settled = draws.get();
|
||||
|
||||
for width in [400.02, 400.04, 400.05] {
|
||||
h.resize((width, 200.0));
|
||||
h.frame();
|
||||
assert_eq!(draws.get(), settled);
|
||||
}
|
||||
|
||||
h.resize((400.06, 200.0));
|
||||
h.frame();
|
||||
assert_eq!(draws.get(), settled + 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn subpixel_box_changes_accumulate_from_the_last_draw() {
|
||||
let mut h = Harness::new((400, 200));
|
||||
let (first, draws, _) = pair(&mut h, OnResize::Redraw);
|
||||
let settled = draws.get();
|
||||
|
||||
for width in [100.02, 100.04, 100.05] {
|
||||
h.rsc[first].size.x = Len::abs(width);
|
||||
h.frame();
|
||||
assert_eq!(draws.get(), settled);
|
||||
}
|
||||
|
||||
h.rsc[first].size.x = Len::abs(100.06);
|
||||
h.frame();
|
||||
assert_eq!(draws.get(), settled + 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reporting_the_same_output_size_does_not_start_a_resize() {
|
||||
let mut h = Harness::new((400, 200));
|
||||
|
||||
Reference in new issue
Block a user