Bug 1496540 - Handle overlapping border corners in webrender r=gw
☠☠ backed out by b1df68082c2f ☠ ☠
authorJamie Nicol <jnicol@mozilla.com>
Fri, 25 Jan 2019 14:22:26 +0000
changeset 515458 f57d14800cbb5cf9f7c135d7e32ea5728fe7b377
parent 515457 d6552069b6d952b2e032422d5bd9f46b1b81edb1
child 515459 d815471fff914f9482658278838592eaa62052b0
push id1953
push userffxbld-merge
push dateMon, 11 Mar 2019 12:10:20 +0000
treeherdermozilla-release@9c35dcbaa899 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgw
bugs1496540
milestone66.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 1496540 - Handle overlapping border corners in webrender r=gw When some of a border's corners have a border-radius, and that radius is larger than the sum of the border width and element size, then it results in the corners of the border overlapping. Webrender draws borders by rasterizing each segment individually in to the cache, then compositing them together. In this overlapping case, this has 2 problems: a) we composite overlapping segments on top of eachother b) corner segments are not correctly clipped to the curve of the overlapping adjacent corners This patch allows corner segments to be clipped by their adjacent corners. We provide the outer corner position and radii of the adjacent corners to the border shader, which then applies those clips, if required, along with the segment's own corner clip when rasterizing the segment. As the adjacent corners now affect the result of the cached segment, they are added to the cache key. We continue to rasterize the entire segment in to the cache as before, but now modify the local rect and texel rect of the BrushSegment so that it only composites the subportion of the corner segment which does not overlap with the opposite edges of the border. Differential Revision: https://phabricator.services.mozilla.com/D16872
gfx/wr/webrender/res/cs_border_solid.glsl
gfx/wr/webrender/src/border.rs
gfx/wr/wrench/reftests/border/border-overlapping-ref.yaml
gfx/wr/wrench/reftests/border/border-overlapping.yaml
gfx/wr/wrench/reftests/border/reftest.list
layout/reftests/border-radius/reftest.list
--- a/gfx/wr/webrender/res/cs_border_solid.glsl
+++ b/gfx/wr/webrender/res/cs_border_solid.glsl
@@ -24,16 +24,22 @@ flat varying int vMixColors;
 // zw = Scale the rect origin by this to get the outer
 // corner from the segment rectangle.
 flat varying vec4 vClipCenter_Sign;
 
 // An outer and inner elliptical radii for border
 // corner clipping.
 flat varying vec4 vClipRadii;
 
+// Position, scale, and radii of horizontally and vertically adjacent corner clips.
+flat varying vec4 vHorizontalClipCenter_Sign;
+flat varying vec2 vHorizontalClipRadii;
+flat varying vec4 vVerticalClipCenter_Sign;
+flat varying vec2 vVerticalClipRadii;
+
 // Local space position
 varying vec2 vPos;
 
 #define SEGMENT_TOP_LEFT        0
 #define SEGMENT_TOP_RIGHT       1
 #define SEGMENT_BOTTOM_RIGHT    2
 #define SEGMENT_BOTTOM_LEFT     3
 
@@ -41,16 +47,18 @@ varying vec2 vPos;
 
 in vec2 aTaskOrigin;
 in vec4 aRect;
 in vec4 aColor0;
 in vec4 aColor1;
 in int aFlags;
 in vec2 aWidths;
 in vec2 aRadii;
