diff --git a/webrender/src/box_shadow.rs b/webrender/src/box_shadow.rs index 360dffe7e9..7c601317df 100644 --- a/webrender/src/box_shadow.rs +++ b/webrender/src/box_shadow.rs @@ -405,8 +405,7 @@ impl<'a> SceneBuilder<'a> { // box-shadow support will be added to this path as a follow up. if is_root_coord_system && clip_mode == BoxShadowClipMode::Outset && - blur_radius < 32.0 && - false { + blur_radius < 32.0 { // Make sure corners don't overlap. ensure_no_corner_overlap(&mut shadow_radius, shadow_rect.size()); @@ -446,7 +445,7 @@ impl<'a> SceneBuilder<'a> { let inner_shadow_rect = shadow_rect.inflate(-sig3, -sig3); let outer_shadow_rect = shadow_rect.inflate( sig3, sig3); - let inner_shadow_rect = extract_inner_rect_k(&inner_shadow_rect, &shadow_radius, 0.5).unwrap_or(LayoutRect::zero()); + let inner_shadow_rect = extract_inner_rect_k(&inner_shadow_rect, &shadow_radius, 1.0).unwrap_or(LayoutRect::zero()); let prim = BoxShadow { color: color.into(), @@ -581,3 +580,104 @@ fn adjust_radius_for_box_shadow(border_radius: f32, spread_amount: f32) -> f32 { 0.0 } } + +pub struct BoxShadowSegment { + pub color: ColorF, + pub blur_radius: f32, + pub clip: ClipDataHandle, + pub pattern_rect: LayoutRect, +} + +impl PatternBuilder for BoxShadowSegment { + fn build( + &self, + _sub_rect: Option, + ctx: &crate::pattern::PatternBuilderContext, + state: &mut crate::pattern::PatternBuilderState, + ) -> Pattern { + let raster_spatial_node_index = ctx.spatial_tree.root_reference_frame_index(); + + let task_size = self.pattern_rect.size().cast_unit().to_i32(); + let content_origin = self.pattern_rect.min.cast_unit(); + let scale_factor = DevicePixelScale::new(1.0); + let uv_rect_kind = UvRectKind::Rect; + + let blur_radius = self.blur_radius * scale_factor.0; + let clips_range = state.clip_store.push_clip_instance(self.clip); + let color_pattern = Pattern::color(self.color); + + let pattern_prim_address_f = quad::write_prim_blocks( + &mut state.frame_gpu_data.f32, + self.pattern_rect, + self.pattern_rect, + color_pattern.base_color, + color_pattern.texture_input.task_id, + &[], + ScaleOffset::identity(), + ); + + let pattern_task_id = state.rg_builder.add().init(RenderTask::new_dynamic( + task_size, + RenderTaskKind::Prim(PrimTask { + pattern: color_pattern.kind, + pattern_input: color_pattern.shader_input, + raster_spatial_node_index, + device_pixel_scale: DevicePixelScale::new(1.0), + content_origin, + prim_address_f: pattern_prim_address_f, + transform_id: TransformPaletteId::IDENTITY, + edge_flags: EdgeAaSegmentMask::empty(), + quad_flags: QuadFlags::APPLY_RENDER_TASK_CLIP | QuadFlags::IGNORE_DEVICE_PIXEL_SCALE, + prim_needs_scissor_rect: false, + texture_input: color_pattern.texture_input.task_id, + }), + )); + + let masks = MaskSubPass { + clip_node_range: clips_range, + prim_spatial_node_index: raster_spatial_node_index, + prim_address_f: pattern_prim_address_f, + }; + + let task = state.rg_builder.get_task_mut(pattern_task_id); + task.add_sub_pass(SubPass::Masks { masks }); + + let blur_task_v = state.rg_builder.add().init(RenderTask::new_dynamic( + task_size, + RenderTaskKind::VerticalBlur(BlurTask { + blur_std_deviation: blur_radius, + target_kind: RenderTargetKind::Color, + blur_region: task_size, + }), + )); + state.rg_builder.add_dependency(blur_task_v, pattern_task_id); + + let blur_task_h = state.rg_builder.add().init(RenderTask::new_dynamic( + task_size, + RenderTaskKind::HorizontalBlur(BlurTask { + blur_std_deviation: blur_radius, + target_kind: RenderTargetKind::Color, + blur_region: task_size, + }), + ).with_uv_rect_kind(uv_rect_kind)); + state.rg_builder.add_dependency(blur_task_h, blur_task_v); + + Pattern::texture( + blur_task_h, + self.color, + ) + } + + fn get_base_color( + &self, + _ctx: &crate::pattern::PatternBuilderContext, + ) -> ColorF { + self.color + } + + fn use_shared_pattern( + &self, + ) -> bool { + false + } +} diff --git a/webrender/src/pattern.rs b/webrender/src/pattern.rs index 3d73d9a746..52ec85bbab 100644 --- a/webrender/src/pattern.rs +++ b/webrender/src/pattern.rs @@ -142,3 +142,33 @@ impl Pattern { } } } + +// A reusable pattern builder for a segment of a larger primitive that can be +// drawn as a solid color +pub struct SolidColorSegment { + pub color: ColorF, +} + +impl PatternBuilder for SolidColorSegment { + fn build( + &self, + _sub_rect: Option, + _ctx: &crate::pattern::PatternBuilderContext, + _state: &mut crate::pattern::PatternBuilderState, + ) -> Pattern { + Pattern::color(self.color) + } + + fn get_base_color( + &self, + _ctx: &crate::pattern::PatternBuilderContext, + ) -> ColorF { + self.color + } + + fn use_shared_pattern( + &self, + ) -> bool { + true + } +} diff --git a/webrender/src/prepare.rs b/webrender/src/prepare.rs index 0a7c0d3188..5612271c00 100644 --- a/webrender/src/prepare.rs +++ b/webrender/src/prepare.rs @@ -11,12 +11,13 @@ use api::{BoxShadowClipMode, BorderStyle, ClipMode}; use api::units::*; use euclid::Scale; use smallvec::SmallVec; +use crate::box_shadow::BoxShadowSegment; use crate::composite::CompositorSurfaceKind; use crate::command_buffer::{CommandBufferIndex, PrimitiveCommand}; use crate::image_tiling::{self, Repetition}; use crate::border::{get_max_scale_for_border, build_border_instances}; -use crate::clip::{ClipStore, ClipNodeRange}; -use crate::pattern::Pattern; +use crate::clip::{ClipNodeRange, ClipStore}; +use crate::pattern::{Pattern, SolidColorSegment}; use crate::spatial_tree::{SpatialNodeIndex, SpatialTree}; use crate::clip::{ClipDataStore, ClipNodeFlags, ClipChainInstance, ClipItemKind}; use crate::frame_builder::{FrameBuildingContext, FrameBuildingState, PictureContext, PictureState}; @@ -324,21 +325,164 @@ fn prepare_interned_prim_for_render( PrimitiveInstanceKind::BoxShadow { data_handle } => { let prim_data = &mut data_stores.box_shadow[*data_handle]; - quad::prepare_quad( - prim_data, - &prim_data.kind.outer_shadow_rect, - prim_instance_index, - prim_spatial_node_index, - &prim_instance.vis.clip_chain, - device_pixel_scale, - frame_context, - pic_context, - targets, - &data_stores.clip, - frame_state, - pic_state, - scratch, - ); + // If there isn't an inner shadow rect, draw as a single primitive + if prim_data.kind.inner_shadow_rect.is_empty() { + quad::prepare_quad( + prim_data, + &prim_data.kind.outer_shadow_rect, + prim_instance_index, + prim_spatial_node_index, + &prim_instance.vis.clip_chain, + device_pixel_scale, + frame_context, + pic_context, + targets, + &data_stores.clip, + frame_state, + pic_state, + scratch, + ); + } else { + // If we have an inner shadow rect, draw that as a solid color + // segment, and box-shadow segments for the outer segments + let dirty_world_rect = frame_state.current_dirty_region().combined; + + let inner_segment = SolidColorSegment { + color: prim_data.kind.color, + }; + + quad::prepare_quad( + &inner_segment, + &prim_data.kind.inner_shadow_rect, + prim_instance_index, + prim_spatial_node_index, + &prim_instance.vis.clip_chain, + device_pixel_scale, + frame_context, + pic_context, + targets, + &data_stores.clip, + frame_state, + pic_state, + scratch, + ); + + let p0 = prim_data.kind.outer_shadow_rect.min; + let p1 = prim_data.kind.inner_shadow_rect.min; + let p3 = prim_data.kind.outer_shadow_rect.max; + let p2 = prim_data.kind.inner_shadow_rect.max; + + let outer_rects = [ + // Corners + LayoutRect::new( + LayoutPoint::new(p0.x, p0.y), + LayoutPoint::new(p1.x, p1.y), + ), + LayoutRect::new( + LayoutPoint::new(p2.x, p0.y), + LayoutPoint::new(p3.x, p1.y), + ), + LayoutRect::new( + LayoutPoint::new(p2.x, p2.y), + LayoutPoint::new(p3.x, p3.y), + ), + LayoutRect::new( + LayoutPoint::new(p0.x, p2.y), + LayoutPoint::new(p1.x, p3.y), + ), + + // Top + Bottom + LayoutRect::new( + LayoutPoint::new(p1.x, p0.y), + LayoutPoint::new(p2.x, p1.y), + ), + LayoutRect::new( + LayoutPoint::new(p1.x, p2.y), + LayoutPoint::new(p2.x, p3.y), + ), + + // Left + Right + LayoutRect::new( + LayoutPoint::new(p0.x, p1.y), + LayoutPoint::new(p1.x, p2.y), + ), + LayoutRect::new( + LayoutPoint::new(p2.x, p1.y), + LayoutPoint::new(p3.x, p2.y), + ), + ]; + + for (i, rect) in outer_rects.iter().enumerate() { + // Edge (non-corner) segments are the same across the + // entire axis, so we can draw a small portion of them + // and stretch when drawing the segment, to reduce + // number of blurred pixels, which is important for swgl. + let fixed_size = 4.0; + + let pattern_rect = match i { + 0 .. 4 => *rect, + 4 .. 6 => { + LayoutRect::new( + rect.min, + LayoutPoint::new(rect.min.x + fixed_size, rect.max.y), + ) + }, + 6 .. 8 => { + LayoutRect::new( + rect.min, + LayoutPoint::new(rect.max.x, rect.min.y + fixed_size), + ) + } + _ => unreachable!(), + }; + + let shadow_segment = BoxShadowSegment { + color: prim_data.kind.color, + blur_radius: prim_data.kind.blur_radius, + clip: prim_data.kind.clip, + pattern_rect, + }; + + frame_state.clip_store.set_active_clips_from_clip_chain( + &prim_instance.vis.clip_chain, + prim_spatial_node_index, + &frame_context.spatial_tree, + &data_stores.clip, + ); + + if let Some(segment_clip_chain) = frame_state + .clip_store + .build_clip_chain_instance( + *rect, + &pic_state.map_local_to_pic, + &pic_state.map_pic_to_world, + &frame_context.spatial_tree, + frame_state.gpu_cache, + frame_state.resource_cache, + device_pixel_scale, + &dirty_world_rect, + &mut data_stores.clip, + frame_state.rg_builder, + false, + ) { + quad::prepare_quad( + &shadow_segment, + rect, + prim_instance_index, + prim_spatial_node_index, + &segment_clip_chain, + device_pixel_scale, + frame_context, + pic_context, + targets, + &data_stores.clip, + frame_state, + pic_state, + scratch, + ); + } + } + } return; } diff --git a/wrench/reftests/boxshadow/box-shadow-border-radii.png b/wrench/reftests/boxshadow/box-shadow-border-radii.png index 0741826160..9d86e6ba2b 100644 Binary files a/wrench/reftests/boxshadow/box-shadow-border-radii.png and b/wrench/reftests/boxshadow/box-shadow-border-radii.png differ diff --git a/wrench/reftests/boxshadow/box-shadow-cache.png b/wrench/reftests/boxshadow/box-shadow-cache.png index 96d2c7fed3..3d5eac2be6 100644 Binary files a/wrench/reftests/boxshadow/box-shadow-cache.png and b/wrench/reftests/boxshadow/box-shadow-cache.png differ diff --git a/wrench/reftests/boxshadow/box-shadow-huge-radius.png b/wrench/reftests/boxshadow/box-shadow-huge-radius.png index f2be685139..a0780b06c1 100644 Binary files a/wrench/reftests/boxshadow/box-shadow-huge-radius.png and b/wrench/reftests/boxshadow/box-shadow-huge-radius.png differ diff --git a/wrench/reftests/boxshadow/box-shadow-stretch-mode-x.png b/wrench/reftests/boxshadow/box-shadow-stretch-mode-x.png index 5d631c182b..53ec507506 100644 Binary files a/wrench/reftests/boxshadow/box-shadow-stretch-mode-x.png and b/wrench/reftests/boxshadow/box-shadow-stretch-mode-x.png differ diff --git a/wrench/reftests/boxshadow/box-shadow-suite-blur.png b/wrench/reftests/boxshadow/box-shadow-suite-blur.png index 67b46a609e..87b02275d0 100644 Binary files a/wrench/reftests/boxshadow/box-shadow-suite-blur.png and b/wrench/reftests/boxshadow/box-shadow-suite-blur.png differ diff --git a/wrench/reftests/boxshadow/overlap1.png b/wrench/reftests/boxshadow/overlap1.png index a47c3ad696..8800119b67 100644 Binary files a/wrench/reftests/boxshadow/overlap1.png and b/wrench/reftests/boxshadow/overlap1.png differ