gioser

tawasuyu/taffy

A high performance rust-powered UI layout library

HTML 5364Rust 86Markdown 12TOML 91841 Definitions

Fix performance issue with deep nesting

f388c84cc8b9722f82e7da9c9b87a5261f561a6a · by Brice DAVIER · (5 years ago) · parent 6879b9a1c · browse the tree at this commit

Semantic delta · 8

src/algo.rs

src/forest.rs

2 files · +32 −13

diff --git a/src/algo.rs b/src/algo.rs
index e22c21244..343e147c6 100644
--- a/src/algo.rs
+++ b/src/algo.rs
@@ -63,7 +63,7 @@ impl Forest {
             || style.max_size.height.is_defined();
 
         let result = if has_root_min_max {
-            let first_pass = self.compute_internal(root, style.size.resolve(size), size, false);
+            let first_pass = self.compute_internal(root, style.size.resolve(size), size, false, true);
 
             self.compute_internal(
                 root,
@@ -83,9 +83,11 @@ impl Forest {
                 },
                 size,
                 true,
+                true,
             )
         } else {
-            self.compute_internal(root, style.size.resolve(size), size, true)
+            self.compute_internal(root, style.size.resolve(size), size, true, true)
+
         };
 
         self.nodes[root].layout = result::Layout { order: 0, size: result.size, location: Point::zero() };
@@ -113,6 +115,14 @@ impl Forest {
         }
     }
 
+    fn cache(&mut self, node: NodeId, main_size: bool) -> &mut Option<result::Cache> {
+        if main_size { 
+            &mut self.nodes[node].main_size_layout_cache
+        } else { 
+            &mut self.nodes[node].other_layout_cache 
+        }
+    }
+
     #[allow(clippy::cognitive_complexity)]
     fn compute_internal(
         &mut self,
@@ -120,11 +130,12 @@ impl Forest {
         node_size: Size<Number>,
         parent_size: Size<Number>,
         perform_layout: bool,
+        main_size: bool,
     ) -> ComputeResult {
         self.nodes[node].is_dirty = false;
 
         // First we check if we have a result for the given input
-        if let Some(ref cache) = self.nodes[node].layout_cache {
+        if let Some(ref cache) = self.cache(node, main_size) {
             if cache.perform_layout || !perform_layout {
                 let width_compatible = if let Number::Defined(width) = node_size.width {
                     sys::abs(width - cache.result.size.width) < f32::EPSILON
@@ -187,7 +198,7 @@ impl Forest {
                     #[cfg(any(feature = "std", feature = "alloc"))]
                     MeasureFunc::Boxed(measure) => ComputeResult { size: measure(node_size) },
                 };
-                self.nodes[node].layout_cache =
+                *self.cache(node, main_size) =
                     Some(result::Cache { node_size, parent_size, perform_layout, result: result.clone() });
                 return result;
             }
@@ -331,6 +342,7 @@ impl Forest {
                     },
                     available_space,
                     false,
+                    true,
                 )
                 .size
                 .main(dir)
@@ -349,7 +361,7 @@ impl Forest {
             // webkit handled various scenarios. Can probably be solved better by passing in
             // min-content max-content constraints from the top
             let min_main = self
-                .compute_internal(child.node, Size::undefined(), available_space, false)
+                .compute_internal(child.node, Size::undefined(), available_space, false, false)
                 .size
                 .main(dir)
                 .maybe_max(child.min_size.main(dir))
@@ -455,6 +467,7 @@ impl Forest {
                             },
                             available_space,
                             false,
+                            false,
                         )
                         .size
                         .main(dir)
@@ -594,7 +607,7 @@ impl Forest {
                     // min-content max-content constraints from the top. Need to figure out correct thing to do here as
                     // just piling on more conditionals.
                     let min_main = if is_row && self.nodes[child.node].measure.is_none() {
-                        self.compute_internal(child.node, Size::undefined(), available_space, false)
+                        self.compute_internal(child.node, Size::undefined(), available_space, false, false)
                             .size
                             .width
                             .maybe_min(child.size.width)
@@ -676,6 +689,7 @@ impl Forest {
                             height: if is_row { available_space.height } else { container_size.main(dir).into() },
                         },
                         false,
+                        false,
                     )
                     .size
                     .cross(dir)
@@ -723,6 +737,7 @@ impl Forest {
                             height: if is_row { node_size.height } else { container_size.height.into() },
                         },
                         true,
+                        false
                     );
 
                     child.baseline = calc_baseline(
@@ -1048,7 +1063,7 @@ impl Forest {
         // layout we are done now.
         if !perform_layout {
             let result = ComputeResult { size: container_size };
-            self.nodes[node].layout_cache =
+            *self.cache(node, main_size) =
                 Some(result::Cache { node_size, parent_size, perform_layout, result: result.clone() });
             return result;
         }
@@ -1121,6 +1136,7 @@ impl Forest {
                         child.target_size.map(|s| s.into()),
                         container_size.map(|s| s.into()),
                         true,
+                        false,
                     );
 
                     let offset_main = total_offset_main
@@ -1218,7 +1234,8 @@ impl Forest {
                     child,
                     Size { width, height },
                     Size { width: container_width, height: container_height },
-                    true,
+                    true, 
+                    false,
                 );
 
                 let free_main_space = container_size.main(dir)
@@ -1308,7 +1325,7 @@ impl Forest {
         }
 
         let result = ComputeResult { size: container_size };
-        self.nodes[node].layout_cache =
+        *self.cache(node, main_size) =
             Some(result::Cache { node_size, parent_size, perform_layout, result: result.clone() });
 
         result
diff --git a/src/forest.rs b/src/forest.rs
index cdd26ec1b..d29dfbf9b 100644
--- a/src/forest.rs
+++ b/src/forest.rs
@@ -13,17 +13,18 @@ pub(crate) struct NodeData {
     pub(crate) style: Style,
     pub(crate) measure: Option<MeasureFunc>,
     pub(crate) layout: Layout,
-    pub(crate) layout_cache: Option<Cache>,
+    pub(crate) main_size_layout_cache: Option<Cache>,
+    pub(crate) other_layout_cache: Option<Cache>,
     pub(crate) is_dirty: bool,
 }
 
 impl NodeData {
     fn new_leaf(style: Style, measure: MeasureFunc) -> Self {
-        Self { style, measure: Some(measure), layout_cache: None, layout: Layout::new(), is_dirty: true }
+        Self { style, measure: Some(measure), main_size_layout_cache: None, other_layout_cache: None, layout: Layout::new(), is_dirty: true }
     }
 
     fn new(style: Style) -> Self {
-        Self { style, measure: None, layout_cache: None, layout: Layout::new(), is_dirty: true }
+        Self { style, measure: None, main_size_layout_cache: None, other_layout_cache: None, layout: Layout::new(), is_dirty: true }
     }
 }
 
@@ -157,7 +158,8 @@ impl Forest {
     pub fn mark_dirty(&mut self, node: NodeId) {
         fn mark_dirty_impl(nodes: &mut sys::Vec<NodeData>, parents: &[sys::ParentsVec<NodeId>], node_id: NodeId) {
             let node = &mut nodes[node_id];
-            node.layout_cache = None;
+            node.main_size_layout_cache = None;
+            node.other_layout_cache = None;
             node.is_dirty = true;
 
             for parent in &parents[node_id] {