mirror of
https://github.com/servo/servo.git
synced 2025-06-22 08:08:59 +01:00
Optimize reflow by changing enums to traits and inlining more
This commit is contained in:
parent
81f5ba7d05
commit
42092921c1
44 changed files with 2725 additions and 2362 deletions
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue