Bug 1402304 - Update webrender to commit 7a5f60aff33010a44d6acbdc67f27f1f63678b5d. r?jrmuizel draft
authorKartikaya Gupta <kgupta@mozilla.com>
Fri, 22 Sep 2017 09:57:06 -0400
changeset 669102 360af82ea6b5acfc0a0e194510e6c4c47725153c
parent 669101 754a0d9a44ea5208b6b204e06033e008a6c17b52
child 669103 aaa20faab3825f7b35295af19116c7094d5341ea
push id81210
push userkgupta@mozilla.com
push dateFri, 22 Sep 2017 14:09:59 +0000
reviewersjrmuizel
bugs1402304
milestone58.0a1
Bug 1402304 - Update webrender to commit 7a5f60aff33010a44d6acbdc67f27f1f63678b5d. r?jrmuizel MozReview-Commit-ID: GSDNyYGg0VY
gfx/doc/README.webrender
gfx/webrender/Cargo.toml
gfx/webrender/res/prim_shared.glsl
gfx/webrender/res/ps_angle_gradient.glsl
gfx/webrender/res/ps_gradient.glsl
gfx/webrender/res/ps_radial_gradient.glsl
gfx/webrender/src/border.rs
gfx/webrender/src/frame.rs
gfx/webrender/src/frame_builder.rs
gfx/webrender/src/glyph_rasterizer.rs
gfx/webrender/src/platform/unix/font.rs
gfx/webrender/src/render_backend.rs
gfx/webrender/src/resource_cache.rs
gfx/webrender/src/scene.rs
gfx/webrender/src/tiling.rs
gfx/webrender_api/Cargo.toml
gfx/webrender_api/src/font.rs
gfx/webrender_bindings/Cargo.toml
--- a/gfx/doc/README.webrender
+++ b/gfx/doc/README.webrender
@@ -74,9 +74,10 @@ there is another crate in m-c called moz
 the same folder to store its rust dependencies. If one of the libraries that is
 required by both mozjs_sys and webrender is updated without updating the other
 project's Cargo.lock file, that results in build bustage.
 This means that any time you do this sort of manual update of packages, you need
 to make sure that mozjs_sys also has its Cargo.lock file updated if needed, hence
 the need to run the cargo update command in js/src as well. Hopefully this will
 be resolved soon.
 
-Latest Commit: 2a005f156b9f25862a2dc8443b57be37168233f2
+Latest Commit: 7a5f60aff33010a44d6acbdc67f27f1f63678b5d
+
--- a/gfx/webrender/Cargo.toml
+++ b/gfx/webrender/Cargo.toml
@@ -1,11 +1,11 @@
 [package]
 name = "webrender"
-version = "0.51.0"
+version = "0.52.0"
 authors = ["Glenn Watson <gw@intuitionlibrary.com>"]
 license = "MPL-2.0"
 repository = "https://github.com/servo/webrender"
 build = "build.rs"
 
 [features]
 default = ["freetype-lib"]
 freetype-lib = ["freetype/servo-freetype-sys"]
@@ -31,22 +31,22 @@ plane-split = "0.6"
 ws = { optional = true, version = "0.7.3" }
 serde_json = { optional = true, version = "1.0" }
 serde = { optional = true, version = "1.0" }
 serde_derive = { optional = true, version = "1.0" }
 
 [dev-dependencies]
 angle = {git = "https://github.com/servo/angle", branch = "servo"}
 rand = "0.3"                # for the benchmarks
-servo-glutin = "0.11"     # for the example apps
+servo-glutin = "0.12"     # for the example apps
 
 [target.'cfg(any(target_os = "android", all(unix, not(target_os = "macos"))))'.dependencies]
 freetype = { version = "0.3", default-features = false }
 
 [target.'cfg(target_os = "windows")'.dependencies]
 dwrote = "0.4"
 gamma-lut = "0.2"
 
 [target.'cfg(target_os = "macos")'.dependencies]
-core-foundation = "0.3"
-core-graphics = "0.8.0"
-core-text = { version = "6.1", default-features = false }
+core-foundation = "0.4"
+core-graphics = "0.9"
+core-text = { version = "7.0", default-features = false }
 gamma-lut = "0.2"
