Hold a request in the arena its nodes are allocated in
A size request was a second expression shape beside the one the layout
pass already has. `SizeRequest` held `Sum`/`Min`/`Max` over `Arc` pairs;
`RequestArena` held the same three operators as `Node { op, a, b }` in a
`Vec`, with the same fold over `independent_order` written a second time,
and `import` walked the first rebuilding it as the second.
There is one node type now. An expression is the pass's nodes in an arena
of its own that lasts as long as the rule holding it, and `import` grafts
those nodes into the pass's arena, resolving fractions as they land. The
fold is `Nodes::combine`, which both the builder and the importer call.
So the `Arc` goes, and no refcount replaces it: nothing shares a request
and nothing outside widget code holds one. A plain length stays inline,
so `size_of::<SizeRule>()` is 40 either way and only an actual expression
allocates. A node's operand is a number within its own arena, and
`RequestedLen` -- the only form that leaves one -- is that plus the epoch
saying which pass numbered it, so the epoch is now checked once where a
handle comes back in rather than at every level of the walk it starts.
`SizeRule::at_least`/`at_most` were the only clones of a request in the
framework, and both read a rule out, moved one end of its bound, and
wrote it back into the slot it came from. `Widgets::edit_bound` does it
where it sits, so nothing copies an expression to cap it.
`SizeRequest` grew a `Display`, since the shrinker prints one and a
derived `Debug` of an arena is not something a tree can be rebuilt from:
`min(30 px;1 leftover;, 2 leftover;)<0.5 rel;`.
Measured, medians of three release runs under `perf stat -e
instructions:u`, each set within 0.005% of its median: bounds_cost
MODE=cap FRAMES=2000 is 5.665B against 5.743B (-1.34%), and
revision_cost resize ROWS=40 FRAMES=500 is 4.855B against 4.893B
(-0.79%).
Format, workspace clippy under -D warnings with and without
layout-diagnostics, 206 ordinary and 210 diagnostic tests, the cold dump
byte-identical to 2ac0843 across all 34,986 boxes, 400 depth-5 trees in
64.24s, 1,000 depth-6 in 160.35s, 2,000 depth-4 in 298.82s, and 400
depth-5 trees in each of the three deferred-request corpora in 205.42s.
This commit is contained in:
1 parent
ea1f836bf9
commit
05e6ced31d
4 files changed
+318
-158
No files matched your search
+27
-17
@@ -1,8 +1,8 @@
|
||||
use std::sync::mpsc::{Receiver, Sender, channel};
|
||||
|
||||
use crate::{
|
||||
Axis, AxisAlign, IdLike, Len, RegionAlign, SizeRequest, SizeRule, SizeRules, StrongWidget,
|
||||
WeakWidget, Widget, WidgetData, WidgetId,
|
||||
Axis, AxisAlign, Bound, IdLike, Len, RegionAlign, SizeRequest, SizeRule, SizeRules,
|
||||
StrongWidget, WeakWidget, Widget, WidgetData, WidgetId,
|
||||
util::{DynBorrower, HashSet, SlotVec, forget_mut, to_mut},
|
||||
};
|
||||
|
||||
@@ -145,30 +145,40 @@ impl Widgets {
|
||||
self.needs_redraw.insert(id);
|
||||
}
|
||||
|
||||
/// Changes the preferred length without changing its bounds.
|
||||
/// Changes the preferred length, leaving the bounds beside it alone.
|
||||
pub fn set_len(&mut self, id: impl IdLike, axis: Axis, len: impl Into<SizeRequest>) {
|
||||
let id = id.id();
|
||||
let rule = SizeRule {
|
||||
request: Some(len.into()),
|
||||
bound: self.size_rules(id)[axis].bound,
|
||||
};
|
||||
self.set_size_rule(id, axis, rule);
|
||||
let request = Some(len.into());
|
||||
let rule = &mut self.data_mut(id).unwrap().size[axis];
|
||||
if rule.request == request {
|
||||
return;
|
||||
}
|
||||
rule.request = request;
|
||||
self.needs_redraw.insert(id);
|
||||
}
|
||||
|
||||
/// Puts a floor under this widget's length on one axis, keeping a cap it
|
||||
/// already had. See [`SizeRule::at_least`].
|
||||
/// already had and the preferred length beside it.
|
||||
pub fn set_min_len(&mut self, id: impl IdLike, axis: Axis, min: Len) {
|
||||
let id = id.id();
|
||||
let rule = self.size_rules(id)[axis].at_least(min);
|
||||
self.set_size_rule(id, axis, rule);
|
||||
self.edit_bound(id.id(), axis, |bound| bound.min = Some(min));
|
||||
}
|
||||
|
||||
/// Puts a cap over it, keeping a floor it already had. See
|
||||
/// [`SizeRule::at_most`].
|
||||
/// Puts a cap over it, keeping a floor it already had.
|
||||
pub fn set_max_len(&mut self, id: impl IdLike, axis: Axis, max: Len) {
|
||||
let id = id.id();
|
||||
let rule = self.size_rules(id)[axis].at_most(max);
|
||||
self.set_size_rule(id, axis, rule);
|
||||
self.edit_bound(id.id(), axis, |bound| bound.max = Some(max));
|
||||
}
|
||||
|
||||
/// Edits one axis's bound where it sits, rather than reading the whole
|
||||
/// rule out and writing it back: an expression beside the bound is not
|
||||
/// this edit's business, and copying it to move one end would be the
|
||||
/// only thing here that ever copies one.
|
||||
fn edit_bound(&mut self, id: WidgetId, axis: Axis, edit: impl FnOnce(&mut Bound)) {
|
||||
let bound = &mut self.data_mut(id).unwrap().size[axis].bound;
|
||||
let before = *bound;
|
||||
edit(bound);
|
||||
if *bound != before {
|
||||
self.needs_redraw.insert(id);
|
||||
}
|
||||
}
|
||||
|
||||
/// Where this widget sits in a box longer than the length it takes.
|
||||
|
||||
Reference in new issue
Block a user