Bug 1656711. Support hardware accelerated <feGaussianBlur> SVG filters if the x and y radius are not equal with webrender. r=gw
☠☠ backed out by dea8656d5ca3 ☠ ☠
authorTimothy Nikkel <tnikkel@gmail.com>
Sun, 02 Aug 2020 21:33:50 +0000
changeset 543040 fed45fbadc26ca8595d6ba86f4b7aba2f5ed6708
parent 543039 e0df0cffb3883937384e769dcaa228d1097c388c
child 543041 dea8656d5ca3eb2766cfd4744ca2b351b9435dc2
push id37662
push userdluca@mozilla.com
push dateMon, 03 Aug 2020 03:30:15 +0000
treeherdermozilla-central@f6a3b097f8af [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgw
bugs1656711
milestone81.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 1656711. Support hardware accelerated <feGaussianBlur> SVG filters if the x and y radius are not equal with webrender. r=gw Pretty straight forward, mostly just plumbing stuff through. Differential Revision: https://phabricator.services.mozilla.com/D85666
gfx/wr/webrender/src/internal_types.rs
gfx/wr/webrender/src/picture.rs
gfx/wr/webrender/src/prim_store/picture.rs
gfx/wr/webrender/src/render_task.rs
gfx/wr/webrender/src/scene_building.rs
gfx/wr/webrender_api/src/display_item.rs
gfx/wr/wrench/src/yaml_helper.rs
layout/svg/FilterInstance.cpp
layout/svg/SVGIntegrationUtils.cpp
--- a/gfx/wr/webrender/src/internal_types.rs
+++ b/gfx/wr/webrender/src/internal_types.rs
@@ -65,17 +65,17 @@ pub type PlaneSplitter = BspSplitter<f64
 const OPACITY_EPSILON: f32 = 0.001;
 
 /// Equivalent to api::FilterOp with added internal information
 #[derive(Clone, Debug, PartialEq)]
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 pub enum Filter {
     Identity,
-    Blur(f32),
+    Blur(f32, f32),
     Brightness(f32),
     Contrast(f32),
     Grayscale(f32),
     HueRotate(f32),
     Invert(f32),
     Opacity(api::PropertyBinding<f32>, f32),
     Saturate(f32),
     Sepia(f32),
@@ -111,17 +111,17 @@ impl Filter {
                 color.a > OPACITY_EPSILON
             }
         }
     }
 
     pub fn is_noop(&self) -> bool {
         match *self {
             Filter::Identity => false, // this is intentional
-            Filter::Blur(length) => length == 0.0,
+            Filter::Blur(width, height) => width == 0.0 && height == 0.0,
             Filter::Brightness(amount) => amount == 1.0,
             Filter::Contrast(amount) => amount == 1.0,
             Filter::Grayscale(amount) => amount == 0.0,
             Filter::HueRotate(amount) => amount == 0.0,
             Filter::Invert(amount) => amount == 0.0,
             Filter::Opacity(api::PropertyBinding::Value(amount), _) => amount >= 1.0,
             Filter::Saturate(amount) => amount == 1.0,
             Filter::Sepia(amount) => amount == 0.0,
@@ -174,17 +174,17 @@ impl Filter {
         }
     }
 }
 
 impl From<FilterOp> for Filter {
     fn from(op: FilterOp) -> Self {
         match op {
             FilterOp::Identity => Filter::Identity,
-            FilterOp::Blur(r) => Filter::Blur(r),
+            FilterOp::Blur(w, h) => Filter::Blur(w, h),
             FilterOp::Brightness(b) => Filter::Brightness(b),
             FilterOp::Contrast(c) => Filter::Contrast(c),
             FilterOp::Grayscale(g) => Filter::Grayscale(g),
             FilterOp::HueRotate(h) => Filter::HueRotate(h),
             FilterOp::Invert(i) => Filter::Invert(i),
             FilterOp::Opacity(binding, opacity) => Filter::Opacity(binding, opacity),
             FilterOp::Saturate(s) => Filter::Saturate(s),
             FilterOp::Sepia(s) => Filter::Sepia(s),
--- a/gfx/wr/webrender/src/picture.rs
+++ b/gfx/wr/webrender/src/picture.rs
@@ -4133,19 +4133,20 @@ pub enum PictureCompositeMode {
     SvgFilter(Vec<FilterPrimitive>, Vec<SFilterData>),
 }
 
 impl PictureCompositeMode {
     pub fn inflate_picture_rect(&self, picture_rect: PictureRect, scale_factors: (f32, f32)) -> PictureRect {
         let mut result_rect = picture_rect;
         match self {
             PictureCompositeMode::Filter(filter) => match filter {
-                Filter::Blur(blur_radius) => {
-                    let inflation_factor = clamp_blur_radius(*blur_radius, scale_factors).ceil() * BLUR_SAMPLE_SCALE;
-                    result_rect = picture_rect.inflate(inflation_factor, inflation_factor);
+                Filter::Blur(width, height) => {
+                    let width_factor = clamp_blur_radius(*width, scale_factors).ceil() * BLUR_SAMPLE_SCALE;
+                    let height_factor = clamp_blur_radius(*height, scale_factors).ceil() * BLUR_SAMPLE_SCALE;
+                    result_rect = picture_rect.inflate(width_factor, height_factor);
                 },
                 Filter::DropShadows(shadows) => {
                     let mut max_inflation: f32 = 0.0;
                     for shadow in shadows {
                         max_inflation = max_inflation.max(shadow.blur_radius);
                     }
                     max_inflation = clamp_blur_radius(max_inflation, scale_factors).ceil() * BLUR_SAMPLE_SCALE;
                     result_rect = picture_rect.inflate(max_inflation, max_inflation);
@@ -4153,18 +4154,19 @@ impl PictureCompositeMode {
                 _ => {}
             }
             PictureCompositeMode::SvgFilter(primitives, _) => {
                 let mut output_rects = Vec::with_capacity(primitives.len());
                 for (cur_index, primitive) in primitives.iter().enumerate() {
                     let output_rect = match primitive.kind {
                         FilterPrimitiveKind::Blur(ref primitive) => {
                             let input = primitive.input.to_index(cur_index).map(|index| output_rects[index]).unwrap_or(picture_rect);
-                            let inflation_factor = primitive.radius.round() * BLUR_SAMPLE_SCALE;
-                            input.inflate(inflation_factor, inflation_factor)
+                            let width_factor = primitive.width.round() * BLUR_SAMPLE_SCALE;
+                            let height_factor = primitive.height.round() * BLUR_SAMPLE_SCALE;
+                            input.inflate(width_factor, height_factor)
                         }
                         FilterPrimitiveKind::DropShadow(ref primitive) => {
                             let inflation_factor = primitive.shadow.blur_radius.ceil() * BLUR_SAMPLE_SCALE;
                             let input = primitive.input.to_index(cur_index).map(|index| output_rects[index]).unwrap_or(picture_rect);
                             let shadow_rect = input.inflate(inflation_factor, inflation_factor);
                             input.union(&shadow_rect.translate(primitive.shadow.offset * Scale::new(1.0)))
                         }
                         FilterPrimitiveKind::Blend(ref primitive) => {
@@ -4881,21 +4883,22 @@ impl PicturePrimitive {
                     }
                     else
                     {
                         None
                     }
                 }
 
                 let dep_info = match raster_config.composite_mode {
-                    PictureCompositeMode::Filter(Filter::Blur(blur_radius)) => {
-                        let blur_std_deviation = clamp_blur_radius(blur_radius, scale_factors) * device_pixel_scale.0;
+                    PictureCompositeMode::Filter(Filter::Blur(width, height)) => {
+                        let width_std_deviation = clamp_blur_radius(width, scale_factors) * device_pixel_scale.0;
+                        let height_std_deviation = clamp_blur_radius(height, scale_factors) * device_pixel_scale.0;
                         let mut blur_std_deviation = DeviceSize::new(
-                            blur_std_deviation * scale_factors.0,
-                            blur_std_deviation * scale_factors.1
+                            width_std_deviation * scale_factors.0,
+                            height_std_deviation * scale_factors.1
                         );
                         let mut device_rect = if self.options.inflate_if_required {
                             let inflation_factor = frame_state.surfaces[raster_config.surface_index.0].inflation_factor;
                             let inflation_factor = inflation_factor * device_pixel_scale.0;
 
                             // The clipped field is the part of the picture that is visible
                             // on screen. The unclipped field is the screen-space rect of
                             // the complete picture, if no screen / clip-chain was applied
@@ -5929,27 +5932,28 @@ impl PicturePrimitive {
                     .get_relative_transform(surface_spatial_node_index, raster_spatial_node_index)
                     .scale_factors();
 
             // This inflation factor is to be applied to all primitives within the surface.
             // Only inflate if the caller hasn't already inflated the bounding rects for this filter.
             let mut inflation_factor = 0.0;
             if self.options.inflate_if_required {
                 match composite_mode {
-                    PictureCompositeMode::Filter(Filter::Blur(blur_radius)) => {
-                        let blur_radius = clamp_blur_radius(blur_radius, scale_factors);
+                    PictureCompositeMode::Filter(Filter::Blur(width, height)) => {
+                        let blur_radius = f32::max(clamp_blur_radius(width, scale_factors), clamp_blur_radius(height, scale_factors));
                         // The amount of extra space needed for primitives inside
                         // this picture to ensure the visibility check is correct.
                         inflation_factor = blur_radius * BLUR_SAMPLE_SCALE;
                     }
                     PictureCompositeMode::SvgFilter(ref primitives, _) => {
                         let mut max = 0.0;
                         for primitive in primitives {
                             if let FilterPrimitiveKind::Blur(ref blur) = primitive.kind {
-                                max = f32::max(max, blur.radius);
+                                max = f32::max(max, blur.width);
+                                max = f32::max(max, blur.height);
                             }
                         }
                         inflation_factor = clamp_blur_radius(max, scale_factors) * BLUR_SAMPLE_SCALE;
                     }
                     PictureCompositeMode::Filter(Filter::DropShadows(ref shadows)) => {
                         // TODO(gw): This is incorrect, since we don't consider the drop shadow
                         //           offset. However, fixing that is a larger task, so this is
                         //           an improvement on the current case (this at least works where
--- a/gfx/wr/webrender/src/prim_store/picture.rs
+++ b/gfx/wr/webrender/src/prim_store/picture.rs
@@ -55,17 +55,17 @@ impl From<CompositeOperator> for Composi
 
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 #[derive(Debug, Clone, MallocSizeOf, PartialEq, Hash, Eq)]
 pub enum FilterPrimitiveKey {
     Identity(ColorSpace, FilterPrimitiveInput),
     Flood(ColorSpace, ColorU),
     Blend(ColorSpace, MixBlendMode, FilterPrimitiveInput, FilterPrimitiveInput),
-    Blur(ColorSpace, Au, FilterPrimitiveInput),
+    Blur(ColorSpace, Au, Au, FilterPrimitiveInput),
     Opacity(ColorSpace, Au, FilterPrimitiveInput),
     ColorMatrix(ColorSpace, [Au; 20], FilterPrimitiveInput),
     DropShadow(ColorSpace, (VectorKey, Au, ColorU), FilterPrimitiveInput),
     ComponentTransfer(ColorSpace, FilterPrimitiveInput, Vec<SFilterData>),
     Offset(ColorSpace, FilterPrimitiveInput, VectorKey),
     Composite(ColorSpace, FilterPrimitiveInput, FilterPrimitiveInput, CompositeOperatorKey),
 }
 
@@ -74,17 +74,17 @@ pub enum FilterPrimitiveKey {
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 #[derive(Debug, Clone, MallocSizeOf, PartialEq, Hash, Eq)]
 pub enum PictureCompositeKey {
     // No visual compositing effect
     Identity,
 
     // FilterOp
-    Blur(Au),
+    Blur(Au, Au),
     Brightness(Au),
     Contrast(Au),
     Grayscale(Au),
     HueRotate(Au),
     Invert(Au),
     Opacity(Au),
     OpacityBinding(PropertyBindingId, Au),
     Saturate(Au),
@@ -135,17 +135,18 @@ impl From<Option<PictureCompositeMode>> 
                     MixBlendMode::Hue => PictureCompositeKey::Hue,
                     MixBlendMode::Saturation => PictureCompositeKey::Saturation,
                     MixBlendMode::Color => PictureCompositeKey::Color,
                     MixBlendMode::Luminosity => PictureCompositeKey::Luminosity,
                 }
             }
             Some(PictureCompositeMode::Filter(op)) => {
                 match op {
-                    Filter::Blur(value) => PictureCompositeKey::Blur(Au::from_f32_px(value)),
+                    Filter::Blur(width, height) =>
+                        PictureCompositeKey::Blur(Au::from_f32_px(width), Au::from_f32_px(height)),
                     Filter::Brightness(value) => PictureCompositeKey::Brightness(Au::from_f32_px(value)),
                     Filter::Contrast(value) => PictureCompositeKey::Contrast(Au::from_f32_px(value)),
                     Filter::Grayscale(value) => PictureCompositeKey::Grayscale(Au::from_f32_px(value)),
                     Filter::HueRotate(value) => PictureCompositeKey::HueRotate(Au::from_f32_px(value)),
                     Filter::Invert(value) => PictureCompositeKey::Invert(Au::from_f32_px(value)),
                     Filter::Saturate(value) => PictureCompositeKey::Saturate(Au::from_f32_px(value)),
                     Filter::Sepia(value) => PictureCompositeKey::Sepia(Au::from_f32_px(value)),
                     Filter::SrgbToLinear => PictureCompositeKey::SrgbToLinear,
@@ -183,17 +184,18 @@ impl From<Option<PictureCompositeMode>> 
                 PictureCompositeKey::ComponentTransfer(handle.uid())
             }
             Some(PictureCompositeMode::SvgFilter(filter_primitives, filter_data)) => {
                 PictureCompositeKey::SvgFilter(filter_primitives.into_iter().map(|primitive| {
                     match primitive.kind {
                         FilterPrimitiveKind::Identity(identity) => FilterPrimitiveKey::Identity(primitive.color_space, identity.input),
                         FilterPrimitiveKind::Blend(blend) => FilterPrimitiveKey::Blend(primitive.color_space, blend.mode, blend.input1, blend.input2),
                         FilterPrimitiveKind::Flood(flood) => FilterPrimitiveKey::Flood(primitive.color_space, flood.color.into()),
-                        FilterPrimitiveKind::Blur(blur) => FilterPrimitiveKey::Blur(primitive.color_space, Au::from_f32_px(blur.radius), blur.input),
+                        FilterPrimitiveKind::Blur(blur) =>
+                            FilterPrimitiveKey::Blur(primitive.color_space, Au::from_f32_px(blur.width), Au::from_f32_px(blur.height), blur.input),
                         FilterPrimitiveKind::Opacity(opacity) =>
                             FilterPrimitiveKey::Opacity(primitive.color_space, Au::from_f32_px(opacity.opacity), opacity.input),
                         FilterPrimitiveKind::ColorMatrix(color_matrix) => {
                             let mut quantized_values: [Au; 20] = [Au(0); 20];
                             for (value, result) in color_matrix.matrix.iter().zip(quantized_values.iter_mut()) {
                                 *result = Au::from_f32_px(*value);
                             }
                             FilterPrimitiveKey::ColorMatrix(primitive.color_space, quantized_values, color_matrix.input)
--- a/gfx/wr/webrender/src/render_task.rs
+++ b/gfx/wr/webrender/src/render_task.rs
@@ -941,29 +941,30 @@ impl RenderTask {
                     render_tasks.add().init(RenderTask::new_svg_filter_primitive(
                         smallvec![],
                         content_size,
                         uv_rect_kind,
                         SvgFilterInfo::Flood(flood.color),
                     ))
                 }
                 FilterPrimitiveKind::Blur(ref blur) => {
-                    let blur_std_deviation = blur.radius * device_pixel_scale.0;
+                    let width_std_deviation = blur.width * device_pixel_scale.0;
+                    let height_std_deviation = blur.height * device_pixel_scale.0;
                     let input_task_id = get_task_input(
                         &blur.input,
                         filter_primitives,
                         render_tasks,
                         cur_index,
                         &outputs,
                         original_task_id,
                         primitive.color_space
                     );
 
                     RenderTask::new_blur(
-                        DeviceSize::new(blur_std_deviation, blur_std_deviation),
+                        DeviceSize::new(width_std_deviation, height_std_deviation),
                         // TODO: This is a hack to ensure that a blur task's input is always
                         // in the blur's previous pass.
                         render_tasks.add().init(RenderTask::new_svg_filter_primitive(
                             smallvec![input_task_id],
                             content_size,
                             uv_rect_kind,
                             SvgFilterInfo::Identity,
                         )),
--- a/gfx/wr/webrender/src/scene_building.rs
+++ b/gfx/wr/webrender/src/scene_building.rs
@@ -2324,17 +2324,17 @@ impl<'a> SceneBuilder<'a> {
 
                     // No point in adding a shadow here if there were no primitives
                     // added to the shadow.
                     if !prim_list.is_empty() {
                         // Create a picture that the shadow primitives will be added to. If the
                         // blur radius is 0, the code in Picture::prepare_for_render will
                         // detect this and mark the picture to be drawn directly into the
                         // parent picture, which avoids an intermediate surface and blur.
-                        let blur_filter = Filter::Blur(std_deviation);
+                        let blur_filter = Filter::Blur(std_deviation, std_deviation);
                         let composite_mode = if blur_filter.is_noop() {
                             None
                         } else {
                             Some(PictureCompositeMode::Filter(blur_filter))
                         };
                         let composite_mode_key = composite_mode.clone().into();
 
                         // Pass through configuration information about whether WR should
--- a/gfx/wr/webrender_api/src/display_item.rs
+++ b/gfx/wr/webrender_api/src/display_item.rs
@@ -929,17 +929,18 @@ impl FloodPrimitive {
         self.color.a = self.color.a.min(1.0).max(0.0);
     }
 }
 
 #[repr(C)]
 #[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
 pub struct BlurPrimitive {
     pub input: FilterPrimitiveInput,
-    pub radius: f32,
+    pub width: f32,
+    pub height: f32,
 }
 
 #[repr(C)]
 #[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
 pub struct OpacityPrimitive {
     pub input: FilterPrimitiveInput,
     pub opacity: f32,
 }
@@ -1056,17 +1057,17 @@ impl FilterPrimitive {
 
 /// CSS filter.
 #[repr(C)]
 #[derive(Clone, Copy, Debug, PartialEq, Deserialize, Serialize, PeekPoke)]
 pub enum FilterOp {
     /// Filter that does no transformation of the colors, needed for
     /// debug purposes only.
     Identity,
-    Blur(f32),
+    Blur(f32, f32),
     Brightness(f32),
     Contrast(f32),
     Grayscale(f32),
     HueRotate(f32),
     Invert(f32),
     Opacity(PropertyBinding<f32>, f32),
     Saturate(f32),
     Sepia(f32),
--- a/gfx/wr/wrench/src/yaml_helper.rs
+++ b/gfx/wr/wrench/src/yaml_helper.rs
@@ -568,18 +568,18 @@ impl YamlHelper for Yaml {
         if let Some(s) = self.as_str() {
             match parse_function(s) {
                 ("identity", _, _) => {
                     Some(FilterOp::Identity)
                 }
                 ("component-transfer", _, _) => {
                     Some(FilterOp::ComponentTransfer)
                 }
-                ("blur", ref args, _) if args.len() == 1 => {
-                    Some(FilterOp::Blur(args[0].parse().unwrap()))
+                ("blur", ref args, _) if args.len() == 2 => {
+                    Some(FilterOp::Blur(args[0].parse().unwrap(), args[1].parse().unwrap()))
                 }
                 ("brightness", ref args, _) if args.len() == 1 => {
                     Some(FilterOp::Brightness(args[0].parse().unwrap()))
                 }
                 ("contrast", ref args, _) if args.len() == 1 => {
                     Some(FilterOp::Contrast(args[0].parse().unwrap()))
                 }
                 ("grayscale", ref args, _) if args.len() == 1 => {
@@ -724,17 +724,18 @@ impl YamlHelper for Yaml {
                 "flood" => {
                     FilterPrimitiveKind::Flood(FloodPrimitive {
                         color: self["color"].as_colorf().unwrap(),
                     })
                 }
                 "blur" => {
                     FilterPrimitiveKind::Blur(BlurPrimitive {
                         input: self["in"].as_filter_input().unwrap(),
-                        radius: self["radius"].as_f32().unwrap(),
+                        width: self["width"].as_f32().unwrap(),
+                        height: self["height"].as_f32().unwrap(),
                     })
                 }
                 "opacity" => {
                     FilterPrimitiveKind::Opacity(OpacityPrimitive {
                         input: self["in"].as_filter_input().unwrap(),
                         opacity: self["opacity"].as_f32().unwrap(),
                     })
                 }
--- a/layout/svg/FilterInstance.cpp
+++ b/layout/svg/FilterInstance.cpp
@@ -225,23 +225,19 @@ bool FilterInstance::BuildWebRenderFilte
         // chain. Clipping after a blur is not equivalent to clipping before
         // a blur, so bail out.
         return false;
       }
 
       const GaussianBlurAttributes& blur = attr.as<GaussianBlurAttributes>();
 
       const Size& stdDev = blur.mStdDeviation;
-      if (stdDev.width != stdDev.height) {
-        return false;
-      }
-
-      float radius = stdDev.width;
-      if (radius != 0.0) {
-        aWrFilters.filters.AppendElement(wr::FilterOp::Blur(radius));
+      if (stdDev.width != 0.0 || stdDev.height != 0.0) {
+        aWrFilters.filters.AppendElement(
+            wr::FilterOp::Blur(stdDev.width, stdDev.height));
       } else {
         filterIsNoop = true;
       }
     } else if (attr.is<DropShadowAttributes>()) {
       if (finalClip) {
         // We have to bail out for the same reason we would with a blur filter.
         return false;
       }
--- a/layout/svg/SVGIntegrationUtils.cpp
+++ b/layout/svg/SVGIntegrationUtils.cpp
@@ -1158,18 +1158,19 @@ bool SVGIntegrationUtils::CreateWebRende
         wrFilters.AppendElement(
             wr::FilterOp::HueRotate(filter.AsHueRotate().ToDegrees()));
         break;
       }
       case StyleFilter::Tag::Blur: {
         // TODO(emilio): we should go directly from css pixels -> device pixels.
         float appUnitsPerDevPixel =
             aFrame->PresContext()->AppUnitsPerDevPixel();
-        wrFilters.AppendElement(wr::FilterOp::Blur(NSAppUnitsToFloatPixels(
-            filter.AsBlur().ToAppUnits(), appUnitsPerDevPixel)));
+        float radius = NSAppUnitsToFloatPixels(filter.AsBlur().ToAppUnits(),
+                                               appUnitsPerDevPixel);
+        wrFilters.AppendElement(wr::FilterOp::Blur(radius, radius));
         break;
       }
       case StyleFilter::Tag::DropShadow: {
         float appUnitsPerDevPixel =
             aFrame->PresContext()->AppUnitsPerDevPixel();
         const StyleSimpleShadow& shadow = filter.AsDropShadow();
         nscolor color = shadow.color.CalcColor(aFrame);