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 · 2023-03-09T17:54:55Z (3 years ago) · parent 6f4bb570b · browse the tree at this commit
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)