Bug 1442422 - Update webrender to commit 0da6c839b3a0e165f1115fb9fe286be7540c24ed. r?jrmuizel draft
authorKartikaya Gupta <kgupta@mozilla.com>
Wed, 07 Mar 2018 10:15:43 -0500
changeset 764275 b123d435b7e2b8e7ce45430d6ac6e9cfb7159187
parent 764272 493e45400842b6ccfffb63b58b40b33a0b8154ab
push id101717
push userkgupta@mozilla.com
push dateWed, 07 Mar 2018 15:16:11 +0000
reviewersjrmuizel
bugs1442422
milestone60.0a1
Bug 1442422 - Update webrender to commit 0da6c839b3a0e165f1115fb9fe286be7540c24ed. r?jrmuizel MozReview-Commit-ID: DmcL7siJnZ2
gfx/webrender/Cargo.toml
gfx/webrender/res/prim_shared.glsl
gfx/webrender/src/clip.rs
gfx/webrender/src/clip_scroll_node.rs
gfx/webrender/src/display_list_flattener.rs
gfx/webrender/src/gpu_types.rs
gfx/webrender/src/picture.rs
gfx/webrender/src/prim_store.rs
gfx/webrender/src/render_backend.rs
gfx/webrender/src/resource_cache.rs
gfx/webrender/src/scene.rs
gfx/webrender/src/util.rs
gfx/webrender/tests/angle_shader_validation.rs
gfx/webrender_bindings/revision.txt
--- a/gfx/webrender/Cargo.toml
+++ b/gfx/webrender/Cargo.toml
@@ -35,17 +35,17 @@ smallvec = "0.6"
 ws = { optional = true, version = "0.7.3" }
 serde_json = { optional = true, version = "1.0" }
 serde = { optional = true, version = "1.0", features = ["serde_derive"] }
 image = { optional = true, version = "0.17" }
 base64 = { optional = true, version = "0.3.0" }
 ron = { optional = true, version = "0.1.7" }
 
 [dev-dependencies]
-angle = {git = "https://github.com/servo/angle", branch = "servo"}
+mozangle = "0.1"
 env_logger = "0.5"
 rand = "0.3"                # for the benchmarks
 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]
--- a/gfx/webrender/res/prim_shared.glsl
+++ b/gfx/webrender/res/prim_shared.glsl
@@ -66,31 +66,41 @@ vec4[2] fetch_from_resource_cache_2(int 
     return vec4[2](
         TEXEL_FETCH(sResourceCache, uv, 0, ivec2(0, 0)),
         TEXEL_FETCH(sResourceCache, uv, 0, ivec2(1, 0))
     );
 }
 
 #ifdef WR_VERTEX_SHADER
 
-#define VECS_PER_CLIP_SCROLL_NODE   5
+#define VECS_PER_CLIP_SCROLL_NODE   9
 #define VECS_PER_LOCAL_CLIP_RECT    1
 #define VECS_PER_RENDER_TASK        3
 #define VECS_PER_PRIM_HEADER        2
 #define VECS_PER_TEXT_RUN           3
 #define VECS_PER_GRADIENT_STOP      2
 
 uniform HIGHP_SAMPLER_FLOAT sampler2D sClipScrollNodes;
 uniform HIGHP_SAMPLER_FLOAT sampler2D sLocalClipRects;
 uniform HIGHP_SAMPLER_FLOAT sampler2D sRenderTasks;
 
 // Instanced attributes
 in ivec4 aData0;
 in ivec4 aData1;
 
