mirror of
https://github.com/servo/servo.git
synced 2025-08-04 21:20:23 +01:00
layout: Implement parallel reflow for the bubble-widths and
assign-heights phases. This uses the new work-stealing deque. By default, 3/4 of a thread per logical CPU is used. This can be tuned with the `-y` flag. I measured a 65% reflow speedup on `perf-rainbow.html` and a 247% reflow speedup on `http://en.wikipedia.org/wiki/South_China_Sea` on a 4-core HyperThreaded Core i7. However, numbers were fairly volatile, especially for the latter.
This commit is contained in:
parent
539cf58f73
commit
54f0f17f83
21 changed files with 706 additions and 141 deletions
|
@ -15,17 +15,17 @@
|
||||||
/// low-level drawing primitives.
|
/// low-level drawing primitives.
|
||||||
|
|
||||||
use color::Color;
|
use color::Color;
|
||||||
use servo_util::geometry::Au;
|
|
||||||
use style::computed_values::border_style;
|
|
||||||
use render_context::RenderContext;
|
use render_context::RenderContext;
|
||||||
use text::TextRun;
|
use text::TextRun;
|
||||||
|
|
||||||
use std::cast::transmute_region;
|
use extra::arc::Arc;
|
||||||
use std::vec::VecIterator;
|
|
||||||
use geom::{Point2D, Rect, Size2D, SideOffsets2D};
|
use geom::{Point2D, Rect, Size2D, SideOffsets2D};
|
||||||
use servo_net::image::base::Image;
|
use servo_net::image::base::Image;
|
||||||
|
use servo_util::geometry::Au;
|
||||||
use servo_util::range::Range;
|
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.
|
/// A list of rendering operations to be performed.
|
||||||
pub struct DisplayList<E> {
|
pub struct DisplayList<E> {
|
||||||
|
|
|
@ -15,6 +15,7 @@ use std::cell::RefCell;
|
||||||
use servo_util::cache::{Cache, HashCache};
|
use servo_util::cache::{Cache, HashCache};
|
||||||
use servo_util::range::Range;
|
use servo_util::range::Range;
|
||||||
use style::computed_values::{text_decoration, font_weight, font_style};
|
use style::computed_values::{text_decoration, font_weight, font_style};
|
||||||
|
|
||||||
use color::Color;
|
use color::Color;
|
||||||
use font_context::FontContext;
|
use font_context::FontContext;
|
||||||
use servo_util::geometry::Au;
|
use servo_util::geometry::Au;
|
||||||
|
@ -228,7 +229,6 @@ impl<'a> Font {
|
||||||
};
|
};
|
||||||
|
|
||||||
let metrics = handle.get_metrics();
|
let metrics = handle.get_metrics();
|
||||||
// TODO(Issue #179): convert between specified and used font style here?
|
|
||||||
|
|
||||||
return Ok(Rc::from_mut(RefCell::new(Font {
|
return Ok(Rc::from_mut(RefCell::new(Font {
|
||||||
handle: handle,
|
handle: handle,
|
||||||
|
|
|
@ -2,22 +2,32 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* 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/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
use font::{Font, FontDescriptor, FontGroup, FontHandleMethods,
|
use font::{Font, FontDescriptor, FontGroup, FontHandleMethods, SelectorPlatformIdentifier};
|
||||||
SelectorPlatformIdentifier};
|
|
||||||
use font::{SpecifiedFontStyle, UsedFontStyle};
|
use font::{SpecifiedFontStyle, UsedFontStyle};
|
||||||
use font_list::FontList;
|
use font_list::FontList;
|
||||||
use servo_util::cache::{Cache, LRUCache};
|
|
||||||
use servo_util::time::ProfilerChan;
|
|
||||||
|
|
||||||
use platform::font::FontHandle;
|
use platform::font::FontHandle;
|
||||||
use platform::font_context::FontContextHandle;
|
use platform::font_context::FontContextHandle;
|
||||||
|
|
||||||
use azure::azure_hl::BackendType;
|
use azure::azure_hl::BackendType;
|
||||||
|
use servo_util::cache::{Cache, LRUCache};
|
||||||
|
use servo_util::time::ProfilerChan;
|
||||||
use std::hashmap::HashMap;
|
use std::hashmap::HashMap;
|
||||||
|
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::cell::RefCell;
|
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 {
|
pub trait FontContextHandleMethods {
|
||||||
fn create_font_from_identifier(&self, ~str, UsedFontStyle) -> Result<FontHandle, ()>;
|
fn create_font_from_identifier(&self, ~str, UsedFontStyle) -> Result<FontHandle, ()>;
|
||||||
|
@ -34,14 +44,13 @@ pub struct FontContext {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FontContext {
|
impl FontContext {
|
||||||
pub fn new(backend: BackendType,
|
pub fn new(info: FontContextInfo) -> FontContext {
|
||||||
needs_font_list: bool,
|
|
||||||
profiler_chan: ProfilerChan)
|
|
||||||
-> FontContext {
|
|
||||||
let handle = FontContextHandle::new();
|
let handle = FontContextHandle::new();
|
||||||
let font_list = if needs_font_list {
|
let font_list = if info.needs_font_list {
|
||||||
Some(FontList::new(&handle, profiler_chan.clone())) }
|
Some(FontList::new(&handle, info.profiler_chan.clone()))
|
||||||
else { None };
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
// TODO: Allow users to specify these.
|
// TODO: Allow users to specify these.
|
||||||
let mut generic_fonts = HashMap::with_capacity(5);
|
let mut generic_fonts = HashMap::with_capacity(5);
|
||||||
|
@ -56,9 +65,9 @@ impl FontContext {
|
||||||
font_list: font_list,
|
font_list: font_list,
|
||||||
group_cache: LRUCache::new(10),
|
group_cache: LRUCache::new(10),
|
||||||
handle: handle,
|
handle: handle,
|
||||||
backend: backend,
|
backend: info.backend,
|
||||||
generic_fonts: generic_fonts,
|
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::FontHandle;
|
||||||
use platform::font_context::FontContextHandle;
|
use platform::font_context::FontContextHandle;
|
||||||
use platform::font_list::FontListHandle;
|
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 style::computed_values::{font_weight, font_style};
|
||||||
|
|
||||||
|
use servo_util::time::{ProfilerChan, profile};
|
||||||
|
use servo_util::time;
|
||||||
use std::hashmap::HashMap;
|
use std::hashmap::HashMap;
|
||||||
|
|
||||||
pub type FontFamilyMap = HashMap<~str, FontFamily>;
|
pub type FontFamilyMap = HashMap<~str, FontFamily>;
|
||||||
|
|
|
@ -8,6 +8,8 @@
|
||||||
use azure::azure_hl::{BackendType, CairoBackend, CoreGraphicsBackend};
|
use azure::azure_hl::{BackendType, CairoBackend, CoreGraphicsBackend};
|
||||||
use azure::azure_hl::{CoreGraphicsAcceleratedBackend, Direct2DBackend, SkiaBackend};
|
use azure::azure_hl::{CoreGraphicsAcceleratedBackend, Direct2DBackend, SkiaBackend};
|
||||||
use extra::getopts::groups;
|
use extra::getopts::groups;
|
||||||
|
use std::num;
|
||||||
|
use std::rt;
|
||||||
|
|
||||||
/// Global flags for Servo, currently set on the command line.
|
/// Global flags for Servo, currently set on the command line.
|
||||||
#[deriving(Clone)]
|
#[deriving(Clone)]
|
||||||
|
@ -34,6 +36,10 @@ pub struct Opts {
|
||||||
/// it to produce output on that interval (`-p`).
|
/// it to produce output on that interval (`-p`).
|
||||||
profiler_period: Option<f64>,
|
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`).
|
/// True to exit after the page load (`-x`).
|
||||||
exit_after_load: bool,
|
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::optopt("t", "threads", "Number of render threads", "1"),
|
||||||
groups::optflagopt("p", "profile", "Profiler flag and output interval", "10"),
|
groups::optflagopt("p", "profile", "Profiler flag and output interval", "10"),
|
||||||
groups::optflag("x", "exit", "Exit after load flag"),
|
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("z", "headless", "Headless mode"),
|
||||||
groups::optflag("f", "hard-fail", "Exit on task failure instead of displaying about:failure"),
|
groups::optflag("f", "hard-fail", "Exit on task failure instead of displaying about:failure"),
|
||||||
groups::optflag("h", "help", "Print this message")
|
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 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 {
|
Opts {
|
||||||
urls: urls,
|
urls: urls,
|
||||||
render_backend: render_backend,
|
render_backend: render_backend,
|
||||||
|
@ -126,6 +138,7 @@ pub fn from_cmdline_args(args: &[~str]) -> Opts {
|
||||||
cpu_painting: cpu_painting,
|
cpu_painting: cpu_painting,
|
||||||
tile_size: tile_size,
|
tile_size: tile_size,
|
||||||
profiler_period: profiler_period,
|
profiler_period: profiler_period,
|
||||||
|
layout_threads: layout_threads,
|
||||||
exit_after_load: opt_match.opt_present("x"),
|
exit_after_load: opt_match.opt_present("x"),
|
||||||
output_file: opt_match.opt_str("o"),
|
output_file: opt_match.opt_str("o"),
|
||||||
headless: opt_match.opt_present("z"),
|
headless: opt_match.opt_present("z"),
|
||||||
|
|
|
@ -9,9 +9,8 @@ use platform::macos::font_context::FontContextHandle;
|
||||||
|
|
||||||
use core_foundation::base::TCFType;
|
use core_foundation::base::TCFType;
|
||||||
use core_foundation::string::{CFString, CFStringRef};
|
use core_foundation::string::{CFString, CFStringRef};
|
||||||
use core_text;
|
|
||||||
use core_text::font_descriptor::{CTFontDescriptor, CTFontDescriptorRef};
|
use core_text::font_descriptor::{CTFontDescriptor, CTFontDescriptorRef};
|
||||||
|
use core_text;
|
||||||
use std::cast;
|
use std::cast;
|
||||||
use std::hashmap::HashMap;
|
use std::hashmap::HashMap;
|
||||||
|
|
||||||
|
@ -44,7 +43,8 @@ impl FontListHandle {
|
||||||
pub fn load_variations_for_family(&self, family: &mut FontFamily) {
|
pub fn load_variations_for_family(&self, family: &mut FontFamily) {
|
||||||
debug!("Looking for faces of family: {:s}", family.family_name);
|
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();
|
let family_descriptors = family_collection.get_descriptors();
|
||||||
for descref in family_descriptors.iter() {
|
for descref in family_descriptors.iter() {
|
||||||
let descref: CTFontDescriptorRef = unsafe { cast::transmute(descref) };
|
let descref: CTFontDescriptorRef = unsafe { cast::transmute(descref) };
|
||||||
|
|
|
@ -25,7 +25,7 @@ use extra::arc::Arc;
|
||||||
|
|
||||||
use buffer_map::BufferMap;
|
use buffer_map::BufferMap;
|
||||||
use display_list::DisplayList;
|
use display_list::DisplayList;
|
||||||
use font_context::FontContext;
|
use font_context::{FontContext, FontContextInfo};
|
||||||
use opts::Opts;
|
use opts::Opts;
|
||||||
use render_context::RenderContext;
|
use render_context::RenderContext;
|
||||||
|
|
||||||
|
@ -159,9 +159,11 @@ impl<C: RenderListener + Send,T:Send+Freeze> RenderTask<C,T> {
|
||||||
port: port,
|
port: port,
|
||||||
compositor: compositor,
|
compositor: compositor,
|
||||||
constellation_chan: constellation_chan,
|
constellation_chan: constellation_chan,
|
||||||
font_ctx: ~FontContext::new(opts.render_backend.clone(),
|
font_ctx: ~FontContext::new(FontContextInfo {
|
||||||
false,
|
backend: opts.render_backend.clone(),
|
||||||
profiler_chan.clone()),
|
needs_font_list: false,
|
||||||
|
profiler_chan: profiler_chan.clone(),
|
||||||
|
}),
|
||||||
opts: opts,
|
opts: opts,
|
||||||
profiler_chan: profiler_chan,
|
profiler_chan: profiler_chan,
|
||||||
|
|
||||||
|
|
|
@ -5,45 +5,45 @@
|
||||||
extern mod harfbuzz;
|
extern mod harfbuzz;
|
||||||
|
|
||||||
use font::{Font, FontHandleMethods, FontTableMethods, FontTableTag};
|
use font::{Font, FontHandleMethods, FontTableMethods, FontTableTag};
|
||||||
use servo_util::geometry::Au;
|
|
||||||
use platform::font::FontTable;
|
use platform::font::FontTable;
|
||||||
use text::glyph::{GlyphStore, GlyphIndex, GlyphData};
|
use text::glyph::{GlyphStore, GlyphIndex, GlyphData};
|
||||||
use text::shaping::ShaperMethods;
|
use text::shaping::ShaperMethods;
|
||||||
use servo_util::range::Range;
|
|
||||||
use text::util::{float_to_fixed, fixed_to_float};
|
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 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_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_add_utf8};
|
||||||
|
use harfbuzz::{hb_buffer_destroy};
|
||||||
use harfbuzz::{hb_buffer_get_glyph_positions};
|
use harfbuzz::{hb_buffer_get_glyph_positions};
|
||||||
use harfbuzz::{hb_buffer_set_direction};
|
use harfbuzz::{hb_buffer_set_direction};
|
||||||
use harfbuzz::{hb_buffer_destroy};
|
|
||||||
use harfbuzz::{hb_face_destroy};
|
use harfbuzz::{hb_face_destroy};
|
||||||
|
use harfbuzz::{hb_face_t, hb_font_t};
|
||||||
use harfbuzz::{hb_font_create};
|
use harfbuzz::{hb_font_create};
|
||||||
use harfbuzz::{hb_font_destroy, hb_buffer_create};
|
use harfbuzz::{hb_font_destroy, hb_buffer_create};
|
||||||
use harfbuzz::{hb_font_funcs_create};
|
use harfbuzz::{hb_font_funcs_create};
|
||||||
use harfbuzz::{hb_font_funcs_destroy};
|
use harfbuzz::{hb_font_funcs_destroy};
|
||||||
use harfbuzz::{hb_font_funcs_set_glyph_func};
|
use harfbuzz::{hb_font_funcs_set_glyph_func};
|
||||||
use harfbuzz::{hb_font_funcs_set_glyph_h_advance_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_funcs};
|
||||||
use harfbuzz::{hb_font_set_ppem};
|
use harfbuzz::{hb_font_set_ppem};
|
||||||
use harfbuzz::{hb_font_set_scale};
|
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_info_t};
|
||||||
use harfbuzz::{hb_glyph_position_t};
|
use harfbuzz::{hb_glyph_position_t};
|
||||||
use harfbuzz::{hb_position_t, hb_tag_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 NO_GLYPH: i32 = -1;
|
||||||
static CONTINUATION_BYTE: i32 = -2;
|
static CONTINUATION_BYTE: i32 = -2;
|
||||||
|
|
|
@ -2,14 +2,13 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* 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/. */
|
* 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 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 style::computed_values::text_decoration;
|
||||||
|
use text::glyph::GlyphStore;
|
||||||
|
|
||||||
/// A text run.
|
/// A text run.
|
||||||
#[deriving(Clone)]
|
#[deriving(Clone)]
|
||||||
|
|
|
@ -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',
|
// 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.
|
// 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 {
|
let mut height = if self.is_root {
|
||||||
// FIXME(pcwalton): The max is taken here so that you can scroll the page, but this is
|
// 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
|
// 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
|
// the root element as having `overflow: scroll` and use the layers-based scrolling
|
||||||
// infrastructure to make it scrollable.
|
// infrastructure to make it scrollable.
|
||||||
Au::max(ctx.screen_size.size.height, cur_y)
|
Au::max(screen_height, cur_y)
|
||||||
} else {
|
} else {
|
||||||
cur_y - top_offset - collapsing
|
cur_y - top_offset - collapsing
|
||||||
};
|
};
|
||||||
|
@ -364,8 +365,11 @@ impl BlockFlow {
|
||||||
noncontent_height = box_.padding.get().top + box_.padding.get().bottom +
|
noncontent_height = box_.padding.get().top + box_.padding.get().bottom +
|
||||||
box_.border.get().top + box_.border.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,
|
let (y, h) = box_.get_y_coord_and_new_height_if_fixed(screen_height,
|
||||||
height, clearance + margin.top, self.is_fixed);
|
height,
|
||||||
|
clearance + margin.top,
|
||||||
|
self.is_fixed);
|
||||||
|
|
||||||
position.origin.y = y;
|
position.origin.y = y;
|
||||||
height = h;
|
height = h;
|
||||||
|
|
||||||
|
@ -623,7 +627,7 @@ impl Flow for BlockFlow {
|
||||||
if self.is_root {
|
if self.is_root {
|
||||||
debug!("Setting root position");
|
debug!("Setting root position");
|
||||||
self.base.position.origin = Au::zero_point();
|
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.floats_in = FloatContext::new(self.base.num_floats);
|
||||||
self.base.flags.set_inorder(false);
|
self.base.flags.set_inorder(false);
|
||||||
}
|
}
|
||||||
|
@ -668,8 +672,13 @@ impl Flow for BlockFlow {
|
||||||
margin_bottom,
|
margin_bottom,
|
||||||
margin_left));
|
margin_left));
|
||||||
|
|
||||||
let (x, w) = box_.get_x_coord_and_new_width_if_fixed(ctx.screen_size.size.width,
|
let screen_size = ctx.shared.screen_size;
|
||||||
ctx.screen_size.size.height, width, box_.offset(), self.is_fixed);
|
let (x, w) = box_.get_x_coord_and_new_width_if_fixed(screen_size.width,
|
||||||
|
screen_size.height,
|
||||||
|
width,
|
||||||
|
box_.offset(),
|
||||||
|
self.is_fixed);
|
||||||
|
|
||||||
x_offset = x;
|
x_offset = x;
|
||||||
remaining_width = w;
|
remaining_width = w;
|
||||||
|
|
||||||
|
|
|
@ -1171,7 +1171,7 @@ impl Box {
|
||||||
iframe_box.pipeline_id,
|
iframe_box.pipeline_id,
|
||||||
iframe_box.subpage_id);
|
iframe_box.subpage_id);
|
||||||
let msg = FrameRectMsg(iframe_box.pipeline_id, iframe_box.subpage_id, rect);
|
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::box_::{UnscannedTextBox, UnscannedTextBoxInfo, InlineInfo, InlineParentInfo};
|
||||||
use layout::context::LayoutContext;
|
use layout::context::LayoutContext;
|
||||||
use layout::float_context::FloatType;
|
use layout::float_context::FloatType;
|
||||||
use layout::flow::{BaseFlow, Flow, MutableFlowUtils};
|
use layout::flow::{BaseFlow, Flow, MutableOwnedFlowUtils};
|
||||||
use layout::inline::InlineFlow;
|
use layout::inline::InlineFlow;
|
||||||
use layout::text::TextRunScanner;
|
use layout::text::TextRunScanner;
|
||||||
use layout::util::LayoutDataAccess;
|
use layout::util::LayoutDataAccess;
|
||||||
|
@ -173,8 +173,6 @@ impl<T> OptVector<T> for Option<~[T]> {
|
||||||
/// An object that knows how to create flows.
|
/// An object that knows how to create flows.
|
||||||
pub struct FlowConstructor<'a> {
|
pub struct FlowConstructor<'a> {
|
||||||
/// The layout context.
|
/// The layout context.
|
||||||
///
|
|
||||||
/// FIXME(pcwalton): Why does this contain `@`??? That destroys parallelism!!!
|
|
||||||
layout_context: &'a mut LayoutContext,
|
layout_context: &'a mut LayoutContext,
|
||||||
|
|
||||||
/// The next flow ID to assign.
|
/// The next flow ID to assign.
|
||||||
|
@ -207,7 +205,7 @@ impl<'fc> FlowConstructor<'fc> {
|
||||||
Some(url) => {
|
Some(url) => {
|
||||||
// FIXME(pcwalton): The fact that image boxes store the cache within them makes
|
// FIXME(pcwalton): The fact that image boxes store the cache within them makes
|
||||||
// little sense to me.
|
// 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) {
|
fn flush_inline_boxes_to_flow(&mut self, boxes: ~[Box], flow: &mut ~Flow, node: LayoutNode) {
|
||||||
if boxes.len() > 0 {
|
if boxes.len() > 0 {
|
||||||
let inline_base = BaseFlow::new(self.next_flow_id(), node);
|
let inline_base = BaseFlow::new(self.next_flow_id(), node);
|
||||||
|
|
||||||
let mut inline_flow = ~InlineFlow::from_boxes(inline_base, boxes) as ~Flow;
|
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);
|
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,
|
self.flush_inline_boxes_to_flow_if_necessary(&mut opt_boxes_for_inline_flow,
|
||||||
flow,
|
flow,
|
||||||
node);
|
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(
|
ConstructionItemConstructionResult(InlineBoxesConstructionItem(
|
||||||
InlineBoxesConstructionResult {
|
InlineBoxesConstructionResult {
|
||||||
|
@ -322,7 +331,10 @@ impl<'fc> FlowConstructor<'fc> {
|
||||||
|
|
||||||
// Push the flow generated by the {ib} split onto our list of
|
// Push the flow generated by the {ib} split onto our list of
|
||||||
// flows.
|
// 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 base = BaseFlow::new(self.next_flow_id(), node);
|
||||||
let box_ = self.build_box_for_node(node);
|
let box_ = self.build_box_for_node(node);
|
||||||
let mut flow = ~BlockFlow::from_box(base, box_, is_fixed) as ~Flow;
|
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);
|
self.build_children_of_block_flow(&mut flow, node);
|
||||||
flow
|
flow
|
||||||
}
|
}
|
||||||
|
@ -358,7 +373,11 @@ impl<'fc> FlowConstructor<'fc> {
|
||||||
-> ~Flow {
|
-> ~Flow {
|
||||||
let base = BaseFlow::new(self.next_flow_id(), node);
|
let base = BaseFlow::new(self.next_flow_id(), node);
|
||||||
let box_ = self.build_box_for_node(node);
|
let box_ = self.build_box_for_node(node);
|
||||||
|
|
||||||
let mut flow = ~BlockFlow::float_from_box(base, float_type, box_) as ~Flow;
|
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);
|
self.build_children_of_block_flow(&mut flow, node);
|
||||||
flow
|
flow
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,18 +5,36 @@
|
||||||
//! Data needed by the layout task.
|
//! Data needed by the layout task.
|
||||||
|
|
||||||
use extra::arc::MutexArc;
|
use extra::arc::MutexArc;
|
||||||
use geom::rect::Rect;
|
use layout::flow::LeafSet;
|
||||||
|
|
||||||
|
use geom::size::Size2D;
|
||||||
use gfx::font_context::FontContext;
|
use gfx::font_context::FontContext;
|
||||||
use servo_msg::constellation_msg::ConstellationChan;
|
use servo_msg::constellation_msg::ConstellationChan;
|
||||||
use servo_net::local_image_cache::LocalImageCache;
|
use servo_net::local_image_cache::LocalImageCache;
|
||||||
use servo_util::geometry::Au;
|
use servo_util::geometry::Au;
|
||||||
|
|
||||||
/// Data needed by the layout task.
|
/// Data shared by all layout workers.
|
||||||
pub struct LayoutContext {
|
#[deriving(Clone)]
|
||||||
font_ctx: ~FontContext,
|
pub struct SharedLayoutInfo {
|
||||||
|
/// The local image cache.
|
||||||
image_cache: MutexArc<LocalImageCache>,
|
image_cache: MutexArc<LocalImageCache>,
|
||||||
screen_size: Rect<Au>,
|
|
||||||
|
/// The current screen size.
|
||||||
|
screen_size: Size2D<Au>,
|
||||||
|
|
||||||
/// A channel up to the constellation.
|
/// A channel up to the constellation.
|
||||||
constellation_chan: ConstellationChan,
|
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::float_context::{FloatContext, Invalid};
|
||||||
use layout::incremental::RestyleDamage;
|
use layout::incremental::RestyleDamage;
|
||||||
use layout::inline::InlineFlow;
|
use layout::inline::InlineFlow;
|
||||||
|
use layout::parallel::{FlowParallelInfo, UnsafeFlow};
|
||||||
|
use layout::parallel;
|
||||||
use layout::wrapper::LayoutNode;
|
use layout::wrapper::LayoutNode;
|
||||||
|
|
||||||
use extra::dlist::{DList, DListIterator, MutDListIterator};
|
use extra::dlist::{DList, DListIterator, MutDListIterator};
|
||||||
|
@ -43,6 +45,8 @@ use gfx::display_list::{ClipDisplayItemClass, DisplayList};
|
||||||
use servo_util::geometry::Au;
|
use servo_util::geometry::Au;
|
||||||
use std::cast;
|
use std::cast;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
|
use std::hashmap::{HashSet, HashSetIterator};
|
||||||
|
use std::sync::atomics::Relaxed;
|
||||||
use style::ComputedValues;
|
use style::ComputedValues;
|
||||||
use style::computed_values::text_align;
|
use style::computed_values::text_align;
|
||||||
|
|
||||||
|
@ -158,6 +162,9 @@ pub trait ImmutableFlowUtils {
|
||||||
/// Returns true if this flow has no children.
|
/// Returns true if this flow has no children.
|
||||||
fn is_leaf(self) -> bool;
|
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.
|
/// Returns true if this flow is a block flow, an inline flow, or a float flow.
|
||||||
fn starts_block_flow(self) -> bool;
|
fn starts_block_flow(self) -> bool;
|
||||||
|
|
||||||
|
@ -182,9 +189,6 @@ pub trait MutableFlowUtils {
|
||||||
|
|
||||||
// Mutators
|
// 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.
|
/// Invokes a closure with the first child of this flow.
|
||||||
fn with_first_child<R>(self, f: |Option<&mut ~Flow>| -> R) -> R;
|
fn with_first_child<R>(self, f: |Option<&mut ~Flow>| -> R) -> R;
|
||||||
|
|
||||||
|
@ -209,6 +213,12 @@ pub trait MutableFlowUtils {
|
||||||
-> bool;
|
-> 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 {
|
pub enum FlowClass {
|
||||||
BlockFlowClass,
|
BlockFlowClass,
|
||||||
InlineFlowClass,
|
InlineFlowClass,
|
||||||
|
@ -233,7 +243,7 @@ pub trait PostorderFlowTraversal {
|
||||||
fn process(&mut self, flow: &mut Flow) -> bool;
|
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
|
/// 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.
|
/// child nodes are visited.
|
||||||
fn should_process(&mut self, _flow: &mut Flow) -> bool {
|
fn should_process(&mut self, _flow: &mut Flow) -> bool {
|
||||||
true
|
true
|
||||||
|
@ -319,6 +329,9 @@ impl FlowFlags {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Data common to all flows.
|
/// 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 {
|
pub struct BaseFlow {
|
||||||
restyle_damage: RestyleDamage,
|
restyle_damage: RestyleDamage,
|
||||||
|
|
||||||
|
@ -342,6 +355,11 @@ pub struct BaseFlow {
|
||||||
/// pixels of all the display list items for correct invalidation.
|
/// pixels of all the display list items for correct invalidation.
|
||||||
overflow: Rect<Au>,
|
overflow: Rect<Au>,
|
||||||
|
|
||||||
|
/// Data used during parallel traversals.
|
||||||
|
///
|
||||||
|
/// TODO(pcwalton): Group with other transient data to save space.
|
||||||
|
parallel: FlowParallelInfo,
|
||||||
|
|
||||||
floats_in: FloatContext,
|
floats_in: FloatContext,
|
||||||
floats_out: FloatContext,
|
floats_out: FloatContext,
|
||||||
num_floats: uint,
|
num_floats: uint,
|
||||||
|
@ -383,6 +401,9 @@ impl BaseFlow {
|
||||||
pref_width: Au::new(0),
|
pref_width: Au::new(0),
|
||||||
position: Au::zero_rect(),
|
position: Au::zero_rect(),
|
||||||
overflow: Au::zero_rect(),
|
overflow: Au::zero_rect(),
|
||||||
|
|
||||||
|
parallel: FlowParallelInfo::new(),
|
||||||
|
|
||||||
floats_in: Invalid,
|
floats_in: Invalid,
|
||||||
floats_out: Invalid,
|
floats_out: Invalid,
|
||||||
num_floats: 0,
|
num_floats: 0,
|
||||||
|
@ -411,6 +432,11 @@ impl<'a> ImmutableFlowUtils for &'a Flow {
|
||||||
base(self).children.len() == 0
|
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.
|
/// Returns true if this flow is a block flow, an inline-block flow, or a float flow.
|
||||||
fn starts_block_flow(self) -> bool {
|
fn starts_block_flow(self) -> bool {
|
||||||
match self.class() {
|
match self.class() {
|
||||||
|
@ -484,11 +510,6 @@ impl<'a> MutableFlowUtils for &'a mut Flow {
|
||||||
traversal.process(self)
|
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.
|
/// Invokes a closure with the first child of this flow.
|
||||||
fn with_first_child<R>(self, f: |Option<&mut ~Flow>| -> R) -> R {
|
fn with_first_child<R>(self, f: |Option<&mut ~Flow>| -> R) -> R {
|
||||||
f(mut_base(self).children.front_mut())
|
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::select::new_stylist;
|
||||||
use css::node_style::StyledNode;
|
use css::node_style::StyledNode;
|
||||||
use layout::construct::{FlowConstructionResult, FlowConstructor, NoConstructionResult};
|
use layout::construct::{FlowConstructionResult, FlowConstructor, NoConstructionResult};
|
||||||
use layout::context::LayoutContext;
|
use layout::context::{LayoutContext, SharedLayoutInfo};
|
||||||
use layout::display_list_builder::{DisplayListBuilder, ToGfxColor};
|
use layout::display_list_builder::{DisplayListBuilder, ToGfxColor};
|
||||||
use layout::extra::LayoutAuxMethods;
|
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::{PostorderFlowTraversal};
|
||||||
use layout::flow;
|
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::util::{LayoutDataAccess, OpaqueNode, LayoutDataWrapper};
|
||||||
use layout::wrapper::LayoutNode;
|
use layout::wrapper::LayoutNode;
|
||||||
|
|
||||||
use extra::arc::{Arc, RWArc, MutexArc};
|
use extra::arc::{Arc, MutexArc, RWArc};
|
||||||
use geom::point::Point2D;
|
|
||||||
use geom::rect::Rect;
|
use geom::rect::Rect;
|
||||||
use geom::size::Size2D;
|
use geom::size::Size2D;
|
||||||
use gfx::display_list::{ClipDisplayItemClass, DisplayItem, DisplayItemIterator, DisplayList};
|
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::opts::Opts;
|
||||||
use gfx::render_task::{RenderMsg, RenderChan, RenderLayer};
|
use gfx::render_task::{RenderMsg, RenderChan, RenderLayer};
|
||||||
use gfx::{render_task, color};
|
use gfx::{render_task, color};
|
||||||
|
@ -78,14 +79,20 @@ pub struct LayoutTask {
|
||||||
/// The local image cache.
|
/// The local image cache.
|
||||||
local_image_cache: MutexArc<LocalImageCache>,
|
local_image_cache: MutexArc<LocalImageCache>,
|
||||||
|
|
||||||
|
/// The set of leaves in the flow tree.
|
||||||
|
leaf_set: MutexArc<LeafSet>,
|
||||||
|
|
||||||
/// The size of the viewport.
|
/// The size of the viewport.
|
||||||
screen_size: Option<Size2D<Au>>,
|
screen_size: Size2D<Au>,
|
||||||
|
|
||||||
/// A cached display list.
|
/// A cached display list.
|
||||||
display_list: Option<Arc<DisplayList<OpaqueNode>>>,
|
display_list: Option<Arc<DisplayList<OpaqueNode>>>,
|
||||||
|
|
||||||
stylist: RWArc<Stylist>,
|
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.
|
/// The channel on which messages can be sent to the profiler.
|
||||||
profiler_chan: ProfilerChan,
|
profiler_chan: ProfilerChan,
|
||||||
|
|
||||||
|
@ -135,7 +142,7 @@ impl PreorderFlowTraversal for PropagateDamageTraversal {
|
||||||
|
|
||||||
/// The bubble-widths traversal, the first part of layout computation. This computes preferred
|
/// The bubble-widths traversal, the first part of layout computation. This computes preferred
|
||||||
/// and intrinsic widths and bubbles them up the tree.
|
/// 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> {
|
impl<'a> PostorderFlowTraversal for BubbleWidthsTraversal<'a> {
|
||||||
#[inline]
|
#[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
|
/// 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
|
/// computation. Determines the final heights for all layout objects, computes positions, and
|
||||||
/// computes overflow regions. In Gecko this corresponds to `FinishAndStoreOverflow`.
|
/// 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> {
|
impl<'a> PostorderFlowTraversal for AssignHeightsAndStoreOverflowTraversal<'a> {
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -239,6 +246,18 @@ impl LayoutTask {
|
||||||
opts: &Opts,
|
opts: &Opts,
|
||||||
profiler_chan: ProfilerChan)
|
profiler_chan: ProfilerChan)
|
||||||
-> LayoutTask {
|
-> 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 {
|
LayoutTask {
|
||||||
id: id,
|
id: id,
|
||||||
|
@ -248,12 +267,13 @@ impl LayoutTask {
|
||||||
script_chan: script_chan,
|
script_chan: script_chan,
|
||||||
render_chan: render_chan,
|
render_chan: render_chan,
|
||||||
image_cache_task: image_cache_task.clone(),
|
image_cache_task: image_cache_task.clone(),
|
||||||
local_image_cache: MutexArc::new(LocalImageCache(image_cache_task)),
|
local_image_cache: local_image_cache,
|
||||||
screen_size: None,
|
screen_size: screen_size,
|
||||||
|
leaf_set: MutexArc::new(LeafSet::new()),
|
||||||
|
|
||||||
display_list: None,
|
display_list: None,
|
||||||
|
|
||||||
stylist: RWArc::new(new_stylist()),
|
stylist: RWArc::new(new_stylist()),
|
||||||
|
parallel_traversal: parallel_traversal,
|
||||||
profiler_chan: profiler_chan,
|
profiler_chan: profiler_chan,
|
||||||
opts: opts.clone()
|
opts: opts.clone()
|
||||||
}
|
}
|
||||||
|
@ -268,16 +288,20 @@ impl LayoutTask {
|
||||||
|
|
||||||
// Create a layout context for use in building display lists, hit testing, &c.
|
// Create a layout context for use in building display lists, hit testing, &c.
|
||||||
fn build_layout_context(&self) -> LayoutContext {
|
fn build_layout_context(&self) -> LayoutContext {
|
||||||
let image_cache = self.local_image_cache.clone();
|
let font_ctx = ~FontContext::new(FontContextInfo {
|
||||||
let font_ctx = ~FontContext::new(self.opts.render_backend, true,
|
backend: self.opts.render_backend,
|
||||||
self.profiler_chan.clone());
|
needs_font_list: true,
|
||||||
let screen_size = self.screen_size.unwrap();
|
profiler_chan: self.profiler_chan.clone(),
|
||||||
|
});
|
||||||
|
|
||||||
LayoutContext {
|
LayoutContext {
|
||||||
image_cache: image_cache,
|
shared: SharedLayoutInfo {
|
||||||
font_ctx: font_ctx,
|
image_cache: self.local_image_cache.clone(),
|
||||||
screen_size: Rect(Point2D(Au(0), Au(0)), screen_size),
|
screen_size: self.screen_size.clone(),
|
||||||
constellation_chan: self.constellation_chan.clone(),
|
constellation_chan: self.constellation_chan.clone(),
|
||||||
|
leaf_set: self.leaf_set.clone(),
|
||||||
|
},
|
||||||
|
font_ctx: font_ctx,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -344,6 +368,12 @@ impl LayoutTask {
|
||||||
/// crash.
|
/// crash.
|
||||||
fn exit_now(&mut self) {
|
fn exit_now(&mut self) {
|
||||||
let (response_port, response_chan) = Chan::new();
|
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));
|
self.render_chan.send(render_task::ExitMsg(response_chan));
|
||||||
response_port.recv()
|
response_port.recv()
|
||||||
}
|
}
|
||||||
|
@ -388,18 +418,46 @@ impl LayoutTask {
|
||||||
fn solve_constraints(&mut self,
|
fn solve_constraints(&mut self,
|
||||||
layout_root: &mut Flow,
|
layout_root: &mut Flow,
|
||||||
layout_context: &mut LayoutContext) {
|
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
|
// FIXME(kmc): We want to prune nodes without the Reflow restyle damage
|
||||||
// bit, but FloatContext values can't be reused, so we need to
|
// bit, but FloatContext values can't be reused, so we need to
|
||||||
// recompute them every time.
|
// recompute them every time.
|
||||||
// NOTE: this currently computes borders, so any pruning should separate that operation out.
|
// NOTE: this currently computes borders, so any pruning should separate that operation
|
||||||
let _ = layout_root.traverse_preorder(&mut AssignWidthsTraversal(layout_context));
|
// out.
|
||||||
|
layout_root.traverse_preorder(&mut AssignWidthsTraversal(layout_context));
|
||||||
|
|
||||||
// For now, this is an inorder traversal
|
// FIXME(pcwalton): Prune this pass as well.
|
||||||
// FIXME: prune this traversal as well
|
layout_root.traverse_postorder(&mut AssignHeightsAndStoreOverflowTraversal(
|
||||||
let _ = layout_root.traverse_postorder(&mut
|
layout_context));
|
||||||
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.
|
/// The high-level routine that performs layout tasks.
|
||||||
|
@ -416,8 +474,9 @@ impl LayoutTask {
|
||||||
|
|
||||||
// Reset the image cache.
|
// Reset the image cache.
|
||||||
unsafe {
|
unsafe {
|
||||||
self.local_image_cache.unsafe_access(
|
self.local_image_cache.unsafe_access(|local_image_cache| {
|
||||||
|cache| cache.next_round(self.make_on_image_available_cb()));
|
local_image_cache.next_round(self.make_on_image_available_cb())
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// true => Do the reflow with full style damage, because content
|
// true => Do the reflow with full style damage, because content
|
||||||
|
@ -427,12 +486,12 @@ impl LayoutTask {
|
||||||
_ => false
|
_ => false
|
||||||
};
|
};
|
||||||
|
|
||||||
let screen_size = Size2D(Au::from_px(data.window_size.width as int),
|
let current_screen_size = Size2D(Au::from_px(data.window_size.width as int),
|
||||||
Au::from_px(data.window_size.height as int));
|
Au::from_px(data.window_size.height as int));
|
||||||
if self.screen_size != Some(screen_size) {
|
if self.screen_size != current_screen_size {
|
||||||
all_style_damage = true;
|
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.
|
// Create a layout context for use throughout the following passes.
|
||||||
let mut layout_ctx = self.build_layout_context();
|
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
|
// Perform the primary layout passes over the flow tree to compute the locations of all
|
||||||
// the boxes.
|
// the boxes.
|
||||||
profile(time::LayoutMainCategory, self.profiler_chan.clone(), || {
|
profile(time::LayoutMainCategory, self.profiler_chan.clone(), || {
|
||||||
|
match self.parallel_traversal {
|
||||||
|
None => {
|
||||||
|
// Sequential mode.
|
||||||
self.solve_constraints(layout_root, &mut layout_ctx)
|
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.
|
// Build the display list if necessary, and send it to the renderer.
|
||||||
if data.goal == ReflowForDisplay {
|
if data.goal == ReflowForDisplay {
|
||||||
profile(time::LayoutDispListBuildCategory, self.profiler_chan.clone(), || {
|
profile(time::LayoutDispListBuildCategory, self.profiler_chan.clone(), || {
|
||||||
|
@ -519,10 +584,17 @@ impl LayoutTask {
|
||||||
|
|
||||||
self.display_list = Some(display_list.clone());
|
self.display_list = Some(display_list.clone());
|
||||||
|
|
||||||
|
debug!("Layout done!");
|
||||||
|
|
||||||
self.render_chan.send(RenderMsg(render_layer));
|
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.
|
// Tell script that we're done.
|
||||||
//
|
//
|
||||||
// FIXME(pcwalton): This should probably be *one* channel, but we can't fix this without
|
// FIXME(pcwalton): This should probably be *one* channel, but we can't fix this without
|
||||||
|
|
344
src/components/main/layout/parallel.rs
Normal file
344
src/components/main/layout/parallel.rs
Normal file
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -93,6 +93,7 @@ pub mod layout {
|
||||||
pub mod layout_task;
|
pub mod layout_task;
|
||||||
pub mod inline;
|
pub mod inline;
|
||||||
pub mod model;
|
pub mod model;
|
||||||
|
pub mod parallel;
|
||||||
pub mod text;
|
pub mod text;
|
||||||
pub mod util;
|
pub mod util;
|
||||||
pub mod incremental;
|
pub mod incremental;
|
||||||
|
|
|
@ -6,10 +6,10 @@ use image::base::Image;
|
||||||
use image_cache_task::{ImageReady, ImageNotReady, ImageFailed};
|
use image_cache_task::{ImageReady, ImageNotReady, ImageFailed};
|
||||||
use local_image_cache::LocalImageCache;
|
use local_image_cache::LocalImageCache;
|
||||||
|
|
||||||
use std::util::replace;
|
|
||||||
use geom::size::Size2D;
|
|
||||||
use extra::url::Url;
|
|
||||||
use extra::arc::{Arc, MutexArc};
|
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
|
// 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
|
// the network stack. This should probably be factored out into an interface and use dependency
|
||||||
|
@ -32,7 +32,7 @@ impl ImageHolder {
|
||||||
url: url,
|
url: url,
|
||||||
image: None,
|
image: None,
|
||||||
cached_size: Size2D(0,0),
|
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
|
// 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
|
// but they are intended to be spread out in time. Ideally prefetch
|
||||||
// should be done as early as possible and decode only once we
|
// should be done as early as possible and decode only once we
|
||||||
// are sure that the image will be used.
|
// are sure that the image will be used.
|
||||||
//
|
|
||||||
// LocalImageCache isn't Freeze so we have to use unsafe_access.
|
|
||||||
unsafe {
|
unsafe {
|
||||||
holder.local_image_cache.unsafe_access(|cache| {
|
holder.local_image_cache.unsafe_access(|local_image_cache| {
|
||||||
cache.prefetch(&holder.url);
|
local_image_cache.prefetch(&holder.url);
|
||||||
cache.decode(&holder.url);
|
local_image_cache.decode(&holder.url);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,8 +77,9 @@ impl ImageHolder {
|
||||||
// the image and store it for the future
|
// the image and store it for the future
|
||||||
if self.image.is_none() {
|
if self.image.is_none() {
|
||||||
let port = unsafe {
|
let port = unsafe {
|
||||||
self.local_image_cache.unsafe_access(
|
self.local_image_cache.unsafe_access(|local_image_cache| {
|
||||||
|cache| cache.get_image(&self.url))
|
local_image_cache.get_image(&self.url)
|
||||||
|
})
|
||||||
};
|
};
|
||||||
match port.recv() {
|
match port.recv() {
|
||||||
ImageReady(image) => {
|
ImageReady(image) => {
|
||||||
|
@ -96,9 +95,9 @@ impl ImageHolder {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clone isn't pure so we have to swap out the mutable image option
|
// 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();
|
let result = image.clone();
|
||||||
replace(&mut self.image, image);
|
util::replace(&mut self.image, image);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
@ -370,11 +370,11 @@ impl ImageCache {
|
||||||
match self.wait_map.pop(&url) {
|
match self.wait_map.pop(&url) {
|
||||||
Some(waiters) => {
|
Some(waiters) => {
|
||||||
unsafe {
|
unsafe {
|
||||||
waiters.unsafe_access( |waiters| {
|
waiters.unsafe_access(|waiters| {
|
||||||
for response in waiters.iter() {
|
for response in waiters.iter() {
|
||||||
response.send(f());
|
response.send(f());
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => ()
|
None => ()
|
||||||
|
@ -404,9 +404,7 @@ impl ImageCache {
|
||||||
let waiters = self.wait_map.find_mut(&url).unwrap();
|
let waiters = self.wait_map.find_mut(&url).unwrap();
|
||||||
let mut response = Some(response);
|
let mut response = Some(response);
|
||||||
unsafe {
|
unsafe {
|
||||||
waiters.unsafe_access(|waiters| {
|
waiters.unsafe_access(|waiters| waiters.push(response.take().unwrap()))
|
||||||
waiters.push(response.take().unwrap());
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.wait_map.insert(url, MutexArc::new(~[response]));
|
self.wait_map.insert(url, MutexArc::new(~[response]));
|
||||||
|
|
|
@ -52,6 +52,7 @@ pub enum ProfilerCategory {
|
||||||
LayoutSelectorMatchCategory,
|
LayoutSelectorMatchCategory,
|
||||||
LayoutTreeBuilderCategory,
|
LayoutTreeBuilderCategory,
|
||||||
LayoutMainCategory,
|
LayoutMainCategory,
|
||||||
|
LayoutParallelWarmupCategory,
|
||||||
LayoutShapingCategory,
|
LayoutShapingCategory,
|
||||||
LayoutDispListBuildCategory,
|
LayoutDispListBuildCategory,
|
||||||
GfxRegenAvailableFontsCategory,
|
GfxRegenAvailableFontsCategory,
|
||||||
|
@ -78,6 +79,7 @@ impl ProfilerCategory {
|
||||||
buckets.insert(LayoutSelectorMatchCategory, ~[]);
|
buckets.insert(LayoutSelectorMatchCategory, ~[]);
|
||||||
buckets.insert(LayoutTreeBuilderCategory, ~[]);
|
buckets.insert(LayoutTreeBuilderCategory, ~[]);
|
||||||
buckets.insert(LayoutMainCategory, ~[]);
|
buckets.insert(LayoutMainCategory, ~[]);
|
||||||
|
buckets.insert(LayoutParallelWarmupCategory, ~[]);
|
||||||
buckets.insert(LayoutShapingCategory, ~[]);
|
buckets.insert(LayoutShapingCategory, ~[]);
|
||||||
buckets.insert(LayoutDispListBuildCategory, ~[]);
|
buckets.insert(LayoutDispListBuildCategory, ~[]);
|
||||||
buckets.insert(GfxRegenAvailableFontsCategory, ~[]);
|
buckets.insert(GfxRegenAvailableFontsCategory, ~[]);
|
||||||
|
@ -93,7 +95,8 @@ impl ProfilerCategory {
|
||||||
pub fn format(self) -> ~str {
|
pub fn format(self) -> ~str {
|
||||||
let padding = match self {
|
let padding = match self {
|
||||||
LayoutAuxInitCategory | LayoutSelectorMatchCategory | LayoutTreeBuilderCategory |
|
LayoutAuxInitCategory | LayoutSelectorMatchCategory | LayoutTreeBuilderCategory |
|
||||||
LayoutMainCategory | LayoutDispListBuildCategory | LayoutShapingCategory=> " - ",
|
LayoutMainCategory | LayoutDispListBuildCategory | LayoutShapingCategory |
|
||||||
|
LayoutParallelWarmupCategory => " - ",
|
||||||
_ => ""
|
_ => ""
|
||||||
};
|
};
|
||||||
format!("{:s}{:?}", padding, self)
|
format!("{:s}{:?}", padding, self)
|
||||||
|
|
|
@ -19,3 +19,4 @@ pub mod vec;
|
||||||
pub mod debug;
|
pub mod debug;
|
||||||
pub mod io;
|
pub mod io;
|
||||||
pub mod task;
|
pub mod task;
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue