Move a resized drawing instead of redrawing it
A region is a fraction of the output plus an offset and the shader resolves it against the window every frame, so a resize already moves the whole drawing without the CPU. Wiping the tree and drawing it again was throwing that away. `Painter::output_size` and `px_size` now record that a widget read pixels, the way reading a child's size records a dependency on it, and a resize marks only those. In the `text` example that is the two wrapping paragraphs out of forty-odd widgets; everything else keeps its drawing and the window uniform puts it in the right place. Two defects the change surfaced, both of which made a resize land somewhere a cold start would not: `redraw` climbed to the highest reader of the changed widget and drew from there, trusting that draw to reach back down. It does not: an intermediate whose own box has not changed is reused as it stands and the draw stops there. Everything between the two is now marked as well, which is the only thing that stops the reuse. Not resize-specific -- `a_change_two_levels_under_its_reader_still_reaches_it` fails on the mutation path too. `mov` cannot stretch a drawing out of a box with no relative extent. `UiScalar::within` puts a part into such a box as a plain offset from its start, and `lerp_inv`'s divide-by-zero fallback then returns a rel of 0 rather than saying it cannot invert, so the remap silently leaves the drawing its old size. `OnResize::Scale` now only reuses across a length change when the old box had a relative extent. The underlying loss belongs to the position chain, which separates the drawn box from the offered one; until then this is the honest predicate. Checked: fmt, clippy and 33 tests. `tabs` (with the image replay), `view`, `minimal` still byte-identical to `upstream/main`, and `text` unchanged at 1920x1200 and 900x1200. Driven live under the GPU as well: started at 1920x1200, resized to 900x1200 and back through sway, and each screenshot matches a cold start at that size byte for byte.
This commit is contained in:
1 parent
9520996623
commit
984f482a7f
5 files changed
+176
-17
No files matched your search
@@ -163,3 +163,90 @@ fn a_parent_that_only_read_a_hint_relays_out_when_the_hint_changes() {
|
||||
|
||||
assert_corners!(h, inner, (0, 0), (400, 120));
|
||||
}
|
||||
|
||||
/// Reads the output's size, which nothing but its own draw can put right.
|
||||
struct ReadsOutput {
|
||||
draws: Rc<Cell<usize>>,
|
||||
}
|
||||
|
||||
impl Widget for ReadsOutput {
|
||||
fn draw(&mut self, painter: &mut Painter) -> Size {
|
||||
self.draws.set(self.draws.get() + 1);
|
||||
Size::abs(painter.output_size() / 4.0)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_resize_does_not_redraw_what_the_shader_can_move() {
|
||||
let mut h = Harness::new((400, 200));
|
||||
let (leaf, draws) = counted(&mut h, Size::REST, OnResize::Redraw);
|
||||
h.set_root(leaf);
|
||||
let settled = draws.get();
|
||||
|
||||
h.resize((800, 100));
|
||||
assert!(h.needs_redraw());
|
||||
h.frame();
|
||||
|
||||
assert_eq!(
|
||||
draws.get(),
|
||||
settled,
|
||||
"its box is the same fraction of a different output"
|
||||
);
|
||||
assert_corners!(h, leaf, (0, 0), (800, 100));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_resize_redraws_what_read_the_output() {
|
||||
let mut h = Harness::new((400, 200));
|
||||
let draws = Rc::new(Cell::new(0));
|
||||
let leaf = ReadsOutput {
|
||||
draws: draws.clone(),
|
||||
}
|
||||
.add(&mut h.rsc);
|
||||
h.set_root(leaf);
|
||||
let settled = draws.get();
|
||||
|
||||
h.resize((800, 100));
|
||||
h.frame();
|
||||
|
||||
assert_eq!(draws.get(), settled + 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn narrowing_the_output_reflows_text_and_relays_out_around_it() {
|
||||
let mut h = Harness::new((600, 400));
|
||||
let para = wtext(
|
||||
"Wrapping shapes one source into as many lines as its container leaves \
|
||||
room for, so the height of a paragraph is an answer rather than a setting.",
|
||||
)
|
||||
.size(20)
|
||||
.wrap(true)
|
||||
.add(&mut h.rsc);
|
||||
let below = rect(Color::RED).add(&mut h.rsc);
|
||||
h.set_root((para, below).span(Dir::DOWN));
|
||||
let top = h.region(&below).expect("drew nothing").top_left.y;
|
||||
|
||||
h.resize((300, 400));
|
||||
h.frame();
|
||||
|
||||
let lower = h.region(&below).expect("drew nothing").top_left.y;
|
||||
assert!(lower > top, "same words, half the width: {top} -> {lower}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_change_two_levels_under_its_reader_still_reaches_it() {
|
||||
let mut h = Harness::new((400, 400));
|
||||
// Every wrapper up to the outer pad read the size below it, so the outer
|
||||
// pad is what draws again -- and the span it hands the box to is the same
|
||||
// size as before, which is what lets a draw reuse its way past the leaf.
|
||||
let (leaf, _) = counted(&mut h, Size::abs((100, 100).into()), OnResize::Redraw);
|
||||
let padded = leaf.pad(10).add(&mut h.rsc);
|
||||
let below = rect(Color::RED).add(&mut h.rsc);
|
||||
h.set_root((padded, below).span(Dir::DOWN).pad(12));
|
||||
assert_corners!(h, below, (12, 132), (388, 388));
|
||||
|
||||
h.rsc[leaf].size = Size::abs((100, 200).into());
|
||||
h.frame();
|
||||
|
||||
assert_corners!(h, below, (12, 232), (388, 388));
|
||||
}
|
||||
Reference in new issue
Block a user