Answer "have I asked this child?" in one read
A container's draw asked it once per child by searching the list of children it had added so far, and four other per-child steps searched a list too, so one draw cost the square of its children: 70% of a 1,600-child redraw was those searches. A draw takes a DrawId and leaves it on every widget it asks about; one note per widget is enough because the handle a container holds a child by cannot be cloned. tests/children_cost.rs is the rig that shows it, and it is the only one here that varies width: 3.680 ms to 0.811 ms at 1,600 children, and flat per child. Beside it, the rest of the fourteenth sweep of #19: a mask's rectangle resolved once per fragment instead of once per instance, which takes the storage buffers out of the fragment stage and is 8.8x on a screenful of deeply nested clips; TextBuffer::shape copying its attrs before the check that would not need them, which allocated once per named-family text per frame; a should_panic test on a debug assertion that made cargo test --release fail; Fixed::div, reached only by its own test; Moves::remove re-uploading an array it cannot have changed; and two comments the branch itself falsified. docs/LAYOUT_LOG.md has all eight with their measurements, the five things looked at and left, and what was verified.
This commit is contained in:
1 parent
cbccfb600a
commit
97fca76108
17 files changed
+588
-149
No files matched your search
@@ -78,3 +78,47 @@ fn unchanged_tree_reuses_layout_storage() {
|
||||
assert_eq!(allocations, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/// A text drawn again at the width it already has places no glyphs and shapes
|
||||
/// nothing, so the frame costs nothing at all -- which is what the shaping
|
||||
/// cache is for, and a copy of the attrs made to ask it undid for any text
|
||||
/// naming its font family.
|
||||
///
|
||||
/// Only that case: a text drawn at a width it has not seen places its glyphs,
|
||||
/// and placing them allocates a list to hold them.
|
||||
#[test]
|
||||
fn redrawing_a_text_at_one_width_allocates_nothing() {
|
||||
let mut h = Harness::new((600, 200));
|
||||
let mut col = Span::empty(Dir::DOWN);
|
||||
let mut texts = Vec::new();
|
||||
for _ in 0..8 {
|
||||
let text =
|
||||
wtext("wrapping shapes one source into as many lines as the box leaves room for")
|
||||
.size(16)
|
||||
// Named rather than generic, because a named one is the family
|
||||
// that costs an allocation to copy.
|
||||
.family(Family::Named("sans-serif".into()))
|
||||
.wrap(true)
|
||||
.add_strong(&mut h.rsc);
|
||||
texts.push(text.id());
|
||||
col.push(text);
|
||||
}
|
||||
let root = col.add(&mut h.rsc);
|
||||
h.set_root(root);
|
||||
let redraw = |h: &mut Harness| {
|
||||
for &id in &texts {
|
||||
h.rsc.widgets_mut().mark_for_redraw(id);
|
||||
}
|
||||
h.frame();
|
||||
};
|
||||
for _ in 0..8 {
|
||||
redraw(&mut h);
|
||||
}
|
||||
COUNT.set(Some(0));
|
||||
for _ in 0..100 {
|
||||
redraw(&mut h);
|
||||
}
|
||||
let allocations = COUNT.replace(None).unwrap();
|
||||
println!("text: {allocations} allocations over 100 redraws of 8 texts");
|
||||
assert_eq!(allocations, 0);
|
||||
}
|
||||
Reference in new issue
Block a user