layout: Stop adding flows to the leaf set that are obviously never going

to be leaves.

60% improvement in flow tree construction time on the rainbow page.
This commit is contained in:
Patrick Walton 2014-01-29 13:28:02 -08:00
parent 6c63de1c03
commit e579daefc2
3 changed files with 100 additions and 52 deletions

View file

@ -26,7 +26,7 @@ use layout::box_::{Box, GenericBox, IframeBox, IframeBoxInfo, ImageBox, ImageBox
use layout::box_::{UnscannedTextBox, UnscannedTextBoxInfo, InlineInfo, InlineParentInfo}; use layout::box_::{UnscannedTextBox, UnscannedTextBoxInfo, InlineInfo, InlineParentInfo};
use layout::context::LayoutContext; use layout::context::LayoutContext;
use layout::float_context::FloatType; use layout::float_context::FloatType;
use layout::flow::{BaseFlow, Flow, LeafSet, MutableOwnedFlowUtils}; use layout::flow::{BaseFlow, Flow, ImmutableFlowUtils, LeafSet, MutableOwnedFlowUtils};
use layout::inline::InlineFlow; use layout::inline::InlineFlow;
use layout::text::TextRunScanner; use layout::text::TextRunScanner;
use layout::util::{LayoutDataAccess, OpaqueNode}; use layout::util::{LayoutDataAccess, OpaqueNode};
@ -274,13 +274,10 @@ impl<'fc> FlowConstructor<'fc> {
let inline_base = BaseFlow::new(self.next_flow_id(), node); let inline_base = BaseFlow::new(self.next_flow_id(), node);
let mut inline_flow = ~InlineFlow::from_boxes(inline_base, boxes) as ~Flow; let mut inline_flow = ~InlineFlow::from_boxes(inline_base, boxes) as ~Flow;
self.layout_context.leaf_set.access(|leaf_set| leaf_set.insert(&inline_flow)); self.layout_context.leaf_set.access(|leaf_set| inline_flow.mark_as_leaf(leaf_set));
TextRunScanner::new().scan_for_runs(self.font_context, inline_flow); TextRunScanner::new().scan_for_runs(self.font_context, inline_flow);
let mut inline_flow = Some(inline_flow); flow.add_new_child(inline_flow)
self.layout_context.leaf_set.access(|leaf_set| {
flow.add_new_child(inline_flow.take_unwrap(), leaf_set)
})
} }
/// Creates an inline flow from a set of inline boxes, if present, and adds it as a child of /// Creates an inline flow from a set of inline boxes, if present, and adds it as a child of
@ -323,10 +320,7 @@ impl<'fc> FlowConstructor<'fc> {
self.flush_inline_boxes_to_flow_if_necessary(&mut opt_boxes_for_inline_flow, self.flush_inline_boxes_to_flow_if_necessary(&mut opt_boxes_for_inline_flow,
flow, flow,
node); node);
let mut kid_flow = Some(kid_flow); flow.add_new_child(kid_flow)
self.layout_context.leaf_set.access(|leaf_set| {
flow.add_new_child(kid_flow.take_unwrap(), leaf_set)
})
} }
ConstructionItemConstructionResult(InlineBoxesConstructionItem( ConstructionItemConstructionResult(InlineBoxesConstructionItem(
InlineBoxesConstructionResult { InlineBoxesConstructionResult {
@ -366,10 +360,7 @@ impl<'fc> FlowConstructor<'fc> {
// Push the flow generated by the {ib} split onto our list of // Push the flow generated by the {ib} split onto our list of
// flows. // flows.
let mut kid_flow = Some(kid_flow); flow.add_new_child(kid_flow)
self.layout_context.leaf_set.access(|leaf_set| {
flow.add_new_child(kid_flow.take_unwrap(), leaf_set)
})
} }
} }
} }
@ -386,6 +377,13 @@ impl<'fc> FlowConstructor<'fc> {
self.flush_inline_boxes_to_flow_if_necessary(&mut opt_boxes_for_inline_flow, self.flush_inline_boxes_to_flow_if_necessary(&mut opt_boxes_for_inline_flow,
flow, flow,
node); node);
// The flow is done. If it ended up with no kids, add the flow to the leaf set.
if flow.child_count() == 0 {
self.layout_context.leaf_set.access(|leaf_set| flow.mark_as_leaf(leaf_set))
} else {
flow.mark_as_nonleaf()
}
} }
/// Builds a flow for a node with `display: block`. This yields a `BlockFlow` with possibly /// Builds a flow for a node with `display: block`. This yields a `BlockFlow` with possibly
@ -395,9 +393,6 @@ impl<'fc> FlowConstructor<'fc> {
let base = BaseFlow::new(self.next_flow_id(), node); let base = BaseFlow::new(self.next_flow_id(), node);
let box_ = self.build_box_for_node(node); let box_ = self.build_box_for_node(node);
let mut flow = ~BlockFlow::from_box(base, box_, is_fixed) as ~Flow; let mut flow = ~BlockFlow::from_box(base, box_, is_fixed) as ~Flow;
self.layout_context.leaf_set.access(|leaf_set| leaf_set.insert(&flow));
self.build_children_of_block_flow(&mut flow, node); self.build_children_of_block_flow(&mut flow, node);
flow flow
} }
@ -410,9 +405,6 @@ impl<'fc> FlowConstructor<'fc> {
let box_ = self.build_box_for_node(node); let box_ = self.build_box_for_node(node);
let mut flow = ~BlockFlow::float_from_box(base, float_type, box_) as ~Flow; let mut flow = ~BlockFlow::float_from_box(base, float_type, box_) as ~Flow;
self.layout_context.leaf_set.access(|leaf_set| leaf_set.insert(&flow));
self.build_children_of_block_flow(&mut flow, node); self.build_children_of_block_flow(&mut flow, node);
flow flow
} }

