layout: Cache IndependentNonReplacedContents::layout() (#36082)

This replaces `IndependentLayout` with `CacheableLayoutResult` and
stores it in `LayoutBoxBase` so it can be reused when we need to lay out
a box multiple times.

This is a generalization of the caching that we had for flexbox, which
is now removed in favor of the new one.

With this, the number of runs per second in the Chromium perf test
`flexbox-deeply-nested-column-flow.html` are multiplied by 3.

Signed-off-by: Oriol Brufau <obrufau@igalia.com>
Signed-off-by: Martin Robinson <mrobinson@igalia.com>
Co-authored-by: Martin Robinson <mrobinson@igalia.com>
This commit is contained in:
Oriol Brufau 2025-03-24 13:33:44 +01:00 committed by GitHub
parent efd6e86393
commit c09eed759b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 216 additions and 160 deletions

View file

@ -25,16 +25,13 @@ use style::values::generics::length::LengthPercentageOrNormal;
use style::values::specified::align::AlignFlags;
use super::geom::{FlexAxis, FlexRelativeRect, FlexRelativeSides, FlexRelativeVec2};
use super::{
CachedBlockSizeContribution, FlexContainer, FlexContainerConfig, FlexItemBox, FlexLevelBox,
};
use super::{FlexContainer, FlexContainerConfig, FlexItemBox, FlexLevelBox};
use crate::cell::ArcRefCell;
use crate::context::LayoutContext;
use crate::formatting_contexts::{
Baselines, IndependentFormattingContextContents, IndependentLayout,
};
use crate::formatting_contexts::{Baselines, IndependentFormattingContextContents};
use crate::fragment_tree::{BoxFragment, CollapsedBlockMargins, Fragment, FragmentFlags};
use crate::geom::{AuOrAuto, LogicalRect, LogicalSides, LogicalVec2, Size, Sizes};
use crate::layout_box_base::CacheableLayoutResult;
use crate::positioned::{
AbsolutelyPositionedBox, PositioningContext, PositioningContextLength, relative_adjustement,
};
@ -650,8 +647,10 @@ impl FlexContainer {
layout_context: &LayoutContext,
positioning_context: &mut PositioningContext,
containing_block: &ContainingBlock,
) -> IndependentLayout {
let depends_on_block_constraints = self.config.flex_direction == FlexDirection::Column;
depends_on_block_constraints: bool,
) -> CacheableLayoutResult {
let depends_on_block_constraints =
depends_on_block_constraints || self.config.flex_direction == FlexDirection::Column;
let mut flex_context = FlexContext {
config: self.config.clone(),
@ -985,13 +984,14 @@ impl FlexContainer {
.or(all_baselines.last),
};
IndependentLayout {
CacheableLayoutResult {
fragments,
content_block_size,
content_inline_size_for_table: None,
baselines,
depends_on_block_constraints,
specific_layout_info: None,
collapsible_margins_in_children: CollapsedBlockMargins::zero(),
}
}
@ -1950,29 +1950,23 @@ 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 {
let layout = non_replaced.layout_with_caching(
flex_context.layout_context,
&mut positioning_context,
&item_as_containing_block,
containing_block,
&independent_formatting_context.base,
flex_axis == FlexAxis::Column ||
self.stretches_to_line() ||
self.depends_on_block_constraints,
);
let CacheableLayoutResult {
fragments,
content_block_size,
baselines: content_box_baselines,
depends_on_block_constraints,
..
} = layout;
let depends_on_block_constraints = depends_on_block_constraints ||
(flex_axis == FlexAxis::Row && self.stretches_to_line());
let has_child_which_depends_on_block_constraints = fragments.iter().any(|fragment| {
fragment.base().is_some_and(|base|
@ -2693,37 +2687,17 @@ impl FlexItemBox {
},
style,
};
let content_block_size = || {
if let Some(cache) = &*self.block_content_size_cache.borrow() {
if inline_size == cache.containing_block_inline_size {
return cache.layout.content_block_size;
} else {
#[cfg(feature = "tracing")]
tracing::warn!(
name: "NonReplaced cache miss",
cached = ?cache.containing_block_inline_size,
required = ?inline_size,
);
}
} else {
#[cfg(feature = "tracing")]
tracing::warn!(name: "NonReplaced no cache", required = ?inline_size);
}
let layout = non_replaced.layout(
flex_context.layout_context,
&mut positioning_context,
&item_as_containing_block,
flex_context.containing_block,
);
let content_block_size = layout.content_block_size;
*self.block_content_size_cache.borrow_mut() =
Some(CachedBlockSizeContribution {
containing_block_inline_size: item_as_containing_block.size.inline,
layout,
positioning_context,
});
content_block_size
let mut content_block_size = || {
non_replaced
.layout_with_caching(
flex_context.layout_context,
&mut positioning_context,
&item_as_containing_block,
flex_context.containing_block,
&self.independent_formatting_context.base,
false, /* depends_on_block_constraints */
)
.content_block_size
};
match intrinsic_sizing_mode {
IntrinsicSizingMode::Contribution => {

View file

@ -2,7 +2,6 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use app_units::Au;
use geom::{FlexAxis, MainStartCrossStart};
use servo_arc::Arc as ServoArc;
use style::logical_geometry::WritingMode;
@ -13,15 +12,15 @@ use style::properties::longhands::flex_wrap::computed_value::T as FlexWrap;
use style::values::computed::{AlignContent, JustifyContent};
use style::values::specified::align::AlignFlags;
use crate::PropagatedBoxTreeData;
use crate::cell::ArcRefCell;
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, IndependentLayout};
use crate::formatting_contexts::IndependentFormattingContext;
use crate::fragment_tree::BaseFragmentInfo;
use crate::positioned::{AbsolutelyPositionedBox, PositioningContext};
use crate::{ContainingBlock, PropagatedBoxTreeData};
use crate::positioned::AbsolutelyPositionedBox;
mod geom;
mod layout;
@ -146,7 +145,6 @@ pub(crate) enum FlexLevelBox {
pub(crate) struct FlexItemBox {
independent_formatting_context: IndependentFormattingContext,
block_content_size_cache: ArcRefCell<Option<CachedBlockSizeContribution>>,
}
impl std::fmt::Debug for FlexItemBox {
@ -159,7 +157,6 @@ impl FlexItemBox {
fn new(independent_formatting_context: IndependentFormattingContext) -> Self {
Self {
independent_formatting_context,
block_content_size_cache: Default::default(),
}
}
@ -171,19 +168,3 @@ impl FlexItemBox {
self.independent_formatting_context.base_fragment_info()
}
}
struct CachedBlockSizeContribution {
containing_block_inline_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.size.inline == self.containing_block_inline_size &&
!item_as_containing_block.size.block.is_definite()
}
}