author | WR 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 id | 35038 |
push user | rmaries@mozilla.com |
push date | Wed, 14 Nov 2018 22:12:17 +0000 |
treeherder | mozilla-central@4e1b2b7e0c37 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | kats |
bugs | 1507134 |
milestone | 65.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
|
--- 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;