gioser

tawasuyu/taffy

A high performance rust-powered UI layout library

Rust 1061HTML 997Markdown 12TOML 83120 Definitions

Fix root measure size resolution

31042ffa3447d323393e6c0ffb084659a8552a3f · by Emil Sjölander · (7 years ago) · parent c18952a0d · browse the tree at this commit

Semantic delta · 11

src/algo.rs

src/style.rs

tests/measure.rs

tests/root_constraints.rs

5 files · +87 −8

diff --git a/Cargo.toml b/Cargo.toml
index 6f696c7a0..e503e5a71 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "stretch"
-version = "0.1.4"
+version = "0.1.5"
 authors = ["Emil Sjölander <emil@visly.app>"]
 edition = "2018"
 include = ["src/**/*", "Cargo.toml"]
diff --git a/src/algo.rs b/src/algo.rs
index 4a52786dd..9aeaa836c 100644
--- a/src/algo.rs
+++ b/src/algo.rs
@@ -59,7 +59,7 @@ pub fn compute(root: &style::Node, size: Size<Number>) -> layout::Node {
     // Probably want to pass min/max down as top level paramerer instead.
     let first_pass = compute_internal(
         root,
-        Size { width: root.size.width.resolve(size.width), height: root.size.height.resolve(size.width) },
+        Size { width: root.size.width.resolve(size.width), height: root.size.height.resolve(size.height) },
         size,
         size.width,
     );
@@ -76,8 +76,8 @@ pub fn compute(root: &style::Node, size: Size<Number>) -> layout::Node {
             height: first_pass
                 .size
                 .height
-                .maybe_max(root.min_size.height.resolve(size.width))
-                .maybe_min(root.max_size.height.resolve(size.width))
+                .maybe_max(root.min_size.height.resolve(size.height))
+                .maybe_min(root.max_size.height.resolve(size.height))
                 .to_number(),
         },
         size,
@@ -118,8 +118,30 @@ fn compute_internal(
             return ComputeResult { size: node_size.map(|s| s.or_else(0.0)), children: vec![] };
         }
 
+        // Measure function can be extremely expensive to call so cache calls to it
         if let Some(ref measure) = node.measure {
-            return ComputeResult { size: measure(node_size), children: vec![] };
+            let mut cache_list = node.measure_cache.get();
+            for cache in &cache_list.caches {
+                if let Some(cache) = cache {
+                    let width_compatible =
+                        if let Number::Defined(width) = node_size.width { width == cache.result.width } else { true };
+
+                    let height_compatible = if let Number::Defined(height) = node_size.height {
+                        height == cache.result.height
+                    } else {
+                        true
+                    };
+
+                    if cache.constraint == node_size || (width_compatible && height_compatible) {
+                        return ComputeResult { size: cache.result, children: vec![] };
+                    }
+                }
+            }
+
+            let size = measure(node_size);
+            cache_list.add(MeasureCache { constraint: node_size, result: size });
+            node.measure_cache.set(cache_list);
+            return ComputeResult { size, children: vec![] };
         }
     }
 
diff --git a/src/style.rs b/src/style.rs
index e80b4a4c8..edc5a8444 100644
--- a/src/style.rs
+++ b/src/style.rs
@@ -191,6 +191,31 @@ impl Default for Size<Dimension> {
     }
 }
 
+type MeasureFunc = Box<Fn(Size<Number>) -> Size<f32>>;
+
+#[derive(Copy, Clone)]
+pub struct MeasureCache {
+    pub constraint: Size<Number>,
+    pub result: Size<f32>,
+}
+
+#[derive(Copy, Clone)]
+pub struct MeasureCacheList {
+    pub caches: [Option<MeasureCache>; 5],
+    pub index: usize,
+}
+
+impl MeasureCacheList {
+    pub fn empty() -> MeasureCacheList {
+        MeasureCacheList { caches: [None, None, None, None, None], index: 0 }
+    }
+
+    pub fn add(&mut self, cache: MeasureCache) {
+        self.caches[self.index] = Some(cache);
+        self.index = (self.index + 1) % 6;
+    }
+}
+
 pub struct Node {
     pub display: Display,
 
@@ -221,9 +246,11 @@ pub struct Node {
     pub max_size: Size<Dimension>,
 
     pub aspect_ratio: Number,
-    pub measure: Option<Box<Fn(Size<Number>) -> Size<f32>>>,
+    pub measure: Option<MeasureFunc>,
 
     pub children: Vec<Node>,
+
+    pub measure_cache: std::cell::Cell<MeasureCacheList>,
 }
 
 impl Default for Node {
@@ -261,6 +288,8 @@ impl Default for Node {
             measure: None,
 
             children: vec![],
+
+            measure_cache: std::cell::Cell::new(MeasureCacheList::empty()),
         }
     }
 }
diff --git a/tests/measure.rs b/tests/measure.rs
index c2c39d88c..a174ade73 100644
--- a/tests/measure.rs
+++ b/tests/measure.rs
@@ -402,4 +402,32 @@ mod measure {
         assert_eq!(layout.children[0].size.width, 100.0);
         assert_eq!(layout.children[0].size.height, 100.0);
     }
+
+    #[test]
+    fn only_measure_once() {
+        let mut num_measure = 0;
+        let num_measure_ptr = &mut num_measure as *mut i32;
+
+        stretch::compute(
+            &stretch::style::Node {
+                children: vec![stretch::style::Node {
+                    children: vec![stretch::style::Node {
+                        measure: Some(Box::new(move |constraint| {
+                            unsafe { (*num_measure_ptr) += 1 };
+                            stretch::geometry::Size {
+                                width: constraint.width.or_else(50.0),
+                                height: constraint.height.or_else(50.0),
+                            }
+                        })),
+                        ..Default::default()
+                    }],
+                    ..Default::default()
+                }],
+                ..Default::default()
+            },
+            stretch::geometry::Size::undefined(),
+        );
+
+        assert_eq!(num_measure, 1);
+    }
 }
diff --git a/tests/root_constraints.rs b/tests/root_constraints.rs
index eaa6aad38..ab12421d0 100644
--- a/tests/root_constraints.rs
+++ b/tests/root_constraints.rs
@@ -12,11 +12,11 @@ mod root_constraints {
                 },
                 ..Default::default()
             },
-            stretch::geometry::Size { width: Number::Defined(100.0), height: Number::Defined(100.0) },
+            stretch::geometry::Size { width: Number::Defined(100.0), height: Number::Defined(200.0) },
         );
 
         assert_eq!(layout.size.width, 100.0);
-        assert_eq!(layout.size.height, 100.0);
+        assert_eq!(layout.size.height, 200.0);
     }
 
     #[test]