View file

@ -197,12 +197,6 @@ pub trait MutableFlowUtils {
/// Invokes a closure with the last child of this flow. /// Invokes a closure with the last child of this flow.
fn with_last_child<R>(self, f: |Option<&mut ~Flow>| -> R) -> R; fn with_last_child<R>(self, f: |Option<&mut ~Flow>| -> R) -> R;
/// Removes the first child of this flow and destroys it.
fn remove_first(self);
/// Removes the last child of this flow and destroys it.
fn remove_last(self);
/// Computes the overflow region for this flow. /// Computes the overflow region for this flow.
fn store_overflow(self, _: &mut LayoutContext); fn store_overflow(self, _: &mut LayoutContext);
@ -218,7 +212,14 @@ pub trait MutableFlowUtils {
pub trait MutableOwnedFlowUtils { pub trait MutableOwnedFlowUtils {
/// Adds a new flow as a child of this flow. Removes the flow from the given leaf set if /// Adds a new flow as a child of this flow. Removes the flow from the given leaf set if
/// it's present. /// it's present.
fn add_new_child(&mut self, new_child: ~Flow, leaf_set: &mut LeafSet); fn add_new_child(&mut self, new_child: ~Flow);
/// Marks the flow as a leaf. The flow must not have children and must not be marked as a
/// nonleaf.
fn mark_as_leaf(&mut self, leaf_set: &mut LeafSet);
/// Marks the flow as a nonleaf. The flow must not be marked as a leaf.
fn mark_as_nonleaf(&mut self);
/// Destroys the flow. /// Destroys the flow.
fn destroy(&mut self, leaf_set: &mut LeafSet); fn destroy(&mut self, leaf_set: &mut LeafSet);
@ -263,7 +264,7 @@ pub trait PostorderFlowTraversal {
} }
#[deriving(Clone)] #[deriving(Clone)]
pub struct FlowFlagsInfo{ pub struct FlowFlagsInfo {
flags: FlowFlags, flags: FlowFlags,
/// text-decoration colors /// text-decoration colors
@ -284,12 +285,12 @@ pub struct FlowFlags(u8);
/// The bitmask of flags that represent text decoration fields that get propagated downward. /// The bitmask of flags that represent text decoration fields that get propagated downward.
/// ///
/// NB: If you update this field, you must update the bitfields below. /// NB: If you update this field, you must update the bitfields below.
static TEXT_DECORATION_OVERRIDE_BITMASK: u8 = 0b00001110; static TEXT_DECORATION_OVERRIDE_BITMASK: u8 = 0b0000_1110;
/// The bitmask of flags that represent the text alignment field. /// The bitmask of flags that represent the text alignment field.
/// ///
/// NB: If you update this field, you must update the bitfields below. /// NB: If you update this field, you must update the bitfields below.
static TEXT_ALIGN_BITMASK: u8 = 0b00110000; static TEXT_ALIGN_BITMASK: u8 = 0b0011_0000;
/// The number of bits we must shift off to handle the text alignment field. /// The number of bits we must shift off to handle the text alignment field.
/// ///
@ -428,22 +429,29 @@ impl FlowFlagsInfo {
} }
// Whether we need an in-order traversal. // Whether we need an in-order traversal.
bitfield!(FlowFlags, inorder, set_inorder, 0x01) bitfield!(FlowFlags, inorder, set_inorder, 0b0000_0001)
// Whether this flow forces `text-decoration: underline` on. // Whether this flow forces `text-decoration: underline` on.
// //
// NB: If you update this, you need to update TEXT_DECORATION_OVERRIDE_BITMASK. // NB: If you update this, you need to update TEXT_DECORATION_OVERRIDE_BITMASK.
bitfield!(FlowFlags, override_underline, set_override_underline, 0x02) bitfield!(FlowFlags, override_underline, set_override_underline, 0b0000_0010)
// Whether this flow forces `text-decoration: overline` on. // Whether this flow forces `text-decoration: overline` on.
// //
// NB: If you update this, you need to update TEXT_DECORATION_OVERRIDE_BITMASK. // NB: If you update this, you need to update TEXT_DECORATION_OVERRIDE_BITMASK.
bitfield!(FlowFlags, override_overline, set_override_overline, 0x04) bitfield!(FlowFlags, override_overline, set_override_overline, 0b0000_0100)
// Whether this flow forces `text-decoration: line-through` on. // Whether this flow forces `text-decoration: line-through` on.
// //
// NB: If you update this, you need to update TEXT_DECORATION_OVERRIDE_BITMASK. // NB: If you update this, you need to update TEXT_DECORATION_OVERRIDE_BITMASK.
bitfield!(FlowFlags, override_line_through, set_override_line_through, 0x08) bitfield!(FlowFlags, override_line_through, set_override_line_through, 0b0000_1000)
// Whether this flow is marked as a leaf. Flows marked as leaves must not have any more kids added
// to them.
bitfield!(FlowFlags, is_leaf, set_is_leaf, 0b0100_0000)
// Whether this flow is marked as a nonleaf. Flows marked as nonleaves must have children.
bitfield!(FlowFlags, is_nonleaf, set_is_nonleaf, 0b1000_0000)
// The text alignment for this flow. // The text alignment for this flow.
impl FlowFlags { impl FlowFlags {
@ -678,16 +686,6 @@ impl<'a> MutableFlowUtils for &'a mut Flow {
f(mut_base(self).children.back_mut()) f(mut_base(self).children.back_mut())
} }
/// Removes the first child of this flow and destroys it.
fn remove_first(self) {
let _ = mut_base(self).children.pop_front();
}
/// Removes the last child of this flow and destroys it.
fn remove_last(self) {
let _ = mut_base(self).children.pop_back();
}
fn store_overflow(self, _: &mut LayoutContext) { fn store_overflow(self, _: &mut LayoutContext) {
let my_position = mut_base(self).position; let my_position = mut_base(self).position;
let mut overflow = my_position; let mut overflow = my_position;
@ -743,23 +741,45 @@ impl<'a> MutableFlowUtils for &'a mut Flow {
} }
impl MutableOwnedFlowUtils for ~Flow { impl MutableOwnedFlowUtils for ~Flow {
/// Adds a new flow as a child of this flow. Removes the flow from the given leaf set if /// Adds a new flow as a child of this flow. Fails if this flow is marked as a leaf.
/// it's present. fn add_new_child(&mut self, mut new_child: ~Flow) {
fn add_new_child(&mut self, mut new_child: ~Flow, leaf_set: &mut LeafSet) {
if self.child_count() == 0 {
leaf_set.remove(self)
}
{ {
let kid_base = mut_base(new_child); let kid_base = mut_base(new_child);
kid_base.parallel.parent = parallel::mut_owned_flow_to_unsafe_flow(self); kid_base.parallel.parent = parallel::mut_owned_flow_to_unsafe_flow(self);
} }
let base = mut_base(*self); let base = mut_base(*self);
assert!(!base.flags_info.flags.is_leaf());
base.children.push_back(new_child); base.children.push_back(new_child);
let _ = base.parallel.children_count.fetch_add(1, Relaxed); let _ = base.parallel.children_count.fetch_add(1, Relaxed);
} }
/// Marks the flow as a leaf. The flow must not have children and must not be marked as a
/// nonleaf.
fn mark_as_leaf(&mut self, leaf_set: &mut LeafSet) {
{
let base = mut_base(*self);
if base.flags_info.flags.is_nonleaf() {
fail!("attempted to mark a nonleaf flow as a leaf!")
}
if base.children.len() != 0 {
fail!("attempted to mark a flow with children as a leaf!")
}
base.flags_info.flags.set_is_leaf(true)
}
leaf_set.insert(self)
}
/// Marks the flow as a nonleaf. The flow must not be marked as a leaf.
fn mark_as_nonleaf(&mut self) {
let base = mut_base(*self);
if base.flags_info.flags.is_leaf() {
fail!("attempted to mark a leaf flow as a nonleaf!")
}
base.flags_info.flags.set_is_nonleaf(true)
// We don't check to make sure there are no children as they might be added later.
}
/// Destroys the flow. /// Destroys the flow.
fn destroy(&mut self, leaf_set: &mut LeafSet) { fn destroy(&mut self, leaf_set: &mut LeafSet) {
let is_leaf = { let is_leaf = {
@ -796,7 +816,7 @@ impl LeafSet {
} }
/// Inserts a newly-created flow into the leaf set. /// Inserts a newly-created flow into the leaf set.
pub fn insert(&mut self, flow: &~Flow) { fn insert(&mut self, flow: &~Flow) {
self.set.insert(parallel::owned_flow_to_unsafe_flow(flow)); self.set.insert(parallel::owned_flow_to_unsafe_flow(flow));
} }

View file

@ -143,6 +143,24 @@ impl PreorderFlowTraversal for PropagateDamageTraversal {
} }
} }
/// The flow tree verification traversal. This is only on in debug builds.
#[cfg(debug)]
struct FlowTreeVerificationTraversal;
#[cfg(debug)]
impl PreorderFlowTraversal for FlowTreeVerificationTraversal {
#[inline]
fn process(&mut self, flow: &mut Flow) -> bool {
let base = flow::base(flow);
if !base.flags_info.flags.is_leaf() && !base.flags_info.flags.is_nonleaf() {
println("flow tree verification failed: flow wasn't a leaf or a nonleaf!");
flow.dump();
fail!("flow tree verification failed")
}
true
}
}
/// The bubble-widths traversal, the first part of layout computation. This computes preferred /// The bubble-widths traversal, the first part of layout computation. This computes preferred
/// and intrinsic widths and bubbles them up the tree. /// and intrinsic widths and bubbles them up the tree.
pub struct BubbleWidthsTraversal<'a> { pub struct BubbleWidthsTraversal<'a> {
@ -472,6 +490,19 @@ impl LayoutTask {
} }
} }
/// Verifies that every node was either marked as a leaf or as a nonleaf in the flow tree.
/// This is only on in debug builds.
#[inline(never)]
#[cfg(debug)]
fn verify_flow_tree(&mut self, layout_root: &mut ~Flow) {
let mut traversal = FlowTreeVerificationTraversal;
layout_root.traverse_preorder(&mut traversal);
}
#[cfg(not(debug))]
fn verify_flow_tree(&mut self, _: &mut ~Flow) {
}
/// The high-level routine that performs layout tasks. /// The high-level routine that performs layout tasks.
fn handle_reflow(&mut self, data: &Reflow) { fn handle_reflow(&mut self, data: &Reflow) {
// FIXME: Isolate this transmutation into a "bridge" module. // FIXME: Isolate this transmutation into a "bridge" module.
@ -544,6 +575,11 @@ impl LayoutTask {
self.profiler_chan.clone(), self.profiler_chan.clone(),
|| self.construct_flow_tree(&mut layout_ctx, *node)); || self.construct_flow_tree(&mut layout_ctx, *node));
// Verification of the flow tree, which ensures that all nodes were either marked as leaves
// or as non-leaves. This becomes a no-op in release builds. (It is inconsequential to
// memory safety but is a useful debugging tool.)
self.verify_flow_tree(&mut layout_root);
// Propagate damage. // Propagate damage.
layout_root.traverse_preorder(&mut PropagateDamageTraversal { layout_root.traverse_preorder(&mut PropagateDamageTraversal {
all_style_damage: all_style_damage all_style_damage: all_style_damage