mirror of
https://github.com/servo/servo.git
synced 2025-08-06 06:00:15 +01:00
layout: Add a repaint-only incremental layout mode (#36978)
This change adds the simplest kind of incremental layout. When Servo detects that all style changes only require a repaint, only run stacking context tree and WebRender display list generation. This means that these kind of restyles do not need a re-layout. Instead, the existing box and fragment trees will be used and the styles of damaged nodes will be updated in their box and fragment tree nodes. This requires a new style repair DOM traversal for nodes that have had their style damaged. In addition, careful accounting of all the places where we store style must happen in order ot update those styles. Testing: This is covered by existing WPT tests as it should not change observable behavior. We have created a test case which shows a 50% speedup when run in Servo, even though there still a long way to go to match the speed of other browsers: https://gist.github.com/mrobinson/44ec87d028c0198917a7715a06dd98a0 Co-authored-by: Oriol Brufau <obrufau@igalia.com> Signed-off-by: Martin Robinson <mrobinson@igalia.com> Signed-off-by: Martin Robinson <mrobinson@igalia.com> Co-authored-by: Oriol Brufau <obrufau@igalia.com>
This commit is contained in:
parent
51fa6cdcb6
commit
8808f9a468
20 changed files with 467 additions and 72 deletions
|
@ -7,7 +7,6 @@ use std::char::{ToLowercase, ToUppercase};
|
|||
|
||||
use icu_segmenter::WordSegmenter;
|
||||
use itertools::izip;
|
||||
use servo_arc::Arc;
|
||||
use style::computed_values::white_space_collapse::T as WhiteSpaceCollapse;
|
||||
use style::values::specified::text::TextTransformCase;
|
||||
use unicode_bidi::Level;
|
||||
|
@ -158,7 +157,7 @@ impl InlineFormattingContextBuilder {
|
|||
independent_formatting_context: IndependentFormattingContext,
|
||||
) -> ArcRefCell<InlineItem> {
|
||||
let inline_level_box = ArcRefCell::new(InlineItem::Atomic(
|
||||
Arc::new(independent_formatting_context),
|
||||
ArcRefCell::new(independent_formatting_context),
|
||||
self.current_text_offset,
|
||||
Level::ltr(), /* This will be assigned later if necessary. */
|
||||
));
|
||||
|
@ -189,7 +188,8 @@ impl InlineFormattingContextBuilder {
|
|||
}
|
||||
|
||||
pub(crate) fn push_float_box(&mut self, float_box: FloatBox) -> ArcRefCell<InlineItem> {
|
||||
let inline_level_box = ArcRefCell::new(InlineItem::OutOfFlowFloatBox(Arc::new(float_box)));
|
||||
let inline_level_box =
|
||||
ArcRefCell::new(InlineItem::OutOfFlowFloatBox(ArcRefCell::new(float_box)));
|
||||
self.inline_items.push(inline_level_box.clone());
|
||||
self.contains_floats = true;
|
||||
inline_level_box
|
||||
|
|
|
@ -7,6 +7,10 @@ use std::vec::IntoIter;
|
|||
use app_units::Au;
|
||||
use fonts::FontMetrics;
|
||||
use malloc_size_of_derive::MallocSizeOf;
|
||||
use script::layout_dom::ServoLayoutNode;
|
||||
use script_layout_interface::wrapper_traits::{LayoutNode, ThreadSafeLayoutNode};
|
||||
use servo_arc::Arc as ServoArc;
|
||||
use style::properties::ComputedValues;
|
||||
|
||||
use super::{
|
||||
InlineContainerState, InlineContainerStateFlags, SharedInlineStyles,
|
||||
|
@ -66,6 +70,16 @@ impl InlineBox {
|
|||
pub(crate) fn layout_style(&self) -> LayoutStyle {
|
||||
LayoutStyle::Default(&self.base.style)
|
||||
}
|
||||
|
||||
pub(crate) fn repair_style(
|
||||
&mut self,
|
||||
node: &ServoLayoutNode,
|
||||
new_style: &ServoArc<ComputedValues>,
|
||||
) {
|
||||
self.base.repair_style(new_style);
|
||||
*self.shared_inline_styles.style.borrow_mut() = new_style.clone();
|
||||
*self.shared_inline_styles.selected.borrow_mut() = node.to_threadsafe().selected_style();
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, MallocSizeOf)]
|
||||
|
|
|
@ -90,12 +90,13 @@ use line::{
|
|||
use line_breaker::LineBreaker;
|
||||
use malloc_size_of_derive::MallocSizeOf;
|
||||
use range::Range;
|
||||
use script::layout_dom::ServoLayoutNode;
|
||||
use servo_arc::Arc;
|
||||
use style::Zero;
|
||||
use style::computed_values::text_wrap_mode::T as TextWrapMode;
|
||||
use style::computed_values::vertical_align::T as VerticalAlign;
|
||||
use style::computed_values::white_space_collapse::T as WhiteSpaceCollapse;
|
||||
use style::context::QuirksMode;
|
||||
use style::context::{QuirksMode, SharedStyleContext};
|
||||
use style::properties::ComputedValues;
|
||||
use style::properties::style_structs::InheritedText;
|
||||
use style::values::generics::box_::VerticalAlignKeyword;
|
||||
|
@ -210,15 +211,41 @@ pub(crate) enum InlineItem {
|
|||
ArcRefCell<AbsolutelyPositionedBox>,
|
||||
usize, /* offset_in_text */
|
||||
),
|
||||
OutOfFlowFloatBox(#[conditional_malloc_size_of] Arc<FloatBox>),
|
||||
OutOfFlowFloatBox(ArcRefCell<FloatBox>),
|
||||
Atomic(
|
||||
#[conditional_malloc_size_of] Arc<IndependentFormattingContext>,
|
||||
ArcRefCell<IndependentFormattingContext>,
|
||||
usize, /* offset_in_text */
|
||||
Level, /* bidi_level */
|
||||
),
|
||||
}
|
||||
|
||||
impl InlineItem {
|
||||
pub(crate) fn repair_style(
|
||||
&self,
|
||||
context: &SharedStyleContext,
|
||||
node: &ServoLayoutNode,
|
||||
new_style: &Arc<ComputedValues>,
|
||||
) {
|
||||
match self {
|
||||
InlineItem::StartInlineBox(inline_box) => {
|
||||
inline_box.borrow_mut().repair_style(node, new_style);
|
||||
},
|
||||
InlineItem::EndInlineBox => {},
|
||||
// TextRun holds a handle the `InlineSharedStyles` which is updated when repairing inline box
|
||||
// and `display: contents` styles.
|
||||
InlineItem::TextRun(..) => {},
|
||||
InlineItem::OutOfFlowAbsolutelyPositionedBox(positioned_box, ..) => positioned_box
|
||||
.borrow_mut()
|
||||
.context
|
||||
.repair_style(context, new_style),
|
||||
InlineItem::OutOfFlowFloatBox(float_box) => float_box
|
||||
.borrow_mut()
|
||||
.contents
|
||||
.repair_style(context, new_style),
|
||||
InlineItem::Atomic(atomic, ..) => atomic.borrow_mut().repair_style(context, new_style),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn invalidate_cached_fragment(&self) {
|
||||
match self {
|
||||
InlineItem::StartInlineBox(inline_box) => {
|
||||
|
@ -232,11 +259,14 @@ impl InlineItem {
|
|||
.base
|
||||
.invalidate_cached_fragment();
|
||||
},
|
||||
InlineItem::OutOfFlowFloatBox(float_box) => {
|
||||
float_box.contents.base.invalidate_cached_fragment()
|
||||
},
|
||||
InlineItem::OutOfFlowFloatBox(float_box) => float_box
|
||||
.borrow()
|
||||
.contents
|
||||
.base
|
||||
.invalidate_cached_fragment(),
|
||||
InlineItem::Atomic(independent_formatting_context, ..) => {
|
||||
independent_formatting_context
|
||||
.borrow()
|
||||
.base
|
||||
.invalidate_cached_fragment();
|
||||
},
|
||||
|
@ -252,9 +282,11 @@ impl InlineItem {
|
|||
InlineItem::OutOfFlowAbsolutelyPositionedBox(positioned_box, ..) => {
|
||||
positioned_box.borrow().context.base.fragments()
|
||||
},
|
||||
InlineItem::OutOfFlowFloatBox(float_box) => float_box.contents.base.fragments(),
|
||||
InlineItem::OutOfFlowFloatBox(float_box) => {
|
||||
float_box.borrow().contents.base.fragments()
|
||||
},
|
||||
InlineItem::Atomic(independent_formatting_context, ..) => {
|
||||
independent_formatting_context.base.fragments()
|
||||
independent_formatting_context.borrow().base.fragments()
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -978,6 +1010,7 @@ impl InlineFormattingContextLayout<'_> {
|
|||
.as_physical(Some(self.containing_block));
|
||||
self.fragments
|
||||
.push(Fragment::Positioning(PositioningFragment::new_anonymous(
|
||||
self.root_nesting_level.style.clone(),
|
||||
physical_line_rect,
|
||||
fragments,
|
||||
)));
|
||||
|
@ -1770,7 +1803,7 @@ impl InlineFormattingContext {
|
|||
InlineItem::EndInlineBox => layout.finish_inline_box(),
|
||||
InlineItem::TextRun(run) => run.borrow().layout_into_line_items(&mut layout),
|
||||
InlineItem::Atomic(atomic_formatting_context, offset_in_text, bidi_level) => {
|
||||
atomic_formatting_context.layout_into_line_items(
|
||||
atomic_formatting_context.borrow().layout_into_line_items(
|
||||
&mut layout,
|
||||
*offset_in_text,
|
||||
*bidi_level,
|
||||
|
@ -1785,7 +1818,7 @@ impl InlineFormattingContext {
|
|||
));
|
||||
},
|
||||
InlineItem::OutOfFlowFloatBox(float_box) => {
|
||||
float_box.layout_into_line_items(&mut layout);
|
||||
float_box.borrow().layout_into_line_items(&mut layout);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -2448,7 +2481,7 @@ impl<'layout_data> ContentSizesComputation<'layout_data> {
|
|||
let InlineContentSizesResult {
|
||||
sizes: outer,
|
||||
depends_on_block_constraints,
|
||||
} = atomic.outer_inline_content_sizes(
|
||||
} = atomic.borrow().outer_inline_content_sizes(
|
||||
self.layout_context,
|
||||
&self.constraint_space.into(),
|
||||
&LogicalVec2::zero(),
|
||||
|
|
|
@ -9,9 +9,11 @@ use app_units::{Au, MAX_AU};
|
|||
use inline::InlineFormattingContext;
|
||||
use malloc_size_of_derive::MallocSizeOf;
|
||||
use rayon::iter::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator};
|
||||
use script::layout_dom::ServoLayoutNode;
|
||||
use servo_arc::Arc;
|
||||
use style::Zero;
|
||||
use style::computed_values::clear::T as StyleClear;
|
||||
use style::context::SharedStyleContext;
|
||||
use style::logical_geometry::Direction;
|
||||
use style::properties::ComputedValues;
|
||||
use style::servo::selector_parser::PseudoElement;
|
||||
|
@ -21,6 +23,7 @@ use style::values::specified::{Display, TextAlignKeyword};
|
|||
|
||||
use crate::cell::ArcRefCell;
|
||||
use crate::context::LayoutContext;
|
||||
use crate::dom::NodeExt;
|
||||
use crate::flow::float::{
|
||||
Clear, ContainingBlockPositionInfo, FloatBox, FloatSide, PlacementAmongFloats,
|
||||
SequentialLayoutState,
|
||||
|
@ -91,6 +94,36 @@ pub(crate) enum BlockLevelBox {
|
|||
}
|
||||
|
||||
impl BlockLevelBox {
|
||||
pub(crate) fn repair_style(
|
||||
&mut self,
|
||||
context: &SharedStyleContext,
|
||||
node: &ServoLayoutNode,
|
||||
new_style: &Arc<ComputedValues>,
|
||||
) {
|
||||
self.with_base_mut(|base| {
|
||||
base.repair_style(new_style);
|
||||
});
|
||||
|
||||
match self {
|
||||
BlockLevelBox::Independent(independent_formatting_context) => {
|
||||
independent_formatting_context.repair_style(context, new_style)
|
||||
},
|
||||
BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(positioned_box) => positioned_box
|
||||
.borrow_mut()
|
||||
.context
|
||||
.repair_style(context, new_style),
|
||||
BlockLevelBox::OutOfFlowFloatBox(float_box) => {
|
||||
float_box.contents.repair_style(context, new_style)
|
||||
},
|
||||
BlockLevelBox::OutsideMarker(outside_marker) => {
|
||||
outside_marker.repair_style(context, node, new_style)
|
||||
},
|
||||
BlockLevelBox::SameFormattingContextBlock { base, .. } => {
|
||||
base.repair_style(new_style);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn invalidate_cached_fragment(&self) {
|
||||
self.with_base(LayoutBoxBase::invalidate_cached_fragment);
|
||||
}
|
||||
|
@ -113,6 +146,20 @@ impl BlockLevelBox {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn with_base_mut<T>(&mut self, callback: impl Fn(&mut LayoutBoxBase) -> T) -> T {
|
||||
match self {
|
||||
BlockLevelBox::Independent(independent_formatting_context) => {
|
||||
callback(&mut independent_formatting_context.base)
|
||||
},
|
||||
BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(positioned_box) => {
|
||||
callback(&mut positioned_box.borrow_mut().context.base)
|
||||
},
|
||||
BlockLevelBox::OutOfFlowFloatBox(float_box) => callback(&mut float_box.contents.base),
|
||||
BlockLevelBox::OutsideMarker(outside_marker) => callback(&mut outside_marker.base),
|
||||
BlockLevelBox::SameFormattingContextBlock { base, .. } => callback(base),
|
||||
}
|
||||
}
|
||||
|
||||
fn contains_floats(&self) -> bool {
|
||||
match self {
|
||||
BlockLevelBox::SameFormattingContextBlock {
|
||||
|
@ -360,6 +407,16 @@ impl OutsideMarker {
|
|||
None,
|
||||
)))
|
||||
}
|
||||
|
||||
fn repair_style(
|
||||
&mut self,
|
||||
context: &SharedStyleContext,
|
||||
node: &ServoLayoutNode,
|
||||
new_style: &Arc<ComputedValues>,
|
||||
) {
|
||||
self.list_item_style = node.style(context);
|
||||
self.base.repair_style(new_style);
|
||||
}
|
||||
}
|
||||
|
||||
impl BlockFormattingContext {
|
||||
|
|
|
@ -59,7 +59,7 @@ impl BoxTree {
|
|||
// > none, user agents must instead apply the overflow-* values of the first such child
|
||||
// > element to the viewport. The element from which the value is propagated must then have a
|
||||
// > used overflow value of visible.
|
||||
let root_style = root_element.style(context);
|
||||
let root_style = root_element.style(context.shared_context());
|
||||
|
||||
let mut viewport_overflow_x = root_style.clone_overflow_x();
|
||||
let mut viewport_overflow_y = root_style.clone_overflow_y();
|
||||
|
@ -76,7 +76,7 @@ impl BoxTree {
|
|||
continue;
|
||||
}
|
||||
|
||||
let style = child.style(context);
|
||||
let style = child.style(context.shared_context());
|
||||
if !style.get_box().display.is_none() {
|
||||
viewport_overflow_x = style.clone_overflow_x();
|
||||
viewport_overflow_y = style.clone_overflow_y();
|
||||
|
@ -293,7 +293,7 @@ fn construct_for_root_element(
|
|||
context: &LayoutContext,
|
||||
root_element: ServoLayoutNode<'_>,
|
||||
) -> Vec<ArcRefCell<BlockLevelBox>> {
|
||||
let info = NodeAndStyleInfo::new(root_element, root_element.style(context));
|
||||
let info = NodeAndStyleInfo::new(root_element, root_element.style(context.shared_context()));
|
||||
let box_style = info.style.get_box();
|
||||
|
||||
let display_inside = match Display::from(box_style.display) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue