mirror of
https://github.com/servo/servo.git
synced 2025-08-04 05:00:08 +01:00
Eliminate the sequential/traversal parallel distinction in favor of a unified adaptive driver.
MozReview-Commit-ID: ADVTNJntzmp
This commit is contained in:
parent
f7c6b2f04e
commit
707ab455bb
13 changed files with 164 additions and 208 deletions
|
@ -21,8 +21,6 @@ use style::dom::UnsafeNode;
|
||||||
use traversal::{AssignBSizes, AssignISizes, BubbleISizes};
|
use traversal::{AssignBSizes, AssignISizes, BubbleISizes};
|
||||||
use traversal::{PostorderFlowTraversal, PreorderFlowTraversal};
|
use traversal::{PostorderFlowTraversal, PreorderFlowTraversal};
|
||||||
|
|
||||||
pub use style::parallel::traverse_dom;
|
|
||||||
|
|
||||||
/// Traversal chunk size.
|
/// Traversal chunk size.
|
||||||
const CHUNK_SIZE: usize = 16;
|
const CHUNK_SIZE: usize = 16;
|
||||||
|
|
||||||
|
|
|
@ -18,8 +18,6 @@ use style::servo::restyle_damage::{REFLOW, REFLOW_OUT_OF_FLOW, STORE_OVERFLOW};
|
||||||
use traversal::{AssignBSizes, AssignISizes, BubbleISizes, BuildDisplayList};
|
use traversal::{AssignBSizes, AssignISizes, BubbleISizes, BuildDisplayList};
|
||||||
use traversal::{InorderFlowTraversal, PostorderFlowTraversal, PreorderFlowTraversal};
|
use traversal::{InorderFlowTraversal, PostorderFlowTraversal, PreorderFlowTraversal};
|
||||||
|
|
||||||
pub use style::sequential::traverse_dom;
|
|
||||||
|
|
||||||
pub fn resolve_generated_content(root: &mut Flow, layout_context: &LayoutContext) {
|
pub fn resolve_generated_content(root: &mut Flow, layout_context: &LayoutContext) {
|
||||||
ResolveGeneratedContent::new(&layout_context).traverse(root, 0);
|
ResolveGeneratedContent::new(&layout_context).traverse(root, 0);
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,14 +15,13 @@ use style::data::ElementData;
|
||||||
use style::dom::{NodeInfo, TElement, TNode};
|
use style::dom::{NodeInfo, TElement, TNode};
|
||||||
use style::selector_parser::RestyleDamage;
|
use style::selector_parser::RestyleDamage;
|
||||||
use style::servo::restyle_damage::{BUBBLE_ISIZES, REFLOW, REFLOW_OUT_OF_FLOW, REPAINT, REPOSITION};
|
use style::servo::restyle_damage::{BUBBLE_ISIZES, REFLOW, REFLOW_OUT_OF_FLOW, REPAINT, REPOSITION};
|
||||||
use style::traversal::{DomTraversal, TraversalDriver, recalc_style_at};
|
use style::traversal::{DomTraversal, recalc_style_at};
|
||||||
use style::traversal::PerLevelTraversalData;
|
use style::traversal::PerLevelTraversalData;
|
||||||
use wrapper::{GetRawData, LayoutNodeLayoutData};
|
use wrapper::{GetRawData, LayoutNodeLayoutData};
|
||||||
use wrapper::ThreadSafeLayoutNodeHelpers;
|
use wrapper::ThreadSafeLayoutNodeHelpers;
|
||||||
|
|
||||||
pub struct RecalcStyleAndConstructFlows<'a> {
|
pub struct RecalcStyleAndConstructFlows<'a> {
|
||||||
context: LayoutContext<'a>,
|
context: LayoutContext<'a>,
|
||||||
driver: TraversalDriver,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> RecalcStyleAndConstructFlows<'a> {
|
impl<'a> RecalcStyleAndConstructFlows<'a> {
|
||||||
|
@ -33,10 +32,9 @@ impl<'a> RecalcStyleAndConstructFlows<'a> {
|
||||||
|
|
||||||
impl<'a> RecalcStyleAndConstructFlows<'a> {
|
impl<'a> RecalcStyleAndConstructFlows<'a> {
|
||||||
/// Creates a traversal context, taking ownership of the shared layout context.
|
/// Creates a traversal context, taking ownership of the shared layout context.
|
||||||
pub fn new(context: LayoutContext<'a>, driver: TraversalDriver) -> Self {
|
pub fn new(context: LayoutContext<'a>) -> Self {
|
||||||
RecalcStyleAndConstructFlows {
|
RecalcStyleAndConstructFlows {
|
||||||
context: context,
|
context: context,
|
||||||
driver: driver,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,10 +83,6 @@ impl<'a, E> DomTraversal<E> for RecalcStyleAndConstructFlows<'a>
|
||||||
fn shared_context(&self) -> &SharedStyleContext {
|
fn shared_context(&self) -> &SharedStyleContext {
|
||||||
&self.context.style_context
|
&self.context.style_context
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_parallel(&self) -> bool {
|
|
||||||
self.driver.is_parallel()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A top-down traversal.
|
/// A top-down traversal.
|
||||||
|
|
|
@ -127,6 +127,7 @@ use style::context::{StyleSystemOptions, ThreadLocalStyleContextCreationInfo};
|
||||||
use style::context::RegisteredSpeculativePainter;
|
use style::context::RegisteredSpeculativePainter;
|
||||||
use style::context::RegisteredSpeculativePainters;
|
use style::context::RegisteredSpeculativePainters;
|
||||||
use style::dom::{ShowSubtree, ShowSubtreeDataAndPrimaryValues, TElement, TNode};
|
use style::dom::{ShowSubtree, ShowSubtreeDataAndPrimaryValues, TElement, TNode};
|
||||||
|
use style::driver;
|
||||||
use style::error_reporting::{NullReporter, RustLogReporter};
|
use style::error_reporting::{NullReporter, RustLogReporter};
|
||||||
use style::invalidation::element::restyle_hints::RestyleHint;
|
use style::invalidation::element::restyle_hints::RestyleHint;
|
||||||
use style::logical_geometry::LogicalPoint;
|
use style::logical_geometry::LogicalPoint;
|
||||||
|
@ -139,7 +140,7 @@ use style::stylesheets::{Origin, OriginSet, Stylesheet, DocumentStyleSheet, Styl
|
||||||
use style::stylist::Stylist;
|
use style::stylist::Stylist;
|
||||||
use style::thread_state;
|
use style::thread_state;
|
||||||
use style::timer::Timer;
|
use style::timer::Timer;
|
||||||
use style::traversal::{DomTraversal, TraversalDriver};
|
use style::traversal::DomTraversal;
|
||||||
use style::traversal_flags::TraversalFlags;
|
use style::traversal_flags::TraversalFlags;
|
||||||
use style_traits::CSSPixel;
|
use style_traits::CSSPixel;
|
||||||
use style_traits::DevicePixel;
|
use style_traits::DevicePixel;
|
||||||
|
@ -1276,14 +1277,14 @@ impl LayoutThread {
|
||||||
let mut layout_context =
|
let mut layout_context =
|
||||||
self.build_layout_context(guards.clone(), true, &map);
|
self.build_layout_context(guards.clone(), true, &map);
|
||||||
|
|
||||||
// NB: Type inference falls apart here for some reason, so we need to be very verbose. :-(
|
let thread_pool = if self.parallel_flag {
|
||||||
let traversal_driver = if self.parallel_flag && self.parallel_traversal.is_some() {
|
self.parallel_traversal.as_ref()
|
||||||
TraversalDriver::Parallel
|
|
||||||
} else {
|
} else {
|
||||||
TraversalDriver::Sequential
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
let traversal = RecalcStyleAndConstructFlows::new(layout_context, traversal_driver);
|
// NB: Type inference falls apart here for some reason, so we need to be very verbose. :-(
|
||||||
|
let traversal = RecalcStyleAndConstructFlows::new(layout_context);
|
||||||
let token = {
|
let token = {
|
||||||
let context = <RecalcStyleAndConstructFlows as
|
let context = <RecalcStyleAndConstructFlows as
|
||||||
DomTraversal<ServoLayoutElement>>::shared_context(&traversal);
|
DomTraversal<ServoLayoutElement>>::shared_context(&traversal);
|
||||||
|
@ -1300,16 +1301,8 @@ impl LayoutThread {
|
||||||
self.time_profiler_chan.clone(),
|
self.time_profiler_chan.clone(),
|
||||||
|| {
|
|| {
|
||||||
// Perform CSS selector matching and flow construction.
|
// Perform CSS selector matching and flow construction.
|
||||||
if traversal_driver.is_parallel() {
|
driver::traverse_dom::<ServoLayoutElement, RecalcStyleAndConstructFlows>(
|
||||||
let pool = self.parallel_traversal.as_ref().unwrap();
|
&traversal, element, token, thread_pool);
|
||||||
// Parallel mode
|
|
||||||
parallel::traverse_dom::<ServoLayoutElement, RecalcStyleAndConstructFlows>(
|
|
||||||
&traversal, element, token, pool);
|
|
||||||
} else {
|
|
||||||
// Sequential mode
|
|
||||||
sequential::traverse_dom::<ServoLayoutElement, RecalcStyleAndConstructFlows>(
|
|
||||||
&traversal, element, token);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
// TODO(pcwalton): Measure energy usage of text shaping, perhaps?
|
// TODO(pcwalton): Measure energy usage of text shaping, perhaps?
|
||||||
let text_shaping_time =
|
let text_shaping_time =
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
#![deny(missing_docs)]
|
#![deny(missing_docs)]
|
||||||
|
|
||||||
use attr::NamespaceConstraint;
|
use attr::NamespaceConstraint;
|
||||||
use parser::{Combinator, Component, SelectorImpl, SelectorIter};
|
use parser::{Combinator, Component, SelectorImpl};
|
||||||
|
|
||||||
/// A trait to visit selector properties.
|
/// A trait to visit selector properties.
|
||||||
///
|
///
|
||||||
|
|
|
@ -386,14 +386,14 @@ impl fmt::Display for TraversalStatistics {
|
||||||
|
|
||||||
impl TraversalStatistics {
|
impl TraversalStatistics {
|
||||||
/// Computes the traversal time given the start time in seconds.
|
/// Computes the traversal time given the start time in seconds.
|
||||||
pub fn finish<E, D>(&mut self, traversal: &D, start: f64)
|
pub fn finish<E, D>(&mut self, traversal: &D, parallel: bool, start: f64)
|
||||||
where E: TElement,
|
where E: TElement,
|
||||||
D: DomTraversal<E>,
|
D: DomTraversal<E>,
|
||||||
{
|
{
|
||||||
let threshold = traversal.shared_context().options.style_statistics_threshold;
|
let threshold = traversal.shared_context().options.style_statistics_threshold;
|
||||||
let stylist = traversal.shared_context().stylist;
|
let stylist = traversal.shared_context().stylist;
|
||||||
|
|
||||||
self.is_parallel = Some(traversal.is_parallel());
|
self.is_parallel = Some(parallel);
|
||||||
self.is_large = Some(self.elements_traversed as usize >= threshold);
|
self.is_large = Some(self.elements_traversed as usize >= threshold);
|
||||||
self.traversal_time_ms = (time::precise_time_s() - start) * 1000.0;
|
self.traversal_time_ms = (time::precise_time_s() - start) * 1000.0;
|
||||||
self.selectors = stylist.num_selectors() as u32;
|
self.selectors = stylist.num_selectors() as u32;
|
||||||
|
|
132
components/style/driver.rs
Normal file
132
components/style/driver.rs
Normal file
|
@ -0,0 +1,132 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
//! Implements traversal over the DOM tree. The traversal starts in sequential
|
||||||
|
//! mode, and optionally parallelizes as it discovers work.
|
||||||
|
|
||||||
|
#![deny(missing_docs)]
|
||||||
|
|
||||||
|
use context::{StyleContext, ThreadLocalStyleContext};
|
||||||
|
use dom::{SendNode, TElement, TNode};
|
||||||
|
use parallel;
|
||||||
|
use parallel::{DispatchMode, WORK_UNIT_MAX};
|
||||||
|
use rayon;
|
||||||
|
use scoped_tls::ScopedTLS;
|
||||||
|
use std::borrow::Borrow;
|
||||||
|
use std::collections::VecDeque;
|
||||||
|
use std::mem;
|
||||||
|
use time;
|
||||||
|
use traversal::{DomTraversal, PerLevelTraversalData, PreTraverseToken};
|
||||||
|
|
||||||
|
/// Do a DOM traversal for top-down and (optionally) bottom-up processing,
|
||||||
|
/// generic over `D`.
|
||||||
|
///
|
||||||
|
/// We use an adaptive traversal strategy. We start out with simple sequential
|
||||||
|
/// processing, until we arrive at a wide enough level in the DOM that the
|
||||||
|
/// parallel traversal would parallelize it. If a thread pool is provided, we
|
||||||
|
/// then transfer control over to the parallel traversal.
|
||||||
|
pub fn traverse_dom<E, D>(
|
||||||
|
traversal: &D,
|
||||||
|
root: E,
|
||||||
|
token: PreTraverseToken,
|
||||||
|
pool: Option<&rayon::ThreadPool>
|
||||||
|
)
|
||||||
|
where
|
||||||
|
E: TElement,
|
||||||
|
D: DomTraversal<E>,
|
||||||
|
{
|
||||||
|
debug_assert!(token.should_traverse());
|
||||||
|
|
||||||
|
let dump_stats = traversal.shared_context().options.dump_style_statistics;
|
||||||
|
let start_time = if dump_stats { Some(time::precise_time_s()) } else { None };
|
||||||
|
|
||||||
|
// Declare the main-thread context, as well as the worker-thread contexts,
|
||||||
|
// which we may or may not instantiate. It's important to declare the worker-
|
||||||
|
// thread contexts first, so that they get dropped second. This matters because:
|
||||||
|
// * ThreadLocalContexts borrow AtomicRefCells in TLS.
|
||||||
|
// * Dropping a ThreadLocalContext can run SequentialTasks.
|
||||||
|
// * Sequential tasks may call into functions like
|
||||||
|
// Servo_StyleSet_GetBaseComputedValuesForElement, which instantiate a
|
||||||
|
// ThreadLocalStyleContext on the main thread. If the main thread
|
||||||
|
// ThreadLocalStyleContext has not released its TLS borrow by that point,
|
||||||
|
// we'll panic on double-borrow.
|
||||||
|
let mut maybe_tls: Option<ScopedTLS<ThreadLocalStyleContext<E>>> = None;
|
||||||
|
let mut tlc = ThreadLocalStyleContext::new(traversal.shared_context());
|
||||||
|
let mut context = StyleContext {
|
||||||
|
shared: traversal.shared_context(),
|
||||||
|
thread_local: &mut tlc,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Process the nodes breadth-first, just like the parallel traversal does.
|
||||||
|
// This helps keep similar traversal characteristics for the style sharing
|
||||||
|
// cache.
|
||||||
|
let mut discovered =
|
||||||
|
VecDeque::<SendNode<E::ConcreteNode>>::with_capacity(WORK_UNIT_MAX * 2);
|
||||||
|
let mut depth = root.depth();
|
||||||
|
let mut nodes_remaining_at_current_depth = 1;
|
||||||
|
discovered.push_back(unsafe { SendNode::new(root.as_node()) });
|
||||||
|
while let Some(node) = discovered.pop_front() {
|
||||||
|
let mut children_to_process = 0isize;
|
||||||
|
let traversal_data = PerLevelTraversalData { current_dom_depth: depth };
|
||||||
|
traversal.process_preorder(&traversal_data, &mut context, *node, |n| {
|
||||||
|
children_to_process += 1;
|
||||||
|
discovered.push_back(unsafe { SendNode::new(n) });
|
||||||
|
});
|
||||||
|
|
||||||
|
traversal.handle_postorder_traversal(&mut context, root.as_node().opaque(),
|
||||||
|
*node, children_to_process);
|
||||||
|
|
||||||
|
nodes_remaining_at_current_depth -= 1;
|
||||||
|
if nodes_remaining_at_current_depth == 0 {
|
||||||
|
depth += 1;
|
||||||
|
// If there is enough work to parallelize over, and the caller allows
|
||||||
|
// parallelism, switch to the parallel driver. We do this only when
|
||||||
|
// moving to the next level in the dom so that we can pass the same
|
||||||
|
// depth for all the children.
|
||||||
|
if pool.is_some() && discovered.len() > WORK_UNIT_MAX {
|
||||||
|
let pool = pool.unwrap();
|
||||||
|
maybe_tls = Some(ScopedTLS::<ThreadLocalStyleContext<E>>::new(pool));
|
||||||
|
let root_opaque = root.as_node().opaque();
|
||||||
|
let drain = discovered.drain(..);
|
||||||
|
pool.install(|| {
|
||||||
|
rayon::scope(|scope| {
|
||||||
|
parallel::traverse_nodes(
|
||||||
|
drain,
|
||||||
|
DispatchMode::TailCall,
|
||||||
|
/* recursion_ok = */ true,
|
||||||
|
root_opaque,
|
||||||
|
PerLevelTraversalData { current_dom_depth: depth },
|
||||||
|
scope,
|
||||||
|
pool,
|
||||||
|
traversal,
|
||||||
|
maybe_tls.as_ref().unwrap()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
nodes_remaining_at_current_depth = discovered.len();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dump statistics to stdout if requested.
|
||||||
|
if dump_stats {
|
||||||
|
let mut aggregate =
|
||||||
|
mem::replace(&mut context.thread_local.statistics, Default::default());
|
||||||
|
let parallel = maybe_tls.is_some();
|
||||||
|
if let Some(tls) = maybe_tls {
|
||||||
|
let slots = unsafe { tls.unsafe_get() };
|
||||||
|
aggregate = slots.iter().fold(aggregate, |acc, t| {
|
||||||
|
match *t.borrow() {
|
||||||
|
None => acc,
|
||||||
|
Some(ref cx) => &cx.borrow().statistics + &acc,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
aggregate.finish(traversal, parallel, start_time.unwrap());
|
||||||
|
if aggregate.is_large_traversal() {
|
||||||
|
println!("{}", aggregate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,21 +7,19 @@
|
||||||
use context::{SharedStyleContext, StyleContext};
|
use context::{SharedStyleContext, StyleContext};
|
||||||
use dom::{TNode, TElement};
|
use dom::{TNode, TElement};
|
||||||
use gecko::wrapper::{GeckoElement, GeckoNode};
|
use gecko::wrapper::{GeckoElement, GeckoNode};
|
||||||
use traversal::{DomTraversal, PerLevelTraversalData, TraversalDriver, recalc_style_at};
|
use traversal::{DomTraversal, PerLevelTraversalData, recalc_style_at};
|
||||||
|
|
||||||
/// This is the simple struct that Gecko uses to encapsulate a DOM traversal for
|
/// This is the simple struct that Gecko uses to encapsulate a DOM traversal for
|
||||||
/// styling.
|
/// styling.
|
||||||
pub struct RecalcStyleOnly<'a> {
|
pub struct RecalcStyleOnly<'a> {
|
||||||
shared: SharedStyleContext<'a>,
|
shared: SharedStyleContext<'a>,
|
||||||
driver: TraversalDriver,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> RecalcStyleOnly<'a> {
|
impl<'a> RecalcStyleOnly<'a> {
|
||||||
/// Create a `RecalcStyleOnly` traversal from a `SharedStyleContext`.
|
/// Create a `RecalcStyleOnly` traversal from a `SharedStyleContext`.
|
||||||
pub fn new(shared: SharedStyleContext<'a>, driver: TraversalDriver) -> Self {
|
pub fn new(shared: SharedStyleContext<'a>) -> Self {
|
||||||
RecalcStyleOnly {
|
RecalcStyleOnly {
|
||||||
shared: shared,
|
shared: shared,
|
||||||
driver: driver,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -50,8 +48,4 @@ impl<'recalc, 'le> DomTraversal<GeckoElement<'le>> for RecalcStyleOnly<'recalc>
|
||||||
fn shared_context(&self) -> &SharedStyleContext {
|
fn shared_context(&self) -> &SharedStyleContext {
|
||||||
&self.shared
|
&self.shared
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_parallel(&self) -> bool {
|
|
||||||
self.driver.is_parallel()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -105,6 +105,7 @@ pub mod counter_style;
|
||||||
pub mod custom_properties;
|
pub mod custom_properties;
|
||||||
pub mod data;
|
pub mod data;
|
||||||
pub mod dom;
|
pub mod dom;
|
||||||
|
pub mod driver;
|
||||||
pub mod element_state;
|
pub mod element_state;
|
||||||
#[cfg(feature = "servo")] mod encoding_support;
|
#[cfg(feature = "servo")] mod encoding_support;
|
||||||
pub mod error_reporting;
|
pub mod error_reporting;
|
||||||
|
@ -128,7 +129,6 @@ pub mod sharing;
|
||||||
pub mod style_resolver;
|
pub mod style_resolver;
|
||||||
pub mod stylist;
|
pub mod stylist;
|
||||||
#[cfg(feature = "servo")] #[allow(unsafe_code)] pub mod servo;
|
#[cfg(feature = "servo")] #[allow(unsafe_code)] pub mod servo;
|
||||||
pub mod sequential;
|
|
||||||
pub mod str;
|
pub mod str;
|
||||||
pub mod style_adjuster;
|
pub mod style_adjuster;
|
||||||
pub mod stylesheet_set;
|
pub mod stylesheet_set;
|
||||||
|
|
|
@ -23,15 +23,13 @@
|
||||||
#![deny(missing_docs)]
|
#![deny(missing_docs)]
|
||||||
|
|
||||||
use arrayvec::ArrayVec;
|
use arrayvec::ArrayVec;
|
||||||
use context::{StyleContext, ThreadLocalStyleContext, TraversalStatistics};
|
use context::{StyleContext, ThreadLocalStyleContext};
|
||||||
use dom::{OpaqueNode, SendNode, TElement, TNode};
|
use dom::{OpaqueNode, SendNode, TElement};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use rayon;
|
use rayon;
|
||||||
use scoped_tls::ScopedTLS;
|
use scoped_tls::ScopedTLS;
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use std::borrow::Borrow;
|
use traversal::{DomTraversal, PerLevelTraversalData};
|
||||||
use time;
|
|
||||||
use traversal::{DomTraversal, PerLevelTraversalData, PreTraverseToken};
|
|
||||||
|
|
||||||
/// The minimum stack size for a thread in the styling pool, in kilobytes.
|
/// The minimum stack size for a thread in the styling pool, in kilobytes.
|
||||||
pub const STYLE_THREAD_STACK_SIZE_KB: usize = 128;
|
pub const STYLE_THREAD_STACK_SIZE_KB: usize = 128;
|
||||||
|
@ -54,59 +52,6 @@ pub const WORK_UNIT_MAX: usize = 16;
|
||||||
/// threads, so we keep it compact.
|
/// threads, so we keep it compact.
|
||||||
type WorkUnit<N> = ArrayVec<[SendNode<N>; WORK_UNIT_MAX]>;
|
type WorkUnit<N> = ArrayVec<[SendNode<N>; WORK_UNIT_MAX]>;
|
||||||
|
|
||||||
/// Entry point for the parallel traversal.
|
|
||||||
#[allow(unsafe_code)]
|
|
||||||
pub fn traverse_dom<E, D>(traversal: &D,
|
|
||||||
root: E,
|
|
||||||
token: PreTraverseToken,
|
|
||||||
pool: &rayon::ThreadPool)
|
|
||||||
where E: TElement,
|
|
||||||
D: DomTraversal<E>,
|
|
||||||
{
|
|
||||||
debug_assert!(traversal.is_parallel());
|
|
||||||
debug_assert!(token.should_traverse());
|
|
||||||
|
|
||||||
let dump_stats = traversal.shared_context().options.dump_style_statistics;
|
|
||||||
let start_time = if dump_stats { Some(time::precise_time_s()) } else { None };
|
|
||||||
|
|
||||||
let traversal_data = PerLevelTraversalData {
|
|
||||||
current_dom_depth: root.depth(),
|
|
||||||
};
|
|
||||||
let tls = ScopedTLS::<ThreadLocalStyleContext<E>>::new(pool);
|
|
||||||
let send_root = unsafe { SendNode::new(root.as_node()) };
|
|
||||||
|
|
||||||
pool.install(|| {
|
|
||||||
rayon::scope(|scope| {
|
|
||||||
let root = send_root;
|
|
||||||
let root_opaque = root.opaque();
|
|
||||||
traverse_nodes(Some(root).into_iter(),
|
|
||||||
DispatchMode::TailCall,
|
|
||||||
true,
|
|
||||||
root_opaque,
|
|
||||||
traversal_data,
|
|
||||||
scope,
|
|
||||||
pool,
|
|
||||||
traversal,
|
|
||||||
&tls);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Dump statistics to stdout if requested.
|
|
||||||
if dump_stats {
|
|
||||||
let slots = unsafe { tls.unsafe_get() };
|
|
||||||
let mut aggregate = slots.iter().fold(TraversalStatistics::default(), |acc, t| {
|
|
||||||
match *t.borrow() {
|
|
||||||
None => acc,
|
|
||||||
Some(ref cx) => &cx.borrow().statistics + &acc,
|
|
||||||
}
|
|
||||||
});
|
|
||||||
aggregate.finish(traversal, start_time.unwrap());
|
|
||||||
if aggregate.is_large_traversal() {
|
|
||||||
println!("{}", aggregate);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A callback to create our thread local context. This needs to be
|
/// A callback to create our thread local context. This needs to be
|
||||||
/// out of line so we don't allocate stack space for the entire struct
|
/// out of line so we don't allocate stack space for the entire struct
|
||||||
/// in the caller.
|
/// in the caller.
|
||||||
|
@ -255,8 +200,10 @@ fn top_down_dom<'a, 'scope, E, D>(nodes: &'a [SendNode<E::ConcreteNode>],
|
||||||
/// Controls whether traverse_nodes may make a recursive call to continue
|
/// Controls whether traverse_nodes may make a recursive call to continue
|
||||||
/// doing work, or whether it should always dispatch work asynchronously.
|
/// doing work, or whether it should always dispatch work asynchronously.
|
||||||
#[derive(Clone, Copy, PartialEq)]
|
#[derive(Clone, Copy, PartialEq)]
|
||||||
enum DispatchMode {
|
pub enum DispatchMode {
|
||||||
|
/// This is the last operation by the caller.
|
||||||
TailCall,
|
TailCall,
|
||||||
|
/// This is not the last operation by the caller.
|
||||||
NotTailCall,
|
NotTailCall,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -267,7 +214,7 @@ impl DispatchMode {
|
||||||
/// Enqueues |nodes| for processing, possibly on this thread if the tail call
|
/// Enqueues |nodes| for processing, possibly on this thread if the tail call
|
||||||
/// conditions are met.
|
/// conditions are met.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn traverse_nodes<'a, 'scope, E, D, I>(
|
pub fn traverse_nodes<'a, 'scope, E, D, I>(
|
||||||
nodes: I,
|
nodes: I,
|
||||||
mode: DispatchMode,
|
mode: DispatchMode,
|
||||||
recursion_ok: bool,
|
recursion_ok: bool,
|
||||||
|
|
|
@ -1,68 +0,0 @@
|
||||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
||||||
|
|
||||||
//! Implements sequential traversal over the DOM tree.
|
|
||||||
|
|
||||||
#![deny(missing_docs)]
|
|
||||||
|
|
||||||
use context::{StyleContext, ThreadLocalStyleContext};
|
|
||||||
use dom::{SendNode, TElement, TNode};
|
|
||||||
use parallel::WORK_UNIT_MAX;
|
|
||||||
use std::collections::VecDeque;
|
|
||||||
use time;
|
|
||||||
use traversal::{DomTraversal, PerLevelTraversalData, PreTraverseToken};
|
|
||||||
|
|
||||||
/// Do a sequential DOM traversal for layout or styling, generic over `D`.
|
|
||||||
pub fn traverse_dom<E, D>(traversal: &D,
|
|
||||||
root: E,
|
|
||||||
token: PreTraverseToken)
|
|
||||||
where E: TElement,
|
|
||||||
D: DomTraversal<E>,
|
|
||||||
{
|
|
||||||
let dump_stats = traversal.shared_context().options.dump_style_statistics;
|
|
||||||
let start_time = if dump_stats { Some(time::precise_time_s()) } else { None };
|
|
||||||
|
|
||||||
debug_assert!(!traversal.is_parallel());
|
|
||||||
debug_assert!(token.should_traverse());
|
|
||||||
|
|
||||||
let mut discovered =
|
|
||||||
VecDeque::<SendNode<E::ConcreteNode>>::with_capacity(WORK_UNIT_MAX * 2);
|
|
||||||
let mut tlc = ThreadLocalStyleContext::new(traversal.shared_context());
|
|
||||||
let mut context = StyleContext {
|
|
||||||
shared: traversal.shared_context(),
|
|
||||||
thread_local: &mut tlc,
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// Process the nodes breadth-first, just like the parallel traversal does.
|
|
||||||
// This helps keep similar traversal characteristics for the style sharing
|
|
||||||
// cache.
|
|
||||||
let mut depth = root.depth();
|
|
||||||
let mut nodes_remaining_at_current_depth = 1;
|
|
||||||
discovered.push_back(unsafe { SendNode::new(root.as_node()) });
|
|
||||||
while let Some(node) = discovered.pop_front() {
|
|
||||||
let mut children_to_process = 0isize;
|
|
||||||
let traversal_data = PerLevelTraversalData { current_dom_depth: depth };
|
|
||||||
traversal.process_preorder(&traversal_data, &mut context, *node, |n| {
|
|
||||||
children_to_process += 1;
|
|
||||||
discovered.push_back(unsafe { SendNode::new(n) });
|
|
||||||
});
|
|
||||||
|
|
||||||
traversal.handle_postorder_traversal(&mut context, root.as_node().opaque(),
|
|
||||||
*node, children_to_process);
|
|
||||||
nodes_remaining_at_current_depth -= 1;
|
|
||||||
if nodes_remaining_at_current_depth == 0 {
|
|
||||||
depth += 1;
|
|
||||||
nodes_remaining_at_current_depth = discovered.len();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dump statistics to stdout if requested.
|
|
||||||
if dump_stats {
|
|
||||||
context.thread_local.statistics.finish(traversal, start_time.unwrap());
|
|
||||||
if context.thread_local.statistics.is_large_traversal() {
|
|
||||||
println!("{}", context.thread_local.statistics);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -40,23 +40,6 @@ impl PreTraverseToken {
|
||||||
pub fn should_traverse(&self) -> bool { self.0 }
|
pub fn should_traverse(&self) -> bool { self.0 }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The kind of traversals we could perform.
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
|
||||||
pub enum TraversalDriver {
|
|
||||||
/// A potentially parallel traversal.
|
|
||||||
Parallel,
|
|
||||||
/// A sequential traversal.
|
|
||||||
Sequential,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TraversalDriver {
|
|
||||||
/// Returns whether this represents a parallel traversal or not.
|
|
||||||
#[inline]
|
|
||||||
pub fn is_parallel(&self) -> bool {
|
|
||||||
matches!(*self, TraversalDriver::Parallel)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "servo")]
|
#[cfg(feature = "servo")]
|
||||||
#[inline]
|
#[inline]
|
||||||
fn is_servo_nonincremental_layout() -> bool {
|
fn is_servo_nonincremental_layout() -> bool {
|
||||||
|
@ -369,14 +352,6 @@ pub trait DomTraversal<E: TElement> : Sync {
|
||||||
|
|
||||||
/// Return the shared style context common to all worker threads.
|
/// Return the shared style context common to all worker threads.
|
||||||
fn shared_context(&self) -> &SharedStyleContext;
|
fn shared_context(&self) -> &SharedStyleContext;
|
||||||
|
|
||||||
/// Whether we're performing a parallel traversal.
|
|
||||||
///
|
|
||||||
/// NB: We do this check on runtime. We could guarantee correctness in this
|
|
||||||
/// regard via the type system via a `TraversalDriver` trait for this trait,
|
|
||||||
/// that could be one of two concrete types. It's not clear whether the
|
|
||||||
/// potential code size impact of that is worth it.
|
|
||||||
fn is_parallel(&self) -> bool;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Manually resolve style by sequentially walking up the parent chain to the
|
/// Manually resolve style by sequentially walking up the parent chain to the
|
||||||
|
|
|
@ -16,6 +16,7 @@ use style::context::{CascadeInputs, QuirksMode, SharedStyleContext, StyleContext
|
||||||
use style::context::ThreadLocalStyleContext;
|
use style::context::ThreadLocalStyleContext;
|
||||||
use style::data::ElementStyles;
|
use style::data::ElementStyles;
|
||||||
use style::dom::{ShowSubtreeData, TElement, TNode};
|
use style::dom::{ShowSubtreeData, TElement, TNode};
|
||||||
|
use style::driver;
|
||||||
use style::element_state::ElementState;
|
use style::element_state::ElementState;
|
||||||
use style::error_reporting::{NullReporter, ParseErrorReporter};
|
use style::error_reporting::{NullReporter, ParseErrorReporter};
|
||||||
use style::font_metrics::{FontMetricsProvider, get_metrics_provider_for_product};
|
use style::font_metrics::{FontMetricsProvider, get_metrics_provider_for_product};
|
||||||
|
@ -100,7 +101,6 @@ use style::gecko_bindings::sugar::refptr::RefPtr;
|
||||||
use style::gecko_properties::style_structs;
|
use style::gecko_properties::style_structs;
|
||||||
use style::invalidation::element::restyle_hints;
|
use style::invalidation::element::restyle_hints;
|
||||||
use style::media_queries::{MediaList, parse_media_query_list};
|
use style::media_queries::{MediaList, parse_media_query_list};
|
||||||
use style::parallel;
|
|
||||||
use style::parser::{ParserContext, self};
|
use style::parser::{ParserContext, self};
|
||||||
use style::properties::{CascadeFlags, ComputedValues, Importance};
|
use style::properties::{CascadeFlags, ComputedValues, Importance};
|
||||||
use style::properties::{IS_FIELDSET_CONTENT, IS_LINK, IS_VISITED_LINK, LonghandIdSet};
|
use style::properties::{IS_FIELDSET_CONTENT, IS_LINK, IS_VISITED_LINK, LonghandIdSet};
|
||||||
|
@ -112,7 +112,6 @@ use style::properties::animated_properties::compare_property_priority;
|
||||||
use style::properties::parse_one_declaration_into;
|
use style::properties::parse_one_declaration_into;
|
||||||
use style::rule_tree::StyleSource;
|
use style::rule_tree::StyleSource;
|
||||||
use style::selector_parser::PseudoElementCascadeType;
|
use style::selector_parser::PseudoElementCascadeType;
|
||||||
use style::sequential;
|
|
||||||
use style::shared_lock::{SharedRwLockReadGuard, StylesheetGuards, ToCssWithGuard, Locked};
|
use style::shared_lock::{SharedRwLockReadGuard, StylesheetGuards, ToCssWithGuard, Locked};
|
||||||
use style::string_cache::Atom;
|
use style::string_cache::Atom;
|
||||||
use style::style_adjuster::StyleAdjuster;
|
use style::style_adjuster::StyleAdjuster;
|
||||||
|
@ -126,7 +125,7 @@ use style::stylesheets::supports_rule::parse_condition_or_declaration;
|
||||||
use style::stylist::RuleInclusion;
|
use style::stylist::RuleInclusion;
|
||||||
use style::thread_state;
|
use style::thread_state;
|
||||||
use style::timer::Timer;
|
use style::timer::Timer;
|
||||||
use style::traversal::{DomTraversal, TraversalDriver};
|
use style::traversal::DomTraversal;
|
||||||
use style::traversal::resolve_style;
|
use style::traversal::resolve_style;
|
||||||
use style::traversal_flags::{TraversalFlags, self};
|
use style::traversal_flags::{TraversalFlags, self};
|
||||||
use style::values::{CustomIdent, KeyframesName};
|
use style::values::{CustomIdent, KeyframesName};
|
||||||
|
@ -237,21 +236,15 @@ fn traverse_subtree(element: GeckoElement,
|
||||||
debug!("Traversing subtree:");
|
debug!("Traversing subtree:");
|
||||||
debug!("{:?}", ShowSubtreeData(element.as_node()));
|
debug!("{:?}", ShowSubtreeData(element.as_node()));
|
||||||
|
|
||||||
let style_thread_pool = &*STYLE_THREAD_POOL;
|
let thread_pool_holder = &*STYLE_THREAD_POOL;
|
||||||
let traversal_driver = if !traversal_flags.contains(traversal_flags::ParallelTraversal) ||
|
let thread_pool = if traversal_flags.contains(traversal_flags::ParallelTraversal) {
|
||||||
style_thread_pool.style_thread_pool.is_none() {
|
thread_pool_holder.style_thread_pool.as_ref()
|
||||||
TraversalDriver::Sequential
|
|
||||||
} else {
|
} else {
|
||||||
TraversalDriver::Parallel
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
let traversal = RecalcStyleOnly::new(shared_style_context, traversal_driver);
|
let traversal = RecalcStyleOnly::new(shared_style_context);
|
||||||
if traversal_driver.is_parallel() {
|
driver::traverse_dom(&traversal, element, token, thread_pool);
|
||||||
parallel::traverse_dom(&traversal, element, token,
|
|
||||||
style_thread_pool.style_thread_pool.as_ref().unwrap());
|
|
||||||
} else {
|
|
||||||
sequential::traverse_dom(&traversal, element, token);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Traverses the subtree rooted at `root` for restyling.
|
/// Traverses the subtree rooted at `root` for restyling.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue