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_subtree(&self, stylist: &Stylist);
unsafe fn cascade_node(&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(
($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 {
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 layout_data_ref = self.borrow_layout_data();
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();
@ -102,6 +119,12 @@ impl<'ln> MatchMethods for LayoutNode<'ln> {
cascade_node!(after_applicable_declarations, after_style);
}
}
}
fn cascade_subtree(&self, parent: Option<LayoutNode>) {
unsafe {
self.cascade_node(parent);
}
for kid in self.children() {
if kid.is_element() {

View file

@ -7,6 +7,7 @@
use extra::arc::MutexArc;
use green::task::GreenTask;
use layout::flow::LeafSet;
use layout::util::OpaqueNode;
use std::cast;
use std::ptr;
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.
stylist: *Stylist,
/// The root node at which we're starting the layout.
reflow_root: OpaqueNode,
}
impl LayoutContext {

View file

@ -289,7 +289,7 @@ impl LayoutTask {
}
// 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 {
backend: self.opts.render_backend,
needs_font_list: true,
@ -303,6 +303,7 @@ impl LayoutTask {
leaf_set: self.leaf_set.clone(),
font_context_info: font_context_info,
stylist: &*self.stylist,
reflow_root: OpaqueNode::from_layout_node(reflow_root),
}
}
@ -505,7 +506,7 @@ impl LayoutTask {
self.screen_size = current_screen_size;
// 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.
//
@ -522,15 +523,21 @@ impl LayoutTask {
match self.parallel_traversal {
None => node.match_subtree(self.stylist),
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(), || {
node.cascade_subtree(None);
});
}
}
}
// Construct the flow tree.
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;
use layout::layout_task::{AssignHeightsAndStoreOverflowTraversal, BubbleWidthsTraversal};
use layout::util::OpaqueNode;
use layout::wrapper::LayoutNode;
use extra::arc::MutexArc;
@ -115,7 +116,7 @@ impl<'a> ParallelPostorderFlowTraversal for BubbleWidthsTraversal<'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>) {
unsafe {
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.
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.
for kid in node.children() {
if kid.is_element() {
proxy.push(WorkUnit {
fun: match_node,
fun: match_and_cascade_node,
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)
}
pub fn match_subtree(root_node: &LayoutNode,
pub fn match_and_cascade_subtree(root_node: &LayoutNode,
layout_context: &mut LayoutContext,
queue: &mut WorkQueue<*mut LayoutContext,UnsafeLayoutNode>) {
unsafe {
@ -169,7 +178,7 @@ pub fn match_subtree(root_node: &LayoutNode,
// Enqueue the root node.
queue.push(WorkUnit {
fun: match_node,
fun: match_and_cascade_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.
pub trait LayoutDataAccess {
/// Borrows the layout data without checks.
///
/// FIXME(pcwalton): Make safe.
// unsafe fn borrow_layout_data_unchecked<'a>(&'a self) -> &'a Option<~LayoutData>;
unsafe fn borrow_layout_data_unchecked(&self) -> *Option<LayoutDataWrapper>;
/// Borrows the layout data immutably. Fails on a conflicting borrow.
fn borrow_layout_data<'a>(&'a self) -> Ref<'a,Option<LayoutDataWrapper>>;
/// Borrows the layout data mutably. Fails on a conflicting borrow.
@ -184,6 +182,11 @@ pub trait LayoutDataAccess {
}
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)]
fn borrow_layout_data<'a>(&'a self) -> Ref<'a,Option<LayoutDataWrapper>> {
unsafe {

View file

@ -154,13 +154,10 @@ impl LayoutDataRef {
/// 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-
/// safe layout data accessor.
///
/// FIXME(pcwalton): Enforce this invariant via the type system. Will require traversal
/// functions to be trusted, but c'est la vie.
// #[inline]
// pub unsafe fn borrow_unchecked<'a>(&'a self) -> &'a () {
// self.data.borrow_unchecked()
// }
#[inline]
pub unsafe fn borrow_unchecked(&self) -> *Option<LayoutData> {
cast::transmute(&self.data_cell)
}
/// Borrows the layout data immutably. This function is *not* thread-safe.
#[inline]