зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
9f4f6d69bc
Коммит
a056ba0f8f
|
@ -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;
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче