Add a layout debug module. This outputs a trace of the layout process to a JSON

file which can be viewed in an external tool. It provides a timelapse view of
how the flow tree and fragments changed during the layout process, which makes
it easier to debug layout bugs.
This commit is contained in:
Glenn Watson 2014-09-03 11:52:40 +10:00
parent 940c013176
commit acedb16670
16 changed files with 261 additions and 16 deletions

View file

@ -66,6 +66,7 @@ pub extern "C" fn cef_run_message_loop() {
bubble_inline_sizes_separately: false,
show_debug_borders: false,
enable_text_antialiasing: true,
trace_layout: false,
};
native::start(0, 0 as *const *const u8, proc() {
servo::run(opts);

View file

@ -21,6 +21,7 @@ extern crate native;
extern crate rustrt;
extern crate stb_image;
extern crate png;
extern crate serialize;
#[phase(plugin)]
extern crate servo_macros = "macros";
extern crate servo_net = "net";

View file

@ -511,6 +511,7 @@ pub struct GlyphStore {
}
int_range_index! {
#[deriving(Encodable)]
#[doc = "An index that refers to a character in a text run. This could \
point to the middle of a glyph."]
struct CharIndex(int)

View file

@ -21,6 +21,7 @@ use flow::{BaseFlow, BlockFlowClass, FlowClass, Flow, ImmutableFlowUtils};
use flow::{MutableFlowUtils, PreorderFlowTraversal, PostorderFlowTraversal, mut_base};
use flow;
use fragment::{Fragment, ImageFragment, ScannedTextFragment};
use layout_debug;
use model::{Auto, IntrinsicISizes, MarginCollapseInfo, MarginsCollapse};
use model::{MarginsCollapseThrough, MaybeAuto, NoCollapsibleMargins, Specified, specified};
use model::{specified_or_none};
@ -48,6 +49,7 @@ use style::computed_values::{display, float, overflow};
use sync::Arc;
/// Information specific to floated blocks.
#[deriving(Encodable)]
pub struct FloatedBlockInfo {
pub containing_inline_size: Au,
@ -492,6 +494,7 @@ fn propagate_layer_flag_from_child(layers_needed_for_descendants: &mut bool, kid
}
// A block formatting context.
#[deriving(Encodable)]
pub struct BlockFlow {
/// Data common to all flows.
pub base: BaseFlow,
@ -808,6 +811,8 @@ impl BlockFlow {
pub fn assign_block_size_block_base<'a>(&mut self,
layout_context: &'a LayoutContext<'a>,
margins_may_collapse: MarginsMayCollapseFlag) {
let _scope = layout_debug::Scope::new(format!("assign_block_size_block_base {:s}", self.base.debug_id()));
// Our current border-box position.
let mut cur_b = Au(0);
@ -1054,6 +1059,8 @@ impl BlockFlow {
///
/// It does not calculate the block-size of the flow itself.
pub fn assign_block_size_float<'a>(&mut self, ctx: &'a LayoutContext<'a>) {
let _scope = layout_debug::Scope::new(format!("assign_block_size_float {:s}", self.base.debug_id()));
let mut floats = Floats::new(self.fragment.style.writing_mode);
for kid in self.base.child_iter() {
flow::mut_base(kid).floats = floats.clone();
@ -1432,6 +1439,10 @@ impl Flow for BlockFlow {
self
}
fn as_immutable_block<'a>(&'a self) -> &'a BlockFlow {
self
}
/// Returns the direction that this flow clears floats in, if any.
fn float_clearance(&self) -> clear::T {
self.fragment.style().get_box().clear
@ -1446,6 +1457,8 @@ impl Flow for BlockFlow {
///
/// TODO(pcwalton): Inline blocks.
fn bubble_inline_sizes(&mut self, _: &LayoutContext) {
let _scope = layout_debug::Scope::new(format!("block::bubble_inline_sizes {:s}", self.base.debug_id()));
let mut flags = self.base.flags;
flags.set_has_left_floated_descendants(false);
flags.set_has_right_floated_descendants(false);
@ -1490,6 +1503,8 @@ impl Flow for BlockFlow {
/// Dual fragments consume some inline-size first, and the remainder is assigned to all child (block)
/// contexts.
fn assign_inline_sizes(&mut self, layout_context: &LayoutContext) {
let _scope = layout_debug::Scope::new(format!("block::assign_inline_sizes {:s}", self.base.debug_id()));
debug!("assign_inline_sizes({}): assigning inline_size for flow",
if self.is_float() {
"float"

View file

@ -11,7 +11,7 @@ use style::computed_values::float;
use sync::Arc;
/// The kind of float: left or right.
#[deriving(Clone)]
#[deriving(Clone, Encodable)]
pub enum FloatKind {
FloatLeft,
FloatRight

View file

@ -49,6 +49,7 @@ use collections::dlist::DList;
use geom::Point2D;
use gfx::display_list::DisplayList;
use gfx::render_task::RenderLayer;
use serialize::{Encoder, Encodable};
use servo_msg::compositor_msg::LayerId;
use servo_util::geometry::Au;
use servo_util::logical_geometry::WritingMode;
@ -74,6 +75,12 @@ pub trait Flow: fmt::Show + ToString + Share {
/// Returns the class of flow that this is.
fn class(&self) -> FlowClass;
/// If this is a block flow, returns the underlying object, borrowed immutably. Fails
/// otherwise.
fn as_immutable_block<'a>(&'a self) -> &'a BlockFlow {
fail!("called as_immutable_block() on a non-block flow")
}
/// If this is a block flow, returns the underlying object. Fails otherwise.
fn as_block<'a>(&'a mut self) -> &'a mut BlockFlow {
debug!("called as_block() on a flow of type {}", self.class());
@ -270,6 +277,21 @@ pub trait Flow: fmt::Show + ToString + Share {
}
}
impl<'a, E, S: Encoder<E>> Encodable<S, E> for &'a Flow {
fn encode(&self, e: &mut S) -> Result<(), E> {
e.emit_struct("flow", 0, |e| {
try!(e.emit_struct_field("class", 0, |e| self.class().encode(e)))
e.emit_struct_field("data", 1, |e| {
match self.class() {
BlockFlowClass => self.as_immutable_block().encode(e),
InlineFlowClass => self.as_immutable_inline().encode(e),
_ => { Ok(()) } // TODO: Support tables
}
})
})
}
}
// Base access
#[inline(always)]
@ -381,7 +403,7 @@ pub trait MutableOwnedFlowUtils {
fn set_abs_descendants(&mut self, abs_descendants: AbsDescendants);
}
#[deriving(PartialEq, Show)]
#[deriving(Encodable, PartialEq, Show)]
pub enum FlowClass {
BlockFlowClass,
InlineFlowClass,
@ -428,7 +450,7 @@ pub trait PostorderFlowTraversal {
}
/// Flags used in flows, tightly packed to save space.
#[deriving(Clone)]
#[deriving(Clone, Encodable)]
pub struct FlowFlags(pub u8);
/// The bitmask of flags that represent the `has_left_floated_descendants` and
@ -595,6 +617,7 @@ pub type DescendantOffsetIter<'a> = Zip<DescendantIter<'a>, MutItems<'a, Au>>;
/// Information needed to compute absolute (i.e. viewport-relative) flow positions (not to be
/// confused with absolutely-positioned flows).
#[deriving(Encodable)]
pub struct AbsolutePositionInfo {
/// The size of the containing block for relatively-positioned descendants.
pub relative_containing_block_size: LogicalSize<Au>,
@ -697,6 +720,26 @@ pub struct BaseFlow {
pub writing_mode: WritingMode,
}
impl<E, S: Encoder<E>> Encodable<S, E> for BaseFlow {
fn encode(&self, e: &mut S) -> Result<(), E> {
e.emit_struct("base", 0, |e| {
try!(e.emit_struct_field("id", 0, |e| self.debug_id().encode(e)))
try!(e.emit_struct_field("abs_position", 1, |e| self.abs_position.encode(e)))
try!(e.emit_struct_field("intrinsic_inline_sizes", 2, |e| self.intrinsic_inline_sizes.encode(e)))
try!(e.emit_struct_field("position", 3, |e| self.position.encode(e)))
e.emit_struct_field("children", 4, |e| {
e.emit_seq(self.children.len(), |e| {
for (i, c) in self.children.iter().enumerate() {
try!(e.emit_seq_elt(i, |e| c.encode(e)))
}
Ok(())
})
})
})
}
}
#[unsafe_destructor]
impl Drop for BaseFlow {
fn drop(&mut self) {
@ -748,6 +791,10 @@ impl BaseFlow {
pub unsafe fn ref_count<'a>(&'a self) -> &'a AtomicUint {
&self.ref_count
}
pub fn debug_id(&self) -> String {
format!("{:p}", self as *const _)
}
}
impl<'a> ImmutableFlowUtils for &'a Flow {

View file

@ -13,6 +13,7 @@ use floats::{ClearBoth, ClearLeft, ClearRight, ClearType};
use flow::Flow;
use flow;
use inline::{InlineFragmentContext, InlineMetrics};
use layout_debug;
use model::{Auto, IntrinsicISizes, MaybeAuto, Specified, specified};
use model;
use text;
@ -33,6 +34,7 @@ use gfx::display_list::{Upright, SidewaysLeft, SidewaysRight};
use gfx::font::FontStyle;
use gfx::text::glyph::CharIndex;
use gfx::text::text_run::TextRun;
use serialize::{Encodable, Encoder};
use servo_msg::constellation_msg::{ConstellationChan, FrameRectMsg, PipelineId, SubpageId};
use servo_net::image::holder::ImageHolder;
use servo_net::local_image_cache::LocalImageCache;
@ -104,6 +106,20 @@ pub struct Fragment {
/// Holds the style context information for fragments
/// that are part of an inline formatting context.
pub inline_context: Option<InlineFragmentContext>,
/// A debug ID that is consistent for the life of
/// this fragment (via transform etc).
pub debug_id: uint,
}
impl<E, S: Encoder<E>> Encodable<S, E> for Fragment {
fn encode(&self, e: &mut S) -> Result<(), E> {
e.emit_struct("fragment", 0, |e| {
try!(e.emit_struct_field("id", 0, |e| self.debug_id().encode(e)))
try!(e.emit_struct_field("border_box", 1, |e| self.border_box.encode(e)))
e.emit_struct_field("margin", 2, |e| self.margin.encode(e))
})
}
}
/// Info specific to the kind of fragment. Keep this enum small.
@ -336,6 +352,7 @@ impl Fragment {
specific: constructor.build_specific_fragment_info_for_node(node),
new_line_pos: vec!(),
inline_context: None,
debug_id: layout_debug::generate_unique_debug_id(),
}
}
@ -352,6 +369,7 @@ impl Fragment {
specific: specific,
new_line_pos: vec!(),
inline_context: None,
debug_id: layout_debug::generate_unique_debug_id(),
}
}
@ -377,6 +395,7 @@ impl Fragment {
specific: specific,
new_line_pos: vec!(),
inline_context: None,
debug_id: layout_debug::generate_unique_debug_id(),
}
}
@ -395,13 +414,14 @@ impl Fragment {
specific: specific,
new_line_pos: vec!(),
inline_context: None,
debug_id: layout_debug::generate_unique_debug_id(),
}
}
/// Returns a debug ID of this fragment. This ID should not be considered stable across multiple
/// layouts or fragment manipulations.
pub fn debug_id(&self) -> uint {
self as *const Fragment as uint
self.debug_id
}
/// Transforms this fragment into another fragment of the given type, with the given size, preserving all
@ -417,6 +437,7 @@ impl Fragment {
specific: specific,
new_line_pos: self.new_line_pos.clone(),
inline_context: self.inline_context.clone(),
debug_id: self.debug_id,
}
}

View file

@ -10,6 +10,7 @@ use floats::{FloatLeft, Floats, PlacementInfo};
use flow::{BaseFlow, FlowClass, Flow, InlineFlowClass};
use flow;
use fragment::{Fragment, ScannedTextFragment, ScannedTextFragmentInfo, SplitInfo};
use layout_debug;
use model::IntrinsicISizes;
use text;
use wrapper::ThreadSafeLayoutNode;
@ -58,6 +59,7 @@ use sync::Arc;
/// with a float or a horizontal wall of the containing block. The block-start
/// inline-start corner of the green zone is the same as that of the line, but
/// the green zone can be taller and wider than the line itself.
#[deriving(Encodable)]
pub struct Line {
/// A range of line indices that describe line breaks.
///
@ -140,6 +142,7 @@ pub struct Line {
}
int_range_index! {
#[deriving(Encodable)]
#[doc = "The index of a fragment in a flattened vector of DOM elements."]
struct FragmentIndex(int)
}
@ -147,7 +150,7 @@ int_range_index! {
/// A line index consists of two indices: a fragment index that refers to the
/// index of a DOM fragment within a flattened inline element; and a glyph index
/// where the 0th glyph refers to the first glyph of that fragment.
#[deriving(Clone, PartialEq, PartialOrd, Eq, Ord, Zero)]
#[deriving(Clone, Encodable, PartialEq, PartialOrd, Eq, Ord, Zero)]
pub struct LineIndices {
/// The index of a fragment into the flattened vector of DOM elements.
///
@ -625,6 +628,7 @@ impl LineBreaker {
}
/// Represents a list of inline fragments, including element ranges.
#[deriving(Encodable)]
pub struct InlineFragments {
/// The fragments themselves.
pub fragments: Vec<Fragment>,
@ -709,6 +713,7 @@ impl InlineFragments {
}
/// Flows for inline layout.
#[deriving(Encodable)]
pub struct InlineFlow {
/// Data common to all flows.
pub base: BaseFlow,
@ -902,6 +907,8 @@ impl Flow for InlineFlow {
}
fn bubble_inline_sizes(&mut self, _: &LayoutContext) {
let _scope = layout_debug::Scope::new(format!("inline::bubble_inline_sizes {:s}", self.base.debug_id()));
let writing_mode = self.base.writing_mode;
for kid in self.base.child_iter() {
flow::mut_base(kid).floats = Floats::new(writing_mode);
@ -927,6 +934,8 @@ impl Flow for InlineFlow {
/// Recursively (top-down) determines the actual inline-size of child contexts and fragments. When called
/// on this context, the context has had its inline-size set by the parent context.
fn assign_inline_sizes(&mut self, _: &LayoutContext) {
let _scope = layout_debug::Scope::new(format!("inline::assign_inline_sizes {:s}", self.base.debug_id()));
// Initialize content fragment inline-sizes if they haven't been initialized already.
//
// TODO: Combine this with `LineBreaker`'s walk in the fragment list, or put this into `Fragment`.
@ -955,6 +964,7 @@ impl Flow for InlineFlow {
/// Calculate and set the block-size of this flow. See CSS 2.1 § 10.6.1.
fn assign_block_size(&mut self, ctx: &LayoutContext) {
let _scope = layout_debug::Scope::new(format!("inline::assign_block_size {:s}", self.base.debug_id()));
debug!("assign_block_size_inline: assigning block_size for flow");
// Divide the fragments into lines.

View file

@ -20,6 +20,7 @@ extern crate gfx;
extern crate layout_traits;
extern crate script;
extern crate script_traits;
extern crate serialize;
extern crate style;
#[phase(plugin)]
extern crate servo_macros = "macros";
@ -42,6 +43,7 @@ pub mod flow;
pub mod flow_list;
pub mod flow_ref;
pub mod fragment;
pub mod layout_debug;
pub mod layout_task;
pub mod inline;
pub mod model;

View file

@ -0,0 +1,112 @@
/* 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/. */
//! Supports writing a trace file created during each layout scope
//! that can be viewed by an external tool to make layout debugging easier.
use flow_ref::FlowRef;
use serialize::json;
use std::cell::RefCell;
use std::io::File;
use std::sync::atomics::{AtomicUint, SeqCst, INIT_ATOMIC_UINT};
local_data_key!(state_key: RefCell<State>)
static mut DEBUG_ID_COUNTER: AtomicUint = INIT_ATOMIC_UINT;
pub struct Scope;
#[deriving(Encodable)]
struct ScopeData {
name: String,
pre: String,
post: String,
children: Vec<Box<ScopeData>>,
}
impl ScopeData {
fn new(name: String, pre: String) -> ScopeData {
ScopeData {
name: name,
pre: pre,
post: String::new(),
children: vec!(),
}
}
}
struct State {
flow_root: FlowRef,
scope_stack: Vec<Box<ScopeData>>,
}
/// A layout debugging scope. The entire state of the flow tree
/// will be output at the beginning and end of this scope.
impl Scope {
pub fn new(name: String) -> Scope {
let maybe_refcell = state_key.get();
match maybe_refcell {
Some(refcell) => {
let mut state = refcell.borrow_mut();
let flow_trace = json::encode(&state.flow_root.get());
let data = box ScopeData::new(name, flow_trace);
state.scope_stack.push(data);
}
None => {}
}
Scope
}
}
impl Drop for Scope {
fn drop(&mut self) {
let maybe_refcell = state_key.get();
match maybe_refcell {
Some(refcell) => {
let mut state = refcell.borrow_mut();
let mut current_scope = state.scope_stack.pop().unwrap();
current_scope.post = json::encode(&state.flow_root.get());
let previous_scope = state.scope_stack.mut_last().unwrap();
previous_scope.children.push(current_scope);
}
None => {}
}
}
}
/// Generate a unique ID. This is used for items such as Fragment
/// which are often reallocated but represent essentially the
/// same data.
pub fn generate_unique_debug_id() -> uint {
unsafe { DEBUG_ID_COUNTER.fetch_add(1, SeqCst) }
}
/// Begin a layout debug trace. If this has not been called,
/// creating debug scopes has no effect.
pub fn begin_trace(flow_root: FlowRef) {
assert!(state_key.get().is_none());
let flow_trace = json::encode(&flow_root.get());
let state = State {
scope_stack: vec![box ScopeData::new("root".to_string(), flow_trace)],
flow_root: flow_root,
};
state_key.replace(Some(RefCell::new(state)));
}
/// End the debug layout trace. This will write the layout
/// trace to disk in the current directory. The output
/// file can then be viewed with an external tool.
pub fn end_trace() {
let task_state_cell = state_key.replace(None).unwrap();
let mut task_state = task_state_cell.borrow_mut();
assert!(task_state.scope_stack.len() == 1);
let mut root_scope = task_state.scope_stack.pop().unwrap();
root_scope.post = json::encode(&task_state.flow_root.get());
let result = json::encode(&root_scope);
let path = Path::new("layout_trace.json");
let mut file = File::create(&path).unwrap();
file.write_str(result.as_slice()).unwrap();
}

View file

@ -14,6 +14,7 @@ use flow::{PreorderFlowTraversal, PostorderFlowTraversal};
use flow;
use flow_ref::FlowRef;
use incremental::RestyleDamage;
use layout_debug;
use parallel::UnsafeFlow;
use parallel;
use util::{LayoutDataAccess, LayoutDataWrapper, OpaqueNodeMethods, ToGfxColor};
@ -530,6 +531,8 @@ impl LayoutTask {
fn solve_constraints<'a>(&mut self,
layout_root: &mut Flow,
layout_context: &'a LayoutContext<'a>) {
let _scope = layout_debug::Scope::new("solve_constraints".to_string());
if layout_context.shared.opts.bubble_inline_sizes_separately {
let mut traversal = BubbleISizesTraversal {
layout_context: layout_context,
@ -667,6 +670,10 @@ impl LayoutTask {
// memory safety but is a useful debugging tool.)
self.verify_flow_tree(&mut layout_root);
if self.opts.trace_layout {
layout_debug::begin_trace(layout_root.clone());
}
// Propagate damage.
profile(time::LayoutDamagePropagateCategory, self.time_profiler_chan.clone(), || {
layout_root.get_mut().traverse_preorder(&mut PropagateDamageTraversal {
@ -778,6 +785,10 @@ impl LayoutTask {
});
}
if self.opts.trace_layout {
layout_debug::end_trace();
}
// Tell script that we're done.
//
// FIXME(pcwalton): This should probably be *one* channel, but we can't fix this without

View file

@ -243,6 +243,7 @@ pub enum MarginCollapseState {
}
/// Intrinsic inline-sizes, which consist of minimum and preferred.
#[deriving(Encodable)]
pub struct IntrinsicISizes {
/// The *minimum inline-size* of the content.
pub minimum_inline_size: Au,

View file

@ -7,6 +7,7 @@ use geom::point::Point2D;
use geom::rect::Rect;
use geom::size::Size2D;
use serialize::{Encodable, Encoder};
use std::default::Default;
use std::num::{NumCast, One, Zero};
use std::fmt;
@ -71,6 +72,12 @@ impl Default for Au {
}
}
impl<E, S: Encoder<E>> Encodable<S, E> for Au {
fn encode(&self, e: &mut S) -> Result<(), E> {
e.emit_f64(to_frac_px(*self))
}
}
impl fmt::Show for Au {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}px", to_frac_px(*self))

View file

@ -9,8 +9,8 @@ use std::cmp::{min, max};
use std::fmt::{Show, Formatter, FormatError};
use std::num::Zero;
bitflags!(
#[deriving(Encodable)]
flags WritingMode: u8 {
static FlagRTL = 1 << 0,
static FlagVertical = 1 << 1,
@ -79,11 +79,11 @@ impl Show for WritingMode {
/// (in addition to taking it as a parameter to methods) and check it.
/// In non-debug builds, make this storage zero-size and the checks no-ops.
#[cfg(ndebug)]
#[deriving(PartialEq, Eq, Clone)]
#[deriving(Encodable, PartialEq, Eq, Clone)]
struct DebugWritingMode;
#[cfg(not(ndebug))]
#[deriving(PartialEq, Eq, Clone)]
#[deriving(Encodable, PartialEq, Eq, Clone)]
struct DebugWritingMode {
mode: WritingMode
}
@ -134,7 +134,7 @@ impl Show for DebugWritingMode {
/// A 2D size in flow-relative dimensions
#[deriving(PartialEq, Eq, Clone)]
#[deriving(Encodable, PartialEq, Eq, Clone)]
pub struct LogicalSize<T> {
pub inline: T, // inline-size, a.k.a. logical width, a.k.a. measure
pub block: T, // block-size, a.k.a. logical height, a.k.a. extent
@ -271,7 +271,7 @@ impl<T: Sub<T, T>> Sub<LogicalSize<T>, LogicalSize<T>> for LogicalSize<T> {
/// A 2D point in flow-relative dimensions
#[deriving(PartialEq, Eq, Clone)]
#[deriving(PartialEq, Encodable, Eq, Clone)]
pub struct LogicalPoint<T> {
pub i: T, /// inline-axis coordinate
pub b: T, /// block-axis coordinate
@ -444,7 +444,7 @@ impl<T: Sub<T,T>> Sub<LogicalSize<T>, LogicalPoint<T>> for LogicalPoint<T> {
/// Represents the four sides of the margins, borders, or padding of a CSS box,
/// or a combination of those.
/// A positive "margin" can be added to a rectangle to obtain a bigger rectangle.
#[deriving(PartialEq, Eq, Clone)]
#[deriving(Encodable, PartialEq, Eq, Clone)]
pub struct LogicalMargin<T> {
pub block_start: T,
pub inline_end: T,
@ -726,7 +726,7 @@ impl<T: Sub<T, T>> Sub<LogicalMargin<T>, LogicalMargin<T>> for LogicalMargin<T>
/// A rectangle in flow-relative dimensions
#[deriving(PartialEq, Eq, Clone)]
#[deriving(Encodable, PartialEq, Eq, Clone)]
pub struct LogicalRect<T> {
pub start: LogicalPoint<T>,
pub size: LogicalSize<T>,

View file

@ -78,6 +78,11 @@ pub struct Opts {
/// where pixel perfect results are required when using fonts such as the Ahem
/// font for layout tests.
pub enable_text_antialiasing: bool,
/// True if each step of layout is traced to an external JSON file
/// for debugging purposes. Settings this implies sequential layout
/// and render.
pub trace_layout: bool,
}
fn print_usage(app: &str, opts: &[getopts::OptGroup]) {
@ -111,6 +116,7 @@ pub fn from_cmdline_args(args: &[String]) -> Option<Opts> {
getopts::optflag("b", "bubble-widths", "Bubble intrinsic widths separately like other engines"),
getopts::optflag("", "show-debug-borders", "Show debugging borders on layers and tiles."),
getopts::optflag("", "disable-text-aa", "Disable antialiasing for text rendering."),
getopts::optflag("", "trace-layout", "Write layout trace to external file for debugging."),
getopts::optflag("h", "help", "Print this message")
);
@ -163,7 +169,7 @@ pub fn from_cmdline_args(args: &[String]) -> Option<Opts> {
ScaleFactor(from_str(dppx_str.as_slice()).unwrap())
);
let n_render_threads: uint = match opt_match.opt_str("t") {
let mut n_render_threads: uint = match opt_match.opt_str("t") {
Some(n_render_threads_str) => from_str(n_render_threads_str.as_slice()).unwrap(),
None => 1, // FIXME: Number of cores.
};
@ -178,11 +184,20 @@ pub fn from_cmdline_args(args: &[String]) -> Option<Opts> {
let cpu_painting = opt_match.opt_present("c");
let layout_threads: uint = match opt_match.opt_str("y") {
let mut layout_threads: uint = match opt_match.opt_str("y") {
Some(layout_threads_str) => from_str(layout_threads_str.as_slice()).unwrap(),
None => cmp::max(rt::default_sched_threads() * 3 / 4, 1),
};
let mut bubble_inline_sizes_separately = opt_match.opt_present("b");
let trace_layout = opt_match.opt_present("trace-layout");
if trace_layout {
n_render_threads = 1;
layout_threads = 1;
bubble_inline_sizes_separately = true;
}
Some(Opts {
urls: urls,
render_backend: render_backend,
@ -198,9 +213,10 @@ pub fn from_cmdline_args(args: &[String]) -> Option<Opts> {
output_file: opt_match.opt_str("o"),
headless: opt_match.opt_present("z"),
hard_fail: opt_match.opt_present("f"),
bubble_inline_sizes_separately: opt_match.opt_present("b"),
bubble_inline_sizes_separately: bubble_inline_sizes_separately,
show_debug_borders: opt_match.opt_present("show-debug-borders"),
enable_text_antialiasing: !opt_match.opt_present("disable-text-aa"),
trace_layout: trace_layout,
})
}

View file

@ -100,7 +100,7 @@ pub enum RangeRelation<I> {
}
/// A range of indices
#[deriving(Clone)]
#[deriving(Clone, Encodable)]
pub struct Range<I> {
begin: I,
length: I,