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

View file

@ -2,7 +2,6 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * 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/. */ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use app_units::Au;
use geom::{FlexAxis, MainStartCrossStart}; use geom::{FlexAxis, MainStartCrossStart};
use servo_arc::Arc as ServoArc; use servo_arc::Arc as ServoArc;
use style::logical_geometry::WritingMode; 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::computed::{AlignContent, JustifyContent};
use style::values::specified::align::AlignFlags; use style::values::specified::align::AlignFlags;
use crate::PropagatedBoxTreeData;
use crate::cell::ArcRefCell; use crate::cell::ArcRefCell;
use crate::construct_modern::{ModernContainerBuilder, ModernItemKind}; use crate::construct_modern::{ModernContainerBuilder, ModernItemKind};
use crate::context::LayoutContext; use crate::context::LayoutContext;
use crate::dom::{LayoutBox, NodeExt}; use crate::dom::{LayoutBox, NodeExt};
use crate::dom_traversal::{NodeAndStyleInfo, NonReplacedContents}; use crate::dom_traversal::{NodeAndStyleInfo, NonReplacedContents};
use crate::formatting_contexts::{IndependentFormattingContext, IndependentLayout}; use crate::formatting_contexts::IndependentFormattingContext;
use crate::fragment_tree::BaseFragmentInfo; use crate::fragment_tree::BaseFragmentInfo;
use crate::positioned::{AbsolutelyPositionedBox, PositioningContext}; use crate::positioned::AbsolutelyPositionedBox;
use crate::{ContainingBlock, PropagatedBoxTreeData};
mod geom; mod geom;
mod layout; mod layout;
@ -146,7 +145,6 @@ pub(crate) enum FlexLevelBox {
pub(crate) struct FlexItemBox { pub(crate) struct FlexItemBox {
independent_formatting_context: IndependentFormattingContext, independent_formatting_context: IndependentFormattingContext,
block_content_size_cache: ArcRefCell<Option<CachedBlockSizeContribution>>,
} }
impl std::fmt::Debug for FlexItemBox { impl std::fmt::Debug for FlexItemBox {
@ -159,7 +157,6 @@ impl FlexItemBox {
fn new(independent_formatting_context: IndependentFormattingContext) -> Self { fn new(independent_formatting_context: IndependentFormattingContext) -> Self {
Self { Self {
independent_formatting_context, independent_formatting_context,
block_content_size_cache: Default::default(),
} }
} }
@ -171,19 +168,3 @@ impl FlexItemBox {
self.independent_formatting_context.base_fragment_info() 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()
}
}

View file

@ -109,15 +109,17 @@ use unicode_bidi::{BidiInfo, Level};
use webrender_api::FontInstanceKey; use webrender_api::FontInstanceKey;
use xi_unicode::linebreak_property; use xi_unicode::linebreak_property;
use super::IndependentFormattingContextContents;
use super::float::{Clear, PlacementAmongFloats}; use super::float::{Clear, PlacementAmongFloats};
use super::{
CacheableLayoutResult, IndependentFloatOrAtomicLayoutResult,
IndependentFormattingContextContents,
};
use crate::cell::ArcRefCell; use crate::cell::ArcRefCell;
use crate::context::LayoutContext; use crate::context::LayoutContext;
use crate::flow::CollapsibleWithParentStartMargin;
use crate::flow::float::{FloatBox, SequentialLayoutState}; use crate::flow::float::{FloatBox, SequentialLayoutState};
use crate::flow::{CollapsibleWithParentStartMargin, FlowLayout};
use crate::formatting_contexts::{ use crate::formatting_contexts::{
Baselines, IndependentFormattingContext, IndependentLayoutResult, Baselines, IndependentFormattingContext, IndependentNonReplacedContents,
IndependentNonReplacedContents,
}; };
use crate::fragment_tree::{ use crate::fragment_tree::{
BoxFragment, CollapsedBlockMargins, CollapsedMargin, Fragment, FragmentFlags, BoxFragment, CollapsedBlockMargins, CollapsedMargin, Fragment, FragmentFlags,
@ -1585,7 +1587,7 @@ impl InlineFormattingContext {
containing_block: &ContainingBlock, containing_block: &ContainingBlock,
sequential_layout_state: Option<&mut SequentialLayoutState>, sequential_layout_state: Option<&mut SequentialLayoutState>,
collapsible_with_parent_start_margin: CollapsibleWithParentStartMargin, collapsible_with_parent_start_margin: CollapsibleWithParentStartMargin,
) -> FlowLayout { ) -> CacheableLayoutResult {
let first_line_inline_start = if self.has_first_formatted_line { let first_line_inline_start = if self.has_first_formatted_line {
containing_block containing_block
.style .style
@ -1697,12 +1699,14 @@ impl InlineFormattingContext {
content_block_size == Au::zero() && content_block_size == Au::zero() &&
collapsible_with_parent_start_margin.0; collapsible_with_parent_start_margin.0;
FlowLayout { CacheableLayoutResult {
fragments: layout.fragments, fragments: layout.fragments,
content_block_size, content_block_size,
collapsible_margins_in_children, collapsible_margins_in_children,
baselines: layout.baselines, baselines: layout.baselines,
depends_on_block_constraints: layout.depends_on_block_constraints, depends_on_block_constraints: layout.depends_on_block_constraints,
content_inline_size_for_table: None,
specific_layout_info: None,
} }
} }
@ -1922,7 +1926,7 @@ impl IndependentFormattingContext {
// We need to know the inline size of the atomic before deciding whether to do the line break. // We need to know the inline size of the atomic before deciding whether to do the line break.
let mut child_positioning_context = PositioningContext::new_for_style(self.style()) let mut child_positioning_context = PositioningContext::new_for_style(self.style())
.unwrap_or_else(|| PositioningContext::new_for_subtree(true)); .unwrap_or_else(|| PositioningContext::new_for_subtree(true));
let IndependentLayoutResult { let IndependentFloatOrAtomicLayoutResult {
mut fragment, mut fragment,
baselines, baselines,
pbm_sums, pbm_sums,

View file

@ -26,7 +26,7 @@ use crate::flow::float::{
}; };
use crate::formatting_contexts::{ use crate::formatting_contexts::{
Baselines, IndependentFormattingContext, IndependentFormattingContextContents, Baselines, IndependentFormattingContext, IndependentFormattingContextContents,
IndependentLayout, IndependentLayoutResult, IndependentNonReplacedContents, IndependentNonReplacedContents,
}; };
use crate::fragment_tree::{ use crate::fragment_tree::{
BaseFragmentInfo, BoxFragment, CollapsedBlockMargins, CollapsedMargin, Fragment, FragmentFlags, BaseFragmentInfo, BoxFragment, CollapsedBlockMargins, CollapsedMargin, Fragment, FragmentFlags,
@ -35,7 +35,7 @@ use crate::geom::{
AuOrAuto, LogicalRect, LogicalSides, LogicalSides1D, LogicalVec2, PhysicalPoint, PhysicalRect, AuOrAuto, LogicalRect, LogicalSides, LogicalSides1D, LogicalVec2, PhysicalPoint, PhysicalRect,
PhysicalSides, Size, Sizes, ToLogical, ToLogicalWithContainingBlock, PhysicalSides, Size, Sizes, ToLogical, ToLogicalWithContainingBlock,
}; };
use crate::layout_box_base::LayoutBoxBase; use crate::layout_box_base::{CacheableLayoutResult, LayoutBoxBase};
use crate::positioned::{AbsolutelyPositionedBox, PositioningContext, PositioningContextLength}; use crate::positioned::{AbsolutelyPositionedBox, PositioningContext, PositioningContextLength};
use crate::replaced::ReplacedContents; use crate::replaced::ReplacedContents;
use crate::sizing::{self, ComputeInlineContentSizes, ContentSizes, InlineContentSizesResult}; use crate::sizing::{self, ComputeInlineContentSizes, ContentSizes, InlineContentSizesResult};
@ -219,18 +219,6 @@ impl BlockLevelBox {
} }
} }
pub(crate) struct FlowLayout {
pub fragments: Vec<Fragment>,
pub content_block_size: Au,
pub collapsible_margins_in_children: CollapsedBlockMargins,
/// The offset of the baselines in this layout in the content area, if there were some. This is
/// used to propagate inflow baselines to the ancestors of `display: inline-block` elements
/// and table content.
pub baselines: Baselines,
/// Whether or not this layout depends on the block size of its containing block.
pub depends_on_block_constraints: bool,
}
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub(crate) struct CollapsibleWithParentStartMargin(bool); pub(crate) struct CollapsibleWithParentStartMargin(bool);
@ -362,7 +350,8 @@ impl BlockFormattingContext {
layout_context: &LayoutContext, layout_context: &LayoutContext,
positioning_context: &mut PositioningContext, positioning_context: &mut PositioningContext,
containing_block: &ContainingBlock, containing_block: &ContainingBlock,
) -> IndependentLayout { depends_on_block_constraints: bool,
) -> CacheableLayoutResult {
let mut sequential_layout_state = if self.contains_floats || !layout_context.use_rayon { let mut sequential_layout_state = if self.contains_floats || !layout_context.use_rayon {
Some(SequentialLayoutState::new(containing_block.size.inline)) Some(SequentialLayoutState::new(containing_block.size.inline))
} else { } else {
@ -395,15 +384,17 @@ impl BlockFormattingContext {
sequential_layout_state.calculate_clearance(Clear::Both, &CollapsedMargin::zero()) sequential_layout_state.calculate_clearance(Clear::Both, &CollapsedMargin::zero())
}); });
IndependentLayout { CacheableLayoutResult {
fragments: flow_layout.fragments, fragments: flow_layout.fragments,
content_block_size: flow_layout.content_block_size + content_block_size: flow_layout.content_block_size +
flow_layout.collapsible_margins_in_children.end.solve() + flow_layout.collapsible_margins_in_children.end.solve() +
clearance.unwrap_or_default(), clearance.unwrap_or_default(),
content_inline_size_for_table: None, content_inline_size_for_table: None,
baselines: flow_layout.baselines, baselines: flow_layout.baselines,
depends_on_block_constraints: flow_layout.depends_on_block_constraints, depends_on_block_constraints: depends_on_block_constraints ||
flow_layout.depends_on_block_constraints,
specific_layout_info: None, specific_layout_info: None,
collapsible_margins_in_children: CollapsedBlockMargins::zero(),
} }
} }
@ -573,7 +564,7 @@ impl BlockContainer {
sequential_layout_state: Option<&mut SequentialLayoutState>, sequential_layout_state: Option<&mut SequentialLayoutState>,
collapsible_with_parent_start_margin: CollapsibleWithParentStartMargin, collapsible_with_parent_start_margin: CollapsibleWithParentStartMargin,
ignore_block_margins_for_stretch: LogicalSides1D<bool>, ignore_block_margins_for_stretch: LogicalSides1D<bool>,
) -> FlowLayout { ) -> CacheableLayoutResult {
match self { match self {
BlockContainer::BlockLevelBoxes(child_boxes) => layout_block_level_children( BlockContainer::BlockLevelBoxes(child_boxes) => layout_block_level_children(
layout_context, layout_context,
@ -627,7 +618,7 @@ fn layout_block_level_children(
mut sequential_layout_state: Option<&mut SequentialLayoutState>, mut sequential_layout_state: Option<&mut SequentialLayoutState>,
collapsible_with_parent_start_margin: CollapsibleWithParentStartMargin, collapsible_with_parent_start_margin: CollapsibleWithParentStartMargin,
ignore_block_margins_for_stretch: LogicalSides1D<bool>, ignore_block_margins_for_stretch: LogicalSides1D<bool>,
) -> FlowLayout { ) -> CacheableLayoutResult {
let mut placement_state = let mut placement_state =
PlacementState::new(collapsible_with_parent_start_margin, containing_block); PlacementState::new(collapsible_with_parent_start_margin, containing_block);
@ -660,12 +651,14 @@ fn layout_block_level_children(
}); });
let (content_block_size, collapsible_margins_in_children, baselines) = placement_state.finish(); let (content_block_size, collapsible_margins_in_children, baselines) = placement_state.finish();
FlowLayout { CacheableLayoutResult {
fragments, fragments,
content_block_size, content_block_size,
collapsible_margins_in_children, collapsible_margins_in_children,
baselines, baselines,
depends_on_block_constraints, depends_on_block_constraints,
content_inline_size_for_table: None,
specific_layout_info: None,
} }
} }
@ -1150,6 +1143,7 @@ impl IndependentNonReplacedContents {
positioning_context, positioning_context,
&containing_block_for_children, &containing_block_for_children,
containing_block, containing_block,
false, /* depends_on_block_constraints */
); );
let inline_size = layout let inline_size = layout
@ -1299,7 +1293,7 @@ impl IndependentNonReplacedContents {
) )
}; };
let compute_block_size = |layout: &IndependentLayout| { let compute_block_size = |layout: &CacheableLayoutResult| {
content_box_sizes.block.resolve( content_box_sizes.block.resolve(
Direction::Block, Direction::Block,
Size::FitContent, Size::FitContent,
@ -1335,6 +1329,7 @@ impl IndependentNonReplacedContents {
style, style,
}, },
containing_block, containing_block,
false, /* depends_on_block_constraints */
); );
content_size = LogicalVec2 { content_size = LogicalVec2 {
@ -1398,6 +1393,7 @@ impl IndependentNonReplacedContents {
style, style,
}, },
containing_block, containing_block,
false, /* depends_on_block_constraints */
); );
let inline_size = if let Some(inline_size) = layout.content_inline_size_for_table { let inline_size = if let Some(inline_size) = layout.content_inline_size_for_table {
@ -2162,6 +2158,12 @@ fn block_size_is_zero_or_intrinsic(size: &StyleSize, containing_block: &Containi
} }
} }
pub(crate) struct IndependentFloatOrAtomicLayoutResult {
pub fragment: BoxFragment,
pub baselines: Option<Baselines>,
pub pbm_sums: LogicalSides<Au>,
}
impl IndependentFormattingContext { impl IndependentFormattingContext {
pub(crate) fn layout_in_flow_block_level( pub(crate) fn layout_in_flow_block_level(
&self, &self,
@ -2196,7 +2198,7 @@ impl IndependentFormattingContext {
layout_context: &LayoutContext, layout_context: &LayoutContext,
child_positioning_context: &mut PositioningContext, child_positioning_context: &mut PositioningContext,
containing_block: &ContainingBlock, containing_block: &ContainingBlock,
) -> IndependentLayoutResult { ) -> IndependentFloatOrAtomicLayoutResult {
let style = self.style(); let style = self.style();
let container_writing_mode = containing_block.style.writing_mode; let container_writing_mode = containing_block.style.writing_mode;
let layout_style = self.layout_style(); let layout_style = self.layout_style();
@ -2280,6 +2282,7 @@ impl IndependentFormattingContext {
child_positioning_context, child_positioning_context,
&containing_block_for_children, &containing_block_for_children,
containing_block, containing_block,
false, /* depends_on_block_constraints */
); );
let inline_size = independent_layout let inline_size = independent_layout
.content_inline_size_for_table .content_inline_size_for_table
@ -2330,7 +2333,7 @@ impl IndependentFormattingContext {
CollapsedBlockMargins::zero(), CollapsedBlockMargins::zero(),
); );
IndependentLayoutResult { IndependentFloatOrAtomicLayoutResult {
fragment, fragment,
baselines, baselines,
pbm_sums, pbm_sums,

View file

@ -365,6 +365,7 @@ impl BoxTree {
layout_context, layout_context,
&mut positioning_context, &mut positioning_context,
&(&initial_containing_block).into(), &(&initial_containing_block).into(),
false, /* depends_on_block_constraints */
); );
let mut root_fragments = independent_layout.fragments.into_iter().collect::<Vec<_>>(); let mut root_fragments = independent_layout.fragments.into_iter().collect::<Vec<_>>();

View file

@ -12,11 +12,10 @@ use crate::dom::NodeExt;
use crate::dom_traversal::{Contents, NodeAndStyleInfo}; use crate::dom_traversal::{Contents, NodeAndStyleInfo};
use crate::flexbox::FlexContainer; use crate::flexbox::FlexContainer;
use crate::flow::BlockFormattingContext; use crate::flow::BlockFormattingContext;
use crate::fragment_tree::{ use crate::fragment_tree::{BaseFragmentInfo, FragmentFlags};
BaseFragmentInfo, BoxFragment, Fragment, FragmentFlags, SpecificLayoutInfo, use crate::layout_box_base::{
CacheableLayoutResult, CacheableLayoutResultAndInputs, LayoutBoxBase,
}; };
use crate::geom::LogicalSides;
use crate::layout_box_base::LayoutBoxBase;
use crate::positioned::PositioningContext; use crate::positioned::PositioningContext;
use crate::replaced::ReplacedContents; use crate::replaced::ReplacedContents;
use crate::sizing::{self, ComputeInlineContentSizes, InlineContentSizesResult}; use crate::sizing::{self, ComputeInlineContentSizes, InlineContentSizesResult};
@ -68,35 +67,6 @@ impl Baselines {
} }
} }
pub(crate) struct IndependentLayout {
pub fragments: Vec<Fragment>,
/// <https://drafts.csswg.org/css2/visudet.html#root-height>
pub content_block_size: Au,
/// If a table has collapsed columns, it can become smaller than what the parent
/// formatting context decided. This is the resulting inline content size.
/// This is None for non-table layouts and for tables without collapsed columns.
pub content_inline_size_for_table: Option<Au>,
/// The offset of the last inflow baseline of this layout in the content area, if
/// there was one. This is used to propagate baselines to the ancestors of `display:
/// inline-block`.
pub baselines: Baselines,
/// Whether or not this layout depends on the containing block size.
pub depends_on_block_constraints: bool,
/// Additional information of this layout that could be used by Javascripts and devtools.
pub specific_layout_info: Option<SpecificLayoutInfo>,
}
pub(crate) struct IndependentLayoutResult {
pub fragment: BoxFragment,
pub baselines: Option<Baselines>,
pub pbm_sums: LogicalSides<Au>,
}
impl IndependentFormattingContext { impl IndependentFormattingContext {
pub fn construct<'dom, Node: NodeExt<'dom>>( pub fn construct<'dom, Node: NodeExt<'dom>>(
context: &LayoutContext, context: &LayoutContext,
@ -255,17 +225,20 @@ impl IndependentNonReplacedContents {
positioning_context: &mut PositioningContext, positioning_context: &mut PositioningContext,
containing_block_for_children: &ContainingBlock, containing_block_for_children: &ContainingBlock,
containing_block: &ContainingBlock, containing_block: &ContainingBlock,
) -> IndependentLayout { depends_on_block_constraints: bool,
) -> CacheableLayoutResult {
match self { match self {
IndependentNonReplacedContents::Flow(bfc) => bfc.layout( IndependentNonReplacedContents::Flow(bfc) => bfc.layout(
layout_context, layout_context,
positioning_context, positioning_context,
containing_block_for_children, containing_block_for_children,
depends_on_block_constraints,
), ),
IndependentNonReplacedContents::Flex(fc) => fc.layout( IndependentNonReplacedContents::Flex(fc) => fc.layout(
layout_context, layout_context,
positioning_context, positioning_context,
containing_block_for_children, containing_block_for_children,
depends_on_block_constraints,
), ),
IndependentNonReplacedContents::Grid(fc) => fc.layout( IndependentNonReplacedContents::Grid(fc) => fc.layout(
layout_context, layout_context,
@ -278,10 +251,70 @@ impl IndependentNonReplacedContents {
positioning_context, positioning_context,
containing_block_for_children, containing_block_for_children,
containing_block, containing_block,
depends_on_block_constraints,
), ),
} }
} }
#[cfg_attr(
feature = "tracing",
tracing::instrument(
name = "IndependentNonReplacedContents::layout_with_caching",
skip_all,
fields(servo_profiling = true),
level = "trace",
)
)]
pub fn layout_with_caching(
&self,
layout_context: &LayoutContext,
positioning_context: &mut PositioningContext,
containing_block_for_children: &ContainingBlock,
containing_block: &ContainingBlock,
base: &LayoutBoxBase,
depends_on_block_constraints: bool,
) -> CacheableLayoutResult {
if let Some(cache) = base.cached_layout_result.borrow().as_ref() {
if cache.containing_block_for_children_size.inline ==
containing_block_for_children.size.inline &&
(cache.containing_block_for_children_size.block ==
containing_block_for_children.size.block ||
!(cache.result.depends_on_block_constraints ||
depends_on_block_constraints))
{
positioning_context.append(cache.positioning_context.clone());
return cache.result.clone();
}
#[cfg(feature = "tracing")]
tracing::debug!(
name: "NonReplaced cache miss",
cached = ?cache.containing_block_for_children_size,
required = ?containing_block_for_children.size,
);
}
let mut child_positioning_context = PositioningContext::new_for_subtree(
positioning_context.collects_for_nearest_positioned_ancestor(),
);
let result = self.layout(
layout_context,
&mut child_positioning_context,
containing_block_for_children,
containing_block,
depends_on_block_constraints,
);
*base.cached_layout_result.borrow_mut() = Some(CacheableLayoutResultAndInputs {
result: result.clone(),
positioning_context: child_positioning_context.clone(),
containing_block_for_children_size: containing_block_for_children.size.clone(),
});
positioning_context.append(child_positioning_context);
result
}
#[inline] #[inline]
pub(crate) fn layout_style<'a>(&'a self, base: &'a LayoutBoxBase) -> LayoutStyle<'a> { pub(crate) fn layout_style<'a>(&'a self, base: &'a LayoutBoxBase) -> LayoutStyle<'a> {
match self { match self {

View file

@ -45,6 +45,7 @@ pub(crate) enum Fragment {
IFrame(ArcRefCell<IFrameFragment>), IFrame(ArcRefCell<IFrameFragment>),
} }
#[derive(Clone)]
pub(crate) struct CollapsedBlockMargins { pub(crate) struct CollapsedBlockMargins {
pub collapsed_through: bool, pub collapsed_through: bool,
pub start: CollapsedMargin, pub start: CollapsedMargin,

View file

@ -2,15 +2,20 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * 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/. */ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use std::fmt::{Debug, Formatter};
use app_units::Au;
use atomic_refcell::AtomicRefCell; use atomic_refcell::AtomicRefCell;
use servo_arc::Arc; use servo_arc::Arc;
use style::properties::ComputedValues; use style::properties::ComputedValues;
use crate::ConstraintSpace;
use crate::context::LayoutContext; use crate::context::LayoutContext;
use crate::fragment_tree::BaseFragmentInfo; use crate::formatting_contexts::Baselines;
use crate::fragment_tree::{BaseFragmentInfo, CollapsedBlockMargins, Fragment, SpecificLayoutInfo};
use crate::geom::SizeConstraint; use crate::geom::SizeConstraint;
use crate::positioned::PositioningContext;
use crate::sizing::{ComputeInlineContentSizes, InlineContentSizesResult}; use crate::sizing::{ComputeInlineContentSizes, InlineContentSizesResult};
use crate::{ConstraintSpace, ContainingBlockSize};
/// A box tree node that handles containing information about style and the original DOM /// A box tree node that handles containing information about style and the original DOM
/// node or pseudo-element that it is based on. This also handles caching of layout values /// node or pseudo-element that it is based on. This also handles caching of layout values
@ -18,12 +23,12 @@ use crate::sizing::{ComputeInlineContentSizes, InlineContentSizesResult};
/// passes. /// passes.
/// ///
/// In the future, this will hold layout results to support incremental layout. /// In the future, this will hold layout results to support incremental layout.
#[derive(Debug)]
pub(crate) struct LayoutBoxBase { pub(crate) struct LayoutBoxBase {
pub base_fragment_info: BaseFragmentInfo, pub base_fragment_info: BaseFragmentInfo,
pub style: Arc<ComputedValues>, pub style: Arc<ComputedValues>,
pub cached_inline_content_size: pub cached_inline_content_size:
AtomicRefCell<Option<(SizeConstraint, InlineContentSizesResult)>>, AtomicRefCell<Option<(SizeConstraint, InlineContentSizesResult)>>,
pub cached_layout_result: AtomicRefCell<Option<CacheableLayoutResultAndInputs>>,
} }
impl LayoutBoxBase { impl LayoutBoxBase {
@ -32,6 +37,7 @@ impl LayoutBoxBase {
base_fragment_info, base_fragment_info,
style, style,
cached_inline_content_size: AtomicRefCell::default(), cached_inline_content_size: AtomicRefCell::default(),
cached_layout_result: AtomicRefCell::default(),
} }
} }
@ -58,3 +64,45 @@ impl LayoutBoxBase {
result result
} }
} }
impl Debug for LayoutBoxBase {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
f.debug_struct("LayoutBoxBase").finish()
}
}
#[derive(Clone)]
pub(crate) struct CacheableLayoutResult {
pub fragments: Vec<Fragment>,
/// <https://drafts.csswg.org/css2/visudet.html#root-height>
pub content_block_size: Au,
/// If this layout is for a block container, this tracks the collapsable size
/// of start and end margins and whether or not the block container collapsed through.
pub collapsible_margins_in_children: CollapsedBlockMargins,
/// The contents of a table may force it to become wider than what we would expect
/// from 'width' and 'min-width'. This is the resulting inline content size,
/// or None for non-table layouts.
pub content_inline_size_for_table: Option<Au>,
/// The offset of the last inflow baseline of this layout in the content area, if
/// there was one. This is used to propagate baselines to the ancestors of `display:
/// inline-block`.
pub baselines: Baselines,
/// Whether or not this layout depends on the containing block size.
pub depends_on_block_constraints: bool,
/// Additional information of this layout that could be used by Javascripts and devtools.
pub specific_layout_info: Option<SpecificLayoutInfo>,
}
pub(crate) struct CacheableLayoutResultAndInputs {
pub result: CacheableLayoutResult,
pub containing_block_for_children_size: ContainingBlockSize,
pub positioning_context: PositioningContext,
}

View file

@ -112,7 +112,7 @@ impl<'a> From<&'_ DefiniteContainingBlock<'a>> for IndefiniteContainingBlock {
} }
} }
#[derive(Debug)] #[derive(Clone, Debug)]
pub(crate) struct ContainingBlockSize { pub(crate) struct ContainingBlockSize {
inline: Au, inline: Au,
block: SizeConstraint, block: SizeConstraint,

View file

@ -41,6 +41,7 @@ pub(crate) struct AbsolutelyPositionedBox {
pub context: IndependentFormattingContext, pub context: IndependentFormattingContext,
} }
#[derive(Clone)]
pub(crate) struct PositioningContext { pub(crate) struct PositioningContext {
for_nearest_positioned_ancestor: Option<Vec<HoistedAbsolutelyPositionedBox>>, for_nearest_positioned_ancestor: Option<Vec<HoistedAbsolutelyPositionedBox>>,
@ -50,6 +51,7 @@ pub(crate) struct PositioningContext {
for_nearest_containing_block_for_all_descendants: Vec<HoistedAbsolutelyPositionedBox>, for_nearest_containing_block_for_all_descendants: Vec<HoistedAbsolutelyPositionedBox>,
} }
#[derive(Clone)]
pub(crate) struct HoistedAbsolutelyPositionedBox { pub(crate) struct HoistedAbsolutelyPositionedBox {
absolutely_positioned_box: ArcRefCell<AbsolutelyPositionedBox>, absolutely_positioned_box: ArcRefCell<AbsolutelyPositionedBox>,
@ -299,7 +301,7 @@ impl PositioningContext {
.push(box_) .push(box_)
} }
fn is_empty(&self) -> bool { pub(crate) fn is_empty(&self) -> bool {
self.for_nearest_containing_block_for_all_descendants self.for_nearest_containing_block_for_all_descendants
.is_empty() && .is_empty() &&
self.for_nearest_positioned_ancestor self.for_nearest_positioned_ancestor
@ -627,6 +629,7 @@ impl HoistedAbsolutelyPositionedBox {
&mut positioning_context, &mut positioning_context,
&containing_block_for_children, &containing_block_for_children,
containing_block, containing_block,
false, /* depends_on_block_constraints */
); );
let inline_size = if let Some(inline_size) = let inline_size = if let Some(inline_size) =

View file

@ -29,7 +29,7 @@ use super::{
TableLayoutStyle, TableSlot, TableSlotCell, TableSlotCoordinates, TableTrack, TableTrackGroup, TableLayoutStyle, TableSlot, TableSlotCell, TableSlotCoordinates, TableTrack, TableTrackGroup,
}; };
use crate::context::LayoutContext; use crate::context::LayoutContext;
use crate::formatting_contexts::{Baselines, IndependentLayout}; use crate::formatting_contexts::Baselines;
use crate::fragment_tree::{ use crate::fragment_tree::{
BaseFragmentInfo, BoxFragment, CollapsedBlockMargins, ExtraBackground, Fragment, FragmentFlags, BaseFragmentInfo, BoxFragment, CollapsedBlockMargins, ExtraBackground, Fragment, FragmentFlags,
PositioningFragment, SpecificLayoutInfo, PositioningFragment, SpecificLayoutInfo,
@ -38,6 +38,7 @@ use crate::geom::{
LogicalRect, LogicalSides, LogicalSides1D, LogicalVec2, PhysicalPoint, PhysicalRect, LogicalRect, LogicalSides, LogicalSides1D, LogicalVec2, PhysicalPoint, PhysicalRect,
PhysicalSides, PhysicalVec, Size, SizeConstraint, ToLogical, ToLogicalWithContainingBlock, PhysicalSides, PhysicalVec, Size, SizeConstraint, ToLogical, ToLogicalWithContainingBlock,
}; };
use crate::layout_box_base::CacheableLayoutResult;
use crate::positioned::{PositioningContext, PositioningContextLength, relative_adjustement}; use crate::positioned::{PositioningContext, PositioningContextLength, relative_adjustement};
use crate::sizing::{ComputeInlineContentSizes, ContentSizes, InlineContentSizesResult}; use crate::sizing::{ComputeInlineContentSizes, ContentSizes, InlineContentSizesResult};
use crate::style_ext::{ use crate::style_ext::{
@ -51,7 +52,7 @@ use crate::{
/// the table. Note that this is only done for slots that are not /// the table. Note that this is only done for slots that are not
/// covered by spans or empty. /// covered by spans or empty.
struct CellLayout { struct CellLayout {
layout: IndependentLayout, layout: CacheableLayoutResult,
padding: LogicalSides<Au>, padding: LogicalSides<Au>,
border: LogicalSides<Au>, border: LogicalSides<Au>,
positioning_context: PositioningContext, positioning_context: PositioningContext,
@ -1137,6 +1138,7 @@ impl<'a> TableLayout<'a> {
layout_context, layout_context,
&mut positioning_context, &mut positioning_context,
&containing_block_for_children, &containing_block_for_children,
false, /* depends_on_block_constraints */
); );
Some(CellLayout { Some(CellLayout {
@ -1539,13 +1541,15 @@ impl<'a> TableLayout<'a> {
positioning_context: &mut PositioningContext, positioning_context: &mut PositioningContext,
containing_block_for_children: &ContainingBlock, containing_block_for_children: &ContainingBlock,
containing_block_for_table: &ContainingBlock, containing_block_for_table: &ContainingBlock,
) -> IndependentLayout { depends_on_block_constraints: bool,
) -> CacheableLayoutResult {
let table_writing_mode = containing_block_for_children.style.writing_mode; let table_writing_mode = containing_block_for_children.style.writing_mode;
self.compute_border_collapse(table_writing_mode); self.compute_border_collapse(table_writing_mode);
let layout_style = self.table.layout_style(Some(&self)); let layout_style = self.table.layout_style(Some(&self));
let depends_on_block_constraints = layout_style let depends_on_block_constraints = depends_on_block_constraints ||
.content_box_sizes_and_padding_border_margin(&containing_block_for_table.into()) layout_style
.depends_on_block_constraints; .content_box_sizes_and_padding_border_margin(&containing_block_for_table.into())
.depends_on_block_constraints;
self.pbm = layout_style self.pbm = layout_style
.padding_border_margin_with_writing_mode_and_containing_block_inline_size( .padding_border_margin_with_writing_mode_and_containing_block_inline_size(
@ -1579,13 +1583,14 @@ impl<'a> TableLayout<'a> {
let offset_from_wrapper = -self.pbm.padding - self.pbm.border; let offset_from_wrapper = -self.pbm.padding - self.pbm.border;
let mut current_block_offset = offset_from_wrapper.block_start; let mut current_block_offset = offset_from_wrapper.block_start;
let mut table_layout = IndependentLayout { let mut table_layout = CacheableLayoutResult {
fragments: Vec::new(), fragments: Vec::new(),
content_block_size: Zero::zero(), content_block_size: Zero::zero(),
content_inline_size_for_table: None, content_inline_size_for_table: None,
baselines: Baselines::default(), baselines: Baselines::default(),
depends_on_block_constraints, depends_on_block_constraints,
specific_layout_info: Some(SpecificLayoutInfo::TableWrapper), specific_layout_info: Some(SpecificLayoutInfo::TableWrapper),
collapsible_margins_in_children: CollapsedBlockMargins::zero(),
}; };
table_layout table_layout
@ -2664,12 +2669,14 @@ impl Table {
positioning_context: &mut PositioningContext, positioning_context: &mut PositioningContext,
containing_block_for_children: &ContainingBlock, containing_block_for_children: &ContainingBlock,
containing_block_for_table: &ContainingBlock, containing_block_for_table: &ContainingBlock,
) -> IndependentLayout { depends_on_block_constraints: bool,
) -> CacheableLayoutResult {
TableLayout::new(self).layout( TableLayout::new(self).layout(
layout_context, layout_context,
positioning_context, positioning_context,
containing_block_for_children, containing_block_for_children,
containing_block_for_table, containing_block_for_table,
depends_on_block_constraints,
) )
} }
} }

View file

@ -18,7 +18,6 @@ use crate::cell::ArcRefCell;
use crate::context::LayoutContext; use crate::context::LayoutContext;
use crate::formatting_contexts::{ use crate::formatting_contexts::{
Baselines, IndependentFormattingContext, IndependentFormattingContextContents, Baselines, IndependentFormattingContext, IndependentFormattingContextContents,
IndependentLayout,
}; };
use crate::fragment_tree::{ use crate::fragment_tree::{
BoxFragment, CollapsedBlockMargins, Fragment, FragmentFlags, SpecificLayoutInfo, BoxFragment, CollapsedBlockMargins, Fragment, FragmentFlags, SpecificLayoutInfo,
@ -27,6 +26,7 @@ use crate::geom::{
LogicalSides, LogicalVec2, PhysicalPoint, PhysicalRect, PhysicalSides, PhysicalSize, Size, LogicalSides, LogicalVec2, PhysicalPoint, PhysicalRect, PhysicalSides, PhysicalSize, Size,
SizeConstraint, Sizes, SizeConstraint, Sizes,
}; };
use crate::layout_box_base::CacheableLayoutResult;
use crate::positioned::{AbsolutelyPositionedBox, PositioningContext, PositioningContextLength}; use crate::positioned::{AbsolutelyPositionedBox, PositioningContext, PositioningContextLength};
use crate::sizing::{ComputeInlineContentSizes, ContentSizes, InlineContentSizesResult}; use crate::sizing::{ComputeInlineContentSizes, ContentSizes, InlineContentSizesResult};
use crate::style_ext::{ComputedValuesExt, LayoutStyle}; use crate::style_ext::{ComputedValuesExt, LayoutStyle};
@ -264,6 +264,7 @@ impl taffy::LayoutPartialTree for TaffyContainerContext<'_> {
&mut child_positioning_context, &mut child_positioning_context,
&content_box_size_override, &content_box_size_override,
containing_block, containing_block,
false, /* depends_on_block_constraints */
); );
// Store layout data on child for later access // Store layout data on child for later access
@ -425,7 +426,7 @@ impl TaffyContainer {
positioning_context: &mut PositioningContext, positioning_context: &mut PositioningContext,
content_box_size_override: &ContainingBlock, content_box_size_override: &ContainingBlock,
containing_block: &ContainingBlock, containing_block: &ContainingBlock,
) -> IndependentLayout { ) -> CacheableLayoutResult {
let mut container_ctx = TaffyContainerContext { let mut container_ctx = TaffyContainerContext {
layout_context, layout_context,
positioning_context, positioning_context,
@ -643,7 +644,7 @@ impl TaffyContainer {
}) })
.collect(); .collect();
IndependentLayout { CacheableLayoutResult {
fragments, fragments,
content_block_size: Au::from_f32_px(output.size.height) - pbm.padding_border_sums.block, content_block_size: Au::from_f32_px(output.size.height) - pbm.padding_border_sums.block,
content_inline_size_for_table: None, content_inline_size_for_table: None,
@ -654,8 +655,8 @@ impl TaffyContainer {
// "true" is a safe default as it will prevent Servo from performing optimizations based // "true" is a safe default as it will prevent Servo from performing optimizations based
// on the assumption that the node's size does not depend on block constraints. // on the assumption that the node's size does not depend on block constraints.
depends_on_block_constraints: true, depends_on_block_constraints: true,
specific_layout_info: container_ctx.specific_layout_info, specific_layout_info: container_ctx.specific_layout_info,
collapsible_margins_in_children: CollapsedBlockMargins::zero(),
} }
} }