servo: Merge #1493 - layout: Implement parallel reflow for the bubble-widths and assign-heights phases (from pcwalton:parallel-layout-new); r=pcwalton,metajack

r? @metajack

Source-Repo: https://github.com/servo/servo
Source-Revision: 11187c0eea517e85b9ab87f0ebef02c1a59cc3e7
This commit is contained in:
Patrick Walton 2014-01-22 16:04:08 -08:00
Родитель 9f4f6d69bc
Коммит a056ba0f8f
21 изменённых файлов: 706 добавлений и 141 удалений

Просмотреть файл

@ -15,17 +15,17 @@
/// low-level drawing primitives.
use color::Color;
use servo_util::geometry::Au;
use style::computed_values::border_style;
use render_context::RenderContext;
use text::TextRun;
use std::cast::transmute_region;
use std::vec::VecIterator;
use extra::arc::Arc;
use geom::{Point2D, Rect, Size2D, SideOffsets2D};
use servo_net::image::base::Image;
use servo_util::geometry::Au;
use servo_util::range::Range;
use extra::arc::Arc;
use std::cast::transmute_region;
use std::vec::VecIterator;
use style::computed_values::border_style;
/// A list of rendering operations to be performed.
pub struct DisplayList<E> {

Просмотреть файл

@ -15,6 +15,7 @@ use std::cell::RefCell;
use servo_util::cache::{Cache, HashCache};
use servo_util::range::Range;
use style::computed_values::{text_decoration, font_weight, font_style};
use color::Color;
use font_context::FontContext;
use servo_util::geometry::Au;
@ -228,7 +229,6 @@ impl<'a> Font {
};
let metrics = handle.get_metrics();
// TODO(Issue #179): convert between specified and used font style here?
return Ok(Rc::from_mut(RefCell::new(Font {
handle: handle,

Просмотреть файл

@ -2,22 +2,32 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use font::{Font, FontDescriptor, FontGroup, FontHandleMethods,
SelectorPlatformIdentifier};
use font::{Font, FontDescriptor, FontGroup, FontHandleMethods, SelectorPlatformIdentifier};
use font::{SpecifiedFontStyle, UsedFontStyle};
use font_list::FontList;
use servo_util::cache::{Cache, LRUCache};
use servo_util::time::ProfilerChan;
use platform::font::FontHandle;
use platform::font_context::FontContextHandle;
use azure::azure_hl::BackendType;
use servo_util::cache::{Cache, LRUCache};
use servo_util::time::ProfilerChan;
use std::hashmap::HashMap;
use std::rc::Rc;
use std::cell::RefCell;
/// Information needed to create a font context.
#[deriving(Clone)]
pub struct FontContextInfo {
/// The painting backend we're using.
backend: BackendType,
/// Whether we need a font list.
needs_font_list: bool,
/// A channel up to the profiler.
profiler_chan: ProfilerChan,
}
pub trait FontContextHandleMethods {
fn create_font_from_identifier(&self, ~str, UsedFontStyle) -> Result<FontHandle, ()>;
@ -34,14 +44,13 @@ pub struct FontContext {
}
impl FontContext {
pub fn new(backend: BackendType,
needs_font_list: bool,
profiler_chan: ProfilerChan)
-> FontContext {
pub fn new(info: FontContextInfo) -> FontContext {
let handle = FontContextHandle::new();
let font_list = if needs_font_list {
Some(FontList::new(&handle, profiler_chan.clone())) }
else { None };
let font_list = if info.needs_font_list {
Some(FontList::new(&handle, info.profiler_chan.clone()))
} else {
None
};
// TODO: Allow users to specify these.
let mut generic_fonts = HashMap::with_capacity(5);
@ -56,9 +65,9 @@ impl FontContext {
font_list: font_list,
group_cache: LRUCache::new(10),
handle: handle,
backend: backend,
backend: info.backend,
generic_fonts: generic_fonts,
profiler_chan: profiler_chan,
profiler_chan: info.profiler_chan.clone(),
}
}

Просмотреть файл

@ -7,11 +7,10 @@ use gfx_font::FontHandleMethods;
use platform::font::FontHandle;
use platform::font_context::FontContextHandle;
use platform::font_list::FontListHandle;
use servo_util::time;
use servo_util::time::profile;
use servo_util::time::ProfilerChan;
use style::computed_values::{font_weight, font_style};
use servo_util::time::{ProfilerChan, profile};
use servo_util::time;
use std::hashmap::HashMap;
pub type FontFamilyMap = HashMap<~str, FontFamily>;
@ -105,7 +104,7 @@ impl FontFamily {
}
pub fn find_font_for_style<'a>(&'a mut self, list: &FontListHandle, style: &SpecifiedFontStyle)
-> Option<&'a FontEntry> {
-> Option<&'a FontEntry> {
self.load_family_variations(list);
// TODO(Issue #189): optimize lookup for

Просмотреть файл

@ -8,6 +8,8 @@
use azure::azure_hl::{BackendType, CairoBackend, CoreGraphicsBackend};
use azure::azure_hl::{CoreGraphicsAcceleratedBackend, Direct2DBackend, SkiaBackend};
use extra::getopts::groups;
use std::num;
use std::rt;
/// Global flags for Servo, currently set on the command line.
#[deriving(Clone)]
@ -34,6 +36,10 @@ pub struct Opts {
/// it to produce output on that interval (`-p`).
profiler_period: Option<f64>,
/// The number of threads to use for layout (`-y`). Defaults to 1, which results in a recursive
/// sequential algorithm.
layout_threads: uint,
/// True to exit after the page load (`-x`).
exit_after_load: bool,
@ -59,6 +65,7 @@ pub fn from_cmdline_args(args: &[~str]) -> Opts {
groups::optopt("t", "threads", "Number of render threads", "1"),
groups::optflagopt("p", "profile", "Profiler flag and output interval", "10"),
groups::optflag("x", "exit", "Exit after load flag"),
groups::optopt("y", "layout-threads", "Number of threads to use for layout", "1"),
groups::optflag("z", "headless", "Headless mode"),
groups::optflag("f", "hard-fail", "Exit on task failure instead of displaying about:failure"),
groups::optflag("h", "help", "Print this message")
@ -119,6 +126,11 @@ pub fn from_cmdline_args(args: &[~str]) -> Opts {
let cpu_painting = opt_match.opt_present("c");
let layout_threads: uint = match opt_match.opt_str("y") {
Some(layout_threads_str) => from_str(layout_threads_str).unwrap(),
None => num::max(rt::default_sched_threads() * 3 / 4, 1),
};
Opts {
urls: urls,
render_backend: render_backend,
@ -126,6 +138,7 @@ pub fn from_cmdline_args(args: &[~str]) -> Opts {
cpu_painting: cpu_painting,
tile_size: tile_size,
profiler_period: profiler_period,
layout_threads: layout_threads,
exit_after_load: opt_match.opt_present("x"),
output_file: opt_match.opt_str("o"),
headless: opt_match.opt_present("z"),

Просмотреть файл

@ -9,9 +9,8 @@ use platform::macos::font_context::FontContextHandle;
use core_foundation::base::TCFType;
use core_foundation::string::{CFString, CFStringRef};
use core_text;
use core_text::font_descriptor::{CTFontDescriptor, CTFontDescriptorRef};
use core_text;
use std::cast;
use std::hashmap::HashMap;
@ -44,7 +43,8 @@ impl FontListHandle {
pub fn load_variations_for_family(&self, family: &mut FontFamily) {
debug!("Looking for faces of family: {:s}", family.family_name);
let family_collection = core_text::font_collection::create_for_family(family.family_name);
let family_collection =
core_text::font_collection::create_for_family(family.family_name);
let family_descriptors = family_collection.get_descriptors();
for descref in family_descriptors.iter() {
let descref: CTFontDescriptorRef = unsafe { cast::transmute(descref) };

Просмотреть файл

@ -25,7 +25,7 @@ use extra::arc::Arc;
use buffer_map::BufferMap;
use display_list::DisplayList;
use font_context::FontContext;
use font_context::{FontContext, FontContextInfo};
use opts::Opts;
use render_context::RenderContext;
@ -159,9 +159,11 @@ impl<C: RenderListener + Send,T:Send+Freeze> RenderTask<C,T> {
port: port,
compositor: compositor,
constellation_chan: constellation_chan,
font_ctx: ~FontContext::new(opts.render_backend.clone(),
false,
profiler_chan.clone()),
font_ctx: ~FontContext::new(FontContextInfo {
backend: opts.render_backend.clone(),
needs_font_list: false,
profiler_chan: profiler_chan.clone(),
}),
opts: opts,
profiler_chan: profiler_chan,

Просмотреть файл

@ -5,45 +5,45 @@
extern mod harfbuzz;
use font::{Font, FontHandleMethods, FontTableMethods, FontTableTag};
use servo_util::geometry::Au;
use platform::font::FontTable;
use text::glyph::{GlyphStore, GlyphIndex, GlyphData};
use text::shaping::ShaperMethods;
use servo_util::range::Range;
use text::util::{float_to_fixed, fixed_to_float};
use std::cast::transmute;
use std::char;
use std::libc::{c_uint, c_int, c_void, c_char};
use std::ptr;
use std::ptr::null;
use std::num;
use std::vec;
use geom::Point2D;
use harfbuzz::{HB_MEMORY_MODE_READONLY, HB_DIRECTION_LTR};
use harfbuzz::{hb_blob_create, hb_face_create_for_tables};
use harfbuzz::{hb_blob_t};
use harfbuzz::{hb_bool_t};
use harfbuzz::{hb_buffer_add_utf8};
use harfbuzz::{hb_buffer_destroy};
use harfbuzz::{hb_buffer_get_glyph_positions};
use harfbuzz::{hb_buffer_set_direction};
use harfbuzz::{hb_buffer_destroy};
use harfbuzz::{hb_face_destroy};
use harfbuzz::{hb_face_t, hb_font_t};
use harfbuzz::{hb_font_create};
use harfbuzz::{hb_font_destroy, hb_buffer_create};
use harfbuzz::{hb_font_funcs_create};
use harfbuzz::{hb_font_funcs_destroy};
use harfbuzz::{hb_font_funcs_set_glyph_func};
use harfbuzz::{hb_font_funcs_set_glyph_h_advance_func};
use harfbuzz::{hb_font_funcs_t, hb_buffer_t, hb_codepoint_t};
use harfbuzz::{hb_font_set_funcs};
use harfbuzz::{hb_font_set_ppem};
use harfbuzz::{hb_font_set_scale};
use harfbuzz::{hb_shape, hb_buffer_get_glyph_infos};
use harfbuzz::{HB_MEMORY_MODE_READONLY, HB_DIRECTION_LTR};
use harfbuzz::{hb_blob_t};
use harfbuzz::{hb_bool_t};
use harfbuzz::{hb_face_t, hb_font_t};
use harfbuzz::{hb_font_funcs_t, hb_buffer_t, hb_codepoint_t};
use harfbuzz::{hb_glyph_info_t};
use harfbuzz::{hb_glyph_position_t};
use harfbuzz::{hb_position_t, hb_tag_t};
use harfbuzz::{hb_shape, hb_buffer_get_glyph_infos};
use servo_util::geometry::Au;
use servo_util::range::Range;
use std::cast::transmute;
use std::char;
use std::libc::{c_uint, c_int, c_void, c_char};
use std::num;
use std::ptr::null;
use std::ptr;
use std::vec;
static NO_GLYPH: i32 = -1;
static CONTINUATION_BYTE: i32 = -2;

Просмотреть файл

@ -2,14 +2,13 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use std::vec::VecIterator;
use servo_util::geometry::Au;
use text::glyph::GlyphStore;
use font::{Font, FontDescriptor, RunMetrics, FontStyle, FontMetrics};
use servo_util::range::Range;
use extra::arc::Arc;
use font::{Font, FontDescriptor, RunMetrics, FontStyle, FontMetrics};
use servo_util::geometry::Au;
use servo_util::range::Range;
use std::vec::VecIterator;
use style::computed_values::text_decoration;
use text::glyph::GlyphStore;
/// A text run.
#[deriving(Clone)]
@ -168,7 +167,7 @@ impl<'a> TextRun {
glyphs
}
pub fn char_len(&self) -> uint {
self.glyphs.get().iter().fold(0u, |len, slice_glyphs| {
len + slice_glyphs.get().char_len()

Просмотреть файл

@ -329,13 +329,14 @@ impl BlockFlow {
// top or bottom borders nor top or bottom padding, and it has a 'height' of either 0 or 'auto',
// and it does not contain a line box, and all of its in-flow children's margins (if any) collapse.
let screen_height = ctx.shared.screen_size.height;
let mut height = if self.is_root {
// FIXME(pcwalton): The max is taken here so that you can scroll the page, but this is
// not correct behavior according to CSS 2.1 § 10.5. Instead I think we should treat
// the root element as having `overflow: scroll` and use the layers-based scrolling
// infrastructure to make it scrollable.
Au::max(ctx.screen_size.size.height, cur_y)
Au::max(screen_height, cur_y)
} else {
cur_y - top_offset - collapsing
};
@ -364,8 +365,11 @@ impl BlockFlow {
noncontent_height = box_.padding.get().top + box_.padding.get().bottom +
box_.border.get().top + box_.border.get().bottom;
let (y, h) = box_.get_y_coord_and_new_height_if_fixed(ctx.screen_size.size.height,
height, clearance + margin.top, self.is_fixed);
let (y, h) = box_.get_y_coord_and_new_height_if_fixed(screen_height,
height,
clearance + margin.top,
self.is_fixed);
position.origin.y = y;
height = h;
@ -623,7 +627,7 @@ impl Flow for BlockFlow {
if self.is_root {
debug!("Setting root position");
self.base.position.origin = Au::zero_point();
self.base.position.size.width = ctx.screen_size.size.width;
self.base.position.size.width = ctx.shared.screen_size.width;
self.base.floats_in = FloatContext::new(self.base.num_floats);
self.base.flags.set_inorder(false);
}
@ -664,12 +668,17 @@ impl Flow for BlockFlow {
};
box_.margin.set(SideOffsets2D::new(margin_top,
margin_right,
margin_bottom,
margin_left));
margin_right,
margin_bottom,
margin_left));
let screen_size = ctx.shared.screen_size;
let (x, w) = box_.get_x_coord_and_new_width_if_fixed(screen_size.width,
screen_size.height,
width,
box_.offset(),
self.is_fixed);
let (x, w) = box_.get_x_coord_and_new_width_if_fixed(ctx.screen_size.size.width,
ctx.screen_size.size.height, width, box_.offset(), self.is_fixed);
x_offset = x;
remaining_width = w;

Просмотреть файл

@ -1171,7 +1171,7 @@ impl Box {
iframe_box.pipeline_id,
iframe_box.subpage_id);
let msg = FrameRectMsg(iframe_box.pipeline_id, iframe_box.subpage_id, rect);
layout_context.constellation_chan.send(msg)
layout_context.shared.constellation_chan.send(msg)
}
}

Просмотреть файл

@ -26,7 +26,7 @@ use layout::box_::{Box, GenericBox, IframeBox, IframeBoxInfo, ImageBox, ImageBox
use layout::box_::{UnscannedTextBox, UnscannedTextBoxInfo, InlineInfo, InlineParentInfo};
use layout::context::LayoutContext;
use layout::float_context::FloatType;
use layout::flow::{BaseFlow, Flow, MutableFlowUtils};
use layout::flow::{BaseFlow, Flow, MutableOwnedFlowUtils};
use layout::inline::InlineFlow;
use layout::text::TextRunScanner;
use layout::util::LayoutDataAccess;
@ -173,8 +173,6 @@ impl<T> OptVector<T> for Option<~[T]> {
/// An object that knows how to create flows.
pub struct FlowConstructor<'a> {
/// The layout context.
///
/// FIXME(pcwalton): Why does this contain `@`??? That destroys parallelism!!!
layout_context: &'a mut LayoutContext,
/// The next flow ID to assign.
@ -207,7 +205,7 @@ impl<'fc> FlowConstructor<'fc> {
Some(url) => {
// FIXME(pcwalton): The fact that image boxes store the cache within them makes
// little sense to me.
Some(ImageBoxInfo::new(&node, url, self.layout_context.image_cache.clone()))
Some(ImageBoxInfo::new(&node, url, self.layout_context.shared.image_cache.clone()))
}
}
}
@ -236,9 +234,17 @@ impl<'fc> FlowConstructor<'fc> {
fn flush_inline_boxes_to_flow(&mut self, boxes: ~[Box], flow: &mut ~Flow, node: LayoutNode) {
if boxes.len() > 0 {
let inline_base = BaseFlow::new(self.next_flow_id(), node);
let mut inline_flow = ~InlineFlow::from_boxes(inline_base, boxes) as ~Flow;
self.layout_context.shared.leaf_set.access(|leaf_set| leaf_set.insert(&inline_flow));
TextRunScanner::new().scan_for_runs(self.layout_context, inline_flow);
flow.add_new_child(inline_flow)
let mut inline_flow = Some(inline_flow);
self.layout_context.shared.leaf_set.access(|leaf_set| {
flow.add_new_child(inline_flow.take_unwrap(), leaf_set)
})
}
}
@ -282,7 +288,10 @@ impl<'fc> FlowConstructor<'fc> {
self.flush_inline_boxes_to_flow_if_necessary(&mut opt_boxes_for_inline_flow,
flow,
node);
flow.add_new_child(kid_flow);
let mut kid_flow = Some(kid_flow);
self.layout_context.shared.leaf_set.access(|leaf_set| {
flow.add_new_child(kid_flow.take_unwrap(), leaf_set)
})
}
ConstructionItemConstructionResult(InlineBoxesConstructionItem(
InlineBoxesConstructionResult {
@ -322,7 +331,10 @@ impl<'fc> FlowConstructor<'fc> {
// Push the flow generated by the {ib} split onto our list of
// flows.
flow.add_new_child(kid_flow);
let mut kid_flow = Some(kid_flow);
self.layout_context.shared.leaf_set.access(|leaf_set| {
flow.add_new_child(kid_flow.take_unwrap(), leaf_set)
})
}
}
}
@ -348,6 +360,9 @@ impl<'fc> FlowConstructor<'fc> {
let base = BaseFlow::new(self.next_flow_id(), node);
let box_ = self.build_box_for_node(node);
let mut flow = ~BlockFlow::from_box(base, box_, is_fixed) as ~Flow;
self.layout_context.shared.leaf_set.access(|leaf_set| leaf_set.insert(&flow));
self.build_children_of_block_flow(&mut flow, node);
flow
}
@ -358,7 +373,11 @@ impl<'fc> FlowConstructor<'fc> {
-> ~Flow {
let base = BaseFlow::new(self.next_flow_id(), node);
let box_ = self.build_box_for_node(node);
let mut flow = ~BlockFlow::float_from_box(base, float_type, box_) as ~Flow;
self.layout_context.shared.leaf_set.access(|leaf_set| leaf_set.insert(&flow));
self.build_children_of_block_flow(&mut flow, node);
flow
}

Просмотреть файл

@ -5,18 +5,36 @@
//! Data needed by the layout task.
use extra::arc::MutexArc;
use geom::rect::Rect;
use layout::flow::LeafSet;
use geom::size::Size2D;
use gfx::font_context::FontContext;
use servo_msg::constellation_msg::ConstellationChan;
use servo_net::local_image_cache::LocalImageCache;
use servo_util::geometry::Au;
/// Data needed by the layout task.
pub struct LayoutContext {
font_ctx: ~FontContext,
/// Data shared by all layout workers.
#[deriving(Clone)]
pub struct SharedLayoutInfo {
/// The local image cache.
image_cache: MutexArc<LocalImageCache>,
screen_size: Rect<Au>,
/// The current screen size.
screen_size: Size2D<Au>,
/// A channel up to the constellation.
constellation_chan: ConstellationChan,
/// The set of leaf flows.
leaf_set: MutexArc<LeafSet>,
}
/// Data specific to a layout worker.
pub struct LayoutContext {
/// Shared layout info.
shared: SharedLayoutInfo,
/// The current font context.
font_ctx: ~FontContext,
}

Просмотреть файл

@ -33,6 +33,8 @@ use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData};
use layout::float_context::{FloatContext, Invalid};
use layout::incremental::RestyleDamage;
use layout::inline::InlineFlow;
use layout::parallel::{FlowParallelInfo, UnsafeFlow};
use layout::parallel;
use layout::wrapper::LayoutNode;
use extra::dlist::{DList, DListIterator, MutDListIterator};
@ -43,6 +45,8 @@ use gfx::display_list::{ClipDisplayItemClass, DisplayList};
use servo_util::geometry::Au;
use std::cast;
use std::cell::RefCell;
use std::hashmap::{HashSet, HashSetIterator};
use std::sync::atomics::Relaxed;
use style::ComputedValues;
use style::computed_values::text_align;
@ -158,6 +162,9 @@ pub trait ImmutableFlowUtils {
/// Returns true if this flow has no children.
fn is_leaf(self) -> bool;
/// Returns the number of children that this flow possesses.
fn child_count(self) -> uint;
/// Returns true if this flow is a block flow, an inline flow, or a float flow.
fn starts_block_flow(self) -> bool;
@ -182,9 +189,6 @@ pub trait MutableFlowUtils {
// Mutators
/// Adds a new flow as a child of this flow.
fn add_new_child(self, new_child: ~Flow);
/// Invokes a closure with the first child of this flow.
fn with_first_child<R>(self, f: |Option<&mut ~Flow>| -> R) -> R;
@ -209,6 +213,12 @@ pub trait MutableFlowUtils {
-> bool;
}
pub trait MutableOwnedFlowUtils {
/// Adds a new flow as a child of this flow. Removes the flow from the given leaf set if
/// it's present.
fn add_new_child(&mut self, new_child: ~Flow, leaf_set: &mut LeafSet);
}
pub enum FlowClass {
BlockFlowClass,
InlineFlowClass,
@ -233,7 +243,7 @@ pub trait PostorderFlowTraversal {
fn process(&mut self, flow: &mut Flow) -> bool;
/// Returns false if this node must be processed in-order. If this returns false, we skip the
/// operation for this node, but continue processing the descendants. This is called *after*
/// operation for this node, but continue processing the ancestors. This is called *after*
/// child nodes are visited.
fn should_process(&mut self, _flow: &mut Flow) -> bool {
true
@ -319,6 +329,9 @@ impl FlowFlags {
}
/// Data common to all flows.
///
/// TODO(pcwalton): Plant a destructor bomb on this type. It is bad if it goes out of scope,
/// because of the leaf list.
pub struct BaseFlow {
restyle_damage: RestyleDamage,
@ -342,6 +355,11 @@ pub struct BaseFlow {
/// pixels of all the display list items for correct invalidation.
overflow: Rect<Au>,
/// Data used during parallel traversals.
///
/// TODO(pcwalton): Group with other transient data to save space.
parallel: FlowParallelInfo,
floats_in: FloatContext,
floats_out: FloatContext,
num_floats: uint,
@ -383,6 +401,9 @@ impl BaseFlow {
pref_width: Au::new(0),
position: Au::zero_rect(),
overflow: Au::zero_rect(),
parallel: FlowParallelInfo::new(),
floats_in: Invalid,
floats_out: Invalid,
num_floats: 0,
@ -411,6 +432,11 @@ impl<'a> ImmutableFlowUtils for &'a Flow {
base(self).children.len() == 0
}
/// Returns the number of children that this flow possesses.
fn child_count(self) -> uint {
base(self).children.len()
}
/// Returns true if this flow is a block flow, an inline-block flow, or a float flow.
fn starts_block_flow(self) -> bool {
match self.class() {
@ -484,11 +510,6 @@ impl<'a> MutableFlowUtils for &'a mut Flow {
traversal.process(self)
}
/// Adds a new flow as a child of this flow.
fn add_new_child(self, new_child: ~Flow) {
mut_base(self).children.push_back(new_child)
}
/// Invokes a closure with the first child of this flow.
fn with_first_child<R>(self, f: |Option<&mut ~Flow>| -> R) -> R {
f(mut_base(self).children.front_mut())
@ -562,3 +583,61 @@ impl<'a> MutableFlowUtils for &'a mut Flow {
}
}
impl MutableOwnedFlowUtils for ~Flow {
/// Adds a new flow as a child of this flow. Removes the flow from the given leaf set if
/// it's present.
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);
kid_base.parallel.parent = parallel::mut_owned_flow_to_unsafe_flow(self);
}
let base = mut_base(*self);
base.children.push_back(new_child);
let _ = base.parallel.children_count.fetch_add(1, Relaxed);
}
}
/// Keeps track of the leaves of the flow tree. This is used to efficiently start bottom-up
/// parallel traversals.
#[deriving(Clone)]
pub struct LeafSet {
priv set: HashSet<UnsafeFlow>,
}
impl LeafSet {
/// Creates a new leaf set.
pub fn new() -> LeafSet {
LeafSet {
set: HashSet::new(),
}
}
/// Inserts a newly-created flow into the leaf set.
pub fn insert(&mut self, flow: &~Flow) {
self.set.insert(parallel::owned_flow_to_unsafe_flow(flow));
}
/// Removes a flow from the leaf set. Asserts that the flow was indeed in the leaf set. (This
/// invariant is needed for memory safety, as there must always be exactly one leaf set.)
fn remove(&mut self, flow: &~Flow) {
let flow = parallel::owned_flow_to_unsafe_flow(flow);
if !self.set.contains(&flow) {
fail!("attempted to remove a flow from the leaf set that wasn't in the set!")
}
self.set.remove(&flow);
}
pub fn clear(&mut self) {
self.set.clear()
}
pub fn iter<'a>(&'a self) -> HashSetIterator<'a,UnsafeFlow> {
self.set.iter()
}
}

Просмотреть файл

@ -9,22 +9,23 @@ use css::matching::MatchMethods;
use css::select::new_stylist;
use css::node_style::StyledNode;
use layout::construct::{FlowConstructionResult, FlowConstructor, NoConstructionResult};
use layout::context::LayoutContext;
use layout::context::{LayoutContext, SharedLayoutInfo};
use layout::display_list_builder::{DisplayListBuilder, ToGfxColor};
use layout::extra::LayoutAuxMethods;
use layout::flow::{Flow, ImmutableFlowUtils, MutableFlowUtils, PreorderFlowTraversal};
use layout::flow::{Flow, ImmutableFlowUtils, LeafSet, MutableFlowUtils, PreorderFlowTraversal};
use layout::flow::{PostorderFlowTraversal};
use layout::flow;
use layout::incremental::{RestyleDamage};
use layout::incremental::RestyleDamage;
use layout::parallel::{AssignHeightsAndStoreOverflowTraversalKind, BubbleWidthsTraversalKind};
use layout::parallel::{ParallelPostorderFlowTraversal};
use layout::util::{LayoutDataAccess, OpaqueNode, LayoutDataWrapper};
use layout::wrapper::LayoutNode;
use extra::arc::{Arc, RWArc, MutexArc};
use geom::point::Point2D;
use extra::arc::{Arc, MutexArc, RWArc};
use geom::rect::Rect;
use geom::size::Size2D;
use gfx::display_list::{ClipDisplayItemClass, DisplayItem, DisplayItemIterator, DisplayList};
use gfx::font_context::FontContext;
use gfx::font_context::{FontContext, FontContextInfo};
use gfx::opts::Opts;
use gfx::render_task::{RenderMsg, RenderChan, RenderLayer};
use gfx::{render_task, color};
@ -78,14 +79,20 @@ pub struct LayoutTask {
/// The local image cache.
local_image_cache: MutexArc<LocalImageCache>,
/// The set of leaves in the flow tree.
leaf_set: MutexArc<LeafSet>,
/// The size of the viewport.
screen_size: Option<Size2D<Au>>,
screen_size: Size2D<Au>,
/// A cached display list.
display_list: Option<Arc<DisplayList<OpaqueNode>>>,
stylist: RWArc<Stylist>,
/// The workers that we use for parallel operation.
parallel_traversal: Option<ParallelPostorderFlowTraversal>,
/// The channel on which messages can be sent to the profiler.
profiler_chan: ProfilerChan,
@ -135,7 +142,7 @@ impl PreorderFlowTraversal for PropagateDamageTraversal {
/// The bubble-widths traversal, the first part of layout computation. This computes preferred
/// and intrinsic widths and bubbles them up the tree.
struct BubbleWidthsTraversal<'a>(&'a mut LayoutContext);
pub struct BubbleWidthsTraversal<'a>(&'a mut LayoutContext);
impl<'a> PostorderFlowTraversal for BubbleWidthsTraversal<'a> {
#[inline]
@ -167,7 +174,7 @@ impl<'a> PreorderFlowTraversal for AssignWidthsTraversal<'a> {
/// The assign-heights-and-store-overflow traversal, the last (and most expensive) part of layout
/// computation. Determines the final heights for all layout objects, computes positions, and
/// computes overflow regions. In Gecko this corresponds to `FinishAndStoreOverflow`.
struct AssignHeightsAndStoreOverflowTraversal<'a>(&'a mut LayoutContext);
pub struct AssignHeightsAndStoreOverflowTraversal<'a>(&'a mut LayoutContext);
impl<'a> PostorderFlowTraversal for AssignHeightsAndStoreOverflowTraversal<'a> {
#[inline]
@ -239,6 +246,18 @@ impl LayoutTask {
opts: &Opts,
profiler_chan: ProfilerChan)
-> LayoutTask {
let local_image_cache = MutexArc::new(LocalImageCache(image_cache_task.clone()));
let screen_size = Size2D(Au(0), Au(0));
let font_context_info = FontContextInfo {
backend: opts.render_backend,
needs_font_list: true,
profiler_chan: profiler_chan.clone(),
};
let parallel_traversal = if opts.layout_threads != 1 {
Some(ParallelPostorderFlowTraversal::new(font_context_info, opts.layout_threads))
} else {
None
};
LayoutTask {
id: id,
@ -248,12 +267,13 @@ impl LayoutTask {
script_chan: script_chan,
render_chan: render_chan,
image_cache_task: image_cache_task.clone(),
local_image_cache: MutexArc::new(LocalImageCache(image_cache_task)),
screen_size: None,
local_image_cache: local_image_cache,
screen_size: screen_size,
leaf_set: MutexArc::new(LeafSet::new()),
display_list: None,
stylist: RWArc::new(new_stylist()),
parallel_traversal: parallel_traversal,
profiler_chan: profiler_chan,
opts: opts.clone()
}
@ -268,16 +288,20 @@ impl LayoutTask {
// Create a layout context for use in building display lists, hit testing, &c.
fn build_layout_context(&self) -> LayoutContext {
let image_cache = self.local_image_cache.clone();
let font_ctx = ~FontContext::new(self.opts.render_backend, true,
self.profiler_chan.clone());
let screen_size = self.screen_size.unwrap();
let font_ctx = ~FontContext::new(FontContextInfo {
backend: self.opts.render_backend,
needs_font_list: true,
profiler_chan: self.profiler_chan.clone(),
});
LayoutContext {
image_cache: image_cache,
shared: SharedLayoutInfo {
image_cache: self.local_image_cache.clone(),
screen_size: self.screen_size.clone(),
constellation_chan: self.constellation_chan.clone(),
leaf_set: self.leaf_set.clone(),
},
font_ctx: font_ctx,
screen_size: Rect(Point2D(Au(0), Au(0)), screen_size),
constellation_chan: self.constellation_chan.clone(),
}
}
@ -344,6 +368,12 @@ impl LayoutTask {
/// crash.
fn exit_now(&mut self) {
let (response_port, response_chan) = Chan::new();
match self.parallel_traversal {
None => {}
Some(ref mut traversal) => traversal.shutdown(),
}
self.render_chan.send(render_task::ExitMsg(response_chan));
response_port.recv()
}
@ -388,18 +418,46 @@ impl LayoutTask {
fn solve_constraints(&mut self,
layout_root: &mut Flow,
layout_context: &mut LayoutContext) {
let _ = layout_root.traverse_postorder(&mut BubbleWidthsTraversal(layout_context));
layout_root.traverse_postorder(&mut BubbleWidthsTraversal(layout_context));
// FIXME(kmc): We want to prune nodes without the Reflow restyle damage
// bit, 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.
let _ = layout_root.traverse_preorder(&mut AssignWidthsTraversal(layout_context));
// NOTE: this currently computes borders, so any pruning should separate that operation
// out.
layout_root.traverse_preorder(&mut AssignWidthsTraversal(layout_context));
// For now, this is an inorder traversal
// FIXME: prune this traversal as well
let _ = layout_root.traverse_postorder(&mut
AssignHeightsAndStoreOverflowTraversal(layout_context));
// FIXME(pcwalton): Prune this pass as well.
layout_root.traverse_postorder(&mut AssignHeightsAndStoreOverflowTraversal(
layout_context));
}
/// Performs layout constraint solving in parallel.
///
/// This corresponds to `Reflow()` in Gecko and `layout()` in WebKit/Blink and should be
/// benchmarked against those two. It is marked `#[inline(never)]` to aid profiling.
#[inline(never)]
fn solve_constraints_parallel(&mut self,
layout_root: &mut Flow,
layout_context: &mut LayoutContext) {
match self.parallel_traversal {
None => fail!("solve_contraints_parallel() called with no parallel traversal ready"),
Some(ref mut traversal) => {
traversal.start(BubbleWidthsTraversalKind,
layout_context,
self.profiler_chan.clone());
// NOTE: this currently computes borders, so any pruning should separate that
// operation out.
// TODO(pcwalton): Run this in parallel as well. This will require a bit more work
// because this is a top-down traversal, unlike the others.
layout_root.traverse_preorder(&mut AssignWidthsTraversal(layout_context));
traversal.start(AssignHeightsAndStoreOverflowTraversalKind,
layout_context,
self.profiler_chan.clone());
}
}
}
/// The high-level routine that performs layout tasks.
@ -416,8 +474,9 @@ impl LayoutTask {
// Reset the image cache.
unsafe {
self.local_image_cache.unsafe_access(
|cache| cache.next_round(self.make_on_image_available_cb()));
self.local_image_cache.unsafe_access(|local_image_cache| {
local_image_cache.next_round(self.make_on_image_available_cb())
});
}
// true => Do the reflow with full style damage, because content
@ -427,12 +486,12 @@ impl LayoutTask {
_ => false
};
let screen_size = Size2D(Au::from_px(data.window_size.width as int),
Au::from_px(data.window_size.height as int));
if self.screen_size != Some(screen_size) {
all_style_damage = true;
let current_screen_size = Size2D(Au::from_px(data.window_size.width as int),
Au::from_px(data.window_size.height as int));
if self.screen_size != current_screen_size {
all_style_damage = true
}
self.screen_size = Some(screen_size);
self.screen_size = current_screen_size;
// Create a layout context for use throughout the following passes.
let mut layout_ctx = self.build_layout_context();
@ -469,12 +528,18 @@ impl LayoutTask {
// Perform the primary layout passes over the flow tree to compute the locations of all
// the boxes.
profile(time::LayoutMainCategory, self.profiler_chan.clone(), || {
self.solve_constraints(layout_root, &mut layout_ctx)
match self.parallel_traversal {
None => {
// Sequential mode.
self.solve_constraints(layout_root, &mut layout_ctx)
}
Some(_) => {
// Parallel mode.
self.solve_constraints_parallel(layout_root, &mut layout_ctx)
}
}
});
debug!("layout: constraint solving done:");
debug!("{:?}", layout_root.dump());
// Build the display list if necessary, and send it to the renderer.
if data.goal == ReflowForDisplay {
profile(time::LayoutDispListBuildCategory, self.profiler_chan.clone(), || {
@ -519,10 +584,17 @@ impl LayoutTask {
self.display_list = Some(display_list.clone());
debug!("Layout done!");
self.render_chan.send(RenderMsg(render_layer));
});
}
// FIXME(pcwalton): Hack because we don't yet reference count flows. When we do reference
// count them, then the destructor should remove the flow from the leaf set once the count
// hits zero.
self.leaf_set.access(|leaf_set| leaf_set.clear());
// Tell script that we're done.
//
// FIXME(pcwalton): This should probably be *one* channel, but we can't fix this without

Просмотреть файл

@ -0,0 +1,344 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
//! Implements parallel traversals over the flow tree.
use layout::context::{LayoutContext, SharedLayoutInfo};
use layout::flow::{Flow, PostorderFlowTraversal};
use layout::flow;
use layout::layout_task::{AssignHeightsAndStoreOverflowTraversal, BubbleWidthsTraversal};
use gfx::font_context::{FontContext, FontContextInfo};
use native;
use servo_util::time::{ProfilerChan, profile};
use servo_util::time;
use std::cast;
use std::comm::SharedChan;
use std::libc::c_void;
use std::ptr;
use std::sync::atomics::{AtomicInt, Relaxed, SeqCst};
use std::sync::deque::{Abort, BufferPool, Data, Empty, Stealer, Worker};
use std::util;
enum WorkerMsg {
/// Tells the worker to start a traversal.
StartMsg(Worker<UnsafeFlow>, TraversalKind, SharedLayoutInfo),
/// Tells the worker to stop. It can be restarted again with a `StartMsg`.
StopMsg,
/// Tells the worker thread to terminate.
ExitMsg,
}
enum SupervisorMsg {
/// Sent once the last flow is processed.
FinishedMsg,
/// Returns the deque to the supervisor.
ReturnDequeMsg(uint, Worker<UnsafeFlow>),
}
pub type UnsafeFlow = (*c_void, *c_void);
pub fn owned_flow_to_unsafe_flow(flow: *~Flow) -> UnsafeFlow {
unsafe {
cast::transmute_copy(&*flow)
}
}
pub fn mut_owned_flow_to_unsafe_flow(flow: *mut ~Flow) -> UnsafeFlow {
unsafe {
cast::transmute_copy(&*flow)
}
}
fn null_unsafe_flow() -> UnsafeFlow {
(ptr::null(), ptr::null())
}
/// Information that we need stored in each flow.
pub struct FlowParallelInfo {
/// The number of children that still need work done.
children_count: AtomicInt,
/// The address of the parent flow.
parent: UnsafeFlow,
}
impl FlowParallelInfo {
pub fn new() -> FlowParallelInfo {
FlowParallelInfo {
children_count: AtomicInt::new(0),
parent: null_unsafe_flow(),
}
}
}
/// Information that the supervisor thread keeps about the worker threads.
struct WorkerInfo {
/// The communication channel to the workers.
chan: Chan<WorkerMsg>,
/// The buffer pool for this deque.
pool: BufferPool<UnsafeFlow>,
/// The worker end of the deque, if we have it.
deque: Option<Worker<UnsafeFlow>>,
/// The thief end of the work-stealing deque.
thief: Stealer<UnsafeFlow>,
}
/// Information that each worker needs to do its job.
struct PostorderWorker {
/// The font context.
font_context: Option<~FontContext>,
/// Communications for the worker.
comm: PostorderWorkerComm,
}
/// Communication channels for postorder workers.
struct PostorderWorkerComm {
/// The index of this worker.
index: uint,
/// The communication port from the supervisor.
port: Port<WorkerMsg>,
/// The communication channel to the supervisor.
chan: SharedChan<SupervisorMsg>,
/// The thief end of the work-stealing deque for all other workers.
other_deques: ~[Stealer<UnsafeFlow>],
}
/// The type of traversal we're performing.
pub enum TraversalKind {
BubbleWidthsTraversalKind,
AssignHeightsAndStoreOverflowTraversalKind,
}
impl PostorderWorker {
/// Starts up the worker and listens for messages.
pub fn start(&mut self) {
loop {
// Wait for a start message.
let (mut deque, kind, shared_layout_info) = match self.comm.port.recv() {
StopMsg => fail!("unexpected stop message"),
StartMsg(deque, kind, shared_layout_info) => (deque, kind, shared_layout_info),
ExitMsg => return,
};
// Set up our traversal context.
let mut traversal = LayoutContext {
shared: shared_layout_info,
font_ctx: self.font_context.take_unwrap(),
};
// And we're off!
'outer: loop {
let unsafe_flow;
match deque.pop() {
Some(the_flow) => unsafe_flow = the_flow,
None => {
// Become a thief.
let mut i = 0;
loop {
if self.comm.other_deques.len() != 0 {
match self.comm.other_deques[i].steal() {
Empty => {
// Try the next one.
i += 1;
if i >= self.comm.other_deques.len() {
i = 0
}
}
Abort => {
// Continue.
}
Data(the_flow) => {
unsafe_flow = the_flow;
break
}
}
}
if i == 0 {
match self.comm.port.try_recv() {
Some(StopMsg) => break 'outer,
Some(ExitMsg) => return,
Some(_) => fail!("unexpected message!"),
None => {}
}
}
}
}
}
// OK, we've got some data. The rest of this is unsafe code.
unsafe {
// Get a real flow.
let flow: &mut ~Flow = cast::transmute(&unsafe_flow);
// Perform the appropriate traversal.
match kind {
BubbleWidthsTraversalKind => {
let mut traversal = BubbleWidthsTraversal(&mut traversal);
if traversal.should_process(*flow) {
traversal.process(*flow);
}
}
AssignHeightsAndStoreOverflowTraversalKind => {
let mut traversal =
AssignHeightsAndStoreOverflowTraversal(&mut traversal);
if traversal.should_process(*flow) {
traversal.process(*flow);
}
}
}
let base = flow::mut_base(*flow);
// Reset the count of children for the next layout traversal.
base.parallel.children_count.store(base.children.len() as int, Relaxed);
// Possibly enqueue the parent.
let unsafe_parent = base.parallel.parent;
if unsafe_parent == null_unsafe_flow() {
// We're done!
self.comm.chan.send(FinishedMsg);
} else {
// No, we're not at the root yet. Then are we the last sibling of our
// parent? If so, we can enqueue our parent; otherwise, we've gotta wait.
let parent: &mut ~Flow = cast::transmute(&unsafe_parent);
let parent_base = flow::mut_base(*parent);
if parent_base.parallel.children_count.fetch_sub(1, SeqCst) == 1 {
// We were the last child of our parent. Enqueue the parent.
deque.push(unsafe_parent)
}
}
}
}
// Destroy the traversal and save the font context.
let LayoutContext {
font_ctx: font_context,
..
} = traversal;
self.font_context = Some(font_context);
// Give the deque back to the supervisor.
self.comm.chan.send(ReturnDequeMsg(self.comm.index, deque))
}
}
}
/// A parallel bottom-up traversal.
pub struct ParallelPostorderFlowTraversal {
/// Information about each of the workers.
workers: ~[WorkerInfo],
/// A port on which information can be received from the workers.
port: Port<SupervisorMsg>,
}
impl ParallelPostorderFlowTraversal {
pub fn new(font_context_info: FontContextInfo, thread_count: uint)
-> ParallelPostorderFlowTraversal {
let (supervisor_port, supervisor_chan) = SharedChan::new();
let (mut infos, mut comms) = (~[], ~[]);
for i in range(0, thread_count) {
let (worker_port, worker_chan) = Chan::new();
let mut pool = BufferPool::new();
let (worker, thief) = pool.deque();
infos.push(WorkerInfo {
chan: worker_chan,
pool: pool,
deque: Some(worker),
thief: thief,
});
comms.push(PostorderWorkerComm {
index: i,
port: worker_port,
chan: supervisor_chan.clone(),
other_deques: ~[],
});
}
for i in range(0, thread_count) {
for j in range(0, thread_count) {
if i != j {
comms[i].other_deques.push(infos[j].thief.clone())
}
}
assert!(comms[i].other_deques.len() == thread_count - 1)
}
for comm in comms.move_iter() {
let font_context_info = font_context_info.clone();
native::task::spawn(proc() {
let mut worker = PostorderWorker {
font_context: Some(~FontContext::new(font_context_info)),
comm: comm,
};
worker.start()
})
}
ParallelPostorderFlowTraversal {
workers: infos,
port: supervisor_port,
}
}
/// TODO(pcwalton): This could be parallelized.
fn warmup(&mut self, layout_context: &mut LayoutContext) {
layout_context.shared.leaf_set.access(|leaf_set| {
for &flow in leaf_set.iter() {
match self.workers[0].deque {
None => fail!("no deque!"),
Some(ref mut deque) => {
deque.push(flow);
}
}
}
});
}
/// Traverses the given flow tree in parallel.
pub fn start(&mut self,
kind: TraversalKind,
layout_context: &mut LayoutContext,
profiler_chan: ProfilerChan) {
profile(time::LayoutParallelWarmupCategory, profiler_chan, || self.warmup(layout_context));
for worker in self.workers.mut_iter() {
worker.chan.send(StartMsg(util::replace(&mut worker.deque, None).unwrap(),
kind,
layout_context.shared.clone()))
}
// Wait for them to finish.
let _ = self.port.recv();
// Tell everyone to stop.
for worker in self.workers.iter() {
worker.chan.send(StopMsg);
}
// Get our deques back.
//
// TODO(pcwalton): Might be able to get a little parallelism over multiple traversals by
// doing this lazily.
for _ in range(0, self.workers.len()) {
match self.port.recv() {
ReturnDequeMsg(returned_deque_index, returned_deque) => {
self.workers[returned_deque_index].deque = Some(returned_deque)
}
_ => fail!("unexpected message received during return queue phase"),
}
}
}
/// Shuts down all the worker threads.
pub fn shutdown(&mut self) {
for worker in self.workers.iter() {
worker.chan.send(ExitMsg)
}
}
}

Просмотреть файл

@ -95,6 +95,7 @@ pub mod layout {
pub mod layout_task;
pub mod inline;
pub mod model;
pub mod parallel;
pub mod text;
pub mod util;
pub mod incremental;

Просмотреть файл

@ -6,10 +6,10 @@ use image::base::Image;
use image_cache_task::{ImageReady, ImageNotReady, ImageFailed};
use local_image_cache::LocalImageCache;
use std::util::replace;
use geom::size::Size2D;
use extra::url::Url;
use extra::arc::{Arc, MutexArc};
use extra::url::Url;
use geom::size::Size2D;
use std::util;
// FIXME: Nasty coupling here This will be a problem if we want to factor out image handling from
// the network stack. This should probably be factored out into an interface and use dependency
@ -32,7 +32,7 @@ impl ImageHolder {
url: url,
image: None,
cached_size: Size2D(0,0),
local_image_cache: local_image_cache,
local_image_cache: local_image_cache.clone(),
};
// Tell the image cache we're going to be interested in this url
@ -40,12 +40,10 @@ impl ImageHolder {
// but they are intended to be spread out in time. Ideally prefetch
// should be done as early as possible and decode only once we
// are sure that the image will be used.
//
// LocalImageCache isn't Freeze so we have to use unsafe_access.
unsafe {
holder.local_image_cache.unsafe_access(|cache| {
cache.prefetch(&holder.url);
cache.decode(&holder.url);
holder.local_image_cache.unsafe_access(|local_image_cache| {
local_image_cache.prefetch(&holder.url);
local_image_cache.decode(&holder.url);
});
}
@ -79,8 +77,9 @@ impl ImageHolder {
// the image and store it for the future
if self.image.is_none() {
let port = unsafe {
self.local_image_cache.unsafe_access(
|cache| cache.get_image(&self.url))
self.local_image_cache.unsafe_access(|local_image_cache| {
local_image_cache.get_image(&self.url)
})
};
match port.recv() {
ImageReady(image) => {
@ -96,9 +95,9 @@ impl ImageHolder {
}
// Clone isn't pure so we have to swap out the mutable image option
let image = replace(&mut self.image, None);
let image = util::replace(&mut self.image, None);
let result = image.clone();
replace(&mut self.image, image);
util::replace(&mut self.image, image);
return result;
}

Просмотреть файл

@ -370,11 +370,11 @@ impl ImageCache {
match self.wait_map.pop(&url) {
Some(waiters) => {
unsafe {
waiters.unsafe_access( |waiters| {
waiters.unsafe_access(|waiters| {
for response in waiters.iter() {
response.send(f());
}
})
});
}
}
None => ()
@ -404,9 +404,7 @@ impl ImageCache {
let waiters = self.wait_map.find_mut(&url).unwrap();
let mut response = Some(response);
unsafe {
waiters.unsafe_access(|waiters| {
waiters.push(response.take().unwrap());
});
waiters.unsafe_access(|waiters| waiters.push(response.take().unwrap()))
}
} else {
self.wait_map.insert(url, MutexArc::new(~[response]));

Просмотреть файл

@ -52,6 +52,7 @@ pub enum ProfilerCategory {
LayoutSelectorMatchCategory,
LayoutTreeBuilderCategory,
LayoutMainCategory,
LayoutParallelWarmupCategory,
LayoutShapingCategory,
LayoutDispListBuildCategory,
GfxRegenAvailableFontsCategory,
@ -78,6 +79,7 @@ impl ProfilerCategory {
buckets.insert(LayoutSelectorMatchCategory, ~[]);
buckets.insert(LayoutTreeBuilderCategory, ~[]);
buckets.insert(LayoutMainCategory, ~[]);
buckets.insert(LayoutParallelWarmupCategory, ~[]);
buckets.insert(LayoutShapingCategory, ~[]);
buckets.insert(LayoutDispListBuildCategory, ~[]);
buckets.insert(GfxRegenAvailableFontsCategory, ~[]);
@ -93,7 +95,8 @@ impl ProfilerCategory {
pub fn format(self) -> ~str {
let padding = match self {
LayoutAuxInitCategory | LayoutSelectorMatchCategory | LayoutTreeBuilderCategory |
LayoutMainCategory | LayoutDispListBuildCategory | LayoutShapingCategory=> " - ",
LayoutMainCategory | LayoutDispListBuildCategory | LayoutShapingCategory |
LayoutParallelWarmupCategory => " - ",
_ => ""
};
format!("{:s}{:?}", padding, self)

Просмотреть файл

@ -19,3 +19,4 @@ pub mod vec;
pub mod debug;
pub mod io;
pub mod task;