Skip to content

Commit

Permalink
feat: Skip measuring of cached siblings in layout
Browse files Browse the repository at this point in the history
  • Loading branch information
marc2332 committed Sep 25, 2023
1 parent 6b2509f commit 87f245d
Show file tree
Hide file tree
Showing 5 changed files with 112 additions and 61 deletions.
56 changes: 37 additions & 19 deletions crates/dom/src/dom_adapter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,16 +77,21 @@ impl DOMAdapter<NodeId> for DioxusDOMAdapter<'_> {
is_node_valid(self.rdom, &mut self.valid_nodes_cache, node_id)
}

fn closest_common_parent(&self, node_id_a: &NodeId, node_id_b: &NodeId) -> Option<NodeId> {
fn closest_common_parent(
&self,
node_id_a: &NodeId,
node_id_b: &NodeId,
) -> Option<(NodeId, Vec<NodeId>)> {
find_common_parent(self.rdom, *node_id_a, *node_id_b)
}
}

/// Walk to the ancestor of `base` with the same height of `target`
fn balance_heights(rdom: &DioxusDOM, base: NodeId, target: NodeId) -> Option<NodeId> {
fn balance_heights(rdom: &DioxusDOM, base: NodeId, target: NodeId) -> Option<Vec<NodeId>> {
let tree = rdom.tree_ref();
let target_height = tree.height(target)?;
let mut current = base;
let mut path = Vec::new();
loop {
if tree.height(current)? == target_height {
break;
Expand All @@ -95,49 +100,62 @@ fn balance_heights(rdom: &DioxusDOM, base: NodeId, target: NodeId) -> Option<Nod
let parent_current = tree.parent_id(current);
if let Some(parent_current) = parent_current {
current = parent_current;
path.push(current);
}
}
Some(current)
Some(path)
}

/// Return the closest common ancestor of both Nodes
fn find_common_parent(rdom: &DioxusDOM, node_a: NodeId, node_b: NodeId) -> Option<NodeId> {
fn find_common_parent(
rdom: &DioxusDOM,
node_a: NodeId,
node_b: NodeId,
) -> Option<(NodeId, Vec<NodeId>)> {
let tree = rdom.tree_ref();
let height_a = tree.height(node_a)?;
let height_b = tree.height(node_b)?;

let (node_a, node_b) = match height_a.cmp(&height_b) {
let (path_a, path_b) = match height_a.cmp(&height_b) {
std::cmp::Ordering::Less => (
node_a,
balance_heights(rdom, node_b, node_a).unwrap_or(node_b),
vec![node_a],
balance_heights(rdom, node_b, node_a).unwrap_or(vec![node_b]),
),
std::cmp::Ordering::Equal => (node_a, node_b),
std::cmp::Ordering::Equal => (vec![node_a], vec![node_b]),
std::cmp::Ordering::Greater => (
balance_heights(rdom, node_a, node_b).unwrap_or(node_a),
node_b,
balance_heights(rdom, node_a, node_b).unwrap_or(vec![node_a]),
vec![node_b],
),
};

let mut currents = (node_a, node_b);
let node_a = *path_a.last().unwrap();
let node_b = *path_b.last().unwrap();

let mut branch_a = vec![node_a];
let mut branch_b = vec![node_b];

loop {
// Common parent of node_a and node_b
if currents.0 == currents.1 {
return Some(currents.0);
if branch_a.last() == branch_b.last() {
let last = *branch_a.last().unwrap();
branch_a.extend(branch_b);
branch_a.extend(path_a);
branch_a.extend(path_b);
return Some((last, branch_a));
}

let parent_a = tree.parent_id(currents.0);
let parent_a = tree.parent_id(*branch_a.last().unwrap());
if let Some(parent_a) = parent_a {
currents.0 = parent_a;
} else if rdom.root_id() != currents.0 {
branch_a.push(parent_a);
} else if rdom.root_id() != *branch_a.last().unwrap() {
// Skip unconected nodes
break;
}

let parent_b = tree.parent_id(currents.1);
let parent_b = tree.parent_id(*branch_b.last().unwrap());
if let Some(parent_b) = parent_b {
currents.1 = parent_b;
} else if rdom.root_id() != currents.1 {
branch_b.push(parent_b);
} else if rdom.root_id() != *branch_b.last().unwrap() {
// Skip unconected nodes
break;
}
Expand Down
9 changes: 7 additions & 2 deletions crates/torin/benches/bench.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,13 @@ impl DOMAdapter<usize> for TestingDOM {
true
}

fn closest_common_parent(&self, node_id_a: &usize, _node_id_b: &usize) -> Option<usize> {
Some(self.parent_of(node_id_a)?)
fn closest_common_parent(
&self,
node_id_a: &usize,
_node_id_b: &usize,
) -> Option<(usize, Vec<usize>)> {
let parent = self.parent_of(node_id_a)?;
Some((parent, vec![parent]))
}
}

Expand Down
6 changes: 5 additions & 1 deletion crates/torin/src/dom_adapter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,5 +53,9 @@ pub trait DOMAdapter<NodeKey> {
fn is_node_valid(&mut self, node_id: &NodeKey) -> bool;

/// Get the closest common parent Node of two Nodes
fn closest_common_parent(&self, node_id_a: &NodeKey, node_id_b: &NodeKey) -> Option<NodeKey>;
fn closest_common_parent(
&self,
node_id_a: &NodeKey,
node_id_b: &NodeKey,
) -> Option<(NodeKey, Vec<NodeKey>)>;
}
88 changes: 52 additions & 36 deletions crates/torin/src/torin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ use crate::{
};

/// Contains the best Root node candidate from where to start measuring
#[derive(PartialEq, Debug, Clone, Copy)]
#[derive(PartialEq, Debug, Clone)]
pub enum RootNodeCandidate<Key: NodeKey> {
/// A valid Node ID
Valid(Key),
Valid(Key, Vec<Key>),

/// None
None,
Expand Down Expand Up @@ -120,7 +120,7 @@ impl<Key: NodeKey> Torin<Key> {
pub fn raw_remove(&mut self, node_id: Key) {
self.results.remove(&node_id);
self.dirty.remove(&node_id);
if let RootNodeCandidate::Valid(id) = self.root_node_candidate {
if let RootNodeCandidate::Valid(id, _) = self.root_node_candidate {
if id == node_id {
self.root_node_candidate = RootNodeCandidate::None
}
Expand Down Expand Up @@ -174,13 +174,16 @@ impl<Key: NodeKey> Torin<Key> {
self.invalidate(node_id);

if RootNodeCandidate::None == self.root_node_candidate {
self.root_node_candidate = RootNodeCandidate::Valid(node_id);
} else if let RootNodeCandidate::Valid(root_candidate) = self.root_node_candidate {
if node_id != root_candidate {
self.root_node_candidate = RootNodeCandidate::Valid(node_id, vec![node_id]);
} else if let RootNodeCandidate::Valid(root_candidate, ref mut prev_path) =
&mut self.root_node_candidate
{
if node_id != *root_candidate {
let closest_parent = dom_adapter.closest_common_parent(&node_id, &root_candidate);

if let Some(closest_parent) = closest_parent {
self.root_node_candidate = RootNodeCandidate::Valid(closest_parent);
if let Some((closest_parent, path)) = closest_parent {
prev_path.extend(path);
*root_candidate = closest_parent;
}
}
}
Expand Down Expand Up @@ -218,12 +221,15 @@ impl<Key: NodeKey> Torin<Key> {

// Try saving using node's parent as root candidate if it has multiple children
if multiple_children {
if let RootNodeCandidate::Valid(root_candidate) = self.root_node_candidate {
if let RootNodeCandidate::Valid(root_candidate, ref mut prev_path) =
&mut self.root_node_candidate
{
let closest_parent =
dom_adapter.closest_common_parent(&parent_id, &root_candidate);

if let Some(closest_parent) = closest_parent {
self.root_node_candidate = RootNodeCandidate::Valid(closest_parent);
if let Some((closest_parent, path)) = closest_parent {
prev_path.extend(path);
*root_candidate = closest_parent;
}
}
}
Expand All @@ -234,7 +240,7 @@ impl<Key: NodeKey> Torin<Key> {

/// Get the Root Node candidate
pub fn get_root_candidate(&self) -> RootNodeCandidate<Key> {
self.root_node_candidate
self.root_node_candidate.clone()
}

/// Find the best root Node from where to start measuring
Expand Down Expand Up @@ -263,11 +269,12 @@ impl<Key: NodeKey> Torin<Key> {
}

// Try the Root candidate otherwise use the provided Root
let root_id = if let RootNodeCandidate::Valid(id) = self.root_node_candidate {
id
} else {
suggested_root_id
};
let (root_id, root_path) =
if let RootNodeCandidate::Valid(id, path) = self.root_node_candidate.clone() {
(id, path)
} else {
(suggested_root_id, vec![suggested_root_id])
};
let root_parent = dom_adapter.parent_of(&root_id);
let areas = root_parent
.and_then(|root_parent| self.get(root_parent).cloned())
Expand Down Expand Up @@ -296,6 +303,7 @@ impl<Key: NodeKey> Torin<Key> {
measurer,
true,
dom_adapter,
&root_path,
);

// Cache the root Node results if it was modified
Expand Down Expand Up @@ -331,6 +339,7 @@ fn measure_node<Key: NodeKey>(
measurer: &mut Option<impl LayoutMeasurer<Key>>,
must_cache: bool,
dom_adapter: &mut impl DOMAdapter<Key>,
root_path: &[Key],
) -> (bool, NodeAreas) {
let must_run = layout.dirty.contains(&node_id) || layout.results.get(&node_id).is_none();
if must_run {
Expand Down Expand Up @@ -431,6 +440,7 @@ fn measure_node<Key: NodeKey>(
must_cache,
&mut measurement_mode,
dom_adapter,
root_path,
);
}

Expand All @@ -446,28 +456,31 @@ fn measure_node<Key: NodeKey>(
} else {
let areas = layout.get(node_id).unwrap().clone();

let mut inner_sizes = areas.inner_sizes;
let mut available_area = areas.inner_area;
if root_path.contains(&node_id) {
let mut inner_sizes = areas.inner_sizes;
let mut available_area = areas.inner_area;

// TODO(marc2332): Should I also cache these?
available_area.origin.x += node.offset_x.get();
available_area.origin.y += node.offset_y.get();
// TODO(marc2332): Should I also cache these?
available_area.origin.x += node.offset_x.get();
available_area.origin.y += node.offset_y.get();

let mut measurement_mode = MeasureMode::ParentIsCached {
inner_area: &areas.inner_area,
};
let mut measurement_mode = MeasureMode::ParentIsCached {
inner_area: &areas.inner_area,
};

measure_inner_nodes(
&node_id,
node,
layout,
&mut available_area,
&mut inner_sizes,
measurer,
must_cache,
&mut measurement_mode,
dom_adapter,
);
measure_inner_nodes(
&node_id,
node,
layout,
&mut available_area,
&mut inner_sizes,
measurer,
must_cache,
&mut measurement_mode,
dom_adapter,
root_path,
);
}

(false, areas)
}
Expand Down Expand Up @@ -510,6 +523,7 @@ fn measure_inner_nodes<Key: NodeKey>(
must_cache: bool,
mode: &mut MeasureMode,
dom_adapter: &mut impl DOMAdapter<Key>,
root_path: &[Key],
) {
let children = dom_adapter.children_of(node_id);

Expand All @@ -531,6 +545,7 @@ fn measure_inner_nodes<Key: NodeKey>(
measurer,
false,
dom_adapter,
root_path,
);

// TODO(marc2332): Should I also reduce the width and heights?
Expand Down Expand Up @@ -573,6 +588,7 @@ fn measure_inner_nodes<Key: NodeKey>(
measurer,
must_cache,
dom_adapter,
root_path,
);

match node.direction {
Expand Down
14 changes: 11 additions & 3 deletions crates/torin/tests/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,13 @@ impl DOMAdapter<usize> for TestingDOM {
true
}

fn closest_common_parent(&self, node_id_a: &usize, _node_id_b: &usize) -> Option<usize> {
Some(self.parent_of(node_id_a).unwrap_or(*node_id_a))
fn closest_common_parent(
&self,
node_id_a: &usize,
_node_id_b: &usize,
) -> Option<(usize, Vec<usize>)> {
let parent = self.parent_of(node_id_a).unwrap_or(*node_id_a);
Some((parent, vec![parent]))
}
}

Expand Down Expand Up @@ -903,7 +908,10 @@ pub fn deep_tree() {
layout.invalidate(4);

layout.find_best_root(&mut mocked_dom);
assert_eq!(layout.get_root_candidate(), RootNodeCandidate::Valid(4));
assert_eq!(
layout.get_root_candidate(),
RootNodeCandidate::Valid(4, vec![4])
);

layout.measure(
0,
Expand Down

0 comments on commit 87f245d

Please sign in to comment.