diff --git a/servo/src/components/gfx/display_list.rs b/servo/src/components/gfx/display_list.rs index 7a5004c03251..1b3df8808cc1 100644 --- a/servo/src/components/gfx/display_list.rs +++ b/servo/src/components/gfx/display_list.rs @@ -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 { diff --git a/servo/src/components/gfx/font.rs b/servo/src/components/gfx/font.rs index ec9c9889a54c..244a4bebb453 100644 --- a/servo/src/components/gfx/font.rs +++ b/servo/src/components/gfx/font.rs @@ -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, diff --git a/servo/src/components/gfx/font_context.rs b/servo/src/components/gfx/font_context.rs index a465df11ffda..87d445ed2924 100644 --- a/servo/src/components/gfx/font_context.rs +++ b/servo/src/components/gfx/font_context.rs @@ -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; @@ -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(), } } diff --git a/servo/src/components/gfx/font_list.rs b/servo/src/components/gfx/font_list.rs index cb49d90f148c..71aa6ce0f3f1 100644 --- a/servo/src/components/gfx/font_list.rs +++ b/servo/src/components/gfx/font_list.rs @@ -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 diff --git a/servo/src/components/gfx/opts.rs b/servo/src/components/gfx/opts.rs index a58b6ed42916..04daf519c878 100644 --- a/servo/src/components/gfx/opts.rs +++ b/servo/src/components/gfx/opts.rs @@ -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, + /// 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"), diff --git a/servo/src/components/gfx/platform/macos/font_list.rs b/servo/src/components/gfx/platform/macos/font_list.rs index 638b651a0a8b..1cff5d3db809 100644 --- a/servo/src/components/gfx/platform/macos/font_list.rs +++ b/servo/src/components/gfx/platform/macos/font_list.rs @@ -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) }; diff --git a/servo/src/components/gfx/render_task.rs b/servo/src/components/gfx/render_task.rs index c692e901714f..a5fcf6b33409 100644 --- a/servo/src/components/gfx/render_task.rs +++ b/servo/src/components/gfx/render_task.rs @@ -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 RenderTask { 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, diff --git a/servo/src/components/gfx/text/shaping/harfbuzz.rs b/servo/src/components/gfx/text/shaping/harfbuzz.rs index 93d590fba9a1..cf67452ccdc0 100644 --- a/servo/src/components/gfx/text/shaping/harfbuzz.rs +++ b/servo/src/components/gfx/text/shaping/harfbuzz.rs @@ -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; diff --git a/servo/src/components/gfx/text/text_run.rs b/servo/src/components/gfx/text/text_run.rs index 0bde3607d3fa..25ee4c1275fd 100644 --- a/servo/src/components/gfx/text/text_run.rs +++ b/servo/src/components/gfx/text/text_run.rs @@ -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() diff --git a/servo/src/components/main/layout/block.rs b/servo/src/components/main/layout/block.rs index a31b1a8fd03a..3d1429b1a8d8 100644 --- a/servo/src/components/main/layout/block.rs +++ b/servo/src/components/main/layout/block.rs @@ -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; diff --git a/servo/src/components/main/layout/box_.rs b/servo/src/components/main/layout/box_.rs index 39cb0004df1d..75e1898145f5 100644 --- a/servo/src/components/main/layout/box_.rs +++ b/servo/src/components/main/layout/box_.rs @@ -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) } } diff --git a/servo/src/components/main/layout/construct.rs b/servo/src/components/main/layout/construct.rs index ee06fa20af74..070e1f5b687a 100644 --- a/servo/src/components/main/layout/construct.rs +++ b/servo/src/components/main/layout/construct.rs @@ -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 OptVector 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 } diff --git a/servo/src/components/main/layout/context.rs b/servo/src/components/main/layout/context.rs index 42568fde5019..4b62d4236d17 100644 --- a/servo/src/components/main/layout/context.rs +++ b/servo/src/components/main/layout/context.rs @@ -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, - screen_size: Rect, + + /// The current screen size. + screen_size: Size2D, /// A channel up to the constellation. constellation_chan: ConstellationChan, + + /// The set of leaf flows. + leaf_set: MutexArc, } + +/// Data specific to a layout worker. +pub struct LayoutContext { + /// Shared layout info. + shared: SharedLayoutInfo, + + /// The current font context. + font_ctx: ~FontContext, +} + diff --git a/servo/src/components/main/layout/flow.rs b/servo/src/components/main/layout/flow.rs index 94a9a5054886..22719ff9f2b3 100644 --- a/servo/src/components/main/layout/flow.rs +++ b/servo/src/components/main/layout/flow.rs @@ -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(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, + /// 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(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, +} + +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() + } +} + diff --git a/servo/src/components/main/layout/layout_task.rs b/servo/src/components/main/layout/layout_task.rs index 7b2dbfe1014c..22e34b2fe976 100644 --- a/servo/src/components/main/layout/layout_task.rs +++ b/servo/src/components/main/layout/layout_task.rs @@ -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, + /// The set of leaves in the flow tree. + leaf_set: MutexArc, + /// The size of the viewport. - screen_size: Option>, + screen_size: Size2D, /// A cached display list. display_list: Option>>, stylist: RWArc, + /// The workers that we use for parallel operation. + parallel_traversal: Option, + /// 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 diff --git a/servo/src/components/main/layout/parallel.rs b/servo/src/components/main/layout/parallel.rs new file mode 100644 index 000000000000..480ec886d8f6 --- /dev/null +++ b/servo/src/components/main/layout/parallel.rs @@ -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, 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), +} + +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, + /// The buffer pool for this deque. + pool: BufferPool, + /// The worker end of the deque, if we have it. + deque: Option>, + /// The thief end of the work-stealing deque. + thief: Stealer, +} + +/// 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, + /// The communication channel to the supervisor. + chan: SharedChan, + /// The thief end of the work-stealing deque for all other workers. + other_deques: ~[Stealer], +} + +/// 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, +} + +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) + } + } +} + diff --git a/servo/src/components/main/servo.rc b/servo/src/components/main/servo.rc index 7e2b934d8d39..60f6902a5915 100755 --- a/servo/src/components/main/servo.rc +++ b/servo/src/components/main/servo.rc @@ -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; diff --git a/servo/src/components/net/image/holder.rs b/servo/src/components/net/image/holder.rs index 7213644551d9..1e6c1c072cc6 100644 --- a/servo/src/components/net/image/holder.rs +++ b/servo/src/components/net/image/holder.rs @@ -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; } diff --git a/servo/src/components/net/image_cache_task.rs b/servo/src/components/net/image_cache_task.rs index 886eb10a6818..6badcee2c17b 100644 --- a/servo/src/components/net/image_cache_task.rs +++ b/servo/src/components/net/image_cache_task.rs @@ -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])); diff --git a/servo/src/components/util/time.rs b/servo/src/components/util/time.rs index 911100bcb36d..b4ac13ef0950 100644 --- a/servo/src/components/util/time.rs +++ b/servo/src/components/util/time.rs @@ -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) diff --git a/servo/src/components/util/util.rc b/servo/src/components/util/util.rc index c427e18eff03..50af03c67a78 100644 --- a/servo/src/components/util/util.rc +++ b/servo/src/components/util/util.rc @@ -19,3 +19,4 @@ pub mod vec; pub mod debug; pub mod io; pub mod task; +