From 0be52090033aa64214f2c357bcf4ec6861e0e9bd Mon Sep 17 00:00:00 2001 From: Oriol Brufau Date: Tue, 3 Dec 2024 13:35:24 +0100 Subject: [PATCH] Improve performance of flex column layouts by caching (#34461) * Obey min and max properties when computing main size of column flex When laying out a column flex container with an auto preferred main size, we were resolving the used main size to the intrinsic max-content size. However, we weren't clamping this amount between the min and max sizes. Co-authored-by: Martin Robinson Signed-off-by: Oriol Brufau * Improve performance of flex column layouts by caching We were already using a cache for layout_for_block_content_size(), but we were only storing the intrinsic block size. Thus when laying out the flex items for real, we would perform new layouts, triggering an exponential complexity in case of nested flexboxes. Now we cache the entire layout result so that we can avoid doing the work again. This improves the results of flexbox-deeply-nested-column-flow.html (a Blink perf test) from ~40 runs/second to ~500 runs/second on my PC. Co-authored-by: Martin Robinson Signed-off-by: Oriol Brufau --------- Signed-off-by: Oriol Brufau Co-authored-by: Martin Robinson --- components/layout_2020/flexbox/layout.rs | 33 ++++++++++++++---------- components/layout_2020/flexbox/mod.rs | 18 ++++++++++--- 2 files changed, 34 insertions(+), 17 deletions(-) diff --git a/components/layout_2020/flexbox/layout.rs b/components/layout_2020/flexbox/layout.rs index e40ba219d55..0848c3ab2f0 100644 --- a/components/layout_2020/flexbox/layout.rs +++ b/components/layout_2020/flexbox/layout.rs @@ -1717,8 +1717,6 @@ impl InitialFlexLineLayout<'_> { } } - let _ = item.item.box_.block_content_size_cache.borrow_mut().take(); - let baseline = item.get_or_synthesize_baseline_with_cross_size(used_cross_size); if matches!( item.item.align_self.0.value(), @@ -1875,9 +1873,6 @@ impl FlexItem<'_> { used_cross_size_override: Option, non_stretch_layout_result: Option<&mut FlexItemLayoutResult>, ) -> Option { - // Clear any layout cache information so that it doesn't persist until the next layout. - self.box_.block_content_size_cache.borrow_mut().take(); - let containing_block = flex_context.containing_block; let mut positioning_context = PositioningContext::new_for_style(self.box_.style()) .unwrap_or_else(|| { @@ -2040,18 +2035,27 @@ impl FlexItem<'_> { } } + let cache = self.box_.block_content_size_cache.borrow_mut().take(); + let layout = if let Some(cache) = cache.filter(|cache| { + cache.compatible_with_item_as_containing_block(&item_as_containing_block) + }) { + positioning_context = cache.positioning_context; + cache.layout + } else { + non_replaced.layout( + flex_context.layout_context, + &mut positioning_context, + &item_as_containing_block, + containing_block, + ) + }; let IndependentLayout { fragments, content_block_size, baselines: content_box_baselines, depends_on_block_constraints, .. - } = non_replaced.layout( - flex_context.layout_context, - &mut positioning_context, - &item_as_containing_block, - containing_block, - ); + } = layout; let depends_on_block_constraints = depends_on_block_constraints || (flex_axis == FlexAxis::Row && self.stretches()); @@ -2806,10 +2810,10 @@ impl FlexItemBox { block_size: AuOrAuto::Auto, style: &non_replaced.style, }; - let mut content_block_size = || { + let content_block_size = || { if let Some(cache) = &*self.block_content_size_cache.borrow() { if inline_size == cache.containing_block_inline_size { - return cache.content_block_size; + return cache.layout.content_block_size; } else { #[cfg(feature = "tracing")] tracing::warn!( @@ -2833,7 +2837,8 @@ impl FlexItemBox { *self.block_content_size_cache.borrow_mut() = Some(CachedBlockSizeContribution { containing_block_inline_size: item_as_containing_block.inline_size, - content_block_size: layout.content_block_size, + layout, + positioning_context, }); content_block_size }; diff --git a/components/layout_2020/flexbox/mod.rs b/components/layout_2020/flexbox/mod.rs index ceca8bad161..3a6ddd6d0bf 100644 --- a/components/layout_2020/flexbox/mod.rs +++ b/components/layout_2020/flexbox/mod.rs @@ -20,9 +20,10 @@ use crate::construct_modern::{ModernContainerBuilder, ModernItemKind}; use crate::context::LayoutContext; use crate::dom::{LayoutBox, NodeExt}; use crate::dom_traversal::{NodeAndStyleInfo, NonReplacedContents}; -use crate::formatting_contexts::IndependentFormattingContext; +use crate::formatting_contexts::{IndependentFormattingContext, IndependentLayout}; use crate::fragment_tree::BaseFragmentInfo; -use crate::positioned::AbsolutelyPositionedBox; +use crate::positioned::{AbsolutelyPositionedBox, PositioningContext}; +use crate::ContainingBlock; mod geom; mod layout; @@ -180,5 +181,16 @@ impl FlexItemBox { struct CachedBlockSizeContribution { containing_block_inline_size: Au, - content_block_size: Au, + layout: IndependentLayout, + positioning_context: PositioningContext, +} + +impl CachedBlockSizeContribution { + fn compatible_with_item_as_containing_block( + &self, + item_as_containing_block: &ContainingBlock, + ) -> bool { + item_as_containing_block.inline_size == self.containing_block_inline_size && + item_as_containing_block.block_size.is_auto() + } }