gfx: Make display lists serializable using serde.

This commit introduces the `serde` dependency, which we will use to
serialize messages going between processes in multiprocess Servo.

This also adds a new debugging flag, `-Z print-display-list-json`,
allowing the output of display list serialization to be visualized.
This will be useful for our experiments with alternate rasterizers.
This commit is contained in:
Patrick Walton 2015-07-08 16:15:23 -07:00
parent b6b95085fb
commit 6eacb0c995
30 changed files with 320 additions and 124 deletions

View file

@ -57,6 +57,8 @@ harfbuzz = "0.1"
smallvec = "0.1"
string_cache = "0.1"
euclid = "0.1"
serde = "*"
serde_macros = "*"
[target.x86_64-apple-darwin.dependencies]
core-foundation = "*"

View file

@ -25,28 +25,27 @@ use text::TextRun;
use azure::azure::AzFloat;
use azure::azure_hl::Color;
use std::collections::linked_list::{self, LinkedList};
use euclid::{Point2D, Rect, SideOffsets2D, Size2D, Matrix2D, Matrix4};
use euclid::approxeq::ApproxEq;
use euclid::num::Zero;
use euclid::{Point2D, Rect, SideOffsets2D, Size2D, Matrix2D, Matrix4};
use libc::uintptr_t;
use paint_task::PaintLayer;
use msg::compositor_msg::{LayerId, LayerKind};
use net_traits::image::base::Image;
use util::opts;
use util::cursor::Cursor;
use util::linked_list::prepend_from;
use util::geometry::{self, Au, MAX_RECT, ZERO_RECT};
use util::mem::HeapSizeOf;
use util::range::Range;
use paint_task::PaintLayer;
use smallvec::SmallVec8;
use std::collections::linked_list::{self, LinkedList};
use std::fmt;
use std::slice::Iter;
use std::sync::Arc;
use style::computed_values::{border_style, cursor, filter, image_rendering, mix_blend_mode};
use style::computed_values::{pointer_events};
use style::properties::ComputedValues;
use util::cursor::Cursor;
use util::geometry::{self, Au, MAX_RECT, ZERO_RECT};
use util::linked_list::{SerializableLinkedList, prepend_from};
use util::mem::HeapSizeOf;
use util::opts;
use util::range::Range;
// It seems cleaner to have layout code not mention Azure directly, so let's just reexport this for
// layout to use.
@ -66,7 +65,7 @@ const MIN_INDENTATION_LENGTH: usize = 4;
/// Because the script task's GC does not trace layout, node data cannot be safely stored in layout
/// data structures. Also, layout code tends to be faster when the DOM is not being accessed, for
/// locality reasons. Using `OpaqueNode` enforces this invariant.
#[derive(Clone, PartialEq, Copy, Debug, HeapSizeOf)]
#[derive(Clone, PartialEq, Copy, Debug, HeapSizeOf, Deserialize, Serialize)]
pub struct OpaqueNode(pub uintptr_t);
impl OpaqueNode {
@ -82,22 +81,22 @@ impl OpaqueNode {
///
/// TODO(pcwalton): We could reduce the size of this structure with a more "skip list"-like
/// structure, omitting several pointers and lengths.
#[derive(HeapSizeOf)]
#[derive(HeapSizeOf, Deserialize, Serialize)]
pub struct DisplayList {
/// The border and backgrounds for the root of this stacking context: steps 1 and 2.
pub background_and_borders: LinkedList<DisplayItem>,
pub background_and_borders: SerializableLinkedList<DisplayItem>,
/// Borders and backgrounds for block-level descendants: step 4.
pub block_backgrounds_and_borders: LinkedList<DisplayItem>,
pub block_backgrounds_and_borders: SerializableLinkedList<DisplayItem>,
/// Floats: step 5. These are treated as pseudo-stacking contexts.
pub floats: LinkedList<DisplayItem>,
pub floats: SerializableLinkedList<DisplayItem>,
/// All non-positioned content.
pub content: LinkedList<DisplayItem>,
pub content: SerializableLinkedList<DisplayItem>,
/// All positioned content that does not get a stacking context.
pub positioned_content: LinkedList<DisplayItem>,
pub positioned_content: SerializableLinkedList<DisplayItem>,
/// Outlines: step 10.
pub outlines: LinkedList<DisplayItem>,
pub outlines: SerializableLinkedList<DisplayItem>,
/// Child stacking contexts.
pub children: LinkedList<Arc<StackingContext>>,
pub children: SerializableLinkedList<Arc<StackingContext>>,
}
impl DisplayList {
@ -105,13 +104,13 @@ impl DisplayList {
#[inline]
pub fn new() -> DisplayList {
DisplayList {
background_and_borders: LinkedList::new(),
block_backgrounds_and_borders: LinkedList::new(),
floats: LinkedList::new(),
content: LinkedList::new(),
positioned_content: LinkedList::new(),
outlines: LinkedList::new(),
children: LinkedList::new(),
background_and_borders: SerializableLinkedList::new(LinkedList::new()),
block_backgrounds_and_borders: SerializableLinkedList::new(LinkedList::new()),
floats: SerializableLinkedList::new(LinkedList::new()),
content: SerializableLinkedList::new(LinkedList::new()),
positioned_content: SerializableLinkedList::new(LinkedList::new()),
outlines: SerializableLinkedList::new(LinkedList::new()),
children: SerializableLinkedList::new(LinkedList::new()),
}
}
@ -119,34 +118,34 @@ impl DisplayList {
/// `other` in the process.
#[inline]
pub fn append_from(&mut self, other: &mut DisplayList) {
self.background_and_borders.append(&mut other.background_and_borders);
self.block_backgrounds_and_borders.append(&mut other.block_backgrounds_and_borders);
self.floats.append(&mut other.floats);
self.content.append(&mut other.content);
self.positioned_content.append(&mut other.positioned_content);
self.outlines.append(&mut other.outlines);
self.children.append(&mut other.children);
self.background_and_borders.append(&mut *other.background_and_borders);
self.block_backgrounds_and_borders.append(&mut *other.block_backgrounds_and_borders);
self.floats.append(&mut *other.floats);
self.content.append(&mut *other.content);
self.positioned_content.append(&mut *other.positioned_content);
self.outlines.append(&mut *other.outlines);
self.children.append(&mut *other.children);
}
/// Merges all display items from all non-float stacking levels to the `float` stacking level.
#[inline]
pub fn form_float_pseudo_stacking_context(&mut self) {
prepend_from(&mut self.floats, &mut self.outlines);
prepend_from(&mut self.floats, &mut self.positioned_content);
prepend_from(&mut self.floats, &mut self.content);
prepend_from(&mut self.floats, &mut self.block_backgrounds_and_borders);
prepend_from(&mut self.floats, &mut self.background_and_borders);
prepend_from(&mut *self.floats, &mut *self.outlines);
prepend_from(&mut *self.floats, &mut *self.positioned_content);
prepend_from(&mut *self.floats, &mut *self.content);
prepend_from(&mut *self.floats, &mut *self.block_backgrounds_and_borders);
prepend_from(&mut *self.floats, &mut *self.background_and_borders);
}
/// Merges all display items from all non-positioned-content stacking levels to the
/// positioned-content stacking level.
#[inline]
pub fn form_pseudo_stacking_context_for_positioned_content(&mut self) {
prepend_from(&mut self.positioned_content, &mut self.outlines);
prepend_from(&mut self.positioned_content, &mut self.content);
prepend_from(&mut self.positioned_content, &mut self.floats);
prepend_from(&mut self.positioned_content, &mut self.block_backgrounds_and_borders);
prepend_from(&mut self.positioned_content, &mut self.background_and_borders);
prepend_from(&mut *self.positioned_content, &mut *self.outlines);
prepend_from(&mut *self.positioned_content, &mut *self.content);
prepend_from(&mut *self.positioned_content, &mut *self.floats);
prepend_from(&mut *self.positioned_content, &mut *self.block_backgrounds_and_borders);
prepend_from(&mut *self.positioned_content, &mut *self.background_and_borders);
}
/// Returns a list of all items in this display list concatenated together. This is extremely
@ -219,7 +218,7 @@ impl DisplayList {
}
}
#[derive(HeapSizeOf)]
#[derive(HeapSizeOf, Deserialize, Serialize)]
/// Represents one CSS stacking context, which may or may not have a hardware layer.
pub struct StackingContext {
/// The display items that make up this stacking context.
@ -443,7 +442,8 @@ impl StackingContext {
} else {
// Optimize the display list to throw out out-of-bounds display items and so forth.
let display_list = DisplayListOptimizer::new(tile_bounds).optimize(&*self.display_list);
let display_list =
DisplayListOptimizer::new(tile_bounds).optimize(&*self.display_list);
self.draw_into_context(&display_list,
paint_context,
@ -515,12 +515,16 @@ impl StackingContext {
// If the point is inside the border, it didn't hit the border!
let interior_rect =
Rect::new(
Point2D::new(border.base.bounds.origin.x + border.border_widths.left,
border.base.bounds.origin.y + border.border_widths.top),
Point2D::new(border.base.bounds.origin.x +
border.border_widths.left,
border.base.bounds.origin.y +
border.border_widths.top),
Size2D::new(border.base.bounds.size.width -
(border.border_widths.left + border.border_widths.right),
(border.border_widths.left +
border.border_widths.right),
border.base.bounds.size.height -
(border.border_widths.top + border.border_widths.bottom)));
(border.border_widths.top +
border.border_widths.bottom)));
if geometry::rect_contains_point(interior_rect, point) {
continue
}
@ -541,7 +545,7 @@ impl StackingContext {
debug_assert!(!topmost_only || result.is_empty());
let frac_point = self.transform.transform_point(&Point2D::new(point.x.to_f32_px(),
point.y.to_f32_px()));
point.y.to_f32_px()));
point = Point2D::new(Au::from_f32_px(frac_point.x), Au::from_f32_px(frac_point.y));
// Iterate through display items in reverse stacking order. Steps here refer to the
@ -639,7 +643,7 @@ pub fn find_stacking_context_with_layer_id(this: &Arc<StackingContext>, layer_id
}
/// One drawing command in the list.
#[derive(Clone, HeapSizeOf)]
#[derive(Clone, Deserialize, HeapSizeOf, Serialize)]
pub enum DisplayItem {
SolidColorClass(Box<SolidColorDisplayItem>),
TextClass(Box<TextDisplayItem>),
@ -651,7 +655,7 @@ pub enum DisplayItem {
}
/// Information common to all display items.
#[derive(Clone, HeapSizeOf)]
#[derive(Clone, Deserialize, HeapSizeOf, Serialize)]
pub struct BaseDisplayItem {
/// The boundaries of the display item, in layer coordinates.
pub bounds: Rect<Au>,
@ -679,7 +683,7 @@ impl BaseDisplayItem {
/// A clipping region for a display item. Currently, this can describe rectangles, rounded
/// rectangles (for `border-radius`), or arbitrary intersections of the two. Arbitrary transforms
/// are not supported because those are handled by the higher-level `StackingContext` abstraction.
#[derive(Clone, PartialEq, Debug, HeapSizeOf)]
#[derive(Clone, PartialEq, Debug, HeapSizeOf, Deserialize, Serialize)]
pub struct ClippingRegion {
/// The main rectangular region. This does not include any corners.
pub main: Rect<Au>,
@ -693,7 +697,7 @@ pub struct ClippingRegion {
/// A complex clipping region. These don't as easily admit arbitrary intersection operations, so
/// they're stored in a list over to the side. Currently a complex clipping region is just a
/// rounded rectangle, but the CSS WGs will probably make us throw more stuff in here eventually.
#[derive(Clone, PartialEq, Debug, HeapSizeOf)]
#[derive(Clone, PartialEq, Debug, HeapSizeOf, Deserialize, Serialize)]
pub struct ComplexClippingRegion {
/// The boundaries of the rectangle.
pub rect: Rect<Au>,
@ -805,7 +809,7 @@ impl ClippingRegion {
/// Metadata attached to each display item. This is useful for performing auxiliary tasks with
/// the display list involving hit testing: finding the originating DOM node and determining the
/// cursor to use when the element is hovered over.
#[derive(Clone, Copy, HeapSizeOf)]
#[derive(Clone, Copy, HeapSizeOf, Deserialize, Serialize)]
pub struct DisplayItemMetadata {
/// The DOM node from which this display item originated.
pub node: OpaqueNode,
@ -834,7 +838,7 @@ impl DisplayItemMetadata {
}
/// Paints a solid color.
#[derive(Clone, HeapSizeOf)]
#[derive(Clone, HeapSizeOf, Deserialize, Serialize)]
pub struct SolidColorDisplayItem {
/// Fields common to all display items.
pub base: BaseDisplayItem,
@ -844,7 +848,7 @@ pub struct SolidColorDisplayItem {
}
/// Paints text.
#[derive(Clone, HeapSizeOf)]
#[derive(Clone, HeapSizeOf, Deserialize, Serialize)]
pub struct TextDisplayItem {
/// Fields common to all display items.
pub base: BaseDisplayItem,
@ -869,7 +873,7 @@ pub struct TextDisplayItem {
pub blur_radius: Au,
}
#[derive(Clone, Eq, PartialEq, HeapSizeOf)]
#[derive(Clone, Eq, PartialEq, HeapSizeOf, Deserialize, Serialize)]
pub enum TextOrientation {
Upright,
SidewaysLeft,
@ -877,7 +881,7 @@ pub enum TextOrientation {
}
/// Paints an image.
#[derive(Clone, HeapSizeOf)]
#[derive(Clone, HeapSizeOf, Deserialize, Serialize)]
pub struct ImageDisplayItem {
pub base: BaseDisplayItem,
#[ignore_heap_size_of = "Because it is non-owning"]
@ -895,7 +899,7 @@ pub struct ImageDisplayItem {
/// Paints a gradient.
#[derive(Clone)]
#[derive(Clone, Deserialize, Serialize)]
pub struct GradientDisplayItem {
/// Fields common to all display items.
pub base: BaseDisplayItem,
@ -926,7 +930,7 @@ impl HeapSizeOf for GradientDisplayItem {
/// Paints a border.
#[derive(Clone, HeapSizeOf)]
#[derive(Clone, HeapSizeOf, Deserialize, Serialize)]
pub struct BorderDisplayItem {
/// Fields common to all display items.
pub base: BaseDisplayItem,
@ -949,7 +953,7 @@ pub struct BorderDisplayItem {
/// Information about the border radii.
///
/// TODO(pcwalton): Elliptical radii.
#[derive(Clone, Default, PartialEq, Debug, Copy, HeapSizeOf)]
#[derive(Clone, Default, PartialEq, Debug, Copy, HeapSizeOf, Deserialize, Serialize)]
pub struct BorderRadii<T> {
pub top_left: T,
pub top_right: T,
@ -979,7 +983,7 @@ impl<T> BorderRadii<T> where T: PartialEq + Zero + Clone {
}
/// Paints a line segment.
#[derive(Clone, HeapSizeOf)]
#[derive(Clone, HeapSizeOf, Deserialize, Serialize)]
pub struct LineDisplayItem {
pub base: BaseDisplayItem,
@ -991,7 +995,7 @@ pub struct LineDisplayItem {
}
/// Paints a box shadow per CSS-BACKGROUNDS.
#[derive(Clone, HeapSizeOf)]
#[derive(Clone, HeapSizeOf, Deserialize, Serialize)]
pub struct BoxShadowDisplayItem {
/// Fields common to all display items.
pub base: BaseDisplayItem,
@ -1016,7 +1020,7 @@ pub struct BoxShadowDisplayItem {
}
/// How a box shadow should be clipped.
#[derive(Clone, Copy, Debug, PartialEq, HeapSizeOf)]
#[derive(Clone, Copy, Debug, PartialEq, HeapSizeOf, Deserialize, Serialize)]
pub enum BoxShadowClipMode {
/// No special clipping should occur. This is used for (shadowed) text decorations.
None,

View file

@ -37,7 +37,8 @@ impl DisplayListOptimizer {
self.add_in_bounds_display_items(&mut result.content, display_list.content.iter());
self.add_in_bounds_display_items(&mut result.positioned_content,
display_list.positioned_content.iter());
self.add_in_bounds_display_items(&mut result.outlines, display_list.outlines.iter());
self.add_in_bounds_display_items(&mut result.outlines,
display_list.outlines.iter());
self.add_in_bounds_stacking_contexts(&mut result.children, display_list.children.iter());
result
}

View file

@ -69,7 +69,7 @@ pub trait FontTableMethods {
fn with_buffer<F>(&self, F) where F: FnOnce(*const u8, usize);
}
#[derive(Clone, Debug)]
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct FontMetrics {
pub underline_size: Au,
pub underline_offset: Au,

View file

@ -14,9 +14,11 @@
#![feature(vec_push_all)]
#![plugin(plugins)]
#![plugin(serde_macros)]
#[macro_use]
extern crate log;
extern crate serde;
extern crate azure;
#[macro_use] extern crate bitflags;

View file

@ -40,7 +40,7 @@ use util::task_state;
use util::task::spawn_named;
/// Information about a hardware graphics layer that layout sends to the painting task.
#[derive(Clone)]
#[derive(Clone, Deserialize, Serialize)]
pub struct PaintLayer {
/// A per-pipeline ID describing this layer that should be stable across reflows.
pub id: LayerId,
@ -353,18 +353,23 @@ impl<C> PaintTask<C> where C: PaintListener + Send + 'static {
let transform = transform.mul(&stacking_context.transform);
let perspective = perspective.mul(&stacking_context.perspective);
let (next_parent_id, page_position, transform, perspective) = match stacking_context.layer {
let (next_parent_id, page_position, transform, perspective) =
match stacking_context.layer {
Some(ref paint_layer) => {
// Layers start at the top left of their overflow rect, as far as the info we give to
// the compositor is concerned.
// Layers start at the top left of their overflow rect, as far as the info we
// give to the compositor is concerned.
let overflow_relative_page_position = *page_position +
stacking_context.bounds.origin +
stacking_context.overflow.origin;
let layer_position =
Rect::new(Point2D::new(overflow_relative_page_position.x.to_nearest_px() as f32,
overflow_relative_page_position.y.to_nearest_px() as f32),
Size2D::new(stacking_context.overflow.size.width.to_nearest_px() as f32,
stacking_context.overflow.size.height.to_nearest_px() as f32));
Rect::new(Point2D::new(overflow_relative_page_position.x.to_nearest_px() as
f32,
overflow_relative_page_position.y.to_nearest_px() as
f32),
Size2D::new(stacking_context.overflow.size.width.to_nearest_px()
as f32,
stacking_context.overflow.size.height.to_nearest_px()
as f32));
let establishes_3d_context = stacking_context.establishes_3d_context;
@ -395,12 +400,7 @@ impl<C> PaintTask<C> where C: PaintListener + Send + 'static {
};
for kid in stacking_context.display_list.children.iter() {
build(properties,
&**kid,
&page_position,
&transform,
&perspective,
next_parent_id);
build(properties, &**kid, &page_position, &transform, &perspective, next_parent_id)
}
}
}

View file

@ -10,6 +10,7 @@ use string_cache::Atom;
/// The identifier is an absolute path, and the bytes
/// field is the loaded data that can be passed to
/// freetype and azure directly.
#[derive(Deserialize, Serialize)]
pub struct FontTemplateData {
pub bytes: Vec<u8>,
pub identifier: Atom,

View file

@ -64,7 +64,7 @@ impl FontHandleMethods for FontHandle {
Some(s) => s.to_f64_px(),
None => 0.0
};
match template.ctfont {
match *template.ctfont {
Some(ref ctfont) => {
Ok(FontHandle {
font_data: template.clone(),

View file

@ -7,14 +7,27 @@ use core_graphics::font::CGFont;
use core_text::font::CTFont;
use core_text;
use serde::de::{Error, Visitor};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::borrow::ToOwned;
use std::ops::Deref;
use string_cache::Atom;
/// Platform specific font representation for mac.
/// The identifier is a PostScript font name. The
/// CTFont object is cached here for use by the
/// paint functions that create CGFont references.
#[derive(Deserialize, Serialize)]
pub struct FontTemplateData {
pub ctfont: Option<CTFont>,
/// The `CTFont` object, if present. This is cached here so that we don't have to keep creating
/// `CTFont` instances over and over. It can always be recreated from the `identifier` and/or
/// `font_data` fields.
///
/// When sending a `FontTemplateData` instance across processes, this will be set to `None` on
/// the other side, because `CTFont` instances cannot be sent across processes. This is
/// harmless, however, because it can always be recreated.
pub ctfont: CachedCTFont,
pub identifier: Atom,
pub font_data: Option<Vec<u8>>
}
@ -39,9 +52,43 @@ impl FontTemplateData {
};
FontTemplateData {
ctfont: ctfont,
ctfont: CachedCTFont(ctfont),
identifier: identifier.to_owned(),
font_data: font_data
}
}
}
pub struct CachedCTFont(Option<CTFont>);
impl Deref for CachedCTFont {
type Target = Option<CTFont>;
fn deref(&self) -> &Option<CTFont> {
&self.0
}
}
impl Serialize for CachedCTFont {
fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error> where S: Serializer {
serializer.visit_none()
}
}
impl Deserialize for CachedCTFont {
fn deserialize<D>(deserializer: &mut D) -> Result<CachedCTFont, D::Error>
where D: Deserializer {
struct NoneOptionVisitor;
impl Visitor for NoneOptionVisitor {
type Value = CachedCTFont;
#[inline]
fn visit_none<E>(&mut self) -> Result<CachedCTFont,E> where E: Error {
Ok(CachedCTFont(None))
}
}
deserializer.visit_option(NoneOptionVisitor)
}
}

View file

@ -19,7 +19,7 @@ use util::vec::*;
/// In the uncommon case (multiple glyphs per unicode character, large glyph index/advance, or
/// glyph offsets), we pack the glyph count into GlyphEntry, and store the other glyph information
/// in DetailedGlyphStore.
#[derive(Clone, Debug, Copy)]
#[derive(Clone, Debug, Copy, Deserialize, Serialize)]
struct GlyphEntry {
value: u32,
}
@ -250,7 +250,7 @@ impl GlyphEntry {
// Stores data for a detailed glyph, in the case that several glyphs
// correspond to one character, or the glyph's data couldn't be packed.
#[derive(Clone, Debug, Copy)]
#[derive(Clone, Debug, Copy, Deserialize, Serialize)]
struct DetailedGlyph {
id: GlyphId,
// glyph's advance, in the text's direction (LTR or RTL)
@ -269,7 +269,7 @@ impl DetailedGlyph {
}
}
#[derive(PartialEq, Clone, Eq, Debug, Copy)]
#[derive(PartialEq, Clone, Eq, Debug, Copy, Deserialize, Serialize)]
struct DetailedGlyphRecord {
// source string offset/GlyphEntry offset in the TextRun
entry_offset: CharIndex,
@ -293,7 +293,7 @@ impl Ord for DetailedGlyphRecord {
// until a lookup is actually performed; this matches the expected
// usage pattern of setting/appending all the detailed glyphs, and
// then querying without setting.
#[derive(Clone)]
#[derive(Clone, Deserialize, Serialize)]
struct DetailedGlyphStore {
// TODO(pcwalton): Allocation of this buffer is expensive. Consider a small-vector
// optimization.
@ -500,7 +500,7 @@ impl<'a> GlyphInfo<'a> {
/// | +---+---+ |
/// +---------------------------------------------+
/// ~~~
#[derive(Clone)]
#[derive(Clone, Deserialize, Serialize)]
pub struct GlyphStore {
// TODO(pcwalton): Allocation of this buffer is expensive. Consider a small-vector
// optimization.
@ -515,7 +515,7 @@ pub struct GlyphStore {
}
int_range_index! {
#[derive(RustcEncodable)]
#[derive(Deserialize, Serialize, RustcEncodable)]
#[doc = "An index that refers to a character in a text run. This could \
point to the middle of a glyph."]
#[derive(HeapSizeOf)]

View file

@ -14,7 +14,7 @@ use std::sync::Arc;
use text::glyph::{CharIndex, GlyphStore};
/// A single "paragraph" of text in one font size and style.
#[derive(Clone)]
#[derive(Clone, Deserialize, Serialize)]
pub struct TextRun {
/// The UTF-8 string represented by this text run.
pub text: Arc<String>,
@ -26,7 +26,7 @@ pub struct TextRun {
}
/// A single series of glyphs within a text run.
#[derive(Clone)]
#[derive(Clone, Deserialize, Serialize)]
pub struct GlyphRun {
/// The glyphs.
pub glyph_store: Arc<GlyphStore>,