+// Work around Angle bug that forgets to update sampler metadata,
+// by making the use of those samplers uniform across programs.
+// https://github.com/servo/webrender/wiki/Driver-issues#texturesize-in-vertex-shaders
+void markCacheTexturesUsed() {
+    vec2 size = vec2(textureSize(sCacheA8, 0)) + vec2(textureSize(sCacheRGBA8, 0));
+    if (size.x > 1000000.0) {
+        gl_Position = vec4(0.0);
+    }
+}
+
 // get_fetch_uv is a macro to work around a macOS Intel driver parsing bug.
 // TODO: convert back to a function once the driver issues are resolved, if ever.
 // https://github.com/servo/webrender/pull/623
 // https://github.com/servo/servo/issues/13953
 #define get_fetch_uv(i, vpi)  ivec2(vpi * (i % (WR_MAX_VERTEX_TEXTURE_WIDTH/vpi)), i / (WR_MAX_VERTEX_TEXTURE_WIDTH/vpi))
 
 
 vec4[8] fetch_from_resource_cache_8(int address) {
@@ -149,35 +159,42 @@ vec4 fetch_from_resource_cache_1_direct(
 
 vec4 fetch_from_resource_cache_1(int address) {
     ivec2 uv = get_resource_cache_uv(address);
     return texelFetch(sResourceCache, uv, 0);
 }
 
 struct ClipScrollNode {
     mat4 transform;
+    mat4 inv_transform;
     bool is_axis_aligned;
 };
 
 ClipScrollNode fetch_clip_scroll_node(int index) {
     ClipScrollNode node;
 
     // Create a UV base coord for each 8 texels.
     // This is required because trying to use an offset
     // of more than 8 texels doesn't work on some versions
     // of OSX.
     ivec2 uv = get_fetch_uv(index, VECS_PER_CLIP_SCROLL_NODE);
     ivec2 uv0 = ivec2(uv.x + 0, uv.y);
+    ivec2 uv1 = ivec2(uv.x + 8, uv.y);
 
     node.transform[0] = TEXEL_FETCH(sClipScrollNodes, uv0, 0, ivec2(0, 0));
     node.transform[1] = TEXEL_FETCH(sClipScrollNodes, uv0, 0, ivec2(1, 0));
     node.transform[2] = TEXEL_FETCH(sClipScrollNodes, uv0, 0, ivec2(2, 0));
     node.transform[3] = TEXEL_FETCH(sClipScrollNodes, uv0, 0, ivec2(3, 0));
 
-    vec4 misc = TEXEL_FETCH(sClipScrollNodes, uv0, 0, ivec2(4, 0));
+    node.inv_transform[0] = TEXEL_FETCH(sClipScrollNodes, uv0, 0, ivec2(4, 0));
+    node.inv_transform[1] = TEXEL_FETCH(sClipScrollNodes, uv0, 0, ivec2(5, 0));
+    node.inv_transform[2] = TEXEL_FETCH(sClipScrollNodes, uv0, 0, ivec2(6, 0));
+    node.inv_transform[3] = TEXEL_FETCH(sClipScrollNodes, uv0, 0, ivec2(7, 0));
+
+    vec4 misc = TEXEL_FETCH(sClipScrollNodes, uv1, 0, ivec2(0, 0));
     node.is_axis_aligned = misc.x == 0.0;
 
     return node;
 }
 
 RectWithSize fetch_clip_chain_rect(int index) {
     ivec2 uv = get_fetch_uv(index, VECS_PER_LOCAL_CLIP_RECT);
     vec4 rect = TEXEL_FETCH(sLocalClipRects, uv, 0, ivec2(0, 0));
@@ -353,16 +370,18 @@ PrimitiveInstance fetch_prim_instance() 
     pi.clip_task_index = aData0.z;
     pi.clip_chain_rect_index = aData0.w / 65536;
     pi.scroll_node_id = aData0.w % 65536;
     pi.z = aData1.x;
     pi.user_data0 = aData1.y;
     pi.user_data1 = aData1.z;
     pi.user_data2 = aData1.w;
 
+    markCacheTexturesUsed();
+
     return pi;
 }
 
 struct CompositeInstance {
     int render_task_index;
     int src_task_index;
     int backdrop_task_index;
     int user_data0;
@@ -380,16 +399,18 @@ CompositeInstance fetch_composite_instan
     ci.backdrop_task_index = aData0.z;
     ci.z = float(aData0.w);
 
     ci.user_data0 = aData1.x;
     ci.user_data1 = aData1.y;
     ci.user_data2 = aData1.z;
     ci.user_data3 = aData1.w;
 
+    markCacheTexturesUsed();
+
     return ci;
 }
 
 struct Primitive {
     ClipScrollNode scroll_node;
     ClipArea clip_area;
     PictureTask task;
     RectWithSize local_rect;
@@ -471,19 +492,18 @@ vec4 untransform(vec2 ref, vec3 n, vec3 
 
 // Given a CSS space position, transform it back into the ClipScrollNode space.
 vec4 get_node_pos(vec2 pos, ClipScrollNode node) {
     // get a point on the scroll node plane
     vec4 ah = node.transform * vec4(0.0, 0.0, 0.0, 1.0);
     vec3 a = ah.xyz / ah.w;
 
     // get the normal to the scroll node plane
-    mat4 inv_transform = inverse(node.transform);
-    vec3 n = transpose(mat3(inv_transform)) * vec3(0.0, 0.0, 1.0);
-    return untransform(pos, n, a, inv_transform);
+    vec3 n = transpose(mat3(node.inv_transform)) * vec3(0.0, 0.0, 1.0);
+    return untransform(pos, n, a, node.inv_transform);
 }
 
 // Compute a snapping offset in world space (adjusted to pixel ratio),
 // given local position on the scroll_node and a snap rectangle.
 vec2 compute_snap_offset(vec2 local_pos,
                          ClipScrollNode scroll_node,
                          RectWithSize snap_rect) {
     // Ensure that the snap rect is at *least* one device pixel in size.
--- a/gfx/webrender/src/clip.rs
+++ b/gfx/webrender/src/clip.rs
@@ -240,21 +240,16 @@ impl ClipSources {
                         tile: None,
                     },
                     gpu_cache,
                 );
             }
         }
     }
 
-    /// Whether or not this ClipSources has any clips (does any clipping).
-    pub fn has_clips(&self) -> bool {
-        !self.clips.is_empty()
-    }
-
     pub fn get_screen_bounds(
         &self,
         transform: &LayerToWorldFastTransform,
         device_pixel_scale: DevicePixelScale,
     ) -> (DeviceIntRect, Option<DeviceIntRect>) {
         // If this translation isn't axis aligned or has a perspective component, don't try to
         // calculate the inner rectangle. The rectangle that we produce would include potentially
         // clipped screen area.
--- a/gfx/webrender/src/clip_scroll_node.rs
+++ b/gfx/webrender/src/clip_scroll_node.rs
@@ -281,23 +281,32 @@ impl ClipScrollNode {
     }
 
     pub fn push_gpu_node_data(&mut self, node_data: &mut Vec<ClipScrollNodeData>) {
         if !self.invertible {
             node_data.push(ClipScrollNodeData::invalid());
             return;
         }
 
+        let inv_transform = match self.world_content_transform.inverse() {
+            Some(inverted) => inverted.to_transform(),
+            None => {
+                node_data.push(ClipScrollNodeData::invalid());
+                return;
+            }
+        };
+
         let transform_kind = if self.world_content_transform.preserves_2d_axis_alignment() {
             TransformedRectKind::AxisAligned
         } else {
             TransformedRectKind::Complex
         };
         let data = ClipScrollNodeData {
             transform: self.world_content_transform.into(),
+            inv_transform,
             transform_kind: transform_kind as u32 as f32,
             padding: [0.0; 3],
         };
 
         // Write the data that will be made available to the GPU for this node.
         node_data.push(data);
     }
 
--- a/gfx/webrender/src/display_list_flattener.rs
+++ b/gfx/webrender/src/display_list_flattener.rs
@@ -211,24 +211,23 @@ impl<'a> DisplayListFlattener<'a> {
         old_builder: FrameBuilder,
         scene: &Scene,
         clip_scroll_tree: &mut ClipScrollTree,
         font_instances: FontInstanceMap,
         tiled_image_map: TiledImageMap,
         view: &DocumentView,
         output_pipelines: &FastHashSet<PipelineId>,
         frame_builder_config: &FrameBuilderConfig,
-        pipeline_epochs: &mut FastHashMap<PipelineId, Epoch>,
+        new_scene: &mut Scene,
     ) -> FrameBuilder {
         // We checked that the root pipeline is available on the render backend.
         let root_pipeline_id = scene.root_pipeline_id.unwrap();
         let root_pipeline = scene.pipelines.get(&root_pipeline_id).unwrap();
 
         let root_epoch = scene.pipeline_epochs[&root_pipeline_id];
-        pipeline_epochs.insert(root_pipeline_id, root_epoch);
 
         let background_color = root_pipeline
             .background_color
             .and_then(|color| if color.a > 0.0 { Some(color) } else { None });
 
         let mut flattener = DisplayListFlattener {
             scene,
             clip_scroll_tree,
@@ -256,17 +255,21 @@ impl<'a> DisplayListFlattener<'a> {
             root_pipeline_id,
             &root_pipeline.viewport_size,
             &root_pipeline.content_size,
         );
         flattener.setup_viewport_offset(view.inner_rect, view.accumulated_scale_factor());
         flattener.flatten_root(root_pipeline, &root_pipeline.viewport_size);
 
         debug_assert!(flattener.picture_stack.is_empty());
-        pipeline_epochs.extend(flattener.pipeline_epochs.drain(..));
+
+        new_scene.root_pipeline_id = Some(root_pipeline_id);
+        new_scene.pipeline_epochs.insert(root_pipeline_id, root_epoch);
+        new_scene.pipeline_epochs.extend(flattener.pipeline_epochs.drain(..));
+        new_scene.pipelines = scene.pipelines.clone();
 
         FrameBuilder::with_display_list_flattener(
             view.inner_rect,
             background_color,
             view.window_size,
             flattener
         )
     }
@@ -1159,17 +1162,22 @@ impl<'a> DisplayListFlattener<'a> {
                 region.rect,
                 region.radii,
                 region.mode,
             ));
         }
 
         let stacking_context = self.sc_stack.last().expect("bug: no stacking context!");
 
-        let clip_sources = self.clip_store.insert(ClipSources::new(clip_sources));
+        let clip_sources = if clip_sources.is_empty() {
+            None
+        } else {
+            Some(self.clip_store.insert(ClipSources::new(clip_sources)))
+        };
+
         let prim_index = self.prim_store.add_primitive(
             &info.rect,
             &info.local_clip.clip_rect(),
             info.is_backface_visible && stacking_context.is_backface_visible,
             clip_sources,
             info.tag,
             container,
         );
@@ -1266,28 +1274,25 @@ impl<'a> DisplayListFlattener<'a> {
             let pic = PicturePrimitive::new_image(
                 None,
                 false,
                 pipeline_id,
                 current_reference_frame_index,
                 None,
             );
 
-            // No clip sources needed for the main framebuffer.
-            let clip_sources = self.clip_store.insert(ClipSources::new(Vec::new()));
-
             // Add root picture primitive. The provided layer rect
             // is zero, because we don't yet know the size of the
             // picture. Instead, this is calculated recursively
             // when we cull primitives.
             let prim_index = self.prim_store.add_primitive(
                 &LayerRect::zero(),
                 &max_clip,
                 true,
-                clip_sources,
+                None,
                 None,
                 PrimitiveContainer::Picture(pic),
             );
 
             self.picture_stack.push(prim_index);
         } else if composite_ops.mix_blend_mode.is_some() && self.sc_stack.len() > 2 {
             // If we have a mix-blend-mode, and we aren't the primary framebuffer,
             // the stacking context needs to be isolated to blend correctly as per
@@ -1346,23 +1351,21 @@ impl<'a> DisplayListFlattener<'a> {
             let container = PicturePrimitive::new_image(
                 None,
                 false,
                 pipeline_id,
                 current_reference_frame_index,
                 None,
             );
 
-            let clip_sources = self.clip_store.insert(ClipSources::new(Vec::new()));
-
             let prim_index = self.prim_store.add_primitive(
                 &LayerRect::zero(),
                 &max_clip,
                 is_backface_visible,
-                clip_sources,
+                None,
                 None,
                 PrimitiveContainer::Picture(container),
             );
 
             let parent_pic_prim_index = *self.picture_stack.last().unwrap();
             let pic_prim_index = self.prim_store.cpu_metadata[parent_pic_prim_index.0].cpu_prim_index;
             let pic = &mut self.prim_store.cpu_pictures[pic_prim_index.0];
             pic.add_primitive(
@@ -1397,23 +1400,22 @@ impl<'a> DisplayListFlattener<'a> {
         for filter in composite_ops.filters.iter().rev() {
             let src_prim = PicturePrimitive::new_image(
                 Some(PictureCompositeMode::Filter(*filter)),
                 false,
                 pipeline_id,
                 current_reference_frame_index,
                 None,
             );
-            let src_clip_sources = self.clip_store.insert(ClipSources::new(Vec::new()));
 
             let src_prim_index = self.prim_store.add_primitive(
                 &LayerRect::zero(),
                 &max_clip,
                 is_backface_visible,
-                src_clip_sources,
+                None,
                 None,
                 PrimitiveContainer::Picture(src_prim),
             );
 
             let pic_prim_index = self.prim_store.cpu_metadata[parent_pic_prim_index.0].cpu_prim_index;
             parent_pic_prim_index = src_prim_index;
             let pic = &mut self.prim_store.cpu_pictures[pic_prim_index.0];
             pic.add_primitive(
@@ -1428,23 +1430,22 @@ impl<'a> DisplayListFlattener<'a> {
         if let Some(mix_blend_mode) = composite_ops.mix_blend_mode {
             let src_prim = PicturePrimitive::new_image(
                 Some(PictureCompositeMode::MixBlend(mix_blend_mode)),
                 false,
                 pipeline_id,
                 current_reference_frame_index,
                 None,
             );
-            let src_clip_sources = self.clip_store.insert(ClipSources::new(Vec::new()));
 
             let src_prim_index = self.prim_store.add_primitive(
                 &LayerRect::zero(),
                 &max_clip,
                 is_backface_visible,
-                src_clip_sources,
+                None,
                 None,
                 PrimitiveContainer::Picture(src_prim),
             );
 
             let pic_prim_index = self.prim_store.cpu_metadata[parent_pic_prim_index.0].cpu_prim_index;
             parent_pic_prim_index = src_prim_index;
             let pic = &mut self.prim_store.cpu_pictures[pic_prim_index.0];
             pic.add_primitive(
@@ -1482,22 +1483,21 @@ impl<'a> DisplayListFlattener<'a> {
         let sc_prim = PicturePrimitive::new_image(
             composite_mode,
             participating_in_3d_context,
             pipeline_id,
             current_reference_frame_index,
             frame_output_pipeline_id,
         );
 
-        let sc_clip_sources = self.clip_store.insert(ClipSources::new(Vec::new()));
         let sc_prim_index = self.prim_store.add_primitive(
             &LayerRect::zero(),
             &max_clip,
             is_backface_visible,
-            sc_clip_sources,
+            None,
             None,
             PrimitiveContainer::Picture(sc_prim),
         );
 
         let pic_prim_index = self.prim_store.cpu_metadata[parent_pic_prim_index.0].cpu_prim_index;
         let sc_pic = &mut self.prim_store.cpu_pictures[pic_prim_index.0];
         sc_pic.add_primitive(
             sc_prim_index,
@@ -1641,18 +1641,16 @@ impl<'a> DisplayListFlattener<'a> {
     pub fn add_clip_node(
         &mut self,
         new_node_id: ClipId,
         parent_id: ClipId,
         clip_region: ClipRegion,
     ) -> ClipScrollNodeIndex {
         let clip_rect = clip_region.main;
         let clip_sources = ClipSources::from(clip_region);
-
-        debug_assert!(clip_sources.has_clips());
         let handle = self.clip_store.insert(clip_sources);
 
         let node_index = self.id_to_index_mapper.get_node_index(new_node_id);
         let clip_chain_index = self.clip_scroll_tree.add_clip_node(
             node_index,
             self.id_to_index_mapper.get_node_index(parent_id),
             handle,
             clip_rect,
@@ -2616,38 +2614,34 @@ impl<'a> DisplayListFlattener<'a> {
             info,
             Vec::new(),
             PrimitiveContainer::Brush(prim),
         );
     }
 }
 
 pub fn build_scene(config: &FrameBuilderConfig, request: SceneRequest) -> BuiltScene {
-    // TODO: mutably pass the scene and update its own pipeline epoch map instead of
-    // creating a new one here.
-    let mut pipeline_epoch_map = FastHashMap::default();
+
     let mut clip_scroll_tree = ClipScrollTree::new();
+    let mut new_scene = Scene::new();
 
     let frame_builder = DisplayListFlattener::create_frame_builder(
         FrameBuilder::empty(), // WIP, we're not really recycling anything here, clean this up.
         &request.scene,
         &mut clip_scroll_tree,
         request.font_instances,
         request.tiled_image_map,
         &request.view,
         &request.output_pipelines,
         config,
-        &mut pipeline_epoch_map
+        &mut new_scene
     );
 
-    let mut scene = request.scene;
-    scene.pipeline_epochs = pipeline_epoch_map;
-
     BuiltScene {
-        scene,
+        scene: new_scene,
         frame_builder,
         clip_scroll_tree,
         removed_pipelines: request.removed_pipelines,
     }
 }
 
 trait PrimitiveInfoTiler {
     fn decompose(
--- a/gfx/webrender/src/gpu_types.rs
+++ b/gfx/webrender/src/gpu_types.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::{DevicePoint, LayerToWorldTransform, PremultipliedColorF};
+use api::{DevicePoint, LayerToWorldTransform, PremultipliedColorF, WorldToLayerTransform};
 use gpu_cache::{GpuCacheAddress, GpuDataRequest};
 use prim_store::EdgeAaSegmentMask;
 use render_task::RenderTaskAddress;
 
 // Contains type that must exactly match the same structures declared in GLSL.
 
 #[repr(i32)]
 #[derive(Debug, Copy, Clone)]
@@ -212,24 +212,26 @@ pub enum BrushImageKind {
 pub struct ClipScrollNodeIndex(pub u32);
 
 #[derive(Debug)]
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 #[repr(C)]
 pub struct ClipScrollNodeData {
     pub transform: LayerToWorldTransform,
+    pub inv_transform: WorldToLayerTransform,
     pub transform_kind: f32,
     pub padding: [f32; 3],
 }
 
 impl ClipScrollNodeData {
     pub fn invalid() -> Self {
         ClipScrollNodeData {
             transform: LayerToWorldTransform::identity(),
+            inv_transform: WorldToLayerTransform::identity(),
             transform_kind: 0.0,
             padding: [0.0; 3],
         }
     }
 }
 
 #[derive(Copy, Debug, Clone, PartialEq)]
 #[repr(C)]
@@ -268,9 +270,9 @@ impl ImageSource {
         request.push([
             self.texture_layer,
             self.user_data[0],
             self.user_data[1],
             self.user_data[2],
         ]);
         request.push(self.color);
     }
-}
\ No newline at end of file
+}
--- a/gfx/webrender/src/picture.rs
+++ b/gfx/webrender/src/picture.rs
@@ -1,28 +1,44 @@
 /* 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::{BoxShadowClipMode, ColorF, DeviceIntPoint, DeviceIntRect, FilterOp, LayerPoint};
-use api::{LayerRect, LayerToWorldScale, LayerVector2D, MixBlendMode, PipelineId};
+use api::{DeviceIntPoint, DeviceIntRect, DeviceIntSize, DeviceSize};
+use api::{LayerPoint, LayerRect, LayerToWorldScale, LayerVector2D};
+use api::{BoxShadowClipMode, ColorF, FilterOp, MixBlendMode, PipelineId};
 use api::{PremultipliedColorF, Shadow};
 use box_shadow::{BLUR_SAMPLE_SCALE, BoxShadowCacheKey};
 use clip_scroll_tree::ClipScrollNodeIndex;
 use frame_builder::{FrameBuildingContext, FrameBuildingState, PictureState};
 use gpu_cache::{GpuCacheHandle, GpuDataRequest};
 use gpu_types::{BrushImageKind, PictureType};
 use prim_store::{BrushKind, BrushPrimitive, PrimitiveIndex, PrimitiveRun, PrimitiveRunLocalRect};
 use prim_store::ScrollNodeAndClipChain;
 use render_task::{ClearMode, RenderTask, RenderTaskCacheKey};
 use render_task::{RenderTaskCacheKeyKind, RenderTaskId, RenderTaskLocation};
 use resource_cache::CacheItem;
 use scene::{FilterOpHelpers, SceneProperties};
 use tiling::RenderTargetKind;
 
+
+// TODO(gw): Rounding the content rect here to device pixels is not
+// technically correct. Ideally we should ceil() here, and ensure that
+// the extra part pixel in the case of fractional sizes is correctly
+// handled. For now, just use rounding which passes the existing
+// Gecko tests.
+// Note: zero-square tasks are prohibited in WR task tree, so
+// we ensure each dimension to be at least the length of 1 after rounding.
+fn to_cache_size(size: DeviceSize) -> DeviceIntSize {
+    DeviceIntSize::new(
+        1.max(size.width.round() as i32),
+        1.max(size.height.round() as i32),
+    )
+}
+
 /*
  A picture represents a dynamically rendered image. It consists of:
 
  * A number of primitives that are drawn onto the picture.
  * A composite operation describing how to composite this
    picture into its parent.
  * A configuration describing how to draw the primitives on
    this picture (e.g. in screen space or local space).
@@ -469,23 +485,17 @@ impl PicturePrimitive {
                     }
                 }
             }
             PictureKind::TextShadow { blur_radius, color, content_rect, .. } => {
                 // This is a 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.
-
-                // TODO(gw): Rounding the content rect here to device pixels is not
-                // technically correct. Ideally we should ceil() here, and ensure that
-                // the extra part pixel in the case of fractional sizes is correctly
-                // handled. For now, just use rounding which passes the existing
-                // Gecko tests.
-                let cache_size = (content_rect.size * content_scale).round().to_i32();
+                let cache_size = to_cache_size(content_rect.size * content_scale);
 
                 // Quote from https://drafts.csswg.org/css-backgrounds-3/#shadow-blur
                 // "the image that would be generated by applying to the shadow a
                 // Gaussian blur with a standard deviation equal to half the blur radius."
                 let device_radius = (blur_radius * frame_context.device_pixel_scale.0).round();
                 let blur_std_deviation = device_radius * 0.5;
 
                 let picture_task = RenderTask::new_picture(
@@ -515,17 +525,17 @@ impl PicturePrimitive {
                 self.surface = Some(PictureSurface::RenderTask(render_task_id));
             }
             PictureKind::BoxShadow { blur_radius, clip_mode, color, content_rect, cache_key, .. } => {
                 // TODO(gw): Rounding the content rect here to device pixels is not
                 // technically correct. Ideally we should ceil() here, and ensure that
                 // the extra part pixel in the case of fractional sizes is correctly
                 // handled. For now, just use rounding which passes the existing
                 // Gecko tests.
-                let cache_size = (content_rect.size * content_scale).round().to_i32();
+                let cache_size = to_cache_size(content_rect.size * content_scale);
 
                 // Request the texture cache item for this box-shadow key. If it
                 // doesn't exist in the cache, the closure is invoked to build
                 // a render task chain to draw the cacheable result.
                 let cache_item = frame_state.resource_cache.request_render_task(
                     RenderTaskCacheKey {
                         size: cache_size,
                         kind: RenderTaskCacheKeyKind::BoxShadow(cache_key),
--- a/gfx/webrender/src/prim_store.rs
+++ b/gfx/webrender/src/prim_store.rs
@@ -163,17 +163,17 @@ pub struct ScreenRect {
     pub clipped: DeviceIntRect,
     pub unclipped: DeviceIntRect,
 }
 
 // TODO(gw): Pack the fields here better!
 #[derive(Debug)]
 pub struct PrimitiveMetadata {
     pub opacity: PrimitiveOpacity,
-    pub clip_sources: ClipSourcesHandle,
+    pub clip_sources: Option<ClipSourcesHandle>,
     pub prim_kind: PrimitiveKind,
     pub cpu_prim_index: SpecificPrimitiveIndex,
     pub gpu_location: GpuCacheHandle,
     pub clip_task_id: Option<RenderTaskId>,
 
     // TODO(gw): In the future, we should just pull these
     //           directly from the DL item, instead of
     //           storing them here.
@@ -959,17 +959,17 @@ impl PrimitiveStore {
         }
     }
 
     pub fn add_primitive(
         &mut self,
         local_rect: &LayerRect,
         local_clip_rect: &LayerRect,
         is_backface_visible: bool,
-        clip_sources: ClipSourcesHandle,
+        clip_sources: Option<ClipSourcesHandle>,
         tag: Option<ItemTag>,
         container: PrimitiveContainer,
     ) -> PrimitiveIndex {
         let prim_index = self.cpu_metadata.len();
 
         let base_metadata = PrimitiveMetadata {
             clip_sources,
             gpu_location: GpuCacheHandle::new(),
@@ -1565,47 +1565,45 @@ impl PrimitiveStore {
         let mut combined_outer_rect =
             prim_screen_rect.intersection(&prim_run_context.clip_chain.combined_outer_screen_rect);
         let clip_chain = prim_run_context.clip_chain.nodes.clone();
 
         let prim_coordinate_system_id = prim_run_context.scroll_node.coordinate_system_id;
         let transform = &prim_run_context.scroll_node.world_content_transform;
         let extra_clip =  {
             let metadata = &self.cpu_metadata[prim_index.0];
-            let prim_clips = frame_state.clip_store.get_mut(&metadata.clip_sources);
-            if prim_clips.has_clips() {
+            metadata.clip_sources.as_ref().map(|ref clip_sources| {
+                let prim_clips = frame_state.clip_store.get_mut(clip_sources);
                 prim_clips.update(
                     frame_state.gpu_cache,
                     frame_state.resource_cache,
                 );
                 let (screen_inner_rect, screen_outer_rect) =
                     prim_clips.get_screen_bounds(transform, frame_context.device_pixel_scale);
 
                 if let Some(outer) = screen_outer_rect {
                     combined_outer_rect = combined_outer_rect.and_then(|r| r.intersection(&outer));
                 }
 
-                Some(Arc::new(ClipChainNode {
+                Arc::new(ClipChainNode {
                     work_item: ClipWorkItem {
                         scroll_node_data_index: prim_run_context.scroll_node.node_data_index,
-                        clip_sources: metadata.clip_sources.weak(),
+                        clip_sources: clip_sources.weak(),
                         coordinate_system_id: prim_coordinate_system_id,
                     },
                     // The local_clip_rect a property of ClipChain nodes that are ClipScrollNodes.
                     // It's used to calculate a local clipping rectangle before we reach this
                     // point, so we can set it to zero here. It should be unused from this point
                     // on.
                     local_clip_rect: LayerRect::zero(),
                     screen_inner_rect,
                     screen_outer_rect: screen_outer_rect.unwrap_or(prim_screen_rect),
                     prev: None,
-                }))
-            } else {
-                None
-            }
+                })
+            })
         };
 
         // If everything is clipped out, then we don't need to render this primitive.
         let combined_outer_rect = match combined_outer_rect {
             Some(rect) if !rect.is_empty() => rect,
             _ => {
                 self.cpu_metadata[prim_index.0].screen_rect = None;
                 return false;
--- a/gfx/webrender/src/render_backend.rs
+++ b/gfx/webrender/src/render_backend.rs
@@ -167,23 +167,59 @@ impl Document {
             dynamic_properties: SceneProperties::new(),
         }
     }
 
     fn can_render(&self) -> bool { self.frame_builder.is_some() }
 
     // TODO: We will probably get rid of this soon and always forward to the scene building thread.
     fn build_scene(&mut self, resource_cache: &mut ResourceCache) {
-        let frame_builder = self.create_frame_builder(resource_cache);
+
+        if self.view.window_size.width == 0 || self.view.window_size.height == 0 {
+            error!("ERROR: Invalid window dimensions! Please call api.set_window_size()");
+        }
+
+        let old_builder = self.frame_builder.take().unwrap_or_else(FrameBuilder::empty);
+        let root_pipeline_id = match self.pending.scene.root_pipeline_id {
+            Some(root_pipeline_id) => root_pipeline_id,
+            None => return,
+        };
+
+        if !self.pending.scene.pipelines.contains_key(&root_pipeline_id) {
+            return;
+        }
+
+        // The DisplayListFlattener will re-create the up-to-date current scene's pipeline epoch
+        // map and clip scroll tree from the information in the pending scene.
+        self.current.scene.pipeline_epochs.clear();
+        let old_scrolling_states = self.clip_scroll_tree.drain();
+
+        let frame_builder = DisplayListFlattener::create_frame_builder(
+            old_builder,
+            &self.pending.scene,
+            &mut self.clip_scroll_tree,
+            resource_cache.get_font_instances(),
+            resource_cache.get_tiled_image_map(),
+            &self.view,
+            &self.output_pipelines,
+            &self.frame_builder_config,
+            &mut self.current.scene,
+        );
+
+        self.clip_scroll_tree.finalize_and_apply_pending_scroll_offsets(old_scrolling_states);
+
         if !self.current.removed_pipelines.is_empty() {
             warn!("Built the scene several times without rendering it.");
         }
+
         self.current.removed_pipelines.extend(self.pending.removed_pipelines.drain(..));
         self.frame_builder = Some(frame_builder);
-        self.current.scene = self.pending.scene.clone();
+
+        // Advance to the next frame.
+        self.frame_id.0 += 1;
     }
 
     fn forward_transaction_to_scene_builder(
         &mut self,
         transaction_msg: TransactionMsg,
         document_ops: &DocumentOps,
         document_id: DocumentId,
         resource_cache: &ResourceCache,
@@ -307,58 +343,16 @@ impl Document {
 
         let old_scrolling_states = self.clip_scroll_tree.drain();
         self.clip_scroll_tree = built_scene.clip_scroll_tree;
         self.clip_scroll_tree.finalize_and_apply_pending_scroll_offsets(old_scrolling_states);
 
         // Advance to the next frame.
         self.frame_id.0 += 1;
     }
-
-    // When changing this, please make the same modification to build_scene,
-    // which will soon replace this method completely.
-    pub fn create_frame_builder(&mut self, resource_cache: &mut ResourceCache) -> FrameBuilder {
-        if self.view.window_size.width == 0 || self.view.window_size.height == 0 {
-            error!("ERROR: Invalid window dimensions! Please call api.set_window_size()");
-        }
-
-        let old_builder = self.frame_builder.take().unwrap_or_else(FrameBuilder::empty);
-        let root_pipeline_id = match self.pending.scene.root_pipeline_id {
-            Some(root_pipeline_id) => root_pipeline_id,
-            None => return old_builder,
-        };
-
-        if !self.pending.scene.pipelines.contains_key(&root_pipeline_id) {
-            return old_builder;
-        }
-
-        // The DisplayListFlattener will re-create the up-to-date current scene's pipeline epoch
-        // map and clip scroll tree from the information in the pending scene.
-        self.current.scene.pipeline_epochs.clear();
-        let old_scrolling_states = self.clip_scroll_tree.drain();
-
-        let frame_builder = DisplayListFlattener::create_frame_builder(
-            old_builder,
-            &self.pending.scene,
-            &mut self.clip_scroll_tree,
-            resource_cache.get_font_instances(),
-            resource_cache.get_tiled_image_map(),
-            &self.view,
-            &self.output_pipelines,
-            &self.frame_builder_config,
-            &mut self.current.scene.pipeline_epochs,
-        );
-
-        self.clip_scroll_tree.finalize_and_apply_pending_scroll_offsets(old_scrolling_states);
-
-        // Advance to the next frame.
-        self.frame_id.0 += 1;
-
-        frame_builder
-    }
 }
 
 struct DocumentOps {
     scroll: bool,
     build: bool,
     render: bool,
     composite: bool,
 }
@@ -1144,20 +1138,25 @@ impl RenderBackend {
     #[cfg(feature = "capture")]
     // Note: the mutable `self` is only needed here for resolving blob images
     fn save_capture(
         &mut self,
         root: PathBuf,
         bits: CaptureBits,
         profile_counters: &mut BackendProfileCounters,
     ) -> DebugOutput {
+        use std::fs;
         use capture::CaptureConfig;
 
         debug!("capture: saving {:?}", root);
-        let (resources, deferred) = self.resource_cache.save_capture(&root);
+        if !root.is_dir() {
+            if let Err(e) = fs::create_dir_all(&root) {
+                panic!("Unable to create capture dir: {:?}", e);
+            }
+        }
         let config = CaptureConfig::new(root, bits);
 
         for (&id, doc) in &mut self.documents {
             debug!("\tdocument {:?}", id);
             if config.bits.contains(CaptureBits::SCENE) {
                 let file_name = format!("scene-{}-{}", (id.0).0, id.1);
                 config.serialize(&doc.current.scene, file_name);
             }
@@ -1170,16 +1169,19 @@ impl RenderBackend {
                 //TODO: write down full `RenderedDocument`?
                 // it has `pipeline_epoch_map` and `layers_bouncing_back`,
                 // which may capture necessary details for some cases.
                 let file_name = format!("frame-{}-{}", (id.0).0, id.1);
                 config.serialize(&rendered_document.frame, file_name);
             }
         }
 
+        debug!("\tresource cache");
+        let (resources, deferred) = self.resource_cache.save_capture(&config.root);
+
         info!("\tbackend");
         let backend = PlainRenderBackend {
             default_device_pixel_ratio: self.default_device_pixel_ratio,
             enable_render_on_scroll: self.enable_render_on_scroll,
             frame_config: self.frame_config.clone(),
             documents: self.documents
                 .iter()
                 .map(|(id, doc)| (*id, doc.view.clone()))
--- a/gfx/webrender/src/resource_cache.rs
+++ b/gfx/webrender/src/resource_cache.rs
@@ -1055,19 +1055,16 @@ impl ResourceCache {
     ) -> (PlainResources, Vec<ExternalCaptureImage>) {
         #[cfg(feature = "png")]
         use device::ReadPixelsFormat;
         use std::fs;
         use std::io::Write;
 
         info!("saving resource cache");
         let res = &self.resources;
-        if !root.is_dir() {
-            fs::create_dir_all(root).unwrap()
-        }
         let path_fonts = root.join("fonts");
         if !path_fonts.is_dir() {
             fs::create_dir(&path_fonts).unwrap();
         }
         let path_images = root.join("images");
         if !path_images.is_dir() {
             fs::create_dir(&path_images).unwrap();
         }
--- a/gfx/webrender/src/scene.rs
+++ b/gfx/webrender/src/scene.rs
@@ -141,16 +141,17 @@ impl Scene {
         self.pipeline_epochs.insert(pipeline_id, epoch);
     }
 
     pub fn remove_pipeline(&mut self, pipeline_id: PipelineId) {
         if self.root_pipeline_id == Some(pipeline_id) {
             self.root_pipeline_id = None;
         }
         self.pipelines.remove(&pipeline_id);
+        self.pipeline_epochs.remove(&pipeline_id);
     }
 
     pub fn update_epoch(&mut self, pipeline_id: PipelineId, epoch: Epoch) {
         self.pipeline_epochs.insert(pipeline_id, epoch);
     }
 }
 
 /// An arbitrary number which we assume opacity is invisible below.
--- a/gfx/webrender/src/util.rs
+++ b/gfx/webrender/src/util.rs
@@ -396,17 +396,17 @@ impl<Src, Dst> FastTransform<Src, Dst> {
     #[inline(always)]
     pub fn pre_mul<NewSrc>(
         &self,
         other: &FastTransform<NewSrc, Src>
     ) -> FastTransform<NewSrc, Dst> {
         match (self, other) {
             (&FastTransform::Offset(ref offset), &FastTransform::Offset(ref other_offset)) => {
                 let offset = TypedVector2D::from_untyped(&offset.to_untyped());
-                FastTransform::Offset((offset + *other_offset))
+                FastTransform::Offset(offset + *other_offset)
             }
             _ => {
                 let new_transform = self.to_transform().pre_mul(&other.to_transform());
                 FastTransform::with_transform(new_transform)
             }
         }
     }
 
--- a/gfx/webrender/tests/angle_shader_validation.rs
+++ b/gfx/webrender/tests/angle_shader_validation.rs
@@ -1,16 +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/. */
 
-extern crate angle;
+extern crate mozangle;
 extern crate webrender;
 
-use angle::hl::{BuiltInResources, Output, ShaderSpec, ShaderValidator};
+use mozangle::shaders::{BuiltInResources, Output, ShaderSpec, ShaderValidator};
 
 // from glslang
 const FRAGMENT_SHADER: u32 = 0x8B30;
 const VERTEX_SHADER: u32 = 0x8B31;
 
 struct Shader {
     name: &'static str,
     features: &'static [&'static str],
@@ -108,17 +108,17 @@ const SHADERS: &[Shader] = &[
         features: &[],
     },
 ];
 
 const VERSION_STRING: &str = "#version 300 es\n";
 
 #[test]
 fn validate_shaders() {
-    angle::hl::initialize().unwrap();
+    mozangle::shaders::initialize().unwrap();
 
     let resources = BuiltInResources::default();
     let vs_validator =
         ShaderValidator::new(VERTEX_SHADER, ShaderSpec::Gles3, Output::Essl, &resources).unwrap();
 
     let fs_validator =
         ShaderValidator::new(FRAGMENT_SHADER, ShaderSpec::Gles3, Output::Essl, &resources).unwrap();
 
--- a/gfx/webrender_bindings/revision.txt
+++ b/gfx/webrender_bindings/revision.txt
@@ -1,1 +1,1 @@
-22b831c02479eea31821f49a0fac7dd699083557
+0da6c839b3a0e165f1115fb9fe286be7540c24ed