Bug 1490822. Update webrender to commit 0f142521b86f201a0f0957cc852aa14923ebfc73
authorJeff Muizelaar <jmuizelaar@mozilla.com>
Thu, 13 Sep 2018 16:44:58 -0400
changeset 436187 dc885dcd5311ad3da16c4220bbae6062faaaf99b
parent 436186 501a1147cb5f656b07001e1b158a981d8378eb39
child 436188 05345cae46fa5ade0a534cac20995f6fd6342f63
push id107812
push userjmuizelaar@mozilla.com
push dateThu, 13 Sep 2018 20:45:22 +0000
treeherdermozilla-inbound@dc885dcd5311 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1490822
milestone64.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 1490822. Update webrender to commit 0f142521b86f201a0f0957cc852aa14923ebfc73
gfx/webrender/src/batch.rs
gfx/webrender/src/border.rs
gfx/webrender/src/frame_builder.rs
gfx/webrender/src/picture.rs
gfx/webrender/src/prim_store.rs
gfx/webrender_bindings/revision.txt
--- a/gfx/webrender/src/batch.rs
+++ b/gfx/webrender/src/batch.rs
@@ -19,17 +19,17 @@ use picture::{PictureCompositeMode, Pict
 use plane_split::{BspSplitter, Clipper, Polygon, Splitter};
 use prim_store::{BrushKind, BrushPrimitive, BrushSegmentTaskId, DeferredResolve};
 use prim_store::{EdgeAaSegmentMask, ImageSource, PrimitiveIndex};
 use prim_store::{PrimitiveMetadata, PrimitiveRun, VisibleGradientTile};
 use prim_store::{BorderSource, Primitive, PrimitiveDetails};
 use render_task::{RenderTaskAddress, RenderTaskId, RenderTaskTree};
 use renderer::{BlendMode, ImageBufferKind, ShaderColorMode};
 use renderer::BLOCKS_PER_UV_RECT;
-use resource_cache::{CacheItem, GlyphFetchResult, ImageRequest, ResourceCache};
+use resource_cache::{CacheItem, GlyphFetchResult, ImageRequest, ResourceCache, ImageProperties};
 use scene::FilterOpHelpers;
 use std::{f32, i32};
 use tiling::{RenderTargetContext};
 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.
 const OPAQUE_TASK_ADDRESS: RenderTaskAddress = RenderTaskAddress(0x7fff);
@@ -1599,16 +1599,40 @@ impl Primitive {
                     BrushKind::Border { .. } |
                     BrushKind::Picture { .. } => {
                         BlendMode::PremultipliedAlpha
                     }
                 }
             }
         }
     }