--- a/gfx/webrender/res/prim_shared.glsl
+++ b/gfx/webrender/res/prim_shared.glsl
@@ -276,32 +276,34 @@ Glyph fetch_glyph(int specific_prim_addr
                   int glyph_index,
                   int subpx_dir) {
     // Two glyphs are packed in each texel in the GPU cache.
     int glyph_address = specific_prim_address +
                         VECS_PER_TEXT_RUN +
                         glyph_index / 2;
     vec4 data = fetch_from_resource_cache_1(glyph_address);
     // Select XY or ZW based on glyph index.
-    vec2 glyph = mix(data.xy, data.zw, bvec2(glyph_index % 2 == 1));
+    // We use "!= 0" instead of "== 1" here in order to work around a driver
+    // bug with equality comparisons on integers.
+    vec2 glyph = mix(data.xy, data.zw, bvec2(glyph_index % 2 != 0));
 
     // In subpixel mode, the subpixel offset has already been
     // accounted for while rasterizing the glyph.
     switch (subpx_dir) {
         case SUBPX_DIR_NONE:
             break;
         case SUBPX_DIR_HORIZONTAL:
             // Glyphs positioned [-0.125, 0.125] get a
             // subpx position of zero. So include that
             // offset in the glyph position to ensure
-            // we truncate to the correct whole position.
-            glyph.x = trunc(glyph.x + 0.125);
+            // we round to the correct whole position.
+            glyph.x = floor(glyph.x + 0.125);
             break;
         case SUBPX_DIR_VERTICAL:
-            glyph.y = trunc(glyph.y + 0.125);
+            glyph.y = floor(glyph.y + 0.125);
             break;
     }
 
     return Glyph(glyph);
 }
 
 struct PrimitiveInstance {
     int prim_address;
--- a/gfx/webrender/res/ps_angle_gradient.glsl
+++ b/gfx/webrender/res/ps_angle_gradient.glsl
@@ -37,17 +37,17 @@ void main(void) {
     vScaledDir = dir / dot(dir, dir);
 
     vTileSize = gradient.tile_size_repeat.xy;
     vTileRepeat = gradient.tile_size_repeat.zw;
 
     vGradientAddress = prim.specific_prim_address + VECS_PER_GRADIENT;
 
     // Whether to repeat the gradient instead of clamping.
-    vGradientRepeat = float(int(gradient.extend_mode.x) == EXTEND_MODE_REPEAT);
+    vGradientRepeat = float(int(gradient.extend_mode.x) != EXTEND_MODE_CLAMP);
 }
 #endif
 
 #ifdef WR_FRAGMENT_SHADER
 void main(void) {
     vec2 pos = mod(vPos, vTileRepeat);
 
     if (pos.x >= vTileSize.x ||
--- a/gfx/webrender/res/ps_gradient.glsl
+++ b/gfx/webrender/res/ps_gradient.glsl
@@ -103,11 +103,11 @@ void main(void) {
     float alpha = 0.0;
     vec2 local_pos = init_transform_fs(vLocalPos, alpha);
 #else
     float alpha = 1.0;
     vec2 local_pos = vPos;
 #endif
 
     alpha = min(alpha, do_clip());
-    oFragColor = dither(vColor * vec4(1.0, 1.0, 1.0, alpha));
+    oFragColor = dither(vColor * alpha);
 }
 #endif
--- a/gfx/webrender/res/ps_radial_gradient.glsl
+++ b/gfx/webrender/res/ps_radial_gradient.glsl
@@ -47,17 +47,17 @@ void main(void) {
     vStartCenter.y *= ratio_xy;
     vEndCenter.y *= ratio_xy;
     vTileSize.y *= ratio_xy;
     vTileRepeat.y *= ratio_xy;
 
     vGradientAddress = prim.specific_prim_address + VECS_PER_GRADIENT;
 
     // Whether to repeat the gradient instead of clamping.
-    vGradientRepeat = float(int(gradient.start_end_radius_ratio_xy_extend_mode.w) == EXTEND_MODE_REPEAT);
+    vGradientRepeat = float(int(gradient.start_end_radius_ratio_xy_extend_mode.w) != EXTEND_MODE_CLAMP);
 }
 #endif
 
 #ifdef WR_FRAGMENT_SHADER
 void main(void) {
     vec2 pos = mod(vPos, vTileRepeat);
 
     if (pos.x >= vTileSize.x ||
--- a/gfx/webrender/src/border.rs
+++ b/gfx/webrender/src/border.rs
@@ -1,19 +1,19 @@
 /* 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/. */
 
 use api::{BorderSide, BorderStyle, BorderWidths, ClipAndScrollInfo, ColorF, LayerPoint, LayerRect};
-use api::{LayerPrimitiveInfo, LayerSize, NormalBorder};
+use api::{LayerPrimitiveInfo, LayerSize, NormalBorder, RepeatMode};
 use clip::ClipSource;
 use ellipse::Ellipse;
 use frame_builder::FrameBuilder;
 use gpu_cache::GpuDataRequest;
-use prim_store::{BorderPrimitiveCpu, PrimitiveContainer};
+use prim_store::{BorderPrimitiveCpu, PrimitiveContainer, TexelRect};
 use tiling::PrimitiveFlags;
 use util::{lerp, pack_as_float};
 
 #[repr(u8)]
 #[derive(Debug, Copy, Clone, PartialEq)]
 pub enum BorderCornerInstance {
     Single, // Single instance needed - corner styles are same or similar.
     Double, // Different corner styles. Draw two instances, one per style.
@@ -790,8 +790,60 @@ struct DotInfo {
     diameter: f32,
 }
 
 impl DotInfo {
     fn new(arc_pos: f32, diameter: f32) -> DotInfo {
         DotInfo { arc_pos, diameter }
     }
 }
+
+#[derive(Debug, Clone)]
+pub struct ImageBorderSegment {
+    pub geom_rect: LayerRect,
+    pub sub_rect: TexelRect,
+    pub stretch_size: LayerSize,
+    pub tile_spacing: LayerSize,
+}
+
+impl ImageBorderSegment {
+    pub fn new(
+        rect: LayerRect,
+        sub_rect: TexelRect,
+        repeat_horizontal: RepeatMode,
+        repeat_vertical: RepeatMode,
+    ) -> ImageBorderSegment {
+        let tile_spacing = LayerSize::zero();
+
+        debug_assert!(sub_rect.uv1.x >= sub_rect.uv0.x);
+        debug_assert!(sub_rect.uv1.y >= sub_rect.uv0.y);
+
+        let image_size = LayerSize::new(
+            sub_rect.uv1.x - sub_rect.uv0.x,
+            sub_rect.uv1.y - sub_rect.uv0.y,
+        );
+
+        let stretch_size_x = match repeat_horizontal {
+            RepeatMode::Stretch => rect.size.width,
+            RepeatMode::Repeat => image_size.width,
+            RepeatMode::Round | RepeatMode::Space => {
+                error!("Round/Space not supported yet!");
+                rect.size.width
+            }
+        };
+
+        let stretch_size_y = match repeat_vertical {
+            RepeatMode::Stretch => rect.size.height,
+            RepeatMode::Repeat => image_size.height,
+            RepeatMode::Round | RepeatMode::Space => {
+                error!("Round/Space not supported yet!");
+                rect.size.height
+            }
+        };
+
+        ImageBorderSegment {
+            geom_rect: rect,
+            sub_rect,
+            stretch_size: LayerSize::new(stretch_size_x, stretch_size_y),
+            tile_spacing,
+        }
+    }
+}
--- a/gfx/webrender/src/frame.rs
+++ b/gfx/webrender/src/frame.rs
@@ -1,30 +1,30 @@
 /* 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/. */
 
-use api::{BuiltDisplayList, BuiltDisplayListIter, ClipAndScrollInfo, ClipId, ColorF};
+use api::{BuiltDisplayListIter, ClipAndScrollInfo, ClipId, ColorF};
 use api::{ComplexClipRegion, DeviceUintRect, DeviceUintSize, DisplayItemRef, Epoch, FilterOp};
 use api::{ImageDisplayItem, ItemRange, LayerPoint, LayerPrimitiveInfo, LayerRect, LayerSize,
           LayerToScrollTransform};
-use api::{LayerVector2D, LayoutSize, LayoutTransform, LocalClip, MixBlendMode, PipelineId};
-use api::{PropertyBinding, ScrollClamping, ScrollEventPhase, ScrollLayerState, ScrollLocation};
+use api::{LayerVector2D, LayoutSize, LayoutTransform, LocalClip, PipelineId};
+use api::{ScrollClamping, ScrollEventPhase, ScrollLayerState, ScrollLocation};
 use api::{ScrollPolicy, ScrollSensitivity, SpecificDisplayItem, StackingContext, TileOffset};
 use api::{TransformStyle, WorldPoint};
 use clip::ClipRegion;
 use clip_scroll_tree::{ClipScrollTree, ScrollStates};
 use euclid::rect;
 use frame_builder::{FrameBuilder, FrameBuilderConfig};
 use gpu_cache::GpuCache;
 use internal_types::{FastHashMap, FastHashSet, RendererFrame};
 use profiler::{GpuCacheProfileCounters, TextureCacheProfileCounters};
 use resource_cache::{ResourceCache, TiledImageMap};
-use scene::{Scene, SceneProperties};
-use tiling::{CompositeOps, DisplayListMap, PrimitiveFlags};
+use scene::{Scene, StackingContextHelpers, ScenePipeline};
+use tiling::{CompositeOps, PrimitiveFlags};
 use util::{subtract_rect, ComplexClipRegionHelpers};
 
 #[derive(Copy, Clone, PartialEq, PartialOrd, Debug, Eq, Ord)]
 pub struct FrameId(pub u32);
 
 static DEFAULT_SCROLLBAR_COLOR: ColorF = ColorF {
     r: 0.3,
     g: 0.3,
@@ -179,105 +179,34 @@ impl<'a> FlattenContext<'a> {
         pipeline_id: PipelineId,
         complex_clips: ItemRange<ComplexClipRegion>,
     ) -> Vec<ComplexClipRegion> {
         if complex_clips.is_empty() {
             return vec![];
         }
 
         self.scene
-            .display_lists
+            .pipelines
             .get(&pipeline_id)
             .expect("No display list?")
+            .display_list
             .get(complex_clips)
             .collect()
     }
 }
 
 // TODO: doc
 pub struct Frame {
     pub clip_scroll_tree: ClipScrollTree,
     pub pipeline_epoch_map: FastHashMap<PipelineId, Epoch>,
     id: FrameId,
     frame_builder_config: FrameBuilderConfig,
     pub frame_builder: Option<FrameBuilder>,
 }
 
-trait FilterOpHelpers {
-    fn resolve(self, properties: &SceneProperties) -> FilterOp;
-    fn is_noop(&self) -> bool;
-}
-
-impl FilterOpHelpers for FilterOp {
-    fn resolve(self, properties: &SceneProperties) -> FilterOp {
-        match self {
-            FilterOp::Opacity(ref value) => {
-                let amount = properties.resolve_float(value, 1.0);
-                FilterOp::Opacity(PropertyBinding::Value(amount))
-            }
-            _ => self,
-        }
-    }
-
-    fn is_noop(&self) -> bool {
-        match *self {
-            FilterOp::Blur(length) => length == 0.0,
-            FilterOp::Brightness(amount) => amount == 1.0,
-            FilterOp::Contrast(amount) => amount == 1.0,
-            FilterOp::Grayscale(amount) => amount == 0.0,
-            FilterOp::HueRotate(amount) => amount == 0.0,
-            FilterOp::Invert(amount) => amount == 0.0,
-            FilterOp::Opacity(value) => match value {
-                PropertyBinding::Value(amount) => amount == 1.0,
-                PropertyBinding::Binding(..) => {
-                    panic!("bug: binding value should be resolved");
-                }
-            },
-            FilterOp::Saturate(amount) => amount == 1.0,
-            FilterOp::Sepia(amount) => amount == 0.0,
-        }
-    }
-}
-
-trait StackingContextHelpers {
-    fn mix_blend_mode_for_compositing(&self) -> Option<MixBlendMode>;
-    fn filter_ops_for_compositing(
-        &self,
-        display_list: &BuiltDisplayList,
-        input_filters: ItemRange<FilterOp>,
-        properties: &SceneProperties,
-    ) -> Vec<FilterOp>;
-}
-
-impl StackingContextHelpers for StackingContext {
-    fn mix_blend_mode_for_compositing(&self) -> Option<MixBlendMode> {
-        match self.mix_blend_mode {
-            MixBlendMode::Normal => None,
-            _ => Some(self.mix_blend_mode),
-        }
-    }
-
-    fn filter_ops_for_compositing(
-        &self,
-        display_list: &BuiltDisplayList,
-        input_filters: ItemRange<FilterOp>,
-        properties: &SceneProperties,
-    ) -> Vec<FilterOp> {
-        let mut filters = vec![];
-        for filter in display_list.get(input_filters) {
-            let filter = filter.resolve(properties);
-            if filter.is_noop() {
-                continue;
-            }
-            filters.push(filter);
-        }
-        filters
-    }
-}
-
 impl Frame {
     pub fn new(config: FrameBuilderConfig) -> Frame {
         Frame {
             pipeline_epoch_map: FastHashMap::default(),
             clip_scroll_tree: ClipScrollTree::new(),
             id: FrameId(0),
             frame_builder: None,
             frame_builder_config: config,
@@ -329,26 +258,21 @@ impl Frame {
         inner_rect: DeviceUintRect,
         device_pixel_ratio: f32,
     ) {
         let root_pipeline_id = match scene.root_pipeline_id {
             Some(root_pipeline_id) => root_pipeline_id,
             None => return,
         };
 
-        let root_pipeline = match scene.pipeline_map.get(&root_pipeline_id) {
+        let root_pipeline = match scene.pipelines.get(&root_pipeline_id) {
             Some(root_pipeline) => root_pipeline,
             None => return,
         };
 
-        let display_list = match scene.display_lists.get(&root_pipeline_id) {
-            Some(display_list) => display_list,
-            None => return,
-        };
-
         if window_size.width == 0 || window_size.height == 0 {
             error!("ERROR: Invalid window dimensions! Please call api.set_window_size()");
         }
 
         let old_scrolling_states = self.reset();
 
         self.pipeline_epoch_map
             .insert(root_pipeline_id, root_pipeline.epoch);
@@ -377,17 +301,17 @@ impl Frame {
             context.builder.setup_viewport_offset(
                 window_size,
                 inner_rect,
                 device_pixel_ratio,
                 &mut self.clip_scroll_tree,
             );
 
             self.flatten_root(
-                &mut display_list.iter(),
+                &mut root_pipeline.display_list.iter(),
                 root_pipeline_id,
                 &mut context,
                 &root_pipeline.content_size,
             );
         }
 
         self.frame_builder = Some(frame_builder);
         self.clip_scroll_tree
@@ -459,21 +383,22 @@ impl Frame {
         // Avoid doing unnecessary work for empty stacking contexts.
         if traversal.current_stacking_context_empty() {
             traversal.skip_current_stacking_context();
             return;
         }
 
         let composition_operations = {
             // TODO(optimization?): self.traversal.display_list()
-            let display_list = context
+            let display_list = &context
                 .scene
-                .display_lists
+                .pipelines
                 .get(&pipeline_id)
-                .expect("No display list?!");
+                .expect("No display list?!")
+                .display_list;
             CompositeOps::new(
                 stacking_context.filter_ops_for_compositing(
                     display_list,
                     filters,
                     &context.scene.properties,
                 ),
                 stacking_context.mix_blend_mode_for_compositing(),
             )
@@ -552,26 +477,21 @@ impl Frame {
         &mut self,
         pipeline_id: PipelineId,
         parent_id: ClipId,
         bounds: &LayerRect,
         local_clip: &LocalClip,
         context: &mut FlattenContext,
         reference_frame_relative_offset: LayerVector2D,
     ) {
-        let pipeline = match context.scene.pipeline_map.get(&pipeline_id) {
+        let pipeline = match context.scene.pipelines.get(&pipeline_id) {
             Some(pipeline) => pipeline,
             None => return,
         };
 
-        let display_list = match context.scene.display_lists.get(&pipeline_id) {
-            Some(display_list) => display_list,
-            None => return,
-        };
-
         let mut clip_region = ClipRegion::create_for_clip_node_with_local_clip(local_clip);
         clip_region.origin += reference_frame_relative_offset;
         let parent_pipeline_id = parent_id.pipeline_id();
         let clip_id = self.clip_scroll_tree
             .generate_new_clip_id(parent_pipeline_id);
         context.builder.add_clip_node(
             clip_id,
             parent_id,
@@ -600,17 +520,17 @@ impl Frame {
             pipeline_id,
             &iframe_rect,
             &pipeline.content_size,
             ScrollSensitivity::ScriptAndInputEvents,
             &mut self.clip_scroll_tree,
         );
 
         self.flatten_root(
-            &mut display_list.iter(),
+            &mut pipeline.display_list.iter(),
             pipeline_id,
             context,
             &pipeline.content_size,
         );
 
         context.builder.pop_reference_frame();
     }
 
@@ -679,17 +599,17 @@ impl Frame {
                         );
                     }
                     None => {
                         warn!("Unknown font instance key: {:?}", text_info.font_key);
                     }
                 }
             }
             SpecificDisplayItem::Rectangle(ref info) => {
-                if !self.try_to_add_rectangle_splitting_on_clip(
+                if !try_to_add_rectangle_splitting_on_clip(
                     context,
                     &prim_info,
                     &info.color,
                     &clip_and_scroll,
                 ) {
                     context.builder.add_solid_rectangle(
                         clip_and_scroll,
                         &prim_info,
@@ -875,75 +795,16 @@ impl Frame {
             }
             SpecificDisplayItem::PopTextShadow => {
                 context.builder.pop_text_shadow();
             }
         }
         None
     }
 
-    /// Try to optimize the rendering of a solid rectangle that is clipped by a single
-    /// rounded rectangle, by only masking the parts of the rectangle that intersect
-    /// the rounded parts of the clip. This is pretty simple now, so has a lot of
-    /// potential for further optimizations.
-    fn try_to_add_rectangle_splitting_on_clip(
-        &mut self,
-        context: &mut FlattenContext,
-        info: &LayerPrimitiveInfo,
-        color: &ColorF,
-        clip_and_scroll: &ClipAndScrollInfo,
-    ) -> bool {
-        // If this rectangle is not opaque, splitting the rectangle up
-        // into an inner opaque region just ends up hurting batching and
-        // doing more work than necessary.
-        if color.a != 1.0 {
-            return false;
-        }
-
-        let inner_unclipped_rect = match &info.local_clip {
-            &LocalClip::Rect(_) => return false,
-            &LocalClip::RoundedRect(_, ref region) => region.get_inner_rect_full(),
-        };
-        let inner_unclipped_rect = match inner_unclipped_rect {
-            Some(rect) => rect,
-            None => return false,
-        };
-
-        // The inner rectangle is not clipped by its assigned clipping node, so we can
-        // let it be clipped by the parent of the clipping node, which may result in
-        // less masking some cases.
-        let mut clipped_rects = Vec::new();
-        subtract_rect(&info.rect, &inner_unclipped_rect, &mut clipped_rects);
-
-        let prim_info = LayerPrimitiveInfo {
-            rect: inner_unclipped_rect,
-            local_clip: LocalClip::from(*info.local_clip.clip_rect()),
-            is_backface_visible: info.is_backface_visible,
-        };
-
-        context.builder.add_solid_rectangle(
-            *clip_and_scroll,
-            &prim_info,
-            color,
-            PrimitiveFlags::None,
-        );
-
-        for clipped_rect in &clipped_rects {
-            let mut info = info.clone();
-            info.rect = *clipped_rect;
-            context.builder.add_solid_rectangle(
-                *clip_and_scroll,
-                &info,
-                color,
-                PrimitiveFlags::None,
-            );
-        }
-        true
-    }
-
     fn flatten_root<'a>(
         &mut self,
         traversal: &mut BuiltDisplayListIter<'a>,
         pipeline_id: PipelineId,
         context: &mut FlattenContext,
         content_size: &LayoutSize,
     ) {
         context.builder.push_stacking_context(
@@ -960,17 +821,17 @@ impl Frame {
         // FIXME(mrobinson): Currently only the first one will, which for the moment is
         // sufficient for all our use cases.
         context.builder.notify_waiting_for_root_stacking_context();
 
         // For the root pipeline, there's no need to add a full screen rectangle
         // here, as it's handled by the framebuffer clear.
         let clip_id = ClipId::root_scroll_node(pipeline_id);
         if context.scene.root_pipeline_id != Some(pipeline_id) {
-            if let Some(pipeline) = context.scene.pipeline_map.get(&pipeline_id) {
+            if let Some(pipeline) = context.scene.pipelines.get(&pipeline_id) {
                 if let Some(bg_color) = pipeline.background_color {
                     let root_bounds = LayerRect::new(LayerPoint::zero(), *content_size);
                     let info = LayerPrimitiveInfo::new(root_bounds);
                     context.builder.add_solid_rectangle(
                         ClipAndScrollInfo::simple(clip_id),
                         &info,
                         &bg_color,
                         PrimitiveFlags::None,
@@ -1338,58 +1199,116 @@ impl Frame {
             );
         }
     }
 
     pub fn build(
         &mut self,
         resource_cache: &mut ResourceCache,
         gpu_cache: &mut GpuCache,
-        display_lists: &DisplayListMap,
+        pipelines: &FastHashMap<PipelineId, ScenePipeline>,
         device_pixel_ratio: f32,
         pan: LayerPoint,
         output_pipelines: &FastHashSet<PipelineId>,
         texture_cache_profile: &mut TextureCacheProfileCounters,
         gpu_cache_profile: &mut GpuCacheProfileCounters,
     ) -> RendererFrame {
         self.clip_scroll_tree.update_all_node_transforms(pan);
         let frame = self.build_frame(
             resource_cache,
             gpu_cache,
-            display_lists,
+            pipelines,
             device_pixel_ratio,
             output_pipelines,
             texture_cache_profile,
             gpu_cache_profile,
         );
         frame
     }
 
     fn build_frame(
         &mut self,
         resource_cache: &mut ResourceCache,
         gpu_cache: &mut GpuCache,
-        display_lists: &DisplayListMap,
+        pipelines: &FastHashMap<PipelineId, ScenePipeline>,
         device_pixel_ratio: f32,
         output_pipelines: &FastHashSet<PipelineId>,
         texture_cache_profile: &mut TextureCacheProfileCounters,
         gpu_cache_profile: &mut GpuCacheProfileCounters,
     ) -> RendererFrame {
         let mut frame_builder = self.frame_builder.take();
         let frame = frame_builder.as_mut().map(|builder| {
             builder.build(
                 resource_cache,
                 gpu_cache,
                 self.id,
                 &mut self.clip_scroll_tree,
-                display_lists,
+                pipelines,
                 device_pixel_ratio,
                 output_pipelines,
                 texture_cache_profile,
                 gpu_cache_profile,
             )
         });
         self.frame_builder = frame_builder;
 
         let nodes_bouncing_back = self.clip_scroll_tree.collect_nodes_bouncing_back();
         RendererFrame::new(self.pipeline_epoch_map.clone(), nodes_bouncing_back, frame)
     }
 }
+
+/// Try to optimize the rendering of a solid rectangle that is clipped by a single
+/// rounded rectangle, by only masking the parts of the rectangle that intersect
+/// the rounded parts of the clip. This is pretty simple now, so has a lot of
+/// potential for further optimizations.
+fn try_to_add_rectangle_splitting_on_clip(
+    context: &mut FlattenContext,
+    info: &LayerPrimitiveInfo,
+    color: &ColorF,
+    clip_and_scroll: &ClipAndScrollInfo,
+) -> bool {
+    // If this rectangle is not opaque, splitting the rectangle up
+    // into an inner opaque region just ends up hurting batching and
+    // doing more work than necessary.
+    if color.a != 1.0 {
+        return false;
+    }
+
+    let inner_unclipped_rect = match &info.local_clip {
+        &LocalClip::Rect(_) => return false,
+        &LocalClip::RoundedRect(_, ref region) => region.get_inner_rect_full(),
+    };
+    let inner_unclipped_rect = match inner_unclipped_rect {
+        Some(rect) => rect,
+        None => return false,
+    };
+
+    // The inner rectangle is not clipped by its assigned clipping node, so we can
+    // let it be clipped by the parent of the clipping node, which may result in
+    // less masking some cases.
+    let mut clipped_rects = Vec::new();
+    subtract_rect(&info.rect, &inner_unclipped_rect, &mut clipped_rects);
+
+    let prim_info = LayerPrimitiveInfo {
+        rect: inner_unclipped_rect,
+        local_clip: LocalClip::from(*info.local_clip.clip_rect()),
+        is_backface_visible: info.is_backface_visible,
+    };
+
+    context.builder.add_solid_rectangle(
+        *clip_and_scroll,
+        &prim_info,
+        color,
+        PrimitiveFlags::None,
+    );
+
+    for clipped_rect in &clipped_rects {
+        let mut info = info.clone();
+        info.rect = *clipped_rect;
+        context.builder.add_solid_rectangle(
+            *clip_and_scroll,
+            &info,
+            color,
+            PrimitiveFlags::None,
+        );
+    }
+    true
+}
--- a/gfx/webrender/src/frame_builder.rs
+++ b/gfx/webrender/src/frame_builder.rs
@@ -8,16 +8,17 @@ use api::{DeviceIntPoint, DeviceIntRect,
 use api::{device_length, ExtendMode, FilterOp, FontInstance, FontRenderMode};
 use api::{GlyphInstance, GlyphOptions, GradientStop};
 use api::{ImageKey, ImageRendering, ItemRange, LayerPoint, LayerPrimitiveInfo, LayerRect,
           LayerSize};
 use api::{LayerToScrollTransform, LayerVector2D, LayoutVector2D, LineOrientation, LineStyle};
 use api::{LocalClip, PipelineId, RepeatMode, ScrollSensitivity, SubpixelDirection, TextShadow};
 use api::{TileOffset, TransformStyle, WorldPixel, YuvColorSpace, YuvData};
 use app_units::Au;
+use border::ImageBorderSegment;
 use clip::{ClipMode, ClipRegion, ClipSource, ClipSources, ClipStore};
 use clip_scroll_node::{ClipInfo, ClipScrollNode, NodeType};
 use clip_scroll_tree::ClipScrollTree;
 use euclid::{SideOffsets2D, vec2, vec3};
 use frame::FrameId;
 use gpu_cache::GpuCache;
 use internal_types::{FastHashMap, FastHashSet, HardwareCompositeOp};
 use plane_split::{BspSplitter, Polygon, Splitter};
@@ -25,76 +26,25 @@ use prim_store::{BoxShadowPrimitiveCpu, 
 use prim_store::{GradientPrimitiveCpu, ImagePrimitiveCpu, LinePrimitive, PrimitiveKind};
 use prim_store::{PrimitiveContainer, PrimitiveIndex};
 use prim_store::{PrimitiveStore, RadialGradientPrimitiveCpu, TextRunMode};
 use prim_store::{RectanglePrimitive, TextRunPrimitiveCpu, TextShadowPrimitiveCpu};
 use profiler::{FrameProfileCounters, GpuCacheProfileCounters, TextureCacheProfileCounters};
 use render_task::{AlphaRenderItem, ClipWorkItem, RenderTask};
 use render_task::{RenderTaskId, RenderTaskLocation, RenderTaskTree};
 use resource_cache::ResourceCache;
+use scene::ScenePipeline;
 use std::{mem, usize, f32, i32};
-use tiling::{ClipScrollGroup, ClipScrollGroupIndex, CompositeOps, DisplayListMap, Frame};
+use tiling::{ClipScrollGroup, ClipScrollGroupIndex, CompositeOps, Frame};
 use tiling::{ContextIsolation, StackingContextIndex};
 use tiling::{PackedLayer, PackedLayerIndex, PrimitiveFlags, PrimitiveRunCmd, RenderPass};
 use tiling::{RenderTargetContext, ScrollbarPrimitive, StackingContext};
 use util::{self, pack_as_float, recycle_vec, subtract_rect};
 use util::{MatrixHelpers, RectHelpers};
 
-#[derive(Debug, Clone)]
-struct ImageBorderSegment {
-    geom_rect: LayerRect,
-    sub_rect: TexelRect,
-    stretch_size: LayerSize,
-    tile_spacing: LayerSize,
-}
-
-impl ImageBorderSegment {
-    fn new(
-        rect: LayerRect,
-        sub_rect: TexelRect,
-        repeat_horizontal: RepeatMode,
-        repeat_vertical: RepeatMode,
-    ) -> ImageBorderSegment {
-        let tile_spacing = LayerSize::zero();
-
-        debug_assert!(sub_rect.uv1.x >= sub_rect.uv0.x);
-        debug_assert!(sub_rect.uv1.y >= sub_rect.uv0.y);
-
-        let image_size = LayerSize::new(
-            sub_rect.uv1.x - sub_rect.uv0.x,
-            sub_rect.uv1.y - sub_rect.uv0.y,
-        );
-
-        let stretch_size_x = match repeat_horizontal {
-            RepeatMode::Stretch => rect.size.width,
-            RepeatMode::Repeat => image_size.width,
-            RepeatMode::Round | RepeatMode::Space => {
-                error!("Round/Space not supported yet!");
-                rect.size.width
-            }
-        };
-
-        let stretch_size_y = match repeat_vertical {
-            RepeatMode::Stretch => rect.size.height,
-            RepeatMode::Repeat => image_size.height,
-            RepeatMode::Round | RepeatMode::Space => {
-                error!("Round/Space not supported yet!");
-                rect.size.height
-            }
-        };
-
-        ImageBorderSegment {
-            geom_rect: rect,
-            sub_rect,
-            stretch_size: LayerSize::new(stretch_size_x, stretch_size_y),
-            tile_spacing,
-        }
-    }
-}
-
 /// Construct a polygon from stacking context boundaries.
 /// `anchor` here is an index that's going to be preserved in all the
 /// splits of the polygon.
 fn make_polygon(
     stacking_context: &StackingContext,
     node: &ClipScrollNode,
     anchor: usize,
 ) -> Polygon<f32, WorldPixel> {
@@ -1060,16 +1010,17 @@ impl FrameBuilder {
         let prim_font = FontInstance::new(
             font.font_key,
             font.size,
             *color,
             normal_render_mode,
             subpx_dir,
             font.platform_options,
             font.variations.clone(),
+            font.synthetic_italics,
         );
         let prim = TextRunPrimitiveCpu {
             font: prim_font,
             glyph_range,
             glyph_count,
             glyph_gpu_blocks: Vec::new(),
             glyph_keys: Vec::new(),
             shadow_render_mode,
@@ -1087,16 +1038,22 @@ impl FrameBuilder {
         // TODO(gw): Refactor to avoid having to store them in a Vec first.
         let mut fast_text_shadow_prims = Vec::new();
         for shadow_prim_index in &self.shadow_prim_stack {
             let shadow_metadata = &self.prim_store.cpu_metadata[shadow_prim_index.0];
             let shadow_prim = &self.prim_store.cpu_text_shadows[shadow_metadata.cpu_prim_index.0];
             if shadow_prim.shadow.blur_radius == 0.0 {
                 let mut text_prim = prim.clone();
                 text_prim.font.color = shadow_prim.shadow.color.into();
+                // If we have translucent text, we need to ensure it won't go
+                // through the subpixel blend mode, which doesn't work with
+                // traditional alpha blending.
+                if shadow_prim.shadow.color.a != 1.0 {
+                    text_prim.font.render_mode = text_prim.font.render_mode.limit_by(FontRenderMode::Alpha);
+                }
                 text_prim.color = shadow_prim.shadow.color;
                 text_prim.offset += shadow_prim.shadow.offset;
                 fast_text_shadow_prims.push(text_prim);
             }
         }
         for text_prim in fast_text_shadow_prims {
             let rect = info.rect;
             let mut info = info.clone();
@@ -1428,29 +1385,29 @@ impl FrameBuilder {
     }
 
     /// Compute the contribution (bounding rectangles, and resources) of layers and their
     /// primitives in screen space.
     fn build_layer_screen_rects_and_cull_layers(
         &mut self,
         screen_rect: &DeviceIntRect,
         clip_scroll_tree: &mut ClipScrollTree,
-        display_lists: &DisplayListMap,
+        pipelines: &FastHashMap<PipelineId, ScenePipeline>,
         resource_cache: &mut ResourceCache,
         gpu_cache: &mut GpuCache,
         render_tasks: &mut RenderTaskTree,
         profile_counters: &mut FrameProfileCounters,
         device_pixel_ratio: f32,
     ) {
         profile_scope!("cull");
         LayerRectCalculationAndCullingPass::create_and_run(
             self,
             screen_rect,
             clip_scroll_tree,
-            display_lists,
+            pipelines,
             resource_cache,
             gpu_cache,
             render_tasks,
             profile_counters,
             device_pixel_ratio,
         );
     }
 
@@ -1803,17 +1760,17 @@ impl FrameBuilder {
     }
 
     pub fn build(
         &mut self,
         resource_cache: &mut ResourceCache,
         gpu_cache: &mut GpuCache,
         frame_id: FrameId,
         clip_scroll_tree: &mut ClipScrollTree,
-        display_lists: &DisplayListMap,
+        pipelines: &FastHashMap<PipelineId, ScenePipeline>,
         device_pixel_ratio: f32,
         output_pipelines: &FastHashSet<PipelineId>,
         texture_cache_profile: &mut TextureCacheProfileCounters,
         gpu_cache_profile: &mut GpuCacheProfileCounters,
     ) -> Frame {
         profile_scope!("build");
 
         let mut profile_counters = FrameProfileCounters::new();
@@ -1834,17 +1791,17 @@ impl FrameBuilder {
 
         self.update_scroll_bars(clip_scroll_tree, gpu_cache);
 
         let mut render_tasks = RenderTaskTree::new();
 
         self.build_layer_screen_rects_and_cull_layers(
             &screen_rect,
             clip_scroll_tree,
-            display_lists,
+            pipelines,
             resource_cache,
             gpu_cache,
             &mut render_tasks,
             &mut profile_counters,
             device_pixel_ratio,
         );
 
         let main_render_task_id = self.build_render_task(
@@ -1913,27 +1870,21 @@ impl FrameBuilder {
             layer_texture_data: self.packed_layers.clone(),
             render_tasks,
             deferred_resolves,
             gpu_cache_updates: Some(gpu_cache_updates),
         }
     }
 }
 
-#[derive(Debug, Clone, Copy)]
-struct LayerClipBounds {
-    outer: DeviceIntRect,
-    inner: DeviceIntRect,
-}
-
 struct LayerRectCalculationAndCullingPass<'a> {
     frame_builder: &'a mut FrameBuilder,
     screen_rect: &'a DeviceIntRect,
     clip_scroll_tree: &'a mut ClipScrollTree,
-    display_lists: &'a DisplayListMap,
+    pipelines: &'a FastHashMap<PipelineId, ScenePipeline>,
     resource_cache: &'a mut ResourceCache,
     gpu_cache: &'a mut GpuCache,
     profile_counters: &'a mut FrameProfileCounters,
     device_pixel_ratio: f32,
     stacking_context_stack: Vec<StackingContextIndex>,
     render_tasks: &'a mut RenderTaskTree,
 
     /// A cached clip info stack, which should handle the most common situation,
@@ -1946,28 +1897,28 @@ struct LayerRectCalculationAndCullingPas
     current_clip_info: Option<(ClipId, Option<DeviceIntRect>)>,
 }
 
 impl<'a> LayerRectCalculationAndCullingPass<'a> {
     fn create_and_run(
         frame_builder: &'a mut FrameBuilder,
         screen_rect: &'a DeviceIntRect,
         clip_scroll_tree: &'a mut ClipScrollTree,
-        display_lists: &'a DisplayListMap,
+        pipelines: &'a FastHashMap<PipelineId, ScenePipeline>,
         resource_cache: &'a mut ResourceCache,
         gpu_cache: &'a mut GpuCache,
         render_tasks: &'a mut RenderTaskTree,
         profile_counters: &'a mut FrameProfileCounters,
         device_pixel_ratio: f32,
     ) {
         let mut pass = LayerRectCalculationAndCullingPass {
             frame_builder,
             screen_rect,
             clip_scroll_tree,
-            display_lists,
+            pipelines,
             resource_cache,
             gpu_cache,
             profile_counters,
             device_pixel_ratio,
             stacking_context_stack: Vec::new(),
             current_clip_stack: Vec::new(),
             current_clip_info: None,
             render_tasks,
@@ -2247,19 +2198,20 @@ impl<'a> LayerRectCalculationAndCullingP
             match self.rebuild_clip_info_stack_if_necessary(clip_and_scroll.clip_node_id()) {
                 Some(rect) => rect,
                 None => return,
             };
 
         let stacking_context =
             &mut self.frame_builder.stacking_context_store[stacking_context_index.0];
         let packed_layer = &self.frame_builder.packed_layers[packed_layer_index.0];
-        let display_list = self.display_lists
+        let display_list = &self.pipelines
             .get(&pipeline_id)
-            .expect("No display list?");
+            .expect("No display list?")
+            .display_list;
         debug!(
             "\tclip_bounds {:?}, layer_local_clip {:?}",
             clip_bounds,
             packed_layer.local_clip_rect
         );
 
         if !stacking_context.is_backface_visible && packed_layer.transform.is_backface_visible() {
             return;
--- a/gfx/webrender/src/glyph_rasterizer.rs
+++ b/gfx/webrender/src/glyph_rasterizer.rs
@@ -406,16 +406,17 @@ fn raterize_200_glyphs() {
     let font = FontInstance::new(
         font_key,
         Au::from_px(32),
         ColorF::new(0.0, 0.0, 0.0, 1.0),
         FontRenderMode::Subpixel,
         SubpixelDirection::Horizontal,
         None,
         Vec::new(),
+        false,
     );
 
     let mut glyph_keys = Vec::with_capacity(200);
     for i in 0 .. 200 {
         glyph_keys.push(GlyphKey::new(
             i,
             LayoutPoint::zero(),
             font.render_mode,
--- a/gfx/webrender/src/platform/unix/font.rs
+++ b/gfx/webrender/src/platform/unix/font.rs
@@ -4,19 +4,19 @@
 
 use api::{FontInstance, FontKey, FontRenderMode, GlyphDimensions};
 use api::{NativeFontHandle, SubpixelDirection};
 use api::GlyphKey;
 use freetype::freetype::{FT_BBox, FT_Outline_Translate, FT_Pixel_Mode, FT_Render_Mode};
 use freetype::freetype::{FT_Done_Face, FT_Error, FT_Get_Char_Index, FT_Int32};
 use freetype::freetype::{FT_Done_FreeType, FT_Library_SetLcdFilter, FT_Pos};
 use freetype::freetype::{FT_F26Dot6, FT_Face, FT_Glyph_Format, FT_Long, FT_UInt};
-use freetype::freetype::{FT_GlyphSlot, FT_LcdFilter, FT_New_Memory_Face};
+use freetype::freetype::{FT_GlyphSlot, FT_LcdFilter, FT_New_Memory_Face, FT_Outline_Transform};
 use freetype::freetype::{FT_Init_FreeType, FT_Load_Glyph, FT_Render_Glyph};
-use freetype::freetype::{FT_Library, FT_Outline_Get_CBox, FT_Set_Char_Size};
+use freetype::freetype::{FT_Library, FT_Matrix, FT_Outline_Get_CBox, FT_Set_Char_Size};
 use internal_types::FastHashMap;
 use std::{mem, ptr, slice};
 use std::sync::Arc;
 
 // This constant is not present in the freetype
 // bindings due to bindgen not handling the way
 // the macro is defined.
 const FT_LOAD_TARGET_LIGHT: FT_Int32 = 1 << 16;
@@ -300,16 +300,30 @@ impl FontContext {
             let outline = &(*slot).outline;
             let mut cbox: FT_BBox = mem::uninitialized();
             FT_Outline_Get_CBox(outline, &mut cbox);
             FT_Outline_Translate(
                 outline,
                 dx - ((cbox.xMin + dx) & !63),
                 dy - ((cbox.yMin + dy) & !63),
             );
+
+            if font.synthetic_italics {
+                // These magic numbers are pre-encoded fixed point
+                // values that apply ~12 degree shear. Borrowed
+                // from the Freetype implementation of the
+                // FT_GlyphSlot_Oblique function.
+                let transform = FT_Matrix {
+                    xx: 0x10000,
+                    yx: 0x00000,
+                    xy: 0x0366A,
+                    yy: 0x10000,
+                };
+                FT_Outline_Transform(outline, &transform);
+            }
         }
 
         let result = unsafe { FT_Render_Glyph(slot, render_mode) };
         if result != SUCCESS {
             error!(
                 "Unable to rasterize {:?} with {:?}, {:?}",
                 key,
                 render_mode,
--- a/gfx/webrender/src/render_backend.rs
+++ b/gfx/webrender/src/render_backend.rs
@@ -101,17 +101,17 @@ impl Document {
         let accumulated_scale_factor = self.accumulated_scale_factor();
         let pan = LayerPoint::new(
             self.pan.x as f32 / accumulated_scale_factor,
             self.pan.y as f32 / accumulated_scale_factor,
         );
         self.frame.build(
             resource_cache,
             gpu_cache,
-            &self.scene.display_lists,
+            &self.scene.pipelines,
             accumulated_scale_factor,
             pan,
             &self.output_pipelines,
             &mut resource_profile.texture_cache,
             &mut resource_profile.gpu_cache,
         )
     }
 }
@@ -296,17 +296,17 @@ impl RenderBackend {
                 );
 
                 DocumentOp::Built
             }
             DocumentMsg::SetRootPipeline(pipeline_id) => {
                 profile_scope!("SetRootPipeline");
 
                 doc.scene.set_root_pipeline_id(pipeline_id);
-                if doc.scene.display_lists.get(&pipeline_id).is_some() {
+                if doc.scene.pipelines.get(&pipeline_id).is_some() {
                     let _timer = profile_counters.total_time.timer();
                     doc.build_scene(&mut self.resource_cache);
                     DocumentOp::Built
                 } else {
                     DocumentOp::Nop
                 }
             }
             DocumentMsg::RemovePipeline(pipeline_id) => {
@@ -640,19 +640,19 @@ impl RenderBackend {
 
     #[cfg(feature = "debugger")]
     fn get_docs_for_debugger(&self) -> String {
         let mut docs = debug_server::DocumentList::new();
 
         for (_, doc) in &self.documents {
             let mut debug_doc = debug_server::TreeNode::new("document");
 
-            for (_, display_list) in &doc.scene.display_lists {
+            for (_, pipeline) in &doc.scene.pipelines {
                 let mut debug_dl = debug_server::TreeNode::new("display_list");
-                self.traverse_items(&mut display_list.iter(), &mut debug_dl);
+                self.traverse_items(&mut pipeline.display_list.iter(), &mut debug_dl);
                 debug_doc.add_child(debug_dl);
             }
 
             docs.add(debug_doc);
         }
 
         serde_json::to_string(&docs).unwrap()
     }
--- a/gfx/webrender/src/resource_cache.rs
+++ b/gfx/webrender/src/resource_cache.rs
@@ -332,32 +332,35 @@ impl ResourceCache {
         &mut self,
         instance_key: FontInstanceKey,
         font_key: FontKey,
         glyph_size: Au,
         options: Option<FontInstanceOptions>,
         platform_options: Option<FontInstancePlatformOptions>,
         variations: Vec<FontVariation>,
     ) {
-        let mut render_mode = FontRenderMode::Subpixel;
+        let mut requested_render_mode = FontRenderMode::Subpixel;
         let mut subpx_dir = SubpixelDirection::Horizontal;
         if let Some(options) = options {
-            render_mode = options.render_mode;
-            if render_mode == FontRenderMode::Mono {
-                subpx_dir = SubpixelDirection::None;
+            if let Some(render_mode) = options.render_mode {
+                requested_render_mode = render_mode;
             }
         }
+        if requested_render_mode == FontRenderMode::Mono {
+            subpx_dir = SubpixelDirection::None;
+        }
         let instance = FontInstance::new(
             font_key,
             glyph_size,
             ColorF::new(0.0, 0.0, 0.0, 1.0),
-            render_mode,
+            requested_render_mode,
             subpx_dir,
             platform_options,
             variations,
+            options.map_or(false, |opts| opts.synthetic_italics),
         );
         self.resources.font_instances.insert(instance_key, instance);
     }
 
     pub fn delete_font_instance(&mut self, instance_key: FontInstanceKey) {
         self.resources.font_instances.remove(&instance_key);
         if let Some(ref mut r) = self.blob_image_renderer {
             r.delete_font_instance(instance_key);
--- a/gfx/webrender/src/scene.rs
+++ b/gfx/webrender/src/scene.rs
@@ -1,14 +1,15 @@
 /* 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/. */
 
 use api::{BuiltDisplayList, ColorF, DynamicProperties, Epoch, LayerSize, LayoutSize};
-use api::{LayoutTransform, PipelineId, PropertyBinding, PropertyBindingId};
+use api::{FilterOp, LayoutTransform, PipelineId, PropertyBinding, PropertyBindingId};
+use api::{ItemRange, MixBlendMode, StackingContext};
 use internal_types::FastHashMap;
 
 /// Stores a map of the animated property bindings for the current display list. These
 /// can be used to animate the transform and/or opacity of a display list without
 /// re-submitting the display list itself.
 pub struct SceneProperties {
     transform_properties: FastHashMap<PropertyBindingId, LayoutTransform>,
     float_properties: FastHashMap<PropertyBindingId, f32>,
@@ -71,69 +72,137 @@ impl SceneProperties {
                     warn!("Property binding {:?} has an invalid value.", key);
                     default_value
                 }),
         }
     }
 }
 
 /// A representation of the layout within the display port for a given document or iframe.
-#[derive(Debug)]
 pub struct ScenePipeline {
     pub pipeline_id: PipelineId,
     pub epoch: Epoch,
     pub viewport_size: LayerSize,
     pub content_size: LayoutSize,
     pub background_color: Option<ColorF>,
+    pub display_list: BuiltDisplayList,
 }
 
 /// A complete representation of the layout bundling visible pipelines together.
 pub struct Scene {
     pub root_pipeline_id: Option<PipelineId>,
-    pub pipeline_map: FastHashMap<PipelineId, ScenePipeline>,
-    pub display_lists: FastHashMap<PipelineId, BuiltDisplayList>,
+    pub pipelines: FastHashMap<PipelineId, ScenePipeline>,
     pub properties: SceneProperties,
 }
 
 impl Scene {
     pub fn new() -> Scene {
         Scene {
             root_pipeline_id: None,
-            pipeline_map: FastHashMap::default(),
-            display_lists: FastHashMap::default(),
+            pipelines: FastHashMap::default(),
             properties: SceneProperties::new(),
         }
     }
 
     pub fn set_root_pipeline_id(&mut self, pipeline_id: PipelineId) {
         self.root_pipeline_id = Some(pipeline_id);
     }
 
     pub fn set_display_list(
         &mut self,
         pipeline_id: PipelineId,
         epoch: Epoch,
-        built_display_list: BuiltDisplayList,
+        display_list: BuiltDisplayList,
         background_color: Option<ColorF>,
         viewport_size: LayerSize,
         content_size: LayoutSize,
     ) {
-        self.display_lists.insert(pipeline_id, built_display_list);
-
         let new_pipeline = ScenePipeline {
             pipeline_id,
             epoch,
             viewport_size,
             content_size,
             background_color,
+            display_list,
         };
 
-        self.pipeline_map.insert(pipeline_id, new_pipeline);
+        self.pipelines.insert(pipeline_id, new_pipeline);
     }
 
     pub fn remove_pipeline(&mut self, pipeline_id: PipelineId) {
         if self.root_pipeline_id == Some(pipeline_id) {
             self.root_pipeline_id = None;
         }
-        self.display_lists.remove(&pipeline_id);
-        self.pipeline_map.remove(&pipeline_id);
+        self.pipelines.remove(&pipeline_id);
     }
 }
+
+pub trait FilterOpHelpers {
+    fn resolve(self, properties: &SceneProperties) -> FilterOp;
+    fn is_noop(&self) -> bool;
+}
+
+impl FilterOpHelpers for FilterOp {
+    fn resolve(self, properties: &SceneProperties) -> FilterOp {
+        match self {
+            FilterOp::Opacity(ref value) => {
+                let amount = properties.resolve_float(value, 1.0);
+                FilterOp::Opacity(PropertyBinding::Value(amount))
+            }
+            _ => self,
+        }
+    }
+
+    fn is_noop(&self) -> bool {
+        match *self {
+            FilterOp::Blur(length) => length == 0.0,
+            FilterOp::Brightness(amount) => amount == 1.0,
+            FilterOp::Contrast(amount) => amount == 1.0,
+            FilterOp::Grayscale(amount) => amount == 0.0,
+            FilterOp::HueRotate(amount) => amount == 0.0,
+            FilterOp::Invert(amount) => amount == 0.0,
+            FilterOp::Opacity(value) => match value {
+                PropertyBinding::Value(amount) => amount == 1.0,
+                PropertyBinding::Binding(..) => {
+                    panic!("bug: binding value should be resolved");
+                }
+            },
+            FilterOp::Saturate(amount) => amount == 1.0,
+            FilterOp::Sepia(amount) => amount == 0.0,
+        }
+    }
+}
+
+pub trait StackingContextHelpers {
+    fn mix_blend_mode_for_compositing(&self) -> Option<MixBlendMode>;
+    fn filter_ops_for_compositing(
+        &self,
+        display_list: &BuiltDisplayList,
+        input_filters: ItemRange<FilterOp>,
+        properties: &SceneProperties,
+    ) -> Vec<FilterOp>;
+}
+
+impl StackingContextHelpers for StackingContext {
+    fn mix_blend_mode_for_compositing(&self) -> Option<MixBlendMode> {
+        match self.mix_blend_mode {
+            MixBlendMode::Normal => None,
+            _ => Some(self.mix_blend_mode),
+        }
+    }
+
+    fn filter_ops_for_compositing(
+        &self,
+        display_list: &BuiltDisplayList,
+        input_filters: ItemRange<FilterOp>,
+        properties: &SceneProperties,
+    ) -> Vec<FilterOp> {
+        let mut filters = vec![];
+        for filter in display_list.get(input_filters) {
+            let filter = filter.resolve(properties);
+            if filter.is_noop() {
+                continue;
+            }
+            filters.push(filter);
+        }
+        filters
+    }
+}
--- a/gfx/webrender/src/tiling.rs
+++ b/gfx/webrender/src/tiling.rs
@@ -1,13 +1,13 @@
 /* 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/. */
 
-use api::{BuiltDisplayList, ClipAndScrollInfo, ClipId, ColorF, DeviceIntPoint, ImageKey};
+use api::{ClipAndScrollInfo, ClipId, ColorF, DeviceIntPoint, ImageKey};
 use api::{DeviceIntRect, DeviceIntSize, DeviceUintPoint, DeviceUintSize};
 use api::{ExternalImageType, FilterOp, FontRenderMode, ImageRendering, LayerRect};
 use api::{LayerToWorldTransform, MixBlendMode, PipelineId, PropertyBinding, TransformStyle};
 use api::{LayerVector2D, TileOffset, WorldToLayerTransform, YuvColorSpace, YuvFormat};
 use border::{BorderCornerInstance, BorderCornerSide};
 use clip::{ClipSource, ClipStore};
 use device::Texture;
 use gpu_cache::{GpuCache, GpuCacheAddress, GpuCacheHandle, GpuCacheUpdateList};
@@ -28,18 +28,16 @@ use std::{cmp, usize, f32, i32};
 use texture_allocator::GuillotineAllocator;
 use util::{TransformedRect, TransformedRectKind};
 
 // Special sentinel value recognized by the shader. It is considered to be
 // a dummy task that doesn't mask out anything.
 const OPAQUE_TASK_ADDRESS: RenderTaskAddress = RenderTaskAddress(i32::MAX as u32);
 const MIN_TARGET_SIZE: u32 = 2048;
 
-pub type DisplayListMap = FastHashMap<PipelineId, BuiltDisplayList>;
-
 trait AlphaBatchHelpers {
     fn get_blend_mode(
         &self,
         metadata: &PrimitiveMetadata,
         transform_kind: TransformedRectKind,
     ) -> BlendMode;
 }
 
--- a/gfx/webrender_api/Cargo.toml
+++ b/gfx/webrender_api/Cargo.toml
@@ -1,11 +1,11 @@
 [package]
 name = "webrender_api"
-version = "0.51.0"
+version = "0.52.0"
 authors = ["Glenn Watson <gw@intuitionlibrary.com>"]
 license = "MPL-2.0"
 repository = "https://github.com/servo/webrender"
 
 [features]
 nightly = ["euclid/unstable", "serde/unstable"]
 ipc = ["ipc-channel"]
 
@@ -16,13 +16,13 @@ byteorder = "1.0"
 euclid = "0.15"
 fxhash = "0.2.1"
 heapsize = ">= 0.3.6, < 0.5"
 ipc-channel = {version = "0.8", optional = true}
 serde = { version = "1.0", features = ["rc", "derive"] }
 time = "0.1"
 
 [target.'cfg(target_os = "macos")'.dependencies]
-core-foundation = "0.3"
-core-graphics = "0.8"
+core-foundation = "0.4"
+core-graphics = "0.9"
 
 [target.'cfg(target_os = "windows")'.dependencies]
 dwrote = "0.4"
--- a/gfx/webrender_api/src/font.rs
+++ b/gfx/webrender_api/src/font.rs
@@ -110,17 +110,17 @@ impl FontRenderMode {
         // Following the conventions of Gecko and Skia, we want
         // to quantize the subpixel position, such that abs(pos) gives:
         // [0.0, 0.125) -> Zero
         // [0.125, 0.375) -> Quarter
         // [0.375, 0.625) -> Half
         // [0.625, 0.875) -> ThreeQuarters,
         // [0.875, 1.0) -> Zero
         // The unit tests below check for this.
-        let apos = (pos.fract() * 8.0).abs() as i32;
+        let apos = ((pos - pos.floor()) * 8.0) as i32;
 
         match apos {
             0 | 7 => SubpixelOffset::Zero,
             1...2 => SubpixelOffset::Quarter,
             3...4 => SubpixelOffset::Half,
             5...6 => SubpixelOffset::ThreeQuarters,
             _ => unreachable!("bug: unexpected quantized result"),
         }
@@ -189,17 +189,18 @@ impl Hash for FontVariation {
 #[derive(Clone, Copy, Debug, Deserialize, Hash, Eq, PartialEq, PartialOrd, Ord, Serialize)]
 pub struct GlyphOptions {
     pub render_mode: FontRenderMode,
 }
 
 #[repr(C)]
 #[derive(Clone, Copy, Debug, Deserialize, Hash, Eq, PartialEq, PartialOrd, Ord, Serialize)]
 pub struct FontInstanceOptions {
-    pub render_mode: FontRenderMode,
+    pub render_mode: Option<FontRenderMode>,
+    pub synthetic_italics: bool,
 }
 
 #[repr(C)]
 #[derive(Clone, Copy, Debug, Deserialize, Hash, Eq, PartialEq, PartialOrd, Ord, Serialize)]
 pub struct FontInstancePlatformOptions {
     // These are currently only used on windows for dwrite fonts.
     pub use_embedded_bitmap: bool,
     pub force_gdi_rendering: bool,
@@ -214,43 +215,46 @@ pub struct FontInstance {
     // TODO(gw): Perhaps consider having LogicalAu and DeviceAu
     //           or something similar to that.
     pub size: Au,
     pub color: ColorU,
     pub render_mode: FontRenderMode,
     pub subpx_dir: SubpixelDirection,
     pub platform_options: Option<FontInstancePlatformOptions>,
     pub variations: Vec<FontVariation>,
+    pub synthetic_italics: bool,
 }
 
 impl FontInstance {
     pub fn new(
         font_key: FontKey,
         size: Au,
         mut color: ColorF,
         render_mode: FontRenderMode,
         subpx_dir: SubpixelDirection,
         platform_options: Option<FontInstancePlatformOptions>,
         variations: Vec<FontVariation>,
+        synthetic_italics: bool,
     ) -> FontInstance {
         // In alpha/mono mode, the color of the font is irrelevant.
         // Forcing it to black in those cases saves rasterizing glyphs
         // of different colors when not needed.
         if render_mode != FontRenderMode::Subpixel {
             color = ColorF::new(0.0, 0.0, 0.0, 1.0);
         }
 
         FontInstance {
             font_key,
             size,
             color: color.into(),
             render_mode,
             subpx_dir,
             platform_options,
             variations,
+            synthetic_italics,
         }
     }
 
     pub fn get_subpx_offset(&self, glyph: &GlyphKey) -> (f64, f64) {
         match self.subpx_dir {
             SubpixelDirection::None => (0.0, 0.0),
             SubpixelDirection::Horizontal => (glyph.subpixel_offset.into(), 0.0),
             SubpixelDirection::Vertical => (0.0, glyph.subpixel_offset.into()),
@@ -342,13 +346,13 @@ mod test {
         assert_eq!(rm.subpixel_quantize_offset(0.89), SubpixelOffset::Zero);
         assert_eq!(rm.subpixel_quantize_offset(0.91), SubpixelOffset::Zero);
         assert_eq!(rm.subpixel_quantize_offset(0.967), SubpixelOffset::Zero);
         assert_eq!(rm.subpixel_quantize_offset(0.999), SubpixelOffset::Zero);
 
         assert_eq!(rm.subpixel_quantize_offset(-1.0), SubpixelOffset::Zero);
         assert_eq!(rm.subpixel_quantize_offset(1.0), SubpixelOffset::Zero);
         assert_eq!(rm.subpixel_quantize_offset(1.5), SubpixelOffset::Half);
-        assert_eq!(rm.subpixel_quantize_offset(-1.625), SubpixelOffset::ThreeQuarters);
-        assert_eq!(rm.subpixel_quantize_offset(-4.33), SubpixelOffset::Quarter);
+        assert_eq!(rm.subpixel_quantize_offset(-1.625), SubpixelOffset::Half);
+        assert_eq!(rm.subpixel_quantize_offset(-4.33), SubpixelOffset::ThreeQuarters);
 
     }
 }
--- a/gfx/webrender_bindings/Cargo.toml
+++ b/gfx/webrender_bindings/Cargo.toml
@@ -1,19 +1,19 @@
 [package]
 name = "webrender_bindings"
 version = "0.1.0"
 authors = ["The Mozilla Project Developers"]
 license = "MPL-2.0"
 
 [dependencies]
-webrender_api = {path = "../webrender_api", version = "0.51.0"}
+webrender_api = {path = "../webrender_api", version = "0.52.0"}
 bincode = "0.8"
 rayon = "0.8"
 thread_profiler = "0.1.1"
 euclid = "0.15"
 app_units = "0.5.6"
 gleam = "0.4"
 
 [dependencies.webrender]
 path = "../webrender"
-version = "0.51.0"
+version = "0.52.0"
 default-features = false