Bug 1424631 - Update webrender to commit 1142dfc557c319119a5117450718c5b67a93cb9f. r=jrmuizel
authorKartikaya Gupta <kgupta@mozilla.com>
Tue, 19 Dec 2017 09:15:02 -0500
changeset 396885 16240aca46cbdcc67b48e5f47bfc92ef852a3e70
parent 396884 9cd0e864b61325db4a27327da47444cd9197cea1
child 396886 f232ff38a0c68e5cd26bd7fafa0a7474671effdc
push id98389
push userbtara@mozilla.com
push dateTue, 19 Dec 2017 22:17:30 +0000
treeherdermozilla-inbound@05eec51a4ec2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjrmuizel
bugs1424631
milestone59.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1424631 - Update webrender to commit 1142dfc557c319119a5117450718c5b67a93cb9f. r=jrmuizel MozReview-Commit-ID: 5y1aWBvZ8Iu
gfx/doc/README.webrender
gfx/webrender/examples/basic.rs
gfx/webrender/res/ellipse.glsl
gfx/webrender/res/prim_shared.glsl
gfx/webrender/res/ps_text_run.glsl
gfx/webrender/src/border.rs
gfx/webrender/src/box_shadow.rs
gfx/webrender/src/clip.rs
gfx/webrender/src/clip_scroll_node.rs
gfx/webrender/src/frame_builder.rs
gfx/webrender/src/glyph_rasterizer.rs
gfx/webrender/src/gpu_cache.rs
gfx/webrender/src/gpu_types.rs
gfx/webrender/src/picture.rs
gfx/webrender/src/prim_store.rs
gfx/webrender/src/renderer.rs
gfx/webrender/src/tiling.rs
--- a/gfx/doc/README.webrender
+++ b/gfx/doc/README.webrender
@@ -170,9 +170,9 @@ 2. Sometimes autoland tip has changed en
    has an env var you can set to do this). In theory you can get the same
    result by resolving the conflict manually but Cargo.lock files are usually not
    trivial to merge by hand. If it's just the third_party/rust dir that has conflicts
    you can delete it and run |mach vendor rust| again to repopulate it.
 
 -------------------------------------------------------------------------------
 
 The version of WebRender currently in the tree is:
