From 7d937678f40b51f6a02d8ffa86ea690595a7f534 Mon Sep 17 00:00:00 2001 From: Martin Robinson Date: Tue, 6 Jun 2017 06:28:00 -0400 Subject: [PATCH] Bug 1365972 - Add initial WebRender support for nsDisplayFilter. r=rhunt, r=mattwoodrow --- gfx/layers/Layers.h | 7 +++ gfx/layers/LayersTypes.cpp | 38 ++++++++++++++++ gfx/layers/LayersTypes.h | 21 +++++++++ gfx/layers/moz.build | 1 + gfx/layers/wr/StackingContextHelper.cpp | 14 ++++-- gfx/layers/wr/StackingContextHelper.h | 6 ++- gfx/layers/wr/WebRenderCompositableHolder.cpp | 3 +- gfx/layers/wr/WebRenderContainerLayer.cpp | 7 ++- gfx/thebes/gfxPrefs.h | 1 + gfx/webrender_bindings/WebRenderAPI.cpp | 6 ++- gfx/webrender_bindings/WebRenderAPI.h | 3 +- gfx/webrender_bindings/WebRenderTypes.h | 33 ++++++++++++++ gfx/webrender_bindings/src/bindings.rs | 43 +++++++++++++++++-- .../webrender_ffi_generated.h | 28 +++++++++++- layout/painting/nsDisplayList.cpp | 31 ++++++++++++- modules/libpref/init/all.js | 1 + 16 files changed, 227 insertions(+), 16 deletions(-) create mode 100644 gfx/layers/LayersTypes.cpp diff --git a/gfx/layers/Layers.h b/gfx/layers/Layers.h index 392f436af8eb..db9d5cea726c 100644 --- a/gfx/layers/Layers.h +++ b/gfx/layers/Layers.h @@ -2250,6 +2250,12 @@ public: return mEventRegionsOverride; } + void SetFilterChain(nsTArray&& aFilterChain) { + mFilterChain = aFilterChain; + } + + nsTArray& GetFilterChain() { return mFilterChain; } + protected: friend class ReadbackProcessor; @@ -2336,6 +2342,7 @@ protected: // the intermediate surface. bool mChildrenChanged; EventRegionsOverride mEventRegionsOverride; + nsTArray mFilterChain; }; /** diff --git a/gfx/layers/LayersTypes.cpp b/gfx/layers/LayersTypes.cpp new file mode 100644 index 000000000000..76d8255a1dba --- /dev/null +++ b/gfx/layers/LayersTypes.cpp @@ -0,0 +1,38 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * 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/. */ + +#include "LayersTypes.h" + +#include "nsStyleStruct.h" // for nsStyleFilter + +namespace mozilla { +namespace layers { + +CSSFilter ToCSSFilter(const nsStyleFilter& filter) +{ + switch (filter.GetType()) { + case NS_STYLE_FILTER_BRIGHTNESS: { + return { + CSSFilterType::BRIGHTNESS, + filter.GetFilterParameter().GetFactorOrPercentValue(), + }; + } + case NS_STYLE_FILTER_CONTRAST: { + return { + CSSFilterType::CONTRAST, + filter.GetFilterParameter().GetFactorOrPercentValue(), + }; + } + // All other filter types should be prevented by the code which converts + // display items into layers. + default: + MOZ_ASSERT_UNREACHABLE("Tried to convert an unsupported filter"); + return { CSSFilterType::CONTRAST, 0 }; + } +} + +} // namespace layers +} // namespace mozilla + diff --git a/gfx/layers/LayersTypes.h b/gfx/layers/LayersTypes.h index b1b21eb6e6fb..9a0108d982ea 100644 --- a/gfx/layers/LayersTypes.h +++ b/gfx/layers/LayersTypes.h @@ -35,6 +35,8 @@ namespace android { class MOZ_EXPORT GraphicBuffer; } // namespace android +struct nsStyleFilter; + namespace mozilla { namespace layers { @@ -309,6 +311,25 @@ enum class ScrollDirection : uint32_t { SENTINEL /* for IPC serialization */ }; +enum class CSSFilterType : int8_t { + BLUR, + BRIGHTNESS, + CONTRAST, + GRAYSCALE, + HUE_ROTATE, + INVERT, + OPACITY, + SATURATE, + SEPIA, +}; + +struct CSSFilter { + CSSFilterType type; + float argument; +}; + +CSSFilter ToCSSFilter(const nsStyleFilter& filter); + } // namespace layers } // namespace mozilla diff --git a/gfx/layers/moz.build b/gfx/layers/moz.build index 8a575f4172ce..6b902006ea24 100644 --- a/gfx/layers/moz.build +++ b/gfx/layers/moz.build @@ -383,6 +383,7 @@ UNIFIED_SOURCES += [ 'LayerScope.cpp', 'LayersLogging.cpp', 'LayerSorter.cpp', + 'LayersTypes.cpp', 'opengl/CompositingRenderTargetOGL.cpp', 'opengl/CompositorOGL.cpp', 'opengl/GLBlitTextureImageHelper.cpp', diff --git a/gfx/layers/wr/StackingContextHelper.cpp b/gfx/layers/wr/StackingContextHelper.cpp index df3f60de7c14..235b9fdca6dc 100644 --- a/gfx/layers/wr/StackingContextHelper.cpp +++ b/gfx/layers/wr/StackingContextHelper.cpp @@ -20,16 +20,19 @@ StackingContextHelper::StackingContextHelper() StackingContextHelper::StackingContextHelper(const StackingContextHelper& aParentSC, wr::DisplayListBuilder& aBuilder, WebRenderLayer* aLayer, - const Maybe& aTransform) + const Maybe& aTransform, + const nsTArray& aFilters) : mBuilder(&aBuilder) { WrRect scBounds = aParentSC.ToRelativeWrRect(aLayer->BoundsForStackingContext()); Layer* layer = aLayer->GetLayer(); mTransform = aTransform.valueOr(layer->GetTransform()); + float opacity = 1.0f; mBuilder->PushStackingContext(scBounds, 0, &opacity, mTransform.IsIdentity() ? nullptr : &mTransform, - wr::ToWrMixBlendMode(layer->GetMixBlendMode())); + wr::ToWrMixBlendMode(layer->GetMixBlendMode()), + aFilters); mOrigin = aLayer->Bounds().TopLeft(); } @@ -38,18 +41,21 @@ StackingContextHelper::StackingContextHelper(const StackingContextHelper& aParen WebRenderLayer* aLayer, uint64_t aAnimationsId, float* aOpacityPtr, - gfx::Matrix4x4* aTransformPtr) + gfx::Matrix4x4* aTransformPtr, + const nsTArray& aFilters) : mBuilder(&aBuilder) { WrRect scBounds = aParentSC.ToRelativeWrRect(aLayer->BoundsForStackingContext()); if (aTransformPtr) { mTransform = *aTransformPtr; } + mBuilder->PushStackingContext(scBounds, aAnimationsId, aOpacityPtr, aTransformPtr, - wr::ToWrMixBlendMode(aLayer->GetLayer()->GetMixBlendMode())); + wr::ToWrMixBlendMode(aLayer->GetLayer()->GetMixBlendMode()), + aFilters); mOrigin = aLayer->Bounds().TopLeft(); } diff --git a/gfx/layers/wr/StackingContextHelper.h b/gfx/layers/wr/StackingContextHelper.h index 57efb1049d33..02bf9d95d559 100644 --- a/gfx/layers/wr/StackingContextHelper.h +++ b/gfx/layers/wr/StackingContextHelper.h @@ -31,7 +31,8 @@ public: StackingContextHelper(const StackingContextHelper& aParentSC, wr::DisplayListBuilder& aBuilder, WebRenderLayer* aLayer, - const Maybe& aTransform = Nothing()); + const Maybe& aTransform = Nothing(), + const nsTArray& aFilters = nsTArray()); // Alternate constructor which invokes the version of PushStackingContext // for animations. StackingContextHelper(const StackingContextHelper& aParentSC, @@ -39,7 +40,8 @@ public: WebRenderLayer* aLayer, uint64_t aAnimationsId, float* aOpacityPtr, - gfx::Matrix4x4* aTransformPtr); + gfx::Matrix4x4* aTransformPtr, + const nsTArray& aFilters = nsTArray()); // This version of the constructor should only be used at the root level // of the tree, so that we have a StackingContextHelper to pass down into // the RenderLayer traversal, but don't actually want it to push a stacking diff --git a/gfx/layers/wr/WebRenderCompositableHolder.cpp b/gfx/layers/wr/WebRenderCompositableHolder.cpp index 84df12666d8a..182b6171f6f5 100644 --- a/gfx/layers/wr/WebRenderCompositableHolder.cpp +++ b/gfx/layers/wr/WebRenderCompositableHolder.cpp @@ -275,7 +275,8 @@ WebRenderCompositableHolder::ApplyAsyncImages(wr::WebRenderAPI* aApi) 0, &opacity, holder->mScTransform.IsIdentity() ? nullptr : &holder->mScTransform, - holder->mMixBlendMode); + holder->mMixBlendMode, + nsTArray()); LayerRect rect(0, 0, holder->mCurrentTexture->GetSize().width, holder->mCurrentTexture->GetSize().height); if (holder->mScaleToSize.isSome()) { diff --git a/gfx/layers/wr/WebRenderContainerLayer.cpp b/gfx/layers/wr/WebRenderContainerLayer.cpp index 611b04f63d3a..209bf0708132 100644 --- a/gfx/layers/wr/WebRenderContainerLayer.cpp +++ b/gfx/layers/wr/WebRenderContainerLayer.cpp @@ -114,8 +114,13 @@ WebRenderContainerLayer::RenderLayer(wr::DisplayListBuilder& aBuilder, transformForSC = nullptr; } + nsTArray filters; + for (const CSSFilter& filter : this->GetFilterChain()) { + filters.AppendElement(wr::ToWrFilterOp(filter)); + } + ScrollingLayersHelper scroller(this, aBuilder, aSc); - StackingContextHelper sc(aSc, aBuilder, this, animationsId, opacityForSC, transformForSC); + StackingContextHelper sc(aSc, aBuilder, this, animationsId, opacityForSC, transformForSC, filters); LayerRect rect = Bounds(); DumpLayerInfo("ContainerLayer", rect); diff --git a/gfx/thebes/gfxPrefs.h b/gfx/thebes/gfxPrefs.h index 7a24aa2426f4..0c4e4be42cd5 100644 --- a/gfx/thebes/gfxPrefs.h +++ b/gfx/thebes/gfxPrefs.h @@ -504,6 +504,7 @@ private: DECL_OVERRIDE_PREF(Live, "layers.advanced.caret-layers", LayersAllowCaretLayers, gfxPrefs::OverrideBase_WebRender()); DECL_OVERRIDE_PREF(Live, "layers.advanced.columnRule-layers", LayersAllowColumnRuleLayers, gfxPrefs::OverrideBase_WebRender()); DECL_OVERRIDE_PREF(Live, "layers.advanced.displaybuttonborder-layers", LayersAllowDisplayButtonBorder, gfxPrefs::OverrideBase_WebRender()); + DECL_OVERRIDE_PREF(Live, "layers.advanced.filter-layers", LayersAllowFilterLayers, gfxPrefs::OverrideBase_WebRender()); DECL_OVERRIDE_PREF(Live, "layers.advanced.image-layers", LayersAllowImageLayers, gfxPrefs::OverrideBase_WebRendest()); DECL_OVERRIDE_PREF(Live, "layers.advanced.outline-layers", LayersAllowOutlineLayers, gfxPrefs::OverrideBase_WebRender()); DECL_OVERRIDE_PREF(Live, "layers.advanced.solid-color", LayersAllowSolidColorLayers, gfxPrefs::OverrideBase_WebRender()); diff --git a/gfx/webrender_bindings/WebRenderAPI.cpp b/gfx/webrender_bindings/WebRenderAPI.cpp index 3b03c0ec0186..ce2bbd789001 100644 --- a/gfx/webrender_bindings/WebRenderAPI.cpp +++ b/gfx/webrender_bindings/WebRenderAPI.cpp @@ -556,7 +556,8 @@ DisplayListBuilder::PushStackingContext(const WrRect& aBounds, const uint64_t& aAnimationId, const float* aOpacity, const gfx::Matrix4x4* aTransform, - const WrMixBlendMode& aMixBlendMode) + const WrMixBlendMode& aMixBlendMode, + const nsTArray& aFilters) { WrMatrix matrix; if (aTransform) { @@ -566,7 +567,8 @@ DisplayListBuilder::PushStackingContext(const WrRect& aBounds, WRDL_LOG("PushStackingContext b=%s t=%s\n", Stringify(aBounds).c_str(), aTransform ? Stringify(*aTransform).c_str() : "none"); wr_dp_push_stacking_context(mWrState, aBounds, aAnimationId, aOpacity, - maybeTransform, aMixBlendMode); + maybeTransform, aMixBlendMode, + aFilters.Elements(), aFilters.Length()); } void diff --git a/gfx/webrender_bindings/WebRenderAPI.h b/gfx/webrender_bindings/WebRenderAPI.h index ea72b897fa72..c4d41c209de0 100644 --- a/gfx/webrender_bindings/WebRenderAPI.h +++ b/gfx/webrender_bindings/WebRenderAPI.h @@ -150,7 +150,8 @@ public: const uint64_t& aAnimationId, const float* aOpacity, const gfx::Matrix4x4* aTransform, - const WrMixBlendMode& aMixBlendMode); + const WrMixBlendMode& aMixBlendMode, + const nsTArray& aFilters); void PopStackingContext(); void PushClip(const WrRect& aClipRect, diff --git a/gfx/webrender_bindings/WebRenderTypes.h b/gfx/webrender_bindings/WebRenderTypes.h index 2c182f962347..0b729bec6795 100644 --- a/gfx/webrender_bindings/WebRenderTypes.h +++ b/gfx/webrender_bindings/WebRenderTypes.h @@ -11,6 +11,7 @@ #include "mozilla/gfx/Matrix.h" #include "mozilla/gfx/Types.h" #include "mozilla/gfx/Tools.h" +#include "mozilla/layers/LayersTypes.h" #include "mozilla/Range.h" #include "Units.h" #include "nsStyleConsts.h" @@ -551,6 +552,38 @@ struct BuiltDisplayList { WrBuiltDisplayListDescriptor dl_desc; }; +static inline WrFilterOpType ToWrFilterOpType(const layers::CSSFilterType type) { + switch (type) { + case layers::CSSFilterType::BLUR: + return WrFilterOpType::Blur; + case layers::CSSFilterType::BRIGHTNESS: + return WrFilterOpType::Brightness; + case layers::CSSFilterType::CONTRAST: + return WrFilterOpType::Contrast; + case layers::CSSFilterType::GRAYSCALE: + return WrFilterOpType::Grayscale; + case layers::CSSFilterType::HUE_ROTATE: + return WrFilterOpType::HueRotate; + case layers::CSSFilterType::INVERT: + return WrFilterOpType::Invert; + case layers::CSSFilterType::OPACITY: + return WrFilterOpType::Opacity; + case layers::CSSFilterType::SATURATE: + return WrFilterOpType::Saturate; + case layers::CSSFilterType::SEPIA: + return WrFilterOpType::Sepia; + } + MOZ_ASSERT_UNREACHABLE("Tried to convert unknown filter type."); + return WrFilterOpType::Grayscale; +} + +static inline WrFilterOp ToWrFilterOp(const layers::CSSFilter& filter) { + return { + ToWrFilterOpType(filter.type), + filter.argument, + }; +} + } // namespace wr } // namespace mozilla diff --git a/gfx/webrender_bindings/src/bindings.rs b/gfx/webrender_bindings/src/bindings.rs index acb270c8dcd7..a39681de7a7b 100644 --- a/gfx/webrender_bindings/src/bindings.rs +++ b/gfx/webrender_bindings/src/bindings.rs @@ -2,7 +2,7 @@ use std::collections::HashSet; use std::ffi::CString; use std::{mem, slice}; use std::path::PathBuf; -use std::os::raw::{c_void, c_char}; +use std::os::raw::{c_void, c_char, c_float}; use std::collections::HashMap; use gleam::gl; @@ -59,6 +59,27 @@ type WrYuvColorSpace = YuvColorSpace; #[derive(Copy, Clone)] pub struct WrExternalImageId(pub u64); +#[repr(u32)] +#[derive(Copy, Clone)] +pub enum WrFilterOpType { + Blur = 0, + Brightness = 1, + Contrast = 2, + Grayscale = 3, + HueRotate = 4, + Invert = 5, + Opacity = 6, + Saturate = 7, + Sepia = 8, +} + +#[repr(C)] +#[derive(Copy, Clone)] +pub struct WrFilterOp { + filter_type: WrFilterOpType, + argument: c_float, +} + impl Into for WrExternalImageId { fn into(self) -> ExternalImageId { ExternalImageId(self.0) @@ -1245,12 +1266,28 @@ pub extern "C" fn wr_dp_push_stacking_context(state: &mut WrState, animation_id: u64, opacity: *const f32, transform: *const WrMatrix, - mix_blend_mode: WrMixBlendMode) { + mix_blend_mode: WrMixBlendMode, + filters: *const WrFilterOp, + filter_count: usize) { assert!(unsafe { !is_in_render_thread() }); let bounds = bounds.into(); - let mut filters: Vec = Vec::new(); + let c_filters = make_slice(filters, filter_count); + let mut filters : Vec = c_filters.iter().map(|c_filter| { + match c_filter.filter_type { + WrFilterOpType::Blur => FilterOp::Blur(Au::from_f32_px(c_filter.argument)), + WrFilterOpType::Brightness => FilterOp::Brightness(c_filter.argument), + WrFilterOpType::Contrast => FilterOp::Contrast(c_filter.argument), + WrFilterOpType::Grayscale => FilterOp::Grayscale(c_filter.argument), + WrFilterOpType::HueRotate => FilterOp::HueRotate(c_filter.argument), + WrFilterOpType::Invert => FilterOp::Invert(c_filter.argument), + WrFilterOpType::Opacity => FilterOp::Opacity(PropertyBinding::Value(c_filter.argument)), + WrFilterOpType::Saturate => FilterOp::Saturate(c_filter.argument), + WrFilterOpType::Sepia => FilterOp::Sepia(c_filter.argument), + } + }).collect(); + let opacity = unsafe { opacity.as_ref() }; if let Some(opacity) = opacity { if *opacity < 1.0 { diff --git a/gfx/webrender_bindings/webrender_ffi_generated.h b/gfx/webrender_bindings/webrender_ffi_generated.h index 08506b069a52..47ca1cafd058 100644 --- a/gfx/webrender_bindings/webrender_ffi_generated.h +++ b/gfx/webrender_bindings/webrender_ffi_generated.h @@ -51,6 +51,20 @@ enum class WrExternalImageType : uint32_t { Sentinel /* this must be last for serialization purposes. */ }; +enum class WrFilterOpType : uint32_t { + Blur = 0, + Brightness = 1, + Contrast = 2, + Grayscale = 3, + HueRotate = 4, + Invert = 5, + Opacity = 6, + Saturate = 7, + Sepia = 8, + + Sentinel /* this must be last for serialization purposes. */ +}; + enum class WrGradientExtendMode : uint32_t { Clamp = 0, Repeat = 1, @@ -423,6 +437,16 @@ struct WrComplexClipRegion { } }; +struct WrFilterOp { + WrFilterOpType filter_type; + float argument; + + bool operator==(const WrFilterOp& aOther) const { + return filter_type == aOther.filter_type && + argument == aOther.argument; + } +}; + struct WrGlyphInstance { uint32_t index; WrPoint point; @@ -784,7 +808,9 @@ void wr_dp_push_stacking_context(WrState *aState, uint64_t aAnimationId, const float *aOpacity, const WrMatrix *aTransform, - WrMixBlendMode aMixBlendMode) + WrMixBlendMode aMixBlendMode, + const WrFilterOp *aFilters, + size_t aFilterCount) WR_FUNC; WR_INLINE diff --git a/layout/painting/nsDisplayList.cpp b/layout/painting/nsDisplayList.cpp index dd748e36079e..0832341829c2 100644 --- a/layout/painting/nsDisplayList.cpp +++ b/layout/painting/nsDisplayList.cpp @@ -8635,6 +8635,17 @@ nsDisplayFilter::BuildLayer(nsDisplayListBuilder* aBuilder, BuildContainerLayerFor(aBuilder, aManager, mFrame, this, &mList, newContainerParameters, nullptr); + LayerState state = this->GetLayerState(aBuilder, aManager, newContainerParameters); + if (container && state != LAYER_SVG_EFFECTS) { + const nsTArray& filters = mFrame->StyleEffects()->mFilters; + nsTArray cssFilters = nsTArray(filters.Length()); + for (const nsStyleFilter& filter : filters) { + cssFilters.AppendElement(ToCSSFilter(filter)); + } + + container->SetFilterChain(Move(cssFilters)); + } + return container.forget(); } @@ -8667,7 +8678,25 @@ nsDisplayFilter::GetLayerState(nsDisplayListBuilder* aBuilder, LayerManager* aManager, const ContainerLayerParameters& aParameters) { - return LAYER_SVG_EFFECTS; + if (mFrame->IsFrameOfType(nsIFrame::eSVG)) { + return LAYER_SVG_EFFECTS; + } + + if (!ShouldUseAdvancedLayer(aManager, gfxPrefs::LayersAllowFilterLayers)) { + return LAYER_SVG_EFFECTS; + } + + // Due to differences in the way that WebRender filters operate + // only the brightness and contrast filters use that path. We + // can gradually enable more filters as WebRender bugs are fixed. + for (const nsStyleFilter& filter : mFrame->StyleEffects()->mFilters) { + if (filter.GetType() != NS_STYLE_FILTER_BRIGHTNESS && + filter.GetType() != NS_STYLE_FILTER_CONTRAST) { + return LAYER_SVG_EFFECTS; + } + } + + return LAYER_ACTIVE; } bool nsDisplayFilter::ComputeVisibility(nsDisplayListBuilder* aBuilder, diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index 2db1be804dc6..1f4355db1f10 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -5740,6 +5740,7 @@ pref("layers.advanced.outline-layers", 2); pref("layers.advanced.solid-color", 2); pref("layers.advanced.table", 2); pref("layers.advanced.text-layers", 2); +pref("layers.advanced.filter-layers", 2); // Whether webrender should be used as much as possible. pref("gfx.webrendest.enabled", false);