layout: Perform cascading in parallel.

Speeds up the cascade by 3x.
This commit is contained in:
Patrick Walton 2014-01-27 14:41:06 -08:00
parent fa02b82b88
commit 9c3af574e5
6 changed files with 74 additions and 31 deletions

View file

@ -17,6 +17,7 @@ pub trait MatchMethods {
fn match_node(&self, stylist: &Stylist); fn match_node(&self, stylist: &Stylist);
fn match_subtree(&self, stylist: &Stylist); fn match_subtree(&self, stylist: &Stylist);
unsafe fn cascade_node(&self, parent: Option<LayoutNode>);
fn cascade_subtree(&self, parent: Option<LayoutNode>); fn cascade_subtree(&self, parent: Option<LayoutNode>);
} }
@ -51,18 +52,34 @@ impl<'ln> MatchMethods for LayoutNode<'ln> {
} }
} }
fn cascade_subtree(&self, parent: Option<LayoutNode>) { unsafe fn cascade_node(&self, parent: Option<LayoutNode>) {
macro_rules! cascade_node( macro_rules! cascade_node(
($applicable_declarations: ident, $style: ident) => {{ ($applicable_declarations: ident, $style: ident) => {{
// Get our parent's style. This must be unsafe so that we don't touch the parent's
// borrow flags.
//
// FIXME(pcwalton): Isolate this unsafety into the `wrapper` module to allow
// enforced safe, race-free access to the parent style.
let parent_style = match parent { let parent_style = match parent {
Some(ref parent) => Some(parent.style()), None => None,
None => None Some(parent_node) => {
let parent_layout_data = parent_node.borrow_layout_data_unchecked();
match *parent_layout_data {
None => fail!("no parent data?!"),
Some(ref parent_layout_data) => {
match parent_layout_data.data.style {
None => fail!("parent hasn't been styled yet?!"),
Some(ref style) => Some(style.get()),
}
}
}
}
}; };
let computed_values = { let computed_values = {
let layout_data_ref = self.borrow_layout_data(); let layout_data_ref = self.borrow_layout_data();
let layout_data = layout_data_ref.get().as_ref().unwrap(); let layout_data = layout_data_ref.get().as_ref().unwrap();
Arc::new(cascade(layout_data.data.$applicable_declarations, parent_style.map(|parent_style| parent_style.get()))) Arc::new(cascade(layout_data.data.$applicable_declarations, parent_style))
}; };
let mut layout_data_ref = self.mutate_layout_data(); let mut layout_data_ref = self.mutate_layout_data();
@ -102,6 +119,12 @@ impl<'ln> MatchMethods for LayoutNode<'ln> {
cascade_node!(after_applicable_declarations, after_style); cascade_node!(after_applicable_declarations, after_style);
} }
} }
}
fn cascade_subtree(&self, parent: Option<LayoutNode>) {
unsafe {
self.cascade_node(parent);
}
for kid in self.children() { for kid in self.children() {
if kid.is_element() { if kid.is_element() {

View file

@ -7,6 +7,7 @@
use extra::arc::MutexArc; use extra::arc::MutexArc;
use green::task::GreenTask; use green::task::GreenTask;
use layout::flow::LeafSet; use layout::flow::LeafSet;
use layout::util::OpaqueNode;
use std::cast; use std::cast;
use std::ptr; use std::ptr;
use std::rt::Runtime; use std::rt::Runtime;
@ -45,6 +46,9 @@ pub struct LayoutContext {
/// ///
/// FIXME(pcwalton): Make this no longer an unsafe pointer once we have fast `RWArc`s. /// FIXME(pcwalton): Make this no longer an unsafe pointer once we have fast `RWArc`s.
stylist: *Stylist, stylist: *Stylist,
/// The root node at which we're starting the layout.
reflow_root: OpaqueNode,
} }
impl LayoutContext { impl LayoutContext {

View file

@ -289,7 +289,7 @@ impl LayoutTask {
} }
// Create a layout context for use in building display lists, hit testing, &c. // Create a layout context for use in building display lists, hit testing, &c.
fn build_layout_context(&self) -> LayoutContext { fn build_layout_context(&self, reflow_root: &LayoutNode) -> LayoutContext {
let font_context_info = FontContextInfo { let font_context_info = FontContextInfo {
backend: self.opts.render_backend, backend: self.opts.render_backend,
needs_font_list: true, needs_font_list: true,
@ -303,6 +303,7 @@ impl LayoutTask {
leaf_set: self.leaf_set.clone(), leaf_set: self.leaf_set.clone(),
font_context_info: font_context_info, font_context_info: font_context_info,
stylist: &*self.stylist, stylist: &*self.stylist,
reflow_root: OpaqueNode::from_layout_node(reflow_root),
} }
} }
@ -505,7 +506,7 @@ impl LayoutTask {
self.screen_size = current_screen_size; self.screen_size = current_screen_size;
// Create a layout context for use throughout the following passes. // Create a layout context for use throughout the following passes.
let mut layout_ctx = self.build_layout_context(); let mut layout_ctx = self.build_layout_context(node);
// Initialize layout data for each node. // Initialize layout data for each node.
// //
@ -522,15 +523,21 @@ impl LayoutTask {
match self.parallel_traversal { match self.parallel_traversal {
None => node.match_subtree(self.stylist), None => node.match_subtree(self.stylist),
Some(ref mut traversal) => { Some(ref mut traversal) => {
parallel::match_subtree(node, &mut layout_ctx, traversal) parallel::match_and_cascade_subtree(node, &mut layout_ctx, traversal)
} }
} }
}); });
// If we're doing layout sequentially, do the cascade separately.
//
// TODO(pcwalton): Integrate this into `match_subtree`.
if self.parallel_traversal.is_none() {
profile(time::LayoutSelectorCascadeCategory, self.profiler_chan.clone(), || { profile(time::LayoutSelectorCascadeCategory, self.profiler_chan.clone(), || {
node.cascade_subtree(None); node.cascade_subtree(None);
}); });
} }
} }
}
// Construct the flow tree. // Construct the flow tree.
let mut layout_root = profile(time::LayoutTreeBuilderCategory, let mut layout_root = profile(time::LayoutTreeBuilderCategory,

View file

@ -11,6 +11,7 @@ use layout::context::LayoutContext;
use layout::flow::{Flow, LeafSet, PostorderFlowTraversal}; use layout::flow::{Flow, LeafSet, PostorderFlowTraversal};
use layout::flow; use layout::flow;
use layout::layout_task::{AssignHeightsAndStoreOverflowTraversal, BubbleWidthsTraversal}; use layout::layout_task::{AssignHeightsAndStoreOverflowTraversal, BubbleWidthsTraversal};
use layout::util::OpaqueNode;
use layout::wrapper::LayoutNode; use layout::wrapper::LayoutNode;
use extra::arc::MutexArc; use extra::arc::MutexArc;
@ -115,7 +116,7 @@ impl<'a> ParallelPostorderFlowTraversal for BubbleWidthsTraversal<'a> {}
impl<'a> ParallelPostorderFlowTraversal for AssignHeightsAndStoreOverflowTraversal<'a> {} impl<'a> ParallelPostorderFlowTraversal for AssignHeightsAndStoreOverflowTraversal<'a> {}
fn match_node(unsafe_layout_node: UnsafeLayoutNode, fn match_and_cascade_node(unsafe_layout_node: UnsafeLayoutNode,
proxy: &mut WorkerProxy<*mut LayoutContext,UnsafeLayoutNode>) { proxy: &mut WorkerProxy<*mut LayoutContext,UnsafeLayoutNode>) {
unsafe { unsafe {
let layout_context: &mut LayoutContext = cast::transmute(*proxy.user_data()); let layout_context: &mut LayoutContext = cast::transmute(*proxy.user_data());
@ -123,19 +124,27 @@ fn match_node(unsafe_layout_node: UnsafeLayoutNode,
// Get a real layout node. // Get a real layout node.
let node: LayoutNode = cast::transmute(unsafe_layout_node); let node: LayoutNode = cast::transmute(unsafe_layout_node);
// Perform the CSS selector matching.
let stylist: &Stylist = cast::transmute(layout_context.stylist);
node.match_node(stylist);
// Perform the CSS cascade.
let parent_opt = if OpaqueNode::from_layout_node(&node) == layout_context.reflow_root {
None
} else {
node.parent_node()
};
node.cascade_node(parent_opt);
// Enqueue kids. // Enqueue kids.
for kid in node.children() { for kid in node.children() {
if kid.is_element() { if kid.is_element() {
proxy.push(WorkUnit { proxy.push(WorkUnit {
fun: match_node, fun: match_and_cascade_node,
data: layout_node_to_unsafe_layout_node(&kid), data: layout_node_to_unsafe_layout_node(&kid),
}); });
} }
} }
// Perform the CSS selector matching.
let stylist: &Stylist = cast::transmute(layout_context.stylist);
node.match_node(stylist);
} }
} }
@ -160,7 +169,7 @@ fn assign_heights_and_store_overflow(unsafe_flow: UnsafeFlow,
assign_heights_traversal.run_parallel(unsafe_flow) assign_heights_traversal.run_parallel(unsafe_flow)
} }
pub fn match_subtree(root_node: &LayoutNode, pub fn match_and_cascade_subtree(root_node: &LayoutNode,
layout_context: &mut LayoutContext, layout_context: &mut LayoutContext,
queue: &mut WorkQueue<*mut LayoutContext,UnsafeLayoutNode>) { queue: &mut WorkQueue<*mut LayoutContext,UnsafeLayoutNode>) {
unsafe { unsafe {
@ -169,7 +178,7 @@ pub fn match_subtree(root_node: &LayoutNode,
// Enqueue the root node. // Enqueue the root node.
queue.push(WorkUnit { queue.push(WorkUnit {
fun: match_node, fun: match_and_cascade_node,
data: layout_node_to_unsafe_layout_node(root_node), data: layout_node_to_unsafe_layout_node(root_node),
}); });

View file

@ -174,9 +174,7 @@ pub struct LayoutDataWrapper {
/// A trait that allows access to the layout data of a DOM node. /// A trait that allows access to the layout data of a DOM node.
pub trait LayoutDataAccess { pub trait LayoutDataAccess {
/// Borrows the layout data without checks. /// Borrows the layout data without checks.
/// unsafe fn borrow_layout_data_unchecked(&self) -> *Option<LayoutDataWrapper>;
/// FIXME(pcwalton): Make safe.
// unsafe fn borrow_layout_data_unchecked<'a>(&'a self) -> &'a Option<~LayoutData>;
/// Borrows the layout data immutably. Fails on a conflicting borrow. /// Borrows the layout data immutably. Fails on a conflicting borrow.
fn borrow_layout_data<'a>(&'a self) -> Ref<'a,Option<LayoutDataWrapper>>; fn borrow_layout_data<'a>(&'a self) -> Ref<'a,Option<LayoutDataWrapper>>;
/// Borrows the layout data mutably. Fails on a conflicting borrow. /// Borrows the layout data mutably. Fails on a conflicting borrow.
@ -184,6 +182,11 @@ pub trait LayoutDataAccess {
} }
impl<'ln> LayoutDataAccess for LayoutNode<'ln> { impl<'ln> LayoutDataAccess for LayoutNode<'ln> {
#[inline(always)]
unsafe fn borrow_layout_data_unchecked(&self) -> *Option<LayoutDataWrapper> {
cast::transmute(self.get().layout_data.borrow_unchecked())
}
#[inline(always)] #[inline(always)]
fn borrow_layout_data<'a>(&'a self) -> Ref<'a,Option<LayoutDataWrapper>> { fn borrow_layout_data<'a>(&'a self) -> Ref<'a,Option<LayoutDataWrapper>> {
unsafe { unsafe {

View file

@ -154,13 +154,10 @@ impl LayoutDataRef {
/// Borrows the layout data immutably, *asserting that there are no mutators*. Bad things will /// Borrows the layout data immutably, *asserting that there are no mutators*. Bad things will
/// happen if you try to mutate the layout data while this is held. This is the only thread- /// happen if you try to mutate the layout data while this is held. This is the only thread-
/// safe layout data accessor. /// safe layout data accessor.
/// #[inline]
/// FIXME(pcwalton): Enforce this invariant via the type system. Will require traversal pub unsafe fn borrow_unchecked(&self) -> *Option<LayoutData> {
/// functions to be trusted, but c'est la vie. cast::transmute(&self.data_cell)
// #[inline] }
// pub unsafe fn borrow_unchecked<'a>(&'a self) -> &'a () {
// self.data.borrow_unchecked()
// }
/// Borrows the layout data immutably. This function is *not* thread-safe. /// Borrows the layout data immutably. This function is *not* thread-safe.
#[inline] #[inline]