Bug 1485791. Update webrender to commit 816ff14c1805c145ccd60d0227d82b1541fc24eb
authorJeff Muizelaar <jmuizelaar@mozilla.com>
Sun, 26 Aug 2018 21:29:25 -0400
changeset 491219 5cfe12075897198b691169b91b6ded86996ffc45
parent 491191 c09a146819d3a65127cee966b326efa9ee8ea4aa
child 491220 9b2aa473b2a807dfd412889ebc31aa62dac67339
push id1815
push userffxbld-merge
push dateMon, 15 Oct 2018 10:40:45 +0000
treeherdermozilla-release@18d4c09e9378 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1485791
milestone63.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 1485791. Update webrender to commit 816ff14c1805c145ccd60d0227d82b1541fc24eb
gfx/webrender/src/border.rs
gfx/webrender/src/clip.rs
gfx/webrender/src/display_list_flattener.rs
gfx/webrender/src/prim_store.rs
gfx/webrender/src/util.rs
gfx/webrender_api/src/display_item.rs
gfx/webrender_bindings/revision.txt
--- a/gfx/webrender/src/border.rs
+++ b/gfx/webrender/src/border.rs
@@ -5,17 +5,17 @@
 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 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::{BorderSource, EdgeAaSegmentMask, PrimitiveContainer, ScrollNodeAndClipChain};
+use prim_store::{EdgeAaSegmentMask, PrimitiveContainer, ScrollNodeAndClipChain};
 use util::{lerp, RectHelpers};
 
 // Using 2048 as the maximum radius in device space before which we
 // start stretching is up for debate.
 // the value must be chosen so that the corners will not use an
 // unreasonable amount of memory but should allow crisp corners in the
 // common cases.
 
@@ -111,16 +111,30 @@ pub struct BorderCacheKey {
     pub right: BorderSideAu,
     pub top: BorderSideAu,
     pub bottom: BorderSideAu,
     pub radius: BorderRadiusAu,
     pub widths: BorderWidthsAu,
     pub scale: Au,
 }
 
