From 9c3af574e59cc48b955c5b4204e0972a0b5e2158 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Mon, 27 Jan 2014 14:41:06 -0800 Subject: [PATCH] layout: Perform cascading in parallel. Speeds up the cascade by 3x. --- src/components/main/css/matching.rs | 31 ++++++++++++++++++++--- src/components/main/layout/context.rs | 4 +++ src/components/main/layout/layout_task.rs | 19 +++++++++----- src/components/main/layout/parallel.rs | 31 +++++++++++++++-------- src/components/main/layout/util.rs | 9 ++++--- src/components/script/dom/node.rs | 11 +++----- 6 files changed, 74 insertions(+), 31 deletions(-) diff --git a/src/components/main/css/matching.rs b/src/components/main/css/matching.rs index f81b1e86da4..d3e217519a6 100644 --- a/src/components/main/css/matching.rs +++ b/src/components/main/css/matching.rs @@ -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); fn cascade_subtree(&self, parent: Option); } @@ -51,18 +52,34 @@ impl<'ln> MatchMethods for LayoutNode<'ln> { } } - fn cascade_subtree(&self, parent: Option) { + unsafe fn cascade_node(&self, parent: Option) { 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) { + unsafe { + self.cascade_node(parent); + } for kid in self.children() { if kid.is_element() { diff --git a/src/components/main/layout/context.rs b/src/components/main/layout/context.rs index 0d5b3931a02..93613a54a19 100644 --- a/src/components/main/layout/context.rs +++ b/src/components/main/layout/context.rs @@ -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 { diff --git a/src/components/main/layout/layout_task.rs b/src/components/main/layout/layout_task.rs index 2bbb5d81e36..9315b225781 100644 --- a/src/components/main/layout/layout_task.rs +++ b/src/components/main/layout/layout_task.rs @@ -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); + }); + } } } diff --git a/src/components/main/layout/parallel.rs b/src/components/main/layout/parallel.rs index 7c5cd9917e6..fa93708053b 100644 --- a/src/components/main/layout/parallel.rs +++ b/src/components/main/layout/parallel.rs @@ -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), }); diff --git a/src/components/main/layout/util.rs b/src/components/main/layout/util.rs index 8ef031bfb3f..43853e67f80 100644 --- a/src/components/main/layout/util.rs +++ b/src/components/main/layout/util.rs @@ -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; /// Borrows the layout data immutably. Fails on a conflicting borrow. fn borrow_layout_data<'a>(&'a self) -> Ref<'a,Option>; /// 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 { + cast::transmute(self.get().layout_data.borrow_unchecked()) + } + #[inline(always)] fn borrow_layout_data<'a>(&'a self) -> Ref<'a,Option> { unsafe { diff --git a/src/components/script/dom/node.rs b/src/components/script/dom/node.rs index 0ec2f989408..a930d57eef1 100644 --- a/src/components/script/dom/node.rs +++ b/src/components/script/dom/node.rs @@ -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 { + cast::transmute(&self.data_cell) + } /// Borrows the layout data immutably. This function is *not* thread-safe. #[inline]