gioser

tawasuyu/taffy

A high performance rust-powered UI layout library

Rust 501HTML 254C 19JavaScript 81412 Sutichaykuna

Split out caching into separate `CacheTree` trait (#758)

63f4adebf32bb8be51b0c5e432b2ec5794ba4b2f · ruwaq Nico Burns · (1 wata ñawpaq) · tayta f37efc58a · kay commitpi sach'ata qhaway

Semántico sayk'uy · 28

examples/custom_tree_owned_partial.rs

examples/custom_tree_owned_unsafe.rs

examples/custom_tree_vec.rs

src/compute/mod.rs

src/tree/taffy_tree.rs

src/tree/traits.rs

6 qillqakuna · +179 −57

diff --git a/examples/custom_tree_owned_partial.rs b/examples/custom_tree_owned_partial.rs
index 430381ef7..7cae4b6ae 100644
--- a/examples/custom_tree_owned_partial.rs
+++ b/examples/custom_tree_owned_partial.rs
@@ -13,7 +13,7 @@ use common::image::{image_measure_function, ImageContext};
 use common::text::{text_measure_function, FontMetrics, TextContext, WritingMode, LOREM_IPSUM};
 use taffy::{
     compute_cached_layout, compute_flexbox_layout, compute_grid_layout, compute_leaf_layout, compute_root_layout,
-    prelude::*, Cache, Layout, Style,
+    prelude::*, Cache, CacheTree, Layout, Style,
 };
 
 #[derive(Debug, Copy, Clone)]
@@ -138,11 +138,6 @@ impl taffy::LayoutPartialTree for Node {
     where
         Self: 'a;
 
-    type CacheMut<'b>
-        = &'b mut Cache
-    where
-        Self: 'b;
-
     fn get_core_container_style(&self, node_id: NodeId) -> Self::CoreContainerStyle<'_> {
         &self.node_from_id(node_id).style
     }
@@ -151,10 +146,6 @@ impl taffy::LayoutPartialTree for Node {
         self.node_from_id_mut(node_id).layout = *layout
     }
 
-    fn get_cache_mut(&mut self, node_id: NodeId) -> &mut Cache {
-        &mut self.node_from_id_mut(node_id).cache
-    }
-
     fn compute_child_layout(&mut self, node_id: NodeId, inputs: taffy::tree::LayoutInput) -> taffy::tree::LayoutOutput {
         compute_cached_layout(self, node_id, inputs, |parent, node_id, inputs| {
             let node = parent.node_from_id_mut(node_id);
@@ -179,6 +170,33 @@ impl taffy::LayoutPartialTree for Node {
     }
 }
 
+impl CacheTree for Node {
+    fn cache_get(
+        &self,
+        node_id: NodeId,
+        known_dimensions: Size<Option<f32>>,
+        available_space: Size<AvailableSpace>,
+        run_mode: taffy::RunMode,
+    ) -> Option<taffy::LayoutOutput> {
+        self.node_from_id(node_id).cache.get(known_dimensions, available_space, run_mode)
+    }
+
+    fn cache_store(
+        &mut self,
+        node_id: NodeId,
+        known_dimensions: Size<Option<f32>>,
+        available_space: Size<AvailableSpace>,
+        run_mode: taffy::RunMode,
+        layout_output: taffy::LayoutOutput,
+    ) {
+        self.node_from_id_mut(node_id).cache.store(known_dimensions, available_space, run_mode, layout_output)
+    }
+
+    fn cache_clear(&mut self, node_id: NodeId) {
+        self.node_from_id_mut(node_id).cache.clear()
+    }
+}
+
 impl taffy::LayoutFlexboxContainer for Node {
     type FlexboxContainerStyle<'a>
         = &'a Style
diff --git a/examples/custom_tree_owned_unsafe.rs b/examples/custom_tree_owned_unsafe.rs
index ce6cd28c0..753424b06 100644
--- a/examples/custom_tree_owned_unsafe.rs
+++ b/examples/custom_tree_owned_unsafe.rs
@@ -8,7 +8,7 @@ use taffy::tree::Cache;
 use taffy::util::print_tree;
 use taffy::{
     compute_cached_layout, compute_flexbox_layout, compute_grid_layout, compute_leaf_layout, compute_root_layout,
-    prelude::*, round_layout,
+    prelude::*, round_layout, CacheTree,
 };
 
 #[derive(Debug, Copy, Clone)]
@@ -135,11 +135,6 @@ impl LayoutPartialTree for StatelessLayoutTree {
     where
         Self: 'a;
 
-    type CacheMut<'b>
-        = &'b mut Cache
-    where
-        Self: 'b;
-
     fn get_core_container_style(&self, node_id: NodeId) -> Self::CoreContainerStyle<'_> {
         unsafe { &node_from_id(node_id).style }
     }
@@ -148,10 +143,6 @@ impl LayoutPartialTree for StatelessLayoutTree {
         unsafe { node_from_id_mut(node_id).unrounded_layout = *layout };
     }
 
-    fn get_cache_mut(&mut self, node_id: NodeId) -> &mut Cache {
-        unsafe { &mut node_from_id_mut(node_id).cache }
-    }
-
     fn compute_child_layout(&mut self, node_id: NodeId, inputs: taffy::tree::LayoutInput) -> taffy::tree::LayoutOutput {
         compute_cached_layout(self, node_id, inputs, |tree, node_id, inputs| {
             let node = unsafe { node_from_id_mut(node_id) };
@@ -176,6 +167,33 @@ impl LayoutPartialTree for StatelessLayoutTree {
     }
 }
 
+impl CacheTree for StatelessLayoutTree {
+    fn cache_get(
+        &self,
+        node_id: NodeId,
+        known_dimensions: Size<Option<f32>>,
+        available_space: Size<AvailableSpace>,
+        run_mode: taffy::RunMode,
+    ) -> Option<taffy::LayoutOutput> {
+        unsafe { node_from_id(node_id) }.cache.get(known_dimensions, available_space, run_mode)
+    }
+
+    fn cache_store(
+        &mut self,
+        node_id: NodeId,
+        known_dimensions: Size<Option<f32>>,
+        available_space: Size<AvailableSpace>,
+        run_mode: taffy::RunMode,
+        layout_output: taffy::LayoutOutput,
+    ) {
+        unsafe { node_from_id_mut(node_id) }.cache.store(known_dimensions, available_space, run_mode, layout_output)
+    }
+
+    fn cache_clear(&mut self, node_id: NodeId) {
+        unsafe { node_from_id_mut(node_id) }.cache.clear()
+    }
+}
+
 impl taffy::LayoutFlexboxContainer for StatelessLayoutTree {
     type FlexboxContainerStyle<'a>
         = &'a Style
diff --git a/examples/custom_tree_vec.rs b/examples/custom_tree_vec.rs
index a2cf7e00b..89ed08584 100644
--- a/examples/custom_tree_vec.rs
+++ b/examples/custom_tree_vec.rs
@@ -7,7 +7,7 @@ use common::text::{text_measure_function, FontMetrics, TextContext, WritingMode,
 use taffy::util::print_tree;
 use taffy::{
     compute_cached_layout, compute_flexbox_layout, compute_grid_layout, compute_leaf_layout, compute_root_layout,
-    prelude::*, round_layout, Cache,
+    prelude::*, round_layout, Cache, CacheTree,
 };
 
 #[derive(Debug, Copy, Clone)]
@@ -144,11 +144,6 @@ impl taffy::LayoutPartialTree for Tree {
     where
         Self: 'a;
 
-    type CacheMut<'b>
-        = &'b mut Cache
-    where
-        Self: 'b;
-
     fn get_core_container_style(&self, node_id: NodeId) -> Self::CoreContainerStyle<'_> {
         &self.node_from_id(node_id).style
     }
@@ -157,10 +152,6 @@ impl taffy::LayoutPartialTree for Tree {
         self.node_from_id_mut(node_id).unrounded_layout = *layout;
     }
 
-    fn get_cache_mut(&mut self, node_id: NodeId) -> &mut Cache {
-        &mut self.node_from_id_mut(node_id).cache
-    }
-
     fn compute_child_layout(&mut self, node_id: NodeId, inputs: taffy::tree::LayoutInput) -> taffy::tree::LayoutOutput {
         compute_cached_layout(self, node_id, inputs, |tree, node_id, inputs| {
             let node = tree.node_from_id_mut(node_id);
@@ -185,6 +176,33 @@ impl taffy::LayoutPartialTree for Tree {
     }
 }
 
+impl CacheTree for Tree {
+    fn cache_get(
+        &self,
+        node_id: NodeId,
+        known_dimensions: Size<Option<f32>>,
+        available_space: Size<AvailableSpace>,
+        run_mode: taffy::RunMode,
+    ) -> Option<taffy::LayoutOutput> {
+        self.node_from_id(node_id).cache.get(known_dimensions, available_space, run_mode)
+    }
+
+    fn cache_store(
+        &mut self,
+        node_id: NodeId,
+        known_dimensions: Size<Option<f32>>,
+        available_space: Size<AvailableSpace>,
+        run_mode: taffy::RunMode,
+        layout_output: taffy::LayoutOutput,
+    ) {
+        self.node_from_id_mut(node_id).cache.store(known_dimensions, available_space, run_mode, layout_output)
+    }
+
+    fn cache_clear(&mut self, node_id: NodeId) {
+        self.node_from_id_mut(node_id).cache.clear()
+    }
+}
+
 impl taffy::LayoutFlexboxContainer for Tree {
     type FlexboxContainerStyle<'a>
         = &'a Style
diff --git a/src/compute/mod.rs b/src/compute/mod.rs
index 1c6ba62a5..42c3b03f3 100644
--- a/src/compute/mod.rs
+++ b/src/compute/mod.rs
@@ -52,7 +52,7 @@ use crate::tree::{
 use crate::util::debug::{debug_log, debug_log_node, debug_pop_node, debug_push_node};
 use crate::util::sys::round;
 use crate::util::ResolveOrZero;
-use crate::{BoxSizing, MaybeMath, MaybeResolve};
+use crate::{BoxSizing, CacheTree, MaybeMath, MaybeResolve};
 
 /// Compute layout for the root node in the tree
 pub fn compute_root_layout(tree: &mut impl LayoutPartialTree, root: NodeId, available_space: Size<AvailableSpace>) {
@@ -153,7 +153,7 @@ pub fn compute_root_layout(tree: &mut impl LayoutPartialTree, root: NodeId, avai
 ///
 /// Uses the provided closure to compute the layout (and then stores the result in the cache) if no cached layout is found.
 #[inline(always)]
-pub fn compute_cached_layout<Tree: LayoutPartialTree + ?Sized, ComputeFunction>(
+pub fn compute_cached_layout<Tree: CacheTree + ?Sized, ComputeFunction>(
     tree: &mut Tree,
     node: NodeId,
     inputs: LayoutInput,
@@ -166,7 +166,7 @@ where
     let LayoutInput { known_dimensions, available_space, run_mode, .. } = inputs;
 
     // First we check if we have a cached result for the given input
-    let cache_entry = tree.get_cache_mut(node).get(known_dimensions, available_space, run_mode);
+    let cache_entry = tree.cache_get(node, known_dimensions, available_space, run_mode);
     if let Some(cached_size_and_baselines) = cache_entry {
         debug_log_node!(known_dimensions, inputs.parent_size, available_space, run_mode, inputs.sizing_mode);
         debug_log!("RESULT (CACHED)", dbg:cached_size_and_baselines.size);
@@ -179,7 +179,7 @@ where
     let computed_size_and_baselines = compute_uncached(tree, node, inputs);
 
     // Cache result
-    tree.get_cache_mut(node).store(known_dimensions, available_space, run_mode, computed_size_and_baselines);
+    tree.cache_store(node, known_dimensions, available_space, run_mode, computed_size_and_baselines);
 
     debug_log!("RESULT", dbg:computed_size_and_baselines.size);
     debug_pop_node!();
@@ -258,9 +258,9 @@ pub fn round_layout(tree: &mut impl RoundTree, node_id: NodeId) {
 
 /// Creates a layout for this node and its children, recursively.
 /// Each hidden node has zero size and is placed at the origin
-pub fn compute_hidden_layout(tree: &mut impl LayoutPartialTree, node: NodeId) -> LayoutOutput {
+pub fn compute_hidden_layout(tree: &mut (impl LayoutPartialTree + CacheTree), node: NodeId) -> LayoutOutput {
     // Clear cache and set zeroed-out layout for the node
-    tree.get_cache_mut(node).clear();
+    tree.cache_clear(node);
     tree.set_unrounded_layout(node, &Layout::with_order(0));
 
     // Perform hidden layout on all children
diff --git a/src/tree/taffy_tree.rs b/src/tree/taffy_tree.rs
index 732acf99a..69bcc43c4 100644
--- a/src/tree/taffy_tree.rs
+++ b/src/tree/taffy_tree.rs
@@ -17,6 +17,7 @@ use crate::util::sys::{new_vec_with_capacity, ChildrenVec, Vec};
 use crate::compute::{
     compute_cached_layout, compute_hidden_layout, compute_leaf_layout, compute_root_layout, round_layout,
 };
+use crate::CacheTree;
 #[cfg(feature = "block_layout")]
 use crate::{compute::compute_block_layout, LayoutBlockContainer};
 #[cfg(feature = "flexbox")]
@@ -190,6 +191,34 @@ impl<NodeContext> TraversePartialTree for TaffyTree<NodeContext> {
 // TraverseTree impl for TaffyTree
 impl<NodeContext> TraverseTree for TaffyTree<NodeContext> {}
 
+// CacheTree impl for TaffyTree
+impl<NodeContext> CacheTree for TaffyTree<NodeContext> {
+    fn cache_get(
+        &self,
+        node_id: NodeId,
+        known_dimensions: Size<Option<f32>>,
+        available_space: Size<AvailableSpace>,
+        run_mode: RunMode,
+    ) -> Option<LayoutOutput> {
+        self.nodes[node_id.into()].cache.get(known_dimensions, available_space, run_mode)
+    }
+
+    fn cache_store(
+        &mut self,
+        node_id: NodeId,
+        known_dimensions: Size<Option<f32>>,
+        available_space: Size<AvailableSpace>,
+        run_mode: RunMode,
+        layout_output: LayoutOutput,
+    ) {
+        self.nodes[node_id.into()].cache.store(known_dimensions, available_space, run_mode, layout_output)
+    }
+
+    fn cache_clear(&mut self, node_id: NodeId) {
+        self.nodes[node_id.into()].cache.clear()
+    }
+}
+
 // PrintTree impl for TaffyTree
 impl<NodeContext> PrintTree for TaffyTree<NodeContext> {
     #[inline(always)]
@@ -284,21 +313,12 @@ where
         = &'a Style
     where
         Self: 'a;
-    type CacheMut<'b>
-        = &'b mut Cache
-    where
-        Self: 'b;
 
     #[inline(always)]
     fn get_core_container_style(&self, node_id: NodeId) -> Self::CoreContainerStyle<'_> {
         &self.taffy.nodes[node_id.into()].style
     }
 
-    #[inline(always)]
-    fn get_cache_mut(&mut self, node: NodeId) -> &mut Cache {
-        &mut self.taffy.nodes[node.into()].cache
-    }
-
     #[inline(always)]
     fn set_unrounded_layout(&mut self, node_id: NodeId, layout: &Layout) {
         self.taffy.nodes[node_id.into()].unrounded_layout = *layout;
@@ -355,6 +375,37 @@ where
     }
 }
 
+impl<NodeContext, MeasureFunction> CacheTree for TaffyView<'_, NodeContext, MeasureFunction>
+where
+    MeasureFunction:
+        FnMut(Size<Option<f32>>, Size<AvailableSpace>, NodeId, Option<&mut NodeContext>, &Style) -> Size<f32>,
+{
+    fn cache_get(
+        &self,
+        node_id: NodeId,
+        known_dimensions: Size<Option<f32>>,
+        available_space: Size<AvailableSpace>,
+        run_mode: RunMode,
+    ) -> Option<LayoutOutput> {
+        self.taffy.nodes[node_id.into()].cache.get(known_dimensions, available_space, run_mode)
+    }
+
+    fn cache_store(
+        &mut self,
+        node_id: NodeId,
+        known_dimensions: Size<Option<f32>>,
+        available_space: Size<AvailableSpace>,
+        run_mode: RunMode,
+        layout_output: LayoutOutput,
+    ) {
+        self.taffy.nodes[node_id.into()].cache.store(known_dimensions, available_space, run_mode, layout_output)
+    }
+
+    fn cache_clear(&mut self, node_id: NodeId) {
+        self.taffy.nodes[node_id.into()].cache.clear()
+    }
+}
+
 #[cfg(feature = "block_layout")]
 impl<NodeContext, MeasureFunction> LayoutBlockContainer for TaffyView<'_, NodeContext, MeasureFunction>
 where
@@ -804,7 +855,7 @@ impl<NodeContext> TaffyTree<NodeContext> {
 
     /// Returns an instance of LayoutTree representing the TaffyTree
     #[cfg(test)]
-    pub(crate) fn as_layout_tree(&mut self) -> impl LayoutPartialTree + '_ {
+    pub(crate) fn as_layout_tree(&mut self) -> impl LayoutPartialTree + CacheTree + '_ {
         TaffyView { taffy: self, measure_function: |_, _, _, _, _| Size::ZERO }
     }
 }
diff --git a/src/tree/traits.rs b/src/tree/traits.rs
index 2642b0749..d70cc7416 100644
--- a/src/tree/traits.rs
+++ b/src/tree/traits.rs
@@ -126,7 +126,7 @@
 //! }
 //! ```
 //!
-use super::{Cache, Layout, LayoutInput, LayoutOutput, NodeId, RequestedAxis, RunMode, SizingMode};
+use super::{Layout, LayoutInput, LayoutOutput, NodeId, RequestedAxis, RunMode, SizingMode};
 use crate::geometry::{AbsoluteAxis, Line, Size};
 use crate::style::{AvailableSpace, CoreStyle};
 #[cfg(feature = "flexbox")]
@@ -135,7 +135,6 @@ use crate::style::{FlexboxContainerStyle, FlexboxItemStyle};
 use crate::style::{GridContainerStyle, GridItemStyle};
 #[cfg(feature = "block_layout")]
 use crate::{BlockContainerStyle, BlockItemStyle};
-use core::ops::{Deref, DerefMut};
 
 /// Taffy's abstraction for downward tree traversal.
 ///
@@ -173,25 +172,43 @@ pub trait LayoutPartialTree: TraversePartialTree {
     where
         Self: 'a;
 
-    /// A mutable reference to the cache. This is an associated type to allow for different
-    /// types of mutable reference such as mutex or refcell guards
-    type CacheMut<'b>: Deref<Target = Cache> + DerefMut
-    where
-        Self: 'b;
-
     /// Get core style
     fn get_core_container_style(&self, node_id: NodeId) -> Self::CoreContainerStyle<'_>;
 
     /// Set the node's unrounded layout
     fn set_unrounded_layout(&mut self, node_id: NodeId, layout: &Layout);
 
-    /// Get a mutable reference to the [`Cache`] for this node.
-    fn get_cache_mut(&mut self, node_id: NodeId) -> Self::CacheMut<'_>;
-
     /// Compute the specified node's size or full layout given the specified constraints
     fn compute_child_layout(&mut self, node_id: NodeId, inputs: LayoutInput) -> LayoutOutput;
 }
 
+/// Trait used by the `compute_cached_layout` method which allows cached layout results to be stored and retrieved.
+///
+/// The `Cache` struct implements a per-node cache that is compatible with this trait.
+pub trait CacheTree {
+    /// Try to retrieve a cached result from the cache
+    fn cache_get(
+        &self,
+        node_id: NodeId,
+        known_dimensions: Size<Option<f32>>,
+        available_space: Size<AvailableSpace>,
+        run_mode: RunMode,
+    ) -> Option<LayoutOutput>;
+
+    /// Store a computed size in the cache
+    fn cache_store(
+        &mut self,
+        node_id: NodeId,
+        known_dimensions: Size<Option<f32>>,
+        available_space: Size<AvailableSpace>,
+        run_mode: RunMode,
+        layout_output: LayoutOutput,
+    );
+
+    /// Clear all cache entries for the node
+    fn cache_clear(&mut self, node_id: NodeId);
+}
+
 /// Trait used by the `round_layout` method which takes a tree of unrounded float-valued layouts and performs
 /// rounding to snap the values to the pixel grid.
 ///