-f9bc4a5c263e707e3498bea47d3ec9096cc3d099
+1142dfc557c319119a5117450718c5b67a93cb9f
--- a/gfx/webrender/examples/basic.rs
+++ b/gfx/webrender/examples/basic.rs
@@ -254,17 +254,17 @@ impl Example for App {
 
         let info = LayoutPrimitiveInfo::new((100, 100).to(200, 200));
         builder.push_border(&info, border_widths, border_details);
         builder.pop_clip_id();
 
         if false {
             // draw text?
             let font_key = api.generate_font_key();
-            let font_bytes = load_file("../wrench/reftest/text/FreeSans.ttf");
+            let font_bytes = load_file("../wrench/reftests/text/FreeSans.ttf");
             resources.add_raw_font(font_key, font_bytes, 0);
 
             let font_instance_key = api.generate_font_instance_key();
             resources.add_font_instance(font_instance_key, font_key, Au::from_px(32), None, None, Vec::new());
 
             let text_bounds = (100, 50).by(700, 200);
             let glyphs = vec![
                 GlyphInstance {
--- a/gfx/webrender/res/ellipse.glsl
+++ b/gfx/webrender/res/ellipse.glsl
@@ -79,19 +79,21 @@ float clip_against_ellipse_if_needed(
     vec4 ellipse_center_radius,
     vec2 sign_modifier,
     float aa_range
 ) {
     if (!all(lessThan(sign_modifier * pos, sign_modifier * ellipse_center_radius.xy))) {
       return current_distance;
     }
 
-    return distance_to_ellipse(pos - ellipse_center_radius.xy,
-                               ellipse_center_radius.zw,
-                               aa_range);
+    float distance = distance_to_ellipse(pos - ellipse_center_radius.xy,
+                                         ellipse_center_radius.zw,
+                                         aa_range);
+
+    return max(distance, current_distance);
 }
 
 float rounded_rect(vec2 pos,
                    vec4 clip_center_radius_tl,
                    vec4 clip_center_radius_tr,
                    vec4 clip_center_radius_br,
                    vec4 clip_center_radius_bl,
                    float aa_range) {
--- a/gfx/webrender/res/prim_shared.glsl
+++ b/gfx/webrender/res/prim_shared.glsl
@@ -665,22 +665,23 @@ VertexInfo write_transform_vertex(RectWi
     // Select the corner of the local rect that we are processing.
     vec2 local_pos = local_segment_rect.p0 + local_segment_rect.size * aPosition.xy;
 
     // Transform the current vertex to the world cpace.
     vec4 world_pos = layer.transform * vec4(local_pos, 0.0, 1.0);
 
     // Convert the world positions to device pixel space.
     vec2 device_pos = world_pos.xy / world_pos.w * uDevicePixelRatio;
+    vec2 task_offset = task.common_data.task_rect.p0 - task.content_origin;
 
     // We want the world space coords to be perspective divided by W.
     // We also want that to apply to any interpolators. However, we
     // want a constant Z across the primitive, since we're using it
     // for draw ordering - so scale by the W coord to ensure this.
-    vec4 final_pos = vec4(world_pos.xy + task.common_data.task_rect.p0 - task.content_origin,
+    vec4 final_pos = vec4(world_pos.xy * uDevicePixelRatio + task_offset,
                           z * world_pos.w,
                           world_pos.w);
     gl_Position = uTransform * final_pos;
 
     vLocalBounds = mix(
         vec4(prim_rect.p0, prim_rect.p1),
         vec4(segment_rect.p0, segment_rect.p1),
         clip_edge_mask
@@ -715,16 +716,17 @@ GlyphResource fetch_glyph_resource(int a
 }
 
 struct ImageResource {
     vec4 uv_rect;
     float layer;
 };
 
 ImageResource fetch_image_resource(int address) {
+    //Note: number of blocks has to match `renderer::BLOCKS_PER_UV_RECT`
     vec4 data[2] = fetch_from_resource_cache_2(address);
     return ImageResource(data[0], data[1].x);
 }
 
 ImageResource fetch_image_resource_direct(ivec2 address) {
     vec4 data[2] = fetch_from_resource_cache_2_direct(address);
     return ImageResource(data[0], data[1].x);
 }
--- a/gfx/webrender/res/ps_text_run.glsl
+++ b/gfx/webrender/res/ps_text_run.glsl
@@ -2,16 +2,17 @@
  * 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/. */
 
 #include shared,prim_shared
 
 flat varying vec4 vColor;
 varying vec3 vUv;
 flat varying vec4 vUvBorder;
+flat varying vec2 vMaskSwizzle;
 
 #ifdef WR_FEATURE_GLYPH_TRANSFORM
 varying vec4 vUvClip;
 #endif
 
 #ifdef WR_VERTEX_SHADER
 
 #define MODE_ALPHA              0
@@ -120,56 +121,55 @@ void main(void) {
     vec2 f = (transform * vi.local_pos - glyph_rect.p0) / glyph_rect.size;
     vUvClip = vec4(f, 1.0 - f);
 #else
     vec2 f = (vi.local_pos - glyph_rect.p0) / glyph_rect.size;
 #endif
 
     write_clip(vi.screen_pos, prim.clip_area);
 
-#ifdef WR_FEATURE_SUBPX_BG_PASS1
-    vColor = vec4(text.color.a) * text.bg_color;
-#else
     switch (uMode) {
         case MODE_ALPHA:
+        case MODE_BITMAP:
+            vMaskSwizzle = vec2(0.0, 1.0);
+            vColor = text.color;
+            break;
         case MODE_SUBPX_PASS1:
         case MODE_SUBPX_BG_PASS2:
-        case MODE_BITMAP:
+            vMaskSwizzle = vec2(1.0, 0.0);
             vColor = text.color;
             break;
         case MODE_SUBPX_CONST_COLOR:
         case MODE_SUBPX_PASS0:
         case MODE_SUBPX_BG_PASS0:
         case MODE_COLOR_BITMAP:
+            vMaskSwizzle = vec2(1.0, 0.0);
             vColor = vec4(text.color.a);
             break;
         case MODE_SUBPX_BG_PASS1:
-            // This should never be reached.
+            vMaskSwizzle = vec2(-1.0, 1.0);
+            vColor = vec4(text.color.a) * text.bg_color;
             break;
     }
-#endif
 
     vec2 texture_size = vec2(textureSize(sColor0, 0));
     vec2 st0 = res.uv_rect.xy / texture_size;
     vec2 st1 = res.uv_rect.zw / texture_size;
 
     vUv = vec3(mix(st0, st1, f), res.layer);
     vUvBorder = (res.uv_rect + vec4(0.5, 0.5, -0.5, -0.5)) / texture_size.xyxy;
 }
 #endif
 
 #ifdef WR_FRAGMENT_SHADER
 void main(void) {
     vec3 tc = vec3(clamp(vUv.xy, vUvBorder.xy, vUvBorder.zw), vUv.z);
     vec4 mask = texture(sColor0, tc);
+    mask.rgb = mask.rgb * vMaskSwizzle.x + mask.aaa * vMaskSwizzle.y;
 
     float alpha = do_clip();
 #ifdef WR_FEATURE_GLYPH_TRANSFORM
     alpha *= float(all(greaterThanEqual(vUvClip, vec4(0.0))));
 #endif
 
-#ifdef WR_FEATURE_SUBPX_BG_PASS1
-    mask.rgb = vec3(mask.a) - mask.rgb;
-#endif
-
     oFragColor = vColor * mask * alpha;
 }
 #endif
--- a/gfx/webrender/src/border.rs
+++ b/gfx/webrender/src/border.rs
@@ -222,41 +222,44 @@ impl NormalBorderHelpers for NormalBorde
             BorderStyle::Groove |
             BorderStyle::Ridge |
             BorderStyle::Dashed |
             BorderStyle::Dotted => (BorderEdgeKind::Clip, width),
         }
     }
 }
 
-fn ensure_no_corner_overlap(radius: &mut BorderRadius, info: &LayerPrimitiveInfo) {
+pub fn ensure_no_corner_overlap(
+    radius: &mut BorderRadius,
+    rect: &LayerRect,
+) {
     let mut ratio = 1.0;
     let top_left_radius = &mut radius.top_left;
     let top_right_radius = &mut radius.top_right;
     let bottom_right_radius = &mut radius.bottom_right;
     let bottom_left_radius = &mut radius.bottom_left;
 
     let sum = top_left_radius.width + bottom_left_radius.width;
-    if info.rect.size.width < sum {
-        ratio = f32::min(ratio, info.rect.size.width / sum);
+    if rect.size.width < sum {
+        ratio = f32::min(ratio, rect.size.width / sum);
     }
 
     let sum = top_right_radius.width + bottom_right_radius.width;
-    if info.rect.size.width < sum {
-        ratio = f32::min(ratio, info.rect.size.width / sum);
+    if rect.size.width < sum {
+        ratio = f32::min(ratio, rect.size.width / sum);
     }
 
     let sum = top_left_radius.height + bottom_left_radius.height;
-    if info.rect.size.height < sum {
-        ratio = f32::min(ratio, info.rect.size.height / sum);
+    if rect.size.height < sum {
+        ratio = f32::min(ratio, rect.size.height / sum);
     }
 
     let sum = top_right_radius.height + bottom_right_radius.height;
-    if info.rect.size.height < sum {
-        ratio = f32::min(ratio, info.rect.size.height / sum);
+    if rect.size.height < sum {
+        ratio = f32::min(ratio, rect.size.height / sum);
     }
 
     if ratio < 1. {
         top_left_radius.width *= ratio;
         top_left_radius.height *= ratio;
 
         top_right_radius.width *= ratio;
         top_right_radius.height *= ratio;
@@ -348,17 +351,17 @@ impl FrameBuilder {
         // a GPU win in fragment shader time.
         // More importantly, the software (OSMesa) implementation we run tests on is
         // particularly slow at running our complex border shader, compared to the
         // rectangle shader. This has the effect of making some of our tests time
         // out more often on CI (the actual cause is simply too many Servo processes and
         // threads being run on CI at once).
 
         let mut border = *border;
-        ensure_no_corner_overlap(&mut border.radius, &info);
+        ensure_no_corner_overlap(&mut border.radius, &info.rect);
 
         let radius = &border.radius;
         let left = &border.left;
         let right = &border.right;
         let top = &border.top;
         let bottom = &border.bottom;
 
         let corners = [
--- a/gfx/webrender/src/box_shadow.rs
+++ b/gfx/webrender/src/box_shadow.rs
@@ -1,19 +1,20 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-use api::{BorderRadiusKind, ColorF, LayerPoint, LayerRect, LayerSize, LayerVector2D};
+use api::{ColorF, LayerPoint, LayerRect, LayerSize, LayerVector2D};
 use api::{BorderRadius, BoxShadowClipMode, LayoutSize, LayerPrimitiveInfo};
 use api::{ClipMode, ClipAndScrollInfo, ComplexClipRegion, LocalClip};
 use api::{PipelineId};
 use app_units::Au;
 use clip::ClipSource;
 use frame_builder::FrameBuilder;
+use gpu_types::BrushImageKind;
 use prim_store::{BrushAntiAliasMode, PrimitiveContainer};
 use prim_store::{BrushMaskKind, BrushKind, BrushPrimitive};
 use picture::PicturePrimitive;
 use util::RectHelpers;
 use render_task::MAX_BLUR_STD_DEVIATION;
 
 // The blur shader samples BLUR_SAMPLE_SCALE * blur_radius surrounding texels.
 pub const BLUR_SAMPLE_SCALE: f32 = 3.0;
@@ -82,17 +83,17 @@ impl FrameBuilder {
             .inflate(spread_amount, spread_amount);
 
         if blur_radius == 0.0 {
             let mut clips = Vec::new();
 
             let fast_info = match clip_mode {
                 BoxShadowClipMode::Outset => {
                     // TODO(gw): Add a fast path for ClipOut + zero border radius!
-                    clips.push(ClipSource::RoundedRectangle(
+                    clips.push(ClipSource::new_rounded_rect(
                         prim_info.rect,
                         border_radius,
                         ClipMode::ClipOut
                     ));
 
                     LayerPrimitiveInfo::with_clip(
                         shadow_rect,
                         LocalClip::RoundedRect(
@@ -101,17 +102,17 @@ impl FrameBuilder {
                                 shadow_rect,
                                 shadow_radius,
                                 ClipMode::Clip,
                             ),
                         ),
                     )
                 }
                 BoxShadowClipMode::Inset => {
-                    clips.push(ClipSource::RoundedRectangle(
+                    clips.push(ClipSource::new_rounded_rect(
                         shadow_rect,
                         shadow_radius,
                         ClipMode::ClipOut
                     ));
 
                     LayerPrimitiveInfo::with_clip(
                         prim_info.rect,
                         LocalClip::RoundedRect(
@@ -158,60 +159,69 @@ impl FrameBuilder {
                 br_bottom_left_h: Au::from_f32_px(border_radius.bottom_left.height),
                 br_bottom_right_w: Au::from_f32_px(border_radius.bottom_right.width),
                 br_bottom_right_h: Au::from_f32_px(border_radius.bottom_right.height),
                 clip_mode,
             };
 
             match clip_mode {
                 BoxShadowClipMode::Outset => {
-                    let width;
-                    let height;
+                    let mut width;
+                    let mut height;
                     let brush_prim;
                     let corner_size = shadow_radius.is_uniform_size();
-                    let radii_kind;
+                    let mut image_kind;
 
                     if !shadow_rect.is_well_formed_and_nonempty() {
                         return;
                     }
 
                     // If the outset box shadow has a uniform corner side, we can
                     // just blur the top left corner, and stretch / mirror that
                     // across the primitive.
                     if let Some(corner_size) = corner_size {
-                        radii_kind = BorderRadiusKind::Uniform;
+                        image_kind = BrushImageKind::Mirror;
                         width = MASK_CORNER_PADDING + corner_size.width.max(BLUR_SAMPLE_SCALE * blur_radius);
                         height = MASK_CORNER_PADDING + corner_size.height.max(BLUR_SAMPLE_SCALE * blur_radius);
 
                         brush_prim = BrushPrimitive::new(
                             BrushKind::Mask {
                                 clip_mode: brush_clip_mode,
                                 kind: BrushMaskKind::Corner(corner_size),
                             },
                             None,
                             BrushAntiAliasMode::Primitive,
                         );
                     } else {
                         // Create a minimal size primitive mask to blur. In this
                         // case, we ensure the size of each corner is the same,
                         // to simplify the shader logic that stretches the blurred
                         // result across the primitive.
-                        radii_kind = BorderRadiusKind::NonUniform;
+                        image_kind = BrushImageKind::NinePatch;
                         let max_width = shadow_radius.top_left.width
                                             .max(shadow_radius.bottom_left.width)
                                             .max(shadow_radius.top_right.width)
                                             .max(shadow_radius.bottom_right.width);
                         let max_height = shadow_radius.top_left.height
                                             .max(shadow_radius.bottom_left.height)
                                             .max(shadow_radius.top_right.height)
                                             .max(shadow_radius.bottom_right.height);
 
                         width = 2.0 * max_width + BLUR_SAMPLE_SCALE * blur_radius;
                         height = 2.0 * max_height + BLUR_SAMPLE_SCALE * blur_radius;
 
+                        // If the width or height ends up being bigger than the original
+                        // primitive shadow rect, just blur the entire rect and draw that
+                        // as a simple blit.
+                        if width > prim_info.rect.size.width || height > prim_info.rect.size.height {
+                            image_kind = BrushImageKind::Simple;
+                            width = prim_info.rect.size.width;
+                            height = prim_info.rect.size.height;
+                        }
+
                         let clip_rect = LayerRect::new(LayerPoint::zero(),
                                                        LayerSize::new(width, height));
 
                         brush_prim = BrushPrimitive::new(
                             BrushKind::Mask {
                                 clip_mode: brush_clip_mode,
                                 kind: BrushMaskKind::RoundedRect(clip_rect, shadow_radius),
                             },
@@ -232,32 +242,32 @@ impl FrameBuilder {
 
                     // Create a box shadow picture and add the mask primitive to it.
                     let pic_rect = shadow_rect.inflate(blur_offset, blur_offset);
                     let mut pic_prim = PicturePrimitive::new_box_shadow(
                         blur_radius,
                         *color,
                         Vec::new(),
                         clip_mode,
-                        radii_kind,
+                        image_kind,
                         cache_key,
                         pipeline_id,
                     );
                     pic_prim.add_primitive(
                         brush_prim_index,
                         clip_and_scroll
                     );
 
                     // TODO(gw): Right now, we always use a clip out
                     //           mask for outset shadows. We can make this
                     //           much more efficient when we have proper
                     //           segment logic, by avoiding drawing
                     //           most of the pixels inside and just
                     //           clipping out along the edges.
-                    extra_clips.push(ClipSource::RoundedRectangle(
+                    extra_clips.push(ClipSource::new_rounded_rect(
                         prim_info.rect,
                         border_radius,
                         ClipMode::ClipOut,
                     ));
 
                     let pic_info = LayerPrimitiveInfo::new(pic_rect);
                     self.add_primitive(
                         clip_and_scroll,
@@ -313,17 +323,17 @@ impl FrameBuilder {
                     // Create a box shadow picture primitive and add
                     // the brush primitive to it.
                     let mut pic_prim = PicturePrimitive::new_box_shadow(
                         blur_radius,
                         *color,
                         Vec::new(),
                         BoxShadowClipMode::Inset,
                         // TODO(gw): Make use of optimization for inset.
-                        BorderRadiusKind::NonUniform,
+                        BrushImageKind::NinePatch,
                         cache_key,
                         pipeline_id,
                     );
                     pic_prim.add_primitive(
                         brush_prim_index,
                         clip_and_scroll
                     );
 
@@ -335,17 +345,17 @@ impl FrameBuilder {
                     let pic_info = LayerPrimitiveInfo::with_clip_rect(
                         pic_rect,
                         prim_info.rect
                     );
 
                     // Add a normal clip to ensure nothing gets drawn
                     // outside the primitive rect.
                     if !border_radius.is_zero() {
-                        extra_clips.push(ClipSource::RoundedRectangle(
+                        extra_clips.push(ClipSource::new_rounded_rect(
                             prim_info.rect,
                             border_radius,
                             ClipMode::Clip,
                         ));
                     }
 
                     // Add the picture primitive to the frame.
                     self.add_primitive(
--- a/gfx/webrender/src/clip.rs
+++ b/gfx/webrender/src/clip.rs
@@ -1,15 +1,15 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use api::{BorderRadius, ClipMode, ComplexClipRegion, DeviceIntRect, ImageMask, ImageRendering};
 use api::{LayerPoint, LayerRect, LayerToWorldTransform, LayoutPoint, LayoutVector2D, LocalClip};
-use border::BorderCornerClipSource;
+use border::{BorderCornerClipSource, ensure_no_corner_overlap};
 use ellipse::Ellipse;
 use freelist::{FreeList, FreeListHandle, WeakFreeListHandle};
 use gpu_cache::{GpuCache, GpuCacheHandle, ToGpuBlocks};
 use prim_store::{ClipData, ImageMaskData};
 use resource_cache::ResourceCache;
 use util::{MaxRect, MatrixHelpers, calculate_screen_bounding_rect, extract_inner_rect_safe};
 
 pub type ClipStore = FreeList<ClipSources>;
@@ -82,28 +82,41 @@ impl From<ClipRegion> for ClipSources {
 
         if let Some(info) = region.image_mask {
             clips.push(ClipSource::Image(info));
         }
 
         clips.push(ClipSource::Rectangle(region.main));
 
         for complex in region.complex_clips {
-            clips.push(ClipSource::RoundedRectangle(
+            clips.push(ClipSource::new_rounded_rect(
                 complex.rect,
                 complex.radii,
                 complex.mode,
             ));
         }
 
         ClipSources::new(clips)
     }
 }
 
 impl ClipSource {
+    pub fn new_rounded_rect(
+        rect: LayerRect,
+        mut radii: BorderRadius,
+        clip_mode: ClipMode
+    ) -> ClipSource {
+        ensure_no_corner_overlap(&mut radii, &rect);
+        ClipSource::RoundedRectangle(
+            rect,
+            radii,
+            clip_mode,
+        )
+    }
+
     pub fn contains(&self, point: &LayerPoint) -> bool {
         // We currently do not handle all BorderCorners, because they aren't used for
         // ClipScrollNodes and this method is only used during hit testing.
         match self {
             &ClipSource::Rectangle(ref rectangle) => rectangle.contains(point),
             &ClipSource::RoundedRectangle(rect, radii, ClipMode::Clip) =>
                 rounded_rectangle_contains_point(point, &rect, &radii),
             &ClipSource::RoundedRectangle(rect, radii, ClipMode::ClipOut) =>
--- a/gfx/webrender/src/clip_scroll_node.rs
+++ b/gfx/webrender/src/clip_scroll_node.rs
@@ -654,19 +654,20 @@ impl ClipScrollNode {
         // reference frame and the offset is the accumulated offset of all the nodes
         // between us and the parent reference frame. If we are a reference frame,
         // we need to reset both these values.
         match self.node_type {
             NodeType::ReferenceFrame(ref info) => {
                 state.parent_reference_frame_transform = self.world_viewport_transform;
                 state.parent_combined_viewport_rect = combined_local_viewport_rect;
                 state.parent_accumulated_scroll_offset = LayerVector2D::zero();
+                let translation = -info.origin_in_parent_reference_frame;
                 state.nearest_scrolling_ancestor_viewport =
                     state.nearest_scrolling_ancestor_viewport
-                       .translate(&info.origin_in_parent_reference_frame);
+                       .translate(&translation);
             }
             NodeType::Clip(..) => {
                 state.parent_combined_viewport_rect = combined_local_viewport_rect;
             },
             NodeType::ScrollFrame(ref scrolling) => {
                 state.parent_combined_viewport_rect =
                         combined_local_viewport_rect.translate(&-scrolling.offset);
                 state.parent_accumulated_scroll_offset =
--- a/gfx/webrender/src/frame_builder.rs
+++ b/gfx/webrender/src/frame_builder.rs
@@ -1,14 +1,14 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use api::{BorderDetails, BorderDisplayItem, BuiltDisplayList};
-use api::{ClipAndScrollInfo, ClipId, ColorF, PropertyBinding};
+use api::{ClipAndScrollInfo, ClipId, ColorF, ColorU, PropertyBinding};
 use api::{DeviceUintPoint, DeviceUintRect, DeviceUintSize};
 use api::{DocumentLayer, ExtendMode, FontRenderMode, LayoutTransform};
 use api::{GlyphInstance, GlyphOptions, GradientStop, HitTestFlags, HitTestItem, HitTestResult};
 use api::{ImageKey, ImageRendering, ItemRange, ItemTag, LayerPoint, LayerPrimitiveInfo, LayerRect};
 use api::{LayerSize, LayerToScrollTransform, LayerVector2D, LayoutVector2D, LineOrientation};
 use api::{LineStyle, LocalClip, PipelineId, RepeatMode};
 use api::{ScrollSensitivity, Shadow, TileOffset, TransformStyle};
 use api::{PremultipliedColorF, WorldPoint, YuvColorSpace, YuvData};
@@ -197,17 +197,18 @@ impl FrameBuilder {
     pub fn create_primitive(
         &mut self,
         info: &LayerPrimitiveInfo,
         mut clip_sources: Vec<ClipSource>,
         container: PrimitiveContainer,
     ) -> PrimitiveIndex {
         if let &LocalClip::RoundedRect(main, region) = &info.local_clip {
             clip_sources.push(ClipSource::Rectangle(main));
-            clip_sources.push(ClipSource::RoundedRectangle(
+
+            clip_sources.push(ClipSource::new_rounded_rect(
                 region.rect,
                 region.radii,
                 region.mode,
             ));
         }
 
         let stacking_context = self.sc_stack.last().expect("bug: no stacking context!");
 
@@ -1340,16 +1341,17 @@ impl FrameBuilder {
         );
         let prim = TextRunPrimitiveCpu {
             font: prim_font,
             glyph_range,
             glyph_count,
             glyph_gpu_blocks: Vec::new(),
             glyph_keys: Vec::new(),
             offset: run_offset,
+            shadow_color: ColorU::new(0, 0, 0, 0),
         };
 
         // Text shadows that have a blur radius of 0 need to be rendered as normal
         // text elements to get pixel perfect results for reftests. It's also a big
         // performance win to avoid blurs and render target allocations where
         // possible. For any text shadows that have zero blur, create a normal text
         // primitive with the shadow's color and offset. These need to be added
         // *before* the visual text primitive in order to get the correct paint
@@ -1357,17 +1359,17 @@ impl FrameBuilder {
         // TODO(gw): Refactor to avoid having to store them in a Vec first.
         let mut fast_shadow_prims = Vec::new();
         for (idx, &(shadow_prim_index, _)) in self.shadow_prim_stack.iter().enumerate() {
             let shadow_metadata = &self.prim_store.cpu_metadata[shadow_prim_index.0];
             let picture_prim = &self.prim_store.cpu_pictures[shadow_metadata.cpu_prim_index.0];
             match picture_prim.kind {
                 PictureKind::TextShadow { offset, color, blur_radius, .. } if blur_radius == 0.0 => {
                     let mut text_prim = prim.clone();
-                    text_prim.font.color = color.into();
+                    text_prim.shadow_color = color.into();
                     text_prim.offset += offset;
                     fast_shadow_prims.push((idx, text_prim));
                 }
                 _ => {}
             }
         }
 
         for (idx, text_prim) in fast_shadow_prims {
--- a/gfx/webrender/src/glyph_rasterizer.rs
+++ b/gfx/webrender/src/glyph_rasterizer.rs
@@ -216,16 +216,27 @@ pub enum GlyphFormat {
     Alpha,
     TransformedAlpha,
     Subpixel,
     TransformedSubpixel,
     Bitmap,
     ColorBitmap,
 }
 
+impl GlyphFormat {
+    pub fn ignore_color(self) -> Self {
+        match self {
+            GlyphFormat::Subpixel => GlyphFormat::Alpha,
+            GlyphFormat::TransformedSubpixel => GlyphFormat::TransformedAlpha,
+            GlyphFormat::ColorBitmap => GlyphFormat::Bitmap,
+            _ => self,
+        }
+    }
+}
+
 pub struct RasterizedGlyph {
     pub top: f32,
     pub left: f32,
     pub width: u32,
     pub height: u32,
     pub scale: f32,
     pub format: GlyphFormat,
     pub bytes: Vec<u8>,
--- a/gfx/webrender/src/gpu_cache.rs
+++ b/gfx/webrender/src/gpu_cache.rs
@@ -27,16 +27,17 @@
 use api::{LayerRect, PremultipliedColorF};
 use device::FrameId;
 use internal_types::UvRect;
 use profiler::GpuCacheProfileCounters;
 use renderer::MAX_VERTEX_TEXTURE_WIDTH;
 use std::{mem, u16, u32};
 use std::ops::Add;
 
+
 pub const GPU_CACHE_INITIAL_HEIGHT: u32 = 512;
 const FRAMES_BEFORE_EVICTION: usize = 10;
 const NEW_ROWS_PER_RESIZE: u32 = 512;
 
 #[derive(Debug, Copy, Clone, Eq, PartialEq)]
 struct Epoch(u32);
 
 impl Epoch {
--- a/gfx/webrender/src/gpu_types.rs
+++ b/gfx/webrender/src/gpu_types.rs
@@ -175,16 +175,17 @@ impl From<BrushInstance> for PrimitiveIn
         }
     }
 }
 
 // Defines how a brush image is stretched onto the primitive.
 // In the future, we may draw with segments for each portion
 // of the primitive, in which case this will be redundant.
 #[repr(C)]
+#[derive(Debug, Copy, Clone)]
 pub enum BrushImageKind {
     Simple = 0,     // A normal rect
     NinePatch = 1,  // A nine-patch image (stretch inside segments)
     Mirror = 2,     // A top left corner only (mirror across x/y axes)
 }
 
 #[derive(Copy, Debug, Clone, PartialEq)]
 #[repr(C)]
--- a/gfx/webrender/src/picture.rs
+++ b/gfx/webrender/src/picture.rs
@@ -1,19 +1,20 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-use api::{BorderRadiusKind, ColorF, ClipAndScrollInfo, FilterOp, MixBlendMode};
+use api::{ColorF, ClipAndScrollInfo, FilterOp, MixBlendMode};
 use api::{device_length, DeviceIntRect, DeviceIntSize, PipelineId};
 use api::{BoxShadowClipMode, LayerPoint, LayerRect, LayerSize, LayerVector2D, Shadow};
 use api::{ClipId, PremultipliedColorF};
 use box_shadow::{BLUR_SAMPLE_SCALE, BoxShadowCacheKey};
 use frame_builder::PrimitiveContext;
 use gpu_cache::GpuDataRequest;
+use gpu_types::BrushImageKind;
 use prim_store::{PrimitiveIndex, PrimitiveRun, PrimitiveRunLocalRect};
 use render_task::{ClearMode, RenderTask, RenderTaskId, RenderTaskTree};
 use scene::{FilterOpHelpers, SceneProperties};
 use tiling::RenderTargetKind;
 
 /*
  A picture represents a dynamically rendered image. It consists of:
 
@@ -54,17 +55,17 @@ pub enum PictureKind {
         blur_radius: f32,
         content_rect: LayerRect,
     },
     BoxShadow {
         blur_radius: f32,
         color: ColorF,
         blur_regions: Vec<LayerRect>,
         clip_mode: BoxShadowClipMode,
-        radii_kind: BorderRadiusKind,
+        image_kind: BrushImageKind,
         content_rect: LayerRect,
         cache_key: BoxShadowCacheKey,
     },
     Image {
         // If a mix-blend-mode, contains the render task for
         // the readback of the framebuffer that we use to sample
         // from in the mix-blend-mode shader.
         // For drop-shadow filter, this will store the original
@@ -151,29 +152,29 @@ impl PicturePrimitive {
         }
     }
 
     pub fn new_box_shadow(
         blur_radius: f32,
         color: ColorF,
         blur_regions: Vec<LayerRect>,
         clip_mode: BoxShadowClipMode,
-        radii_kind: BorderRadiusKind,
+        image_kind: BrushImageKind,
         cache_key: BoxShadowCacheKey,
         pipeline_id: PipelineId,
     ) -> Self {
         PicturePrimitive {
             runs: Vec::new(),
             render_task_id: None,
             kind: PictureKind::BoxShadow {
                 blur_radius,
                 color,
                 blur_regions,
                 clip_mode,
-                radii_kind,
+                image_kind,
                 content_rect: LayerRect::zero(),
                 cache_key,
             },
             pipeline_id,
             cull_children: false,
             rasterization_kind: RasterizationSpace::Local,
         }
     }
@@ -254,39 +255,39 @@ impl PicturePrimitive {
 
                 *content_rect = local_content_rect.inflate(
                     blur_offset,
                     blur_offset,
                 );
 
                 content_rect.translate(&offset)
             }
-            PictureKind::BoxShadow { blur_radius, clip_mode, radii_kind, ref mut content_rect, .. } => {
+            PictureKind::BoxShadow { blur_radius, clip_mode, image_kind, ref mut content_rect, .. } => {
                 // We need to inflate the content rect if outset.
                 match clip_mode {
                     BoxShadowClipMode::Outset => {
                         let blur_offset = blur_radius * BLUR_SAMPLE_SCALE;
 
                         // If the radii are uniform, we can render just the top
                         // left corner and mirror it across the primitive. In
                         // this case, shift the content rect to leave room
                         // for the blur to take effect.
-                        match radii_kind {
-                            BorderRadiusKind::Uniform => {
+                        match image_kind {
+                            BrushImageKind::Mirror => {
                                 let origin = LayerPoint::new(
                                     local_content_rect.origin.x - blur_offset,
                                     local_content_rect.origin.y - blur_offset,
                                 );
                                 let size = LayerSize::new(
                                     local_content_rect.size.width + blur_offset,
                                     local_content_rect.size.height + blur_offset,
                                 );
                                 *content_rect = LayerRect::new(origin, size);
                             }
-                            BorderRadiusKind::NonUniform => {
+                            BrushImageKind::NinePatch | BrushImageKind::Simple => {
                                 // For a non-uniform radii, we need to expand
                                 // the content rect on all sides for the blur.
                                 *content_rect = local_content_rect.inflate(
                                     blur_offset,
                                     blur_offset,
                                 );
                             }
                         }
--- a/gfx/webrender/src/prim_store.rs
+++ b/gfx/webrender/src/prim_store.rs
@@ -1,13 +1,13 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-use api::{BorderRadius, BuiltDisplayList, ClipAndScrollInfo, ClipId, ClipMode, ColorF};
+use api::{BorderRadius, BuiltDisplayList, ClipAndScrollInfo, ClipId, ClipMode, ColorF, ColorU};
 use api::{ComplexClipRegion, DeviceIntRect, DevicePoint, ExtendMode, FontRenderMode};
 use api::{GlyphInstance, GlyphKey, GradientStop, ImageKey, ImageRendering, ItemRange, ItemTag};
 use api::{LayerPoint, LayerRect, LayerSize, LayerToWorldTransform, LayerVector2D, LineOrientation};
 use api::{LineStyle, PipelineId, PremultipliedColorF, TileOffset, WorldToLayerTransform};
 use api::{YuvColorSpace, YuvFormat};
 use border::BorderCornerInstance;
 use clip_scroll_tree::{CoordinateSystemId, ClipScrollTree};
 use clip::{ClipSource, ClipSourcesHandle, ClipStore};
@@ -16,24 +16,25 @@ use glyph_rasterizer::{FontInstance, Fon
 use internal_types::{FastHashMap};
 use gpu_cache::{GpuBlockData, GpuCache, GpuCacheAddress, GpuCacheHandle, GpuDataRequest,
                 ToGpuBlocks};
 use gpu_types::ClipScrollNodeData;
 use picture::{PictureKind, PicturePrimitive, RasterizationSpace};
 use profiler::FrameProfileCounters;
 use render_task::{ClipChain, ClipChainNode, ClipChainNodeIter, ClipWorkItem, RenderTask};
 use render_task::{RenderTaskId, RenderTaskTree};
-use renderer::MAX_VERTEX_TEXTURE_WIDTH;
+use renderer::{BLOCKS_PER_UV_RECT, MAX_VERTEX_TEXTURE_WIDTH};
 use resource_cache::{ImageProperties, ResourceCache};
 use scene::{ScenePipeline, SceneProperties};
 use std::{mem, u16, usize};
 use std::rc::Rc;
 use util::{MatrixHelpers, calculate_screen_bounding_rect, extract_inner_rect_safe, pack_as_float};
 use util::recycle_vec;
 
+
 const MIN_BRUSH_SPLIT_AREA: f32 = 128.0 * 128.0;
 
 #[derive(Debug)]
 pub struct PrimitiveRun {
     pub base_prim_index: PrimitiveIndex,
     pub count: usize,
     pub clip_and_scroll: ClipAndScrollInfo,
 }
@@ -443,17 +444,17 @@ impl ToGpuBlocks for LinePrimitive {
 
 #[derive(Debug)]
 pub struct ImagePrimitiveCpu {
     pub image_key: ImageKey,
     pub image_rendering: ImageRendering,
     pub tile_offset: Option<TileOffset>,
     pub tile_spacing: LayerSize,
     // TODO(gw): Build on demand
-    pub gpu_blocks: [GpuBlockData; 2],
+    pub gpu_blocks: [GpuBlockData; BLOCKS_PER_UV_RECT],
 }
 
 impl ToGpuBlocks for ImagePrimitiveCpu {
     fn write_gpu_blocks(&self, mut request: GpuDataRequest) {
         request.extend_from_slice(&self.gpu_blocks);
     }
 }
 
@@ -734,19 +735,19 @@ impl RadialGradientPrimitiveCpu {
 #[derive(Debug, Clone)]
 pub struct TextRunPrimitiveCpu {
     pub font: FontInstance,
     pub offset: LayerVector2D,
     pub glyph_range: ItemRange<GlyphInstance>,
     pub glyph_count: usize,
     pub glyph_keys: Vec<GlyphKey>,
     pub glyph_gpu_blocks: Vec<GpuBlockData>,
+    pub shadow_color: ColorU,
 }
 
-
 impl TextRunPrimitiveCpu {
     pub fn get_font(
         &self,
         device_pixel_ratio: f32,
         transform: &LayerToWorldTransform,
         rasterization_kind: RasterizationSpace,
     ) -> FontInstance {
         let mut font = self.font.clone();
@@ -756,16 +757,20 @@ impl TextRunPrimitiveCpu {
                 font.render_mode = font.render_mode.limit_by(FontRenderMode::Alpha);
             } else {
                 font.transform = FontTransform::from(transform).quantize();
             }
         }
         font
     }
 
+    pub fn is_shadow(&self) -> bool {
+        self.shadow_color.a != 0
+    }
+
     fn prepare_for_render(
         &mut self,
         resource_cache: &mut ResourceCache,
         device_pixel_ratio: f32,
         transform: &LayerToWorldTransform,
         display_list: &BuiltDisplayList,
         gpu_cache: &mut GpuCache,
         rasterization_kind: RasterizationSpace,
@@ -807,17 +812,22 @@ impl TextRunPrimitiveCpu {
             }
         }
 
         resource_cache.request_glyphs(font, &self.glyph_keys, gpu_cache);
     }
 
     fn write_gpu_blocks(&self, request: &mut GpuDataRequest) {
         let bg_color = ColorF::from(self.font.bg_color);
-        request.push(ColorF::from(self.font.color).premultiplied());
+        let color = ColorF::from(if self.is_shadow() {
+            self.shadow_color
+        } else {
+            self.font.color
+        });
+        request.push(color.premultiplied());
         // this is the only case where we need to provide plain color to GPU
         request.extend_from_slice(&[
             GpuBlockData { data: [bg_color.r, bg_color.g, bg_color.b, 1.0] }
         ]);
         request.push([
             self.offset.x,
             self.offset.y,
             0.0,
--- a/gfx/webrender/src/renderer.rs
+++ b/gfx/webrender/src/renderer.rs
@@ -71,16 +71,19 @@ use time::precise_time_ns;
 use util::TransformedRectKind;
 
 pub const MAX_VERTEX_TEXTURE_WIDTH: usize = 1024;
 /// Enabling this toggle would force the GPU cache scattered texture to
 /// be resized every frame, which enables GPU debuggers to see if this
 /// is performed correctly.
 const GPU_CACHE_RESIZE_TEST: bool = false;
 
+/// Number of GPU blocks per UV rectangle provided for an image.
+pub const BLOCKS_PER_UV_RECT: usize = 2;
+
 const GPU_TAG_BRUSH_SOLID: GpuProfileTag = GpuProfileTag {
     label: "B_Solid",
     color: debug_colors::RED,
 };
 const GPU_TAG_BRUSH_MASK: GpuProfileTag = GpuProfileTag {
     label: "B_Mask",
     color: debug_colors::BLACK,
 };
@@ -1589,17 +1592,16 @@ pub struct Renderer {
     // The are "primitive shaders". These shaders draw and blend
     // final results on screen. They are aware of tile boundaries.
     // Most draw directly to the framebuffer, but some use inputs
     // from the cache shaders to draw. Specifically, the box
     // shadow primitive shader stretches the box shadow cache
     // output, and the cache_image shader blits the results of
     // a cache shader (e.g. blur) to the screen.
     ps_text_run: TextShader,
-    ps_text_run_subpx_bg_pass1: TextShader,
     ps_image: Vec<Option<PrimitiveShader>>,
     ps_yuv_image: Vec<Option<PrimitiveShader>>,
     ps_border_corner: PrimitiveShader,
     ps_border_edge: PrimitiveShader,
     ps_gradient: PrimitiveShader,
     ps_angle_gradient: PrimitiveShader,
     ps_radial_gradient: PrimitiveShader,
     ps_line: PrimitiveShader,
@@ -1855,23 +1857,16 @@ impl Renderer {
 
         let ps_text_run = try!{
             TextShader::new("ps_text_run",
                             &mut device,
                             &[],
                            options.precache_shaders)
         };
 
-        let ps_text_run_subpx_bg_pass1 = try!{
-            TextShader::new("ps_text_run",
-                            &mut device,
-                            &["SUBPX_BG_PASS1"],
-                            options.precache_shaders)
-        };
-
         // All image configuration.
         let mut image_features = Vec::new();
         let mut ps_image: Vec<Option<PrimitiveShader>> = Vec::new();
         // PrimitiveShader is not clonable. Use push() to initialize the vec.
         for _ in 0 .. IMAGE_BUFFER_KINDS.len() {
             ps_image.push(None);
         }
         for buffer_kind in 0 .. IMAGE_BUFFER_KINDS.len() {
@@ -2236,17 +2231,16 @@ impl Renderer {
             brush_image_rgba8,
             brush_image_rgba8_alpha_mask,
             brush_image_a8,
             brush_solid,
             cs_clip_rectangle,
             cs_clip_border,
             cs_clip_image,
             ps_text_run,
-            ps_text_run_subpx_bg_pass1,
             ps_image,
             ps_yuv_image,
             ps_border_corner,
             ps_border_edge,
             ps_gradient,
             ps_angle_gradient,
             ps_radial_gradient,
             ps_blend,
@@ -3553,17 +3547,17 @@ impl Renderer {
                                     &batch.instances,
                                     VertexArrayKind::Primitive,
                                     &batch.key.textures,
                                     stats,
                                 );
 
                                 self.device.set_blend_mode_subpixel_with_bg_color_pass1();
 
-                                self.ps_text_run_subpx_bg_pass1.bind(
+                                self.ps_text_run.bind(
                                     &mut self.device,
                                     glyph_format,
                                     transform_kind,
                                     projection,
                                     TextShaderMode::SubpixelWithBgColorPass1,
                                     &mut self.renderer_errors,
                                 );
 
@@ -3918,17 +3912,17 @@ impl Renderer {
             };
 
             self.texture_resolver
                 .external_images
                 .insert((ext_image.id, ext_image.channel_index), texture);
 
             list.updates.push(GpuCacheUpdate::Copy {
                 block_index: list.blocks.len(),
-                block_count: 2,
+                block_count: BLOCKS_PER_UV_RECT,
                 address: deferred_resolve.address,
             });
             list.blocks.push([image.u0, image.v0, image.u1, image.v1].into());
             list.blocks.push([0f32; 4].into());
         }
 
         Some(list)
     }
@@ -4429,17 +4423,16 @@ impl Renderer {
         self.brush_image_rgba8.deinit(&mut self.device);
         self.brush_image_rgba8_alpha_mask.deinit(&mut self.device);
         self.brush_image_a8.deinit(&mut self.device);
         self.brush_solid.deinit(&mut self.device);
         self.cs_clip_rectangle.deinit(&mut self.device);
         self.cs_clip_image.deinit(&mut self.device);
         self.cs_clip_border.deinit(&mut self.device);
         self.ps_text_run.deinit(&mut self.device);
-        self.ps_text_run_subpx_bg_pass1.deinit(&mut self.device);
         for shader in self.ps_image {
             if let Some(shader) = shader {
                 shader.deinit(&mut self.device);
             }
         }
         for shader in self.ps_yuv_image {
             if let Some(shader) = shader {
                 shader.deinit(&mut self.device);
--- a/gfx/webrender/src/tiling.rs
+++ b/gfx/webrender/src/tiling.rs
@@ -1,13 +1,13 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-use api::{BorderRadiusKind, ClipId, ColorF, DeviceIntPoint, ImageKey};
+use api::{ClipId, ColorF, DeviceIntPoint, ImageKey};
 use api::{DeviceIntRect, DeviceIntSize, device_length, DeviceUintPoint, DeviceUintRect, DeviceUintSize};
 use api::{DocumentLayer, ExternalImageType, FilterOp};
 use api::{ImageFormat, ImageRendering};
 use api::{LayerRect, MixBlendMode, PipelineId};
 use api::{SubpixelDirection, TileOffset, YuvColorSpace, YuvFormat};
 use api::{LayerToWorldTransform, WorldPixel};
 use border::{BorderCornerInstance, BorderCornerSide};
 use clip::{ClipSource, ClipStore};
@@ -24,18 +24,18 @@ use internal_types::{BatchTextures, Rend
 use picture::{PictureCompositeMode, PictureKind, PicturePrimitive, RasterizationSpace};
 use plane_split::{BspSplitter, Polygon, Splitter};
 use prim_store::{PrimitiveIndex, PrimitiveKind, PrimitiveMetadata, PrimitiveStore};
 use prim_store::{BrushPrimitive, BrushMaskKind, BrushKind, BrushSegmentKind, DeferredResolve, PrimitiveRun};
 use profiler::FrameProfileCounters;
 use render_task::{ClipWorkItem};
 use render_task::{RenderTaskAddress, RenderTaskId, RenderTaskKey, RenderTaskKind};
 use render_task::{BlurTask, ClearMode, RenderTaskLocation, RenderTaskTree};
-use renderer::BlendMode;
-use renderer::ImageBufferKind;
+use renderer::{BlendMode, ImageBufferKind};
+use renderer::BLOCKS_PER_UV_RECT;
 use resource_cache::{GlyphFetchResult, ResourceCache};
 use std::{cmp, usize, f32, i32};
 use std::collections::hash_map::Entry;
 use texture_allocator::GuillotineAllocator;
 use util::{MatrixHelpers, TransformedRectKind};
 
 // Special sentinel value recognized by the shader. It is considered to be
 // a dummy task that doesn't mask out anything.
@@ -565,27 +565,32 @@ fn add_to_batch(
                 RasterizationSpace::Screen,
             );
 
             ctx.resource_cache.fetch_glyphs(
                 font,
                 &text_cpu.glyph_keys,
                 glyph_fetch_buffer,
                 gpu_cache,
-                |texture_id, glyph_format, glyphs| {
+                |texture_id, mut glyph_format, glyphs| {
                     debug_assert_ne!(texture_id, SourceTexture::Invalid);
 
                     let textures = BatchTextures {
                         colors: [
                             texture_id,
                             SourceTexture::Invalid,
                             SourceTexture::Invalid,
                         ],
                     };
 
+                    // Ignore color and only sample alpha when shadowing.
+                    if text_cpu.is_shadow() {
+                        glyph_format = glyph_format.ignore_color();
+                    }
+
                     let kind = BatchKind::Transformable(
                         transform_kind,
                         TransformBatchKind::TextRun(glyph_format),
                     );
 
                     let blend_mode = match glyph_format {
                         GlyphFormat::Subpixel |
                         GlyphFormat::TransformedSubpixel => {
@@ -645,33 +650,24 @@ fn add_to_batch(
                                 clip_task_address,
                                 z,
                                 segment_kind: 0,
                                 user_data0: cache_task_address.0 as i32,
                                 user_data1: BrushImageKind::Simple as i32,
                             };
                             batch.push(PrimitiveInstance::from(instance));
                         }
-                        PictureKind::BoxShadow { radii_kind, .. } => {
+                        PictureKind::BoxShadow { image_kind, .. } => {
                             let kind = BatchKind::Brush(
                                 BrushBatchKind::Image(
                                     BrushImageSourceKind::from_render_target_kind(picture.target_kind())),
                             );
                             let key = BatchKey::new(kind, blend_mode, textures);
                             let batch = batch_list.get_suitable_batch(key, item_bounding_rect);
 
-                            let image_kind = match radii_kind {
-                                BorderRadiusKind::Uniform => {
-                                    BrushImageKind::Mirror
-                                }
-                                BorderRadiusKind::NonUniform => {
-                                    BrushImageKind::NinePatch
-                                }
-                            };
-
                             let instance = BrushInstance {
                                 picture_address: task_address,
                                 prim_address: prim_cache_address,
                                 clip_id,
                                 scroll_id,
                                 clip_task_address,
                                 z,
                                 segment_kind: 0,
@@ -2107,17 +2103,17 @@ fn resolve_image(
         Some(image_properties) => {
             // Check if an external image that needs to be resolved
             // by the render thread.
             match image_properties.external_image {
                 Some(external_image) => {
                     // This is an external texture - we will add it to
                     // the deferred resolves list to be patched by
                     // the render thread...
-                    let cache_handle = gpu_cache.push_deferred_per_frame_blocks(2);
+                    let cache_handle = gpu_cache.push_deferred_per_frame_blocks(BLOCKS_PER_UV_RECT);
                     deferred_resolves.push(DeferredResolve {
                         image_properties,
                         address: gpu_cache.get_address(&cache_handle),
                     });
 
                     (SourceTexture::External(external_image), cache_handle)
                 }
                 None => {