Bug 1507134 - Update webrender to commit b5c232fdc1e6731cb1afadbf6a6b9b8038ccc829 (WR PR #3306). r=kats
authorWR Updater Bot <graphics-team@mozilla.staktrace.com>
Wed, 14 Nov 2018 13:52:59 +0000
changeset 446181 d227b05e7e96992ddc23ab2ed40916b7c318cfc0
parent 446180 2fbaf123b20b792af3601a22879cc25a54d713ba
child 446182 d58c53ac8dfc966f3e3da01f4ef95da55bf5e31d
push id35038
push userrmaries@mozilla.com
push dateWed, 14 Nov 2018 22:12:17 +0000
treeherdermozilla-central@4e1b2b7e0c37 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskats
bugs1507134
milestone65.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 1507134 - Update webrender to commit b5c232fdc1e6731cb1afadbf6a6b9b8038ccc829 (WR PR #3306). r=kats Differential Revision: https://phabricator.services.mozilla.com/D11878
gfx/webrender/src/box_shadow.rs
gfx/webrender/src/clip.rs
gfx/webrender/src/prim_store.rs
gfx/webrender_bindings/revision.txt
--- a/gfx/webrender/src/box_shadow.rs
+++ b/gfx/webrender/src/box_shadow.rs
@@ -1,29 +1,23 @@
 /* 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, BoxShadowClipMode, ClipMode, ColorF, DeviceIntSize, LayoutPrimitiveInfo};
-use api::{LayoutRect, LayoutSize, LayoutToDeviceScale, LayoutVector2D, MAX_BLUR_RADIUS};
+use api::{LayoutRect, LayoutSize, LayoutVector2D, MAX_BLUR_RADIUS};
 use clip::ClipItemKey;
 use display_list_flattener::DisplayListFlattener;
 use gpu_cache::GpuCacheHandle;
 use gpu_types::BoxShadowStretchMode;
 use prim_store::{BrushKind, BrushPrimitive, PrimitiveContainer};
 use prim_store::ScrollNodeAndClipChain;
 use render_task::RenderTaskCacheEntryHandle;
 use util::RectHelpers;
 
-/// In the majority of cases box-shadow radius in device pixels should be quite
-/// small (< 256), so the choice of max is trade off between panic! in
-/// pathological case of being presented with huge radius and using too much
-/// texture memory for better rendering of such cases.
-const MAX_BOX_SHADOW_RESOLUTION: u32 = 2048;
-
 #[derive(Debug, Clone)]
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 pub struct BoxShadowClipSource {
     // Parameters that define the shadow and are constant.
     pub shadow_radius: BorderRadius,
     pub blur_radius: f32,
     pub clip_mode: BoxShadowClipMode,
@@ -34,16 +28,20 @@ pub struct BoxShadowClipSource {
     // to the cached clip region and blurred texture.
     pub cache_key: Option<(DeviceIntSize, BoxShadowCacheKey)>,
     pub cache_handle: Option<RenderTaskCacheEntryHandle>,
     pub clip_data_handle: GpuCacheHandle,
 
     // Local-space size of the required render task size.
     pub shadow_rect_alloc_size: LayoutSize,
 
+    // Local-space size of the required render task size without any downscaling
+    // applied. This is needed to stretch the shadow properly.
+    pub original_alloc_size: LayoutSize,
+
     // The minimal shadow rect for the parameters above,
     // used when drawing the shadow rect to be blurred.
     pub minimal_shadow_rect: LayoutRect,
 
     // Local space rect for the shadow to be drawn or
     // stretched in the shadow primitive.
     pub prim_shadow_rect: LayoutRect,
 }
@@ -55,17 +53,19 @@ pub const BLUR_SAMPLE_SCALE: f32 = 3.0;
 // and blurred box-shadow rect that can be stored in the
 // texture cache and applied to clip-masks.
 #[derive(Debug, Clone, Eq, Hash, PartialEq)]
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 pub struct BoxShadowCacheKey {
     pub blur_radius_dp: i32,
     pub clip_mode: BoxShadowClipMode,
-    pub rect_size: DeviceIntSize,
+    // NOTE(emilio): Only the original allocation size needs to be in the cache
+    // key, since the actual size is derived from that.
+    pub original_alloc_size: DeviceIntSize,
     pub br_top_left: DeviceIntSize,
     pub br_top_right: DeviceIntSize,
     pub br_bottom_right: DeviceIntSize,
     pub br_bottom_left: DeviceIntSize,
 }
 
 impl<'a> DisplayListFlattener<'a> {
     pub fn add_box_shadow(
@@ -245,13 +245,8 @@ fn adjust_corner_for_box_shadow(corner: 
 
 fn adjust_radius_for_box_shadow(border_radius: f32, spread_amount: f32) -> f32 {
     if border_radius > 0.0 {
         (border_radius + spread_amount).max(0.0)
     } else {
         0.0
     }
 }
-
-pub fn get_max_scale_for_box_shadow(rect_size: &LayoutSize) -> LayoutToDeviceScale {
-    let r = rect_size.width.max(rect_size.height);
-    LayoutToDeviceScale::new(MAX_BOX_SHADOW_RESOLUTION as f32 / r)
-}
--- a/gfx/webrender/src/clip.rs
+++ b/gfx/webrender/src/clip.rs
@@ -5,17 +5,16 @@
 use api::{BorderRadius, ClipMode, ComplexClipRegion, DeviceIntRect, DevicePixelScale, ImageMask};
 use api::{ImageRendering, LayoutRect, LayoutSize, LayoutPoint, LayoutVector2D};
 use api::{BoxShadowClipMode, LayoutToWorldScale, PicturePixel, WorldPixel};
 use api::{PictureRect, LayoutPixel, WorldPoint, WorldSize, WorldRect, LayoutToWorldTransform};
 use api::{VoidPtrToSizeFn, LayoutRectAu, ImageKey, AuHelpers};
 use app_units::Au;
 use border::{ensure_no_corner_overlap, BorderRadiusAu};
 use box_shadow::{BLUR_SAMPLE_SCALE, BoxShadowClipSource, BoxShadowCacheKey};
-use box_shadow::get_max_scale_for_box_shadow;
 use clip_scroll_tree::{ClipScrollTree, ROOT_SPATIAL_NODE_INDEX, SpatialNodeIndex};
 use ellipse::Ellipse;
 use gpu_cache::{GpuCache, GpuCacheHandle, ToGpuBlocks};
 use gpu_types::{BoxShadowStretchMode};
 use image::{self, Repetition};
 use intern;
 use internal_types::FastHashSet;
 use prim_store::{ClipData, ImageMaskData, SpaceMapper, VisibleMaskImageTile};
@@ -334,18 +333,18 @@ impl ClipNode {
                         }
                         resource_cache.request_image(request, gpu_cache);
                     }
                 }
             }
             ClipItem::BoxShadow(ref mut info) => {
                 if let Some(mut request) = gpu_cache.request(&mut self.gpu_cache_handle) {
                     request.push([
-                        info.shadow_rect_alloc_size.width,
-                        info.shadow_rect_alloc_size.height,
+                        info.original_alloc_size.width,
+                        info.original_alloc_size.height,
                         info.clip_mode as i32 as f32,
                         0.0,
                     ]);
                     request.push([
                         info.stretch_mode_x as i32 as f32,
                         info.stretch_mode_y as i32 as f32,
                         0.0,
                         0.0,
@@ -353,31 +352,25 @@ impl ClipNode {
                     request.push(info.prim_shadow_rect);
                 }
 
                 // Quote from https://drafts.csswg.org/css-backgrounds-3/#shadow-blur
                 // "the image that would be generated by applying to the shadow a
                 // Gaussian blur with a standard deviation equal to half the blur radius."
                 let blur_radius_dp = info.blur_radius * 0.5;
 
-                // Create scaling from requested size to cache size.  If the
-                // requested size exceeds the maximum size of the cache, the
-                // box shadow will be scaled down and stretched when rendered
-                // into the document.
-                let world_scale = LayoutToWorldScale::new(1.0);
-                let mut content_scale = world_scale * device_pixel_scale;
-                let max_scale = get_max_scale_for_box_shadow(&info.shadow_rect_alloc_size);
-                content_scale.0 = content_scale.0.min(max_scale.0);
+                // Create scaling from requested size to cache size.
+                let content_scale = LayoutToWorldScale::new(1.0) * device_pixel_scale;
 
                 // Create the cache key for this box-shadow render task.
                 let cache_size = to_cache_size(info.shadow_rect_alloc_size * content_scale);
                 let bs_cache_key = BoxShadowCacheKey {
                     blur_radius_dp: (blur_radius_dp * content_scale.0).round() as i32,
                     clip_mode: info.clip_mode,
-                    rect_size: (info.shadow_rect_alloc_size * content_scale).round().to_i32(),
+                    original_alloc_size: (info.original_alloc_size * content_scale).round().to_i32(),
                     br_top_left: (info.shadow_radius.top_left * content_scale).round().to_i32(),
                     br_top_right: (info.shadow_radius.top_right * content_scale).round().to_i32(),
                     br_bottom_right: (info.shadow_radius.bottom_right * content_scale).round().to_i32(),
                     br_bottom_left: (info.shadow_radius.bottom_left * content_scale).round().to_i32(),
                 };
 
                 info.cache_key = Some((cache_size, bs_cache_key));
 
@@ -876,113 +869,171 @@ impl ClipItemKey {
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 pub enum ClipItem {
     Rectangle(LayoutRect, ClipMode),
     RoundedRectangle(LayoutRect, BorderRadius, ClipMode),
     Image { mask: ImageMask, visible_tiles: Option<Vec<VisibleMaskImageTile>> },
     BoxShadow(BoxShadowClipSource),
 }
 
+fn compute_box_shadow_parameters(
+    shadow_rect: LayoutRect,
+    mut shadow_radius: BorderRadius,
+    prim_shadow_rect: LayoutRect,
+    blur_radius: f32,
+    clip_mode: BoxShadowClipMode,
+) -> BoxShadowClipSource {
+    // Make sure corners don't overlap.
+    ensure_no_corner_overlap(&mut shadow_radius, &shadow_rect);
+
+    // Get the fractional offsets required to match the
+    // source rect with a minimal rect.
+    let fract_offset = LayoutPoint::new(
+        shadow_rect.origin.x.fract().abs(),
+        shadow_rect.origin.y.fract().abs(),
+    );
+    let fract_size = LayoutSize::new(
+        shadow_rect.size.width.fract().abs(),
+        shadow_rect.size.height.fract().abs(),
+    );
+
+    // Create a minimal size primitive mask to blur. In this
+    // case, we ensure the size of each corner is the same,
+    // to simplify the shader logic that stretches the blurred
+    // result across the primitive.
+    let max_corner_width = shadow_radius.top_left.width
+                                .max(shadow_radius.bottom_left.width)
+                                .max(shadow_radius.top_right.width)
+                                .max(shadow_radius.bottom_right.width);
+    let max_corner_height = shadow_radius.top_left.height
+                                .max(shadow_radius.bottom_left.height)
+                                .max(shadow_radius.top_right.height)
+                                .max(shadow_radius.bottom_right.height);
+
+    // Get maximum distance that can be affected by given blur radius.
+    let blur_region = (BLUR_SAMPLE_SCALE * blur_radius).ceil();
+
+    // If the largest corner is smaller than the blur radius, we need to ensure
+    // that it's big enough that the corners don't affect the middle segments.
+    let used_corner_width = max_corner_width.max(blur_region);
+    let used_corner_height = max_corner_height.max(blur_region);
+
+    // Minimal nine-patch size, corner + internal + corner.
+    let min_shadow_rect_size = LayoutSize::new(
+        2.0 * used_corner_width + blur_region,
+        2.0 * used_corner_height + blur_region,
+    );
+
+    // The minimal rect to blur.
+    let mut minimal_shadow_rect = LayoutRect::new(
+        LayoutPoint::new(
+            blur_region + fract_offset.x,
+            blur_region + fract_offset.y,
+        ),
+        LayoutSize::new(
+            min_shadow_rect_size.width + fract_size.width,
+            min_shadow_rect_size.height + fract_size.height,
+        ),
+    );
+
+    // If the width or height ends up being bigger than the original
+    // primitive shadow rect, just blur the entire rect along that
+    // axis and draw that as a simple blit. This is necessary for
+    // correctness, since the blur of one corner may affect the blur
+    // in another corner.
+    let mut stretch_mode_x = BoxShadowStretchMode::Stretch;
+    if shadow_rect.size.width < minimal_shadow_rect.size.width {
+        minimal_shadow_rect.size.width = shadow_rect.size.width;
+        stretch_mode_x = BoxShadowStretchMode::Simple;
+    }
+
+    let mut stretch_mode_y = BoxShadowStretchMode::Stretch;
+    if shadow_rect.size.height < minimal_shadow_rect.size.height {
+        minimal_shadow_rect.size.height = shadow_rect.size.height;
+        stretch_mode_y = BoxShadowStretchMode::Simple;
+    }
+
+    // Expand the shadow rect by enough room for the blur to take effect.
+    let shadow_rect_alloc_size = LayoutSize::new(
+        2.0 * blur_region + minimal_shadow_rect.size.width.ceil(),
+        2.0 * blur_region + minimal_shadow_rect.size.height.ceil(),
+    );
+
+    BoxShadowClipSource {
+        original_alloc_size: shadow_rect_alloc_size,
+        shadow_rect_alloc_size,
+        shadow_radius,
+        prim_shadow_rect,
+        blur_radius,
+        clip_mode,
+        stretch_mode_x,
+        stretch_mode_y,
+        cache_handle: None,
+        cache_key: None,
+        clip_data_handle: GpuCacheHandle::new(),
+        minimal_shadow_rect,
+    }
+}
+
 impl ClipItem {
     pub fn new_box_shadow(
         shadow_rect: LayoutRect,
         mut shadow_radius: BorderRadius,
         prim_shadow_rect: LayoutRect,
         blur_radius: f32,
         clip_mode: BoxShadowClipMode,
     ) -> Self {
-        // Make sure corners don't overlap.
-        ensure_no_corner_overlap(&mut shadow_radius, &shadow_rect);
-
-        // Get the fractional offsets required to match the
-        // source rect with a minimal rect.
-        let fract_offset = LayoutPoint::new(
-            shadow_rect.origin.x.fract().abs(),
-            shadow_rect.origin.y.fract().abs(),
-        );
-        let fract_size = LayoutSize::new(
-            shadow_rect.size.width.fract().abs(),
-            shadow_rect.size.height.fract().abs(),
-        );
-
-        // Create a minimal size primitive mask to blur. In this
-        // case, we ensure the size of each corner is the same,
-        // to simplify the shader logic that stretches the blurred
-        // result across the primitive.
-        let max_corner_width = shadow_radius.top_left.width
-                                    .max(shadow_radius.bottom_left.width)
-                                    .max(shadow_radius.top_right.width)
-                                    .max(shadow_radius.bottom_right.width);
-        let max_corner_height = shadow_radius.top_left.height
-                                    .max(shadow_radius.bottom_left.height)
-                                    .max(shadow_radius.top_right.height)
-                                    .max(shadow_radius.bottom_right.height);
-
-        // Get maximum distance that can be affected by given blur radius.
-        let blur_region = (BLUR_SAMPLE_SCALE * blur_radius).ceil();
-
-        // If the largest corner is smaller than the blur radius, we need to ensure
-        // that it's big enough that the corners don't affect the middle segments.
-        let used_corner_width = max_corner_width.max(blur_region);
-        let used_corner_height = max_corner_height.max(blur_region);
-
-        // Minimal nine-patch size, corner + internal + corner.
-        let min_shadow_rect_size = LayoutSize::new(
-            2.0 * used_corner_width + blur_region,
-            2.0 * used_corner_height + blur_region,
-        );
-
-        // The minimal rect to blur.
-        let mut minimal_shadow_rect = LayoutRect::new(
-            LayoutPoint::new(
-                blur_region + fract_offset.x,
-                blur_region + fract_offset.y,
-            ),
-            LayoutSize::new(
-                min_shadow_rect_size.width + fract_size.width,
-                min_shadow_rect_size.height + fract_size.height,
-            ),
-        );
-
-        // If the width or height ends up being bigger than the original
-        // primitive shadow rect, just blur the entire rect along that
-        // axis and draw that as a simple blit. This is necessary for
-        // correctness, since the blur of one corner may affect the blur
-        // in another corner.
-        let mut stretch_mode_x = BoxShadowStretchMode::Stretch;
-        if shadow_rect.size.width < minimal_shadow_rect.size.width {
-            minimal_shadow_rect.size.width = shadow_rect.size.width;
-            stretch_mode_x = BoxShadowStretchMode::Simple;
-        }
-
-        let mut stretch_mode_y = BoxShadowStretchMode::Stretch;
-        if shadow_rect.size.height < minimal_shadow_rect.size.height {
-            minimal_shadow_rect.size.height = shadow_rect.size.height;
-            stretch_mode_y = BoxShadowStretchMode::Simple;
-        }
-
-        // Expand the shadow rect by enough room for the blur to take effect.
-        let shadow_rect_alloc_size = LayoutSize::new(
-            2.0 * blur_region + minimal_shadow_rect.size.width.ceil(),
-            2.0 * blur_region + minimal_shadow_rect.size.height.ceil(),
-        );
-
-        ClipItem::BoxShadow(BoxShadowClipSource {
-            shadow_rect_alloc_size,
+        let mut source = compute_box_shadow_parameters(
+            shadow_rect,
             shadow_radius,
             prim_shadow_rect,
             blur_radius,
             clip_mode,
-            stretch_mode_x,
-            stretch_mode_y,
-            cache_handle: None,
-            cache_key: None,
-            clip_data_handle: GpuCacheHandle::new(),
-            minimal_shadow_rect,
-        })
+        );
+
+        fn needed_downscaling(source: &BoxShadowClipSource) -> Option<f32> {
+            // This size is fairly arbitrary, but it's the same as the size that
+            // we use to avoid caching big blurred stacking contexts.
+            //
+            // If you change it, ensure that the reftests
+            // box-shadow-large-blur-radius-* still hit the downscaling path,
+            // and that they render correctly.
+            const MAX_SIZE: f32 = 2048.;
+
+            let max_dimension =
+                source.shadow_rect_alloc_size.width.max(source.shadow_rect_alloc_size.height);
+
+            if max_dimension > MAX_SIZE {
+                Some(MAX_SIZE / max_dimension)
+            } else {
+                None
+            }
+        }
+
+        if let Some(downscale) = needed_downscaling(&source) {
+            shadow_radius.bottom_left.height *= downscale;
+            shadow_radius.bottom_left.width *= downscale;
+            shadow_radius.bottom_right.height *= downscale;
+            shadow_radius.bottom_right.width *= downscale;
+            shadow_radius.top_left.height *= downscale;
+            shadow_radius.top_left.width *= downscale;
+            shadow_radius.top_right.height *= downscale;
+            shadow_radius.top_right.width *= downscale;
+
+            let original_alloc_size = source.shadow_rect_alloc_size;
+            source = compute_box_shadow_parameters(
+                shadow_rect.scale(downscale, downscale),
+                shadow_radius,
+                prim_shadow_rect,
+                blur_radius * downscale,
+                clip_mode,
+            );
+            source.original_alloc_size = original_alloc_size;
+        }
+        ClipItem::BoxShadow(source)
     }
 
     // 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 {
--- a/gfx/webrender/src/prim_store.rs
+++ b/gfx/webrender/src/prim_store.rs
@@ -2718,18 +2718,18 @@ impl BrushPrimitive {
 
                     // Push a region into the segment builder where the
                     // box-shadow can have an effect on the result. This
                     // ensures clip-mask tasks get allocated for these
                     // pixel regions, even if no other clips affect them.
                     segment_builder.push_mask_region(
                         info.prim_shadow_rect,
                         info.prim_shadow_rect.inflate(
-                            -0.5 * info.shadow_rect_alloc_size.width,
-                            -0.5 * info.shadow_rect_alloc_size.height,
+                            -0.5 * info.original_alloc_size.width,
+                            -0.5 * info.original_alloc_size.height,
                         ),
                         inner_clip_mode,
                     );
 
                     continue;
                 }
                 ClipItem::Image { .. } => {
                     rect_clips_only = false;
--- a/gfx/webrender_bindings/revision.txt
+++ b/gfx/webrender_bindings/revision.txt
@@ -1,1 +1,1 @@
-2a9ddf230c9816d5801a36848981e44f2e442fea
+b5c232fdc1e6731cb1afadbf6a6b9b8038ccc829