Semantic delta · 11
tests/root_constraints.rs
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]