tawasuyu

tawasuyu/taffy

A high performance rust-powered UI layout library

Rust 520HTML 244Markdown 7TOML 41339 Definitions

Reduce style accesses (#386)

* Use cached flex_grow and flex_shrink values when resolving flexible lengths

* Cache auto margins on FlexItem struct

* Use constants.is_wrap instead of re-resolving style

* Use cached align_content value rather than re-resolving style

* Cache justify_content style in AlgoConstants

6be6a471e913666180be2df76ab019f0b2385f0c · by Nico Burns · (3 years ago) · parent 6f4bb570b · browse the tree at this commit

Semantic delta · 13

src/compute/flexbox.rs

1 files · +42 −68

diff --git a/src/compute/flexbox.rs b/src/compute/flexbox.rs
index 62626338c..13458ea4d 100644
--- a/src/compute/flexbox.rs
+++ b/src/compute/flexbox.rs
@@ -76,6 +76,8 @@ struct FlexItem {
     inset: Rect<Option<f32>>,
     /// The margin of this item
     margin: Rect<f32>,
+    /// Whether each margin is an auto margin or not
+    margin_is_auto: Rect<bool>,
     /// The padding of this item
     padding: Rect<f32>,
     /// The border of this item
@@ -153,6 +155,8 @@ struct AlgoConstants {
     align_items: AlignItems,
     /// The align_content property of this node
     align_content: AlignContent,
+    /// The justify_content property of this node
+    justify_content: Option<JustifyContent>,
 
     /// The border-box size of the node being laid out (if known)
     node_outer_size: Size<Option<f32>>,
@@ -283,7 +287,7 @@ fn compute_preliminary(
     // 5. Collect flex items into flex lines.
     #[cfg(feature = "debug")]
     NODE_LOGGER.log("collect_flex_lines");
-    let mut flex_lines = collect_flex_lines(tree, node, &constants, available_space, &mut flex_items);
+    let mut flex_lines = collect_flex_lines(&constants, available_space, &mut flex_items);
 
     // If container size is undefined, determine the container's main size
     // and then re-resolve gaps based on newly determined size
@@ -309,7 +313,7 @@ fn compute_preliminary(
     #[cfg(feature = "debug")]
     NODE_LOGGER.log("resolve_flexible_lengths");
     for line in &mut flex_lines {
-        resolve_flexible_lengths(tree, line, &constants, original_gap);
+        resolve_flexible_lengths(line, &constants, original_gap);
     }
 
     // 9.4. Cross Size Determination
@@ -330,7 +334,7 @@ fn compute_preliminary(
     // 8. Calculate the cross size of each flex line.
     #[cfg(feature = "debug")]
     NODE_LOGGER.log("calculate_cross_size");
-    calculate_cross_size(tree, &mut flex_lines, known_dimensions, &constants);
+    calculate_cross_size(&mut flex_lines, known_dimensions, &constants);
 
     // 9. Handle 'align-content: stretch'.
     #[cfg(feature = "debug")]
@@ -362,14 +366,14 @@ fn compute_preliminary(
     // 12. Distribute any remaining free space.
     #[cfg(feature = "debug")]
     NODE_LOGGER.log("distribute_remaining_free_space");
-    distribute_remaining_free_space(tree, &mut flex_lines, node, &constants);
+    distribute_remaining_free_space(&mut flex_lines, &constants);
 
     // 9.6. Cross-Axis Alignment
 
     // 13. Resolve cross-axis auto margins (also includes 14).
     #[cfg(feature = "debug")]
     NODE_LOGGER.log("resolve_cross_axis_auto_margins");
-    resolve_cross_axis_auto_margins(tree, &mut flex_lines, &constants);
+    resolve_cross_axis_auto_margins(&mut flex_lines, &constants);
 
     // 15. Determine the flex container’s used cross size.
     #[cfg(feature = "debug")]
@@ -385,7 +389,7 @@ fn compute_preliminary(
     // 16. Align all flex lines per align-content.
     #[cfg(feature = "debug")]
     NODE_LOGGER.log("align_flex_lines_per_align_content");
-    align_flex_lines_per_align_content(tree, &mut flex_lines, node, &constants, total_line_cross_size);
+    align_flex_lines_per_align_content(&mut flex_lines, &constants, total_line_cross_size);
 
     // Do a final layout pass and gather the resulting layouts
     #[cfg(feature = "debug")]
@@ -450,8 +454,9 @@ fn compute_constants(
     let margin = style.margin.resolve_or_zero(parent_size.width);
     let padding = style.padding.resolve_or_zero(parent_size.width);
     let border = style.border.resolve_or_zero(parent_size.width);
-    let align_items = style.align_items.unwrap_or(crate::style::AlignItems::Stretch);
-    let align_content = style.align_content.unwrap_or(crate::style::AlignContent::Stretch);
+    let align_items = style.align_items.unwrap_or(AlignItems::Stretch);
+    let align_content = style.align_content.unwrap_or(AlignContent::Stretch);
+    let justify_content = style.justify_content;
 
     let padding_border = padding + border;
 
@@ -474,6 +479,7 @@ fn compute_constants(
         padding_border,
         align_items,
         align_content,
+        justify_content,
         node_outer_size,
         node_inner_size,
         container_size,
@@ -508,6 +514,7 @@ fn generate_anonymous_flex_items(tree: &impl LayoutTree, node: Node, constants:
 
                 inset: child_style.inset.zip_size(constants.node_inner_size, |p, s| p.maybe_resolve(s)),
                 margin: child_style.margin.resolve_or_zero(constants.node_inner_size.width),
+                margin_is_auto: child_style.margin.map(|m| m == LengthPercentageAuto::Auto),
                 padding: child_style.padding.resolve_or_zero(constants.node_inner_size.width),
                 border: child_style.border.resolve_or_zero(constants.node_inner_size.width),
                 align_self: child_style.align_self.unwrap_or(constants.align_items),
@@ -725,13 +732,11 @@ fn determine_flex_base_size(
 ///         **Note that the "collect as many" line will collect zero-sized flex items onto the end of the previous line even if the last non-zero item exactly "filled up" the line**.
 #[inline]
 fn collect_flex_lines<'a>(
-    tree: &impl LayoutTree,
-    node: Node,
     constants: &AlgoConstants,
     available_space: Size<AvailableSpace>,
     flex_items: &'a mut Vec<FlexItem>,
 ) -> Vec<FlexLine<'a>> {
-    if tree.style(node).flex_wrap == FlexWrap::NoWrap {
+    if !constants.is_wrap {
         let mut lines = new_vec_with_capacity(1);
         lines.push(FlexLine { items: flex_items.as_mut_slice(), cross_size: 0.0, offset_cross: 0.0 });
         lines
@@ -966,12 +971,7 @@ fn determine_container_main_size(
 ///
 /// # [9.7. Resolving Flexible Lengths](https://www.w3.org/TR/css-flexbox-1/#resolve-flexible-lengths)
 #[inline]
-fn resolve_flexible_lengths(
-    tree: &mut impl LayoutTree,
-    line: &mut FlexLine,
-    constants: &AlgoConstants,
-    original_gap: Size<f32>,
-) {
+fn resolve_flexible_lengths(line: &mut FlexLine, constants: &AlgoConstants, original_gap: Size<f32>) {
     let total_original_main_axis_gap = sum_axis_gaps(original_gap.main(constants.dir), line.items.len());
     let total_main_axis_gap = sum_axis_gaps(constants.gap.main(constants.dir), line.items.len());
 
@@ -997,8 +997,7 @@ fn resolve_flexible_lengths(
         let inner_target_size = child.hypothetical_inner_size.main(constants.dir);
         child.target_size.set_main(constants.dir, inner_target_size);
 
-        let child_style = tree.style(child.node);
-        if (child_style.flex_grow == 0.0 && child_style.flex_shrink == 0.0)
+        if (child.flex_grow == 0.0 && child.flex_shrink == 0.0)
             || (growing && child.flex_basis > child.hypothetical_inner_size.main(constants.dir))
             || (shrinking && child.flex_basis < child.hypothetical_inner_size.main(constants.dir))
         {
@@ -1054,8 +1053,7 @@ fn resolve_flexible_lengths(
 
         let (sum_flex_grow, sum_flex_shrink): (f32, f32) =
             unfrozen.iter().fold((0.0, 0.0), |(flex_grow, flex_shrink), item| {
-                let style = tree.style(item.node);
-                (flex_grow + style.flex_grow, flex_shrink + style.flex_shrink)
+                (flex_grow + item.flex_grow, flex_shrink + item.flex_shrink)
             });
 
         let free_space = if growing && sum_flex_grow < 1.0 {
@@ -1091,18 +1089,17 @@ fn resolve_flexible_lengths(
         if free_space.is_normal() {
             if growing && sum_flex_grow > 0.0 {
                 for child in &mut unfrozen {
-                    child.target_size.set_main(
-                        constants.dir,
-                        child.flex_basis + free_space * (tree.style(child.node).flex_grow / sum_flex_grow),
-                    );
+                    child
+                        .target_size
+                        .set_main(constants.dir, child.flex_basis + free_space * (child.flex_grow / sum_flex_grow));
                 }
             } else if shrinking && sum_flex_shrink > 0.0 {
                 let sum_scaled_shrink_factor: f32 =
-                    unfrozen.iter().map(|child| child.inner_flex_basis * tree.style(child.node).flex_shrink).sum();
+                    unfrozen.iter().map(|child| child.inner_flex_basis * child.flex_shrink).sum();
 
                 if sum_scaled_shrink_factor > 0.0 {
                     for child in &mut unfrozen {
-                        let scaled_shrink_factor = child.inner_flex_basis * tree.style(child.node).flex_shrink;
+                        let scaled_shrink_factor = child.inner_flex_basis * child.flex_shrink;
                         child.target_size.set_main(
                             constants.dir,
                             child.flex_basis + free_space * (scaled_shrink_factor / sum_scaled_shrink_factor),
@@ -1296,12 +1293,7 @@ fn calculate_children_base_lines(
 ///         If the flex container is single-line, then clamp the line’s cross-size to be within the container’s computed min and max cross sizes.
 ///         **Note that if CSS 2.1’s definition of min/max-width/height applied more generally, this behavior would fall out automatically**.
 #[inline]
-fn calculate_cross_size(
-    tree: &mut impl LayoutTree,
-    flex_lines: &mut [FlexLine],
-    node_size: Size<Option<f32>>,
-    constants: &AlgoConstants,
-) {
+fn calculate_cross_size(flex_lines: &mut [FlexLine], node_size: Size<Option<f32>>, constants: &AlgoConstants) {
     // Note: AlignContent::SpaceEvenly and AlignContent::SpaceAround behave like AlignContent::Stretch when there is only
     // a single flex line in the container. See: https://www.w3.org/TR/css-flexbox-1/#align-content-property
     // Also: align_content is ignored entirely (and thus behaves like Stretch) when `flex_wrap` is set to `nowrap`.
@@ -1335,10 +1327,9 @@ fn calculate_cross_size(
                 .items
                 .iter()
                 .map(|child| {
-                    let child_style = tree.style(child.node);
                     if child.align_self == AlignSelf::Baseline
-                        && child_style.margin.cross_start(constants.dir) != LengthPercentageAuto::Auto
-                        && child_style.margin.cross_end(constants.dir) != LengthPercentageAuto::Auto
+                        && !child.margin_is_auto.cross_start(constants.dir)
+                        && !child.margin_is_auto.cross_end(constants.dir)
                     {
                         max_baseline - child.baseline + child.hypothetical_outer_size.cross(constants.dir)
                     } else {
@@ -1395,8 +1386,8 @@ fn determine_used_cross_size(tree: &mut impl LayoutTree, flex_lines: &mut [FlexL
             child.target_size.set_cross(
                 constants.dir,
                 if child.align_self == AlignSelf::Stretch
-                    && child_style.margin.cross_start(constants.dir) != LengthPercentageAuto::Auto
-                    && child_style.margin.cross_end(constants.dir) != LengthPercentageAuto::Auto
+                    && !child.margin_is_auto.cross_start(constants.dir)
+                    && !child.margin_is_auto.cross_end(constants.dir)
                     && child_style.size.cross(constants.dir) == Dimension::Auto
                 {
                     // For some reason this particular usage of max_width is an exception to the rule that max_width's transfer
@@ -1432,12 +1423,7 @@ fn determine_used_cross_size(tree: &mut impl LayoutTree, flex_lines: &mut [FlexL
 ///
 ///     2. Align the items along the main-axis per `justify-content`.
 #[inline]
-fn distribute_remaining_free_space(
-    tree: &mut impl LayoutTree,
-    flex_lines: &mut [FlexLine],
-    node: Node,
-    constants: &AlgoConstants,
-) {
+fn distribute_remaining_free_space(flex_lines: &mut [FlexLine], constants: &AlgoConstants) {
     for line in flex_lines {
         let total_main_axis_gap = sum_axis_gaps(constants.gap.main(constants.dir), line.items.len());
         let used_space: f32 = total_main_axis_gap
@@ -1446,11 +1432,10 @@ fn distribute_remaining_free_space(
         let mut num_auto_margins = 0;
 
         for child in line.items.iter_mut() {
-            let child_style = tree.style(child.node);
-            if child_style.margin.main_start(constants.dir) == LengthPercentageAuto::Auto {
+            if child.margin_is_auto.main_start(constants.dir) {
                 num_auto_margins += 1;
             }
-            if child_style.margin.main_end(constants.dir) == LengthPercentageAuto::Auto {
+            if child.margin_is_auto.main_end(constants.dir) {
                 num_auto_margins += 1;
             }
         }
@@ -1459,15 +1444,14 @@ fn distribute_remaining_free_space(
             let margin = free_space / num_auto_margins as f32;
 
             for child in line.items.iter_mut() {
-                let child_style = tree.style(child.node);
-                if child_style.margin.main_start(constants.dir) == LengthPercentageAuto::Auto {
+                if child.margin_is_auto.main_start(constants.dir) {
                     if constants.is_row {
                         child.margin.left = margin;
                     } else {
                         child.margin.top = margin;
                     }
                 }
-                if child_style.margin.main_end(constants.dir) == LengthPercentageAuto::Auto {
+                if child.margin_is_auto.main_end(constants.dir) {
                     if constants.is_row {
                         child.margin.right = margin;
                     } else {
@@ -1479,8 +1463,7 @@ fn distribute_remaining_free_space(
             let num_items = line.items.len();
             let layout_reverse = constants.dir.is_reverse();
             let gap = constants.gap.main(constants.dir);
-            let justify_content_mode: JustifyContent =
-                tree.style(node).justify_content.unwrap_or(JustifyContent::FlexStart);
+            let justify_content_mode = constants.justify_content.unwrap_or(JustifyContent::FlexStart);
 
             let justify_item = |(i, child): (usize, &mut FlexItem)| {
                 child.offset_main =
@@ -1509,18 +1492,15 @@ fn distribute_remaining_free_space(
 ///     - Otherwise, if the block-start or inline-start margin (whichever is in the cross axis) is auto, set it to zero.
 ///         Set the opposite margin so that the outer cross size of the item equals the cross size of its flex line.
 #[inline]
-fn resolve_cross_axis_auto_margins(tree: &mut impl LayoutTree, flex_lines: &mut [FlexLine], constants: &AlgoConstants) {
+fn resolve_cross_axis_auto_margins(flex_lines: &mut [FlexLine], constants: &AlgoConstants) {
     for line in flex_lines {
         let line_cross_size = line.cross_size;
         let max_baseline: f32 = line.items.iter_mut().map(|child| child.baseline).fold(0.0, |acc, x| acc.max(x));
 
         for child in line.items.iter_mut() {
             let free_space = line_cross_size - child.outer_target_size.cross(constants.dir);
-            let child_style = tree.style(child.node);
 
-            if child_style.margin.cross_start(constants.dir) == LengthPercentageAuto::Auto
-                && child_style.margin.cross_end(constants.dir) == LengthPercentageAuto::Auto
-            {
+            if child.margin_is_auto.cross_start(constants.dir) && child.margin_is_auto.cross_end(constants.dir) {
                 if constants.is_row {
                     child.margin.top = free_space / 2.0;
                     child.margin.bottom = free_space / 2.0;
@@ -1528,13 +1508,13 @@ fn resolve_cross_axis_auto_margins(tree: &mut impl LayoutTree, flex_lines: &mut
                     child.margin.left = free_space / 2.0;
                     child.margin.right = free_space / 2.0;
                 }
-            } else if child_style.margin.cross_start(constants.dir) == LengthPercentageAuto::Auto {
+            } else if child.margin_is_auto.cross_start(constants.dir) {
                 if constants.is_row {
                     child.margin.top = free_space;
                 } else {
                     child.margin.left = free_space;
                 }
-            } else if child_style.margin.cross_end(constants.dir) == LengthPercentageAuto::Auto {
+            } else if child.margin_is_auto.cross_end(constants.dir) {
                 if constants.is_row {
                     child.margin.bottom = free_space;
                 } else {
@@ -1642,16 +1622,10 @@ fn determine_container_cross_size(
 ///
 /// - [**Align all flex lines**](https://www.w3.org/TR/css-flexbox-1/#algo-line-align) per `align-content`.
 #[inline]
-fn align_flex_lines_per_align_content(
-    tree: &impl LayoutTree,
-    flex_lines: &mut [FlexLine],
-    node: Node,
-    constants: &AlgoConstants,
-    total_cross_size: f32,
-) {
+fn align_flex_lines_per_align_content(flex_lines: &mut [FlexLine], constants: &AlgoConstants, total_cross_size: f32) {
     let num_lines = flex_lines.len();
     let gap = constants.gap.cross(constants.dir);
-    let align_content_mode = tree.style(node).align_content.unwrap_or(AlignContent::Stretch);
+    let align_content_mode = constants.align_content;
     let total_cross_axis_gap = sum_axis_gaps(gap, num_lines);
     let free_space = constants.inner_container_size.cross(constants.dir) - total_cross_size - total_cross_axis_gap;
 
@@ -1927,7 +1901,7 @@ fn perform_absolute_layout_on_absolute_children(tree: &mut impl LayoutTree, node
         } else {
             // Stretch is an invalid value for justify_content in the flexbox algorithm, so we
             // treat it as if it wasn't set (and thus we default to FlexStart behaviour)
-            match (tree.style(node).justify_content.unwrap_or(JustifyContent::Start), constants.is_wrap_reverse) {
+            match (constants.justify_content.unwrap_or(JustifyContent::Start), constants.is_wrap_reverse) {
                 (JustifyContent::SpaceBetween, _)
                 | (JustifyContent::Start, _)
                 | (JustifyContent::Stretch, false)