mirror of
https://github.com/servo/servo.git
synced 2025-08-06 14:10:11 +01:00
layout: Perform cascading in parallel.
Speeds up the cascade by 3x.
This commit is contained in:
parent
fa02b82b88
commit
9c3af574e5
6 changed files with 74 additions and 31 deletions
|
@ -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() {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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,13 +523,19 @@ 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)
|
||||
}
|
||||
}
|
||||
});
|
||||
profile(time::LayoutSelectorCascadeCategory, self.profiler_chan.clone(), || {
|
||||
node.cascade_subtree(None);
|
||||
});
|
||||
|
||||
// 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);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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,27 +116,35 @@ impl<'a> ParallelPostorderFlowTraversal for BubbleWidthsTraversal<'a> {}
|
|||
|
||||
impl<'a> ParallelPostorderFlowTraversal for AssignHeightsAndStoreOverflowTraversal<'a> {}
|
||||
|
||||
fn match_node(unsafe_layout_node: UnsafeLayoutNode,
|
||||
proxy: &mut WorkerProxy<*mut LayoutContext,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());
|
||||
|
||||
// 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,16 +169,16 @@ fn assign_heights_and_store_overflow(unsafe_flow: UnsafeFlow,
|
|||
assign_heights_traversal.run_parallel(unsafe_flow)
|
||||
}
|
||||
|
||||
pub fn match_subtree(root_node: &LayoutNode,
|
||||
layout_context: &mut LayoutContext,
|
||||
queue: &mut WorkQueue<*mut LayoutContext,UnsafeLayoutNode>) {
|
||||
pub fn match_and_cascade_subtree(root_node: &LayoutNode,
|
||||
layout_context: &mut LayoutContext,
|
||||
queue: &mut WorkQueue<*mut LayoutContext,UnsafeLayoutNode>) {
|
||||
unsafe {
|
||||
queue.data = cast::transmute(layout_context)
|
||||
}
|
||||
|
||||
// Enqueue the root node.
|
||||
queue.push(WorkUnit {
|
||||
fun: match_node,
|
||||
fun: match_and_cascade_node,
|
||||
data: layout_node_to_unsafe_layout_node(root_node),
|
||||
});
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue