Bug 1403971 - Update webrender to commit aa81aebba2c1b8ff8af5d40796154d66349fd131. r=mstange
authorKartikaya Gupta <kgupta@mozilla.com>
Tue, 03 Oct 2017 13:15:13 -0400
changeset 384264 01d20d2ebb57d1be52e87e4666e661577335c16e
parent 384263 89ffef35daade74a97ad36134a78898a8cf3155b
child 384265 65a5054a1f922b83929c80658062f441ca3da6a0
child 384279 ccd48f2cb1eb49321295252aef94d7d482c2f101
push id95753
push userkwierso@gmail.com
push dateTue, 03 Oct 2017 21:54:22 +0000
treeherdermozilla-inbound@6669a57218a0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmstange
bugs1403971
milestone58.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1403971 - Update webrender to commit aa81aebba2c1b8ff8af5d40796154d66349fd131. r=mstange MozReview-Commit-ID: FE8qFq751hv
gfx/doc/README.webrender
gfx/webrender/Cargo.toml
gfx/webrender/examples/basic.rs
gfx/webrender/examples/common/boilerplate.rs
gfx/webrender/src/clip_scroll_node.rs
gfx/webrender/src/clip_scroll_tree.rs
gfx/webrender/src/frame_builder.rs
gfx/webrender/src/prim_store.rs
gfx/webrender/src/render_task.rs
gfx/webrender_api/src/display_item.rs
--- a/gfx/doc/README.webrender
+++ b/gfx/doc/README.webrender
@@ -74,9 +74,9 @@ 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: 9c5f8682e75839ad8c26480b89f87bbb37aa7894
+Latest Commit: aa81aebba2c1b8ff8af5d40796154d66349fd131
--- a/gfx/webrender/Cargo.toml
+++ b/gfx/webrender/Cargo.toml
@@ -30,16 +30,17 @@ thread_profiler = "0.1.1"
 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"}
+env_logger = "0.4"
 rand = "0.3"                # for the benchmarks
 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"
--- a/gfx/webrender/examples/basic.rs
+++ b/gfx/webrender/examples/basic.rs
@@ -242,28 +242,28 @@ impl Example for App {
             right: border_side,
             bottom: border_side,
             left: border_side,
             radius: BorderRadius::uniform(20.0),
         });
 
         let info = LayoutPrimitiveInfo::new((100, 100).to(200, 200));
         builder.push_border(&info, border_widths, border_details);
