Optimize reflow by changing enums to traits and inlining more

This commit is contained in:
Patrick Walton 2013-10-24 16:57:42 -07:00
parent 81f5ba7d05
commit 42092921c1
44 changed files with 2725 additions and 2362 deletions

View file

@ -11,7 +11,9 @@ use layout::aux::LayoutAuxMethods;
use layout::box_builder::LayoutTreeBuilder;
use layout::context::LayoutContext;
use layout::display_list_builder::{DisplayListBuilder};
use layout::flow::FlowContext;
use layout::flow::{FlowContext, ImmutableFlowUtils, MutableFlowUtils, PreorderFlowTraversal};
use layout::flow::{PostorderFlowTraversal};
use layout::flow;
use layout::incremental::{RestyleDamage, BubbleWidths};
use std::cast::transmute;
@ -42,7 +44,7 @@ use script::layout_interface::{ReflowForDisplay, ReflowMsg};
use script::script_task::{ReflowCompleteMsg, ScriptChan, SendEventMsg};
use servo_msg::constellation_msg::{ConstellationChan, PipelineId};
use servo_net::image_cache_task::{ImageCacheTask, ImageResponseMsg};
use servo_net::local_image_cache::LocalImageCache;
use servo_net::local_image_cache::{ImageResponder, LocalImageCache};
use servo_util::tree::TreeNodeRef;
use servo_util::time::{ProfilerChan, profile};
use servo_util::time;
@ -67,6 +69,129 @@ struct LayoutTask {
profiler_chan: ProfilerChan,
}
/// The damage computation traversal.
#[deriving(Clone)]
struct ComputeDamageTraversal;
impl PostorderFlowTraversal for ComputeDamageTraversal {
#[inline]
fn process(&mut self, flow: &mut FlowContext) -> bool {
let mut damage = flow::base(flow).restyle_damage;
for child in flow::child_iter(flow) {
damage.union_in_place(flow::base(*child).restyle_damage)
}
flow::mut_base(flow).restyle_damage = damage;
true
}
}
/// Propagates restyle damage up and down the tree as appropriate.
///
/// FIXME(pcwalton): Merge this with flow tree building and/or other traversals.
struct PropagateDamageTraversal {
resized: bool,
}
impl PreorderFlowTraversal for PropagateDamageTraversal {
#[inline]
fn process(&mut self, flow: &mut FlowContext) -> bool {
// Also set any damage implied by resize.
if self.resized {
flow::mut_base(flow).restyle_damage.union_in_place(RestyleDamage::for_resize())
}
let prop = flow::base(flow).restyle_damage.propagate_down();
if prop.is_nonempty() {
for kid_ctx in flow::child_iter(flow) {
flow::mut_base(*kid_ctx).restyle_damage.union_in_place(prop)
}
}
true
}
}
/// The bubble-widths traversal, the first part of layout computation. This computes preferred
/// and intrinsic widths and bubbles them up the tree.
struct BubbleWidthsTraversal<'self>(&'self mut LayoutContext);
impl<'self> PostorderFlowTraversal for BubbleWidthsTraversal<'self> {
#[inline]
fn process(&mut self, flow: &mut FlowContext) -> bool {
flow.bubble_widths(**self);
true
}
#[inline]
fn should_prune(&mut self, flow: &mut FlowContext) -> bool {
flow::mut_base(flow).restyle_damage.lacks(BubbleWidths)
}
}
/// The assign-widths traversal. In Gecko this corresponds to `Reflow`.
struct AssignWidthsTraversal<'self>(&'self mut LayoutContext);
impl<'self> PreorderFlowTraversal for AssignWidthsTraversal<'self> {
#[inline]
fn process(&mut self, flow: &mut FlowContext) -> bool {
flow.assign_widths(**self);
true
}
}
/// The assign-heights traversal, the last (and most expensive) part of layout computation.
/// Determines the final heights for all layout objects. In Gecko this corresponds to
/// `FinishAndStoreOverflow`.
struct AssignHeightsTraversal<'self>(&'self mut LayoutContext);
impl<'self> PostorderFlowTraversal for AssignHeightsTraversal<'self> {
#[inline]
fn process(&mut self, flow: &mut FlowContext) -> bool {
flow.assign_height(**self);
true
}
#[inline]
fn should_process(&mut self, flow: &mut FlowContext) -> bool {
!flow::base(flow).is_inorder
}
}
/// The display list building traversal. In WebKit this corresponds to `paint`. In Gecko this
/// corresponds to `BuildDisplayListForChild`.
struct DisplayListBuildingTraversal<'self> {
builder: DisplayListBuilder<'self>,
root_pos: Rect<Au>,
display_list: ~Cell<DisplayList<AbstractNode<()>>>,
}
impl<'self> PreorderFlowTraversal for DisplayListBuildingTraversal<'self> {
#[inline]
fn process(&mut self, _: &mut FlowContext) -> bool {
true
}
#[inline]
fn should_prune(&mut self, flow: &mut FlowContext) -> bool {
flow.build_display_list(&self.builder, &self.root_pos, self.display_list)
}
}
struct LayoutImageResponder {
id: PipelineId,
script_chan: ScriptChan,
}
impl ImageResponder for LayoutImageResponder {
fn respond(&self) -> ~fn(ImageResponseMsg) {
let id = self.id.clone();
let script_chan = self.script_chan.clone();
let f: ~fn(ImageResponseMsg) = |_| {
script_chan.send(SendEventMsg(id.clone(), ReflowEvent))
};
f
}
}
impl LayoutTask {
pub fn create(id: PipelineId,
port: Port<Msg>,
@ -180,14 +305,13 @@ impl LayoutTask {
// FIXME: Bad copy!
let doc_url = data.url.clone();
let script_chan = data.script_chan.clone();
debug!("layout: received layout request for: %s", doc_url.to_str());
debug!("layout: damage is %?", data.damage);
debug!("layout: parsed Node tree");
debug!("%?", node.dump());
// Reset the image cache.
self.local_image_cache.next_round(self.make_on_image_available_cb(script_chan));
self.local_image_cache.next_round(self.make_on_image_available_cb());
self.doc_url = Some(doc_url);
let screen_size = Size2D(Au::from_px(data.window_size.width as int),
@ -218,10 +342,10 @@ impl LayoutTask {
}
// Construct the flow tree.
let mut layout_root: FlowContext = do profile(time::LayoutTreeBuilderCategory,
self.profiler_chan.clone()) {
let mut layout_root: ~FlowContext: = do profile(time::LayoutTreeBuilderCategory,
self.profiler_chan.clone()) {
let mut builder = LayoutTreeBuilder::new();
let layout_root: FlowContext = match builder.construct_trees(&layout_ctx, *node) {
let layout_root: ~FlowContext: = match builder.construct_trees(&layout_ctx, *node) {
Ok(root) => root,
Err(*) => fail!(~"Root flow should always exist")
};
@ -229,41 +353,11 @@ impl LayoutTask {
layout_root
};
// Propagate restyle damage up and down the tree, as appropriate.
// FIXME: Merge this with flow tree building and/or the other traversals.
do layout_root.each_preorder |flow| {
// Also set any damage implied by resize.
if resized {
do flow.with_mut_base |base| {
base.restyle_damage.union_in_place(RestyleDamage::for_resize());
}
}
let prop = flow.with_base(|base| base.restyle_damage.propagate_down());
if prop.is_nonempty() {
for kid_ctx in flow.child_iter() {
do kid_ctx.with_mut_base |kid| {
kid.restyle_damage.union_in_place(prop);
}
}
}
true
};
do layout_root.each_postorder |flow| {
let mut damage = do flow.with_base |base| {
base.restyle_damage
};
for child in flow.child_iter() {
do child.with_base |child_base| {
damage.union_in_place(child_base.restyle_damage);
}
}
do flow.with_mut_base |base| {
base.restyle_damage = damage;
}
true
};
// Propagate damage.
layout_root.traverse_preorder(&mut PropagateDamageTraversal {
resized: resized,
});
layout_root.traverse_postorder(&mut ComputeDamageTraversal.clone());
debug!("layout: constructed Flow tree");
debug!("%?", layout_root.dump());
@ -271,51 +365,37 @@ impl LayoutTask {
// Perform the primary layout passes over the flow tree to compute the locations of all
// the boxes.
do profile(time::LayoutMainCategory, self.profiler_chan.clone()) {
do layout_root.each_postorder_prune(|f| f.restyle_damage().lacks(BubbleWidths)) |flow| {
flow.bubble_widths(&mut layout_ctx);
true
};
let _ = layout_root.traverse_postorder(&mut BubbleWidthsTraversal(&mut layout_ctx));
// FIXME: We want to do
// FIXME(kmc): We want to do
// for flow in layout_root.traverse_preorder_prune(|f| f.restyle_damage().lacks(Reflow))
// but FloatContext values can't be reused, so we need to recompute them every time.
// NOTE: this currently computes borders, so any pruning should separate that operation out.
debug!("assigning widths");
do layout_root.each_preorder |flow| {
flow.assign_widths(&mut layout_ctx);
true
};
let _ = layout_root.traverse_preorder(&mut AssignWidthsTraversal(&mut layout_ctx));
// For now, this is an inorder traversal
// FIXME: prune this traversal as well
debug!("assigning height");
do layout_root.each_bu_sub_inorder |flow| {
flow.assign_height(&mut layout_ctx);
true
};
let _ = layout_root.traverse_postorder(&mut AssignHeightsTraversal(&mut layout_ctx));
}
// Build the display list if necessary, and send it to the renderer.
if data.goal == ReflowForDisplay {
do profile(time::LayoutDispListBuildCategory, self.profiler_chan.clone()) {
let builder = DisplayListBuilder {
ctx: &layout_ctx,
};
let display_list = ~Cell::new(DisplayList::<AbstractNode<()>>::new());
// TODO: Set options on the builder before building.
// TODO: Be smarter about what needs painting.
let root_pos = &layout_root.position().clone();
layout_root.each_preorder_prune(|flow| {
flow.build_display_list(&builder, root_pos, display_list)
}, |_| { true } );
let root_size = do layout_root.with_base |base| {
base.position.size
let mut traversal = DisplayListBuildingTraversal {
builder: DisplayListBuilder {
ctx: &layout_ctx,
},
root_pos: flow::base(layout_root).position.clone(),
display_list: ~Cell::new(DisplayList::<AbstractNode<()>>::new()),
};
let display_list = Arc::new(display_list.take());
let _ = layout_root.traverse_preorder(&mut traversal);
let root_size = flow::base(layout_root).position.size;
let display_list = Arc::new(traversal.display_list.take());
for i in range(0,display_list.get().list.len()) {
let node: AbstractNode<LayoutView> = unsafe {
@ -445,7 +525,7 @@ impl LayoutTask {
Some(ref list) => {
let display_list = list.get();
let (x, y) = (Au::from_frac_px(point.x as f64),
Au::from_frac_px(point.y as f64));
Au::from_frac_px(point.y as f64));
let mut resp = Err(());
// iterate in reverse to ensure we have the most recently painted render box
for display_item in display_list.list.rev_iter() {
@ -481,21 +561,15 @@ impl LayoutTask {
// to the script task, and ultimately cause the image to be
// re-requested. We probably don't need to go all the way back to
// the script task for this.
fn make_on_image_available_cb(&self, script_chan: ScriptChan)
-> ~fn() -> ~fn(ImageResponseMsg) {
fn make_on_image_available_cb(&self) -> @ImageResponder {
// This has a crazy signature because the image cache needs to
// make multiple copies of the callback, and the dom event
// channel is not a copyable type, so this is actually a
// little factory to produce callbacks
let id = self.id.clone();
let f: ~fn() -> ~fn(ImageResponseMsg) = || {
let script_chan = script_chan.clone();
let f: ~fn(ImageResponseMsg) = |_| {
script_chan.send(SendEventMsg(id.clone(), ReflowEvent))
};
f
};
return f;
@LayoutImageResponder {
id: self.id.clone(),
script_chan: self.script_chan.clone(),
} as @ImageResponder
}
}