+in vec4 aHorizontallyAdjacentCorner;
+in vec4 aVerticallyAdjacentCorner;
 
 vec2 get_outer_corner_scale(int segment) {
     vec2 p;
 
     switch (segment) {
         case SEGMENT_TOP_LEFT:
             p = vec2(0.0, 0.0);
             break;
@@ -97,16 +105,28 @@ void main(void) {
     vPos = aRect.zw * aPosition.xy;
 
     vColor0 = aColor0;
     vColor1 = aColor1;
     vClipCenter_Sign = vec4(outer + clip_sign * aRadii, clip_sign);
     vClipRadii = vec4(aRadii, max(aRadii - aWidths, 0.0));
     vColorLine = vec4(outer, aWidths.y * -clip_sign.y, aWidths.x * clip_sign.x);
 
+    vec2 horizontal_clip_sign = vec2(-clip_sign.x, clip_sign.y);
+    vHorizontalClipCenter_Sign = vec4(aHorizontallyAdjacentCorner.xy +
+                                      horizontal_clip_sign * aHorizontallyAdjacentCorner.zw,
+                                      horizontal_clip_sign);
+    vHorizontalClipRadii = aHorizontallyAdjacentCorner.zw;
+
+    vec2 vertical_clip_sign = vec2(clip_sign.x, -clip_sign.y);
+    vVerticalClipCenter_Sign = vec4(aVerticallyAdjacentCorner.xy +
+                                    vertical_clip_sign * aVerticallyAdjacentCorner.zw,
+                                    vertical_clip_sign);
+    vVerticalClipRadii = aVerticallyAdjacentCorner.zw;
+
     gl_Position = uTransform * vec4(aTaskOrigin + aRect.xy + vPos, 0.0, 1.0);
 }
 #endif
 
 #ifdef WR_FRAGMENT_SHADER
 void main(void) {
     float aa_range = compute_aa_range(vPos);
     bool do_aa = vMixColors != MIX_NO_AA;
@@ -116,24 +136,40 @@ void main(void) {
         float d_line = distance_to_line(vColorLine.xy, vColorLine.zw, vPos);
         if (do_aa) {
             mix_factor = distance_aa(aa_range, -d_line);
         } else {
             mix_factor = d_line + EPSILON >= 0. ? 1.0 : 0.0;
         }
     }
 
-    // Check if inside corner clip-region
+    // Check if inside main corner clip-region
     vec2 clip_relative_pos = vPos - vClipCenter_Sign.xy;
     bool in_clip_region = all(lessThan(vClipCenter_Sign.zw * clip_relative_pos, vec2(0.0)));
 
     float d = -1.0;
     if (in_clip_region) {
         float d_radii_a = distance_to_ellipse(clip_relative_pos, vClipRadii.xy, aa_range);
         float d_radii_b = distance_to_ellipse(clip_relative_pos, vClipRadii.zw, aa_range);
         d = max(d_radii_a, -d_radii_b);
     }
 
+    // And again for horizontally-adjacent corner
+    clip_relative_pos = vPos - vHorizontalClipCenter_Sign.xy;
+    in_clip_region = all(lessThan(vHorizontalClipCenter_Sign.zw * clip_relative_pos, vec2(0.0)));
+    if (in_clip_region) {
+        float d_radii = distance_to_ellipse(clip_relative_pos, vHorizontalClipRadii.xy, aa_range);
+        d = max(d_radii, d);
+    }
+
+    // And finally for vertically-adjacent corner
+    clip_relative_pos = vPos - vVerticalClipCenter_Sign.xy;
+    in_clip_region = all(lessThan(vVerticalClipCenter_Sign.zw * clip_relative_pos, vec2(0.0)));
+    if (in_clip_region) {
+        float d_radii = distance_to_ellipse(clip_relative_pos, vVerticalClipRadii.xy, aa_range);
+        d = max(d_radii, d);
+    }
+
     float alpha = do_aa ? distance_aa(aa_range, d) : 1.0;
     vec4 color = mix(vColor0, vColor1, mix_factor);
     oFragColor = color * alpha;
 }
 #endif
--- a/gfx/wr/webrender/src/border.rs
+++ b/gfx/wr/webrender/src/border.rs
@@ -1,16 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use api::{BorderRadius, BorderSide, BorderStyle, ColorF, ColorU, DeviceRect, DeviceSize};
 use api::{LayoutSideOffsets, LayoutSizeAu, LayoutPrimitiveInfo, LayoutToDeviceScale};
 use api::{DeviceVector2D, DevicePoint, LayoutRect, LayoutSize, DeviceIntSize};
-use api::{AuHelpers, LayoutPoint, RepeatMode, TexelRect};
+use api::{AuHelpers, LayoutPoint, LayoutPointAu, RepeatMode, TexelRect};
 use api::NormalBorder as ApiNormalBorder;
 use ellipse::Ellipse;
 use euclid::vec2;
 use display_list_flattener::DisplayListFlattener;
 use gpu_types::{BorderInstance, BorderSegment, BrushFlags};
 use prim_store::{BorderSegmentInfo, BrushSegment, NinePatchDescriptor};
 use prim_store::{EdgeAaSegmentMask, ScrollNodeAndClipChain};
 use prim_store::borders::NormalBorderPrim;
@@ -153,16 +153,20 @@ impl From<NormalBorderAu> for ApiNormalB
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 pub struct BorderSegmentCacheKey {
     pub size: LayoutSizeAu,
     pub radius: LayoutSizeAu,
     pub side0: BorderSideAu,
     pub side1: BorderSideAu,
     pub segment: BorderSegment,
     pub do_aa: bool,
+    pub h_adjacent_corner_outer: LayoutPointAu,
+    pub h_adjacent_corner_radius: LayoutSizeAu,
+    pub v_adjacent_corner_outer: LayoutPointAu,
+    pub v_adjacent_corner_radius: LayoutSizeAu,
 }
 
 pub fn ensure_no_corner_overlap(
     radius: &mut BorderRadius,
     size: LayoutSize,
 ) {
     let mut ratio = 1.0;
     let top_left_radius = &mut radius.top_left;
@@ -757,73 +761,113 @@ pub fn create_border_segments(
 
     add_corner_segment(
         LayoutRect::from_floats(
             rect.origin.x,
             rect.origin.y,
             rect.origin.x + local_size_tl.width,
             rect.origin.y + local_size_tl.height,
         ),
+        LayoutRect::from_floats(
+            rect.origin.x,
+            rect.origin.y,
+            rect.max_x() - widths.right,
+            rect.max_y() - widths.bottom
+        ),
         border.left,
         border.top,
         LayoutSize::new(widths.left, widths.top),
         border.radius.top_left,
         BorderSegment::TopLeft,
         EdgeAaSegmentMask::TOP | EdgeAaSegmentMask::LEFT,
+        rect.top_right(),
+        border.radius.top_right,
+        rect.bottom_left(),
+        border.radius.bottom_left,
         brush_segments,
         border_segments,
         border.do_aa,
     );
     add_corner_segment(
         LayoutRect::from_floats(
             rect.origin.x + rect.size.width - local_size_tr.width,
             rect.origin.y,
             rect.origin.x + rect.size.width,
             rect.origin.y + local_size_tr.height,
         ),
+        LayoutRect::from_floats(
+            rect.origin.x + widths.left,
+            rect.origin.y,
+            rect.max_x(),
+            rect.max_y() - widths.bottom,
+        ),
         border.top,
         border.right,
         LayoutSize::new(widths.right, widths.top),
         border.radius.top_right,
         BorderSegment::TopRight,
         EdgeAaSegmentMask::TOP | EdgeAaSegmentMask::RIGHT,
+        rect.origin,
+        border.radius.top_left,
+        rect.bottom_right(),
+        border.radius.bottom_right,
         brush_segments,
         border_segments,
         border.do_aa,
     );
     add_corner_segment(
         LayoutRect::from_floats(
             rect.origin.x + rect.size.width - local_size_br.width,
             rect.origin.y + rect.size.height - local_size_br.height,
             rect.origin.x + rect.size.width,
             rect.origin.y + rect.size.height,
         ),
+        LayoutRect::from_floats(
+            rect.origin.x + widths.left,
+            rect.origin.y + widths.top,
+            rect.max_x(),
+            rect.max_y(),
+        ),
         border.right,
         border.bottom,
         LayoutSize::new(widths.right, widths.bottom),
         border.radius.bottom_right,
         BorderSegment::BottomRight,
         EdgeAaSegmentMask::BOTTOM | EdgeAaSegmentMask::RIGHT,
+        rect.bottom_left(),
+        border.radius.bottom_left,
+        rect.top_right(),
+        border.radius.top_right,
         brush_segments,
         border_segments,
         border.do_aa,
     );
     add_corner_segment(
         LayoutRect::from_floats(
             rect.origin.x,
             rect.origin.y + rect.size.height - local_size_bl.height,
             rect.origin.x + local_size_bl.width,
             rect.origin.y + rect.size.height,
         ),
+        LayoutRect::from_floats(
+            rect.origin.x,
+            rect.origin.y + widths.top,
+            rect.max_x() - widths.right,
+            rect.max_y(),
+        ),
         border.bottom,
         border.left,
         LayoutSize::new(widths.left, widths.bottom),
         border.radius.bottom_left,
         BorderSegment::BottomLeft,
         EdgeAaSegmentMask::BOTTOM | EdgeAaSegmentMask::LEFT,
+        rect.bottom_right(),
+        border.radius.bottom_right,
+        rect.origin,
+        border.radius.top_left,
         brush_segments,
         border_segments,
         border.do_aa,
     );
 }
 
 /// Computes the maximum scale that we allow for this set of border parameters.
 /// capping the scale will result in rendering very large corners at a lower
@@ -855,16 +899,20 @@ fn add_segment(
     style1: BorderStyle,
     color0: ColorF,
     color1: ColorF,
     segment: BorderSegment,
     instances: &mut Vec<BorderInstance>,
     widths: DeviceSize,
     radius: DeviceSize,
     do_aa: bool,
+    h_adjacent_corner_outer: DevicePoint,
+    h_adjacent_corner_radius: DeviceSize,
+    v_adjacent_corner_outer: DevicePoint,
+    v_adjacent_corner_radius: DeviceSize,
 ) {
     let base_flags = (segment as i32) |
                      ((style0 as i32) << 8) |
                      ((style1 as i32) << 16) |
                      ((do_aa as i32) << 28);
 
     let base_instance = BorderInstance {
         task_origin: DevicePoint::zero(),
@@ -909,17 +957,31 @@ fn add_segment(
                         &base_instance,
                         instances,
                     )
                 }
                 _ => Err(()),
             };
 
             if dashed_or_dotted_corner.is_err() {
-                instances.push(base_instance);
+                let clip_params = [
+                    h_adjacent_corner_outer.x,
+                    h_adjacent_corner_outer.y,
+                    h_adjacent_corner_radius.width,
+                    h_adjacent_corner_radius.height,
+                    v_adjacent_corner_outer.x,
+                    v_adjacent_corner_outer.y,
+                    v_adjacent_corner_radius.width,
+                    v_adjacent_corner_radius.height,
+                ];
+
+                instances.push(BorderInstance {
+                    clip_params,
+                    ..base_instance
+                });
             }
         }
         BorderSegment::Top |
         BorderSegment::Bottom |
         BorderSegment::Right |
         BorderSegment::Left => {
             let is_vertical = segment == BorderSegment::Left ||
                               segment == BorderSegment::Right;
@@ -968,61 +1030,143 @@ fn add_segment(
         }
     }
 }
 
 /// Add a corner segment (if valid) to the list of
 /// border segments for this primitive.
 fn add_corner_segment(
     image_rect: LayoutRect,
+    non_overlapping_rect: LayoutRect,
     side0: BorderSide,
     side1: BorderSide,
     widths: LayoutSize,
     radius: LayoutSize,
     segment: BorderSegment,
     edge_flags: EdgeAaSegmentMask,
+    h_adjacent_corner_outer: LayoutPoint,
+    h_adjacent_corner_radius: LayoutSize,
+    v_adjacent_corner_outer: LayoutPoint,
+    v_adjacent_corner_radius: LayoutSize,
     brush_segments: &mut Vec<BrushSegment>,
     border_segments: &mut Vec<BorderSegmentInfo>,
     do_aa: bool,
 ) {
     if side0.color.a <= 0.0 && side1.color.a <= 0.0 {
         return;
     }
 
     if widths.width <= 0.0 && widths.height <= 0.0 {
         return;
     }
 
     if side0.style.is_hidden() && side1.style.is_hidden() {
         return;
     }
 
-    if image_rect.size.width <= 0. || image_rect.size.height <= 0. {
+    let segment_rect = image_rect.intersection(&non_overlapping_rect)
+        .unwrap_or(LayoutRect::zero());
+
+    if segment_rect.size.width <= 0. || segment_rect.size.height <= 0. {
         return;
     }
 
+    let texture_rect = segment_rect.translate(&-image_rect.origin.to_vector()).round();
+
     brush_segments.push(
         BrushSegment::new(
-            image_rect,
+            segment_rect,
             /* may_need_clip_mask = */ true,
             edge_flags,
-            [0.0; 4],
-            BrushFlags::SEGMENT_RELATIVE,
+            [texture_rect.min_x(), texture_rect.min_y(), texture_rect.max_x(), texture_rect.max_y()],
+            BrushFlags::SEGMENT_RELATIVE | BrushFlags::SEGMENT_TEXEL_RECT,
         )
     );
 
+    // If the radii of the adjacent corners do not overlap with this segment,
+    // then set the outer position to this segment's corner and the radii to zero.
+    // That way the cache key is unaffected by non-overlapping corners, resulting
+    // in fewer misses.
+    let (h_corner_outer, h_corner_radius) = match segment {
+        BorderSegment::TopLeft => {
+            if h_adjacent_corner_outer.x - h_adjacent_corner_radius.width < image_rect.max_x() {
+                (h_adjacent_corner_outer, h_adjacent_corner_radius)
+            } else {
+                (LayoutPoint::new(image_rect.max_x(), image_rect.min_y() - 1.0), LayoutSize::zero())
+            }
+        }
+        BorderSegment::TopRight => {
+            if h_adjacent_corner_outer.x + h_adjacent_corner_radius.width > image_rect.min_x() {
+                (h_adjacent_corner_outer, h_adjacent_corner_radius)
+            } else {
+                (LayoutPoint::new(image_rect.min_x() - 1.0, image_rect.min_y() - 1.0), LayoutSize::zero())
+            }
+        }
+        BorderSegment::BottomRight => {
+            if h_adjacent_corner_outer.x + h_adjacent_corner_radius.width > image_rect.min_x() {
+                (h_adjacent_corner_outer, h_adjacent_corner_radius)
+            } else {
+                (LayoutPoint::new(image_rect.min_x() - 1.0, image_rect.max_y()), LayoutSize::zero())
+            }
+        }
+        BorderSegment::BottomLeft => {
+            if h_adjacent_corner_outer.x - h_adjacent_corner_radius.width < image_rect.max_x() {
+                (h_adjacent_corner_outer, h_adjacent_corner_radius)
+            } else {
+                (image_rect.bottom_right(), LayoutSize::zero())
+            }
+        }
+        _ => unreachable!()
+    };
+
+    let (v_corner_outer, v_corner_radius) = match segment {
+        BorderSegment::TopLeft => {
+            if v_adjacent_corner_outer.y - v_adjacent_corner_radius.height < image_rect.max_y() {
+                (v_adjacent_corner_outer, v_adjacent_corner_radius)
+            } else {
+                (LayoutPoint::new(image_rect.min_x() - 1.0, image_rect.max_y()), LayoutSize::zero())
+            }
+        }
+        BorderSegment::TopRight => {
+            if v_adjacent_corner_outer.y - v_adjacent_corner_radius.height < image_rect.max_y() {
+                (v_adjacent_corner_outer, v_adjacent_corner_radius)
+            } else {
+                (image_rect.bottom_right(), LayoutSize::zero())
+            }
+        }
+        BorderSegment::BottomRight => {
+            if v_adjacent_corner_outer.y + v_adjacent_corner_radius.height > image_rect.min_y() {
+                (v_adjacent_corner_outer, v_adjacent_corner_radius)
+            } else {
+                (LayoutPoint::new(image_rect.max_x(), image_rect.min_y() - 1.0), LayoutSize::zero())
+            }
+        }
+        BorderSegment::BottomLeft => {
+            if v_adjacent_corner_outer.y + v_adjacent_corner_radius.height > image_rect.min_y() {
+                (v_adjacent_corner_outer, v_adjacent_corner_radius)
+            } else {
+                (LayoutPoint::new(image_rect.min_x() - 1.0, image_rect.min_y() - 1.0), LayoutSize::zero())
+            }
+        }
+        _ => unreachable!()
+    };
+
     border_segments.push(BorderSegmentInfo {
         local_task_size: image_rect.size,
         cache_key: BorderSegmentCacheKey {
             do_aa,
             side0: side0.into(),
             side1: side1.into(),
             segment,
             radius: radius.to_au(),
             size: widths.to_au(),
+            h_adjacent_corner_outer: (h_corner_outer - image_rect.origin).to_point().to_au(),
+            h_adjacent_corner_radius: h_corner_radius.to_au(),
+            v_adjacent_corner_outer: (v_corner_outer - image_rect.origin).to_point().to_au(),
+            v_adjacent_corner_radius: v_corner_radius.to_au(),
         },
     });
 }
 
 /// Add an edge segment (if valid) to the list of
 /// border segments for this primitive.
 fn add_edge_segment(
     image_rect: LayoutRect,
@@ -1073,16 +1217,20 @@ fn add_edge_segment(
         local_task_size: size,
         cache_key: BorderSegmentCacheKey {
             do_aa,
             side0: side.into(),
             side1: side.into(),
             radius: LayoutSizeAu::zero(),
             size: size.to_au(),
             segment,
+            h_adjacent_corner_outer: LayoutPointAu::zero(),
+            h_adjacent_corner_radius: LayoutSizeAu::zero(),
+            v_adjacent_corner_outer: LayoutPointAu::zero(),
+            v_adjacent_corner_radius: LayoutSizeAu::zero(),
         },
     });
 }
 
 /// Build the set of border instances needed to draw a border
 /// segment into the render task cache.
 pub fn build_border_instances(
     cache_key: &BorderSegmentCacheKey,
@@ -1115,27 +1263,36 @@ pub fn build_border_instances(
     };
 
     let color0 = side0.border_color(flip0);
     let color1 = side1.border_color(flip1);
 
     let widths = (LayoutSize::from_au(cache_key.size) * scale).ceil();
     let radius = (LayoutSize::from_au(cache_key.radius) * scale).ceil();
 
+    let h_corner_outer = (LayoutPoint::from_au(cache_key.h_adjacent_corner_outer) * scale).round();
+    let h_corner_radius = (LayoutSize::from_au(cache_key.h_adjacent_corner_radius) * scale).ceil();
+    let v_corner_outer = (LayoutPoint::from_au(cache_key.v_adjacent_corner_outer) * scale).round();
+    let v_corner_radius = (LayoutSize::from_au(cache_key.v_adjacent_corner_radius) * scale).ceil();
+
     add_segment(
         DeviceRect::new(DevicePoint::zero(), cache_size.to_f32()),
         style0,
         style1,
         color0,
         color1,
         cache_key.segment,
         &mut instances,
         widths,
         radius,
         border.do_aa,
+        h_corner_outer,
+        h_corner_radius,
+        v_corner_outer,
+        v_corner_radius,
     );
 
     instances
 }
 
 impl NinePatchDescriptor {
     pub fn create_segments(
         &self,
new file mode 100644
--- /dev/null
+++ b/gfx/wr/wrench/reftests/border/border-overlapping-ref.yaml
@@ -0,0 +1,19 @@
+---
+root:
+  items:
+    - type: stacking-context
+      bounds: [0, 0, 200, 200]
+      items:
+        - type: clip
+          bounds: [ 0, 0, 200, 200 ]
+          complex:
+            - rect: [ 10, 10, 180, 180 ]
+              radius:
+                top-left: [180, 180]
+                top-right: [0, 0]
+                bottom-left: [0, 0]
+                bottom-right: [180, 180]
+          items:
+            - type: rect
+              bounds: [ 0, 0, 200, 200 ]
+              color: [ 0, 0, 255, 0.5 ]
new file mode 100644
--- /dev/null
+++ b/gfx/wr/wrench/reftests/border/border-overlapping.yaml
@@ -0,0 +1,17 @@
+---
+root:
+  items:
+    - type: stacking-context
+      bounds: [0, 0, 200, 200]
+      items:
+        - type: border
+          bounds: [ 10, 10, 180, 180 ]
+          width: 90
+          border-type: normal
+          style: solid
+          radius:
+            top-left: 180
+            bottom-right: 180
+            top-right: 0
+            bottom-left: 0
+          color: [ [0, 0, 255, 0.5] ]
--- a/gfx/wr/wrench/reftests/border/reftest.list
+++ b/gfx/wr/wrench/reftests/border/reftest.list
@@ -1,15 +1,16 @@
 platform(linux,mac) == border-clamp-corner-radius.yaml border-clamp-corner-radius.png
 == border-gradient-simple.yaml border-gradient-simple-ref.yaml
 platform(linux,mac) == border-gradient-nine-patch.yaml border-gradient-nine-patch.png
 == border-radial-gradient-simple.yaml border-radial-gradient-simple-ref.yaml
 platform(linux,mac) == border-radial-gradient-nine-patch.yaml border-radial-gradient-nine-patch.png
 == border-radii.yaml border-radii.png
 == border-none.yaml border-none-ref.yaml
+fuzzy(1,68) == border-overlapping.yaml border-overlapping-ref.yaml
 == border-invisible.yaml border-invisible-ref.yaml
 platform(linux,mac) == border-suite.yaml border-suite.png
 platform(linux,mac) fuzzy(1,57) == border-suite-2.yaml border-suite-2.png
 platform(linux,mac) == border-suite-3.yaml border-suite-3.png
 == border-double-simple.yaml border-double-simple-ref.yaml
 == border-double-simple-2.yaml border-double-simple-2-ref.yaml
 fuzzy(64,24) == border-groove-simple.yaml border-groove-simple-ref.yaml
 fuzzy(64,24) == border-ridge-simple.yaml border-ridge-simple-ref.yaml
--- a/layout/reftests/border-radius/reftest.list
+++ b/layout/reftests/border-radius/reftest.list
@@ -24,17 +24,17 @@ fuzzy-if(skiaContent,0-1,0-343) == perce
 
 # more serious tests, using SVG reference
 fuzzy-if(skiaContent,0-17,0-58) fuzzy-if(webrender,30-30,70-70) == border-circle-2.html border-circle-2-ref.xhtml
 fuzzy-if(gtkWidget,0-14,0-280) fuzzy-if(cocoaWidget,0-4,0-582) fuzzy-if(Android,0-36,0-264) fuzzy-if(d2d,0-51,0-323) fuzzy-if(winWidget&&!d2d,0-16,0-377) fuzzy-if(skiaContent,0-63,0-507) fuzzy-if(webrender,62-62,931-931) == curved-stripe-border.html curved-stripe-border-ref.svg # bug 459945
 
 # Corners
 fuzzy-if(skiaContent,0-17,0-47) fuzzy-if(webrender,30-30,58-58) == corner-1.html corner-1-ref.svg # bottom corners different radius than top corners
 fuzzy-if(gtkWidget,0-23,0-5) fuzzy-if(winWidget&&!d2d,0-23,0-5) fuzzy-if(d2d,0-32,0-8) fuzzy-if(Android,0-10,0-8) fuzzy-if(skiaContent,0-18,0-49) fuzzy-if(webrender,30-30,57-57) == corner-2.html corner-2-ref.svg # right corners different radius than left corners; see bug 500804
-fuzzy-if(gtkWidget,0-3,0-10) fuzzy-if(winWidget&&!d2d,0-3,0-10) fuzzy-if(d2d,0-15,0-32) fuzzy-if(Android,0-3,0-15) fuzzy-if(skiaContent,0-18,0-90) fails-if(webrender) == corner-3.html corner-3-ref.svg
+fuzzy-if(gtkWidget,0-3,0-10) fuzzy-if(winWidget&&!d2d,0-3,0-10) fuzzy-if(d2d,0-15,0-32) fuzzy-if(Android,0-3,0-15) fuzzy-if(skiaContent,0-18,0-90) fuzzy-if(webrender,23-23,105-105) == corner-3.html corner-3-ref.svg
 fuzzy-if(skiaContent,0-13,0-83) fuzzy-if(webrender,13-13,104-104) == corner-4.html corner-4-ref.svg
 
 # Test that radii too long are reduced
 == border-reduce-height.html border-reduce-height-ref.html
 
 # Tests for border clipping
 fails == clipping-1.html clipping-1-ref.html # background color should completely fill box; bug 466572
 != clipping-2.html about:blank # background color clipped to inner/outer border, can't get