Resolve deferred size comparisons before allocating span slots
This commit is contained in:
1 parent
de1eb7e406
commit
8780b40bb7
23 files changed
+1471
-130
No files matched your search
+100
-28
@@ -94,7 +94,10 @@ pub struct UiRenderState {
|
||||
deferred: crate::util::HashSet<WidgetId>,
|
||||
/// What the walk has left to settle, deepest last. Ordered rather than
|
||||
/// searched for, so finding the next one is not a pass over the marks.
|
||||
pending: std::collections::BTreeSet<(usize, WidgetId)>,
|
||||
pending: std::collections::BinaryHeap<(usize, WidgetId)>,
|
||||
pub(super) requests: crate::RequestArena,
|
||||
changed: Vec<WidgetId>,
|
||||
request_readers: HashMap<WidgetId, crate::util::HashSet<WidgetId>>,
|
||||
pub moves: Moves,
|
||||
}
|
||||
|
||||
@@ -108,6 +111,9 @@ impl UiRenderState {
|
||||
slots: Default::default(),
|
||||
deferred: Default::default(),
|
||||
pending: Default::default(),
|
||||
requests: Default::default(),
|
||||
changed: Vec::new(),
|
||||
request_readers: Default::default(),
|
||||
moves: Default::default(),
|
||||
resized: false,
|
||||
}
|
||||
@@ -194,6 +200,7 @@ impl UiRenderState {
|
||||
weak widgets: {all:#?}"
|
||||
);
|
||||
}
|
||||
self.requests.reset();
|
||||
let root = root.into();
|
||||
if self.root_changed(root) {
|
||||
self.redraw_all(root, rsc);
|
||||
@@ -211,8 +218,13 @@ impl UiRenderState {
|
||||
let _layout = diag::timer(TimerKind::FullLayout);
|
||||
self.clear(rsc);
|
||||
if let Some(id) = root {
|
||||
let ask =
|
||||
Placing::WINDOW.ask(rsc.widgets(), self.output_size, id.id(), PlaceDesc::WHOLE);
|
||||
let ask = Placing::WINDOW.ask(
|
||||
rsc.widgets(),
|
||||
&mut self.requests,
|
||||
self.output_size,
|
||||
id.id(),
|
||||
PlaceDesc::WHOLE,
|
||||
);
|
||||
let info = self.root_info(&ask, rsc.widgets().is_region_node(id.id()));
|
||||
self.draw_inner(id.id(), info, None, rsc);
|
||||
}
|
||||
@@ -327,7 +339,19 @@ impl UiRenderState {
|
||||
let mask_slot = old
|
||||
.as_ref()
|
||||
.and_then(|old| old.mask_region.map(|_| old.mask));
|
||||
let old_children = old.map_or_else(Vec::new, |old| old.children);
|
||||
let (mut old_children, textures, primitives, request_deps, mut scratch) = match old {
|
||||
Some(old) => (
|
||||
old.children,
|
||||
old.textures,
|
||||
old.primitives,
|
||||
old.request_deps,
|
||||
old.scratch,
|
||||
),
|
||||
None => Default::default(),
|
||||
};
|
||||
let children = std::mem::take(&mut scratch.children);
|
||||
let size_deps = std::mem::take(&mut scratch.size_deps);
|
||||
let under = std::mem::take(&mut scratch.under);
|
||||
rsc.widgets_mut().needs_redraw.remove(&id);
|
||||
let window = self.output_size;
|
||||
let mut painter = Painter {
|
||||
@@ -339,17 +363,19 @@ impl UiRenderState {
|
||||
layer: info.layer,
|
||||
own_layer: info.layer,
|
||||
id,
|
||||
textures: Vec::new(),
|
||||
primitives: Vec::new(),
|
||||
textures,
|
||||
primitives,
|
||||
mask_region: None,
|
||||
mask_slot,
|
||||
children: Vec::new(),
|
||||
size_deps: Vec::new(),
|
||||
children,
|
||||
size_deps,
|
||||
request_deps,
|
||||
scratch,
|
||||
// What the ask holds for is part of what the drawing holds for:
|
||||
// a box the widget's own rule took past the offer was decided in
|
||||
// this window, and at the root nobody else keeps that range.
|
||||
own: info.ask_holds,
|
||||
under: Vec::new(),
|
||||
under,
|
||||
answer_under: LayoutHolds::ANY,
|
||||
depth: info.depth,
|
||||
move_idx,
|
||||
@@ -381,8 +407,10 @@ impl UiRenderState {
|
||||
own,
|
||||
answer_under,
|
||||
children,
|
||||
size_deps,
|
||||
under,
|
||||
mut size_deps,
|
||||
request_deps,
|
||||
mut scratch,
|
||||
mut under,
|
||||
move_idx,
|
||||
layer,
|
||||
own_layer: _,
|
||||
@@ -402,14 +430,19 @@ impl UiRenderState {
|
||||
// resolved into the rel base when the widget was asked, and resolving
|
||||
// it again here would take the fraction of a fraction.
|
||||
let rules = rsc.widgets().size_rules(id);
|
||||
let ruled = |axis: Axis, reported: LayoutLen| match rules[axis].exact() {
|
||||
None => reported,
|
||||
Some(len) if len.leftover == Weight::ZERO => LayoutLen {
|
||||
rel: info.rel_base[axis].rel,
|
||||
px: info.rel_base[axis].px,
|
||||
leftover: Weight::ZERO,
|
||||
},
|
||||
Some(len) => len.within_len(info.rel_base[axis]),
|
||||
let ruled = |axis: Axis, reported: LayoutLen| {
|
||||
if matches!(rules[axis], crate::SizeRule::Request(_)) {
|
||||
return info.rel_base[axis].into();
|
||||
}
|
||||
match rules[axis].exact() {
|
||||
None => reported,
|
||||
Some(len) if len.leftover == Weight::ZERO => LayoutLen {
|
||||
rel: info.rel_base[axis].rel,
|
||||
px: info.rel_base[axis].px,
|
||||
leftover: Weight::ZERO,
|
||||
},
|
||||
Some(len) => len.within_len(info.rel_base[axis]),
|
||||
}
|
||||
};
|
||||
let mut size = Size {
|
||||
x: ruled(Axis::X, size.x),
|
||||
@@ -483,8 +516,8 @@ impl UiRenderState {
|
||||
}
|
||||
let answer_holds = own_holds.and(answer_under);
|
||||
let holds = under
|
||||
.into_iter()
|
||||
.fold(answer_holds, |holds, (_, child)| holds.and(child));
|
||||
.iter()
|
||||
.fold(answer_holds, |holds, (_, child)| holds.and(*child));
|
||||
debug_assert!(
|
||||
holds.contains(self.output_size, info.rel_base, region),
|
||||
"'{}' ({id:?}) drew in {}, outside the ranges it reported: {holds:?}",
|
||||
@@ -520,6 +553,15 @@ impl UiRenderState {
|
||||
}
|
||||
}
|
||||
|
||||
for &dep in &request_deps {
|
||||
self.request_readers.entry(dep).or_default().insert(id);
|
||||
}
|
||||
old_children.clear();
|
||||
size_deps.clear();
|
||||
under.clear();
|
||||
scratch.children = old_children;
|
||||
scratch.size_deps = size_deps;
|
||||
scratch.under = under;
|
||||
let active = ActiveData {
|
||||
id,
|
||||
placement: region,
|
||||
@@ -539,6 +581,8 @@ impl UiRenderState {
|
||||
primitives,
|
||||
mask_region,
|
||||
children,
|
||||
request_deps,
|
||||
scratch,
|
||||
declared: info.declared,
|
||||
bounds: info.bounds,
|
||||
own_align: rsc.widgets().alignment(id),
|
||||
@@ -585,6 +629,7 @@ impl UiRenderState {
|
||||
if !active.drawn
|
||||
|| active.is_region_node() != info.region_node
|
||||
|| active.parent_move != info.parent_move
|
||||
|| active.bounds != info.bounds
|
||||
{
|
||||
return None;
|
||||
}
|
||||
@@ -841,6 +886,11 @@ impl UiRenderState {
|
||||
fn remove(&mut self, id: WidgetId, undraw: bool, rsc: &mut dyn UiRsc) -> Option<ActiveData> {
|
||||
let mut active = self.active.remove(&id);
|
||||
if let Some(active) = &mut active {
|
||||
for dep in active.request_deps.drain(..) {
|
||||
if let Some(readers) = self.request_readers.get_mut(&dep) {
|
||||
readers.remove(&id);
|
||||
}
|
||||
}
|
||||
for primitive in &active.primitives {
|
||||
let mask = self.layers.free(&primitive.handle);
|
||||
if mask != MaskIdx::NONE {
|
||||
@@ -914,6 +964,8 @@ impl UiRenderState {
|
||||
primitives: Vec::new(),
|
||||
mask_region: None,
|
||||
children: Vec::new(),
|
||||
request_deps: Vec::new(),
|
||||
scratch: Default::default(),
|
||||
move_idx: info.parent_move,
|
||||
declared: Declared::NONE,
|
||||
bounds: Bounds::ANY,
|
||||
@@ -927,6 +979,7 @@ impl UiRenderState {
|
||||
}
|
||||
|
||||
fn clear(&mut self, rsc: &mut dyn UiRsc) {
|
||||
self.request_readers.clear();
|
||||
for (_, active) in self.active.drain() {
|
||||
if active.drawn {
|
||||
rsc.on_undraw(&active);
|
||||
@@ -947,6 +1000,7 @@ impl UiRenderState {
|
||||
rsc.on_remove(id);
|
||||
self.remove(id, true, rsc);
|
||||
self.drop_slot(id);
|
||||
self.request_readers.remove(&id);
|
||||
}
|
||||
rsc.ui_mut().textures.free();
|
||||
}
|
||||
@@ -954,6 +1008,18 @@ impl UiRenderState {
|
||||
pub fn redraw_updates(&mut self, rsc: &mut dyn UiRsc) {
|
||||
#[cfg(feature = "layout-diagnostics")]
|
||||
let _layout = diag::timer(TimerKind::IncrementalLayout);
|
||||
self.changed.clear();
|
||||
self.changed
|
||||
.extend(rsc.widgets().needs_redraw.iter().copied());
|
||||
while let Some(id) = self.changed.pop() {
|
||||
if let Some(readers) = self.request_readers.get(&id) {
|
||||
for &reader in readers {
|
||||
if rsc.widgets_mut().needs_redraw.insert(reader) {
|
||||
self.changed.push(reader);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Deepest first, and strictly: a widget that cannot settle where it
|
||||
// is defers to its parent rather than drawing the parent from
|
||||
// inside itself. It marks the parent, stays marked, and waits here
|
||||
@@ -965,21 +1031,21 @@ impl UiRenderState {
|
||||
// something below is about to change it -- which is the whole class
|
||||
// of defect where a widget settles inside its parent's draw, clears
|
||||
// its mark there, and tells nobody its answer moved.
|
||||
// The queue is that set, ordered: a mark made while the walk runs
|
||||
// queues itself through `mark`. What ends the walk is still the set
|
||||
// A mark made while the walk runs queues itself through `mark`.
|
||||
// What ends the walk is still the set
|
||||
// being spent, not the queue, so a mark that reached it another way
|
||||
// cannot be left for the next frame.
|
||||
loop {
|
||||
for &id in rsc.widgets().needs_redraw.iter() {
|
||||
if !self.deferred.contains(&id) {
|
||||
let depth = self.depth(id);
|
||||
self.pending.insert((depth, id));
|
||||
self.pending.push((depth, id));
|
||||
}
|
||||
}
|
||||
if self.pending.is_empty() {
|
||||
break;
|
||||
}
|
||||
while let Some((depth, id)) = self.pending.pop_last() {
|
||||
while let Some((depth, id)) = self.pending.pop() {
|
||||
// Settled inside an ancestor's draw, or deferred to one,
|
||||
// since the mark that queued it.
|
||||
if self.deferred.contains(&id) || !rsc.widgets().needs_redraw.contains(&id) {
|
||||
@@ -990,7 +1056,7 @@ impl UiRenderState {
|
||||
// depth it had under the parent it left.
|
||||
let now = self.depth(id);
|
||||
if now != depth {
|
||||
self.pending.insert((now, id));
|
||||
self.pending.push((now, id));
|
||||
continue;
|
||||
}
|
||||
#[cfg(feature = "layout-diagnostics")]
|
||||
@@ -1007,7 +1073,7 @@ impl UiRenderState {
|
||||
fn mark(&mut self, id: WidgetId, widgets: &mut Widgets) {
|
||||
if widgets.needs_redraw.insert(id) && !self.deferred.contains(&id) {
|
||||
let depth = self.depth(id);
|
||||
self.pending.insert((depth, id));
|
||||
self.pending.push((depth, id));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1114,7 +1180,13 @@ impl UiRenderState {
|
||||
Some(parent) => self.placing_of(parent, self.active[&parent].region),
|
||||
None => Placing::WINDOW,
|
||||
};
|
||||
let ask = at.ask(rsc.widgets(), self.output_size, id, active.asked);
|
||||
let ask = at.ask(
|
||||
rsc.widgets(),
|
||||
&mut self.requests,
|
||||
self.output_size,
|
||||
id,
|
||||
active.asked,
|
||||
);
|
||||
let active = &self.active[&id];
|
||||
let declared_changed = ask.declared != active.declared;
|
||||
let alignment_changed = rsc.widgets().alignment(id) != active.own_align;
|
||||
|
||||
Reference in new issue
Block a user