Bug 1507751 - Cherry-pick webrender commit 3b2ed3d2ea21c4873b09da6a371ce07357c9c118 (WR PR #3313). r=kats
authorWR Updater Bot <graphics-team@mozilla.staktrace.com>
Fri, 16 Nov 2018 11:51:10 +0000
changeset 503179 9cfcc27a9446824df7777bd93b2a4878e348098d
parent 503178 112ed7029b78bdeaec3a6a9956cb44460ff7caa1
child 503180 6da0db82505cb9e55b15c4306725503cf168553d
push id10290
push userffxbld-merge
push dateMon, 03 Dec 2018 16:23:23 +0000
treeherdermozilla-beta@700bed2445e6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskats
bugs1507751
milestone65.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 1507751 - Cherry-pick webrender commit 3b2ed3d2ea21c4873b09da6a371ce07357c9c118 (WR PR #3313). r=kats Differential Revision: https://phabricator.services.mozilla.com/D12109
gfx/webrender/src/batch.rs
gfx/webrender/src/frame_builder.rs
gfx/webrender/src/picture.rs
gfx/webrender/src/prim_store.rs
gfx/webrender_bindings/revision.txt
--- a/gfx/webrender/src/batch.rs
+++ b/gfx/webrender/src/batch.rs
@@ -1,15 +1,15 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use api::{AlphaType, ClipMode, DeviceIntRect, DeviceIntSize};
 use api::{DeviceUintRect, DeviceUintPoint, ExternalImageType, FilterOp, ImageRendering};
-use api::{YuvColorSpace, YuvFormat, WorldRect, ColorDepth};
+use api::{YuvColorSpace, YuvFormat, ColorDepth, PictureRect};
 use clip::{ClipDataStore, ClipNodeFlags, ClipNodeRange, ClipItem, ClipStore};
 use clip_scroll_tree::{ClipScrollTree, ROOT_SPATIAL_NODE_INDEX, SpatialNodeIndex};
 use glyph_rasterizer::GlyphFormat;
 use gpu_cache::{GpuCache, GpuCacheHandle, GpuCacheAddress};
 use gpu_types::{BrushFlags, BrushInstance, PrimitiveHeaders, ZBufferId, ZBufferIdGenerator};
 use gpu_types::{ClipMaskInstance, SplitCompositeInstance};
 use gpu_types::{PrimitiveInstanceData, RasterizationSpace, GlyphInstance};
 use gpu_types::{PrimitiveHeader, PrimitiveHeaderIndex, TransformPaletteId, TransformPalette};
@@ -120,17 +120,17 @@ impl BatchKey {
 
 #[inline]
 fn textures_compatible(t1: TextureSource, t2: TextureSource) -> bool {
     t1 == TextureSource::Invalid || t2 == TextureSource::Invalid || t1 == t2
 }
 
 pub struct AlphaBatchList {
     pub batches: Vec<PrimitiveBatch>,
-    pub item_rects: Vec<Vec<WorldRect>>,
+    pub item_rects: Vec<Vec<PictureRect>>,
     current_batch_index: usize,
     current_z_id: ZBufferId,
 }
 
 impl AlphaBatchList {
     fn new() -> Self {
         AlphaBatchList {
             batches: Vec::new(),
@@ -138,17 +138,17 @@ impl AlphaBatchList {
             current_z_id: ZBufferId::invalid(),
             current_batch_index: usize::MAX,
         }
     }
 
     pub fn set_params_and_get_batch(
         &mut self,
         key: BatchKey,
-        bounding_rect: &WorldRect,
+        bounding_rect: &PictureRect,
         z_id: ZBufferId,
     ) -> &mut Vec<PrimitiveInstanceData> {
         if z_id != self.current_z_id ||
            self.current_batch_index == usize::MAX ||
            !self.batches[self.current_batch_index].key.is_compatible_with(&key) {
             let mut selected_batch_index = None;
 
             match key.blend_mode {
@@ -219,17 +219,17 @@ impl OpaqueBatchList {
             pixel_area_threshold_for_new_batch,
             current_batch_index: usize::MAX,
         }
     }
 
     pub fn set_params_and_get_batch(
         &mut self,
         key: BatchKey,
-        bounding_rect: &WorldRect,
+        bounding_rect: &PictureRect,
     ) -> &mut Vec<PrimitiveInstanceData> {
         if self.current_batch_index == usize::MAX ||
            !self.batches[self.current_batch_index].key.is_compatible_with(&key) {
             let mut selected_batch_index = None;
             let item_area = bounding_rect.size.area();
 
             // If the area of this primitive is larger than the given threshold,
             // then it is large enough to warrant breaking a batch for. In this
@@ -291,17 +291,17 @@ impl BatchList {
             alpha_batch_list: AlphaBatchList::new(),
             opaque_batch_list: OpaqueBatchList::new(batch_area_threshold),
         }
     }
 
     pub fn push_single_instance(
         &mut self,
         key: BatchKey,
-        bounding_rect: &WorldRect,
+        bounding_rect: &PictureRect,
         z_id: ZBufferId,
         instance: PrimitiveInstanceData,
     ) {
         match key.blend_mode {
             BlendMode::None => {
                 self.opaque_batch_list
                     .set_params_and_get_batch(key, bounding_rect)
                     .push(instance);
@@ -317,17 +317,17 @@ impl BatchList {
                     .push(instance);
             }
         }
     }
 
     pub fn set_params_and_get_batch(
         &mut self,
         key: BatchKey,
-        bounding_rect: &WorldRect,
+        bounding_rect: &PictureRect,
         z_id: ZBufferId,
     ) -> &mut Vec<PrimitiveInstanceData> {
         match key.blend_mode {
             BlendMode::None => {
                 self.opaque_batch_list
                     .set_params_and_get_batch(key, bounding_rect)
             }
             BlendMode::Alpha |
@@ -509,17 +509,17 @@ impl AlphaBatchBuilder {
         task_id: RenderTaskId,
         task_address: RenderTaskAddress,
         deferred_resolves: &mut Vec<DeferredResolve>,
         prim_headers: &mut PrimitiveHeaders,
         transforms: &mut TransformPalette,
         root_spatial_node_index: SpatialNodeIndex,
         z_generator: &mut ZBufferIdGenerator,
     ) {
-        if prim_instance.clipped_world_rect.is_none() {
+        if prim_instance.bounding_rect.is_none() {
             return;
         }
 
         #[cfg(debug_assertions)] //TODO: why is this needed?
         debug_assert_eq!(prim_instance.prepared_frame_id, render_tasks.frame_id());
 
         let transform_id = transforms
             .get_id(
@@ -527,17 +527,17 @@ impl AlphaBatchBuilder {
                 root_spatial_node_index,
                 ctx.clip_scroll_tree,
             );
 
         // TODO(gw): Calculating this for every primitive is a bit
         //           wasteful. We should probably cache this in
         //           the scroll node...
         let transform_kind = transform_id.transform_kind();
-        let bounding_rect = prim_instance.clipped_world_rect
+        let bounding_rect = prim_instance.bounding_rect
                                          .as_ref()
                                          .expect("bug");
         let z_id = z_generator.next();
 
         // Get the clip task address for the global primitive, if one was set.
         let clip_task_address = get_clip_task_address(
             &ctx.prim_store.clip_mask_instances,
             prim_instance.clip_task_index,
@@ -872,17 +872,17 @@ impl AlphaBatchBuilder {
                             let instance = SplitCompositeInstance::new(
                                 prim_header_index,
                                 child.gpu_address,
                                 z_id,
                             );
 
                             self.batch_list.push_single_instance(
                                 key,
-                                &prim_instance.clipped_world_rect.as_ref().expect("bug"),
+                                &prim_instance.bounding_rect.as_ref().expect("bug"),
                                 z_id,
                                 PrimitiveInstanceData::from(instance),
                             );
                         }
                     }
                     // Ignore the 3D pictures that are not in the root of preserve-3D
                     // hierarchy, since we process them with the root.
                     Picture3DContext::In { root_data: None, .. } => return,
@@ -1360,17 +1360,17 @@ impl AlphaBatchBuilder {
 
     fn add_image_tile_to_batch(
         &mut self,
         batch_kind: BrushBatchKind,
         blend_mode: BlendMode,
         textures: BatchTextures,
         prim_header_index: PrimitiveHeaderIndex,
         clip_task_address: RenderTaskAddress,
-        bounding_rect: &WorldRect,
+        bounding_rect: &PictureRect,
         edge_flags: EdgeAaSegmentMask,
         uv_rect_address: GpuCacheAddress,
         z_id: ZBufferId,
     ) {
         let base_instance = BrushInstance {
             prim_header_index,
             clip_task_address,
             segment_index: 0,
@@ -1396,17 +1396,17 @@ impl AlphaBatchBuilder {
     fn add_segment_to_batch(
         &mut self,
         segment: &BrushSegment,
         segment_data: &SegmentInstanceData,
         segment_index: i32,
         batch_kind: BrushBatchKind,
         prim_header_index: PrimitiveHeaderIndex,
         alpha_blend_mode: BlendMode,
-        bounding_rect: &WorldRect,
+        bounding_rect: &PictureRect,
         transform_kind: TransformedRectKind,
         render_tasks: &RenderTaskTree,
         z_id: ZBufferId,
         prim_opacity: PrimitiveOpacity,
         clip_task_index: ClipTaskIndex,
         ctx: &RenderTargetContext,
     ) {
         debug_assert!(clip_task_index != ClipTaskIndex::INVALID);
@@ -1456,17 +1456,17 @@ impl AlphaBatchBuilder {
     fn add_brush_to_batch(
         &mut self,
         brush: &BrushPrimitive,
         params: &BrushBatchParameters,
         alpha_blend_mode: BlendMode,
         non_segmented_blend_mode: BlendMode,
         prim_header_index: PrimitiveHeaderIndex,
         clip_task_address: RenderTaskAddress,
-        bounding_rect: &WorldRect,
+        bounding_rect: &PictureRect,
         transform_kind: TransformedRectKind,
         render_tasks: &RenderTaskTree,
         z_id: ZBufferId,
         clip_task_index: ClipTaskIndex,
         ctx: &RenderTargetContext,
     ) {
         match (&brush.segment_desc, &params.segment_data) {
             (Some(ref segment_desc), SegmentDataKind::Instanced(ref segment_data)) => {
@@ -1551,17 +1551,17 @@ impl AlphaBatchBuilder {
     }
 }
 
 fn add_gradient_tiles(
     visible_tiles: &[VisibleGradientTile],
     stops_handle: &GpuCacheHandle,
     kind: BrushBatchKind,
     blend_mode: BlendMode,
-    bounding_rect: &WorldRect,
+    bounding_rect: &PictureRect,
     clip_task_address: RenderTaskAddress,
     gpu_cache: &GpuCache,
     batch_list: &mut BatchList,
     base_prim_header: &PrimitiveHeader,
     prim_headers: &mut PrimitiveHeaders,
     z_id: ZBufferId,
 ) {
     let batch = batch_list.set_params_and_get_batch(
--- a/gfx/webrender/src/frame_builder.rs
+++ b/gfx/webrender/src/frame_builder.rs
@@ -7,17 +7,17 @@ use api::{DeviceUintPoint, DeviceUintRec
 use api::{LayoutPoint, LayoutRect, LayoutSize, PipelineId, RasterSpace, WorldPoint, WorldRect, WorldPixel};
 use clip::{ClipDataStore, ClipStore};
 use clip_scroll_tree::{ClipScrollTree, ROOT_SPATIAL_NODE_INDEX, SpatialNodeIndex};
 use display_list_flattener::{DisplayListFlattener};
 use gpu_cache::GpuCache;
 use gpu_types::{PrimitiveHeaders, TransformPalette, UvRectKind, ZBufferIdGenerator};
 use hit_test::{HitTester, HitTestingRun};
 use internal_types::{FastHashMap, PlaneSplitter};
-use picture::{PictureSurface, PictureUpdateContext, SurfaceInfo, ROOT_SURFACE_INDEX, SurfaceIndex};
+use picture::{PictureSurface, PictureUpdateState, SurfaceInfo, ROOT_SURFACE_INDEX, SurfaceIndex};
 use prim_store::{PrimitiveStore, SpaceMapper, PictureIndex, PrimitiveDebugId};
 use profiler::{FrameProfileCounters, GpuCacheProfileCounters, TextureCacheProfileCounters};
 use render_backend::{FrameResources, FrameId};
 use render_task::{RenderTask, RenderTaskId, RenderTaskLocation, RenderTaskTree};
 use resource_cache::{ResourceCache};
 use scene::{ScenePipeline, SceneProperties};
 use segment::SegmentBuilder;
 use spatial_node::SpatialNode;
@@ -218,46 +218,42 @@ impl FrameBuilder {
         let root_surface = SurfaceInfo::new(
             ROOT_SPATIAL_NODE_INDEX,
             ROOT_SPATIAL_NODE_INDEX,
             world_rect,
             clip_scroll_tree,
         );
         surfaces.push(root_surface);
 
-        let pic_update_context = PictureUpdateContext::new(
-            ROOT_SURFACE_INDEX,
-            ROOT_SPATIAL_NODE_INDEX,
-        );
+        let mut pic_update_state = PictureUpdateState::new(surfaces);
 
         // The first major pass of building a frame is to walk the picture
         // tree. This pass must be quick (it should never touch individual
         // primitives). For now, all we do here is determine which pictures
         // will create surfaces. In the future, this will be expanded to
         // set up render tasks, determine scaling of surfaces, and detect
         // which surfaces have valid cached surfaces that don't need to
         // be rendered this frame.
         self.prim_store.update_picture(
             self.root_pic_index,
-            &pic_update_context,
+            &mut pic_update_state,
             &frame_context,
-            surfaces,
         );
 
         let mut frame_state = FrameBuildingState {
             render_tasks,
             profile_counters,
             clip_store: &mut self.clip_store,
             resource_cache,
             gpu_cache,
             special_render_passes,
             transforms: transform_palette,
             resources,
             segment_builder: SegmentBuilder::new(),
-            surfaces,
+            surfaces: pic_update_state.surfaces,
         };
 
         let (pic_context, mut pic_state, mut prim_list) = self
             .prim_store
             .pictures[self.root_pic_index.0]
             .take_context(
                 self.root_pic_index,
                 root_spatial_node_index,
--- a/gfx/webrender/src/picture.rs
+++ b/gfx/webrender/src/picture.rs
@@ -31,37 +31,87 @@ use util::{TransformedRectKind, MatrixHe
 
  * 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).
  */
 
-/// A context structure passed from parent to child
-/// during the first frame building pass, when just
-/// pictures are traversed.
-pub struct PictureUpdateContext {
-    /// Index of the current surface as the picture
-    /// tree is traversed.
-    pub surface_index: SurfaceIndex,
-    /// Used for backface visibility calculations.
-    pub parent_spatial_node_index: SpatialNodeIndex,
+/// Information about a picture that is pushed / popped on the
+/// PictureUpdateState during picture traversal pass.
+struct PictureInfo {
+    /// The spatial node for this picture.
+    spatial_node_index: SpatialNodeIndex,
+}
+
+/// Maintains a stack of picture and surface information, that
+/// is used during the initial picture traversal.
+pub struct PictureUpdateState<'a> {
+    pub surfaces: &'a mut Vec<SurfaceInfo>,
+    surface_stack: Vec<SurfaceIndex>,
+    picture_stack: Vec<PictureInfo>,
 }
 
-impl PictureUpdateContext {
-    pub fn new(
-        surface_index: SurfaceIndex,
-        parent_spatial_node_index: SpatialNodeIndex,
-    ) -> Self {
-        PictureUpdateContext {
-            surface_index,
-            parent_spatial_node_index,
+impl<'a> PictureUpdateState<'a> {
+    pub fn new(surfaces: &'a mut Vec<SurfaceInfo>) -> Self {
+        PictureUpdateState {
+            surfaces,
+            surface_stack: vec![SurfaceIndex(0)],
+            picture_stack: Vec::new(),
         }
     }
+
+    /// Return the current surface
+    fn current_surface(&self) -> &SurfaceInfo {
+        &self.surfaces[self.surface_stack.last().unwrap().0]
+    }
+
+    /// Return the current surface (mutable)
+    fn current_surface_mut(&mut self) -> &mut SurfaceInfo {
+        &mut self.surfaces[self.surface_stack.last().unwrap().0]
+    }
+
+    /// Push a new surface onto the update stack.
+    fn push_surface(
+        &mut self,
+        surface: SurfaceInfo,
+    ) -> SurfaceIndex {
+        let surface_index = SurfaceIndex(self.surfaces.len());
+        self.surfaces.push(surface);
+        self.surface_stack.push(surface_index);
+        surface_index
+    }
+
+    /// Pop a surface on the way up the picture traversal
+    fn pop_surface(
+        &mut self,
+    ) {
+        self.surface_stack.pop().unwrap();
+    }
+
+    /// Return the current picture, or None if stack is empty.
+    fn current_picture(&self) -> Option<&PictureInfo> {
+        self.picture_stack.last()
+    }
+
+    /// Push information about a picture on the update stack
+    fn push_picture(
+        &mut self,
+        info: PictureInfo,
+    ) {
+        self.picture_stack.push(info);
+    }
+
+    /// Pop the picture info off, on the way up the picture traversal
+    fn pop_picture(
+        &mut self,
+    ) -> PictureInfo {
+        self.picture_stack.pop().unwrap()
+    }
 }
 
 #[derive(Debug, Copy, Clone)]
 pub struct SurfaceIndex(pub usize);
 
 pub const ROOT_SURFACE_INDEX: SurfaceIndex = SurfaceIndex(0);
 
 /// Information about an offscreen surface. For now,
@@ -236,26 +286,30 @@ impl PrimitiveCluster {
     }
 }
 
 #[derive(Debug, Copy, Clone)]
 pub struct PrimitiveClusterIndex(pub u32);
 
 pub type ClusterRange = ops::Range<u32>;
 
+/// A list of pictures, stored by the PrimitiveList to enable a
+/// fast traversal of just the pictures.
+pub type PictureList = SmallVec<[PictureIndex; 4]>;
+
 /// A list of primitive instances that are added to a picture
 /// This ensures we can keep a list of primitives that
 /// are pictures, for a fast initial traversal of the picture
 /// tree without walking the instance list.
 pub struct PrimitiveList {
     /// The primitive instances, in render order.
     pub prim_instances: Vec<PrimitiveInstance>,
     /// List of pictures that are part of this list.
     /// Used to implement the picture traversal pass.
-    pub pictures: SmallVec<[PictureIndex; 4]>,
+    pub pictures: PictureList,
     /// List of primitives grouped into clusters.
     pub clusters: SmallVec<[PrimitiveCluster; 4]>,
     /// This maps from the cluster_range in a primitive
     /// instance to a set of cluster(s) that the
     /// primitive instance belongs to.
     pub prim_cluster_map: Vec<PrimitiveClusterIndex>,
 }
 
@@ -646,16 +700,17 @@ impl PicturePrimitive {
     /// Add a primitive instance to the plane splitter. The function would generate
     /// an appropriate polygon, clip it against the frustum, and register with the
     /// given plane splitter.
     pub fn add_split_plane(
         splitter: &mut PlaneSplitter,
         transforms: &TransformPalette,
         prim_instance: &PrimitiveInstance,
         original_local_rect: LayoutRect,
+        world_rect: WorldRect,
         plane_split_anchor: usize,
     ) -> bool {
         let transform = transforms
             .get_world_transform(prim_instance.spatial_node_index);
         let matrix = transform.cast();
 
         // Apply the local clip rect here, before splitting. This is
         // because the local clip rect can't be applied in the vertex
@@ -664,16 +719,17 @@ impl PicturePrimitive {
         // since we determine the UVs by doing a bilerp with a factor
         // from the original local rect.
         let local_rect = match original_local_rect
             .intersection(&prim_instance.combined_local_clip_rect)
         {
             Some(rect) => rect.cast(),
             None => return false,
         };
+        let world_rect = world_rect.cast();
 
         match transform.transform_kind() {
             TransformedRectKind::AxisAligned => {
                 let inv_transform = transforms
                     .get_world_inv_transform(prim_instance.spatial_node_index);
                 let polygon = Polygon::from_transformed_rect_with_inverse(
                     local_rect,
                     &matrix,
@@ -685,17 +741,17 @@ impl PicturePrimitive {
             TransformedRectKind::Complex => {
                 let mut clipper = Clipper::new();
                 let results = clipper.clip_transformed(
                     Polygon::from_rect(
                         local_rect,
                         plane_split_anchor,
                     ),
                     &matrix,
-                    prim_instance.clipped_world_rect.map(|r| r.to_f64()),
+                    Some(world_rect),
                 );
                 if let Ok(results) = results {
                     for poly in results {
                         splitter.add(poly);
                     }
                 }
             }
         }
@@ -748,136 +804,124 @@ impl PicturePrimitive {
         }
     }
 
     /// Called during initial picture traversal, before we know the
     /// bounding rect of children. It is possible to determine the
     /// surface / raster config now though.
     pub fn pre_update(
         &mut self,
-        context: &PictureUpdateContext,
+        state: &mut PictureUpdateState,
         frame_context: &FrameBuildingContext,
-        surfaces: &mut Vec<SurfaceInfo>,
-    ) -> Option<(PictureUpdateContext, SmallVec<[PictureIndex; 4]>)> {
+    ) -> Option<PictureList> {
         // Reset raster config in case we early out below.
         self.raster_config = None;
 
         // Resolve animation properties, and early out if the filter
         // properties make this picture invisible.
         if !self.resolve_scene_properties(frame_context.scene_properties) {
             return None;
         }
 
+        // Push information about this pic on stack for children to read.
+        state.push_picture(PictureInfo {
+            spatial_node_index: self.spatial_node_index,
+        });
+
         // See if this picture actually needs a surface for compositing.
         let actual_composite_mode = match self.requested_composite_mode {
             Some(PictureCompositeMode::Filter(filter)) if filter.is_noop() => None,
             mode => mode,
         };
 
-        let surface_index = match actual_composite_mode {
-            Some(composite_mode) => {
-                // Retrieve the positioning node information for the parent surface.
-                let parent_raster_spatial_node_index = surfaces[context.surface_index.0].raster_spatial_node_index;
-                let surface_spatial_node_index = self.spatial_node_index;
+        if let Some(composite_mode) = actual_composite_mode {
+            // Retrieve the positioning node information for the parent surface.
+            let parent_raster_spatial_node_index = state.current_surface().raster_spatial_node_index;
+            let surface_spatial_node_index = self.spatial_node_index;
 
-                // Check if there is perspective, and thus whether a new
-                // rasterization root should be established.
-                let xf = frame_context.clip_scroll_tree.get_relative_transform(
-                    parent_raster_spatial_node_index,
-                    surface_spatial_node_index,
-                ).expect("BUG: unable to get relative transform");
+            // Check if there is perspective, and thus whether a new
+            // rasterization root should be established.
+            let xf = frame_context.clip_scroll_tree.get_relative_transform(
+                parent_raster_spatial_node_index,
+                surface_spatial_node_index,
+            ).expect("BUG: unable to get relative transform");
 
-                // TODO(gw): A temporary hack here to revert behavior to
-                //           always raster in screen-space. This is not
-                //           a problem yet, since we're not taking advantage
-                //           of this for caching yet. This is a workaround
-                //           for some existing issues with handling scale
-                //           when rasterizing in local space mode. Once
-                //           the fixes for those are in-place, we can
-                //           remove this hack!
-                //let local_scale = raster_space.local_scale();
-                // let wants_raster_root = xf.has_perspective_component() ||
-                //                         local_scale.is_some();
-                let establishes_raster_root = xf.has_perspective_component();
+            // TODO(gw): A temporary hack here to revert behavior to
+            //           always raster in screen-space. This is not
+            //           a problem yet, since we're not taking advantage
+            //           of this for caching yet. This is a workaround
+            //           for some existing issues with handling scale
+            //           when rasterizing in local space mode. Once
+            //           the fixes for those are in-place, we can
+            //           remove this hack!
+            //let local_scale = raster_space.local_scale();
+            // let wants_raster_root = xf.has_perspective_component() ||
+            //                         local_scale.is_some();
+            let establishes_raster_root = xf.has_perspective_component();
 
-                // TODO(gw): For now, we always raster in screen space. Soon,
-                //           we will be able to respect the requested raster
-                //           space, and/or override the requested raster root
-                //           if it makes sense to.
-                let raster_space = RasterSpace::Screen;
+            // TODO(gw): For now, we always raster in screen space. Soon,
+            //           we will be able to respect the requested raster
+            //           space, and/or override the requested raster root
+            //           if it makes sense to.
+            let raster_space = RasterSpace::Screen;
 
-                let raster_spatial_node_index = if establishes_raster_root {
-                    surface_spatial_node_index
-                } else {
-                    parent_raster_spatial_node_index
-                };
+            let raster_spatial_node_index = if establishes_raster_root {
+                surface_spatial_node_index
+            } else {
+                parent_raster_spatial_node_index
+            };
 
-                let surface_index = SurfaceIndex(surfaces.len());
-                let surface = SurfaceInfo::new(
+            let surface_index = state.push_surface(
+                SurfaceInfo::new(
                     surface_spatial_node_index,
                     raster_spatial_node_index,
                     frame_context.world_rect,
                     &frame_context.clip_scroll_tree,
-                );
-                surfaces.push(surface);
+                )
+            );
 
-                self.raster_config = Some(RasterConfig {
-                    composite_mode,
-                    surface_index,
-                });
+            self.raster_config = Some(RasterConfig {
+                composite_mode,
+                surface_index,
+            });
 
-                // If we have a cache key / descriptor for this surface,
-                // update any transforms it cares about.
-                if let Some(ref mut surface_desc) = self.surface_desc {
-                    surface_desc.update(
-                        surface_spatial_node_index,
-                        raster_spatial_node_index,
-                        frame_context.clip_scroll_tree,
-                        raster_space,
-                    );
-                }
+            // If we have a cache key / descriptor for this surface,
+            // update any transforms it cares about.
+            if let Some(ref mut surface_desc) = self.surface_desc {
+                surface_desc.update(
+                    surface_spatial_node_index,
+                    raster_spatial_node_index,
+                    frame_context.clip_scroll_tree,
+                    raster_space,
+                );
+            }
+        }
 
-                surface_index
-            }
-            None => {
-                context.surface_index
-            }
-        };
-
-        let child_context = PictureUpdateContext::new(
-            surface_index,
-            self.spatial_node_index,
-        );
-
-        Some((child_context, mem::replace(&mut self.prim_list.pictures, SmallVec::new())))
+        Some(mem::replace(&mut self.prim_list.pictures, SmallVec::new()))
     }
 
     /// Called after updating child pictures during the initial
     /// picture traversal.
     pub fn post_update(
         &mut self,
-        parent_context: &PictureUpdateContext,
-        this_context: &PictureUpdateContext,
-        child_pictures: SmallVec<[PictureIndex; 4]>,
-        surfaces: &mut [SurfaceInfo],
+        child_pictures: PictureList,
+        state: &mut PictureUpdateState,
         frame_context: &FrameBuildingContext,
     ) {
-        // Check visibility of each cluster, and then use
-        // the cluster bounding rect to calculate the
-        // surface bounding rect.
+        // Pop the state information about this picture.
+        state.pop_picture();
 
         for cluster in &mut self.prim_list.clusters {
-            // Reset visibility
-            cluster.is_visible = false;
-
             // Skip the cluster if backface culled.
             if !cluster.is_backface_visible {
                 let containing_block_index = match self.context_3d {
                     Picture3DContext::Out => {
-                        parent_context.parent_spatial_node_index
+                        state.current_picture().map_or(ROOT_SPATIAL_NODE_INDEX, |info| {
+                            info.spatial_node_index
+                        })
                     }
                     Picture3DContext::In { root_data: Some(_), ancestor_index } => {
                         ancestor_index
                     }
                     Picture3DContext::In { root_data: None, ancestor_index } => {
                         ancestor_index
                     }
                 };
@@ -900,17 +944,17 @@ impl PicturePrimitive {
                 .clip_scroll_tree
                 .spatial_nodes[cluster.spatial_node_index.0];
             if !spatial_node.invertible {
                 continue;
             }
 
             // Map the cluster bounding rect into the space of the surface, and
             // include it in the surface bounding rect.
-            let surface = &mut surfaces[this_context.surface_index.0];
+            let surface = state.current_surface_mut();
             surface.map_local_to_surface.set_target_spatial_node(
                 cluster.spatial_node_index,
                 frame_context.clip_scroll_tree,
             );
 
             // Mark the cluster visible, since it passed the invertible and
             // backface checks. In future, this will include spatial clustering
             // which will allow the frame building code to skip most of the
@@ -927,49 +971,53 @@ impl PicturePrimitive {
             Some(RasterConfig { composite_mode: PictureCompositeMode::Filter(FilterOp::DropShadow(_, blur_radius, _)), .. }) => {
                 Some((blur_radius * BLUR_SAMPLE_SCALE).ceil())
             }
             _ => {
                 None
             }
         };
         if let Some(inflation_size) = inflation_size {
-            let surface = &mut surfaces[this_context.surface_index.0];
+            let surface = state.current_surface_mut();
             surface.rect = surface.rect.inflate(inflation_size, inflation_size);
         }
 
+        // Restore the pictures list used during recursion.
+        self.prim_list.pictures = child_pictures;
+
         // If this picture establishes a surface, then map the surface bounding
         // rect into the parent surface coordinate space, and propagate that up
         // to the parent.
-        if let Some(ref raster_config) = self.raster_config {
-            let surface_rect = surfaces[raster_config.surface_index.0].rect;
+        if self.raster_config.is_some() {
+            let surface_rect = state.current_surface().rect;
             let surface_rect = TypedRect::from_untyped(&surface_rect.to_untyped());
 
+            // Pop this surface from the stack
+            state.pop_surface();
+
             // Propagate up to parent surface, now that we know this surface's static rect
-            let parent_surface = &mut surfaces[parent_context.surface_index.0];
+            let parent_surface = state.current_surface_mut();
             parent_surface.map_local_to_surface.set_target_spatial_node(
                 self.spatial_node_index,
                 frame_context.clip_scroll_tree,
             );
             if let Some(parent_surface_rect) = parent_surface
                 .map_local_to_surface
                 .map(&surface_rect) {
                 parent_surface.rect = parent_surface.rect.union(&parent_surface_rect);
             }
         }
-
-        // Restore the pictures list used during recursion.
-        self.prim_list.pictures = child_pictures;
     }
 
     pub fn prepare_for_render(
         &mut self,
         pic_index: PictureIndex,
         prim_instance: &PrimitiveInstance,
         prim_local_rect: &LayoutRect,
+        clipped_prim_bounding_rect: WorldRect,
         surface_index: SurfaceIndex,
         frame_context: &FrameBuildingContext,
         frame_state: &mut FrameBuildingState,
     ) -> bool {
         let (mut pic_state_for_children, pic_context) = self.take_state_and_context();
 
         if let Some(ref mut splitter) = pic_state_for_children.plane_splitter {
             self.resolve_split_planes(
@@ -995,26 +1043,27 @@ impl PicturePrimitive {
         let (map_raster_to_world, map_pic_to_raster) = create_raster_mappers(
             prim_instance.spatial_node_index,
             raster_spatial_node_index,
             frame_context,
         );
 
         let pic_rect = PictureRect::from_untyped(&prim_local_rect.to_untyped());
 
-        let (clipped, unclipped, transform) = match get_raster_rects(
+        let (clipped, unclipped) = match get_raster_rects(
             pic_rect,
             &map_pic_to_raster,
             &map_raster_to_world,
-            prim_instance.clipped_world_rect.expect("bug1"),
+            clipped_prim_bounding_rect,
             frame_context.device_pixel_scale,
         ) {
             Some(info) => info,
             None => return false,
         };
+        let transform = map_pic_to_raster.get_transform();
 
         // TODO(gw): Almost all of the Picture types below use extra_gpu_cache_data
         //           to store the same type of data. The exception is the filter
         //           with a ColorMatrix, which stores the color matrix here. It's
         //           probably worth tidying this code up to be a bit more consistent.
         //           Perhaps store the color matrix after the common data, even though
         //           it's not used by that shader.
 
--- a/gfx/webrender/src/prim_store.rs
+++ b/gfx/webrender/src/prim_store.rs
@@ -1,14 +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/. */
 
 use api::{AlphaType, BorderRadius, BuiltDisplayList, ClipMode, ColorF, PictureRect, ColorU, LayoutPrimitiveInfo};
-use api::{DeviceIntRect, DeviceIntSize, DevicePixelScale, ExtendMode, DeviceRect, PictureToRasterTransform};
+use api::{DeviceIntRect, DeviceIntSize, DevicePixelScale, ExtendMode, DeviceRect};
 use api::{FilterOp, GlyphInstance, GradientStop, ImageKey, ImageRendering, ItemRange, TileOffset};
 use api::{RasterSpace, LayoutPoint, LayoutRect, LayoutSideOffsets, LayoutSize, LayoutToWorldTransform};
 use api::{LayoutVector2D, PremultipliedColorF, PropertyBinding, Shadow, YuvColorSpace, YuvFormat, LayoutRectAu};
 use api::{DeviceIntSideOffsets, WorldPixel, BoxShadowClipMode, NormalBorder, WorldRect, LayoutToWorldScale};
 use api::{PicturePixel, RasterPixel, ColorDepth, LineStyle, LineOrientation, LayoutSizeAu, AuHelpers, LayoutVector2DAu};
 use app_units::Au;
 use border::{get_max_scale_for_border, build_border_instances, create_normal_border_prim};
 use clip_scroll_tree::{ClipScrollTree, SpatialNodeIndex};
@@ -16,18 +16,18 @@ use clip::{ClipNodeFlags, ClipChainId, C
 use euclid::{TypedTransform3D, TypedRect, TypedScale};
 use frame_builder::{FrameBuildingContext, FrameBuildingState, PictureContext, PictureState};
 use frame_builder::PrimitiveContext;
 use glyph_rasterizer::{FontInstance, FontTransform, GlyphKey, FONT_SIZE_LIMIT};
 use gpu_cache::{GpuCache, GpuCacheAddress, GpuCacheHandle, GpuDataRequest, ToGpuBlocks};
 use gpu_types::BrushFlags;
 use image::{self, Repetition};
 use intern;
-use picture::{ClusterRange, PictureCompositeMode, PicturePrimitive, PictureUpdateContext};
-use picture::{PrimitiveList, SurfaceInfo, SurfaceIndex};
+use picture::{PictureCompositeMode, PicturePrimitive, PictureUpdateState};
+use picture::{ClusterRange, PrimitiveList, SurfaceIndex};
 #[cfg(debug_assertions)]
 use render_backend::FrameId;
 use render_task::{BlitSource, RenderTask, RenderTaskCacheKey, RenderTaskTree, to_cache_size};
 use render_task::{RenderTaskCacheKeyKind, RenderTaskId, RenderTaskCacheEntryHandle};
 use renderer::{MAX_VERTEX_TEXTURE_WIDTH};
 use resource_cache::{ImageProperties, ImageRequest, ResourceCache};
 use scene::SceneProperties;
 use std::{cmp, fmt, mem, ops, u32, usize};
@@ -881,17 +881,17 @@ impl BrushSegment {
     ) -> ClipMaskKind {
         match clip_chain {
             Some(clip_chain) => {
                 if !clip_chain.needs_mask ||
                    (!self.may_need_clip_mask && !clip_chain.has_non_local_clips) {
                     return ClipMaskKind::None;
                 }
 
-                let (device_rect, _, _) = match get_raster_rects(
+                let (device_rect, _) = match get_raster_rects(
                     clip_chain.pic_clip_rect,
                     &pic_state.map_pic_to_raster,
                     &pic_state.map_raster_to_world,
                     prim_bounding_rect,
                     frame_context.device_pixel_scale,
                 ) {
                     Some(info) => info,
                     None => {
@@ -1859,20 +1859,20 @@ pub struct PrimitiveInstance {
     #[cfg(debug_assertions)]
     pub id: PrimitiveDebugId,
 
     /// The last frame ID (of the `RenderTaskTree`) this primitive
     /// was prepared for rendering in.
     #[cfg(debug_assertions)]
     pub prepared_frame_id: FrameId,
 
-    /// The current world rect of this primitive, clipped to the
-    /// world rect of the screen. None means the primitive is
-    /// completely off-screen.
-    pub clipped_world_rect: Option<WorldRect>,
+    /// The current minimal bounding rect of this primitive in picture space.
+    /// Includes the primitive rect, and any clipping rects from the same
+    /// coordinate system.
+    pub bounding_rect: Option<PictureRect>,
 
     /// An index into the clip task instances array in the primitive
     /// store. If this is ClipTaskIndex::INVALID, then the primitive
     /// has no clip mask. Otherwise, it may store the offset of the
     /// global clip mask task for this primitive, or the first of
     /// a list of clip task ids (one per segment).
     pub clip_task_index: ClipTaskIndex,
 
@@ -1892,17 +1892,17 @@ impl PrimitiveInstance {
         prim_data_handle: PrimitiveDataHandle,
         clip_chain_id: ClipChainId,
         spatial_node_index: SpatialNodeIndex,
     ) -> Self {
         PrimitiveInstance {
             kind,
             prim_data_handle,
             combined_local_clip_rect: LayoutRect::zero(),
-            clipped_world_rect: None,
+            bounding_rect: None,
             #[cfg(debug_assertions)]
             prepared_frame_id: FrameId::INVALID,
             #[cfg(debug_assertions)]
             id: PrimitiveDebugId(NEXT_PRIM_ID.fetch_add(1, Ordering::Relaxed)),
             clip_task_index: ClipTaskIndex::INVALID,
             clip_chain_id,
             spatial_node_index,
             cluster_range: ClusterRange { start: 0, end: 0 },
@@ -1961,39 +1961,34 @@ impl PrimitiveStore {
     }
 
     /// Update a picture, determining surface configuration,
     /// rasterization roots, and (in future) whether there
     /// are cached surfaces that can be used by this picture.
     pub fn update_picture(
         &mut self,
         pic_index: PictureIndex,
-        context: &PictureUpdateContext,
+        state: &mut PictureUpdateState,
         frame_context: &FrameBuildingContext,
-        surfaces: &mut Vec<SurfaceInfo>,
     ) {
-        if let Some((child_context, children)) = self.pictures[pic_index.0].pre_update(
-            context,
+        if let Some(children) = self.pictures[pic_index.0].pre_update(
+            state,
             frame_context,
-            surfaces,
         ) {
             for child_pic_index in &children {
                 self.update_picture(
                     *child_pic_index,
-                    &child_context,
+                    state,
                     frame_context,
-                    surfaces,
                 );
             }
 
             self.pictures[pic_index.0].post_update(
-                context,
-                &child_context,
                 children,
-                surfaces,
+                state,
                 frame_context,
             );
         }
     }
 
     pub fn add_primitive(
         &mut self,
         local_rect: &LayoutRect,
@@ -2230,18 +2225,26 @@ impl PrimitiveStore {
                 (prim_data.prim_rect, prim_data.clip_rect)
             }
             PrimitiveInstanceKind::LegacyPrimitive { prim_index } => {
                 let prim = &self.primitives[prim_index.0];
                 (prim.local_rect, prim.local_clip_rect)
             }
         };
 
+        // TODO(gw): Having this declared outside is a hack / workaround. We
+        //           need it in pic.prepare_for_render below, but that code
+        //           path will only read it in the !is_passthrough case
+        //           below. This should be much tidier once we port this
+        //           traversal to work with a state stack like the initial
+        //           picture traversal now works.
+        let mut clipped_world_rect = WorldRect::zero();
+
         if is_passthrough {
-            prim_instance.clipped_world_rect = Some(pic_state.map_pic_to_world.bounds);
+            prim_instance.bounding_rect = Some(pic_state.map_local_to_pic.bounds);
         } else {
             if prim_local_rect.size.width <= 0.0 ||
                prim_local_rect.size.height <= 0.0
             {
                 if prim_instance.is_chased() {
                     println!("\tculled for zero local rectangle");
                 }
                 return false;
@@ -2284,17 +2287,17 @@ impl PrimitiveStore {
                 );
 
             let clip_chain = match clip_chain {
                 Some(clip_chain) => clip_chain,
                 None => {
                     if prim_instance.is_chased() {
                         println!("\tunable to build the clip chain, skipping");
                     }
-                    prim_instance.clipped_world_rect = None;
+                    prim_instance.bounding_rect = None;
                     return false;
                 }
             };
 
             if prim_instance.is_chased() {
                 println!("\teffective clip chain from {:?} {}",
                     clip_chain.clips_range,
                     if pic_context.apply_local_clip_rect { "(applied)" } else { "" },
@@ -2314,24 +2317,24 @@ impl PrimitiveStore {
                 .map(&clip_chain.pic_clip_rect)
             {
                 Some(world_rect) => world_rect,
                 None => {
                     return false;
                 }
             };
 
-            let clipped_world_rect = match world_rect.intersection(&frame_context.world_rect) {
+            clipped_world_rect = match world_rect.intersection(&frame_context.world_rect) {
                 Some(rect) => rect,
                 None => {
                     return false;
                 }
             };
 
-            prim_instance.clipped_world_rect = Some(clipped_world_rect);
+            prim_instance.bounding_rect = Some(clip_chain.pic_clip_rect);
 
             prim_instance.update_clip_task(
                 prim_local_rect,
                 prim_local_clip_rect,
                 prim_context,
                 clipped_world_rect,
                 pic_context.raster_spatial_node_index,
                 &clip_chain,
@@ -2356,31 +2359,33 @@ impl PrimitiveStore {
 
         match prim_instance.kind {
             PrimitiveInstanceKind::Picture { pic_index } => {
                 let pic = &mut self.pictures[pic_index.0];
                 if pic.prepare_for_render(
                     pic_index,
                     prim_instance,
                     &prim_local_rect,
+                    clipped_world_rect,
                     pic_context.surface_index,
                     frame_context,
                     frame_state,
                 ) {
                     if let Some(ref mut splitter) = pic_state.plane_splitter {
                         PicturePrimitive::add_split_plane(
                             splitter,
                             frame_state.transforms,
                             prim_instance,
                             prim_local_rect,
+                            frame_context.world_rect,
                             plane_split_anchor,
                         );
                     }
                 } else {
-                    prim_instance.clipped_world_rect = None;
+                    prim_instance.bounding_rect = None;
                 }
 
                 if let Some(mut request) = frame_state.gpu_cache.request(&mut pic.gpu_location) {
                     request.push(PremultipliedColorF::WHITE);
                     request.push(PremultipliedColorF::WHITE);
                     request.push([
                         pic.local_rect.size.width,
                         pic.local_rect.size.height,
@@ -2433,17 +2438,17 @@ impl PrimitiveStore {
     ) {
         let display_list = &frame_context
             .pipelines
             .get(&pic_context.pipeline_id)
             .expect("No display list?")
             .display_list;
 
         for (plane_split_anchor, prim_instance) in prim_list.prim_instances.iter_mut().enumerate() {
-            prim_instance.clipped_world_rect = None;
+            prim_instance.bounding_rect = None;
 
             if prim_instance.is_chased() {
                 #[cfg(debug_assertions)]
                 println!("\tpreparing {:?} in {:?}",
                     prim_instance.id, pic_context.pipeline_id);
             }
 
             // Run through the list of cluster(s) this primitive belongs
@@ -2538,34 +2543,31 @@ fn build_gradient_stops_request(
 fn decompose_repeated_primitive(
     visible_tiles: &mut Vec<VisibleGradientTile>,
     instance: &mut PrimitiveInstance,
     prim_local_rect: &LayoutRect,
     stretch_size: &LayoutSize,
     tile_spacing: &LayoutSize,
     prim_context: &PrimitiveContext,
     frame_state: &mut FrameBuildingState,
+    world_rect: &WorldRect,
     callback: &mut FnMut(&LayoutRect, GpuDataRequest),
 ) {
     visible_tiles.clear();
 
     // Tighten the clip rect because decomposing the repeated image can
     // produce primitives that are partially covering the original image
     // rect and we want to clip these extra parts out.
     let tight_clip_rect = instance
         .combined_local_clip_rect
         .intersection(prim_local_rect).unwrap();
 
-    let clipped_world_rect = &instance
-        .clipped_world_rect
-        .unwrap();
-
     let visible_rect = compute_conservative_visible_rect(
         prim_context,
-        clipped_world_rect,
+        world_rect,
         &tight_clip_rect
     );
     let stride = *stretch_size + *tile_spacing;
 
     let repetitions = image::repetitions(prim_local_rect, &visible_rect, stride);
     for Repetition { origin, .. } in repetitions {
         let mut handle = GpuCacheHandle::new();
         let rect = LayoutRect {
@@ -2585,29 +2587,29 @@ fn decompose_repeated_primitive(
     }
 
     if visible_tiles.is_empty() {
         // At this point if we don't have tiles to show it means we could probably
         // have done a better a job at culling during an earlier stage.
         // Clearing the screen rect has the effect of "culling out" the primitive
         // from the point of view of the batch builder, and ensures we don't hit
         // assertions later on because we didn't request any image.
-        instance.clipped_world_rect = None;
+        instance.bounding_rect = None;
     }
 }
 
 fn compute_conservative_visible_rect(
     prim_context: &PrimitiveContext,
-    clipped_world_rect: &WorldRect,
+    world_rect: &WorldRect,
     local_clip_rect: &LayoutRect,
 ) -> LayoutRect {
     if let Some(layer_screen_rect) = prim_context
         .spatial_node
         .world_content_transform
-        .unapply(clipped_world_rect) {
+        .unapply(world_rect) {
 
         return local_clip_rect.intersection(&layer_screen_rect).unwrap_or(LayoutRect::zero());
     }
 
     *local_clip_rect
 }
 
 fn edge_flags_for_tile_spacing(tile_spacing: &LayoutSize) -> EdgeAaSegmentMask {
@@ -3169,17 +3171,17 @@ impl PrimitiveInstance {
                                 // produce primitives that are partially covering the original image
                                 // rect and we want to clip these extra parts out.
                                 let tight_clip_rect = self
                                     .combined_local_clip_rect
                                     .intersection(&prim_local_rect).unwrap();
 
                                 let visible_rect = compute_conservative_visible_rect(
                                     prim_context,
-                                    &self.clipped_world_rect.unwrap(),
+                                    &frame_context.world_rect,
                                     &tight_clip_rect
                                 );
 
                                 let base_edge_flags = edge_flags_for_tile_spacing(tile_spacing);
 
                                 let stride = stretch_size + *tile_spacing;
 
                                 visible_tiles.clear();
@@ -3230,17 +3232,17 @@ impl PrimitiveInstance {
                                 }
 
                                 if visible_tiles.is_empty() {
                                     // At this point if we don't have tiles to show it means we could probably
                                     // have done a better a job at culling during an earlier stage.
                                     // Clearing the screen rect has the effect of "culling out" the primitive
                                     // from the point of view of the batch builder, and ensures we don't hit
                                     // assertions later on because we didn't request any image.
-                                    self.clipped_world_rect = None;
+                                    self.bounding_rect = None;
                                 }
                             } else if request_source_image {
                                 frame_state.resource_cache.request_image(
                                     request,
                                     frame_state.gpu_cache,
                                 );
                             }
 
@@ -3363,16 +3365,17 @@ impl PrimitiveInstance {
                             decompose_repeated_primitive(
                                 visible_tiles,
                                 self,
                                 &prim_local_rect,
                                 &stretch_size,
                                 &tile_spacing,
                                 prim_context,
                                 frame_state,
+                                &frame_context.world_rect,
                                 &mut |rect, mut request| {
                                     request.push([
                                         center.x,
                                         center.y,
                                         start_radius,
                                         end_radius,
                                     ]);
                                     request.push([
@@ -3416,16 +3419,17 @@ impl PrimitiveInstance {
                             decompose_repeated_primitive(
                                 visible_tiles,
                                 self,
                                 &prim_local_rect,
                                 &stretch_size,
                                 &tile_spacing,
                                 prim_context,
                                 frame_state,
+                                &frame_context.world_rect,
                                 &mut |rect, mut request| {
                                     request.push([
                                         start_point.x,
                                         start_point.y,
                                         end_point.x,
                                         end_point.y,
                                     ]);
                                     request.push([
@@ -3517,17 +3521,17 @@ impl PrimitiveInstance {
         ) {
             if self.is_chased() {
                 println!("\tsegment tasks have been created for clipping");
             }
             return;
         }
 
         if clip_chain.needs_mask {
-            if let Some((device_rect, _, _)) = get_raster_rects(
+            if let Some((device_rect, _)) = get_raster_rects(
                 clip_chain.pic_clip_rect,
                 &pic_state.map_pic_to_raster,
                 &pic_state.map_raster_to_world,
                 prim_bounding_rect,
                 frame_context.device_pixel_scale,
             ) {
                 let clip_task = RenderTask::new_mask(
                     device_rect,
@@ -3556,17 +3560,17 @@ impl PrimitiveInstance {
 }
 
 pub fn get_raster_rects(
     pic_rect: PictureRect,
     map_to_raster: &SpaceMapper<PicturePixel, RasterPixel>,
     map_to_world: &SpaceMapper<RasterPixel, WorldPixel>,
     prim_bounding_rect: WorldRect,
     device_pixel_scale: DevicePixelScale,
-) -> Option<(DeviceIntRect, DeviceRect, PictureToRasterTransform)> {
+) -> Option<(DeviceIntRect, DeviceRect)> {
     let unclipped_raster_rect = map_to_raster.map(&pic_rect)?;
 
     let unclipped = raster_rect_to_device_pixels(
         unclipped_raster_rect,
         device_pixel_scale,
     );
 
     let unclipped_world_rect = map_to_world.map(&unclipped_raster_rect)?;
@@ -3577,19 +3581,17 @@ pub fn get_raster_rects(
 
     let clipped_raster_rect = clipped_raster_rect.intersection(&unclipped_raster_rect)?;
 
     let clipped = raster_rect_to_device_pixels(
         clipped_raster_rect,
         device_pixel_scale,
     );
 
-    let transform = map_to_raster.get_transform();
-
-    Some((clipped.to_i32(), unclipped, transform))
+    Some((clipped.to_i32(), unclipped))
 }
 
 /// Get the inline (horizontal) and block (vertical) sizes
 /// for a given line decoration.
 fn get_line_decoration_sizes(
     rect_size: &LayoutSize,
     orientation: LineOrientation,
     style: LineStyle,
--- a/gfx/webrender_bindings/revision.txt
+++ b/gfx/webrender_bindings/revision.txt
@@ -1,2 +1,2 @@
-02387f7e5c77c415cfa232366c321d6f621cae28
+9ec08a4cf0ce6762a98ddd4df34611dbf0694703
 plus servo/webrender#3316 cherry-picked