-
+        builder.pop_clip_id();
 
         if false {
             // draw text?
             let font_key = api.generate_font_key();
-            let font_bytes = load_file("res/FreeSans.ttf");
+            let font_bytes = load_file("../wrench/reftest/text/FreeSans.ttf");
             resources.add_raw_font(font_key, font_bytes, 0);
 
             let font_instance_key = api.generate_font_instance_key();
             resources.add_font_instance(font_instance_key, font_key, Au::from_px(32), None, None, Vec::new());
 
-            let text_bounds = (100, 200).by(700, 300);
+            let text_bounds = (100, 50).by(700, 200);
             let glyphs = vec![
                 GlyphInstance {
                     index: 48,
                     point: LayoutPoint::new(100.0, 100.0),
                 },
                 GlyphInstance {
                     index: 68,
                     point: LayoutPoint::new(150.0, 100.0),
@@ -339,17 +339,16 @@ impl Example for App {
                 color,
                 blur_radius,
                 spread_radius,
                 simple_border_radius,
                 box_shadow_type,
             );
         }
 
-        builder.pop_clip_id();
         builder.pop_stacking_context();
     }
 
     fn on_event(&mut self, event: glutin::Event, api: &RenderApi, document_id: DocumentId) -> bool {
         match event {
             glutin::Event::Touch(touch) => match self.touch_state.handle_event(touch) {
                 TouchResult::Pan(pan) => {
                     api.set_pan(document_id, pan);
--- a/gfx/webrender/examples/common/boilerplate.rs
+++ b/gfx/webrender/examples/common/boilerplate.rs
@@ -1,12 +1,14 @@
 /* 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/. */
 
+extern crate env_logger;
+
 use gleam::gl;
 use glutin;
 use std::env;
 use std::path::PathBuf;
 use webrender;
 use webrender::api::*;
 
 struct Notifier {
@@ -72,16 +74,18 @@ pub trait Example {
         _gl: &gl::Gl,
     ) -> Option<Box<webrender::OutputImageHandler>> {
         None
     }
     fn draw_custom(&self, _gl: &gl::Gl) {}
 }
 
 pub fn main_wrapper(example: &mut Example, options: Option<webrender::RendererOptions>) {
+    env_logger::init().unwrap();
+
     let args: Vec<String> = env::args().collect();
     let res_path = if args.len() > 1 {
         Some(PathBuf::from(&args[1]))
     } else {
         None
     };
 
     let window = glutin::WindowBuilder::new()
--- a/gfx/webrender/src/clip_scroll_node.rs
+++ b/gfx/webrender/src/clip_scroll_node.rs
@@ -1,60 +1,48 @@
 /* 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::{ClipId, DeviceIntRect, LayerPixel, LayerPoint, LayerRect, LayerSize};
+use api::{ClipId, LayerPixel, LayerPoint, LayerRect, LayerSize};
 use api::{LayerToScrollTransform, LayerToWorldTransform, LayerVector2D, PipelineId};
 use api::{ScrollClamping, ScrollEventPhase, ScrollLocation, ScrollSensitivity, StickyFrameInfo};
 use api::WorldPoint;
 use clip::{ClipRegion, ClipSources, ClipSourcesHandle, ClipStore};
 use clip_scroll_tree::TransformUpdateState;
 use geometry::ray_intersects_rect;
 use spring::{Spring, DAMPING, STIFFNESS};
 use tiling::PackedLayerIndex;
-use util::{MatrixHelpers, TransformedRectKind};
+use util::MatrixHelpers;
 
 #[cfg(target_os = "macos")]
 const CAN_OVERSCROLL: bool = true;
 
 #[cfg(not(target_os = "macos"))]
 const CAN_OVERSCROLL: bool = false;
 
 #[derive(Debug)]
 pub struct ClipInfo {
     /// The clips for this node.
     pub clip_sources: ClipSourcesHandle,
 
     /// The packed layer index for this node, which is used to render a clip mask
     /// for it, if necessary.
     pub packed_layer_index: PackedLayerIndex,
-
-    /// The final transformed rectangle of this clipping region for this node,
-    /// which depends on the screen rectangle and the transformation of all of
-    /// the parents.
-    pub screen_bounding_rect: Option<(TransformedRectKind, DeviceIntRect)>,
-
-    /// A rectangle which defines the rough boundaries of this clip in reference
-    /// frame relative coordinates (with no scroll offsets).
-    pub clip_rect: LayerRect,
 }
 
 impl ClipInfo {
     pub fn new(
         clip_region: ClipRegion,
         packed_layer_index: PackedLayerIndex,
         clip_store: &mut ClipStore,
     ) -> ClipInfo {
-        let clip_rect = LayerRect::new(clip_region.origin, clip_region.main.size);
         ClipInfo {
             clip_sources: clip_store.insert(ClipSources::from(clip_region)),
             packed_layer_index,
-            screen_bounding_rect: None,
-            clip_rect: clip_rect,
         }
     }
 }
 
 #[derive(Debug)]
 pub enum NodeType {
     /// A reference frame establishes a new coordinate space in the tree.
     ReferenceFrame(ReferenceFrameInfo),
@@ -139,20 +127,25 @@ impl ClipScrollNode {
                 LayerSize::new(
                     (content_size.width - frame_rect.size.width).max(0.0),
                     (content_size.height - frame_rect.size.height).max(0.0)
                 )
             )),
         }
     }
 
-    pub fn new(pipeline_id: PipelineId, parent_id: ClipId, clip_info: ClipInfo) -> ClipScrollNode {
+    pub fn new_clip_node(
+        pipeline_id: PipelineId,
+        parent_id: ClipId,
+        clip_info: ClipInfo,
+        clip_rect: LayerRect,
+    ) -> ClipScrollNode {
         ClipScrollNode {
-            local_viewport_rect: clip_info.clip_rect,
-            local_clip_rect: clip_info.clip_rect,
+            local_viewport_rect: clip_rect,
+            local_clip_rect: clip_rect,
             combined_local_viewport_rect: LayerRect::zero(),
             world_viewport_transform: LayerToWorldTransform::identity(),
             world_content_transform: LayerToWorldTransform::identity(),
             reference_frame_relative_scroll_offset: LayerVector2D::zero(),
             parent: Some(parent_id),
             children: Vec::new(),
             pipeline_id,
             node_type: NodeType::Clip(clip_info),
--- a/gfx/webrender/src/clip_scroll_tree.rs
+++ b/gfx/webrender/src/clip_scroll_tree.rs
@@ -161,17 +161,17 @@ impl ClipScrollTree {
             }
         };
 
         if !node.local_clip_rect.contains(&transformed_point) {
             cache.insert(*node_id, None);
             return false;
         }
 
-        let point_in_clips = transformed_point - clip_info.clip_rect.origin.to_vector();
+        let point_in_clips = transformed_point - node.local_clip_rect.origin.to_vector();
         for &(ref clip, _) in clip_store.get(&clip_info.clip_sources).clips() {
             if !clip.contains(&point_in_clips) {
                 cache.insert(*node_id, None);
                 return false;
             }
         }
 
         cache.insert(*node_id, Some(point_in_layer));
@@ -458,20 +458,16 @@ impl ClipScrollTree {
     }
 
     fn print_node<T: PrintTreePrinter>(&self, id: &ClipId, pt: &mut T, clip_store: &ClipStore) {
         let node = self.nodes.get(id).unwrap();
 
         match node.node_type {
             NodeType::Clip(ref info) => {
                 pt.new_level("Clip".to_owned());
-                pt.add_item(format!(
-                    "screen_bounding_rect: {:?}",
-                    info.screen_bounding_rect
-                ));
 
                 let clips = clip_store.get(&info.clip_sources).clips();
                 pt.new_level(format!("Clip Sources [{}]", clips.len()));
                 for source in clips {
                     pt.add_item(format!("{:?}", source));
                 }
                 pt.end_level();
             }
--- a/gfx/webrender/src/frame_builder.rs
+++ b/gfx/webrender/src/frame_builder.rs
@@ -1,15 +1,16 @@
 /* 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::{BorderDetails, BorderDisplayItem, BorderRadius, BoxShadowClipMode, ClipAndScrollInfo};
-use api::{ClipId, ColorF, DeviceIntPoint, DeviceIntRect, DeviceIntSize, DeviceUintRect};
-use api::{DeviceUintSize, ExtendMode, FIND_ALL, FilterOp, FontInstance, FontRenderMode};
+use api::{BorderDetails, BorderDisplayItem, BorderRadius, BoxShadowClipMode, BuiltDisplayList};
+use api::{ClipAndScrollInfo, ClipId, ColorF};
+use api::{DeviceIntPoint, DeviceIntRect, DeviceIntSize, DeviceUintRect, DeviceUintSize};
+use api::{ExtendMode, FIND_ALL, FilterOp, FontInstance, FontRenderMode};
 use api::{GlyphInstance, GlyphOptions, GradientStop, HitTestFlags, HitTestItem, HitTestResult};
 use api::{ImageKey, ImageRendering, ItemRange, ItemTag, LayerPoint, LayerPrimitiveInfo, LayerRect};
 use api::{LayerSize, LayerToScrollTransform, LayerVector2D, LayoutVector2D, LineOrientation};
 use api::{LineStyle, LocalClip, POINT_RELATIVE_TO_PIPELINE_VIEWPORT, PipelineId, RepeatMode};
 use api::{ScrollSensitivity, SubpixelDirection, TextShadow, TileOffset, TransformStyle};
 use api::{WorldPixel, WorldPoint, YuvColorSpace, YuvData, device_length};
 use app_units::Au;
 use border::ImageBorderSegment;
@@ -19,17 +20,17 @@ 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};
 use prim_store::{BoxShadowPrimitiveCpu, TexelRect, YuvImagePrimitiveCpu};
 use prim_store::{GradientPrimitiveCpu, ImagePrimitiveCpu, LinePrimitive, PrimitiveKind};
 use prim_store::{PrimitiveContainer, PrimitiveIndex};
-use prim_store::{PrimitiveStore, RadialGradientPrimitiveCpu, TextRunMode};
+use prim_store::{PrimitiveStore, RadialGradientPrimitiveCpu};
 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, Frame};
@@ -89,17 +90,19 @@ pub struct FrameBuilder {
     prim_store: PrimitiveStore,
     pub clip_store: ClipStore,
     cmds: Vec<PrimitiveRunCmd>,
     hit_testing_runs: Vec<HitTestingRun>,
     config: FrameBuilderConfig,
 
     stacking_context_store: Vec<StackingContext>,
     clip_scroll_group_store: Vec<ClipScrollGroup>,
-    clip_scroll_group_indices: FastHashMap<ClipAndScrollInfo, ClipScrollGroupIndex>,
+    // Note: value here is meant to be `ClipScrollGroupIndex`,
+    // but we already have `ClipAndScrollInfo` in the key
+    clip_scroll_group_indices: FastHashMap<ClipAndScrollInfo, usize>,
     packed_layers: Vec<PackedLayer>,
 
     // A stack of the current text-shadow primitives.
     shadow_prim_stack: Vec<PrimitiveIndex>,
 
     scrollbar_prims: Vec<ScrollbarPrimitive>,
 
     /// A stack of scroll nodes used during display list processing to properly
@@ -121,27 +124,31 @@ pub struct PrimitiveContext<'a> {
 
     // Clip items that apply for this primitive run.
     // In the future, we'll build these once at the
     // start of the frame when updating the
     // clip-scroll tree.
     pub current_clip_stack: Vec<ClipWorkItem>,
     pub clip_bounds: DeviceIntRect,
     pub clip_id: ClipId,
+
+    pub display_list: &'a BuiltDisplayList,
 }
 
 impl<'a> PrimitiveContext<'a> {
     fn new(
         packed_layer_index: PackedLayerIndex,
         packed_layer: &'a PackedLayer,
         clip_id: ClipId,
         screen_rect: &DeviceIntRect,
         clip_scroll_tree: &ClipScrollTree,
         clip_store: &ClipStore,
-        device_pixel_ratio: f32) -> Option<Self> {
+        device_pixel_ratio: f32,
+        display_list: &'a BuiltDisplayList,
+    ) -> Option<Self> {
 
         let mut current_clip_stack = Vec::new();
         let mut clip_bounds = *screen_rect;
         let mut current_id = Some(clip_id);
         // Indicates if the next non-reference-frame that we encounter needs to have its
         // local combined clip rectangle backed into the clip mask.
         let mut next_node_needs_region_mask = false;
         while let Some(id) = current_id {
@@ -160,25 +167,16 @@ impl<'a> PrimitiveContext<'a> {
                 }
             };
 
             let clip_sources = clip_store.get(&clip.clip_sources);
             if !clip_sources.is_masking() {
                 continue;
             }
 
-            // apply the screen bounds of the clip node
-            //Note: these are based on the local combined viewport, so can be tighter
-            if let Some((_kind, ref screen_rect)) = clip.screen_bounding_rect {
-                clip_bounds = match clip_bounds.intersection(screen_rect) {
-                    Some(rect) => rect,
-                    None => return None,
-                }
-            }
-
             // apply the outer device bounds of the clip stack
             if let Some(ref outer) = clip_sources.bounds.outer {
                 clip_bounds = match clip_bounds.intersection(&outer.device_rect) {
                     Some(rect) => rect,
                     None => return None,
                 }
             }
 
@@ -195,16 +193,17 @@ impl<'a> PrimitiveContext<'a> {
 
         Some(PrimitiveContext {
             packed_layer_index,
             packed_layer,
             current_clip_stack,
             clip_bounds,
             device_pixel_ratio,
             clip_id,
+            display_list,
         })
     }
 }
 
 impl FrameBuilder {
     pub fn new(
         previous: Option<FrameBuilder>,
         screen_size: DeviceUintSize,
@@ -246,36 +245,30 @@ impl FrameBuilder {
                 screen_size,
                 background_color,
                 config,
                 has_root_stacking_context: false,
             },
         }
     }
 
-    pub fn create_clip_scroll_group_if_necessary(&mut self, info: ClipAndScrollInfo) {
-        if self.clip_scroll_group_indices.contains_key(&info) {
-            return;
-        }
-
-        let group_index = self.create_clip_scroll_group(info);
-        self.clip_scroll_group_indices.insert(info, group_index);
-    }
-
     /// Create a primitive and add it to the prim store. This method doesn't
     /// add the primitive to the draw list, so can be used for creating
     /// sub-primitives.
     fn create_primitive(
         &mut self,
         clip_and_scroll: ClipAndScrollInfo,
         info: &LayerPrimitiveInfo,
         mut clip_sources: Vec<ClipSource>,
         container: PrimitiveContainer,
     ) -> PrimitiveIndex {
-        self.create_clip_scroll_group_if_necessary(clip_and_scroll);
+        if !self.clip_scroll_group_indices.contains_key(&clip_and_scroll) {
+            let group_id = self.create_clip_scroll_group(&clip_and_scroll);
+            self.clip_scroll_group_indices.insert(clip_and_scroll, group_id);
+        }
 
         if let &LocalClip::RoundedRect(main, region) = &info.local_clip {
             clip_sources.push(ClipSource::Rectangle(main));
             clip_sources.push(ClipSource::RoundedRectangle(
                 region.rect,
                 region.radii,
                 ClipMode::Clip,
             ));
@@ -356,28 +349,29 @@ impl FrameBuilder {
     ) -> PrimitiveIndex {
         self.add_primitive_to_hit_testing_list(info, clip_and_scroll);
         let prim_index = self.create_primitive(clip_and_scroll, info, clip_sources, container);
 
         self.add_primitive_to_draw_list(prim_index, clip_and_scroll);
         prim_index
     }
 
-    pub fn create_clip_scroll_group(&mut self, info: ClipAndScrollInfo) -> ClipScrollGroupIndex {
+    fn create_clip_scroll_group(&mut self, info: &ClipAndScrollInfo) -> usize {
         let packed_layer_index = PackedLayerIndex(self.packed_layers.len());
         self.packed_layers.push(PackedLayer::empty());
 
+        let group_id = self.clip_scroll_group_store.len();
         self.clip_scroll_group_store.push(ClipScrollGroup {
             scroll_node_id: info.scroll_node_id,
             clip_node_id: info.clip_node_id(),
             packed_layer_index,
             screen_bounding_rect: None,
         });
 
-        ClipScrollGroupIndex(self.clip_scroll_group_store.len() - 1, info)
+        group_id
     }
 
     pub fn notify_waiting_for_root_stacking_context(&mut self) {
         self.has_root_stacking_context = false;
     }
 
     pub fn push_stacking_context(
         &mut self,
@@ -543,22 +537,23 @@ impl FrameBuilder {
     pub fn add_clip_node(
         &mut self,
         new_node_id: ClipId,
         parent_id: ClipId,
         pipeline_id: PipelineId,
         clip_region: ClipRegion,
         clip_scroll_tree: &mut ClipScrollTree,
     ) {
+        let clip_rect = LayerRect::new(clip_region.origin, clip_region.main.size);
         let clip_info = ClipInfo::new(
             clip_region,
             PackedLayerIndex(self.packed_layers.len()),
             &mut self.clip_store,
         );
-        let node = ClipScrollNode::new(pipeline_id, parent_id, clip_info);
+        let node = ClipScrollNode::new_clip_node(pipeline_id, parent_id, clip_info, clip_rect);
         clip_scroll_tree.add_node(node, new_node_id);
         self.packed_layers.push(PackedLayer::empty());
     }
 
     pub fn add_scroll_frame(
         &mut self,
         new_node_id: ClipId,
         parent_id: ClipId,
@@ -804,116 +799,139 @@ impl FrameBuilder {
                 );
                 let size = LayerSize::new(
                     rect.size.width + border.outset.left + border.outset.right,
                     rect.size.height + border.outset.top + border.outset.bottom,
                 );
                 let rect = LayerRect::new(origin, size);
 
                 // Calculate the local texel coords of the slices.
-                let px0 = 0;
-                let px1 = border.patch.slice.left;
-                let px2 = border.patch.width - border.patch.slice.right;
-                let px3 = border.patch.width;
+                let px0 = 0.0;
+                let px1 = border.patch.slice.left as f32;
+                let px2 = border.patch.width as f32 - border.patch.slice.right as f32;
+                let px3 = border.patch.width as f32;
 
-                let py0 = 0;
-                let py1 = border.patch.slice.top;
-                let py2 = border.patch.height - border.patch.slice.bottom;
-                let py3 = border.patch.height;
+                let py0 = 0.0;
+                let py1 = border.patch.slice.top as f32;
+                let py2 = border.patch.height as f32 - border.patch.slice.bottom as f32;
+                let py3 = border.patch.height as f32;
 
                 let tl_outer = LayerPoint::new(rect.origin.x, rect.origin.y);
                 let tl_inner = tl_outer + vec2(border_item.widths.left, border_item.widths.top);
 
                 let tr_outer = LayerPoint::new(rect.origin.x + rect.size.width, rect.origin.y);
                 let tr_inner = tr_outer + vec2(-border_item.widths.right, border_item.widths.top);
 
                 let bl_outer = LayerPoint::new(rect.origin.x, rect.origin.y + rect.size.height);
                 let bl_inner = bl_outer + vec2(border_item.widths.left, -border_item.widths.bottom);
 
                 let br_outer = LayerPoint::new(
                     rect.origin.x + rect.size.width,
                     rect.origin.y + rect.size.height,
                 );
                 let br_inner = br_outer - vec2(border_item.widths.right, border_item.widths.bottom);
 
+                fn add_segment(
+                    segments: &mut Vec<ImageBorderSegment>,
+                    rect: LayerRect,
+                    uv_rect: TexelRect,
+                    repeat_horizontal: RepeatMode,
+                    repeat_vertical: RepeatMode) {
+                    if uv_rect.uv1.x > uv_rect.uv0.x &&
+                       uv_rect.uv1.y > uv_rect.uv0.y {
+                        segments.push(ImageBorderSegment::new(
+                            rect,
+                            uv_rect,
+                            repeat_horizontal,
+                            repeat_vertical,
+                        ));
+                    }
+                }
+
                 // Build the list of image segments
-                let mut segments = vec![
-                    // Top left
-                    ImageBorderSegment::new(
-                        LayerRect::from_floats(tl_outer.x, tl_outer.y, tl_inner.x, tl_inner.y),
-                        TexelRect::new(px0, py0, px1, py1),
-                        RepeatMode::Stretch,
-                        RepeatMode::Stretch,
-                    ),
-                    // Top right
-                    ImageBorderSegment::new(
-                        LayerRect::from_floats(tr_inner.x, tr_outer.y, tr_outer.x, tr_inner.y),
-                        TexelRect::new(px2, py0, px3, py1),
-                        RepeatMode::Stretch,
-                        RepeatMode::Stretch,
-                    ),
-                    // Bottom right
-                    ImageBorderSegment::new(
-                        LayerRect::from_floats(br_inner.x, br_inner.y, br_outer.x, br_outer.y),
-                        TexelRect::new(px2, py2, px3, py3),
-                        RepeatMode::Stretch,
-                        RepeatMode::Stretch,
-                    ),
-                    // Bottom left
-                    ImageBorderSegment::new(
-                        LayerRect::from_floats(bl_outer.x, bl_inner.y, bl_inner.x, bl_outer.y),
-                        TexelRect::new(px0, py2, px1, py3),
-                        RepeatMode::Stretch,
-                        RepeatMode::Stretch,
-                    ),
-                ];
+                let mut segments = vec![];
+
+                // Top left
+                add_segment(
+                    &mut segments,
+                    LayerRect::from_floats(tl_outer.x, tl_outer.y, tl_inner.x, tl_inner.y),
+                    TexelRect::new(px0, py0, px1, py1),
+                    RepeatMode::Stretch,
+                    RepeatMode::Stretch
+                );
+                // Top right
+                add_segment(
+                    &mut segments,
+                    LayerRect::from_floats(tr_inner.x, tr_outer.y, tr_outer.x, tr_inner.y),
+                    TexelRect::new(px2, py0, px3, py1),
+                    RepeatMode::Stretch,
+                    RepeatMode::Stretch
+                );
+                // Bottom right
+                add_segment(
+                    &mut segments,
+                    LayerRect::from_floats(br_inner.x, br_inner.y, br_outer.x, br_outer.y),
+                    TexelRect::new(px2, py2, px3, py3),
+                    RepeatMode::Stretch,
+                    RepeatMode::Stretch
+                );
+                // Bottom left
+                add_segment(
+                    &mut segments,
+                    LayerRect::from_floats(bl_outer.x, bl_inner.y, bl_inner.x, bl_outer.y),
+                    TexelRect::new(px0, py2, px1, py3),
+                    RepeatMode::Stretch,
+                    RepeatMode::Stretch
+                );
 
                 // Center
                 if border.fill {
-                    segments.push(ImageBorderSegment::new(
+                    add_segment(
+                        &mut segments,
                         LayerRect::from_floats(tl_inner.x, tl_inner.y, tr_inner.x, bl_inner.y),
                         TexelRect::new(px1, py1, px2, py2),
                         border.repeat_horizontal,
-                        border.repeat_vertical,
-                    ))
+                        border.repeat_vertical
+                    );
                 }
 
-                // Add edge segments if valid size.
-                if px1 < px2 && py1 < py2 {
-                    segments.extend_from_slice(&[
-                        // Top
-                        ImageBorderSegment::new(
-                            LayerRect::from_floats(tl_inner.x, tl_outer.y, tr_inner.x, tl_inner.y),
-                            TexelRect::new(px1, py0, px2, py1),
-                            border.repeat_horizontal,
-                            RepeatMode::Stretch,
-                        ),
-                        // Bottom
-                        ImageBorderSegment::new(
-                            LayerRect::from_floats(bl_inner.x, bl_inner.y, br_inner.x, bl_outer.y),
-                            TexelRect::new(px1, py2, px2, py3),
-                            border.repeat_horizontal,
-                            RepeatMode::Stretch,
-                        ),
-                        // Left
-                        ImageBorderSegment::new(
-                            LayerRect::from_floats(tl_outer.x, tl_inner.y, tl_inner.x, bl_inner.y),
-                            TexelRect::new(px0, py1, px1, py2),
-                            RepeatMode::Stretch,
-                            border.repeat_vertical,
-                        ),
-                        // Right
-                        ImageBorderSegment::new(
-                            LayerRect::from_floats(tr_inner.x, tr_inner.y, br_outer.x, br_inner.y),
-                            TexelRect::new(px2, py1, px3, py2),
-                            RepeatMode::Stretch,
-                            border.repeat_vertical,
-                        ),
-                    ]);
-                }
+                // Add edge segments.
+
+                // Top
+                add_segment(
+                    &mut segments,
+                    LayerRect::from_floats(tl_inner.x, tl_outer.y, tr_inner.x, tl_inner.y),
+                    TexelRect::new(px1, py0, px2, py1),
+                    border.repeat_horizontal,
+                    RepeatMode::Stretch,
+                );
+                // Bottom
+                add_segment(
+                    &mut segments,
+                    LayerRect::from_floats(bl_inner.x, bl_inner.y, br_inner.x, bl_outer.y),
+                    TexelRect::new(px1, py2, px2, py3),
+                    border.repeat_horizontal,
+                    RepeatMode::Stretch,
+                );
+                // Left
+                add_segment(
+                    &mut segments,
+                    LayerRect::from_floats(tl_outer.x, tl_inner.y, tl_inner.x, bl_inner.y),
+                    TexelRect::new(px0, py1, px1, py2),
+                    RepeatMode::Stretch,
+                    border.repeat_vertical,
+                );
+                // Right
+                add_segment(
+                    &mut segments,
+                    LayerRect::from_floats(tr_inner.x, tr_inner.y, br_outer.x, br_inner.y),
+                    TexelRect::new(px2, py1, px3, py2),
+                    RepeatMode::Stretch,
+                    border.repeat_vertical,
+                );
 
                 for segment in segments {
                     let mut info = info.clone();
                     info.rect = segment.geom_rect;
                     self.add_image(
                         clip_and_scroll,
                         &info,
                         &segment.stretch_size,
@@ -1554,18 +1572,18 @@ impl FrameBuilder {
         stacking_context.screen_bounds = DeviceIntRect::zero();
         stacking_context.isolated_items_bounds = LayerRect::zero();
     }
 
     pub fn get_packed_layer_index_if_visible(
         &self,
         clip_and_scroll: &ClipAndScrollInfo
     ) -> Option<PackedLayerIndex> {
-        let group_index = self.clip_scroll_group_indices.get(&clip_and_scroll).unwrap();
-        let clip_scroll_group = &self.clip_scroll_group_store[group_index.0];
+        let group_id = self.clip_scroll_group_indices[&clip_and_scroll];
+        let clip_scroll_group = &self.clip_scroll_group_store[group_id];
         if clip_scroll_group.is_visible() {
             Some(clip_scroll_group.packed_layer_index)
         } else {
             None
         }
     }
 
     pub fn hit_test(
@@ -1645,32 +1663,32 @@ impl FrameBuilder {
         render_tasks: &mut RenderTaskTree,
         gpu_cache: &mut GpuCache,
         resource_cache: &mut ResourceCache,
         pipelines: &FastHashMap<PipelineId, ScenePipeline>,
         clip_scroll_tree: &ClipScrollTree,
         screen_rect: &DeviceIntRect,
         device_pixel_ratio: f32,
         profile_counters: &mut FrameProfileCounters,
-    ) {
+    ) -> bool {
         let stacking_context_index = *self.stacking_context_stack.last().unwrap();
         let packed_layer_index =
             match self.get_packed_layer_index_if_visible(&clip_and_scroll) {
             Some(index) => index,
             None => {
                 debug!("{:?} of invisible {:?}", base_prim_index, stacking_context_index);
-                return;
+                return false;
             }
         };
 
         let pipeline_id = {
             let stacking_context =
                 &mut self.stacking_context_store[stacking_context_index.0];
             if !stacking_context.can_contribute_to_scene() {
-                return;
+                return false;
             }
 
             // At least one primitive in this stacking context is visible, so the stacking
             // context is visible.
             stacking_context.is_visible = true;
             stacking_context.pipeline_id
         };
 
@@ -1685,63 +1703,68 @@ impl FrameBuilder {
             &mut self.stacking_context_store[stacking_context_index.0];
         let packed_layer = &self.packed_layers[packed_layer_index.0];
         let display_list = &pipelines
             .get(&pipeline_id)
             .expect("No display list?")
             .display_list;
 
         if !stacking_context.is_backface_visible && packed_layer.transform.is_backface_visible() {
-            return;
+            return false;
         }
 
         let prim_context = PrimitiveContext::new(
             packed_layer_index,
             packed_layer,
             clip_and_scroll.clip_node_id(),
             screen_rect,
             clip_scroll_tree,
             &self.clip_store,
             device_pixel_ratio,
+            display_list,
         );
 
         let prim_context = match prim_context {
             Some(prim_context) => prim_context,
-            None => return,
+            None => {
+                let group_id = self.clip_scroll_group_indices[&clip_and_scroll];
+                self.clip_scroll_group_store[group_id].screen_bounding_rect = None;
+                return false
+            },
         };
 
         debug!(
             "\tclip_bounds {:?}, layer_local_clip {:?}",
             prim_context.clip_bounds,
             packed_layer.local_clip_rect
         );
 
         for i in 0 .. prim_count {
             let prim_index = PrimitiveIndex(base_prim_index.0 + i);
 
             if let Some(prim_geom) = self.prim_store.prepare_prim_for_render(
                 prim_index,
                 &prim_context,
                 resource_cache,
                 gpu_cache,
-                display_list,
-                TextRunMode::Normal,
                 render_tasks,
                 &mut self.clip_store,
             ) {
                 stacking_context.screen_bounds = stacking_context
                     .screen_bounds
                     .union(&prim_geom.device_rect);
                 stacking_context.isolated_items_bounds = stacking_context
                     .isolated_items_bounds
                     .union(&prim_geom.local_rect);
 
                 profile_counters.visible_primitives.inc();
             }
         }
+
+        true //visible
     }
 
     fn handle_pop_stacking_context(&mut self, screen_rect: &DeviceIntRect) {
         let stacking_context_index = self.stacking_context_stack.pop().unwrap();
 
         let (bounding_rect, is_visible, is_preserve_3d, reference_id, reference_bounds) = {
             let stacking_context =
                 &mut self.stacking_context_store[stacking_context_index.0];
@@ -1792,30 +1815,28 @@ impl FrameBuilder {
             let packed_layer = &mut self.packed_layers[packed_layer_index.0];
 
             // The coordinates of the mask are relative to the origin of the node itself,
             // so we need to account for that origin in the transformation we assign to
             // the packed layer.
             let transform = node.world_viewport_transform
                 .pre_translate(node.local_viewport_rect.origin.to_vector().to_3d());
 
-            node_clip_info.screen_bounding_rect = if packed_layer.set_transform(transform) {
+            if packed_layer.set_transform(transform) {
                 // Meanwhile, the combined viewport rect is relative to the reference frame, so
                 // we move it into the local coordinate system of the node.
                 let local_viewport_rect = node.combined_local_viewport_rect
                     .translate(&-node.local_viewport_rect.origin.to_vector());
 
                 packed_layer.set_rect(
                     &local_viewport_rect,
                     screen_rect,
                     device_pixel_ratio,
-                )
-            } else {
-                None
-            };
+                );
+            }
 
             let clip_sources = self.clip_store.get_mut(&node_clip_info.clip_sources);
             clip_sources.update(
                 &transform,
                 gpu_cache,
                 resource_cache,
                 device_pixel_ratio,
             );
@@ -2236,20 +2257,20 @@ impl FrameBuilder {
                     }
                 }
                 PrimitiveRunCmd::PrimitiveRun(first_prim_index, prim_count, clip_and_scroll) => {
                     let stacking_context_index = *sc_stack.last().unwrap();
                     if !self.stacking_context_store[stacking_context_index.0].is_visible {
                         continue;
                     }
 
-                    let group_index = *self.clip_scroll_group_indices
-                        .get(&clip_and_scroll)
-                        .unwrap();
-                    if self.clip_scroll_group_store[group_index.0]
+                    let group_id = self.clip_scroll_group_indices[&clip_and_scroll];
+                    let group_index = ClipScrollGroupIndex(group_id, clip_and_scroll);
+
+                    if self.clip_scroll_group_store[group_id]
                         .screen_bounding_rect
                         .is_none()
                     {
                         debug!("\tcs-group {:?} screen rect is None", group_index);
                         continue;
                     }
 
                     debug!("\trun of {} items", prim_count);
--- a/gfx/webrender/src/prim_store.rs
+++ b/gfx/webrender/src/prim_store.rs
@@ -13,17 +13,17 @@ use clip::{ClipMode, ClipSourcesHandle, 
 use euclid::Size2D;
 use frame_builder::PrimitiveContext;
 use gpu_cache::{GpuBlockData, GpuCache, GpuCacheAddress, GpuCacheHandle, GpuDataRequest,
                 ToGpuBlocks};
 use render_task::{ClipWorkItem, RenderTask, RenderTaskId, RenderTaskTree};
 use renderer::MAX_VERTEX_TEXTURE_WIDTH;
 use resource_cache::{ImageProperties, ResourceCache};
 use std::{mem, usize};
-use util::{pack_as_float, recycle_vec, TransformedRect};
+use util::{MatrixHelpers, pack_as_float, recycle_vec, TransformedRect};
 
 #[derive(Debug, Copy, Clone)]
 pub struct PrimitiveOpacity {
     pub is_opaque: bool,
 }
 
 impl PrimitiveOpacity {
     pub fn opaque() -> PrimitiveOpacity {
@@ -52,20 +52,20 @@ impl PrimitiveOpacity {
 /// updated on the CPU when the texture size changes.
 #[derive(Copy, Clone, Debug)]
 pub struct TexelRect {
     pub uv0: DevicePoint,
     pub uv1: DevicePoint,
 }
 
 impl TexelRect {
-    pub fn new(u0: u32, v0: u32, u1: u32, v1: u32) -> TexelRect {
+    pub fn new(u0: f32, v0: f32, u1: f32, v1: f32) -> TexelRect {
         TexelRect {
-            uv0: DevicePoint::new(u0 as f32, v0 as f32),
-            uv1: DevicePoint::new(u1 as f32, v1 as f32),
+            uv0: DevicePoint::new(u0, v0),
+            uv1: DevicePoint::new(u1, v1),
         }
     }
 
     pub fn invalid() -> TexelRect {
         TexelRect {
             uv0: DevicePoint::new(-1.0, -1.0),
             uv1: DevicePoint::new(-1.0, -1.0),
         }
@@ -1055,31 +1055,296 @@ impl PrimitiveStore {
         }
 
         if let Some(clip_task_id) = metadata.clip_task_id {
             task.children.push(clip_task_id);
         }
     }
 
     /// Returns true if the bounding box needs to be updated.
+    fn prepare_prim_for_render_inner(
+        &mut self,
+        prim_index: PrimitiveIndex,
+        prim_context: &PrimitiveContext,
+        resource_cache: &mut ResourceCache,
+        gpu_cache: &mut GpuCache,
+        // For some primitives, we need to mark dependencies as needed for rendering
+        // without spawning new tasks, since there will be another call to
+        // `prepare_prim_for_render_inner` specifically for this primitive later on.
+        render_tasks: Option<&mut RenderTaskTree>,
+        text_run_mode: TextRunMode,
+    ) {
+        let metadata = &mut self.cpu_metadata[prim_index.0];
+        match metadata.prim_kind {
+            PrimitiveKind::Rectangle | PrimitiveKind::Border | PrimitiveKind::Line => {}
+            PrimitiveKind::BoxShadow => {
+                // TODO(gw): Account for zoom factor!
+                // Here, we calculate the size of the patch required in order
+                // to create the box shadow corner. First, scale it by the
+                // device pixel ratio since the cache shader expects vertices
+                // in device space. The shader adds a 1-pixel border around
+                // the patch, in order to prevent bilinear filter artifacts as
+                // the patch is clamped / mirrored across the box shadow rect.
+                let box_shadow = &mut self.cpu_box_shadows[metadata.cpu_prim_index.0];
+                let edge_size = box_shadow.edge_size.ceil() * prim_context.device_pixel_ratio;
+                let edge_size = edge_size as i32 + 2; // Account for bilinear filtering
+                let cache_size = DeviceIntSize::new(edge_size, edge_size);
+
+                let cache_key = BoxShadowPrimitiveCacheKey {
+                    blur_radius: Au::from_f32_px(box_shadow.blur_radius),
+                    border_radius: Au::from_f32_px(box_shadow.border_radius),
+                    inverted: box_shadow.inverted != 0.0,
+                    shadow_rect_size: Size2D::new(
+                        Au::from_f32_px(box_shadow.bs_rect.size.width),
+                        Au::from_f32_px(box_shadow.bs_rect.size.height),
+                    ),
+                };
+
+                // Create a render task for this box shadow primitive. This renders a small
+                // portion of the box shadow to a render target. That portion is then
+                // stretched over the actual primitive rect by the box shadow primitive
+                // shader, to reduce the number of pixels that the expensive box
+                // shadow shader needs to run on.
+                // TODO(gw): In the future, we can probably merge the box shadow
+                // primitive (stretch) shader with the generic cached primitive shader.
+                let render_task = RenderTask::new_box_shadow(
+                    cache_key,
+                    cache_size,
+                    prim_index
+                );
+
+                // ignore the new task if we are in a dependency context
+                box_shadow.render_task_id = render_tasks.map(|rt| rt.add(render_task));
+            }
+            PrimitiveKind::TextShadow => {
+                let shadow = &mut self.cpu_text_shadows[metadata.cpu_prim_index.0];
+
+                // This is a text-shadow element. Create a render task that will
+                // render the text run to a target, and then apply a gaussian
+                // blur to that text run in order to build the actual primitive
+                // which will be blitted to the framebuffer.
+                let cache_width =
+                    (metadata.local_rect.size.width * prim_context.device_pixel_ratio).ceil() as i32;
+                let cache_height =
+                    (metadata.local_rect.size.height * prim_context.device_pixel_ratio).ceil() as i32;
+                let cache_size = DeviceIntSize::new(cache_width, cache_height);
+                let blur_radius = device_length(shadow.shadow.blur_radius, prim_context.device_pixel_ratio);
+
+                // ignore new tasks if we are in a dependency context
+                shadow.render_task_id = render_tasks.map(|rt| {
+                    let prim_cache_task = RenderTask::new_prim_cache(cache_size, prim_index);
+                    let prim_cache_task_id = rt.add(prim_cache_task);
+                    let render_task =
+                        RenderTask::new_blur(blur_radius, prim_cache_task_id, rt);
+                    rt.add(render_task)
+                });
+            }
+            PrimitiveKind::TextRun => {
+                let text = &mut self.cpu_text_runs[metadata.cpu_prim_index.0];
+                text.prepare_for_render(
+                    resource_cache,
+                    prim_context.device_pixel_ratio,
+                    prim_context.display_list,
+                    text_run_mode,
+                    gpu_cache,
+                );
+            }
+            PrimitiveKind::Image => {
+                let image_cpu = &mut self.cpu_images[metadata.cpu_prim_index.0];
+
+                resource_cache.request_image(
+                    image_cpu.image_key,
+                    image_cpu.image_rendering,
+                    image_cpu.tile_offset,
+                    gpu_cache,
+                );
+
+                // TODO(gw): This doesn't actually need to be calculated each frame.
+                // It's cheap enough that it's not worth introducing a cache for images
+                // right now, but if we introduce a cache for images for some other
+                // reason then we might as well cache this with it.
+                if let Some(image_properties) =
+                    resource_cache.get_image_properties(image_cpu.image_key)
+                {
+                    metadata.opacity.is_opaque = image_properties.descriptor.is_opaque &&
+                        image_cpu.tile_spacing.width == 0.0 &&
+                        image_cpu.tile_spacing.height == 0.0;
+                }
+            }
+            PrimitiveKind::YuvImage => {
+                let image_cpu = &mut self.cpu_yuv_images[metadata.cpu_prim_index.0];
+
+                let channel_num = image_cpu.format.get_plane_num();
+                debug_assert!(channel_num <= 3);
+                for channel in 0 .. channel_num {
+                    resource_cache.request_image(
+                        image_cpu.yuv_key[channel],
+                        image_cpu.image_rendering,
+                        None,
+                        gpu_cache,
+                    );
+                }
+            }
+            PrimitiveKind::AlignedGradient |
+            PrimitiveKind::AngleGradient |
+            PrimitiveKind::RadialGradient => {}
+        }
+
+        // Mark this GPU resource as required for this frame.
+        if let Some(mut request) = gpu_cache.request(&mut metadata.gpu_location) {
+            request.push(metadata.local_rect);
+            request.push(metadata.local_clip_rect);
+
+            match metadata.prim_kind {
+                PrimitiveKind::Rectangle => {
+                    let rect = &self.cpu_rectangles[metadata.cpu_prim_index.0];
+                    rect.write_gpu_blocks(request);
+                }
+                PrimitiveKind::Line => {
+                    let line = &self.cpu_lines[metadata.cpu_prim_index.0];
+                    line.write_gpu_blocks(request);
+                }
+                PrimitiveKind::Border => {
+                    let border = &self.cpu_borders[metadata.cpu_prim_index.0];
+                    border.write_gpu_blocks(request);
+                }
+                PrimitiveKind::BoxShadow => {
+                    let box_shadow = &self.cpu_box_shadows[metadata.cpu_prim_index.0];
+                    box_shadow.write_gpu_blocks(request);
+                }
+                PrimitiveKind::Image => {
+                    let image = &self.cpu_images[metadata.cpu_prim_index.0];
+                    image.write_gpu_blocks(request);
+                }
+                PrimitiveKind::YuvImage => {
+                    let yuv_image = &self.cpu_yuv_images[metadata.cpu_prim_index.0];
+                    yuv_image.write_gpu_blocks(request);
+                }
+                PrimitiveKind::AlignedGradient => {
+                    let gradient = &self.cpu_gradients[metadata.cpu_prim_index.0];
+                    metadata.opacity = gradient.build_gpu_blocks_for_aligned(prim_context.display_list, request);
+                }
+                PrimitiveKind::AngleGradient => {
+                    let gradient = &self.cpu_gradients[metadata.cpu_prim_index.0];
+                    gradient.build_gpu_blocks_for_angle_radial(prim_context.display_list, request);
+                }
+                PrimitiveKind::RadialGradient => {
+                    let gradient = &self.cpu_radial_gradients[metadata.cpu_prim_index.0];
+                    gradient.build_gpu_blocks_for_angle_radial(prim_context.display_list, request);
+                }
+                PrimitiveKind::TextRun => {
+                    let text = &self.cpu_text_runs[metadata.cpu_prim_index.0];
+                    text.write_gpu_blocks(&mut request);
+                }
+                PrimitiveKind::TextShadow => {
+                    let prim = &self.cpu_text_shadows[metadata.cpu_prim_index.0];
+                    request.push(prim.shadow.color);
+                    request.push([
+                        prim.shadow.offset.x,
+                        prim.shadow.offset.y,
+                        prim.shadow.blur_radius,
+                        0.0,
+                    ]);
+                }
+            }
+        }
+    }
+
+    fn update_clip_task(
+        &mut self,
+        prim_index: PrimitiveIndex,
+        prim_context: &PrimitiveContext,
+        prim_screen_rect: DeviceIntRect,
+        resource_cache: &mut ResourceCache,
+        gpu_cache: &mut GpuCache,
+        render_tasks: &mut RenderTaskTree,
+        clip_store: &mut ClipStore,
+    ) -> bool {
+        let metadata = &mut self.cpu_metadata[prim_index.0];
+        clip_store.get_mut(&metadata.clip_sources).update(
+            &prim_context.packed_layer.transform,
+            gpu_cache,
+            resource_cache,
+            prim_context.device_pixel_ratio,
+        );
+
+        // Try to create a mask if we may need to.
+        let prim_clips = clip_store.get(&metadata.clip_sources);
+        let is_axis_aligned = prim_context.packed_layer.transform.preserves_2d_axis_alignment();
+        let clip_task = if prim_clips.is_masking() {
+            // Take into account the actual clip info of the primitive, and
+            // mutate the current bounds accordingly.
+            let mask_rect = match prim_clips.bounds.outer {
+                Some(ref outer) => match prim_screen_rect.intersection(&outer.device_rect) {
+                    Some(rect) => rect,
+                    None => {
+                        metadata.screen_rect = None;
+                        return false;
+                    }
+                },
+                _ => prim_screen_rect,
+            };
+
+            let extra = ClipWorkItem {
+                layer_index: prim_context.packed_layer_index,
+                clip_sources: metadata.clip_sources.weak(),
+                apply_rectangles: false,
+            };
+
+            RenderTask::new_mask(
+                None,
+                mask_rect,
+                &prim_context.current_clip_stack,
+                Some(extra),
+                prim_screen_rect,
+                clip_store,
+                is_axis_aligned,
+            )
+        } else if !prim_context.current_clip_stack.is_empty() {
+            // If the primitive doesn't have a specific clip, key the task ID off the
+            // stacking context. This means that two primitives which are only clipped
+            // by the stacking context stack can share clip masks during render task
+            // assignment to targets.
+            RenderTask::new_mask(
+                Some(prim_context.clip_id),
+                prim_context.clip_bounds,
+                &prim_context.current_clip_stack,
+                None,
+                prim_screen_rect,
+                clip_store,
+                is_axis_aligned,
+            )
+        } else {
+            None
+        };
+
+        metadata.clip_task_id = clip_task.map(|clip_task| render_tasks.add(clip_task));
+        true
+    }
+
+    /// Returns true if the bounding box needs to be updated.
     pub fn prepare_prim_for_render(
         &mut self,
         prim_index: PrimitiveIndex,
         prim_context: &PrimitiveContext,
         resource_cache: &mut ResourceCache,
         gpu_cache: &mut GpuCache,
-        display_list: &BuiltDisplayList,
-        text_run_mode: TextRunMode,
         render_tasks: &mut RenderTaskTree,
         clip_store: &mut ClipStore,
     ) -> Option<Geometry> {
-        let (prim_local_rect, prim_screen_rect, prim_kind, cpu_prim_index) = {
+        let (geometry, dependent_primitives) = {
             let metadata = &mut self.cpu_metadata[prim_index.0];
             metadata.screen_rect = None;
 
+            if metadata.local_rect.size.width <= 0.0 ||
+               metadata.local_rect.size.height <= 0.0 {
+                warn!("invalid primitive rect {:?}", metadata.local_rect);
+                return None;
+            }
+
             if !metadata.is_backface_visible &&
                prim_context.packed_layer.transform.is_backface_visible() {
                 return None;
             }
 
             let local_rect = metadata
                 .local_rect
                 .intersection(&metadata.local_clip_rect)
@@ -1095,270 +1360,70 @@ impl PrimitiveStore {
                 &prim_context.packed_layer.transform,
                 prim_context.device_pixel_ratio
             );
 
             metadata.screen_rect = xf_rect
                 .bounding_rect
                 .intersection(&prim_context.clip_bounds);
 
-            match metadata.screen_rect {
-                Some(screen_rect) => (local_rect, screen_rect, metadata.prim_kind, metadata.cpu_prim_index),
+            let geometry = match metadata.screen_rect {
+                Some(device_rect) => Geometry {
+                    local_rect,
+                    device_rect,
+                },
                 None => return None,
-            }
+            };
+
+            let dependencies = match metadata.prim_kind {
+                PrimitiveKind::TextShadow =>
+                    self.cpu_text_shadows[metadata.cpu_prim_index.0].primitives.clone(),
+                _ => Vec::new(),
+            };
+            (geometry, dependencies)
         };
 
         // Recurse into any sub primitives and prepare them for rendering first.
         // TODO(gw): This code is a bit hacky to work around the borrow checker.
         //           Specifically, the clone() below on the primitive list for
         //           text shadow primitives. Consider restructuring this code to
         //           avoid borrow checker issues.
-        if prim_kind == PrimitiveKind::TextShadow {
-            for sub_prim_index in self.cpu_text_shadows[cpu_prim_index.0].primitives.clone() {
-                self.prepare_prim_for_render(
-                    sub_prim_index,
-                    prim_context,
-                    resource_cache,
-                    gpu_cache,
-                    display_list,
-                    TextRunMode::Shadow,
-                    render_tasks,
-                    clip_store,
-                );
-            }
+        for sub_prim_index in dependent_primitives {
+            self.prepare_prim_for_render_inner(
+                sub_prim_index,
+                prim_context,
+                resource_cache,
+                gpu_cache,
+                None,
+                TextRunMode::Shadow,
+            );
         }
 
-        let metadata = &mut self.cpu_metadata[prim_index.0];
-        clip_store.get_mut(&metadata.clip_sources).update(
-            &prim_context.packed_layer.transform,
+        if !self.update_clip_task(
+            prim_index,
+            prim_context,
+            geometry.device_rect,
+            resource_cache,
             gpu_cache,
+            render_tasks,
+            clip_store,
+        ) {
+            return None;
+        }
+
+        self.prepare_prim_for_render_inner(
+            prim_index,
+            prim_context,
             resource_cache,
-            prim_context.device_pixel_ratio,
+            gpu_cache,
+            Some(render_tasks),
+            TextRunMode::Normal,
         );
 
-        // Try to create a mask if we may need to.
-        let prim_clips = clip_store.get(&metadata.clip_sources);
-        let clip_task = if prim_clips.is_masking() {
-            // Take into account the actual clip info of the primitive, and
-            // mutate the current bounds accordingly.
-            let mask_rect = match prim_clips.bounds.outer {
-                Some(ref outer) => match prim_screen_rect.intersection(&outer.device_rect) {
-                    Some(rect) => rect,
-                    None => return None,
-                },
-                _ => prim_screen_rect,
-            };
-
-            let extra = ClipWorkItem {
-                layer_index: prim_context.packed_layer_index,
-                clip_sources: metadata.clip_sources.weak(),
-                apply_rectangles: false,
-            };
-
-            RenderTask::new_mask(
-                None,
-                mask_rect,
-                &prim_context.current_clip_stack,
-                Some(extra),
-                prim_screen_rect,
-                clip_store,
-            )
-        } else if !prim_context.current_clip_stack.is_empty() {
-            // If the primitive doesn't have a specific clip, key the task ID off the
-            // stacking context. This means that two primitives which are only clipped
-            // by the stacking context stack can share clip masks during render task
-            // assignment to targets.
-            RenderTask::new_mask(
-                Some(prim_context.clip_id),
-                prim_context.clip_bounds,
-                &prim_context.current_clip_stack,
-                None,
-                prim_screen_rect,
-                clip_store,
-            )
-        } else {
-            None
-        };
-
-        metadata.clip_task_id = clip_task.map(|clip_task| render_tasks.add(clip_task));
-
-        match metadata.prim_kind {
-            PrimitiveKind::Rectangle | PrimitiveKind::Border | PrimitiveKind::Line => {}
-            PrimitiveKind::BoxShadow => {
-                // TODO(gw): Account for zoom factor!
-                // Here, we calculate the size of the patch required in order
-                // to create the box shadow corner. First, scale it by the
-                // device pixel ratio since the cache shader expects vertices
-                // in device space. The shader adds a 1-pixel border around
-                // the patch, in order to prevent bilinear filter artifacts as
-                // the patch is clamped / mirrored across the box shadow rect.
-                let box_shadow = &mut self.cpu_box_shadows[cpu_prim_index.0];
-                let edge_size = box_shadow.edge_size.ceil() * prim_context.device_pixel_ratio;
-                let edge_size = edge_size as i32 + 2; // Account for bilinear filtering
-                let cache_size = DeviceIntSize::new(edge_size, edge_size);
-
-                let cache_key = BoxShadowPrimitiveCacheKey {
-                    blur_radius: Au::from_f32_px(box_shadow.blur_radius),
-                    border_radius: Au::from_f32_px(box_shadow.border_radius),
-                    inverted: box_shadow.inverted != 0.0,
-                    shadow_rect_size: Size2D::new(
-                        Au::from_f32_px(box_shadow.bs_rect.size.width),
-                        Au::from_f32_px(box_shadow.bs_rect.size.height),
-                    ),
-                };
-
-                // Create a render task for this box shadow primitive. This renders a small
-                // portion of the box shadow to a render target. That portion is then
-                // stretched over the actual primitive rect by the box shadow primitive
-                // shader, to reduce the number of pixels that the expensive box
-                // shadow shader needs to run on.
-                // TODO(gw): In the future, we can probably merge the box shadow
-                // primitive (stretch) shader with the generic cached primitive shader.
-                let render_task = RenderTask::new_box_shadow(
-                    cache_key,
-                    cache_size,
-                    prim_index
-                );
-                let render_task_id = render_tasks.add(render_task);
-
-                box_shadow.render_task_id = Some(render_task_id);
-            }
-            PrimitiveKind::TextShadow => {
-                let shadow = &mut self.cpu_text_shadows[cpu_prim_index.0];
-
-                // This is a text-shadow element. Create a render task that will
-                // render the text run to a target, and then apply a gaussian
-                // blur to that text run in order to build the actual primitive
-                // which will be blitted to the framebuffer.
-                let cache_width =
-                    (metadata.local_rect.size.width * prim_context.device_pixel_ratio).ceil() as i32;
-                let cache_height =
-                    (metadata.local_rect.size.height * prim_context.device_pixel_ratio).ceil() as i32;
-                let cache_size = DeviceIntSize::new(cache_width, cache_height);
-                let blur_radius = device_length(shadow.shadow.blur_radius, prim_context.device_pixel_ratio);
-                let prim_cache_task = RenderTask::new_prim_cache(cache_size, prim_index);
-                let prim_cache_task_id = render_tasks.add(prim_cache_task);
-                let render_task =
-                    RenderTask::new_blur(blur_radius, prim_cache_task_id, render_tasks);
-                shadow.render_task_id = Some(render_tasks.add(render_task));
-            }
-            PrimitiveKind::TextRun => {
-                let text = &mut self.cpu_text_runs[cpu_prim_index.0];
-                text.prepare_for_render(
-                    resource_cache,
-                    prim_context.device_pixel_ratio,
-                    display_list,
-                    text_run_mode,
-                    gpu_cache,
-                );
-            }
-            PrimitiveKind::Image => {
-                let image_cpu = &mut self.cpu_images[cpu_prim_index.0];
-
-                resource_cache.request_image(
-                    image_cpu.image_key,
-                    image_cpu.image_rendering,
-                    image_cpu.tile_offset,
-                    gpu_cache,
-                );
-
-                // TODO(gw): This doesn't actually need to be calculated each frame.
-                // It's cheap enough that it's not worth introducing a cache for images
-                // right now, but if we introduce a cache for images for some other
-                // reason then we might as well cache this with it.
-                if let Some(image_properties) =
-                    resource_cache.get_image_properties(image_cpu.image_key)
-                {
-                    metadata.opacity.is_opaque = image_properties.descriptor.is_opaque &&
-                        image_cpu.tile_spacing.width == 0.0 &&
-                        image_cpu.tile_spacing.height == 0.0;
-                }
-            }
-            PrimitiveKind::YuvImage => {
-                let image_cpu = &mut self.cpu_yuv_images[cpu_prim_index.0];
-
-                let channel_num = image_cpu.format.get_plane_num();
-                debug_assert!(channel_num <= 3);
-                for channel in 0 .. channel_num {
-                    resource_cache.request_image(
-                        image_cpu.yuv_key[channel],
-                        image_cpu.image_rendering,
-                        None,
-                        gpu_cache,
-                    );
-                }
-            }
-            PrimitiveKind::AlignedGradient |
-            PrimitiveKind::AngleGradient |
-            PrimitiveKind::RadialGradient => {}
-        }
-
-        // Mark this GPU resource as required for this frame.
-        if let Some(mut request) = gpu_cache.request(&mut metadata.gpu_location) {
-            request.push(metadata.local_rect);
-            request.push(metadata.local_clip_rect);
-
-            match metadata.prim_kind {
-                PrimitiveKind::Rectangle => {
-                    let rect = &self.cpu_rectangles[cpu_prim_index.0];
-                    rect.write_gpu_blocks(request);
-                }
-                PrimitiveKind::Line => {
-                    let line = &self.cpu_lines[metadata.cpu_prim_index.0];
-                    line.write_gpu_blocks(request);
-                }
-                PrimitiveKind::Border => {
-                    let border = &self.cpu_borders[cpu_prim_index.0];
-                    border.write_gpu_blocks(request);
-                }
-                PrimitiveKind::BoxShadow => {
-                    let box_shadow = &self.cpu_box_shadows[cpu_prim_index.0];
-                    box_shadow.write_gpu_blocks(request);
-                }
-                PrimitiveKind::Image => {
-                    let image = &self.cpu_images[cpu_prim_index.0];
-                    image.write_gpu_blocks(request);
-                }
-                PrimitiveKind::YuvImage => {
-                    let yuv_image = &self.cpu_yuv_images[cpu_prim_index.0];
-                    yuv_image.write_gpu_blocks(request);
-                }
-                PrimitiveKind::AlignedGradient => {
-                    let gradient = &self.cpu_gradients[cpu_prim_index.0];
-                    metadata.opacity = gradient.build_gpu_blocks_for_aligned(display_list, request);
-                }
-                PrimitiveKind::AngleGradient => {
-                    let gradient = &self.cpu_gradients[cpu_prim_index.0];
-                    gradient.build_gpu_blocks_for_angle_radial(display_list, request);
-                }
-                PrimitiveKind::RadialGradient => {
-                    let gradient = &self.cpu_radial_gradients[cpu_prim_index.0];
-                    gradient.build_gpu_blocks_for_angle_radial(display_list, request);
-                }
-                PrimitiveKind::TextRun => {
-                    let text = &self.cpu_text_runs[cpu_prim_index.0];
-                    text.write_gpu_blocks(&mut request);
-                }
-                PrimitiveKind::TextShadow => {
-                    let prim = &self.cpu_text_shadows[cpu_prim_index.0];
-                    request.push(prim.shadow.color);
-                    request.push([
-                        prim.shadow.offset.x,
-                        prim.shadow.offset.y,
-                        prim.shadow.blur_radius,
-                        0.0,
-                    ]);
-                }
-            }
-        }
-
-        Some(Geometry {
-            local_rect: prim_local_rect,
-            device_rect: prim_screen_rect,
-        })
+        Some(geometry)
     }
 }
 
 
 //Test for one clip region contains another
 trait InsideTest<T> {
     fn might_contain(&self, clip: &T) -> bool;
 }
--- a/gfx/webrender/src/render_task.rs
+++ b/gfx/webrender/src/render_task.rs
@@ -302,16 +302,17 @@ impl RenderTask {
 
     pub fn new_mask(
         key: Option<ClipId>,
         task_rect: DeviceIntRect,
         raw_clips: &[ClipWorkItem],
         extra_clip: Option<ClipWorkItem>,
         prim_rect: DeviceIntRect,
         clip_store: &ClipStore,
+        is_axis_aligned: bool,
     ) -> Option<RenderTask> {
         // Filter out all the clip instances that don't contribute to the result
         let mut inner_rect = Some(task_rect);
         let clips: Vec<_> = raw_clips
             .iter()
             .chain(extra_clip.iter())
             .filter(|work_item| {
                 let clip_info = clip_store
@@ -353,17 +354,17 @@ impl RenderTask {
         //           more complex types of clip mask geometry.
         let mut geometry_kind = MaskGeometryKind::Default;
         if let Some(inner_rect) = inner_rect {
             // If the inner rect completely contains the primitive
             // rect, then this mask can't affect the primitive.
             if inner_rect.contains_rect(&prim_rect) {
                 return None;
             }
-            if clips.len() == 1 {
+            if is_axis_aligned && clips.len() == 1 {
                 geometry_kind = clips[0].get_geometry_kind(clip_store);
             }
         }
 
         Some(RenderTask {
             cache_key: key.map(RenderTaskKey::CacheMask),
             children: Vec::new(),
             location: RenderTaskLocation::Dynamic(None, task_rect.size),
--- a/gfx/webrender_api/src/display_item.rs
+++ b/gfx/webrender_api/src/display_item.rs
@@ -553,16 +553,17 @@ impl LocalClip {
                     rect: complex.rect.translate(offset),
                     radii: complex.radii,
                 },
             ),
         }
     }
 }
 
+#[repr(C)]
 #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
 pub struct ComplexClipRegion {
     /// The boundaries of the rectangle.
     pub rect: LayoutRect,
     /// Border radii of this rectangle.
     pub radii: BorderRadius,
 }