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 <mrobinson@igalia.com>
Signed-off-by: Oriol Brufau <obrufau@igalia.com>

* 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 <mrobinson@igalia.com>
Signed-off-by: Oriol Brufau <obrufau@igalia.com>

---------

Signed-off-by: Oriol Brufau <obrufau@igalia.com>
Co-authored-by: Martin Robinson <mrobinson@igalia.com>
This commit is contained in:
Oriol Brufau 2024-12-03 13:35:24 +01:00 committed by GitHub
parent ad448da7de
commit 0be5209003
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 34 additions and 17 deletions

View file

@ -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<Au>,
non_stretch_layout_result: Option<&mut FlexItemLayoutResult>,
) -> Option<FlexItemLayoutResult> {
// 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
};

View file

@ -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()
}
}