Backed out 3 changesets (bug 1540200) for causing merge conflicts
authorCoroiu Cristina <ccoroiu@mozilla.com>
Mon, 06 May 2019 00:37:06 +0300
changeset 531417 647a90020b45dc8cdc45370bf3a91df815084b8c
parent 531416 64b5ab45b68ba0b879a4866027c8c0e6e5c1a9ee
child 531418 1e3244e602fc0373508049b6fe544332f800f033
push id11265
push userffxbld-merge
push dateMon, 13 May 2019 10:53:39 +0000
treeherdermozilla-beta@77e0fe8dbdd3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1540200
milestone68.0a1
backs out4352d1f0da341b26f79de16ca5b966ba17caa5da
ff2ca836d8a7710ff5a3f7735c7767d90095c1c7
3e4cc0d312d0d4c953484a964a0ef8ecb3201601
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
Backed out 3 changesets (bug 1540200) for causing merge conflicts Backed out changeset 4352d1f0da34 (bug 1540200) Backed out changeset ff2ca836d8a7 (bug 1540200) Backed out changeset 3e4cc0d312d0 (bug 1540200)
gfx/wr/webrender/res/brush.glsl
gfx/wr/webrender/res/prim_shared.glsl
gfx/wr/webrender/res/snap.glsl
gfx/wr/webrender/src/batch.rs
gfx/wr/webrender/src/gpu_types.rs
gfx/wr/webrender/src/picture.rs
gfx/wr/webrender/src/prim_store/mod.rs
gfx/wr/wrench/reftests/filters/filter-drop-shadow-clip.png
gfx/wr/wrench/reftests/transforms/nested-preserve-3d.png
gfx/wr/wrench/reftests/transforms/nested-rotate-x.png
testing/web-platform/meta/css/compositing/mix-blend-mode/mix-blend-mode-both-parent-and-blended-with-3D-transform.html.ini
--- a/gfx/wr/webrender/res/brush.glsl
+++ b/gfx/wr/webrender/res/brush.glsl
@@ -19,16 +19,17 @@ void brush_vs(
 
 #define VECS_PER_SEGMENT                    2
 
 #define BRUSH_FLAG_PERSPECTIVE_INTERPOLATION    1
 #define BRUSH_FLAG_SEGMENT_RELATIVE             2
 #define BRUSH_FLAG_SEGMENT_REPEAT_X             4
 #define BRUSH_FLAG_SEGMENT_REPEAT_Y             8
 #define BRUSH_FLAG_TEXEL_RECT                  16
+#define BRUSH_FLAG_SNAP_TO_PRIMITIVE           32
 
 #define INVALID_SEGMENT_INDEX                   0xffff
 
 void main(void) {
     // Load the brush instance from vertex attributes.
     int prim_header_address = aData.x;
     int clip_address = aData.y;
     int segment_index = aData.z & 0xffff;
@@ -59,24 +60,25 @@ void main(void) {
     // Fetch the dynamic picture that we are drawing on.
     PictureTask pic_task = fetch_picture_task(ph.render_task_index);
     ClipArea clip_area = fetch_clip_area(clip_address);
 
     Transform transform = fetch_transform(ph.transform_id);
 
     // Write the normal vertex information out.
     if (transform.is_axis_aligned) {
+        bool snap_to_primitive = (brush_flags & BRUSH_FLAG_SNAP_TO_PRIMITIVE) != 0;
         vi = write_vertex(
             segment_rect,
             ph.local_clip_rect,
             ph.z,
             transform,
             pic_task,
             ph.local_rect,
-            ph.snap_offsets
+            snap_to_primitive
         );
 
         // TODO(gw): transform bounds may be referenced by
         //           the fragment shader when running in
         //           the alpha pass, even on non-transformed
         //           items. For now, just ensure it has no
         //           effect. We can tidy this up as we move
         //           more items to be brush shaders.
--- a/gfx/wr/webrender/res/prim_shared.glsl
+++ b/gfx/wr/webrender/res/prim_shared.glsl
@@ -42,37 +42,35 @@ varying vec4 vClipMaskUv;
 #define COLOR_MODE_IMAGE              9
 
 uniform HIGHP_SAMPLER_FLOAT sampler2D sPrimitiveHeadersF;
 uniform HIGHP_SAMPLER_FLOAT isampler2D sPrimitiveHeadersI;
 
 // Instanced attributes
 in ivec4 aData;
 
-#define VECS_PER_PRIM_HEADER_F 3U
+#define VECS_PER_PRIM_HEADER_F 2U
 #define VECS_PER_PRIM_HEADER_I 2U
 
 struct PrimitiveHeader {
     RectWithSize local_rect;
     RectWithSize local_clip_rect;
-    vec4 snap_offsets;
     float z;
     int specific_prim_address;
     int render_task_index;
     int transform_id;
     ivec4 user_data;
 };
 
 PrimitiveHeader fetch_prim_header(int index) {
     PrimitiveHeader ph;
 
     ivec2 uv_f = get_fetch_uv(index, VECS_PER_PRIM_HEADER_F);
     vec4 local_rect = TEXEL_FETCH(sPrimitiveHeadersF, uv_f, 0, ivec2(0, 0));
     vec4 local_clip_rect = TEXEL_FETCH(sPrimitiveHeadersF, uv_f, 0, ivec2(1, 0));
-    ph.snap_offsets = TEXEL_FETCH(sPrimitiveHeadersF, uv_f, 0, ivec2(2, 0));
     ph.local_rect = RectWithSize(local_rect.xy, local_rect.zw);
     ph.local_clip_rect = RectWithSize(local_clip_rect.xy, local_clip_rect.zw);
 
     ivec2 uv_i = get_fetch_uv(index, VECS_PER_PRIM_HEADER_I);
     ivec4 data0 = TEXEL_FETCH(sPrimitiveHeadersI, uv_i, 0, ivec2(0, 0));
     ivec4 data1 = TEXEL_FETCH(sPrimitiveHeadersI, uv_i, 0, ivec2(1, 0));
     ph.z = float(data0.x);
     ph.render_task_index = data0.y;
@@ -90,29 +88,41 @@ struct VertexInfo {
 };
 
 VertexInfo write_vertex(RectWithSize instance_rect,
                         RectWithSize local_clip_rect,
                         float z,
                         Transform transform,
                         PictureTask task,
                         RectWithSize snap_rect,
-                        vec4 snap_offsets) {
+                        bool snap_to_primitive) {
 
     // Select the corner of the local rect that we are processing.
     vec2 local_pos = instance_rect.p0 + instance_rect.size * aPosition.xy;
 
     // Clamp to the two local clip rects.
     vec2 clamped_local_pos = clamp_rect(local_pos, local_clip_rect);
 
+    RectWithSize visible_rect;
+    if (snap_to_primitive) {
+        // We are producing a picture. The snap rect has already taken into
+        // account the clipping we wish to consider for snapping purposes.
+        visible_rect = snap_rect;
+    } else {
+        // Compute the visible rect to snap against. This ensures segments along
+        // the edges are snapped consistently with other nearby primitives.
+        visible_rect = intersect_rects(local_clip_rect, snap_rect);
+    }
+
     /// Compute the snapping offset.
     vec2 snap_offset = compute_snap_offset(
         clamped_local_pos,
-        snap_rect,
-        snap_offsets
+        transform.m,
+        visible_rect,
+        task.device_pixel_scale
     );
 
     // Transform the current vertex to world space.
     vec4 world_pos = transform.m * vec4(clamped_local_pos, 0.0, 1.0);
 
     // Convert the world positions to device pixel space.
     vec2 device_pos = world_pos.xy * task.device_pixel_scale;
 
--- a/gfx/wr/webrender/res/snap.glsl
+++ b/gfx/wr/webrender/res/snap.glsl
@@ -1,22 +1,68 @@
 /* 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/. */
 
 #ifdef WR_VERTEX_SHADER
 
+vec4 compute_snap_positions(
+    mat4 transform,
+    RectWithSize snap_rect,
+    float device_pixel_scale
+) {
+    // Ensure that the snap rect is at *least* one device pixel in size.
+    // TODO(gw): It's not clear to me that this is "correct". Specifically,
+    //           how should it interact with sub-pixel snap rects when there
+    //           is a transform with scale present? But it does fix
+    //           the test cases we have in Servo that are failing without it
+    //           and seem better than not having this at all.
+    snap_rect.size = max(snap_rect.size, vec2(1.0 / device_pixel_scale));
+
+    // Transform the snap corners to the world space.
+    vec4 world_snap_p0 = transform * vec4(snap_rect.p0, 0.0, 1.0);
+    vec4 world_snap_p1 = transform * vec4(snap_rect.p0 + snap_rect.size, 0.0, 1.0);
+    // Snap bounds in world coordinates, adjusted for pixel ratio. XY = top left, ZW = bottom right
+    vec4 world_snap = device_pixel_scale * vec4(world_snap_p0.xy, world_snap_p1.xy) /
+                                           vec4(world_snap_p0.ww, world_snap_p1.ww);
+    return world_snap;
+}
+
 /// Given a point within a local rectangle, and the device space corners
-/// offsets for the unsnapped primitive, return the snap offsets. This *must*
-/// exactly match the logic in the Rust compute_snap_offset_impl function.
-vec2 compute_snap_offset(
+/// of a snapped primitive, return the snap offsets. This *must* exactly
+/// match the logic in the GLSL compute_snap_offset_impl function.
+vec2 compute_snap_offset_impl(
     vec2 reference_pos,
     RectWithSize reference_rect,
     vec4 snap_positions
 ) {
+    /// World offsets applied to the corners of the snap rectangle.
+    vec4 snap_offsets = floor(snap_positions + 0.5) - snap_positions;
+
     /// Compute the position of this vertex inside the snap rectangle.
     vec2 normalized_snap_pos = (reference_pos - reference_rect.p0) / reference_rect.size;
 
     /// Compute the actual world offset for this vertex needed to make it snap.
-    return mix(snap_positions.xy, snap_positions.zw, normalized_snap_pos);
+    return mix(snap_offsets.xy, snap_offsets.zw, normalized_snap_pos);
+}
+
+// Compute a snapping offset in world space (adjusted to pixel ratio),
+// given local position on the transform and a snap rectangle.
+vec2 compute_snap_offset(vec2 local_pos,
+                         mat4 transform,
+                         RectWithSize snap_rect,
+                         float device_pixel_scale) {
+    vec4 snap_positions = compute_snap_positions(
+        transform,
+        snap_rect,
+        device_pixel_scale
+    );
+
+    vec2 snap_offsets = compute_snap_offset_impl(
+        local_pos,
+        snap_rect,
+        snap_positions
+    );
+
+    return snap_offsets;
 }
 
 #endif //WR_VERTEX_SHADER
--- a/gfx/wr/webrender/src/batch.rs
+++ b/gfx/wr/webrender/src/batch.rs
@@ -13,17 +13,16 @@ use gpu_types::{BrushFlags, BrushInstanc
 use gpu_types::{ClipMaskInstance, SplitCompositeInstance, SnapOffsets};
 use gpu_types::{PrimitiveInstanceData, RasterizationSpace, GlyphInstance};
 use gpu_types::{PrimitiveHeader, PrimitiveHeaderIndex, TransformPaletteId, TransformPalette};
 use internal_types::{FastHashMap, SavedTargetIndex, TextureSource};
 use picture::{Picture3DContext, PictureCompositeMode, PicturePrimitive, PictureSurface};
 use prim_store::{DeferredResolve, EdgeAaSegmentMask, PrimitiveInstanceKind, PrimitiveVisibilityIndex};
 use prim_store::{VisibleGradientTile, PrimitiveInstance, PrimitiveOpacity, SegmentInstanceIndex};
 use prim_store::{BrushSegment, ClipMaskKind, ClipTaskIndex, VECS_PER_SEGMENT};
-use prim_store::{recompute_snap_offsets};
 use prim_store::image::ImageSource;
 use render_backend::DataStores;
 use render_task::{RenderTaskAddress, RenderTaskId, RenderTaskTree, TileBlit};
 use renderer::{BlendMode, ImageBufferKind, ShaderColorMode};
 use renderer::{BLOCKS_PER_UV_RECT, MAX_VERTEX_TEXTURE_WIDTH};
 use resource_cache::{CacheItem, GlyphFetchResult, ImageRequest, ResourceCache, ImageProperties};
 use scene::FilterOpHelpers;
 use smallvec::SmallVec;
@@ -621,35 +620,32 @@ impl AlphaBatchBuilder {
         let z_id = z_generator.next();
 
         let prim_common_data = &ctx.data_stores.as_common_data(&prim_instance);
         let prim_rect = LayoutRect::new(
             prim_instance.prim_origin,
             prim_common_data.prim_size,
         );
 
-        let snap_offsets = prim_info.snap_offsets;
-
         if is_chased {
             println!("\tbatch {:?} with bound {:?}", prim_rect, bounding_rect);
         }
 
         match prim_instance.kind {
             PrimitiveInstanceKind::Clear { data_handle } => {
                 let prim_data = &ctx.data_stores.prim[data_handle];
                 let prim_cache_address = gpu_cache.get_address(&prim_data.gpu_cache_handle);
 
                 // TODO(gw): We can abstract some of the common code below into
                 //           helper methods, as we port more primitives to make
                 //           use of interning.
 
                 let prim_header = PrimitiveHeader {
                     local_rect: prim_rect,
                     local_clip_rect: prim_info.combined_local_clip_rect,
-                    snap_offsets,
                     task_address,
                     specific_prim_address: prim_cache_address,
                     transform_id,
                 };
 
                 let prim_header_index = prim_headers.push(
                     &prim_header,
                     z_id,
@@ -714,17 +710,16 @@ impl AlphaBatchBuilder {
                     specified_blend_mode
                 } else {
                     BlendMode::None
                 };
 
                 let prim_header = PrimitiveHeader {
                     local_rect: prim_rect,
                     local_clip_rect: prim_info.combined_local_clip_rect,
-                    snap_offsets,
                     task_address,
                     specific_prim_address: prim_cache_address,
                     transform_id,
                 };
 
                 let batch_params = BrushBatchParameters::instanced(
                     BrushBatchKind::Image(ImageBufferKind::Texture2DArray),
                     [
@@ -767,17 +762,16 @@ impl AlphaBatchBuilder {
                 let prim_data = &ctx.data_stores.text_run[data_handle];
                 let glyph_fetch_buffer = &mut self.glyph_fetch_buffer;
                 let alpha_batch_list = &mut self.batch_lists.last_mut().unwrap().alpha_batch_list;
                 let prim_cache_address = gpu_cache.get_address(&prim_data.gpu_cache_handle);
 
                 let prim_header = PrimitiveHeader {
                     local_rect: prim_rect,
                     local_clip_rect: prim_info.combined_local_clip_rect,
-                    snap_offsets,
                     task_address,
                     specific_prim_address: prim_cache_address,
                     transform_id,
                 };
 
                 let clip_task_address = ctx.get_prim_clip_task_address(
                     prim_info.clip_task_index,
                     render_tasks,
@@ -935,17 +929,16 @@ impl AlphaBatchBuilder {
                     BlendMode::PremultipliedAlpha
                 } else {
                     BlendMode::None
                 };
 
                 let prim_header = PrimitiveHeader {
                     local_rect: prim_rect,
                     local_clip_rect: prim_info.combined_local_clip_rect,
-                    snap_offsets,
                     task_address,
                     specific_prim_address: prim_cache_address,
                     transform_id,
                 };
 
                 let prim_header_index = prim_headers.push(
                     &prim_header,
                     z_id,
@@ -980,19 +973,18 @@ impl AlphaBatchBuilder {
                 );
             }
             PrimitiveInstanceKind::Picture { pic_index, segment_instance_index, .. } => {
                 let picture = &ctx.prim_store.pictures[pic_index.0];
                 let non_segmented_blend_mode = BlendMode::PremultipliedAlpha;
                 let prim_cache_address = gpu_cache.get_address(&ctx.globals.default_image_handle);
 
                 let prim_header = PrimitiveHeader {
-                    local_rect: picture.snapped_local_rect,
+                    local_rect: picture.local_rect,
                     local_clip_rect: prim_info.combined_local_clip_rect,
-                    snap_offsets,
                     task_address,
                     specific_prim_address: prim_cache_address,
                     transform_id,
                 };
 
                 match picture.context_3d {
                     // Convert all children of the 3D hierarchy root into batches.
                     Picture3DContext::In { root_data: Some(ref list), .. } => {
@@ -1019,19 +1011,18 @@ impl AlphaBatchBuilder {
 
                             // Get clip task, if set, for the picture primitive.
                             let clip_task_address = ctx.get_prim_clip_task_address(
                                 prim_info.clip_task_index,
                                 render_tasks,
                             ).unwrap_or(OPAQUE_TASK_ADDRESS);
 
                             let prim_header = PrimitiveHeader {
-                                local_rect: pic.snapped_local_rect,
+                                local_rect: pic.local_rect,
                                 local_clip_rect: prim_info.combined_local_clip_rect,
-                                snap_offsets,
                                 task_address,
                                 specific_prim_address: GpuCacheAddress::invalid(),
                                 transform_id: transforms
                                     .get_id(
                                         child.spatial_node_index,
                                         root_spatial_node_index,
                                         ctx.clip_scroll_tree,
                                     ),
@@ -1083,22 +1074,29 @@ impl AlphaBatchBuilder {
                     // hierarchy, since we process them with the root.
                     Picture3DContext::In { root_data: None, .. } => return,
                     // Proceed for non-3D pictures.
                     Picture3DContext::Out => ()
                 }
 
                 match picture.raster_config {
                     Some(ref raster_config) => {
+                        // All pictures must snap to their primitive rect instead of the
+                        // visible rect like most primitives. This is because the picture's
+                        // visible rect includes the effect of the picture's clip rect,
+                        // which was not considered by the picture's children. The primitive
+                        // rect however is simply the union of the visible rect of the
+                        // children, which they snapped to, which is precisely what we also
+                        // need to snap to in order to be consistent.
+                        let mut brush_flags = BrushFlags::SNAP_TO_PRIMITIVE;
+
                         // If the child picture was rendered in local space, we can safely
                         // interpolate the UV coordinates with perspective correction.
-                        let brush_flags = if raster_config.establishes_raster_root {
-                            BrushFlags::PERSPECTIVE_INTERPOLATION
-                        } else {
-                            BrushFlags::empty()
+                        if raster_config.establishes_raster_root {
+                            brush_flags |= BrushFlags::PERSPECTIVE_INTERPOLATION;
                         };
 
                         let clip_task_address = ctx.get_prim_clip_task_address(
                             prim_info.clip_task_index,
                             render_tasks,
                         ).unwrap_or(OPAQUE_TASK_ADDRESS);
 
                         let surface = ctx.surfaces[raster_config.surface_index.0]
@@ -1128,17 +1126,17 @@ impl AlphaBatchBuilder {
 
                                     return;
                                 }
 
                                 // Construct a local clip rect that ensures we only draw pixels where
                                 // the local bounds of the picture extend to within the edge tiles.
                                 let local_clip_rect = prim_info
                                     .combined_local_clip_rect
-                                    .intersection(&picture.snapped_local_rect)
+                                    .intersection(&picture.local_rect)
                                     .and_then(|rect| {
                                         rect.intersection(&tile_cache.local_clip_rect)
                                     });
 
                                 if let Some(local_clip_rect) = local_clip_rect {
                                     // Step through each tile in the cache, and draw it with an image
                                     // brush primitive if visible.
 
@@ -1147,27 +1145,19 @@ impl AlphaBatchBuilder {
                                     );
 
                                     for tile_index in &tile_cache.tiles_to_draw {
                                         let tile = &tile_cache.tiles[tile_index.0];
 
                                         // Get the local rect of the tile.
                                         let tile_rect = tile.local_rect;
 
-                                        // Adjust the snap offsets for the tile.
-                                        let snap_offsets = recompute_snap_offsets(
-                                            tile_rect,
-                                            prim_rect,
-                                            snap_offsets,
-                                        );
-
                                         let prim_header = PrimitiveHeader {
                                             local_rect: tile_rect,
                                             local_clip_rect,
-                                            snap_offsets,
                                             task_address,
                                             specific_prim_address: prim_cache_address,
                                             transform_id,
                                         };
 
                                         let prim_header_index = prim_headers.push(&prim_header, z_id, [
                                             ShaderColorMode::Image as i32 | ((AlphaType::PremultipliedAlpha as i32) << 16),
                                             RasterizationSpace::Local as i32,
@@ -1359,17 +1349,16 @@ impl AlphaBatchBuilder {
                                             get_shader_opacity(1.0),
                                             0,
                                         ]);
 
                                         let shadow_rect = prim_header.local_rect.translate(&offset);
 
                                         let shadow_prim_header = PrimitiveHeader {
                                             local_rect: shadow_rect,
-                                            snap_offsets: prim_info.shadow_snap_offsets,
                                             specific_prim_address: shadow_prim_address,
                                             ..prim_header
                                         };
 
                                         let shadow_prim_header_index = prim_headers.push(&shadow_prim_header, z_id_shadow, [
                                             ShaderColorMode::Alpha as i32 | ((AlphaType::PremultipliedAlpha as i32) << 16),
                                             RasterizationSpace::Screen as i32,
                                             get_shader_opacity(1.0),
@@ -1649,19 +1638,18 @@ impl AlphaBatchBuilder {
                                     let segment_instance = &ctx.scratch.segment_instances[segment_instance_index];
                                     let segments = Some(&ctx.scratch.segments[segment_instance.segments_range]);
                                     (gpu_cache.get_address(&segment_instance.gpu_cache_handle), segments)
                                 } else {
                                     (prim_cache_address, None)
                                 };
 
                                 let prim_header = PrimitiveHeader {
-                                    local_rect: picture.snapped_local_rect,
+                                    local_rect: picture.local_rect,
                                     local_clip_rect: prim_info.combined_local_clip_rect,
-                                    snap_offsets,
                                     task_address,
                                     specific_prim_address: prim_cache_address,
                                     transform_id,
                                 };
 
                                 let prim_header_index = prim_headers.push(
                                     &prim_header,
                                     z_id,
@@ -1737,17 +1725,16 @@ impl AlphaBatchBuilder {
                     specified_blend_mode
                 } else {
                     BlendMode::None
                 };
 
                 let prim_header = PrimitiveHeader {
                     local_rect: prim_rect,
                     local_clip_rect: prim_info.combined_local_clip_rect,
-                    snap_offsets: snap_offsets,
                     task_address,
                     specific_prim_address: prim_cache_address,
                     transform_id,
                 };
 
                 let batch_params = BrushBatchParameters::shared(
                     BrushBatchKind::Image(get_buffer_kind(cache_item.texture_id)),
                     textures,
@@ -1811,17 +1798,16 @@ impl AlphaBatchBuilder {
                     let segment_instance = &ctx.scratch.segment_instances[segment_instance_index];
                     let segments = Some(&ctx.scratch.segments[segment_instance.segments_range]);
                     (gpu_cache.get_address(&segment_instance.gpu_cache_handle), segments)
                 };
 
                 let prim_header = PrimitiveHeader {
                     local_rect: prim_rect,
                     local_clip_rect: prim_info.combined_local_clip_rect,
-                    snap_offsets: snap_offsets,
                     task_address,
                     specific_prim_address: prim_cache_address,
                     transform_id,
                 };
 
                 let prim_header_index = prim_headers.push(
                     &prim_header,
                     z_id,
@@ -1919,17 +1905,16 @@ impl AlphaBatchBuilder {
                     let segment_instance = &ctx.scratch.segment_instances[segment_instance_index];
                     let segments = Some(&ctx.scratch.segments[segment_instance.segments_range]);
                     (gpu_cache.get_address(&segment_instance.gpu_cache_handle), segments)
                 };
 
                 let prim_header = PrimitiveHeader {
                     local_rect: prim_rect,
                     local_clip_rect: prim_info.combined_local_clip_rect,
-                    snap_offsets: snap_offsets,
                     task_address,
                     specific_prim_address: prim_cache_address,
                     transform_id,
                 };
 
                 let prim_header_index = prim_headers.push(
                     &prim_header,
                     z_id,
@@ -2024,17 +2009,16 @@ impl AlphaBatchBuilder {
                         let segment_instance = &ctx.scratch.segment_instances[image_instance.segment_instance_index];
                         let segments = Some(&ctx.scratch.segments[segment_instance.segments_range]);
                         (gpu_cache.get_address(&segment_instance.gpu_cache_handle), segments)
                     };
 
                     let prim_header = PrimitiveHeader {
                         local_rect: prim_rect,
                         local_clip_rect: prim_info.combined_local_clip_rect,
-                        snap_offsets: snap_offsets,
                         task_address,
                         specific_prim_address: prim_cache_address,
                         transform_id,
                     };
 
                     let prim_header_index = prim_headers.push(
                         &prim_header,
                         z_id,
@@ -2077,17 +2061,16 @@ impl AlphaBatchBuilder {
                             gpu_blocks.push(tile_rect.into());
                             gpu_blocks.push(GpuBlockData::EMPTY);
                         }
 
                         let gpu_handle = gpu_cache.push_per_frame_blocks(&gpu_blocks);
                         let prim_header = PrimitiveHeader {
                             local_rect: prim_rect,
                             local_clip_rect: image_instance.tight_local_clip_rect,
-                            snap_offsets,
                             task_address,
                             specific_prim_address: gpu_cache.get_address(&gpu_handle),
                             transform_id,
                         };
                         let prim_header_index = prim_headers.push(&prim_header, z_id, prim_user_data);
 
                         for (i, tile) in chunk.iter().enumerate() {
                             if let Some((batch_kind, textures, uv_rect_address)) = get_image_tile_params(
@@ -2123,17 +2106,16 @@ impl AlphaBatchBuilder {
             PrimitiveInstanceKind::LinearGradient { data_handle, gradient_index, .. } => {
                 let gradient = &ctx.prim_store.linear_gradients[gradient_index];
                 let prim_data = &ctx.data_stores.linear_grad[data_handle];
                 let specified_blend_mode = BlendMode::PremultipliedAlpha;
 
                 let mut prim_header = PrimitiveHeader {
                     local_rect: prim_rect,
                     local_clip_rect: prim_info.combined_local_clip_rect,
-                    snap_offsets,
                     task_address,
                     specific_prim_address: GpuCacheAddress::invalid(),
                     transform_id,
                 };
 
                 let non_segmented_blend_mode = if !prim_data.opacity.is_opaque ||
                     prim_info.clip_task_index != ClipTaskIndex::INVALID ||
                     transform_kind == TransformedRectKind::Complex
@@ -2262,17 +2244,16 @@ impl AlphaBatchBuilder {
             }
             PrimitiveInstanceKind::RadialGradient { data_handle, ref visible_tiles_range, .. } => {
                 let prim_data = &ctx.data_stores.radial_grad[data_handle];
                 let specified_blend_mode = BlendMode::PremultipliedAlpha;
 
                 let mut prim_header = PrimitiveHeader {
                     local_rect: prim_rect,
                     local_clip_rect: prim_info.combined_local_clip_rect,
-                    snap_offsets,
                     task_address,
                     specific_prim_address: GpuCacheAddress::invalid(),
                     transform_id,
                 };
 
                 if visible_tiles_range.is_empty() {
                     let non_segmented_blend_mode = if !prim_data.opacity.is_opaque ||
                         prim_info.clip_task_index != ClipTaskIndex::INVALID ||
@@ -2532,28 +2513,20 @@ fn add_gradient_tiles(
         },
         bounding_rect,
         z_id,
     );
 
     let user_data = [stops_handle.as_int(gpu_cache), 0, 0, 0];
 
     for tile in visible_tiles {
-        // Adjust the snap offsets for the tile.
-        let snap_offsets = recompute_snap_offsets(
-            tile.local_rect,
-            base_prim_header.local_rect,
-            base_prim_header.snap_offsets,
-        );
-
         let prim_header = PrimitiveHeader {
             specific_prim_address: gpu_cache.get_address(&tile.handle),
             local_rect: tile.local_rect,
             local_clip_rect: tile.local_clip_rect,
-            snap_offsets,
             ..*base_prim_header
         };
         let prim_header_index = prim_headers.push(&prim_header, z_id, user_data);
 
         batch.push(PrimitiveInstanceData::from(
             BrushInstance {
                 prim_header_index,
                 clip_task_address,
--- a/gfx/wr/webrender/src/gpu_types.rs
+++ b/gfx/wr/webrender/src/gpu_types.rs
@@ -230,17 +230,16 @@ impl PrimitiveHeaders {
         user_data: [i32; 4],
     ) -> PrimitiveHeaderIndex {
         debug_assert_eq!(self.headers_int.len(), self.headers_float.len());
         let id = self.headers_float.len();
 
         self.headers_float.push(PrimitiveHeaderF {
             local_rect: prim_header.local_rect,
             local_clip_rect: prim_header.local_clip_rect,
-            snap_offsets: prim_header.snap_offsets,
         });
 
         self.headers_int.push(PrimitiveHeaderI {
             z,
             task_address: prim_header.task_address,
             specific_prim_address: prim_header.specific_prim_address.as_int(),
             transform_id: prim_header.transform_id,
             user_data,
@@ -251,31 +250,29 @@ impl PrimitiveHeaders {
 }
 
 // This is a convenience type used to make it easier to pass
 // the common parts around during batching.
 #[derive(Debug)]
 pub struct PrimitiveHeader {
     pub local_rect: LayoutRect,
     pub local_clip_rect: LayoutRect,
-    pub snap_offsets: SnapOffsets,
     pub task_address: RenderTaskAddress,
     pub specific_prim_address: GpuCacheAddress,
     pub transform_id: TransformPaletteId,
 }
 
 // f32 parts of a primitive header
 #[derive(Debug)]
 #[repr(C)]
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 pub struct PrimitiveHeaderF {
     pub local_rect: LayoutRect,
     pub local_clip_rect: LayoutRect,
-    pub snap_offsets: SnapOffsets,
 }
 
 // i32 parts of a primitive header
 // TODO(gw): Compress parts of these down to u16
 #[derive(Debug)]
 #[repr(C)]
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
@@ -361,16 +358,18 @@ bitflags! {
         /// rather than primitive rect.
         const SEGMENT_RELATIVE = 0x2;
         /// Repeat UVs horizontally.
         const SEGMENT_REPEAT_X = 0x4;
         /// Repeat UVs vertically.
         const SEGMENT_REPEAT_Y = 0x8;
         /// The extra segment data is a texel rect.
         const SEGMENT_TEXEL_RECT = 0x10;
+        /// Snap to the primitive rect instead of the visible rect.
+        const SNAP_TO_PRIMITIVE = 0x20;
     }
 }
 
 // TODO(gw): Some of these fields can be moved to the primitive
 //           header since they are constant, and some can be
 //           compressed to a smaller size.
 #[repr(C)]
 pub struct BrushInstance {
@@ -624,21 +623,16 @@ pub struct SnapOffsets {
 
 impl SnapOffsets {
     pub fn empty() -> Self {
         SnapOffsets {
             top_left: DeviceVector2D::zero(),
             bottom_right: DeviceVector2D::zero(),
         }
     }
-
-    pub fn is_empty(&self) -> bool {
-        let zero = DeviceVector2D::zero();
-        self.top_left == zero && self.bottom_right == zero
-    }
 }
 
 #[derive(Debug, Copy, Clone)]
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 pub struct ImageSource {
     pub p0: DevicePoint,
     pub p1: DevicePoint,
--- a/gfx/wr/webrender/src/picture.rs
+++ b/gfx/wr/webrender/src/picture.rs
@@ -853,28 +853,21 @@ impl TileCache {
                 None => true,
             };
             self.opacity_bindings.insert(*id, OpacityBindingInfo {
                 value: *value,
                 changed,
             });
         }
 
-        // Map the picture rect to world and device space and work out the tiles
-        // that we need in order to ensure the screen is covered. We haven't done
-        // any snapping yet, so we need to round out in device space to ensure we
-        // cover all pixels the picture may touch.
-        let pic_device_rect = {
-            let unsnapped_world_rect = world_mapper
-                .map(&pic_rect)
-                .expect("bug: unable to map picture rect to world");
-            (unsnapped_world_rect * frame_context.global_device_pixel_scale)
-                .round_out()
-        };
-        let pic_world_rect = pic_device_rect / frame_context.global_device_pixel_scale;
+        // Map the picture rect to world space and work out the tiles that we need
+        // in order to ensure the screen is covered.
+        let pic_world_rect = world_mapper
+            .map(&pic_rect)
+            .expect("bug: unable to map picture rect to world");
 
         // If the bounding rect of the picture to cache doesn't intersect with
         // the visible world rect at all, just take the screen world rect as
         // a reference for the area to create tiles for. This allows existing
         // tiles to be retained in case they are still valid if / when they
         // get scrolled back onto the screen.
 
         let needed_world_rect = frame_context
@@ -894,16 +887,17 @@ impl TileCache {
 
         // Apply the scroll delta so that existing tiles still get used.
         world_ref_point += scroll_delta;
 
         // Work out the required device rect that we need to cover the screen,
         // given the world reference point constraint.
         let device_ref_point = world_ref_point * frame_context.global_device_pixel_scale;
         let device_world_rect = frame_context.screen_world_rect * frame_context.global_device_pixel_scale;
+        let pic_device_rect = pic_world_rect * frame_context.global_device_pixel_scale;
         let needed_device_rect = pic_device_rect
             .intersection(&device_world_rect)
             .unwrap_or(device_world_rect);
 
         // Expand the needed device rect vertically by a small number of tiles. This
         // ensures that as tiles are scrolled in/out of view, they are retained for
         // a while before being discarded.
         // TODO(gw): On some pages it might be worth also inflating horizontally.
@@ -1734,16 +1728,17 @@ impl<'a> PictureUpdateState<'a> {
                     clip_data_store,
                 );
             }
 
             picture_primitives[pic_index.0].post_update(
                 prim_list,
                 self,
                 frame_context,
+                gpu_cache,
             );
         }
     }
 
     /// Process the picture tree again in a depth-first order,
     /// and adjust the raster roots of the pictures that want to establish
     /// their own roots but are not able to due to the size constraints.
     fn assign_raster_roots(
@@ -2201,26 +2196,18 @@ pub struct PicturePrimitive {
     // picture.
     pub extra_gpu_data_handle: GpuCacheHandle,
 
     /// The spatial node index of this picture when it is
     /// composited into the parent picture.
     pub spatial_node_index: SpatialNodeIndex,
 
     /// The local rect of this picture. It is built
-    /// dynamically when updating visibility. It takes
-    /// into account snapping in device space for its
-    /// children.
-    pub snapped_local_rect: LayoutRect,
-
-    /// The local rect of this picture. It is built
-    /// dynamically during the first picture traversal. It
-    /// does not take into account snapping in device for
-    /// its children.
-    pub unsnapped_local_rect: LayoutRect,
+    /// dynamically during the first picture traversal.
+    pub local_rect: LayoutRect,
 
     /// If false, this picture needs to (re)build segments
     /// if it supports segment rendering. This can occur
     /// if the local rect of the picture changes due to
     /// transform animation and/or scrolling.
     pub segments_are_valid: bool,
 
     /// If Some(..) the tile cache that is associated with this picture.
@@ -2235,18 +2222,17 @@ impl PicturePrimitive {
     pub fn print<T: PrintTreePrinter>(
         &self,
         pictures: &[Self],
         self_index: PictureIndex,
         pt: &mut T,
     ) {
         pt.new_level(format!("{:?}", self_index));
         pt.add_item(format!("prim_count: {:?}", self.prim_list.prim_instances.len()));
-        pt.add_item(format!("snapped_local_rect: {:?}", self.snapped_local_rect));
-        pt.add_item(format!("unsnapped_local_rect: {:?}", self.unsnapped_local_rect));
+        pt.add_item(format!("local_rect: {:?}", self.local_rect));
         pt.add_item(format!("spatial_node_index: {:?}", self.spatial_node_index));
         pt.add_item(format!("raster_config: {:?}", self.raster_config));
         pt.add_item(format!("requested_composite_mode: {:?}", self.requested_composite_mode));
 
         for (index, _) in &self.prim_list.pictures {
             pictures[index.0].print(pictures, *index, pt);
         }
 
@@ -2345,18 +2331,17 @@ impl PicturePrimitive {
             context_3d,
             frame_output_pipeline_id,
             extra_gpu_data_handle: GpuCacheHandle::new(),
             apply_local_clip_rect,
             is_backface_visible,
             pipeline_id,
             requested_raster_space,
             spatial_node_index,
-            snapped_local_rect: LayoutRect::zero(),
-            unsnapped_local_rect: LayoutRect::zero(),
+            local_rect: LayoutRect::zero(),
             tile_cache,
             options,
             segments_are_valid: false,
         }
     }
 
     pub fn take_context(
         &mut self,
@@ -2770,16 +2755,17 @@ impl PicturePrimitive {
 
     /// Called after updating child pictures during the initial
     /// picture traversal.
     fn post_update(
         &mut self,
         prim_list: PrimitiveList,
         state: &mut PictureUpdateState,
         frame_context: &FrameBuildingContext,
+        gpu_cache: &mut GpuCache,
     ) {
         // Restore the pictures list used during recursion.
         self.prim_list = prim_list;
 
         // Pop the state information about this picture.
         state.pop_picture();
 
         for cluster in &mut self.prim_list.clusters {
@@ -2842,20 +2828,32 @@ impl PicturePrimitive {
                 surface.rect = surface.rect.inflate(inflation_size, inflation_size);
                 surface.rect * TypedScale::new(1.0)
             };
 
             // Pop this surface from the stack
             let surface_index = state.pop_surface();
             debug_assert_eq!(surface_index, raster_config.surface_index);
 
-            // Snapping may change the local rect slightly, and as such should just be
-            // considered an estimated size for determining if we need raster roots and
-            // preparing the tile cache.
-            self.unsnapped_local_rect = surface_rect;
+            // If the local rect changed (due to transforms in child primitives) then
+            // invalidate the GPU cache location to re-upload the new local rect
+            // and stretch size. Drop shadow filters also depend on the local rect
+            // size for the extra GPU cache data handle.
+            // TODO(gw): In future, if we support specifying a flag which gets the
+            //           stretch size from the segment rect in the shaders, we can
+            //           remove this invalidation here completely.
+            if self.local_rect != surface_rect {
+                if let PictureCompositeMode::Filter(FilterOp::DropShadow(..)) = raster_config.composite_mode {
+                    gpu_cache.invalidate(&self.extra_gpu_data_handle);
+                }
+                // Invalidate any segments built for this picture, since the local
+                // rect has changed.
+                self.segments_are_valid = false;
+                self.local_rect = surface_rect;
+            }
 
             // Check if any of the surfaces can't be rasterized in local space but want to.
             if raster_config.establishes_raster_root {
                 if surface_rect.size.width > MAX_SURFACE_SIZE ||
                     surface_rect.size.height > MAX_SURFACE_SIZE
                 {
                     raster_config.establishes_raster_root = false;
                     state.are_raster_roots_assigned = false;
@@ -2919,17 +2917,17 @@ 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.screen_world_rect,
             frame_context.clip_scroll_tree,
         );
 
-        let pic_rect = PictureRect::from_untyped(&self.snapped_local_rect.to_untyped());
+        let pic_rect = PictureRect::from_untyped(&self.local_rect.to_untyped());
 
         let (clipped, unclipped) = match get_raster_rects(
             pic_rect,
             &map_pic_to_raster,
             &map_raster_to_world,
             clipped_prim_bounding_rect,
             device_pixel_scale,
         ) {
@@ -3081,24 +3079,24 @@ impl PicturePrimitive {
                     //           drop-shadow where we need a different local
                     //           rect for the shadow. To tidy this up in future,
                     //           we could consider abstracting the code in prim_store.rs
                     //           that writes a brush primitive header.
 
                     // Basic brush primitive header is (see end of prepare_prim_for_render_inner in prim_store.rs)
                     //  [brush specific data]
                     //  [segment_rect, segment data]
-                    let shadow_rect = self.snapped_local_rect.translate(&offset);
+                    let shadow_rect = self.local_rect.translate(&offset);
 
                     // ImageBrush colors
                     request.push(color.premultiplied());
                     request.push(PremultipliedColorF::WHITE);
                     request.push([
-                        self.snapped_local_rect.size.width,
-                        self.snapped_local_rect.size.height,
+                        self.local_rect.size.width,
+                        self.local_rect.size.height,
                         0.0,
                         0.0,
                     ]);
 
                     // segment rect / extra data
                     request.push(shadow_rect);
                     request.push([0.0, 0.0, 0.0, 0.0]);
                 }
--- a/gfx/wr/webrender/src/prim_store/mod.rs
+++ b/gfx/wr/webrender/src/prim_store/mod.rs
@@ -5,35 +5,34 @@
 use api::{BorderRadius, ClipMode, ColorF};
 use api::{FilterOp, ImageRendering, RepeatMode};
 use api::{PremultipliedColorF, PropertyBinding, Shadow, GradientStop};
 use api::{BoxShadowClipMode, LineStyle, LineOrientation};
 use api::{PrimitiveKeyKind, RasterSpace};
 use api::units::*;
 use border::{get_max_scale_for_border, build_border_instances};
 use border::BorderSegmentCacheKey;
-use box_shadow::{BLUR_SAMPLE_SCALE};
 use clip::{ClipStore};
 use clip_scroll_tree::{ROOT_SPATIAL_NODE_INDEX, ClipScrollTree, SpatialNodeIndex, VisibleFace};
 use clip::{ClipDataStore, ClipNodeFlags, ClipChainId, ClipChainInstance, ClipItem};
 use debug_colors;
 use debug_render::DebugItem;
 use display_list_flattener::{CreateShadow, IsVisible};
-use euclid::{SideOffsets2D, TypedTransform3D, TypedRect, TypedScale, TypedSize2D, TypedPoint2D};
+use euclid::{SideOffsets2D, TypedTransform3D, TypedRect, TypedScale, TypedSize2D};
 use euclid::approxeq::ApproxEq;
 use frame_builder::{FrameBuildingContext, FrameBuildingState, PictureContext, PictureState};
 use frame_builder::{FrameVisibilityContext, FrameVisibilityState};
 use glyph_rasterizer::GlyphKey;
 use gpu_cache::{GpuCache, GpuCacheAddress, GpuCacheHandle, GpuDataRequest, ToGpuBlocks};
 use gpu_types::{BrushFlags, SnapOffsets};
 use image::{Repetition};
 use intern;
 use malloc_size_of::MallocSizeOf;
 use picture::{PictureCompositeMode, PicturePrimitive, SurfaceInfo};
-use picture::{ClusterIndex, PrimitiveList, RecordedDirtyRegion, SurfaceIndex, RetainedTiles};
+use picture::{ClusterIndex, PrimitiveList, RecordedDirtyRegion, SurfaceIndex, RetainedTiles, RasterConfig};
 use prim_store::borders::{ImageBorderDataHandle, NormalBorderDataHandle};
 use prim_store::gradient::{GRADIENT_FP_STOPS, GradientCacheKey, GradientStopKey};
 use prim_store::gradient::{LinearGradientPrimitive, LinearGradientDataHandle, RadialGradientDataHandle};
 use prim_store::image::{ImageDataHandle, ImageInstance, VisibleImageTile, YuvImageDataHandle};
 use prim_store::line_dec::LineDecorationDataHandle;
 use prim_store::picture::PictureDataHandle;
 use prim_store::text_run::{TextRunDataHandle, TextRunPrimitive};
 #[cfg(debug_assertions)]
@@ -45,17 +44,17 @@ use renderer::{MAX_VERTEX_TEXTURE_WIDTH}
 use resource_cache::{ImageProperties, ImageRequest};
 use scene::SceneProperties;
 use segment::SegmentBuilder;
 use std::{cmp, fmt, hash, ops, u32, usize, mem};
 #[cfg(debug_assertions)]
 use std::sync::atomic::{AtomicUsize, Ordering};
 use storage;
 use texture_cache::TEXTURE_REGION_DIMENSIONS;
-use util::{ScaleOffset, MatrixHelpers, MaxRect, Recycler};
+use util::{ScaleOffset, MatrixHelpers, MaxRect, Recycler, TransformedRectKind};
 use util::{pack_as_float, project_rect, raster_rect_to_device_pixels};
 use util::{scale_factors, clamp_to_scale_factor};
 use internal_types::LayoutPrimitiveInfo;
 use smallvec::SmallVec;
 
 pub mod borders;
 pub mod gradient;
 pub mod image;
@@ -1366,28 +1365,16 @@ pub struct PrimitiveVisibility {
     /// 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,
 
     /// The current combined local clip for this primitive, from
     /// the primitive local clip above and the current clip chain.
     pub combined_local_clip_rect: LayoutRect,
-
-    /// The snap offsets in device space for this primitive. They are
-    /// generated based on the visible rect, which is the local rect
-    /// clipped by the combined local clip for most primitives, or
-    /// just the local rect for pictures.
-    pub snap_offsets: SnapOffsets,
-
-    /// The snap offsets in device space for the drop shadow for
-    /// picture primitives, if applicable. Similar to snap offsets,
-    /// they are generated based on the local rect translated by the
-    /// drop shadow offset.
-    pub shadow_snap_offsets: SnapOffsets,
 }
 
 #[derive(Clone, Debug)]
 #[cfg_attr(feature = "capture", derive(Serialize))]
 pub struct PrimitiveInstance {
     /// Identifies the kind of primitive this
     /// instance is, and references to where
     /// the relevant information for the primitive
@@ -1730,34 +1717,34 @@ impl PrimitiveStore {
     /// Update visibility pass - update each primitive visibility struct, and
     /// build the clip chain instance if appropriate.
     pub fn update_visibility(
         &mut self,
         pic_index: PictureIndex,
         parent_surface_index: SurfaceIndex,
         frame_context: &FrameVisibilityContext,
         frame_state: &mut FrameVisibilityState,
-    ) -> Option<PictureRect> {
+    ) {
         let (mut prim_list, surface_index, apply_local_clip_rect, raster_space) = {
             let pic = &mut self.pictures[pic_index.0];
 
             let prim_list = mem::replace(&mut pic.prim_list, PrimitiveList::empty());
             let surface_index = match pic.raster_config {
                 Some(ref raster_config) => raster_config.surface_index,
                 None => parent_surface_index,
             };
 
             if let Some(mut tile_cache) = pic.tile_cache.take() {
                 debug_assert!(frame_state.tile_cache.is_none());
 
                 // If we have a tile cache for this picture, see if any of the
                 // relative transforms have changed, which means we need to
                 // re-map the dependencies of any child primitives.
                 tile_cache.pre_update(
-                    pic.unsnapped_local_rect,
+                    pic.local_rect,
                     frame_context,
                     frame_state,
                     surface_index,
                 );
 
                 frame_state.tile_cache = Some(tile_cache);
             }
 
@@ -1772,23 +1759,16 @@ impl PrimitiveStore {
 
         let map_surface_to_world = SpaceMapper::new_with_target(
             ROOT_SPATIAL_NODE_INDEX,
             surface.surface_spatial_node_index,
             frame_context.screen_world_rect,
             frame_context.clip_scroll_tree,
         );
 
-        let mut map_local_to_raster = SpaceMapper::new(
-            surface.raster_spatial_node_index,
-            RasterRect::max_rect(),
-        );
-
-        let mut surface_rect = PictureRect::zero();
-
         for prim_instance in &mut prim_list.prim_instances {
             prim_instance.reset();
 
             if prim_instance.is_chased() {
                 #[cfg(debug_assertions)] // needed for ".id" part
                 println!("\tpreparing {:?} in {:?}", prim_instance.id, pic_index);
             }
 
@@ -1800,22 +1780,17 @@ impl PrimitiveStore {
                 continue;
             }
 
             map_local_to_surface.set_target_spatial_node(
                 prim_instance.spatial_node_index,
                 frame_context.clip_scroll_tree,
             );
 
-            map_local_to_raster.set_target_spatial_node(
-                prim_instance.spatial_node_index,
-                frame_context.clip_scroll_tree,
-            );
-
-            let (is_passthrough, snap_to_visible, prim_local_rect, prim_shadow_rect) = match prim_instance.kind {
+            let (is_passthrough, prim_local_rect) = match prim_instance.kind {
                 PrimitiveInstanceKind::Picture { pic_index, .. } => {
                     let is_composite = {
                         let pic = &self.pictures[pic_index.0];
                         if !pic.is_visible() {
                             continue;
                         }
 
                         // If this picture has a surface, we will handle any active clips from parents
@@ -1831,17 +1806,17 @@ impl PrimitiveStore {
                     };
 
                     if is_composite {
                         frame_state.clip_chain_stack.push_surface();
                     } else {
                         frame_state.clip_chain_stack.push_clip(prim_instance.clip_chain_id);
                     }
 
-                    let pic_surface_rect = self.update_visibility(
+                    self.update_visibility(
                         pic_index,
                         surface_index,
                         frame_context,
                         frame_state,
                     );
 
                     let pic = &self.pictures[pic_index.0];
 
@@ -1849,71 +1824,48 @@ impl PrimitiveStore {
                     // the content of children, which may move due to the spatial
                     // node they are attached to. Other parts of the code (such as
                     // segment generation) reads the origin from the prim instance,
                     // so ensure that is kept up to date here.
                     // TODO(gw): It's unfortunate that the prim origin is duplicated
                     //           this way. In future, we could perhaps just store the
                     //           size in the picture primitive, to that there isn't
                     //           any duplicated data.
-                    prim_instance.prim_origin = pic.snapped_local_rect.origin;
+                    prim_instance.prim_origin = pic.local_rect.origin;
 
                     // Similar to above, pop either the clip chain or root entry off the current clip stack.
                     if is_composite {
                         frame_state.clip_chain_stack.pop_surface();
                     } else {
                         frame_state.clip_chain_stack.pop_clip();
                     }
 
-                    let shadow_rect = match pic.raster_config {
-                        Some(ref rc) => match rc.composite_mode {
-                            // If we have a drop shadow filter, we also need to include the shadow in
-                            // our local rect for the purpose of calculating the size of the picture.
-                            PictureCompositeMode::Filter(FilterOp::DropShadow(offset, ..)) => pic.snapped_local_rect.translate(&offset),
-                            _ => LayoutRect::zero(),
-                        }
-                        None => {
-                            if let Some(ref rect) = pic_surface_rect {
-                                surface_rect = surface_rect.union(rect);
-                            }
-                            LayoutRect::zero()
-                        }
-                    };
-
-                    if prim_instance.is_chased() {
-                        if pic.unsnapped_local_rect != pic.snapped_local_rect {
-                            println!("\tsnapped from {:?} to {:?}", pic.unsnapped_local_rect, pic.snapped_local_rect);
-                        }
-                    }
-
-                    (pic.raster_config.is_none(), false, pic.snapped_local_rect, shadow_rect)
+                    (pic.raster_config.is_none(), pic.local_rect)
                 }
                 _ => {
                     let prim_data = &frame_state.data_stores.as_common_data(&prim_instance);
 
                     let prim_rect = LayoutRect::new(
                         prim_instance.prim_origin,
                         prim_data.prim_size,
                     );
 
-                    (false, true, prim_rect, LayoutRect::zero())
+                    (false, prim_rect)
                 }
             };
 
             if is_passthrough {
                 let vis_index = PrimitiveVisibilityIndex(frame_state.scratch.prim_info.len() as u32);
 
                 frame_state.scratch.prim_info.push(
                     PrimitiveVisibility {
                         clipped_world_rect: WorldRect::max_rect(),
                         clip_chain: ClipChainInstance::empty(),
                         clip_task_index: ClipTaskIndex::INVALID,
                         combined_local_clip_rect: LayoutRect::zero(),
-                        snap_offsets: SnapOffsets::empty(),
-                        shadow_snap_offsets: SnapOffsets::empty(),
                     }
                 );
 
                 prim_instance.visibility_info = vis_index;
             } 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");
@@ -1998,81 +1950,38 @@ impl PrimitiveStore {
 
                 if prim_instance.is_chased() {
                     println!("\teffective clip chain from {:?} {}",
                         clip_chain.clips_range,
                         if apply_local_clip_rect { "(applied)" } else { "" },
                     );
                 }
 
+                let combined_local_clip_rect = if apply_local_clip_rect {
+                    clip_chain.local_clip_rect
+                } else {
+                    prim_instance.local_clip_rect
+                };
+
                 // Check if the clip bounding rect (in pic space) is visible on screen
                 // This includes both the prim bounding rect + local prim clip rect!
                 let world_rect = match map_surface_to_world.map(&clip_chain.pic_clip_rect) {
                     Some(world_rect) => world_rect,
                     None => {
                         continue;
                     }
                 };
 
                 let clipped_world_rect = match world_rect.intersection(&frame_context.screen_world_rect) {
                     Some(rect) => rect,
                     None => {
                         continue;
                     }
                 };
 
-                let combined_local_clip_rect = if apply_local_clip_rect {
-                    clip_chain.local_clip_rect
-                } else {
-                    prim_instance.local_clip_rect
-                };
-
-                // All pictures must snap to their primitive rect instead of the
-                // visible rect like most primitives. This is because the picture's
-                // visible rect includes the effect of the picture's clip rect,
-                // which was not considered by the picture's children. The primitive
-                // rect however is simply the union of the visible rect of the
-                // children, which they snapped to, which is precisely what we also
-                // need to snap to in order to be consistent.
-                let visible_rect = if snap_to_visible {
-                    combined_local_clip_rect
-                        .intersection(&prim_local_rect)
-                        .unwrap_or(LayoutRect::zero())
-                } else {
-                    prim_local_rect
-                };
-
-                // This is how primitives get snapped. In general, snapping a picture's
-                // visible rect here will have no effect, but if it is rasterized in its
-                // own space, or it has a blur or drop shadow effect applied, it may
-                // provide a snapping offset.
-                let (snapped_visible_rect, snap_offsets) = get_snapped_rect(
-                    visible_rect,
-                    &map_local_to_raster,
-                    surface.device_pixel_scale,
-                ).unwrap_or((visible_rect, SnapOffsets::empty()));
-
-                let (combined_visible_rect, shadow_snap_offsets) = if !prim_shadow_rect.is_empty() {
-                    let (snapped_shadow_rect, shadow_snap_offsets) = get_snapped_rect(
-                        prim_shadow_rect,
-                        &map_local_to_raster,
-                        surface.device_pixel_scale,
-                    ).unwrap_or((prim_shadow_rect, SnapOffsets::empty()));
-
-                    (snapped_visible_rect.union(&snapped_shadow_rect), shadow_snap_offsets)
-                } else {
-                    (snapped_visible_rect, SnapOffsets::empty())
-                };
-
-                // Include the snapped primitive/picture local rect, including any shadows,
-                // in the area affected by the surface.
-                if let Some(rect) = map_local_to_surface.map(&combined_visible_rect) {
-                    surface_rect = surface_rect.union(&rect);
-                }
-
                 // When the debug display is enabled, paint a colored rectangle around each
                 // primitive.
                 if frame_context.debug_flags.contains(::api::DebugFlags::PRIMITIVE_DBG) {
                     let debug_color = match prim_instance.kind {
                         PrimitiveInstanceKind::Picture { .. } => ColorF::TRANSPARENT,
                         PrimitiveInstanceKind::TextRun { .. } => debug_colors::RED,
                         PrimitiveInstanceKind::LineDecoration { .. } => debug_colors::PURPLE,
                         PrimitiveInstanceKind::NormalBorder { .. } |
@@ -2093,18 +2002,16 @@ impl PrimitiveStore {
                 let vis_index = PrimitiveVisibilityIndex(frame_state.scratch.prim_info.len() as u32);
 
                 frame_state.scratch.prim_info.push(
                     PrimitiveVisibility {
                         clipped_world_rect,
                         clip_chain,
                         clip_task_index: ClipTaskIndex::INVALID,
                         combined_local_clip_rect,
-                        snap_offsets,
-                        shadow_snap_offsets,
                     }
                 );
 
                 prim_instance.visibility_info = vis_index;
 
                 self.request_resources_for_prim(
                     prim_instance,
                     surface,
@@ -2112,74 +2019,32 @@ impl PrimitiveStore {
                     &map_local_to_surface,
                     frame_context,
                     frame_state,
                 );
             }
         }
 
         let pic = &mut self.pictures[pic_index.0];
+
+        if let Some(RasterConfig { composite_mode: PictureCompositeMode::TileCache { .. }, .. }) = pic.raster_config {
+            let mut tile_cache = frame_state.tile_cache.take().unwrap();
+
+            // Build the dirty region(s) for this tile cache.
+            tile_cache.post_update(
+                frame_state.resource_cache,
+                frame_state.gpu_cache,
+                frame_context,
+                frame_state.scratch,
+            );
+
+            pic.tile_cache = Some(tile_cache);
+        }
+
         pic.prim_list = prim_list;
-
-        // If the local rect changed (due to transforms in child primitives) then
-        // invalidate the GPU cache location to re-upload the new local rect
-        // and stretch size. Drop shadow filters also depend on the local rect
-        // size for the extra GPU cache data handle.
-        // TODO(gw): In future, if we support specifying a flag which gets the
-        //           stretch size from the segment rect in the shaders, we can
-        //           remove this invalidation here completely.
-        if let Some(ref raster_config) = pic.raster_config {
-            // Inflate the local bounding rect if required by the filter effect.
-            // This inflaction factor is to be applied to the surface itself.
-            let inflation_size = match raster_config.composite_mode {
-                PictureCompositeMode::Filter(FilterOp::Blur(_)) => surface.inflation_factor,
-                PictureCompositeMode::Filter(FilterOp::DropShadow(_, blur_radius, _)) =>
-                    (blur_radius * BLUR_SAMPLE_SCALE).ceil(),
-                _ => 0.0,
-            };
-            surface_rect = surface_rect.inflate(inflation_size, inflation_size);
-
-            // Layout space for the picture is picture space from the
-            // perspective of its child primitives.
-            let pic_local_rect = surface_rect * TypedScale::new(1.0);
-            if pic.snapped_local_rect != pic_local_rect {
-                if let PictureCompositeMode::Filter(FilterOp::DropShadow(..)) = raster_config.composite_mode {
-                    frame_state.gpu_cache.invalidate(&pic.extra_gpu_data_handle);
-                }
-                // Invalidate any segments built for this picture, since the local
-                // rect has changed.
-                pic.segments_are_valid = false;
-                pic.snapped_local_rect = pic_local_rect;
-            }
-
-            if let PictureCompositeMode::TileCache { .. } = raster_config.composite_mode {
-                let mut tile_cache = frame_state.tile_cache.take().unwrap();
-
-                // Build the dirty region(s) for this tile cache.
-                tile_cache.post_update(
-                    frame_state.resource_cache,
-                    frame_state.gpu_cache,
-                    frame_context,
-                    frame_state.scratch,
-                );
-
-                pic.tile_cache = Some(tile_cache);
-            }
-
-            None
-        } else {
-            let parent_surface = &frame_context.surfaces[parent_surface_index.0 as usize];
-            let map_surface_to_parent_surface = SpaceMapper::new_with_target(
-                parent_surface.surface_spatial_node_index,
-                surface.surface_spatial_node_index,
-                PictureRect::max_rect(),
-                frame_context.clip_scroll_tree,
-            );
-            map_surface_to_parent_surface.map(&surface_rect)
-        }
     }
 
     fn request_resources_for_prim(
         &mut self,
         prim_instance: &mut PrimitiveInstance,
         surface: &SurfaceInfo,
         raster_space: RasterSpace,
         map_local_to_surface: &SpaceMapper<LayoutPixel, PicturePixel>,
@@ -2590,17 +2455,17 @@ impl PrimitiveStore {
                     frame_state,
                     data_stores,
                 ) {
                     if let Some(ref mut splitter) = pic_state.plane_splitter {
                         PicturePrimitive::add_split_plane(
                             splitter,
                             frame_state.transforms,
                             prim_instance,
-                            pic.snapped_local_rect,
+                            pic.local_rect,
                             &prim_info.combined_local_clip_rect,
                             frame_context.screen_world_rect,
                             plane_split_anchor,
                         );
                     }
 
                     // If this picture uses segments, ensure the GPU cache is
                     // up to date with segment local rects.
@@ -3431,17 +3296,17 @@ impl PrimitiveInstance {
                     // building logic below will be run.
                     if !pic.segments_are_valid {
                         *segment_instance_index = SegmentInstanceIndex::INVALID;
                         pic.segments_are_valid = true;
                     }
 
                     // Override the prim local rect with the dynamically calculated
                     // local rect for the picture.
-                    prim_local_rect = pic.snapped_local_rect;
+                    prim_local_rect = pic.local_rect;
 
                     segment_instance_index
                 } else {
                     return;
                 }
             }
             PrimitiveInstanceKind::TextRun { .. } |
             PrimitiveInstanceKind::NormalBorder { .. } |
@@ -3503,16 +3368,17 @@ impl PrimitiveInstance {
         frame_context: &FrameBuildingContext,
         frame_state: &mut FrameBuildingState,
         prim_store: &PrimitiveStore,
         data_stores: &mut DataStores,
         segments_store: &mut SegmentStorage,
         segment_instances_store: &mut SegmentInstanceStorage,
         clip_mask_instances: &mut Vec<ClipMaskKind>,
         unclipped: &DeviceRect,
+        prim_snap_offsets: SnapOffsets,
         device_pixel_scale: DevicePixelScale,
     ) -> bool {
         let segments = match self.kind {
             PrimitiveInstanceKind::TextRun { .. } |
             PrimitiveInstanceKind::Clear { .. } |
             PrimitiveInstanceKind::LineDecoration { .. } => {
                 return false;
             }
@@ -3612,17 +3478,17 @@ impl PrimitiveInstance {
                 prim_info.clipped_world_rect,
                 root_spatial_node_index,
                 pic_context.surface_index,
                 pic_state,
                 frame_context,
                 frame_state,
                 &mut data_stores.clip,
                 unclipped,
-                prim_info.snap_offsets,
+                prim_snap_offsets,
                 device_pixel_scale,
             );
             clip_mask_instances.push(clip_mask_kind);
         } else {
             let dirty_world_rect = frame_state.current_dirty_region().combined.world_rect;
 
             for segment in segments {
                 // Build a clip chain for the smaller segment rect. This will
@@ -3654,17 +3520,17 @@ impl PrimitiveInstance {
                     prim_info.clipped_world_rect,
                     root_spatial_node_index,
                     pic_context.surface_index,
                     pic_state,
                     frame_context,
                     frame_state,
                     &mut data_stores.clip,
                     unclipped,
-                    prim_info.snap_offsets,
+                    prim_snap_offsets,
                     device_pixel_scale,
                 );
                 clip_mask_instances.push(clip_mask_kind);
             }
         }
 
         true
     }
@@ -3682,23 +3548,28 @@ impl PrimitiveInstance {
     ) {
         let prim_info = &mut scratch.prim_info[self.visibility_info.0 as usize];
         let device_pixel_scale = frame_state.surfaces[pic_context.surface_index.0].device_pixel_scale;
 
         if self.is_chased() {
             println!("\tupdating clip task with pic rect {:?}", prim_info.clip_chain.pic_clip_rect);
         }
 
-        // Get the device space rect for the primitive if it was unclipped.
-        let unclipped = match get_unclipped_device_rect(
+        // Get the device space rect for the primitive if it was unclipped,
+        // including any snap offsets applied to the corners.
+        let (unclipped, prim_snap_offsets) = match get_unclipped_device_rect(
+            self.spatial_node_index,
+            root_spatial_node_index,
             prim_info.clip_chain.pic_clip_rect,
             &pic_state.map_pic_to_raster,
             device_pixel_scale,
+            frame_context,
+            frame_state,
         ) {
-            Some(rect) => rect,
+            Some(info) => info,
             None => return,
         };
 
         self.build_segments_if_needed(
             &prim_info.clip_chain,
             frame_state,
             prim_store,
             data_stores,
@@ -3715,31 +3586,32 @@ impl PrimitiveInstance {
             frame_context,
             frame_state,
             prim_store,
             data_stores,
             &mut scratch.segments,
             &mut scratch.segment_instances,
             &mut scratch.clip_mask_instances,
             &unclipped,
+            prim_snap_offsets,
             device_pixel_scale,
         ) {
             if self.is_chased() {
                 println!("\tsegment tasks have been created for clipping");
             }
             return;
         }
 
         if prim_info.clip_chain.needs_mask {
             // Get a minimal device space rect, clipped to the screen that we
             // need to allocate for the clip mask, as well as interpolated
             // snap offsets.
             if let Some((device_rect, snap_offsets)) = get_clipped_device_rect(
                 &unclipped,
-                prim_info.snap_offsets,
+                prim_snap_offsets,
                 &pic_state.map_raster_to_world,
                 prim_info.clipped_world_rect,
                 device_pixel_scale,
             ) {
                 let clip_task = RenderTask::new_mask(
                     device_rect,
                     prim_info.clip_chain.clips_range,
                     root_spatial_node_index,
@@ -3769,24 +3641,25 @@ impl PrimitiveInstance {
 }
 
 /// Mimics the GLSL mix() function.
 fn mix(x: f32, y: f32, a: f32) -> f32 {
     x * (1.0 - a) + y * a
 }
 
 /// Given a point within a local rectangle, and the device space corners
-/// of a snapped primitive, return the snap offsets.
-fn compute_snap_offset_impl<PixelSpace>(
-    reference_pos: TypedPoint2D<f32, PixelSpace>,
-    reference_rect: TypedRect<f32, PixelSpace>,
+/// of a snapped primitive, return the snap offsets. This *must* exactly
+/// match the logic in the GLSL compute_snap_offset_impl function.
+fn compute_snap_offset_impl(
+    reference_pos: PicturePoint,
+    reference_rect: PictureRect,
     prim_top_left: DevicePoint,
     prim_bottom_right: DevicePoint,
 ) -> DeviceVector2D {
-    let normalized_snap_pos = TypedPoint2D::<f32, PixelSpace>::new(
+    let normalized_snap_pos = PicturePoint::new(
         (reference_pos.x - reference_rect.origin.x) / reference_rect.size.width,
         (reference_pos.y - reference_rect.origin.y) / reference_rect.size.height,
     );
 
     let top_left = DeviceVector2D::new(
         (prim_top_left.x + 0.5).floor() - prim_top_left.x,
         (prim_top_left.y + 0.5).floor() - prim_top_left.y,
     );
@@ -3797,65 +3670,69 @@ fn compute_snap_offset_impl<PixelSpace>(
     );
 
     DeviceVector2D::new(
         mix(top_left.x, bottom_right.x, normalized_snap_pos.x),
         mix(top_left.y, bottom_right.y, normalized_snap_pos.y),
     )
 }
 
-/// Given the snapping offsets for a primitive rectangle, recompute
-/// the snapping offsets to be relative to given local rectangle.
-/// This *must* exactly match the logic in the GLSL
-/// compute_snap_offset function.
-pub fn recompute_snap_offsets<PixelSpace>(
-    local_rect: TypedRect<f32, PixelSpace>,
-    prim_rect: TypedRect<f32, PixelSpace>,
-    snap_offsets: SnapOffsets,
-) -> SnapOffsets
-{
-    if prim_rect.is_empty() || snap_offsets.is_empty() {
-        return SnapOffsets::empty();
-    }
-
-    let normalized_top_left = TypedPoint2D::<f32, PixelSpace>::new(
-        (local_rect.origin.x - prim_rect.origin.x) / prim_rect.size.width,
-        (local_rect.origin.y - prim_rect.origin.y) / prim_rect.size.height,
-    );
-
-    let normalized_bottom_right = TypedPoint2D::<f32, PixelSpace>::new(
-        (local_rect.origin.x + local_rect.size.width - prim_rect.origin.x) / prim_rect.size.width,
-        (local_rect.origin.y + local_rect.size.height - prim_rect.origin.y) / prim_rect.size.height,
-    );
-
-    let top_left = DeviceVector2D::new(
-        mix(snap_offsets.top_left.x, snap_offsets.bottom_right.x, normalized_top_left.x),
-        mix(snap_offsets.top_left.y, snap_offsets.bottom_right.y, normalized_top_left.y),
-    );
-
-    let bottom_right = DeviceVector2D::new(
-        mix(snap_offsets.top_left.x, snap_offsets.bottom_right.x, normalized_bottom_right.x),
-        mix(snap_offsets.top_left.y, snap_offsets.bottom_right.y, normalized_bottom_right.y),
-    );
-
-    SnapOffsets {
-        top_left,
-        bottom_right,
-    }
-}
-
 /// Retrieve the exact unsnapped device space rectangle for a primitive.
+/// If the transform is axis-aligned, compute the snapping offsets that
+/// the shaders will apply.
 fn get_unclipped_device_rect(
+    prim_spatial_node_index: SpatialNodeIndex,
+    root_spatial_node_index: SpatialNodeIndex,
     prim_rect: PictureRect,
     map_to_raster: &SpaceMapper<PicturePixel, RasterPixel>,
     device_pixel_scale: DevicePixelScale,
-) -> Option<DeviceRect> {
-    let raster_rect = map_to_raster.map(&prim_rect)?;
-    let world_rect = raster_rect * TypedScale::new(1.0);
-    Some(world_rect * device_pixel_scale)
+    frame_context: &FrameBuildingContext,
+    frame_state: &mut FrameBuildingState,
+) -> Option<(DeviceRect, SnapOffsets)> {
+    let unclipped_raster_rect = map_to_raster.map(&prim_rect)?;
+
+    let unclipped_device_rect = {
+        let world_rect = unclipped_raster_rect * TypedScale::new(1.0);
+        let device_rect = world_rect * device_pixel_scale;
+        device_rect
+    };
+
+    let transform_id = frame_state.transforms.get_id(
+        prim_spatial_node_index,
+        root_spatial_node_index,
+        frame_context.clip_scroll_tree,
+    );
+
+    match transform_id.transform_kind() {
+        TransformedRectKind::AxisAligned => {
+            let top_left = compute_snap_offset_impl(
+                prim_rect.origin,
+                prim_rect,
+                unclipped_device_rect.origin,
+                unclipped_device_rect.bottom_right(),
+            );
+
+            let bottom_right = compute_snap_offset_impl(
+                prim_rect.bottom_right(),
+                prim_rect,
+                unclipped_device_rect.origin,
+                unclipped_device_rect.bottom_right(),
+            );
+
+            let snap_offsets = SnapOffsets {
+                top_left,
+                bottom_right,
+            };
+
+            Some((unclipped_device_rect, snap_offsets))
+        }
+        TransformedRectKind::Complex => {
+            Some((unclipped_device_rect, SnapOffsets::empty()))
+        }
+    }
 }
 
 /// Given an unclipped device rect, try to find a minimal device space
 /// rect to allocate a clip mask for, by clipping to the screen. This
 /// function is very similar to get_raster_rects below. It is far from
 /// ideal, and should be refactored as part of the support for setting
 /// scale per-raster-root.
 fn get_clipped_device_rect(
@@ -3943,71 +3820,16 @@ pub fn get_raster_rects(
     // Ensure that we won't try to allocate a zero-sized clip render task.
     if clipped.is_empty() {
         return None;
     }
 
     Some((clipped.to_i32(), unclipped))
 }
 
-/// Snap the given rect in raster space if the transform is
-/// axis-aligned. It return the snapped rect transformed back into the
-/// given pixel space, and the snap offsets in device space.
-pub fn get_snapped_rect<PixelSpace>(
-    prim_rect: TypedRect<f32, PixelSpace>,
-    map_to_raster: &SpaceMapper<PixelSpace, RasterPixel>,
-    device_pixel_scale: DevicePixelScale,
-) -> Option<(TypedRect<f32, PixelSpace>, SnapOffsets)> where PixelSpace: fmt::Debug {
-    let is_axis_aligned = match map_to_raster.kind {
-        CoordinateSpaceMapping::Local |
-        CoordinateSpaceMapping::ScaleOffset(..) => true,
-        CoordinateSpaceMapping::Transform(ref transform) => transform.preserves_2d_axis_alignment(),
-    };
-
-    if is_axis_aligned {
-       let raster_rect = map_to_raster.map(&prim_rect)?;
-
-       let device_rect = {
-            let world_rect = raster_rect * TypedScale::new(1.0);
-            world_rect * device_pixel_scale
-        };
-
-        let top_left = compute_snap_offset_impl(
-            prim_rect.origin,
-            prim_rect,
-            device_rect.origin,
-            device_rect.bottom_right(),
-        );
-
-        let bottom_right = compute_snap_offset_impl(
-            prim_rect.bottom_right(),
-            prim_rect,
-            device_rect.origin,
-            device_rect.bottom_right(),
-        );
-
-        let snap_offsets = SnapOffsets {
-            top_left,
-            bottom_right,
-        };
-
-        let snapped_device_rect = DeviceRect::new(
-            device_rect.origin + top_left,
-            device_rect.size + (bottom_right - top_left).to_size()
-        );
-
-        let snapped_world_rect = snapped_device_rect / device_pixel_scale;
-        let snapped_raster_rect = snapped_world_rect * TypedScale::new(1.0);
-        let snapped_prim_rect = map_to_raster.unmap(&snapped_raster_rect)?;
-        Some((snapped_prim_rect, snap_offsets))
-    } else {
-        None
-    }
-}
-
 /// Get the inline (horizontal) and block (vertical) sizes
 /// for a given line decoration.
 pub fn get_line_decoration_sizes(
     rect_size: &LayoutSize,
     orientation: LineOrientation,
     style: LineStyle,
     wavy_line_thickness: f32,
 ) -> Option<(f32, f32)> {
index 7e36607b04bcceffac9372814d976f1f9f969ba4..543ffe227fc5cb9a6d592215e14d4763f9dad1f7
GIT binary patch
literal 11200
zc%1E6Ygm(KwzUWX(s~I!Ql?T6iO7IZiEV`-0Vh@nnWTacLy4&u%xHT?xx^Sq$Y6CW
z#R76Qnu~)#gm8kIb_Rlw(Ru+1iCoeS=MZAKB;+eil#3w=B<KCmajZK3&ad-;56^>1
z_V=#6_S$RjHwB*%cD}XzH_MkSS@KqV95!*uk~cWuH_mMtc%>O_&s?%3_)I)DcJ~S1
zFFfM-A5E!0m?K1Gha9)hZj8rb-@g5R=PKu$`C~EfpIW=;owyZiw;sFn@bCGau4@o0
z*19cQmRP!UT`XelJ170(di;*;Ept2a$y@))o@R8?C2Y*++q7*e@_*$t5m`e&%Y=Fj
zt!ZZD#z&b^PwFvSPmpjyW3}UxVYTI6HC^)Z#Jwe<jzk<YRyLOqH0EMhd}HyA|Km4&
z?}~~Xr2CY)n_lysBiihHa{a1~3ZZ@4xAx$1kyORf74Ih1afXSkKl*F<%0X4$lj87h
zj2=bNh0G|DtY{*CaCOwK6K8mLptRfUD#!OgzKq^6PBvHCk0KgOQS{86kFY=aWUdis
ztu|BaD=&A+2ji<8J?Y9sRaC8fs=w3crsru!2f0zXV~}CNoDFqk22$)ht0EG%O_ke~
zNr4P!;@d;(XxAdWGPGlk_hoZk{A`QwB7j8zivSh@ECN^run1rgz#@P}0E++?0W1Po
z1h5F;|2Y5$aT6zvf1t|oIAi<s7lqQf)g4>BxQBYXQI!FbNNaDmK%yiS`cA24@i1Of
zxxM|hcv(YL7%0u7*R$<%7=^CdqEfBF=*;Z8E?%vGfolsUye^4JSL)o3d-^vDW1WJs
z3M_cxD8Zu?ORw$kO|FiNsBabDDYi<cxuIE=IYU8AB0@wkx(PPk_?>b;J#@Ie%ds!p
zQiZmh+{CHcQIKRlEtX)d_bxahqx;~@BWoR~^N}s<PgFlz5mBga`N|gRIMXvmHIA;V
zl2<wf<ttot(#V#?gkDcI`=<t?WwTEHXBAAUlwdGnUPo%@&M?lj;7EMw6-oas0I^^Q
zb9-*;pU25<uO*su18`c=qBsvel~9T`Ny7Hh6LoWIrc_q^GYJg+8%irN1X@V^Zc<(9
z4O~rY-$0Q?&st!ViR0+&%g`qSmzzqYd-#RTrb*5_6#H0c`zWU^*Nl-XDltx6oqr!*
z#=Kj4J9#HnHqfWGR9N&|%ED>&dVa5#0gV<tEVzXp4N1}a8(ZgmKd$1m6oxsD3Uka<
z`LN?lhrm%}p=~J}-=>}-)HCk9{=COp4y2LWDn~ezu4}$@PShc$xY3>bH30<>uKtb~
zOV6B~_+z@=`w&+u*7Ffe5<+TP{&EdlXI&i=D3jf=lKtclt|80ZYZN<lsQLS<wS@?g
zG2tV04o7UEiM-p05LtwE=u6n7S88(y<FE<YX0~H;&;HcqHO}Ex=T*=sZc{vQxA~6P
zSTLkLVkZj?<7`kWq!sh!MC&WrteB-fvFn@c)eNc?Q^b)U4k|%(r0tLH>&b0E##hS{
z3rPh~je_~(TvoKWj0R)jI9~T0`y<Sx<;=a|1MA}aNV@F@DkHj7tm^^s(?%;IB~WAd
z;vRTHUlZuw0*moNC*>8<xH^7nn(t&#&B7uVmk<~CgBQB+D1**B0YUO%yVHmyJxwQk
z%*}&)OTqY|@l^7p&np7*y5Kt>C}f(8IdgkhAxoVTor1`_Yo`BI9%SRw@`THqxOlom
zAC)jliLWQSi$~F~v{_gHXO?Q3dsjHeu;rJ>2s(THck$Eek%j^&_1i1aV-+NAS8n$~
zNFNp0K-+IeJ@_}6mfMSE_HG92NtR>eJwIegSx87?IFNK@K*11&+6?Hm+&9>mcP9w6
zoV^Hj#d(wD-vDwBv2$h^(Kh2z`%qwl>z-wZIQM|-SK>&tnv0-o(&*E*7q?OPL>)sk
zz*kN;1~de3){>=&lupK~mz_v>bhbFhen%^H6!?DY7Uw=%5fX|plnow^1+8A5D#Ps#
z3{)8h=LD+kX#qsu)`K?TaNW2)NNw!PLS#i-c#bj2CxNTu+z;Nr<zIU!v$nD(u)#K*
zEX*AH)Woj4Y~HOh6bwxX^A8~#OHF<+8&vcf`^sj!u_;fdDRx{|-LT0VC+2LOoM}kl
zR^wcX+(F0;%&JT+4ai}z-w>CD>!&B8O{_X<Cu*cY-}-sVt>0Q!yrRiXSI6a}9MG#&
zOPFbBeuiT+FgP|U$Ewj<yNPmJuOlzbTFiz7h>nVoO^M;eOpyyLc_3Ibo}_P<b!Byr
z1~766peCgZWbLcaMUQs~=N#AfeC>%DH1E;aT4acEM28P~FYY7Np7JXaJo&Ey4Zuy<
zU79T-rgCbyA%Sz0{+h?!p|V)z*ZuPTZrZH*8Sf4S0Ixn6f39q>lUKV234}}iYZ7Q8
zyu>&c)v6#*@|vMuEtT?$N)FG}W9Y2%%-FdS?e`{CBzR0VIi*I7n#5Nk8-gj+c1U1=
z(sXSqNDw^J2<V&_ekprJAALX{gt^bOBNIcWI%80N8jwD6Du9t%p5(8oGWExEQ3RSq
zOXTxLi}aU)y>{AwXkJnOmiEzvqT<Apvp==pgN9V<ikPYxW5;Boj9Hfsh(?gV4IuEd
zMX13Dl|UDv3Sy_2&D}-WFH4L#LUb5LE|<GJUYb{I*h|AL2rVGtYu+e97xG<zC_Bq=
zWJ6CYPkC_2(nz?^L#n<W+9j1Uj@NwuvMzqZ(W%z@)~2n(jG7<Wq(w0}KT73)7@)IJ
zNrjpB(`3Np!f7p8lIStqw!k3v6SL}dcvSWQfX8F_%-nl<oF4#~ny%~q6#hBd5i+5m
z<xgM*5D(0>*5?v3@1gur1w*xV?~TAZ+yh?1VpRF~)`ooL`zRN0j!ZfkSpeW<e0O_m
zRYYs*`K0Ry<9}3*kNafZ6{F}xE(-F7-LzGlD{)5r%09orIP}FY@$6qBcB#us)SXQ|
zFXmNBggqJR%`}4^N)mLUs?G6jtq23@WZ+4}3)*2Bs^0UhWo>_<@KJqU?`pbHo4SJn
zv(}+T2vy8F=6xU>ps2Pcgt{~Mrf01Civ^$P0tFBK%6Bq%cAmV`4{QPL{rw<~Zv#X;
zMXlm2gqtpkxU5uW)(Xn9=M~yy%@odWb@<KaGg)|-I_q(7XOPFR>)d;NZOZaX1pgAZ
zgBLu$@or1tAKpcX;Chfzpd$bu6Cq8bWY86+$gzqT|AQxAU?j{j2`c25*KE2bGiT9}
z4_3!H_jae(aj~Y3#vRPc!=dTjf@Wx3Y9$Xt%&k??eaxLX8+sY1lV5y_eAWx_8oe&>
zswv~RiIHT+J}Pff!21ZYFtb7ucA#Dw)PuG*=2ZsIumU8qA?AI`zKVx0C?#yZ_E*X^
zWBmfN>JM*7SEiQJn1B6>&Zx}%K!}7Q68S)QZ6>nhl-T)2iCDsXeQSBEFvI4KT%lTo
zeWY;tup8_%rz6I=sSmhu_r!UXWtKDrc*(qB7o`PpJPD?fz!Jd~y+e9Y><<fK2p$U6
zcr%oGo@O8vl4-PpBq5@0WIBY`dH9E1!5H^_BoHVHPxE}-2Ky4p0X^Pzf7#;$c`M~4
z5-d>vlC7J<`OIoYXLZ_GMSG1}bh|!x{}a{<p&FF=1a-G6%Cipp$=AGnk)N*C{sK~<
zS}H(2g{v_CFmG)&gRbyDA?pk8^DY=*$2Sm}8$ABoQ@t$%0`w_pJ@E3Dd#Ku6@vxvI
zNTLfKw!til+aXGj@Ox1-oy9wLya|oGLM(`>>)Sr^CP`COE2BYr0Hh*r=Zy4ry-j;&
zVLOFI0UiR>srMOHWrJ!q*`Zf7ks0oF@eradfy#pfq$9g7TR-(|6WK!s0$ci8bE?g#
zeC#8v{>aceJ07^=?1iR?Xq@*~z9!JGc^}ddH>Yr$nuB)n;gujDN7&z+Qf*BJChxGR
zw`rnAwq>YQ&svpPHJb%z^11xU?8ZA^`Tmm2)BH?uhm!nDg4r$gxz)0;1Uj;pF<rZD
zCb^n1Js^zL03<m?gmftz5w~Egddjeh4(H*pzND!~*vfYzI(;OvX_p2g7aH`MNmiB9
zq;>(mrfGDqDSo(NeMxA^(@BATlT!yv<NJ?*G`@I$?h?lJX=xuaYuBs=jn!Eluo<1R
zQ^?*mawKKAp_OOTUXC8)-Uyb+JZd}yZa6TwsMlumU_$O5jf%#(ufaUT9HN`_5bn}?
zyn&GLdm30g*!ej-4}oTvUIDr|Zsl5SbvF`C@xXihkSF{%f{th1?PS0W{}H<nh|E0@
zGmZ%`u~W32KIG%gP-bc!?=K96H>q$?)pkG`JY)Vnvu>yi^zWP5UUANdb)Fy@&v&&3
zATjM+H!QJZGwY*ag_%YJ622){G(&y<YOY8T!LQ5_jNM4&{8)~j6pZ_@m-V_3PvJot
zfh4o=0c=}ip90%jCdupXYZLx{K7`R<y1vJTqe|f2OmVoANDH1}Fk#yii4@*o7xZt0
zjZ-;5S%{wb6u3lav-O;9J{i-6`ffC;C-*`^g}1uG7y5J!1~RSBfW+pWh@h<F`Q^u+
z`ook9EQkMT|7CXL*h#UkOiP`S-Y88!1L0~u&nG}i&o<6q#!R=8g`;h8HJ2F&m&u$H
zZ#^?7FIde-9Z!G?^2NU9Y!fX>+leA)UXfsDr0ET4u&)TiNRAxdNJBzfVV5^3CX6`^
zxiH~bv2f*zOk1OImQ!Ni>47#aWhRK?N7>XOO*43;@Uir<`(j(?97+MWQZgQMjRYDP
zOuMeoUO3(~rv%4)O7cdTrm22aCh`wG{IQDE>!9*It*wy^k!jYOqT^Z7^}yHQdd0d!
zxP_I+auRkHiWclCh_v~uBy*n8XNz#GC^aW(Z~bX-eF8(P&zqpntEA{AthOs<<CM&0
z+3o?`H(UvRIww42Ks5}G8MY=8=D6Iv;sRq5w?^|@Qf5;+cmR?4!2y9Kir&jto=73?
z4ot$hBJ#0&kd2Ygqs=T8i2l2>IM+%LSQ?{|PRzPWO*y#uT~Dr-iKYk8)+jo%p{!o`
z^FYNFf~?Qmso+?}RS0(GuyI&&wl*NSyXs;qyosh@65J0$9%1Mls-Y`|jhlM_TzSwi
zfI3$+L~;VIxpAKe9<=S3;MUWc;jzEfmfvKY9Hh-idCrMl!~$SI*T0D0Ed<pAg`^|Y
zsHT22zNIwx()toN?j_0Fq~~Wl=E2#4y!j3igK3mn%R@_o3X5(}gzL9>UDNS_2tWxd
za*RP5!<(>yUF*A(z3iFdT=O2nij(?Up->f=JWk9*p_uL(MMU@MM$2z^vBBAc=V%|#
zD16}2j;7#gS0kyg%JUw%0+oNb)piY_PMa!stMLfXA*RXv@(5AO`4573QGj>sCRLl^
zy|N*g;Q}a!58j|OE`u3gIsX7Kep6X&nLvxt7!qJ?^k68qTQJT}5B-A>L-pa3RF|`e
zyGa7~04L!7tS6y$sEp$uEgU|31{{7?>^n0s4=Gh@QF+W>Zo)axVd(3;c!LxeCVFqT
z1s~m)p>BdL*GJff7H4g3eHZ2nGr1n{byL04HjzgcO-Zrf5@!5%6<t>_#89Mv!>mLZ
z1!GgQ_^RmKoqmDOSDG`@)_hp1moYi4^6+CbBhjr?je&&&w@PMR*vB(LnA?Y$U1|+9
zs78SyP#ytJ&d2&a+oRJot}x&C@+M|Cray%4&95^@q7&R}PKnd6*g$3Q#*m|f^Y2Yk
z9B44Qs%WT^pc1!$e=cMa<CpN!o|p{KB@=U3Rqaw)pM~rXZs+|~)%^qI;rf;5P7{m}
zM`Q(mza8flwK|+{cr!&m3`H;GY$=2FX$1Pr?c)pUiSXz~AK7=vpQ4*vm)9Zxvt3a>
zu2DyT!GMtTa+RMwKQsxxLQJ|JwZsb93|Su+(Z3Czl%!2K&=6o99eNC@z`i<#HJ#5|
zUIVioYrxZnCssU*@58V`{2RRaSrM8a1m@UH&-|#QgrtIj47EnxlBUtOs30Jb!1)z%
zaH+52Xpx>Y#xc4tY%D?x7Z}UR&t^u1%6UA+9ozr~XzO*JED%J9TBFbx!M31Ypt||j
zf%5~u4g|9`sHE5H3ZtJ@8BSA3)s43&njs^!7P!$qoOAkhP+@elY$dEt)%+k(r|VVg
zV*rjcAF{e5IEx7{c9UNSWC%65{pwgqP@h}Lk$v2$Yvu(HwFoi5Lt|1r{8Vi4seTbY
zKRb&n=!OKdmG(+N&lLAz!PSdOxCd@48E0W~Jmkj!Ip^?&ekxWwM2m1AJ#tP5_z-NJ
zDF)Xnop(tYh=H9FO=IlWqZbakLj_}YU=-RZG}ES%g^5m<HCZ>Uz_l8L;8vHR#$ab3
zy!|X-DXhAxJOk+RZ+M&jWC&HU1Xi*v#(1IYN7{@AOGio~bvM?7q9gh%9&U?h^+apQ
z3q5A3f|uPAb@)Fb<PgmOAL4FnY{#)azsFNK_n!V6FyO$2ObD>T<deZbVZNP5Hskeq
zI-e~xCknW3vI_gcf8YWS>!q1ae9+_^ep=&9N1kA$=#@%F=t7)T4B{@PZOWJC%BNOD
zi-TkN<uS6Dx)@32RiP=HON$uE);V#Vg7#Hhk~q^rofya4r3*xSM@2{DnCnR%`{+Ww
zm<+^$i1Py1uh!HKFh#N|#aU6UOEVF1?&4GR!8cpaD@*yNFM8qR=6{5fD-|O*YxG<d
zys4D9)xe!R_HB&_mBts*v`fQX5}gVz0k{*{oB0tW^U)gkMt%1vSd>_PXpjBys9=HX
z-a?xrOAG@i?}9lg#w<O!99-CA-IGYS8i4C$&>bJcnmTIN0ZqL-Kz@^^?L-Tk7Pe=%
z04K|}y_=>f`PuMHF4-7e_hQKW4<KX%+T$k(&E$MY<OBEi0|!nz>HtHyN7#;2{DrR#
znq{2zaH0Rkv4KLbXLeP0n5+{%tP~-PZFQViNAc<oA8-czpHuif^yUi-o+Lj9ix4f(
z0wT=6L603ZYs1AR(1bArC+3Ed;QTcA8aTa2*iUXX{7(5k|K`7<TIIPQ>f`9_&7(r}
z#kMbXc=#d?K8=6&?`cME0ypmPkv|*oJk3q@{JZdeCO7B8C1)wNcVKLrr82}ga5|B5
zFW}n%e?<Y}KyK#|u|bkU=aszTwx`LNIS!EATP#K5^Du!M{M88fxF3A$2K57+-uU3@
kT5#-m;PLjUW%iZd`&=wt|3Tl#67UnhgMgK6|Lo-d0_o|x=Kufz
index 102ed077b6862077b415096aa92e79714acb2265..4ce47d9b6980e76bdfa3cadca09402b0cb9f1c24
GIT binary patch
literal 3827
zc%1E5ZA?>V6uwxc%<$z0C=*-77!8=pL>w8=0u~rL5e5nau`q%-$^?QU6-q%CkPp2h
z+7VnsC1i?PD5%#04%&sVGKO}<*(x8SXaN;kO0g}awCpVu=MOg!-9LWZyyreS_ul86
z^Stjlw;(9c-(uy*D<KH72>6`%B?Oshz@;-Y1&H0Y6$%KlYz!cJho&>6y)O#rk+%GP
zYOqVt?&bN`_CCpG-klG4o0sRCM|95IR^0Ik+Kr;iqLR;qoJ_fp{FSzxqaUL1T2+-;
z;pFDENjfF7#so?sVj!Oa59qtq6v%9=0<zvGG1+vf4du1H1f3n^U<FkyIV?FWIV?HM
zIPl~m5|uTk%OGDnsOFoBY7kZbUK(pm5l7}E0YFfWsKnkhR?7G>GMfYdt+qO<{tqmf
z;nvwDVE#DWgvLcBNXLit;m@Sq!Wz;jY=QAikp6ZN0J2_TIROB8YJ#+#4uFv-!saX~
zOmI70gCzA$#-4|<k@qqi2z%;JvypW>0g!JMnK4yQ*psfVK{k8}fTDGg-QB~mg=PDx
zm!V`XP0eME4eTcm2>=()2}Ct`0I+@a4Ou-30QONNZ72}1RVI)}`v5@5x|25e1pwru
zI(H&w{T|<g5_q+ylBfsyu#E|MWiSZ)07i*kW$7=7%a-1F>|cV;E391hXVt%2xmdl1
zNzi@2(K)OYUpRlZo$aS=8>Tf6lkP7YG`?SOe3`4GDp-<J@w5tBtkwVMCjRh=J{juq
z<wEM(`70isu42J-ty&A+z4|rQ7qi|GU-{90#JNx_k~0}j(KT8{lMS@yRb);KjSJJq
zhdwloi`%r}8s$wRjhueDtJ@UUOw0_cgBP$h%u8{~x?3C(y4KZ3yIsLcp12u-LnY2s
z*g4eR=8E4Ut)DYQ{C4Fq6^aa*BjV<#@16ESXi7qv;f7dlN5n*%%)2}`ficQD#Y76W
zXp5qwwxca<q_PdQw>5tE&}z!D@M97uqdq#eDmpePc$QUej;RG6F6TUIQH~edk2na-
z9jr#O3oCb(wz=fCq1>;$)m7Fz96O*H$J_c#%3{h&*oNd@SXC0A7w6Y}whhHUH}mk0
z?1e7w;)mDphjAoXYzheD2Rn^HtkG2y#e)V$zZYj36Gm?^Mrq?8hbxIUEkUPwwFZ~%
zmy~%+ayAB~gbsozttrvyx4QBgQmL#=T^Il9IS-c|E{d5Ve&R5jCu@p${VK!5*(<Ey
zZ2@1)j3MJ@qn}l4Gu;#9uVvR3lA)gFi(>nRtNcX>)~R%-y3XyNY$O*OgLoHtyjF=?
z22?Ud`F~Y{@q8IOF!t;{R08pfXSZM8>CwsXhODN!om=tL<+9iLS(UgsF50R;c^#%V
zHQ}?XuN%nC+aU8=L}Iz<ZZ_@cj35~vPx$R@(%il^&!6Uqck$8=&1N&$Bq4NhR*OCh
z>jWB@N#cQpb&sCLNBs6-(Q6_$%+BSD{rDR+ZpTwuJ={LydAER3;pHK$6POB&yq!Zc
z{nk_k8@IrUMO&g8AXYsU%M;Gfns=0Gfw5E;1K&G$P6v(I9azvgY>#)G9@VM|({jNt
zN%tq^6$D|r<`&Y;(Z=S+3nDV)R|vv-%56l%27pc{#2tAKy9P3jONRvar~g8g;t-W4
z6-*ndZ^<Gy7%pH8qLKuF;X-|aEMNkFiAT}~_5nbZNG5Srd@RFV)Wu2C)&0tctD~BG
zup4^riMoo`0iY$n+A|9Ppf_}#-vEHtqH3vB$u~t0e0J&v(#O@3gcFLN5YQxJb0aws
z8RJ(0+sQi!Xx_28VeE)|sn-D8GfDy1S0>IMdBAu9yISg-X!#&PSDzP51v}6#wP8Wk
q91;u54Tiq~vw|+I`5=>{lGM<6oL!q-77l)9p#a}NVvSGq*}nlt`uw2)
index dba46d8899047ff1a09ca3b7ab8cd17ded292138..a057fddec5c0ac561376e79436b41cc6fae447d7
GIT binary patch
literal 3829
zc%1E5e^AnA82=h7)LGEgoR#&>b+@|O%3HT$SANm7b9N<dVwh&&xJwt7tr9dKM4hcQ
z$L{vSe!{wL8djoC&{6|xle@XawE|5OVWutw#S%q+?gi$oTbK0XPygUv-p_sRc|Py^
zJn!>7-{ad8<7dxWI17TH*$E%UeGEYX4)Akff&r3$x+x5T<}@Y5#gOvOnz|l}zTX+D
z)Tfh@gTE}%;R<6hb0SuBEWj7*zKL12(7VMK|CklLvdezsVd+S3xc-V{psC)aLamm<
z&T0)-|47^Iy7NAygSVLjmKNwx(U1aN02N}PfIt|UfkB`^92>$cwL-Y)X~HyNnlMf9
z5<+B7jWU#Sy^di_;TjJNe?k{i#+0E%K^?=C$~BtJ8T9%b0FbNdMvXfGFifLY5CI@_
zYdh+58r{w4TY`Nir`^^5*RLlutJ4kq#e>>?CYKWcEv1?zQ2@|h-)C}+1K^kA8iZru
zQ}Fq<5|z+SJFz||bpx}Cz-Y+-S)$|vU;{I?ktrrH^2Tc=tz`gMo0-ZQ0}T9%HX&C!
z+7s_?4TX_9hJgg+TK3XaLLdgaq>eGL1E?GMl-?!=z?-Y<Mh(dTu+ZsBJ^;{et!#^#
zmqCVt+>r{xRx@@Uo0`iA+7A9WZUzDjy`#S$x)xGRQQKEo<=Z*bg5JupkIcODHaXOq
zJaj>ainZ_pez>HKSD;IZ;BcFe;w)GaNwy%froR8kaEG+fJ+96fo6%S4wO}CT&-kBN
z0}AyHxtR~bqDBX&R2_kX;IK+;NcAp{lSmRAM!jm_%x6<CB9ZcgUY+2?@S?UVOMr2k
ze?m60+m$U>(l`(+$>&J;DDb7dPu`N=phngSq6SdyXFQxKlaXq**6>S{Ds0ebxz7=y
zcjilbpN!E>m^)H4>}0`mEy=rAwdwlZ=@3`upDa8}$}eRO{U__F>vUNwWKVu|VXbAH
z>|NLHqN~??I35+eWe>WYSdyLWeymz<mCt?4`a6)fYS~mv^vOo1y}f}c=Bwwj_oQT+
zFZo<02`qua1)2qp2|1o^AdE@kPz^JYwHO3fZj;}g$KHcuFWqY4iI)3}8Bj??^Qjm!
zFH(&z7wFa&=};N_wMJUc?F-0`c=M@@a|*F1KfcB(GD~uUWv6wh_%FP)%U}7sWN5FO
zXg&9@K4sAJ1~l`YJb|C;DzRIm6q&&a^bHbx@<^d7q!3&DL4Ga2lqj^NEcU7NgbBaN
zcOQETvEdP3TJ?J`xD<<AwdANtlVO#zu5{v)XYf;>=o#VYGj$^CZk-#T<xa*;hxs?A
zGm!(u7!OVUo4*ULukR0J9Vod^8l+xyeV6&v3H(&|%w&01<}Hi!-N)O0<*=Mk?0b79
zWov+~!y+s-r*a~JpYwqHE|X`YI+ZPr%!vv&bqgd=iiEO9K@O*{lJN<)cB`v-LYBVY
zy}7~9w(xvmYzHX4q9p%?k;S7m!^WKxwJg?Bv61)E-X{xHp0%1^npQti<E9)S!UPC-
zyd4sxpjq@fdl#5C;6>uy{`W4nvqRZZLwoVw<zQ1?-DPWJF2(tx?m>$!mzzEOAb}40
zrg9DzmNM)NZuV!rTj(kQ0APZYVLJ>M_Ms~ryVTyz(i!h*w6Y=oyStmROn?65YS#~T
zX$HFiu>XFR>GlNxm^wALgaAl9n`OFF;j(`I?ea9c<0?U#_qbegsNY6Ac4mDV%h5oP
zHXN#uu*LvTwkEBS4H)=C<r2_wXvc~UI>$5Vgrg&}>|uQ@y&@5aA&aG>j(i|S7elY#
z4*;T2%5WY5a`iXF+|Diq_V2|6Eu0qaV7XU<dq@wM?;B^ph{IFgdn4L6>i`UftevlK
blr&Q!PG(;&(DkUm=PZ=4IWewk6SeRkRAb?b
--- a/testing/web-platform/meta/css/compositing/mix-blend-mode/mix-blend-mode-both-parent-and-blended-with-3D-transform.html.ini
+++ b/testing/web-platform/meta/css/compositing/mix-blend-mode/mix-blend-mode-both-parent-and-blended-with-3D-transform.html.ini
@@ -1,3 +1,3 @@
 [mix-blend-mode-both-parent-and-blended-with-3D-transform.html]
   expected:
-    if webrender or (os == "android" and not e10s): FAIL
+    if os == "android" and not e10s: FAIL