iris: process dirty widgets one at a time
This commit is contained in:
1 parent
b1416d86bf
commit
77b39a97e3
2 files changed
+41
-47
No files matched your search
+6
-5
@@ -141,11 +141,12 @@ which is correct always, just not free.
|
|||||||
|
|
||||||
**Size invalidation travels upward before drawing; drawing itself travels only
|
**Size invalidation travels upward before drawing; drawing itself travels only
|
||||||
downward.** Every active widget retains the direct children whose size it read
|
downward.** Every active widget retains the direct children whose size it read
|
||||||
through `DrawResult::size()` or `Painter::known_len`. Before a frame draws,
|
through `DrawResult::size()` or `Painter::known_len`. `redraw_updates` takes
|
||||||
`redraw_updates` follows only those dependency edges from each dirty child and
|
one id from the dirty set, follows only those dependency edges upward and marks
|
||||||
marks the affected ancestors dirty. It then selects the highest dirty roots and
|
that path dirty, then redraws its highest already-dirty ancestor. Drawing that
|
||||||
draws them top-down. Drawing never synchronously invalidates or invokes a
|
ancestor consumes the marks of every dirty descendant it reaches; the loop
|
||||||
parent, so there is no layout recursion and no provisional child draw on a
|
then takes whatever remains. Drawing never synchronously invalidates or invokes
|
||||||
|
a parent, so there is no layout recursion and no provisional child draw on a
|
||||||
different layer.
|
different layer.
|
||||||
|
|
||||||
An exact `size_hint` stops propagation when both axes still equal the retained
|
An exact `size_hint` stops propagation when both axes still equal the retained
|
||||||
|
|||||||
@@ -40,10 +40,10 @@ pub struct UiRenderState {
|
|||||||
|
|
||||||
old_root: Option<WidgetId>,
|
old_root: Option<WidgetId>,
|
||||||
resized: bool,
|
resized: bool,
|
||||||
/// It used to only ever be inserted into, and `redraw` removed the id
|
/// Widgets whose `draw` call is on the stack now. A reentrant draw would
|
||||||
/// *before* testing for it, which made the test constant `false`: the
|
/// create two retained primitive sets with one owner, so it is rejected;
|
||||||
/// guard could never fire and the set grew by one entry per widget
|
/// completed draws are removed immediately and are instead tracked by
|
||||||
/// ever drawn and was never emptied.
|
/// consuming their outstanding dirty mark.
|
||||||
draw_started: HashSet<WidgetId>,
|
draw_started: HashSet<WidgetId>,
|
||||||
/// Widgets which asked from inside `draw` to be drawn on the following
|
/// Widgets which asked from inside `draw` to be drawn on the following
|
||||||
/// frame. Kept separate from `Widgets::needs_redraw` until `update_at`
|
/// frame. Kept separate from `Widgets::needs_redraw` until `update_at`
|
||||||
@@ -824,49 +824,42 @@ impl UiRenderState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn redraw_updates(&mut self, rsc: &mut dyn UiRsc) {
|
pub fn redraw_updates(&mut self, rsc: &mut dyn UiRsc) {
|
||||||
while rsc.widgets().has_updates() {
|
while let Some(id) = rsc.widgets().needs_redraw.iter().next().copied() {
|
||||||
let pending: Vec<_> = rsc.widgets().needs_redraw.iter().copied().collect();
|
// A parent which read this child's size must lay out again before
|
||||||
for mut child in pending {
|
// the changed child is drawn. Mark the whole dependent path so
|
||||||
for _ in 0..PARENT_CHAIN_LIMIT {
|
// drawing its highest member reaches every dirty descendant.
|
||||||
if self.size_matches_hints(child, rsc) {
|
let mut child = id;
|
||||||
break;
|
for _ in 0..PARENT_CHAIN_LIMIT {
|
||||||
}
|
if self.size_matches_hints(child, rsc) {
|
||||||
let Some(parent) = self.active.get(&child).and_then(|active| active.parent)
|
break;
|
||||||
else {
|
|
||||||
break;
|
|
||||||
};
|
|
||||||
let depends = self
|
|
||||||
.active
|
|
||||||
.get(&parent)
|
|
||||||
.is_some_and(|active| active.size_dependencies.contains(&child));
|
|
||||||
if !depends {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
rsc.widgets_mut().needs_redraw.insert(parent);
|
|
||||||
child = parent;
|
|
||||||
}
|
}
|
||||||
|
let Some(parent) = self.active.get(&child).and_then(|active| active.parent) else {
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
let depends = self
|
||||||
|
.active
|
||||||
|
.get(&parent)
|
||||||
|
.is_some_and(|active| active.size_dependencies.contains(&child));
|
||||||
|
if !depends {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
rsc.widgets_mut().needs_redraw.insert(parent);
|
||||||
|
child = parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
let dirty: Vec<_> = rsc.widgets().needs_redraw.iter().copied().collect();
|
// Prefer an already-dirty ancestor. Its draw either consumes this
|
||||||
let mut roots = Vec::new();
|
// mark on the way down or leaves it for the next loop iteration if
|
||||||
for id in dirty {
|
// a retained intermediate subtree means it never reaches here.
|
||||||
let mut ancestor = self.active.get(&id).and_then(|active| active.parent);
|
let mut root = id;
|
||||||
let mut covered = false;
|
let mut ancestor = self.active.get(&id).and_then(|active| active.parent);
|
||||||
for _ in 0..PARENT_CHAIN_LIMIT {
|
for _ in 0..PARENT_CHAIN_LIMIT {
|
||||||
let Some(parent) = ancestor else { break };
|
let Some(parent) = ancestor else { break };
|
||||||
if rsc.widgets().needs_redraw.contains(&parent) {
|
if rsc.widgets().needs_redraw.contains(&parent) {
|
||||||
covered = true;
|
root = parent;
|
||||||
break;
|
|
||||||
}
|
|
||||||
ancestor = self.active.get(&parent).and_then(|active| active.parent);
|
|
||||||
}
|
|
||||||
if !covered {
|
|
||||||
roots.push(id);
|
|
||||||
}
|
}
|
||||||
|
ancestor = self.active.get(&parent).and_then(|active| active.parent);
|
||||||
}
|
}
|
||||||
for id in roots {
|
self.redraw(root, rsc);
|
||||||
self.redraw(id, rsc);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
rsc.free();
|
rsc.free();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user