Give a child a part of the container's extent rather than its raw box
`Pad` and `Stack` read `Painter::placement` to put their children inside their own drawing, and reading it is what says the drawing holds for that placement alone. So a pad or a stack anywhere in a row was drawn again -- with its whole subtree -- the moment an earlier sibling changed length, however little else had moved. `widget_within` now takes a `DrawRegion`, and `DrawRegion::Extent(part)` gives the child a part of the extent without reading it. What is retained is the part rather than the box it resolved to, so moving the extent re-places the child through the same rule instead of redrawing the parent: `inherited_children` becomes `extent_children`, carrying `Inherit` for the wrapper case `Painter::widget` already had and `Within(part)` for the new one. The dependency that goes up is a range on the container's extent rather than on its frame, since only the part's *length* reaches the child and where the part sits is re-placed. A declared length is unchanged: it is a length of the frame wherever the box it sits in came from. What still pins the placement is a report with a fraction in it -- the same fraction of a different extent is a different length -- and that pin is on the answer, which `extent_frames_keep_fractional_reports_and_numeric_dependencies_valid` fails without. Three tests from the first attempt at this come with it, and the diagnostics rig now says which of the three contracts refused a reuse, which is what found the above. Measured, seed 1 at depth 8, median frame: `many` 0.667 -> 0.613 ms and `resize` 48 -> 32 us; seed 13's `many` 6.35 -> 5.15 ms. Green: fmt, clippy, 109 suite and 20 core tests, the oracle at 100 seeds, the shrinker at 400 trees of depth 5, 1000 seeds at depth 6, and 2000 seeds at depth 4 over all fifteen cases. The five reference renders are byte-identical to `0e107f0` on Venus, as are `tabs` resized to 900x1200 and `random` to 1280x800 against cold renders there. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
0e107f0e89
commit
e6ba570d07
7 files changed
+358
-57
No files matched your search
+32
-15
@@ -288,7 +288,7 @@ impl UiRenderState {
|
||||
&& let Some(old_parent) = self.active.get_mut(&old_parent)
|
||||
{
|
||||
old_parent.children.retain(|child| *child != id);
|
||||
old_parent.inherited_children.retain(|child| *child != id);
|
||||
old_parent.extent_children.retain(|(child, _)| *child != id);
|
||||
}
|
||||
(answer.0, answer.1, settled.1)
|
||||
}
|
||||
@@ -361,7 +361,7 @@ impl UiRenderState {
|
||||
textures: Vec::new(),
|
||||
primitives: Vec::new(),
|
||||
mask_region: None,
|
||||
inherited_children: Vec::new(),
|
||||
extent_children: Vec::new(),
|
||||
children: Vec::new(),
|
||||
offered: Vec::new(),
|
||||
offered_px: info.offered_px,
|
||||
@@ -398,7 +398,7 @@ impl UiRenderState {
|
||||
textures,
|
||||
primitives,
|
||||
mask_region,
|
||||
inherited_children,
|
||||
extent_children,
|
||||
extent_own,
|
||||
answer_under,
|
||||
children,
|
||||
@@ -503,7 +503,7 @@ impl UiRenderState {
|
||||
textures,
|
||||
primitives,
|
||||
mask_region,
|
||||
inherited_children,
|
||||
extent_children,
|
||||
children,
|
||||
size_deps,
|
||||
declared: declared_lens(rsc.widgets(), id),
|
||||
@@ -680,6 +680,24 @@ impl UiRenderState {
|
||||
if !active.holds.contains(info.px, placement) {
|
||||
#[cfg(feature = "layout-diagnostics")]
|
||||
{
|
||||
// Which of the three said no, so a frame that redraws more
|
||||
// than it should says where to look. They overlap: a drawing
|
||||
// can be outside two of them at once.
|
||||
let holds = active.holds;
|
||||
if holds.placement.is_some_and(|pinned| pinned != placement) {
|
||||
diag::bump(Counter::OutsidePlacement);
|
||||
}
|
||||
for axis in AXES {
|
||||
let n = axis as usize;
|
||||
if !holds.frame[n].contains(info.px.axis(axis)) {
|
||||
diag::bump(Counter::OutsideFrame);
|
||||
}
|
||||
if !holds.extent[n]
|
||||
.contains(placement.axis(axis).len().to_px(info.px.axis(axis)))
|
||||
{
|
||||
diag::bump(Counter::OutsideExtent);
|
||||
}
|
||||
}
|
||||
diag::bump(Counter::ReuseOutside);
|
||||
diag::reuse(id, ReuseOutcome::Outside);
|
||||
}
|
||||
@@ -752,19 +770,18 @@ impl UiRenderState {
|
||||
}
|
||||
let parent_move = active.move_idx;
|
||||
let mask = active.mask;
|
||||
let children = active.inherited_children.len();
|
||||
let children = active.extent_children.len();
|
||||
for index in 0..children {
|
||||
let child = self.active[&id].inherited_children[index];
|
||||
let (child, extent) = self.active[&id].extent_children[index];
|
||||
let (part, slot) = extent.resolve(placement);
|
||||
let active = &self.active[&child];
|
||||
let (child_local, chosen) = ask_box(
|
||||
UiRegion::FULL,
|
||||
active.declared,
|
||||
active.own_align,
|
||||
[Some(placement.x), Some(placement.y)],
|
||||
);
|
||||
let (child_local, chosen) = ask_box(part, active.declared, active.own_align, slot);
|
||||
// What it took of that box is its own answer, which this move did
|
||||
// not ask again: keep the placement it has on any axis this
|
||||
// widget is not the one choosing.
|
||||
let child_placement = UiRegion {
|
||||
x: chosen[0].unwrap_or(UiSpan::FULL),
|
||||
y: chosen[1].unwrap_or(UiSpan::FULL),
|
||||
x: chosen[0].unwrap_or(active.placement.x),
|
||||
y: chosen[1].unwrap_or(active.placement.y),
|
||||
};
|
||||
let child_info = DrawInfo {
|
||||
layer: active.layer,
|
||||
@@ -924,7 +941,7 @@ impl UiRenderState {
|
||||
textures: Vec::new(),
|
||||
primitives: Vec::new(),
|
||||
mask_region: None,
|
||||
inherited_children: Vec::new(),
|
||||
extent_children: Vec::new(),
|
||||
children: Vec::new(),
|
||||
size_deps: Vec::new(),
|
||||
move_idx: info.parent_move,
|
||||
|
||||
Reference in new issue
Block a user