+impl BorderCacheKey {
+    pub fn new(border: &NormalBorder, widths: &BorderWidths) -> Self {
+        BorderCacheKey {
+            left: border.left.into(),
+            top: border.top.into(),
+            right: border.right.into(),
+            bottom: border.bottom.into(),
+            widths: (*widths).into(),
+            radius: border.radius.into(),
+            scale: Au::from_f32_px(0.0),
+        }
+    }
+}
+
 pub fn ensure_no_corner_overlap(
     radius: &mut BorderRadius,
     rect: &LayoutRect,
 ) {
     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;
@@ -168,33 +182,17 @@ impl<'a> DisplayListFlattener<'a> {
         border: &NormalBorder,
         widths: &BorderWidths,
         clip_and_scroll: ScrollNodeAndClipChain,
     ) {
         let mut border = *border;
         ensure_no_corner_overlap(&mut border.radius, &info.rect);
 
         let prim = BrushPrimitive::new(
-            BrushKind::Border {
-                source: BorderSource::Border {
-                    border,
-                    widths: *widths,
-                    cache_key: BorderCacheKey {
-                        left: border.left.into(),
-                        top: border.top.into(),
-                        right: border.right.into(),
-                        bottom: border.bottom.into(),
-                        widths: (*widths).into(),
-                        radius: border.radius.into(),
-                        scale: Au::from_f32_px(0.0),
-                    },
-                    task_info: None,
-                    handle: None,
-                },
-            },
+            BrushKind::new_border(border, *widths),
             None,
         );
 
         self.add_primitive(
             clip_and_scroll,
             info,
             Vec::new(),
             PrimitiveContainer::Brush(prim),
--- a/gfx/webrender/src/clip.rs
+++ b/gfx/webrender/src/clip.rs
@@ -1,26 +1,28 @@
 /* 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, DevicePixelScale, ImageMask};
 use api::{ImageRendering, LayoutRect, LayoutSize, LayoutPoint, LayoutVector2D, LocalClip};
-use api::{BoxShadowClipMode, LayoutToWorldScale, LineOrientation, LineStyle, LayoutTransform};
+use api::{BoxShadowClipMode, LayoutToWorldScale, LineOrientation, LineStyle};
+use api::{LayoutToWorldTransform, WorldPixel, WorldRect, WorldPoint, WorldSize};
 use border::{ensure_no_corner_overlap};
 use box_shadow::{BLUR_SAMPLE_SCALE, BoxShadowClipSource, BoxShadowCacheKey};
 use clip_scroll_tree::{ClipScrollTree, CoordinateSystemId, SpatialNodeIndex};
 use ellipse::Ellipse;
 use gpu_cache::{GpuCache, GpuCacheHandle, ToGpuBlocks};
 use gpu_types::BoxShadowStretchMode;
-use prim_store::{BrushClipMaskKind, ClipData, ImageMaskData};
+use plane_split::{Clipper, Polygon};
+use prim_store::{ClipData, ImageMaskData};
 use render_task::to_cache_size;
 use resource_cache::{ImageRequest, ResourceCache};
-use std::u32;
-use util::{extract_inner_rect_safe, pack_as_float, recycle_vec, MatrixHelpers};
+use std::{cmp, u32};
+use util::{extract_inner_rect_safe, pack_as_float, recycle_vec, MaxRect};
 
 /*
 
  Module Overview
 
  There are a number of data structures involved in the clip module:
 
  ClipStore - Main interface used by other modules.
@@ -194,51 +196,17 @@ pub struct ClipNodeRange {
 // of clip sources and primitives.
 // todo(gw): optimize:
 //  separate arrays for matrices
 //  cache and only build as needed.
 #[derive(Debug)]
 enum ClipSpaceConversion {
     Local,
     Offset(LayoutVector2D),
-    Transform(LayoutTransform, LayoutTransform),
-}
-
-impl ClipSpaceConversion {
-    fn transform_to_prim_space(&self, rect: &LayoutRect) -> Option<LayoutRect> {
-        match *self {
-            ClipSpaceConversion::Local => {
-                Some(*rect)
-            }
-            ClipSpaceConversion::Offset(ref offset) => {
-                Some(rect.translate(offset))
-            }
-            ClipSpaceConversion::Transform(ref transform, _) => {
-                if transform.has_perspective_component() {
-                    None
-                } else {
-                    transform.transform_rect(rect)
-                }
-            }
-        }
-    }
-
-    fn transform_from_prim_space(&self, rect: &LayoutRect) -> Option<LayoutRect> {
-        match *self {
-            ClipSpaceConversion::Local => {
-                Some(*rect)
-            }
-            ClipSpaceConversion::Offset(offset) => {
-                Some(rect.translate(&-offset))
-            }
-            ClipSpaceConversion::Transform(_, ref inv_transform) => {
-                inv_transform.transform_rect(rect)
-            }
-        }
-    }
+    Transform(LayoutToWorldTransform),
 }
 
 // Temporary information that is cached and reused
 // during building of a clip chain instance.
 struct ClipNodeInfo {
     conversion: ClipSpaceConversion,
     node_index: ClipNodeIndex,
     has_non_root_coord_system: bool,
@@ -351,18 +319,17 @@ pub struct ClipStore {
 
 // A clip chain instance is what gets built for a given clip
 // chain id + local primitive region + positioning node.
 #[derive(Debug)]
 pub struct ClipChainInstance {
     pub clips_range: ClipNodeRange,
     pub local_clip_rect: LayoutRect,
     pub has_non_root_coord_system: bool,
-    pub local_bounding_rect: LayoutRect,
-    pub clip_mask_kind: BrushClipMaskKind,
+    pub world_clip_rect: WorldRect,
 }
 
 impl ClipStore {
     pub fn new() -> Self {
         ClipStore {
             clip_nodes: Vec::new(),
             clip_chain_nodes: Vec::new(),
             clip_node_indices: Vec::new(),
@@ -448,27 +415,22 @@ impl ClipStore {
         local_prim_rect: LayoutRect,
         local_prim_clip_rect: LayoutRect,
         spatial_node_index: SpatialNodeIndex,
         clip_scroll_tree: &ClipScrollTree,
         gpu_cache: &mut GpuCache,
         resource_cache: &mut ResourceCache,
         device_pixel_scale: DevicePixelScale,
     ) -> Option<ClipChainInstance> {
-        // Trivial check to see if the primitive is clipped out by the
-        // local clip rect of the primitive itself.
-        let mut local_bounding_rect = match local_prim_rect.intersection(&local_prim_clip_rect) {
-            Some(rect) => rect,
-            None => return None,
-        };
-        let mut current_local_clip_rect = local_prim_clip_rect;
+        let mut local_clip_rect = local_prim_clip_rect;
+        let mut world_clip_rect = WorldRect::max_rect();
         let spatial_nodes = &clip_scroll_tree.spatial_nodes;
 
         // Walk the clip chain to build local rects, and collect the
-        // smallest possible local clip area.
+        // smallest possible local/device clip area.
 
         self.clip_node_info.clear();
         let ref_spatial_node = &spatial_nodes[spatial_node_index.0];
         let mut current_clip_chain_id = clip_chain_id;
 
         // for each clip chain node
         while current_clip_chain_id != ClipChainId::NONE {
             let clip_chain_node = &self.clip_chain_nodes[current_clip_chain_id.0 as usize];
@@ -486,95 +448,112 @@ impl ClipStore {
                     Some(ClipSpaceConversion::Local)
                 } else if ref_spatial_node.coordinate_system_id == clip_spatial_node.coordinate_system_id {
                     let offset = clip_spatial_node.coordinate_system_relative_offset -
                                  ref_spatial_node.coordinate_system_relative_offset;
                     Some(ClipSpaceConversion::Offset(offset))
                 } else {
                     let xf = clip_scroll_tree.get_relative_transform(
                         clip_node.spatial_node_index,
-                        spatial_node_index,
+                        SpatialNodeIndex(0),
                     );
 
-                    xf.and_then(|xf| {
-                        xf.inverse().map(|inv| {
-                            ClipSpaceConversion::Transform(xf, inv)
-                        })
+                    xf.map(|xf| {
+                        ClipSpaceConversion::Transform(xf.with_destination::<WorldPixel>())
                     })
                 };
 
                 // If we can convert spaces, try to reduce the size of the region
                 // requested, and cache the conversion information for the next step.
                 if let Some(conversion) = conversion {
                     if let Some(clip_rect) = clip_node.item.get_local_clip_rect() {
-                        let clip_rect = conversion.transform_to_prim_space(&clip_rect);
-                        if let Some(clip_rect) = clip_rect {
-                            local_bounding_rect = match local_bounding_rect.intersection(&clip_rect) {
-                                Some(new_local_bounding_rect) => new_local_bounding_rect,
-                                None => return None,
-                            };
+                        match conversion {
+                            ClipSpaceConversion::Local => {
+                                local_clip_rect = match local_clip_rect.intersection(&clip_rect) {
+                                    Some(local_clip_rect) => local_clip_rect,
+                                    None => return None,
+                                };
+                            }
+                            ClipSpaceConversion::Offset(ref offset) => {
+                                let clip_rect = clip_rect.translate(offset);
+                                local_clip_rect = match local_clip_rect.intersection(&clip_rect) {
+                                    Some(local_clip_rect) => local_clip_rect,
+                                    None => return None,
+                                };
+                            }
+                            ClipSpaceConversion::Transform(ref transform) => {
+                                let world_clip_rect_for_item = match project_rect(
+                                    transform,
+                                    &clip_rect,
+                                ) {
+                                    Some(rect) => rect,
+                                    None => return None,
+                                };
 
-                            if ref_spatial_node.coordinate_system_id == clip_spatial_node.coordinate_system_id {
-                                current_local_clip_rect = match current_local_clip_rect.intersection(&clip_rect) {
-                                    Some(new_local_clip_rect) => new_local_clip_rect,
-                                    None => {
-                                        return None
-                                    }
-                                }
+                                world_clip_rect = match world_clip_rect.intersection(&world_clip_rect_for_item) {
+                                    Some(world_clip_rect) => world_clip_rect,
+                                    None => return None,
+                                };
                             }
                         }
                     }
                     self.clip_node_info.push(ClipNodeInfo {
                         conversion,
                         node_index: clip_node_index,
                         has_non_root_coord_system: clip_spatial_node.coordinate_system_id != CoordinateSystemId::root(),
                     })
                 }
             }
 
             current_clip_chain_id = clip_chain_node.parent_clip_chain_id;
         }
 
+        let local_bounding_rect = match local_prim_rect.intersection(&local_clip_rect) {
+            Some(rect) => rect,
+            None => return None,
+        };
+
+        let world_bounding_rect = match project_rect(
+            &ref_spatial_node.world_content_transform.to_transform(),
+            &local_bounding_rect,
+        ) {
+            Some(world_bounding_rect) => world_bounding_rect,
+            None => return None,
+        };
+
+        let world_clip_rect = match world_clip_rect.intersection(&world_bounding_rect) {
+            Some(world_clip_rect) => world_clip_rect,
+            None => return None,
+        };
+
         // Now, we've collected all the clip nodes that *potentially* affect this
         // primitive region, and reduced the size of the prim region as much as possible.
 
         // Run through the clip nodes, and see which ones affect this prim region.
 
         let first_clip_node_index = self.clip_node_indices.len() as u32;
         let mut has_non_root_coord_system = false;
-        let mut clip_mask_kind = BrushClipMaskKind::Individual;
 
         // For each potential clip node
         for node_info in self.clip_node_info.drain(..) {
             let node = &mut self.clip_nodes[node_info.node_index.0 as usize];
 
-            // TODO(gw): We can easily extend the segment builder to support these clip sources in
-            // the future, but they are rarely used.
-            // We must do this check here in case we continue early below.
-            if node.item.is_image_or_line_decoration_clip() {
-                clip_mask_kind = BrushClipMaskKind::Global;
-            }
-
-            // Convert the prim rect into the clip nodes local space
-            let prim_rect = node_info
-                .conversion
-                .transform_from_prim_space(&current_local_clip_rect);
-
             // See how this clip affects the prim region.
-            let clip_result = match prim_rect {
-                Some(prim_rect) => {
-                    node.item.get_clip_result(&prim_rect)
+            let clip_result = match node_info.conversion {
+                ClipSpaceConversion::Local => {
+                    node.item.get_clip_result(&local_bounding_rect)
                 }
-                None => {
-                    // If we can't get a local rect due to perspective
-                    // weirdness, just assume that we need a clip mask
-                    // for this case.
-                    // TODO(gw): We can probably improve on this once
-                    //           we support local space picture raster.
-                    ClipResult::Partial
+                ClipSpaceConversion::Offset(offset) => {
+                    node.item.get_clip_result(&local_bounding_rect.translate(&-offset))
+                }
+                ClipSpaceConversion::Transform(ref transform) => {
+                    node.item.get_clip_result_complex(
+                        transform,
+                        &world_bounding_rect,
+                    )
                 }
             };
 
             match clip_result {
                 ClipResult::Accept => {
                     // Doesn't affect the primitive at all, so skip adding to list
                 }
                 ClipResult::Reject => {
@@ -593,25 +572,19 @@ impl ClipStore {
 
                     // Calculate some flags that are required for the segment
                     // building logic.
                     let flags = match node_info.conversion {
                         ClipSpaceConversion::Local => {
                             ClipNodeFlags::SAME_SPATIAL_NODE | ClipNodeFlags::SAME_COORD_SYSTEM
                         }
                         ClipSpaceConversion::Offset(..) => {
-                            if !node.item.is_rect() {
-                                clip_mask_kind = BrushClipMaskKind::Global;
-                            }
                             ClipNodeFlags::SAME_COORD_SYSTEM
                         }
                         ClipSpaceConversion::Transform(..) => {
-                            // If this primitive is clipped by clips from a different coordinate system, then we
-                            // need to apply a clip mask for the entire primitive.
-                            clip_mask_kind = BrushClipMaskKind::Global;
                             ClipNodeFlags::empty()
                         }
                     };
 
                     // Store this in the index buffer for this clip chain instance.
                     self.clip_node_indices
                         .push(ClipNodeInstance::new(node_info.node_index, flags));
 
@@ -625,19 +598,18 @@ impl ClipStore {
             first: first_clip_node_index,
             count: self.clip_node_indices.len() as u32 - first_clip_node_index,
         };
 
         // Return a valid clip chain instance
         Some(ClipChainInstance {
             clips_range,
             has_non_root_coord_system,
-            local_clip_rect: current_local_clip_rect,
-            local_bounding_rect,
-            clip_mask_kind,
+            local_clip_rect,
+            world_clip_rect,
         })
     }
 }
 
 #[derive(Debug)]
 pub struct LineDecorationClipSource {
     rect: LayoutRect,
     style: LineStyle,
@@ -868,30 +840,16 @@ impl ClipItem {
                 })
             }
             _ => {
                 panic!("bug: other clip sources not expected here yet");
             }
         }
     }
 
-    pub fn is_rect(&self) -> bool {
-        match *self {
-            ClipItem::Rectangle(..) => true,
-            _ => false,
-        }
-    }
-
-    pub fn is_image_or_line_decoration_clip(&self) -> bool {
-        match *self {
-            ClipItem::Image(..) | ClipItem::LineDecoration(..) => true,
-            _ => false,
-        }
-    }
-
     // Get an optional clip rect that a clip source can provide to
     // reduce the size of a primitive region. This is typically
     // used to eliminate redundant clips, and reduce the size of
     // any clip mask that eventually gets drawn.
     fn get_local_clip_rect(&self) -> Option<LayoutRect> {
         match *self {
             ClipItem::Rectangle(clip_rect, ClipMode::Clip) => Some(clip_rect),
             ClipItem::Rectangle(_, ClipMode::ClipOut) => None,
@@ -899,16 +857,63 @@ impl ClipItem {
             ClipItem::RoundedRectangle(_, _, ClipMode::ClipOut) => None,
             ClipItem::Image(ref mask) if mask.repeat => None,
             ClipItem::Image(ref mask) => Some(mask.rect),
             ClipItem::BoxShadow(..) => None,
             ClipItem::LineDecoration(..) => None,
         }
     }
 
+    fn get_clip_result_complex(
+        &self,
+        transform: &LayoutToWorldTransform,
+        prim_rect: &WorldRect,
+    ) -> ClipResult {
+        let (clip_rect, inner_rect) = match *self {
+            ClipItem::Rectangle(clip_rect, ClipMode::Clip) => {
+                (clip_rect, Some(clip_rect))
+            }
+            ClipItem::RoundedRectangle(ref clip_rect, ref radius, ClipMode::Clip) => {
+                let inner_clip_rect = extract_inner_rect_safe(clip_rect, radius);
+                (*clip_rect, inner_clip_rect)
+            }
+            ClipItem::Rectangle(_, ClipMode::ClipOut) |
+            ClipItem::RoundedRectangle(_, _, ClipMode::ClipOut) |
+            ClipItem::Image(..) |
+            ClipItem::BoxShadow(..) |
+            ClipItem::LineDecoration(..) => {
+                return ClipResult::Partial
+            }
+        };
+
+        let inner_clip_rect = inner_rect.and_then(|ref inner_rect| {
+            project_inner_rect(transform, inner_rect)
+        });
+
+        if let Some(inner_clip_rect) = inner_clip_rect {
+            if inner_clip_rect.contains_rect(prim_rect) {
+                return ClipResult::Accept;
+            }
+        }
+
+        let outer_clip_rect = match project_rect(transform, &clip_rect) {
+            Some(outer_clip_rect) => outer_clip_rect,
+            None => return ClipResult::Partial,
+        };
+
+        match outer_clip_rect.intersection(prim_rect) {
+            Some(..) => {
+                ClipResult::Partial
+            }
+            None => {
+                ClipResult::Reject
+            }
+        }
+    }
+
     // Check how a given clip source affects a local primitive region.
     fn get_clip_result(
         &self,
         prim_rect: &LayoutRect,
     ) -> ClipResult {
         match *self {
             ClipItem::Rectangle(ref clip_rect, ClipMode::Clip) => {
                 if clip_rect.contains_rect(prim_rect) {
@@ -1051,8 +1056,76 @@ pub fn rounded_rectangle_contains_point(
                              LayoutVector2D::new(radii.bottom_left.width, -radii.bottom_left.height);
     if bottom_left_center.x > point.x && bottom_left_center.y < point.y &&
        !Ellipse::new(radii.bottom_left).contains(*point - bottom_left_center.to_vector()) {
         return false;
     }
 
     true
 }
+
+fn project_rect(
+    transform: &LayoutToWorldTransform,
+    rect: &LayoutRect,
+) -> Option<WorldRect> {
+    let homogens = [
+        transform.transform_point2d_homogeneous(&rect.origin),
+        transform.transform_point2d_homogeneous(&rect.top_right()),
+        transform.transform_point2d_homogeneous(&rect.bottom_left()),
+        transform.transform_point2d_homogeneous(&rect.bottom_right()),
+    ];
+
+    // Note: we only do the full frustum collision when the polygon approaches the camera plane.
+    // Otherwise, it will be clamped to the screen bounds anyway.
+    if homogens.iter().any(|h| h.w <= 0.0) {
+        let mut clipper = Clipper::new();
+        clipper.add_frustum(
+            transform,
+            None,
+        );
+
+        let polygon = Polygon::from_rect(*rect, 1);
+        let results = clipper.clip(polygon);
+        if results.is_empty() {
+            return None
+        }
+
+        Some(WorldRect::from_points(results
+            .into_iter()
+            // filter out parts behind the view plane
+            .flat_map(|poly| &poly.points)
+            .map(|p| {
+                let mut homo = transform.transform_point2d_homogeneous(&p.to_2d());
+                homo.w = homo.w.max(0.00000001); // avoid infinite values
+                homo.to_point2d().unwrap()
+            })
+        ))
+    } else {
+        // we just checked for all the points to be in positive hemisphere, so `unwrap` is valid
+        Some(WorldRect::from_points(&[
+            homogens[0].to_point2d().unwrap(),
+            homogens[1].to_point2d().unwrap(),
+            homogens[2].to_point2d().unwrap(),
+            homogens[3].to_point2d().unwrap(),
+        ]))
+    }
+}
+
+pub fn project_inner_rect(
+    transform: &LayoutToWorldTransform,
+    rect: &LayoutRect,
+) -> Option<WorldRect> {
+    let points = [
+        transform.transform_point2d(&rect.origin)?,
+        transform.transform_point2d(&rect.top_right())?,
+        transform.transform_point2d(&rect.bottom_left())?,
+        transform.transform_point2d(&rect.bottom_right())?,
+    ];
+
+    let mut xs = [points[0].x, points[1].x, points[2].x, points[3].x];
+    let mut ys = [points[0].y, points[1].y, points[2].y, points[3].y];
+    xs.sort_by(|a, b| a.partial_cmp(b).unwrap_or(cmp::Ordering::Equal));
+    ys.sort_by(|a, b| a.partial_cmp(b).unwrap_or(cmp::Ordering::Equal));
+    Some(WorldRect::new(
+        WorldPoint::new(xs[1], ys[1]),
+        WorldSize::new(xs[2] - xs[1], ys[2] - ys[1]),
+    ))
+}
--- a/gfx/webrender/src/display_list_flattener.rs
+++ b/gfx/webrender/src/display_list_flattener.rs
@@ -19,17 +19,17 @@ use euclid::vec2;
 use frame_builder::{ChasePrimitive, FrameBuilder, FrameBuilderConfig};
 use glyph_rasterizer::FontInstance;
 use gpu_cache::GpuCacheHandle;
 use gpu_types::BrushFlags;
 use hit_test::{HitTestingItem, HitTestingRun};
 use image::simplify_repeated_primitive;
 use internal_types::{FastHashMap, FastHashSet};
 use picture::{PictureCompositeMode, PictureId, PicturePrimitive};
-use prim_store::{BrushClipMaskKind, BrushKind, BrushPrimitive, BrushSegmentDescriptor};
+use prim_store::{BrushKind, BrushPrimitive, BrushSegmentDescriptor};
 use prim_store::{EdgeAaSegmentMask, ImageSource};
 use prim_store::{BorderSource, BrushSegment, PrimitiveContainer, PrimitiveIndex, PrimitiveStore};
 use prim_store::{OpacityBinding, ScrollNodeAndClipChain, TextRunPrimitive};
 use render_backend::{DocumentView};
 use resource_cache::{FontInstanceMap, ImageRequest};
 use scene::{Scene, ScenePipeline, StackingContextHelpers};
 use scene_builder::{BuiltScene, SceneRequest};
 use spatial_node::{SpatialNodeType, StickyFrameInfo};
@@ -1701,17 +1701,16 @@ impl<'a> DisplayListFlattener<'a> {
                     &mut segments,
                     LayoutRect::from_floats(tr_inner.x, tr_inner.y, br_outer.x, br_inner.y),
                     TexelRect::new(px2, py1, px3, py2),
                     RepeatMode::Stretch,
                     border.repeat_vertical,
                 );
                 let descriptor = BrushSegmentDescriptor {
                     segments,
-                    clip_mask_kind: BrushClipMaskKind::Unknown,
                 };
 
                 let brush_kind = match border.source {
                     NinePatchBorderSource::Image(image_key) => {
                         BrushKind::Border {
                             source: BorderSource::Image(ImageRequest {
                                 key: image_key,
                                 rendering: ImageRendering::Auto,
--- a/gfx/webrender/src/prim_store.rs
+++ b/gfx/webrender/src/prim_store.rs
@@ -23,17 +23,17 @@ use picture::{PictureCompositeMode, Pict
 #[cfg(debug_assertions)]
 use render_backend::FrameId;
 use render_task::{BlitSource, RenderTask, RenderTaskCacheKey};
 use render_task::{RenderTaskCacheKeyKind, RenderTaskId, RenderTaskCacheEntryHandle};
 use renderer::{MAX_VERTEX_TEXTURE_WIDTH};
 use resource_cache::{ImageProperties, ImageRequest, ResourceCache};
 use scene::SceneProperties;
 use segment::SegmentBuilder;
-use std::{mem, usize};
+use std::{cmp, mem, usize};
 use util::{MatrixHelpers, calculate_screen_bounding_rect};
 use util::{pack_as_float, recycle_vec, TransformedRectKind};
 
 
 const MIN_BRUSH_SPLIT_AREA: f32 = 256.0 * 256.0;
 pub const VECS_PER_SEGMENT: usize = 2;
 
 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
@@ -415,16 +415,51 @@ impl BrushKind {
 
     // Construct a brush that is a solid color rectangle.
     pub fn new_solid(color: ColorF) -> BrushKind {
         BrushKind::Solid {
             color,
             opacity_binding: OpacityBinding::new(),
         }
     }
+
+    // Construct a brush that is a border with `border` style and `widths`
+    // dimensions.
+    pub fn new_border(border: NormalBorder, widths: BorderWidths) -> BrushKind {
+        let cache_key = BorderCacheKey::new(&border, &widths);
+        BrushKind::Border {
+            source: BorderSource::Border {
+                border,
+                widths,
+                cache_key,
+                task_info: None,
+                handle: None,
+            }
+        }
+    }
+
+    // Construct a brush that is an image wisth `stretch_size` dimensions and
+    // `color`.
+    pub fn new_image(
+        request: ImageRequest,
+        stretch_size: LayoutSize,
+        color: ColorF
+    ) -> BrushKind {
+        BrushKind::Image {
+            request,
+            alpha_type: AlphaType::PremultipliedAlpha,
+            stretch_size,
+            tile_spacing: LayoutSize::new(0., 0.),
+            color,
+            source: ImageSource::Default,
+            sub_rect: None,
+            opacity_binding: OpacityBinding::new(),
+            visible_tiles: Vec::new(),
+        }
+    }
 }
 
 bitflags! {
     /// Each bit of the edge AA mask is:
     /// 0, when the edge of the primitive needs to be considered for AA
     /// 1, when the edge of the segment needs to be considered for AA
     ///
     /// *Note*: the bit values have to match the shader logic in
@@ -477,27 +512,19 @@ impl BrushSegment {
             may_need_clip_mask,
             edge_flags,
             extra_data,
             brush_flags,
         }
     }
 }
 
-#[derive(Copy, Clone, Debug, PartialEq)]
-pub enum BrushClipMaskKind {
-    Unknown,
-    Individual,
-    Global,
-}
-
 #[derive(Debug)]
 pub struct BrushSegmentDescriptor {
     pub segments: Vec<BrushSegment>,
-    pub clip_mask_kind: BrushClipMaskKind,
 }
 
 #[derive(Debug)]
 pub struct BrushPrimitive {
     pub kind: BrushKind,
     pub segment_desc: Option<BrushSegmentDescriptor>,
 }
 
@@ -1237,21 +1264,45 @@ impl PrimitiveContainer {
             PrimitiveContainer::Brush(ref brush) => {
                 match brush.kind {
                     BrushKind::Solid { .. } => {
                         PrimitiveContainer::Brush(BrushPrimitive::new(
                             BrushKind::new_solid(shadow.color),
                             None,
                         ))
                     }
+                    BrushKind::Border { ref source } => {
+                        let source = match *source {
+                            BorderSource::Image(request) => {
+                                BrushKind::Border {
+                                    source: BorderSource::Image(request)
+                                }
+                            },
+                            BorderSource::Border { border, widths, .. } => {
+                                let border = border.with_color(shadow.color);
+                                BrushKind::new_border(border, widths)
+
+                            }
+                        };
+                        PrimitiveContainer::Brush(BrushPrimitive::new(
+                            source,
+                            None,
+                        ))
+                    }
+                    BrushKind::Image { request, stretch_size, .. } => {
+                        PrimitiveContainer::Brush(BrushPrimitive::new(
+                            BrushKind::new_image(request.clone(),
+                                                 stretch_size.clone(),
+                                                 shadow.color),
+                            None,
+                        ))
+                    }
                     BrushKind::Clear |
                     BrushKind::Picture { .. } |
-                    BrushKind::Image { .. } |
                     BrushKind::YuvImage { .. } |
-                    BrushKind::Border { .. } |
                     BrushKind::RadialGradient { .. } |
                     BrushKind::LinearGradient { .. } => {
                         panic!("bug: other brush kinds not expected here yet");
                     }
                 }
             }
         }
     }
@@ -1654,31 +1705,19 @@ impl PrimitiveStore {
                 if cfg!(debug_assertions) && is_chased {
                     println!("\tculled for being behind the near plane of transform: {:?}",
                         prim_context.spatial_node.world_content_transform);
                 }
                 return None
             }
         };
 
-        let clipped_device_rect = match calculate_screen_bounding_rect(
-            &prim_context.spatial_node.world_content_transform,
-            &clip_chain.local_bounding_rect,
-            frame_context.device_pixel_scale,
-            None,
-        ) {
-            Some(rect) => rect,
-            None => {
-                if cfg!(debug_assertions) && is_chased {
-                    println!("\tculled for being behind the near plane of transform: {:?}",
-                        prim_context.spatial_node.world_content_transform);
-                }
-                return None
-            }
-        };
+        let clipped_device_rect = (clip_chain.world_clip_rect * frame_context.device_pixel_scale)
+            .round_out()
+            .to_i32();
 
         let clipped_device_rect = match clipped_device_rect.intersection(&frame_context.screen_rect) {
             Some(clipped_device_rect) => clipped_device_rect,
             None => return None,
         };
 
         prim.metadata.screen_rect = Some(ScreenRect {
             clipped: clipped_device_rect,
@@ -1966,22 +2005,19 @@ impl<'a> GpuDataRequest<'a> {
 
 fn write_brush_segment_description(
     brush: &mut BrushPrimitive,
     metadata: &PrimitiveMetadata,
     clip_chain: &ClipChainInstance,
     frame_state: &mut FrameBuildingState,
 ) {
     match brush.segment_desc {
-        Some(ref segment_desc) => {
-            // If we already have a segment descriptor, only run through the
-            // clips list if we haven't already determined the mask kind.
-            if segment_desc.clip_mask_kind == clip_chain.clip_mask_kind {
-                return;
-            }
+        Some(..) => {
+            // If we already have a segment descriptor, skip segment build.
+            return;
         }
         None => {
             // If no segment descriptor built yet, see if it is a brush
             // type that wants to be segmented.
             if !brush.kind.supports_segments(frame_state.resource_cache) {
                 return;
             }
         }
@@ -2001,28 +2037,31 @@ fn write_brush_segment_description(
 
     let mut segment_builder = SegmentBuilder::new(
         metadata.local_rect,
         None,
         metadata.local_clip_rect
     );
 
     // Segment the primitive on all the local-space clip sources that we can.
+    let mut local_clip_count = 0;
     for i in 0 .. clip_chain.clips_range.count {
         let (clip_node, flags) = frame_state.clip_store.get_node_from_range(&clip_chain.clips_range, i);
 
         // If this clip item is positioned by another positioning node, its relative position
         // could change during scrolling. This means that we would need to resegment. Instead
         // of doing that, only segment with clips that have the same positioning node.
         // TODO(mrobinson, #2858): It may make sense to include these nodes, resegmenting only
         // when necessary while scrolling.
         if !flags.contains(ClipNodeFlags::SAME_SPATIAL_NODE) {
             continue;
         }
 
+        local_clip_count += 1;
+
         let (local_clip_rect, radius, mode) = match clip_node.item {
             ClipItem::RoundedRectangle(rect, radii, clip_mode) => {
                 rect_clips_only = false;
                 (rect, Some(radii), clip_mode)
             }
             ClipItem::Rectangle(rect, mode) => {
                 (rect, None, mode)
             }
@@ -2058,20 +2097,50 @@ fn write_brush_segment_description(
                 continue;
             }
         };
 
         segment_builder.push_clip_rect(local_clip_rect, radius, mode);
     }
 
     if is_large || rect_clips_only {
+        // If there were no local clips, then we will subdivide the primitive into
+        // a uniform grid (up to 8x8 segments). This will typically result in
+        // a significant number of those segments either being completely clipped,
+        // or determined to not need a clip mask for that segment.
+        if local_clip_count == 0 && clip_chain.clips_range.count > 0 {
+            let x_clip_count = cmp::min(8, (metadata.local_rect.size.width / 128.0).ceil() as i32);
+            let y_clip_count = cmp::min(8, (metadata.local_rect.size.height / 128.0).ceil() as i32);
+
+            for y in 0 .. y_clip_count {
+                let y0 = metadata.local_rect.size.height * y as f32 / y_clip_count as f32;
+                let y1 = metadata.local_rect.size.height * (y+1) as f32 / y_clip_count as f32;
+
+                for x in 0 .. x_clip_count {
+                    let x0 = metadata.local_rect.size.width * x as f32 / x_clip_count as f32;
+                    let x1 = metadata.local_rect.size.width * (x+1) as f32 / x_clip_count as f32;
+
+                    let rect = LayoutRect::new(
+                        LayoutPoint::new(
+                            x0 + metadata.local_rect.origin.x,
+                            y0 + metadata.local_rect.origin.y,
+                        ),
+                        LayoutSize::new(
+                            x1 - x0,
+                            y1 - y0,
+                        ),
+                    );
+
+                    segment_builder.push_mask_region(rect, LayoutRect::zero(), None);
+                }
+            }
+        }
+
         match brush.segment_desc {
-            Some(ref mut segment_desc) => {
-                segment_desc.clip_mask_kind = clip_chain.clip_mask_kind;
-            }
+            Some(..) => panic!("bug: should not already have descriptor"),
             None => {
                 // TODO(gw): We can probably make the allocation
                 //           patterns of this and the segment
                 //           builder significantly better, by
                 //           retaining it across primitives.
                 let mut segments = Vec::new();
 
                 segment_builder.build(|segment| {
@@ -2083,87 +2152,95 @@ fn write_brush_segment_description(
                             [0.0; 4],
                             BrushFlags::empty(),
                         ),
                     );
                 });
 
                 brush.segment_desc = Some(BrushSegmentDescriptor {
                     segments,
-                    clip_mask_kind: clip_chain.clip_mask_kind,
                 });
             }
         }
     }
 }
 
 impl Primitive {
     fn update_clip_task_for_brush(
         &mut self,
         prim_context: &PrimitiveContext,
-        clip_chain: &ClipChainInstance,
+        prim_clip_chain: &ClipChainInstance,
         combined_outer_rect: &DeviceIntRect,
         pic_state: &mut PictureState,
         frame_context: &FrameBuildingContext,
         frame_state: &mut FrameBuildingState,
     ) -> bool {
         debug_assert!(frame_context.screen_rect.contains_rect(combined_outer_rect));
 
         let brush = match self.details {
             PrimitiveDetails::Brush(ref mut brush) => brush,
             PrimitiveDetails::TextRun(..) => return false,
         };
 
         write_brush_segment_description(
             brush,
             &self.metadata,
-            clip_chain,
+            prim_clip_chain,
             frame_state,
         );
 
         let segment_desc = match brush.segment_desc {
             Some(ref mut description) => description,
             None => return false,
         };
-        let clip_mask_kind = segment_desc.clip_mask_kind;
 
         for segment in &mut segment_desc.segments {
-            if !segment.may_need_clip_mask && clip_mask_kind != BrushClipMaskKind::Global {
-                segment.clip_task_id = BrushSegmentTaskId::Opaque;
-                continue;
-            }
+            // Build a clip chain for the smaller segment rect. This will
+            // often manage to eliminate most/all clips, and sometimes
+            // clip the segment completely.
+            let segment_clip_chain = frame_state
+                .clip_store
+                .build_clip_chain_instance(
+                    self.metadata.clip_chain_id,
+                    segment.local_rect,
+                    self.metadata.local_clip_rect,
+                    prim_context.spatial_node_index,
+                    &frame_context.clip_scroll_tree,
+                    frame_state.gpu_cache,
+                    frame_state.resource_cache,
+                    frame_context.device_pixel_scale,
+                );
 
-            let intersected_rect = calculate_screen_bounding_rect(
-                &prim_context.spatial_node.world_content_transform,
-                &segment.local_rect,
-                frame_context.device_pixel_scale,
-                Some(&combined_outer_rect),
-            );
+            match segment_clip_chain {
+                Some(segment_clip_chain) => {
+                    if segment_clip_chain.clips_range.count == 0 {
+                        segment.clip_task_id = BrushSegmentTaskId::Opaque;
+                        continue;
+                    }
+
+                    let bounds = (segment_clip_chain.world_clip_rect * frame_context.device_pixel_scale)
+                        .round_out()
+                        .to_i32();
 
-            let bounds = match intersected_rect {
-                Some(bounds) => bounds,
+                    let clip_task = RenderTask::new_mask(
+                        bounds,
+                        segment_clip_chain.clips_range,
+                        frame_state.clip_store,
+                        frame_state.gpu_cache,
+                        frame_state.resource_cache,
+                        frame_state.render_tasks,
+                    );
+
+                    let clip_task_id = frame_state.render_tasks.add(clip_task);
+                    pic_state.tasks.push(clip_task_id);
+                    segment.clip_task_id = BrushSegmentTaskId::RenderTaskId(clip_task_id);
+                }
                 None => {
                     segment.clip_task_id = BrushSegmentTaskId::Empty;
-                    continue;
                 }
-            };
-
-            if clip_chain.clips_range.count > 0 {
-                let clip_task = RenderTask::new_mask(
-                    bounds,
-                    clip_chain.clips_range,
-                    frame_state.clip_store,
-                    frame_state.gpu_cache,
-                    frame_state.resource_cache,
-                    frame_state.render_tasks,
-                );
-
-                let clip_task_id = frame_state.render_tasks.add(clip_task);
-                pic_state.tasks.push(clip_task_id);
-                segment.clip_task_id = BrushSegmentTaskId::RenderTaskId(clip_task_id);
             }
         }
 
         true
     }
 
     fn reset_clip_task(&mut self) {
         self.metadata.clip_task_id = None;
@@ -2749,17 +2826,16 @@ impl Primitive {
                         task_id
                     }
                 )
             });
 
             if needs_update {
                 brush.segment_desc = Some(BrushSegmentDescriptor {
                     segments: new_segments,
-                    clip_mask_kind: BrushClipMaskKind::Unknown,
                 });
 
                 // The segments have changed, so force the GPU cache to
                 // re-upload the primitive information.
                 frame_state.gpu_cache.invalidate(&mut self.metadata.gpu_location);
             }
         }
     }
--- a/gfx/webrender/src/util.rs
+++ b/gfx/webrender/src/util.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::{BorderRadius, DeviceIntPoint, DeviceIntRect, DeviceIntSize, DevicePixelScale};
-use api::{DevicePoint, DeviceRect, DeviceSize, LayoutPixel, LayoutPoint, LayoutRect, LayoutSize};
+use api::{DeviceRect, LayoutPixel, LayoutRect};
 use api::{WorldPixel, WorldRect};
 use euclid::{Point2D, Rect, Size2D, TypedPoint2D, TypedRect, TypedSize2D};
 use euclid::{TypedTransform2D, TypedTransform3D, TypedVector2D};
 use euclid::{HomogeneousVector};
 use num_traits::Zero;
 use plane_split::{Clipper, Polygon};
 use std::{i32, f32};
 use std::borrow::Cow;
@@ -326,47 +326,38 @@ pub mod test {
         assert_eq!(m1.inverse_project(&p0), Some(Point2D::new(2.0, 2.0)));
     }
 }
 
 pub trait MaxRect {
     fn max_rect() -> Self;
 }
 
-impl MaxRect for LayoutRect {
-    fn max_rect() -> Self {
-        LayoutRect::new(
-            LayoutPoint::new(f32::MIN / 2.0, f32::MIN / 2.0),
-            LayoutSize::new(f32::MAX, f32::MAX),
-        )
-    }
-}
-
 impl MaxRect for DeviceIntRect {
     fn max_rect() -> Self {
         DeviceIntRect::new(
             DeviceIntPoint::new(i32::MIN / 2, i32::MIN / 2),
             DeviceIntSize::new(i32::MAX, i32::MAX),
         )
     }
 }
 
-impl MaxRect for DeviceRect {
+impl<U> MaxRect for TypedRect<f32, U> {
     fn max_rect() -> Self {
         // Having an unlimited bounding box is fine up until we try
         // to cast it to `i32`, where we get `-2147483648` for any
         // values larger than or equal to 2^31.
         //
         // Note: clamping to i32::MIN and i32::MAX is not a solution,
         // with explanation left as an exercise for the reader.
         const MAX_COORD: f32 = 1.0e9;
 
-        DeviceRect::new(
-            DevicePoint::new(-MAX_COORD, -MAX_COORD),
-            DeviceSize::new(2.0 * MAX_COORD, 2.0 * MAX_COORD),
+        TypedRect::new(
+            TypedPoint2D::new(-MAX_COORD, -MAX_COORD),
+            TypedSize2D::new(2.0 * MAX_COORD, 2.0 * MAX_COORD),
         )
     }
 }
 
 /// An enum that tries to avoid expensive transformation matrix calculations
 /// when possible when dealing with non-perspective axis-aligned transformations.
 #[derive(Debug, Clone, Copy)]
 pub enum FastTransform<Src, Dst> {
--- a/gfx/webrender_api/src/display_item.rs
+++ b/gfx/webrender_api/src/display_item.rs
@@ -258,16 +258,28 @@ pub struct TextDisplayItem {
 pub struct NormalBorder {
     pub left: BorderSide,
     pub right: BorderSide,
     pub top: BorderSide,
     pub bottom: BorderSide,
     pub radius: BorderRadius,
 }
 
+impl NormalBorder {
+    // Construct a border based upon self with color
+    pub fn with_color(&self, color: ColorF) -> Self {
+        let mut b = *self;
+        b.left.color = color;
+        b.right.color = color;
+        b.top.color = color;
+        b.bottom.color = color;
+        b
+    }
+}
+
 #[repr(u32)]
 #[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
 pub enum RepeatMode {
     Stretch,
     Repeat,
     Round,
     Space,
 }
--- a/gfx/webrender_bindings/revision.txt
+++ b/gfx/webrender_bindings/revision.txt
@@ -1,1 +1,1 @@
-93997662842b6d8bafbdb3dde79009c930db66ca
+816ff14c1805c145ccd60d0227d82b1541fc24eb