+
+    pub fn is_cacheable(
+        &self,
+        resource_cache: &ResourceCache
+    ) -> bool {
+        let image_key = match self.details {
+            PrimitiveDetails::Brush(BrushPrimitive { kind: BrushKind::Image{ request, ..  }, .. }) => {
+                request.key
+            }
+            PrimitiveDetails::Brush(BrushPrimitive { kind: BrushKind::YuvImage{ yuv_key, .. }, .. }) => {
+                yuv_key[0]
+            }
+            PrimitiveDetails::Brush(_) |
+            PrimitiveDetails::TextRun(..) => {
+                return true
+            }
+        };
+        match resource_cache.get_image_properties(image_key) {
+            Some(ImageProperties { external_image: Some(_), .. }) => {
+                false
+            }
+            _ => true
+        }
+    }
 }
 
 impl PictureSurface {
     // Retrieve the uv rect handle, and texture for a picture surface.
     fn resolve(
         &self,
         render_tasks: &RenderTaskTree,
         resource_cache: &ResourceCache,
--- a/gfx/webrender/src/border.rs
+++ b/gfx/webrender/src/border.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, BorderSide, BorderStyle, BorderWidths, ColorF};
 use api::{ColorU, DeviceRect, DeviceSize, LayoutSizeAu, LayoutPrimitiveInfo, LayoutToDeviceScale};
-use api::{DevicePixel, DeviceVector2D, DevicePoint, DeviceIntSize, LayoutRect, LayoutSize, NormalBorder};
+use api::{DeviceVector2D, DevicePoint, DeviceIntSize, LayoutRect, LayoutSize, NormalBorder};
 use app_units::Au;
 use ellipse::Ellipse;
 use display_list_flattener::DisplayListFlattener;
 use gpu_types::{BorderInstance, BorderSegment, BrushFlags};
 use prim_store::{BrushKind, BrushPrimitive, BrushSegment};
 use prim_store::{EdgeAaSegmentMask, PrimitiveContainer, ScrollNodeAndClipChain};
 use util::{lerp, RectHelpers};
 
@@ -19,17 +19,17 @@ use util::{lerp, RectHelpers};
 // unreasonable amount of memory but should allow crisp corners in the
 // common cases.
 
 /// Maximum resolution in device pixels at which borders are rasterized.
 pub const MAX_BORDER_RESOLUTION: u32 = 2048;
 /// Maximum number of dots or dashes per segment to avoid freezing and filling up
 /// memory with unreasonable inputs. It would be better to address this by not building
 /// a list of per-dot information in the first place.
-pub const MAX_DASH_COUNT: usize = 2048;
+pub const MAX_DASH_COUNT: u32 = 2048;
 
 trait AuSizeConverter {
     fn to_au(&self) -> LayoutSizeAu;
 }
 
 impl AuSizeConverter for LayoutSize {
     fn to_au(&self) -> LayoutSizeAu {
         LayoutSizeAu::new(
@@ -236,293 +236,281 @@ impl BorderSideHelpers for BorderSide {
 #[repr(C)]
 #[derive(Copy, Debug, Clone, PartialEq)]
 pub enum BorderClipKind {
     DashCorner = 1,
     DashEdge = 2,
     Dot = 3,
 }
 
-/// The source data for a border corner clip mask.
-#[derive(Debug, Clone)]
-struct BorderCornerClipSource {
-    // FIXME(emilio): the `max_clip_count` name makes no sense for dashed
-    // borders now that it represents half-dashes.
-    max_clip_count: usize,
-    kind: BorderClipKind,
+fn compute_outer_and_clip_sign(
+    corner_segment: BorderSegment,
+    radius: DeviceSize,
+) -> (DevicePoint, DeviceVector2D) {
+    let outer_scale = match corner_segment {
+        BorderSegment::TopLeft => DeviceVector2D::new(0.0, 0.0),
+        BorderSegment::TopRight => DeviceVector2D::new(1.0, 0.0),
+        BorderSegment::BottomRight => DeviceVector2D::new(1.0, 1.0),
+        BorderSegment::BottomLeft => DeviceVector2D::new(0.0, 1.0),
+        _ => panic!("bug: expected a corner segment"),
+    };
+    let outer = DevicePoint::new(
+        outer_scale.x * radius.width,
+        outer_scale.y * radius.height,
+    );
+
+    let clip_sign = DeviceVector2D::new(
+        1.0 - 2.0 * outer_scale.x,
+        1.0 - 2.0 * outer_scale.y,
+    );
+
+    (outer, clip_sign)
+}
+
+fn write_dashed_corner_instances(
+    corner_radius: DeviceSize,
     widths: DeviceSize,
-    radius: DeviceSize,
-    ellipse: Ellipse<DevicePixel>,
+    segment: BorderSegment,
+    base_instance: &BorderInstance,
+    instances: &mut Vec<BorderInstance>,
+) -> Result<(), ()> {
+    let ellipse = Ellipse::new(corner_radius);
+
+    let average_border_width = 0.5 * (widths.width + widths.height);
+
+    let (_half_dash, num_half_dashes) =
+        compute_half_dash(average_border_width, ellipse.total_arc_length);
+
+    if num_half_dashes == 0 {
+        return Err(());
+    }
+
+    let num_half_dashes = num_half_dashes.min(MAX_DASH_COUNT);
+
+    let (outer, clip_sign) = compute_outer_and_clip_sign(segment, corner_radius);
+
+    let instance_count = num_half_dashes / 4 + 1;
+    instances.reserve(instance_count as usize);
+
+    let half_dash_arc_length =
+        ellipse.total_arc_length / num_half_dashes as f32;
+    let dash_length = 2. * half_dash_arc_length;
+
+    let mut current_length = 0.;
+    for i in 0..instance_count {
+        let arc_length0 = current_length;
+        current_length += if i == 0 {
+            half_dash_arc_length
+        } else {
+            dash_length
+        };
+
+        let arc_length1 = current_length;
+        current_length += dash_length;
+
+        let alpha = ellipse.find_angle_for_arc_length(arc_length0);
+        let beta = ellipse.find_angle_for_arc_length(arc_length1);
+
+        let (point0, tangent0) = ellipse.get_point_and_tangent(alpha);
+        let (point1, tangent1) = ellipse.get_point_and_tangent(beta);
+
+        let point0 = DevicePoint::new(
+            outer.x + clip_sign.x * (corner_radius.width - point0.x),
+            outer.y + clip_sign.y * (corner_radius.height - point0.y),
+        );
+
+        let tangent0 = DeviceVector2D::new(
+            -tangent0.x * clip_sign.x,
+            -tangent0.y * clip_sign.y,
+        );
+
+        let point1 = DevicePoint::new(
+            outer.x + clip_sign.x * (corner_radius.width - point1.x),
+            outer.y + clip_sign.y * (corner_radius.height - point1.y),
+        );
+
+        let tangent1 = DeviceVector2D::new(
+            -tangent1.x * clip_sign.x,
+            -tangent1.y * clip_sign.y,
+        );
+
+        instances.push(BorderInstance {
+            flags: base_instance.flags | ((BorderClipKind::DashCorner as i32) << 24),
+            clip_params: [
+                point0.x,
+                point0.y,
+                tangent0.x,
+                tangent0.y,
+                point1.x,
+                point1.y,
+                tangent1.x,
+                tangent1.y,
+            ],
+            .. *base_instance
+        });
+    }
+
+    Ok(())
 }
 
-impl BorderCornerClipSource {
-    pub fn new(
-        corner_radius: DeviceSize,
-        widths: DeviceSize,
-        kind: BorderClipKind,
-    ) -> BorderCornerClipSource {
-        // Work out a dash length (and therefore dash count)
-        // based on the width of the border edges. The "correct"
-        // dash length is not mentioned in the CSS borders
-        // spec. The calculation below is similar, but not exactly
-        // the same as what Gecko uses.
-        // TODO(gw): Iterate on this to get it closer to what Gecko
-        //           uses for dash length.
-
-        let (ellipse, max_clip_count) = match kind {
-            BorderClipKind::DashEdge => unreachable!("not for corners"),
-            BorderClipKind::DashCorner => {
-                let ellipse = Ellipse::new(corner_radius);
-
-                let average_border_width = 0.5 * (widths.width + widths.height);
-
-                let (_half_dash, num_half_dashes) =
-                    compute_half_dash(average_border_width, ellipse.total_arc_length);
+fn write_dotted_corner_instances(
+    corner_radius: DeviceSize,
+    widths: DeviceSize,
+    segment: BorderSegment,
+    base_instance: &BorderInstance,
+    instances: &mut Vec<BorderInstance>,
+) -> Result<(), ()> {
+    let mut corner_radius = corner_radius;
+    if corner_radius.width < (widths.width / 2.0) {
+        corner_radius.width = 0.0;
+    }
+    if corner_radius.height < (widths.height / 2.0) {
+        corner_radius.height = 0.0;
+    }
 
-                // Round that up to the nearest integer, so that the dash length
-                // doesn't exceed the ratio above. Add one extra dash to cover
-                // the last half-dash of the arc.
-                (ellipse, num_half_dashes as usize)
-            }
-            BorderClipKind::Dot => {
-                let mut corner_radius = corner_radius;
-                if corner_radius.width < (widths.width / 2.0) {
-                    corner_radius.width = 0.0;
-                }
-                if corner_radius.height < (widths.height / 2.0) {
-                    corner_radius.height = 0.0;
-                }
+    let (ellipse, max_dot_count) =
+        if corner_radius.width == 0. && corner_radius.height == 0. {
+            (Ellipse::new(corner_radius), 1)
+        } else {
+            // The centers of dots follow an ellipse along the middle of the
+            // border radius.
+            let inner_radius = (corner_radius - widths * 0.5).abs();
+            let ellipse = Ellipse::new(inner_radius);
 
-                if corner_radius.width == 0. && corner_radius.height == 0. {
-                    (Ellipse::new(corner_radius), 1)
-                } else {
-                    // The centers of dots follow an ellipse along the middle of the
-                    // border radius.
-                    let inner_radius = (corner_radius - widths * 0.5).abs();
-                    let ellipse = Ellipse::new(inner_radius);
+            // Allocate a "worst case" number of dot clips. This can be
+            // calculated by taking the minimum edge radius, since that
+            // will result in the maximum number of dots along the path.
+            let min_diameter = widths.width.min(widths.height);
 
-                    // Allocate a "worst case" number of dot clips. This can be
-                    // calculated by taking the minimum edge radius, since that
-                    // will result in the maximum number of dots along the path.
-                    let min_diameter = widths.width.min(widths.height);
+            // Get the number of circles (assuming spacing of one diameter
+            // between dots).
+            let max_dot_count = 0.5 * ellipse.total_arc_length / min_diameter;
 
-                    // Get the number of circles (assuming spacing of one diameter
-                    // between dots).
-                    let max_dot_count = 0.5 * ellipse.total_arc_length / min_diameter;
-
-                    // Add space for one extra dot since they are centered at the
-                    // start of the arc.
-                    (ellipse, max_dot_count.ceil() as usize)
-                }
-            }
+            // Add space for one extra dot since they are centered at the
+            // start of the arc.
+            (ellipse, max_dot_count.ceil() as usize)
         };
 
-        BorderCornerClipSource {
-            kind,
-            max_clip_count,
-            ellipse,
-            widths,
-            radius: corner_radius,
+    if max_dot_count == 0 {
+        return Err(());
+    }
+
+    if max_dot_count == 1 {
+        let dot_diameter = lerp(widths.width, widths.height, 0.5);
+        instances.push(BorderInstance {
+            flags: base_instance.flags | ((BorderClipKind::Dot as i32) << 24),
+            clip_params: [
+                widths.width / 2.0, widths.height / 2.0, 0.5 * dot_diameter, 0.,
+                0., 0., 0., 0.,
+            ],
+            .. *base_instance
+        });
+        return Ok(());
+    }
+
+    let max_dot_count = max_dot_count.min(MAX_DASH_COUNT as usize);
+
+    // FIXME(emilio): Should probably use SmallVec.
+    let mut forward_dots = Vec::with_capacity(max_dot_count / 2 + 1);
+    let mut back_dots = Vec::with_capacity(max_dot_count / 2 + 1);
+    let mut leftover_arc_length = 0.0;
+
+    // Alternate between adding dots at the start and end of the
+    // ellipse arc. This ensures that we always end up with an exact
+    // half dot at each end of the arc, to match up with the edges.
+    forward_dots.push(DotInfo::new(widths.width, widths.width));
+    back_dots.push(DotInfo::new(
+        ellipse.total_arc_length - widths.height,
+        widths.height,
+    ));
+
+    let (outer, clip_sign) = compute_outer_and_clip_sign(segment, corner_radius);
+    for dot_index in 0 .. max_dot_count {
+        let prev_forward_pos = *forward_dots.last().unwrap();
+        let prev_back_pos = *back_dots.last().unwrap();
+
+        // Select which end of the arc to place a dot from.
+        // This just alternates between the start and end of
+        // the arc, which ensures that there is always an
+        // exact half-dot at each end of the ellipse.
+        let going_forward = dot_index & 1 == 0;
+
+        let (next_dot_pos, leftover) = if going_forward {
+            let next_dot_pos =
+                prev_forward_pos.arc_pos + 2.0 * prev_forward_pos.diameter;
+            (next_dot_pos, prev_back_pos.arc_pos - next_dot_pos)
+        } else {
+            let next_dot_pos = prev_back_pos.arc_pos - 2.0 * prev_back_pos.diameter;
+            (next_dot_pos, next_dot_pos - prev_forward_pos.arc_pos)
+        };
+
+        // Use a lerp between each edge's dot
+        // diameter, based on the linear distance
+        // along the arc to get the diameter of the
+        // dot at this arc position.
+        let t = next_dot_pos / ellipse.total_arc_length;
+        let dot_diameter = lerp(widths.width, widths.height, t);
+
+        // If we can't fit a dot, bail out.
+        if leftover < dot_diameter {
+            leftover_arc_length = leftover;
+            break;
+        }
+
+        // We can place a dot!
+        let dot = DotInfo::new(next_dot_pos, dot_diameter);
+        if going_forward {
+            forward_dots.push(dot);
+        } else {
+            back_dots.push(dot);
         }
     }
 
-    // TODO(gw): The naming and structure of BorderCornerClipSource
-    //           don't really make sense. I've left it this way
-    //           for now in order to reduce the size of the
-    //           patch a bit. In the future, when we spent some
-    //           time working on dot/dash placement, we should
-    //           restructure this code to be more consistent
-    //           with how border rendering works now.
-    pub fn write(self, segment: BorderSegment) -> Vec<[f32; 8]> {
-        let mut dot_dash_data = Vec::new();
-
-        if self.max_clip_count == 0 {
-            return dot_dash_data;
-        }
+    // Now step through the dots, and distribute any extra
+    // leftover space on the arc between them evenly. Once
+    // the final arc position is determined, generate the correct
+    // arc positions and angles that get passed to the clip shader.
+    let number_of_dots = forward_dots.len() + back_dots.len();
+    let extra_space_per_dot = leftover_arc_length / (number_of_dots - 1) as f32;
 
-        let outer_scale = match segment {
-            BorderSegment::TopLeft => DeviceVector2D::new(0.0, 0.0),
-            BorderSegment::TopRight => DeviceVector2D::new(1.0, 0.0),
-            BorderSegment::BottomRight => DeviceVector2D::new(1.0, 1.0),
-            BorderSegment::BottomLeft => DeviceVector2D::new(0.0, 1.0),
-            _ => unreachable!(),
-        };
-        let outer = DevicePoint::new(
-            outer_scale.x * self.radius.width,
-            outer_scale.y * self.radius.height,
-        );
-        let clip_sign = DeviceVector2D::new(
-            1.0 - 2.0 * outer_scale.x,
-            1.0 - 2.0 * outer_scale.y,
+    let create_dot_data = |arc_length: f32, dot_radius: f32| -> [f32; 8] {
+        // Represents the GPU data for drawing a single dot to a clip mask. The order
+        // these are specified must stay in sync with the way this data is read in the
+        // dot clip shader.
+        let theta = ellipse.find_angle_for_arc_length(arc_length);
+        let (center, _) = ellipse.get_point_and_tangent(theta);
+
+        let center = DevicePoint::new(
+            outer.x + clip_sign.x * (corner_radius.width - center.x),
+            outer.y + clip_sign.y * (corner_radius.height - center.y),
         );
 
-        let max_clip_count = self.max_clip_count.min(MAX_DASH_COUNT);
-
-        match self.kind {
-            BorderClipKind::DashEdge => unreachable!("not for corners"),
-            BorderClipKind::DashCorner => {
-                // Get the correct half-dash arc length.
-                let half_dash_arc_length =
-                    self.ellipse.total_arc_length / max_clip_count as f32;
-                let dash_length = 2. * half_dash_arc_length;
-
-                let mut current_length = 0.;
-
-                dot_dash_data.reserve(max_clip_count / 4 + 1);
-                for i in 0 .. (max_clip_count / 4 + 1) {
-                    let arc_length0 = current_length;
-                    current_length += if i == 0 {
-                        half_dash_arc_length
-                    } else {
-                        dash_length
-                    };
-
-                    let arc_length1 = current_length;
-                    current_length += dash_length;
-
-                    let alpha = self.ellipse.find_angle_for_arc_length(arc_length0);
-                    let beta = self.ellipse.find_angle_for_arc_length(arc_length1);
-
-                    let (point0, tangent0) = self.ellipse.get_point_and_tangent(alpha);
-                    let (point1, tangent1) = self.ellipse.get_point_and_tangent(beta);
-
-                    let point0 = DevicePoint::new(
-                        outer.x + clip_sign.x * (self.radius.width - point0.x),
-                        outer.y + clip_sign.y * (self.radius.height - point0.y),
-                    );
-
-                    let tangent0 = DeviceVector2D::new(
-                        -tangent0.x * clip_sign.x,
-                        -tangent0.y * clip_sign.y,
-                    );
-
-                    let point1 = DevicePoint::new(
-                        outer.x + clip_sign.x * (self.radius.width - point1.x),
-                        outer.y + clip_sign.y * (self.radius.height - point1.y),
-                    );
-
-                    let tangent1 = DeviceVector2D::new(
-                        -tangent1.x * clip_sign.x,
-                        -tangent1.y * clip_sign.y,
-                    );
-
-                    dot_dash_data.push([
-                        point0.x,
-                        point0.y,
-                        tangent0.x,
-                        tangent0.y,
-                        point1.x,
-                        point1.y,
-                        tangent1.x,
-                        tangent1.y,
-                    ]);
-                }
-            }
-            BorderClipKind::Dot if max_clip_count == 1 => {
-                let dot_diameter = lerp(self.widths.width, self.widths.height, 0.5);
-                dot_dash_data.push([
-                    self.widths.width / 2.0, self.widths.height / 2.0, 0.5 * dot_diameter, 0.,
-                    0., 0., 0., 0.,
-                ]);
-            }
-            BorderClipKind::Dot => {
-                let mut forward_dots = Vec::with_capacity(max_clip_count / 2 + 1);
-                let mut back_dots = Vec::with_capacity(max_clip_count / 2 + 1);
-                let mut leftover_arc_length = 0.0;
+        [center.x, center.y, dot_radius, 0.0, 0.0, 0.0, 0.0, 0.0]
+    };
 
-                // Alternate between adding dots at the start and end of the
-                // ellipse arc. This ensures that we always end up with an exact
-                // half dot at each end of the arc, to match up with the edges.
-                forward_dots.push(DotInfo::new(self.widths.width, self.widths.width));
-                back_dots.push(DotInfo::new(
-                    self.ellipse.total_arc_length - self.widths.height,
-                    self.widths.height,
-                ));
-
-                for dot_index in 0 .. max_clip_count {
-                    let prev_forward_pos = *forward_dots.last().unwrap();
-                    let prev_back_pos = *back_dots.last().unwrap();
-
-                    // Select which end of the arc to place a dot from.
-                    // This just alternates between the start and end of
-                    // the arc, which ensures that there is always an
-                    // exact half-dot at each end of the ellipse.
-                    let going_forward = dot_index & 1 == 0;
-
-                    let (next_dot_pos, leftover) = if going_forward {
-                        let next_dot_pos =
-                            prev_forward_pos.arc_pos + 2.0 * prev_forward_pos.diameter;
-                        (next_dot_pos, prev_back_pos.arc_pos - next_dot_pos)
-                    } else {
-                        let next_dot_pos = prev_back_pos.arc_pos - 2.0 * prev_back_pos.diameter;
-                        (next_dot_pos, next_dot_pos - prev_forward_pos.arc_pos)
-                    };
-
-                    // Use a lerp between each edge's dot
-                    // diameter, based on the linear distance
-                    // along the arc to get the diameter of the
-                    // dot at this arc position.
-                    let t = next_dot_pos / self.ellipse.total_arc_length;
-                    let dot_diameter = lerp(self.widths.width, self.widths.height, t);
-
-                    // If we can't fit a dot, bail out.
-                    if leftover < dot_diameter {
-                        leftover_arc_length = leftover;
-                        break;
-                    }
+    instances.reserve(number_of_dots);
+    for (i, dot) in forward_dots.iter().enumerate() {
+        let extra_dist = i as f32 * extra_space_per_dot;
+        instances.push(BorderInstance {
+            flags: base_instance.flags | ((BorderClipKind::Dot as i32) << 24),
+            clip_params: create_dot_data(dot.arc_pos + extra_dist, 0.5 * dot.diameter),
+            .. *base_instance
+        });
+    }
 
-                    // We can place a dot!
-                    let dot = DotInfo::new(next_dot_pos, dot_diameter);
-                    if going_forward {
-                        forward_dots.push(dot);
-                    } else {
-                        back_dots.push(dot);
-                    }
-                }
-
-                // Now step through the dots, and distribute any extra
-                // leftover space on the arc between them evenly. Once
-                // the final arc position is determined, generate the correct
-                // arc positions and angles that get passed to the clip shader.
-                let number_of_dots = forward_dots.len() + back_dots.len();
-                let extra_space_per_dot = leftover_arc_length / (number_of_dots - 1) as f32;
-
-                let create_dot_data = |ellipse: &Ellipse<DevicePixel>, arc_length: f32, radius: f32| -> [f32; 8] {
-                    // Represents the GPU data for drawing a single dot to a clip mask. The order
-                    // these are specified must stay in sync with the way this data is read in the
-                    // dot clip shader.
-                    let theta = ellipse.find_angle_for_arc_length(arc_length);
-                    let (center, _) = ellipse.get_point_and_tangent(theta);
+    for (i, dot) in back_dots.iter().enumerate() {
+        let extra_dist = i as f32 * extra_space_per_dot;
+        instances.push(BorderInstance {
+            flags: base_instance.flags | ((BorderClipKind::Dot as i32) << 24),
+            clip_params: create_dot_data(dot.arc_pos - extra_dist, 0.5 * dot.diameter),
+            .. *base_instance
+        });
+    }
 
-                    let center = DevicePoint::new(
-                        outer.x + clip_sign.x * (self.radius.width - center.x),
-                        outer.y + clip_sign.y * (self.radius.height - center.y),
-                    );
-
-                    [center.x, center.y, radius, 0.0, 0.0, 0.0, 0.0, 0.0]
-                };
-
-                dot_dash_data.reserve(forward_dots.len() + back_dots.len());
-
-                for (i, dot) in forward_dots.iter().enumerate() {
-                    let extra_dist = i as f32 * extra_space_per_dot;
-                    let dot_data = create_dot_data(&self.ellipse, dot.arc_pos + extra_dist, 0.5 * dot.diameter);
-                    dot_dash_data.push(dot_data);
-                }
-
-                for (i, dot) in back_dots.iter().enumerate() {
-                    let extra_dist = i as f32 * extra_space_per_dot;
-                    let dot_data = create_dot_data(&self.ellipse, dot.arc_pos - extra_dist, 0.5 * dot.diameter);
-                    dot_dash_data.push(dot_data);
-                }
-            }
-        }
-
-        dot_dash_data
-    }
+    Ok(())
 }
 
 #[derive(Copy, Clone, Debug)]
 struct DotInfo {
     arc_pos: f32,
     diameter: f32,
 }
 
@@ -1037,49 +1025,40 @@ fn add_segment(
             //           that is dashed on one edge, and dotted on another. We can handle this
             //           in the future by submitting two instances, each one with one side
             //           color set to have an alpha of 0.
             if (style0 == BorderStyle::Dotted && style1 == BorderStyle::Dashed) ||
                (style0 == BorderStyle::Dashed && style0 == BorderStyle::Dotted) {
                 warn!("TODO: Handle a corner with dotted / dashed transition.");
             }
 
-            let clip_kind = match style0 {
-                BorderStyle::Dashed => Some(BorderClipKind::DashCorner),
-                BorderStyle::Dotted => Some(BorderClipKind::Dot),
-                _ => None,
-            };
-
-            match clip_kind {
-                Some(clip_kind) => {
-                    let clip_source = BorderCornerClipSource::new(
+            let dashed_or_dotted_corner = match style0 {
+                BorderStyle::Dashed => {
+                    write_dashed_corner_instances(
                         radius,
                         widths,
-                        clip_kind,
-                    );
-
-                    // TODO(gw): Restructure the BorderCornerClipSource code
-                    //           so that we don't allocate a Vec here.
-                    let clip_list = clip_source.write(segment);
+                        segment,
+                        &base_instance,
+                        instances,
+                    )
+                }
+                BorderStyle::Dotted => {
+                    write_dotted_corner_instances(
+                        radius,
+                        widths,
+                        segment,
+                        &base_instance,
+                        instances,
+                    )
+                }
+                _ => Err(()),
+            };
 
-                    if clip_list.is_empty() {
-                        instances.push(base_instance);
-                    } else {
-                        for params in clip_list {
-                            instances.push(BorderInstance {
-                                flags: base_flags | ((clip_kind as i32) << 24),
-                                clip_params: params,
-                                ..base_instance
-                            });
-                        }
-                    }
-                }
-                None => {
-                    instances.push(base_instance);
-                }
+            if dashed_or_dotted_corner.is_err() {
+                instances.push(base_instance);
             }
         }
         BorderSegment::Top |
         BorderSegment::Bottom |
         BorderSegment::Right |
         BorderSegment::Left => {
             let is_vertical = segment == BorderSegment::Left ||
                               segment == BorderSegment::Right;
--- a/gfx/webrender/src/frame_builder.rs
+++ b/gfx/webrender/src/frame_builder.rs
@@ -95,16 +95,17 @@ pub struct PictureContext {
     pub is_passthrough: bool,
     pub establishes_raster_root: bool,
 }
 
 #[derive(Debug)]
 pub struct PictureState {
     pub tasks: Vec<RenderTaskId>,
     pub has_non_root_coord_system: bool,
+    pub is_cacheable: bool,
     pub local_rect_changed: bool,
     pub map_local_to_pic: SpaceMapper<LayoutPixel, PicturePixel>,
     pub map_pic_to_world: SpaceMapper<PicturePixel, WorldPixel>,
     pub map_pic_to_raster: SpaceMapper<PicturePixel, RasterPixel>,
     pub map_raster_to_world: SpaceMapper<RasterPixel, WorldPixel>,
     pub surface_spatial_node_index: SpatialNodeIndex,
     pub raster_spatial_node_index: SpatialNodeIndex,
 }
--- a/gfx/webrender/src/picture.rs
+++ b/gfx/webrender/src/picture.rs
@@ -286,16 +286,17 @@ impl PicturePrimitive {
                 surface: None,
                 raster_spatial_node_index,
             }
         });
 
         let state = PictureState {
             tasks: Vec::new(),
             has_non_root_coord_system: false,
+            is_cacheable: true,
             local_rect_changed: false,
             raster_spatial_node_index,
             surface_spatial_node_index,
             map_local_to_pic,
             map_pic_to_world,
             map_pic_to_raster,
             map_raster_to_world,
         };
@@ -467,17 +468,18 @@ impl PicturePrimitive {
                         );
 
                         // If we are drawing a blur that has primitives or clips that contain
                         // a complex coordinate system, don't bother caching them (for now).
                         // It's likely that they are animating and caching may not help here
                         // anyway. In the future we should relax this a bit, so that we can
                         // cache tasks with complex coordinate systems if we detect the
                         // relevant transforms haven't changed from frame to frame.
-                        let surface = if pic_state_for_children.has_non_root_coord_system {
+                        let surface = if pic_state_for_children.has_non_root_coord_system ||
+                                         !pic_state_for_children.is_cacheable {
                             let picture_task = RenderTask::new_picture(
                                 RenderTaskLocation::Dynamic(None, device_rect.size),
                                 unclipped.size,
                                 prim_index,
                                 device_rect.origin,
                                 pic_state_for_children.tasks,
                                 uv_rect_kind,
                                 pic_state_for_children.raster_spatial_node_index,
--- a/gfx/webrender/src/prim_store.rs
+++ b/gfx/webrender/src/prim_store.rs
@@ -1646,16 +1646,20 @@ impl PrimitiveStore {
 
                 let pic_rect = if is_passthrough {
                     *current_pic_rect = current_pic_rect.union(&pic_rect);
                     None
                 } else {
                     Some(pic_rect)
                 };
 
+                if !pic_state_for_children.is_cacheable {
+                  pic_state.is_cacheable = false;
+                }
+
                 // Restore the dependencies (borrow check dance)
                 let prim = &mut self.primitives[prim_index.0];
                 let (new_local_rect, clip_node_collector) = prim
                     .as_pic_mut()
                     .restore_context(
                         pic_context_for_children,
                         pic_state_for_children,
                         pic_rect,
@@ -1672,16 +1676,20 @@ impl PrimitiveStore {
             }
             None => {
                 (false, None)
             }
         };
 
         let prim = &mut self.primitives[prim_index.0];
 
+        if !prim.is_cacheable(frame_state.resource_cache) {
+            pic_state.is_cacheable = false;
+        }
+
         if is_passthrough {
             prim.metadata.clipped_world_rect = Some(pic_state.map_pic_to_world.bounds);
         } else {
             if prim.metadata.local_rect.size.width <= 0.0 ||
                prim.metadata.local_rect.size.height <= 0.0 {
                 if cfg!(debug_assertions) && is_chased {
                     println!("\tculled for zero local rectangle");
                 }
--- a/gfx/webrender_bindings/revision.txt
+++ b/gfx/webrender_bindings/revision.txt
@@ -1,1 +1,1 @@
-70edb5f8a75ea1e1440ba7984cc42df9eb05ae69
+0f142521b86f201a0f0957cc852aa14923ebfc73