diff options
-rw-r--r-- | Cargo.lock | 1 | ||||
-rw-r--r-- | components/layout_2020/Cargo.toml | 1 | ||||
-rw-r--r-- | components/layout_2020/flow/root.rs | 1 | ||||
-rw-r--r-- | components/layout_2020/fragments.rs | 40 | ||||
-rw-r--r-- | components/layout_2020/geom.rs | 6 | ||||
-rw-r--r-- | components/layout_2020/layout_debug.rs | 110 | ||||
-rw-r--r-- | components/layout_2020/lib.rs | 5 | ||||
-rw-r--r-- | components/layout_thread_2020/lib.rs | 35 |
8 files changed, 186 insertions, 13 deletions
diff --git a/Cargo.lock b/Cargo.lock index 83d69e12ebc..d5d51efdc46 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2927,6 +2927,7 @@ dependencies = [ "script_layout_interface", "script_traits", "serde", + "serde_json", "servo_arc", "servo_geometry", "servo_url", diff --git a/components/layout_2020/Cargo.toml b/components/layout_2020/Cargo.toml index d75d12e20c8..8d4a6d62f63 100644 --- a/components/layout_2020/Cargo.toml +++ b/components/layout_2020/Cargo.toml @@ -33,6 +33,7 @@ rayon_croissant = "0.2.0" script_layout_interface = {path = "../script_layout_interface"} script_traits = {path = "../script_traits"} serde = "1.0" +serde_json = "1.0" servo_arc = { path = "../servo_arc" } servo_geometry = {path = "../geometry"} servo_url = {path = "../url"} diff --git a/components/layout_2020/flow/root.rs b/components/layout_2020/flow/root.rs index 028dfa317d1..ed880a3d9ce 100644 --- a/components/layout_2020/flow/root.rs +++ b/components/layout_2020/flow/root.rs @@ -30,6 +30,7 @@ use style_traits::CSSPixel; pub struct BoxTreeRoot(BlockFormattingContext); +#[derive(Serialize)] pub struct FragmentTreeRoot { /// The children of the root of the fragment tree. children: Vec<Fragment>, diff --git a/components/layout_2020/fragments.rs b/components/layout_2020/fragments.rs index c46b332dec5..87b362c14f0 100644 --- a/components/layout_2020/fragments.rs +++ b/components/layout_2020/fragments.rs @@ -6,6 +6,7 @@ use crate::geom::flow_relative::{Rect, Sides, Vec2}; use crate::geom::{PhysicalPoint, PhysicalRect}; use gfx::text::glyph::GlyphStore; use gfx_traits::print_tree::PrintTree; +use serde::ser::{Serialize, SerializeStruct, Serializer}; use servo_arc::Arc as ServoArc; use std::sync::Arc; use style::computed_values::overflow_x::T as ComputedOverflow; @@ -16,6 +17,7 @@ use style::values::computed::Length; use style::Zero; use webrender_api::{FontInstanceKey, ImageKey}; +#[derive(Serialize)] pub(crate) enum Fragment { Box(BoxFragment), Anonymous(AnonymousFragment), @@ -43,19 +45,21 @@ pub(crate) struct BoxFragment { pub scrollable_overflow_from_children: PhysicalRect<Length>, } +#[derive(Serialize)] pub(crate) struct CollapsedBlockMargins { pub collapsed_through: bool, pub start: CollapsedMargin, pub end: CollapsedMargin, } -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Serialize)] pub(crate) struct CollapsedMargin { max_positive: Length, min_negative: Length, } /// Can contain child fragments with relative coordinates, but does not contribute to painting itself. +#[derive(Serialize)] pub(crate) struct AnonymousFragment { pub rect: Rect<Length>, pub children: Vec<Fragment>, @@ -342,3 +346,37 @@ impl CollapsedMargin { self.max_positive + self.min_negative } } + +impl Serialize for BoxFragment { + fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> { + let mut serializer = serializer.serialize_struct("BoxFragment", 6)?; + serializer.serialize_field("content_rect", &self.content_rect)?; + serializer.serialize_field("padding", &self.padding)?; + serializer.serialize_field("border", &self.border)?; + serializer.serialize_field("margin", &self.margin)?; + serializer.serialize_field( + "block_margins_collapsed_with_children", + &self.block_margins_collapsed_with_children, + )?; + serializer.serialize_field("children", &self.children)?; + serializer.end() + } +} + +impl Serialize for TextFragment { + fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> { + let mut serializer = serializer.serialize_struct("TextFragment", 3)?; + serializer.serialize_field("rect", &self.rect)?; + serializer.serialize_field("ascent", &self.ascent)?; + serializer.serialize_field("glyphs", &self.glyphs)?; + serializer.end() + } +} + +impl Serialize for ImageFragment { + fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> { + let mut serializer = serializer.serialize_struct("ImageFragment", 1)?; + serializer.serialize_field("rect", &self.rect)?; + serializer.end() + } +} diff --git a/components/layout_2020/geom.rs b/components/layout_2020/geom.rs index a3b60bd5cef..5499836c004 100644 --- a/components/layout_2020/geom.rs +++ b/components/layout_2020/geom.rs @@ -18,19 +18,19 @@ pub type PhysicalRect<U> = euclid::Rect<U, CSSPixel>; pub type PhysicalSides<U> = euclid::SideOffsets2D<U, CSSPixel>; pub(crate) mod flow_relative { - #[derive(Clone)] + #[derive(Clone, Serialize)] pub(crate) struct Vec2<T> { pub inline: T, pub block: T, } - #[derive(Clone)] + #[derive(Clone, Serialize)] pub(crate) struct Rect<T> { pub start_corner: Vec2<T>, pub size: Vec2<T>, } - #[derive(Clone, Debug)] + #[derive(Clone, Serialize)] pub(crate) struct Sides<T> { pub inline_start: T, pub inline_end: T, diff --git a/components/layout_2020/layout_debug.rs b/components/layout_2020/layout_debug.rs new file mode 100644 index 00000000000..32ab13fc484 --- /dev/null +++ b/components/layout_2020/layout_debug.rs @@ -0,0 +1,110 @@ +/* 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 https://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 crate::flow::FragmentTreeRoot; +use serde_json::{to_string, to_value, Value}; +use std::cell::RefCell; +use std::fs::File; +use std::io::Write; +use std::sync::Arc; + +thread_local!(static STATE_KEY: RefCell<Option<State>> = RefCell::new(None)); + +pub struct Scope; + +#[macro_export] +macro_rules! layout_debug_scope( + ($($arg:tt)*) => ( + if cfg!(debug_assertions) { + layout_debug::Scope::new(format!($($arg)*)) + } else { + layout_debug::Scope + } + ) +); + +#[derive(Serialize)] +struct ScopeData { + name: String, + pre: Value, + post: Value, + children: Vec<Box<ScopeData>>, +} + +impl ScopeData { + fn new(name: String, pre: Value) -> ScopeData { + ScopeData { + name: name, + pre: pre, + post: Value::Null, + children: vec![], + } + } +} + +struct State { + fragment: Arc<FragmentTreeRoot>, + scope_stack: Vec<Box<ScopeData>>, +} + +/// A layout debugging scope. The entire state of the fragment tree +/// will be output at the beginning and end of this scope. +impl Scope { + pub fn new(name: String) -> Scope { + STATE_KEY.with(|ref r| { + if let Some(ref mut state) = *r.borrow_mut() { + let fragment_tree = to_value(&state.fragment).unwrap(); + let data = Box::new(ScopeData::new(name.clone(), fragment_tree)); + state.scope_stack.push(data); + } + }); + Scope + } +} + +#[cfg(debug_assertions)] +impl Drop for Scope { + fn drop(&mut self) { + STATE_KEY.with(|ref r| { + if let Some(ref mut state) = *r.borrow_mut() { + let mut current_scope = state.scope_stack.pop().unwrap(); + current_scope.post = to_value(&state.fragment).unwrap(); + let previous_scope = state.scope_stack.last_mut().unwrap(); + previous_scope.children.push(current_scope); + } + }); + } +} + +/// Begin a layout debug trace. If this has not been called, +/// creating debug scopes has no effect. +pub fn begin_trace(root: Arc<FragmentTreeRoot>) { + assert!(STATE_KEY.with(|ref r| r.borrow().is_none())); + + STATE_KEY.with(|ref r| { + let root_trace = to_value(&root).unwrap(); + let state = State { + scope_stack: vec![Box::new(ScopeData::new("root".to_owned(), root_trace))], + fragment: root.clone(), + }; + *r.borrow_mut() = Some(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(generation: u32) { + let mut thread_state = STATE_KEY.with(|ref r| r.borrow_mut().take().unwrap()); + assert_eq!(thread_state.scope_stack.len(), 1); + let mut root_scope = thread_state.scope_stack.pop().unwrap(); + root_scope.post = to_value(&thread_state.fragment).unwrap(); + + let result = to_string(&root_scope).unwrap(); + let mut file = File::create(format!("layout_trace-{}.json", generation)).unwrap(); + file.write_all(result.as_bytes()).unwrap(); +} diff --git a/components/layout_2020/lib.rs b/components/layout_2020/lib.rs index 28ad31ae146..62e1f1b9470 100644 --- a/components/layout_2020/lib.rs +++ b/components/layout_2020/lib.rs @@ -5,6 +5,9 @@ #![deny(unsafe_code)] #![feature(exact_size_is_empty)] +#[macro_use] +extern crate serde; + pub mod context; pub mod data; pub mod display_list; @@ -14,6 +17,8 @@ mod flow; mod formatting_contexts; mod fragments; mod geom; +#[macro_use] +pub mod layout_debug; mod opaque_node; mod positioned; pub mod query; diff --git a/components/layout_thread_2020/lib.rs b/components/layout_thread_2020/lib.rs index be24d768595..6a2ba3b706b 100644 --- a/components/layout_thread_2020/lib.rs +++ b/components/layout_thread_2020/lib.rs @@ -36,6 +36,7 @@ use ipc_channel::ipc::{self, IpcReceiver, IpcSender}; use ipc_channel::router::ROUTER; use layout::context::LayoutContext; use layout::display_list::{DisplayListBuilder, WebRenderImageInfo}; +use layout::layout_debug; use layout::query::{ process_content_box_request, process_content_boxes_request, LayoutRPCImpl, LayoutThreadData, }; @@ -175,7 +176,7 @@ pub struct LayoutThread { box_tree_root: RefCell<Option<BoxTreeRoot>>, /// The root of the fragment tree. - fragment_tree_root: RefCell<Option<FragmentTreeRoot>>, + fragment_tree_root: RefCell<Option<Arc<FragmentTreeRoot>>>, /// The document-specific shared lock used for author-origin stylesheets document_shared_lock: Option<SharedRwLock>, @@ -234,6 +235,10 @@ pub struct LayoutThread { /// Emits notifications when there is a relayout. relayout_event: bool, + + /// True if each step of layout is traced to an external JSON file + /// for debugging purposes. + trace_layout: bool, } impl LayoutThreadFactory for LayoutThread { @@ -266,7 +271,7 @@ impl LayoutThreadFactory for LayoutThread { dump_rule_tree: bool, relayout_event: bool, _nonincremental_layout: bool, - _trace_layout: bool, + trace_layout: bool, dump_flow_tree: bool, ) { thread::Builder::new() @@ -314,6 +319,7 @@ impl LayoutThreadFactory for LayoutThread { dump_style_tree, dump_rule_tree, dump_flow_tree, + trace_layout, ); let reporter_name = format!("layout-reporter-{}", id); @@ -482,6 +488,7 @@ impl LayoutThread { dump_style_tree: bool, dump_rule_tree: bool, dump_flow_tree: bool, + trace_layout: bool, ) -> LayoutThread { // Let webrender know about this pipeline by sending an empty display list. webrender_api_sender.send_initial_transaction(webrender_document, id.to_webrender()); @@ -567,6 +574,7 @@ impl LayoutThread { dump_style_tree, dump_rule_tree, dump_flow_tree, + trace_layout, } } @@ -866,9 +874,9 @@ impl LayoutThread { self.dump_style_tree, self.dump_rule_tree, self.relayout_event, - true, // nonincremental_layout - false, // trace_layout - self.dump_flow_tree, + true, // nonincremental_layout + self.trace_layout, // trace_layout + self.dump_flow_tree, // dump_flow_tree ); } @@ -1165,7 +1173,7 @@ impl LayoutThread { run_layout() }; *self.box_tree_root.borrow_mut() = Some(box_tree); - *self.fragment_tree_root.borrow_mut() = Some(fragment_tree); + *self.fragment_tree_root.borrow_mut() = Some(Arc::new(fragment_tree)); } for element in elements_with_snapshot { @@ -1195,7 +1203,7 @@ impl LayoutThread { // Perform post-style recalculation layout passes. if let Some(root) = &*self.fragment_tree_root.borrow() { self.perform_post_style_recalc_layout_passes( - root, + root.clone(), &data.reflow_goal, Some(&document), &mut layout_context, @@ -1358,7 +1366,7 @@ impl LayoutThread { let mut layout_context = self.build_layout_context(guards, false, &snapshots, origin); self.perform_post_style_recalc_layout_passes( - root, + root.clone(), &ReflowGoal::TickAnimations, None, &mut layout_context, @@ -1369,7 +1377,7 @@ impl LayoutThread { fn perform_post_style_recalc_layout_passes( &self, - fragment_tree: &FragmentTreeRoot, + fragment_tree: Arc<FragmentTreeRoot>, reflow_goal: &ReflowGoal, document: Option<&ServoLayoutDocument>, context: &mut LayoutContext, @@ -1384,6 +1392,11 @@ impl LayoutThread { .needs_paint_from_layout(); return; } + + if self.trace_layout { + layout_debug::begin_trace(fragment_tree.clone()); + } + if let Some(document) = document { document.will_paint(); } @@ -1426,6 +1439,10 @@ impl LayoutThread { display_list.wr.finalize(), ); + if self.trace_layout { + layout_debug::end_trace(self.generation.get()); + } + self.generation.set(self.generation.get() + 1); } |