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_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() {
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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,13 +523,19 @@ 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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
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::{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,27 +116,35 @@ 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());
|
||||||
|
|
||||||
// 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,16 +169,16 @@ 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 {
|
||||||
queue.data = cast::transmute(layout_context)
|
queue.data = cast::transmute(layout_context)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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]
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue