Bug 1505871. C++ code to get component transfer filter data into webrender. r=jrmuizel
☠☠ backed out by f38de0791ab5 ☠ ☠
authorTimothy Nikkel <tnikkel@gmail.com>
Mon, 25 Feb 2019 22:45:15 -0600
changeset 461114 2bf33f573505edb8ca5f5048dc054f472bd7436e
parent 461113 c4ce50209f1966dfd7b10c6060b5a625395e0dca
child 461115 90660632d6410ebb7e3530c048f04137d36b6587
push id35618
push usershindli@mozilla.com
push dateTue, 26 Feb 2019 16:54:44 +0000
treeherdermozilla-central@d326a9d5f77b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjrmuizel
bugs1505871
milestone67.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 1505871. C++ code to get component transfer filter data into webrender. r=jrmuizel Have to use a pointer/size pair to transfer the value list to rust. We use a "filter holder" that contains an nsTArray that owns the values.
gfx/layers/wr/StackingContextHelper.h
gfx/layers/wr/WebRenderCommandBuilder.cpp
gfx/layers/wr/WebRenderCommandBuilder.h
gfx/layers/wr/WebRenderLayerManager.cpp
gfx/layers/wr/WebRenderLayerManager.h
gfx/webrender_bindings/WebRenderAPI.cpp
gfx/webrender_bindings/WebRenderAPI.h
gfx/webrender_bindings/src/bindings.rs
layout/base/PresShell.cpp
layout/painting/nsDisplayList.cpp
layout/painting/nsDisplayList.h
layout/svg/nsFilterInstance.cpp
layout/svg/nsFilterInstance.h
layout/svg/nsSVGIntegrationUtils.cpp
layout/svg/nsSVGIntegrationUtils.h
--- a/gfx/layers/wr/StackingContextHelper.h
+++ b/gfx/layers/wr/StackingContextHelper.h
@@ -7,16 +7,17 @@
 #ifndef GFX_STACKINGCONTEXTHELPER_H
 #define GFX_STACKINGCONTEXTHELPER_H
 
 #include "mozilla/Attributes.h"
 #include "mozilla/gfx/MatrixFwd.h"
 #include "mozilla/webrender/WebRenderAPI.h"
 #include "mozilla/webrender/WebRenderTypes.h"
 #include "Units.h"
+#include "nsSVGIntegrationUtils.h" // for WrFiltersHolder
 
 class nsDisplayTransform;
 
 namespace mozilla {
 
 struct ActiveScrolledRoot;
 
 namespace layers {
--- a/gfx/layers/wr/WebRenderCommandBuilder.cpp
+++ b/gfx/layers/wr/WebRenderCommandBuilder.cpp
@@ -1420,17 +1420,17 @@ void WebRenderCommandBuilder::EmptyTrans
 bool WebRenderCommandBuilder::NeedsEmptyTransaction() {
   return !mLastCanvasDatas.IsEmpty();
 }
 
 void WebRenderCommandBuilder::BuildWebRenderCommands(
     wr::DisplayListBuilder& aBuilder,
     wr::IpcResourceUpdateQueue& aResourceUpdates, nsDisplayList* aDisplayList,
     nsDisplayListBuilder* aDisplayListBuilder, WebRenderScrollData& aScrollData,
-    wr::LayoutSize& aContentSize, nsTArray<wr::FilterOp>&& aFilters) {
+    wr::LayoutSize& aContentSize, WrFiltersHolder&& aFilters) {
   AUTO_PROFILER_LABEL_CATEGORY_PAIR(GRAPHICS_WRDisplayList);
 
   StackingContextHelper sc;
   aScrollData = WebRenderScrollData(mManager);
   MOZ_ASSERT(mLayerScrollData.empty());
   mLastCanvasDatas.Clear();
   mLastAsr = nullptr;
   mBuilderDumpIndex = 0;
@@ -1446,17 +1446,18 @@ void WebRenderCommandBuilder::BuildWebRe
     }
 
     nsPresContext* presContext =
         aDisplayListBuilder->RootReferenceFrame()->PresContext();
     bool isTopLevelContent =
         presContext->Document()->IsTopLevelContentDocument();
 
     wr::StackingContextParams params;
-    params.mFilters = std::move(aFilters);
+    params.mFilters = std::move(aFilters.filters);
+    params.mFilterDatas = std::move(aFilters.filter_datas);
     params.animation = mZoomProp.ptrOr(nullptr);
     params.cache_tiles = isTopLevelContent;
     params.clip =
         wr::WrStackingContextClip::ClipChain(aBuilder.CurrentClipChainId());
 
     StackingContextHelper pageRootSc(sc, nullptr, nullptr, nullptr, aBuilder,
                                      params);
     if (ShouldDumpDisplayList(aDisplayListBuilder)) {
--- a/gfx/layers/wr/WebRenderCommandBuilder.h
+++ b/gfx/layers/wr/WebRenderCommandBuilder.h
@@ -52,17 +52,17 @@ class WebRenderCommandBuilder {
   bool NeedsEmptyTransaction();
 
   void BuildWebRenderCommands(wr::DisplayListBuilder& aBuilder,
                               wr::IpcResourceUpdateQueue& aResourceUpdates,
                               nsDisplayList* aDisplayList,
                               nsDisplayListBuilder* aDisplayListBuilder,
                               WebRenderScrollData& aScrollData,
                               wr::LayoutSize& aContentSize,
-                              nsTArray<wr::FilterOp>&& aFilters);
+                              WrFiltersHolder&& aFilters);
 
   void PushOverrideForASR(const ActiveScrolledRoot* aASR,
                           const wr::WrSpatialId& aSpatialId);
   void PopOverrideForASR(const ActiveScrolledRoot* aASR);
 
   Maybe<wr::ImageKey> CreateImageKey(
       nsDisplayItem* aItem, ImageContainer* aContainer,
       mozilla::wr::DisplayListBuilder& aBuilder,
--- a/gfx/layers/wr/WebRenderLayerManager.cpp
+++ b/gfx/layers/wr/WebRenderLayerManager.cpp
@@ -241,17 +241,17 @@ void WebRenderLayerManager::EndTransacti
                                            EndTransactionFlags aFlags) {
   // This should never get called, all callers should use
   // EndTransactionWithoutLayer instead.
   MOZ_ASSERT(false);
 }
 
 void WebRenderLayerManager::EndTransactionWithoutLayer(
     nsDisplayList* aDisplayList, nsDisplayListBuilder* aDisplayListBuilder,
-    nsTArray<wr::FilterOp>&& aFilters, WebRenderBackgroundData* aBackground) {
+    WrFiltersHolder&& aFilters, WebRenderBackgroundData* aBackground) {
   AUTO_PROFILER_TRACING("Paint", "RenderLayers", GRAPHICS);
 
   // Since we don't do repeat transactions right now, just set the time
   mAnimationReadyTime = TimeStamp::Now();
 
   WrBridge()->BeginTransaction();
 
   LayoutDeviceIntSize size = mWidget->GetClientSize();
--- a/gfx/layers/wr/WebRenderLayerManager.h
+++ b/gfx/layers/wr/WebRenderLayerManager.h
@@ -74,17 +74,17 @@ class WebRenderLayerManager final : publ
 
   virtual bool BeginTransactionWithTarget(gfxContext* aTarget,
                                           const nsCString& aURL) override;
   virtual bool BeginTransaction(const nsCString& aURL) override;
   virtual bool EndEmptyTransaction(
       EndTransactionFlags aFlags = END_DEFAULT) override;
   void EndTransactionWithoutLayer(
       nsDisplayList* aDisplayList, nsDisplayListBuilder* aDisplayListBuilder,
-      nsTArray<wr::FilterOp>&& aFilters = nsTArray<wr::FilterOp>(),
+      WrFiltersHolder&& aFilters = WrFiltersHolder(),
       WebRenderBackgroundData* aBackground = nullptr);
   virtual void EndTransaction(
       DrawPaintedLayerCallback aCallback, void* aCallbackData,
       EndTransactionFlags aFlags = END_DEFAULT) override;
 
   virtual LayersBackend GetBackendType() override {
     return LayersBackend::LAYERS_WR;
   }
--- a/gfx/webrender_bindings/WebRenderAPI.cpp
+++ b/gfx/webrender_bindings/WebRenderAPI.cpp
@@ -685,17 +685,17 @@ Maybe<wr::WrSpatialId> DisplayListBuilde
   const wr::LayoutTransform* maybeTransform = transform ? &matrix : nullptr;
   WRDL_LOG("PushStackingContext b=%s t=%s\n", mWrState,
            Stringify(aBounds).c_str(),
            transform ? Stringify(*transform).c_str() : "none");
 
   auto spatialId = wr_dp_push_stacking_context(
       mWrState, aBounds, mCurrentSpaceAndClipChain.space, &aParams,
       maybeTransform, aParams.mFilters.Elements(), aParams.mFilters.Length(),
-      aRasterSpace);
+      aParams.mFilterDatas.Elements(), aParams.mFilterDatas.Length(), aRasterSpace);
 
   return spatialId.id != 0 ? Some(spatialId) : Nothing();
 }
 
 void DisplayListBuilder::PopStackingContext(bool aIsReferenceFrame) {
   WRDL_LOG("PopStackingContext\n", mWrState);
   wr_dp_pop_stacking_context(mWrState, aIsReferenceFrame);
 }
--- a/gfx/webrender_bindings/WebRenderAPI.h
+++ b/gfx/webrender_bindings/WebRenderAPI.h
@@ -324,16 +324,17 @@ struct MOZ_STACK_CLASS StackingContextPa
                                 wr::MixBlendMode::Normal} {}
 
   void SetPreserve3D(bool aPreserve) {
     transform_style =
         aPreserve ? wr::TransformStyle::Preserve3D : wr::TransformStyle::Flat;
   }
 
   nsTArray<wr::FilterOp> mFilters;
+  nsTArray<wr::WrFilterData> mFilterDatas;
   wr::LayoutRect mBounds = wr::ToLayoutRect(LayoutDeviceRect());
   const gfx::Matrix4x4* mBoundTransform = nullptr;
   const gfx::Matrix4x4* mTransformPtr = nullptr;
   Maybe<nsDisplayTransform*> mDeferredTransformItem;
   // Whether the stacking context is possibly animated. This alters how
   // coordinates are transformed/snapped to invalidate less when transforms
   // change frequently.
   bool mAnimated = false;
--- a/gfx/webrender_bindings/src/bindings.rs
+++ b/gfx/webrender_bindings/src/bindings.rs
@@ -8,17 +8,17 @@ use std::os::unix::ffi::OsStringExt;
 use std::io::Cursor;
 use std::{mem, slice, ptr, env};
 use std::path::PathBuf;
 use std::rc::Rc;
 use std::cell::RefCell;
 use std::sync::Arc;
 use std::sync::atomic::{AtomicUsize, Ordering};
 use std::ops::Range;
-use std::os::raw::{c_void, c_char};
+use std::os::raw::{c_void, c_char, c_float};
 #[cfg(target_os = "android")]
 use std::os::raw::{c_int};
 use gleam::gl;
 
 use webrender::api::*;
 use webrender::{ReadPixelsFormat, Renderer, RendererOptions, RendererStats, ThreadListener};
 use webrender::{ExternalImage, ExternalImageHandler, ExternalImageSource};
 use webrender::DebugFlags;
@@ -484,16 +484,34 @@ impl ExternalImageHandler for WrExternal
               id: ExternalImageId,
               channel_index: u8) {
         unsafe {
             (self.unlock_func)(self.external_image_obj, id.into(), channel_index);
         }
     }
 }
 
+#[repr(C)]
+#[derive(Clone, Copy)]
+// Used for ComponentTransfer only
+pub struct WrFilterData {
+    funcR_type: ComponentTransferFuncType,
+    R_values: *mut c_float,
+    R_values_count: usize,
+    funcG_type: ComponentTransferFuncType,
+    G_values: *mut c_float,
+    G_values_count: usize,
+    funcB_type: ComponentTransferFuncType,
+    B_values: *mut c_float,
+    B_values_count: usize,
+    funcA_type: ComponentTransferFuncType,
+    A_values: *mut c_float,
+    A_values_count: usize,
+}
+
 #[repr(u32)]
 pub enum WrAnimationType {
     Transform = 0,
     Opacity = 1,
 }
 
 #[repr(C)]
 pub struct WrAnimationProperty {
@@ -1947,16 +1965,18 @@ pub struct WrStackingContextParams {
 pub extern "C" fn wr_dp_push_stacking_context(
     state: &mut WrState,
     mut bounds: LayoutRect,
     spatial_id: WrSpatialId,
     params: &WrStackingContextParams,
     transform: *const LayoutTransform,
     filters: *const FilterOp,
     filter_count: usize,
+    filter_datas: *const WrFilterData,
+    filter_datas_count: usize,
     glyph_raster_space: RasterSpace,
 ) -> WrSpatialId {
     debug_assert!(unsafe { !is_in_render_thread() });
 
     let c_filters = make_slice(filters, filter_count);
     let mut filters : Vec<FilterOp> = c_filters.iter().map(|c_filter| {
                                                            *c_filter
     }).collect();
--- a/layout/base/PresShell.cpp
+++ b/layout/base/PresShell.cpp
@@ -6069,17 +6069,17 @@ void PresShell::Paint(nsView* aViewToPai
 
   if (layerManager->GetBackendType() == layers::LayersBackend::LAYERS_WR) {
     nsPresContext* pc = GetPresContext();
     LayoutDeviceRect bounds = LayoutDeviceRect::FromAppUnits(
         pc->GetVisibleArea(), pc->AppUnitsPerDevPixel());
     bgcolor = NS_ComposeColors(bgcolor, mCanvasBackgroundColor);
     WebRenderBackgroundData data(wr::ToLayoutRect(bounds),
                                  wr::ToColorF(ToDeviceColor(bgcolor)));
-    nsTArray<wr::FilterOp> wrFilters;
+    WrFiltersHolder wrFilters;
 
     MaybeSetupTransactionIdAllocator(layerManager, presContext);
     layerManager->AsWebRenderLayerManager()->EndTransactionWithoutLayer(
         nullptr, nullptr, std::move(wrFilters), &data);
     return;
   }
 
   RefPtr<ColorLayer> root = layerManager->CreateColorLayer();
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -2599,21 +2599,21 @@ already_AddRefed<LayerManager> nsDisplay
         }
         // This must be called even if PluginGeometryUpdates were not computed.
         rootPresContext->CollectPluginGeometryUpdates(layerManager);
       }
 
       auto* wrManager = static_cast<WebRenderLayerManager*>(layerManager.get());
 
       nsIDocShell* docShell = presContext->GetDocShell();
-      nsTArray<wr::FilterOp> wrFilters;
+      WrFiltersHolder wrFilters;
       gfx::Matrix5x4* colorMatrix =
           nsDocShell::Cast(docShell)->GetColorMatrix();
       if (colorMatrix) {
-        wrFilters.AppendElement(
+        wrFilters.filters.AppendElement(
             wr::FilterOp::ColorMatrix(colorMatrix->components));
       }
 
       wrManager->EndTransactionWithoutLayer(this, aBuilder,
                                             std::move(wrFilters));
     }
 
     // For layers-free mode, we check the invalidation state bits in the
@@ -9316,70 +9316,70 @@ void nsDisplayFilters::PaintAsLayer(nsDi
 }
 
 static float ClampStdDeviation(float aStdDeviation) {
   // Cap software blur radius for performance reasons.
   return std::min(std::max(0.0f, aStdDeviation), 100.0f);
 }
 
 bool nsDisplayFilters::CreateWebRenderCSSFilters(
-    nsTArray<mozilla::wr::FilterOp>& wrFilters) {
+    WrFiltersHolder& wrFilters) {
   // All CSS filters are supported by WebRender. SVG filters are not fully
   // supported, those use NS_STYLE_FILTER_URL and are handled separately.
   const nsTArray<nsStyleFilter>& filters = mFrame->StyleEffects()->mFilters;
 
   // If there are too many filters to render, then just pretend that we
   // succeeded, and don't render any of them.
   if (filters.Length() > gfxPrefs::WebRenderMaxFilterOpsPerChain()) {
     return true;
   }
-  wrFilters.SetCapacity(filters.Length());
+  wrFilters.filters.SetCapacity(filters.Length());
 
   for (const nsStyleFilter& filter : filters) {
     switch (filter.GetType()) {
       case NS_STYLE_FILTER_BRIGHTNESS:
-        wrFilters.AppendElement(wr::FilterOp::Brightness(
+        wrFilters.filters.AppendElement(wr::FilterOp::Brightness(
             filter.GetFilterParameter().GetFactorOrPercentValue()));
         break;
       case NS_STYLE_FILTER_CONTRAST:
-        wrFilters.AppendElement(wr::FilterOp::Contrast(
+        wrFilters.filters.AppendElement(wr::FilterOp::Contrast(
             filter.GetFilterParameter().GetFactorOrPercentValue()));
         break;
       case NS_STYLE_FILTER_GRAYSCALE:
-        wrFilters.AppendElement(wr::FilterOp::Grayscale(
+        wrFilters.filters.AppendElement(wr::FilterOp::Grayscale(
             filter.GetFilterParameter().GetFactorOrPercentValue()));
         break;
       case NS_STYLE_FILTER_INVERT:
-        wrFilters.AppendElement(wr::FilterOp::Invert(
+        wrFilters.filters.AppendElement(wr::FilterOp::Invert(
             filter.GetFilterParameter().GetFactorOrPercentValue()));
         break;
       case NS_STYLE_FILTER_OPACITY: {
         float opacity = filter.GetFilterParameter().GetFactorOrPercentValue();
-        wrFilters.AppendElement(wr::FilterOp::Opacity(
+        wrFilters.filters.AppendElement(wr::FilterOp::Opacity(
             wr::PropertyBinding<float>::Value(opacity), opacity));
         break;
       }
       case NS_STYLE_FILTER_SATURATE:
-        wrFilters.AppendElement(wr::FilterOp::Saturate(
+        wrFilters.filters.AppendElement(wr::FilterOp::Saturate(
             filter.GetFilterParameter().GetFactorOrPercentValue()));
         break;
       case NS_STYLE_FILTER_SEPIA: {
-        wrFilters.AppendElement(wr::FilterOp::Sepia(
+        wrFilters.filters.AppendElement(wr::FilterOp::Sepia(
             filter.GetFilterParameter().GetFactorOrPercentValue()));
         break;
       }
       case NS_STYLE_FILTER_HUE_ROTATE: {
-        wrFilters.AppendElement(wr::FilterOp::HueRotate(
+        wrFilters.filters.AppendElement(wr::FilterOp::HueRotate(
             (float)filter.GetFilterParameter().GetAngleValueInDegrees()));
         break;
       }
       case NS_STYLE_FILTER_BLUR: {
         float appUnitsPerDevPixel =
             mFrame->PresContext()->AppUnitsPerDevPixel();
-        wrFilters.AppendElement(mozilla::wr::FilterOp::Blur(ClampStdDeviation(
+        wrFilters.filters.AppendElement(mozilla::wr::FilterOp::Blur(ClampStdDeviation(
             NSAppUnitsToFloatPixels(filter.GetFilterParameter().GetCoordValue(),
                                     appUnitsPerDevPixel))));
         break;
       }
       case NS_STYLE_FILTER_DROP_SHADOW: {
         float appUnitsPerDevPixel =
             mFrame->PresContext()->AppUnitsPerDevPixel();
         nsCSSShadowArray* shadows = filter.GetDropShadow();
@@ -9401,30 +9401,30 @@ bool nsDisplayFilters::CreateWebRenderCS
             NSAppUnitsToFloatPixels(shadow->mRadius, appUnitsPerDevPixel),
             {
                 NS_GET_R(color) / 255.0f,
                 NS_GET_G(color) / 255.0f,
                 NS_GET_B(color) / 255.0f,
                 NS_GET_A(color) / 255.0f,
             });
 
-        wrFilters.AppendElement(filterOp);
+        wrFilters.filters.AppendElement(filterOp);
         break;
       }
       default:
         return false;
     }
   }
 
   return true;
 }
 
 bool nsDisplayFilters::CanCreateWebRenderCommands(
     nsDisplayListBuilder* aBuilder) {
-  nsTArray<mozilla::wr::FilterOp> wrFilters;
+  WrFiltersHolder wrFilters;
   Maybe<nsRect> filterClip;
   if (!CreateWebRenderCSSFilters(wrFilters) &&
       !nsSVGIntegrationUtils::BuildWebRenderFilters(mFrame, wrFilters,
                                                     filterClip)) {
     return false;
   }
   return true;
 }
@@ -9432,17 +9432,17 @@ bool nsDisplayFilters::CanCreateWebRende
 bool nsDisplayFilters::CreateWebRenderCommands(
     mozilla::wr::DisplayListBuilder& aBuilder,
     mozilla::wr::IpcResourceUpdateQueue& aResources,
     const StackingContextHelper& aSc,
     mozilla::layers::RenderRootStateManager* aManager,
     nsDisplayListBuilder* aDisplayListBuilder) {
   float auPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
 
-  nsTArray<mozilla::wr::FilterOp> wrFilters;
+  WrFiltersHolder wrFilters;
   Maybe<nsRect> filterClip;
   if (!CreateWebRenderCSSFilters(wrFilters) &&
       !nsSVGIntegrationUtils::BuildWebRenderFilters(mFrame, wrFilters,
                                                     filterClip)) {
     return false;
   }
 
   wr::WrStackingContextClip clip;
@@ -9453,17 +9453,18 @@ bool nsDisplayFilters::CreateWebRenderCo
         aBuilder.DefineClip(Nothing(), wr::ToRoundedLayoutRect(devPxRect));
     clip = wr::WrStackingContextClip::ClipId(clipId);
   } else {
     clip = wr::WrStackingContextClip::ClipChain(aBuilder.CurrentClipChainId());
   }
 
   float opacity = mFrame->StyleEffects()->mOpacity;
   wr::StackingContextParams params;
-  params.mFilters = std::move(wrFilters);
+  params.mFilters = std::move(wrFilters.filters);
+  params.mFilterDatas = std::move(wrFilters.filter_datas);
   params.opacity = opacity != 1.0f && mHandleOpacity ? &opacity : nullptr;
   params.clip = clip;
   StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder,
                            params);
 
   nsDisplayEffectsBase::CreateWebRenderCommands(aBuilder, aResources, sc,
                                                 aManager, aDisplayListBuilder);
 
--- a/layout/painting/nsDisplayList.h
+++ b/layout/painting/nsDisplayList.h
@@ -59,16 +59,17 @@ class nsIContent;
 class nsDisplayList;
 class nsDisplayTableItem;
 class nsIScrollableFrame;
 class nsSubDocumentFrame;
 class nsDisplayCompositorHitTestInfo;
 class nsDisplayScrollInfoLayer;
 class nsCaret;
 enum class nsDisplayOwnLayerFlags;
+struct WrFiltersHolder;
 
 namespace mozilla {
 class FrameLayerBuilder;
 struct MotionPathData;
 namespace layers {
 struct FrameMetrics;
 class RenderRootStateManager;
 class Layer;
@@ -6384,17 +6385,17 @@ class nsDisplayFilters : public nsDispla
   bool CreateWebRenderCommands(
       mozilla::wr::DisplayListBuilder& aBuilder,
       mozilla::wr::IpcResourceUpdateQueue& aResources,
       const StackingContextHelper& aSc,
       mozilla::layers::RenderRootStateManager* aManager,
       nsDisplayListBuilder* aDisplayListBuilder) override;
   bool CanCreateWebRenderCommands(nsDisplayListBuilder* aBuilder);
 
-  bool CreateWebRenderCSSFilters(nsTArray<mozilla::wr::FilterOp>& wrFilters);
+  bool CreateWebRenderCSSFilters(WrFiltersHolder& wrFilters);
 
  private:
   // relative to mFrame
   nsRect mEffectsBounds;
 };
 
 /* A display item that applies a transformation to all of its descendant
  * elements.  This wrapper should only be used if there is a transform applied
--- a/layout/svg/nsFilterInstance.cpp
+++ b/layout/svg/nsFilterInstance.cpp
@@ -90,20 +90,42 @@ void nsFilterInstance::PaintFilteredFram
                             *metrics, filterChain, /* InputIsTainted */ true,
                             aPaintCallback, scaleMatrixInDevUnits, aDirtyArea,
                             nullptr, nullptr, nullptr);
   if (instance.IsInitialized()) {
     instance.Render(aCtx, aImgParams, aOpacity);
   }
 }
 
+static mozilla::wr::ComponentTransferFuncType
+FuncTypeToWr(uint8_t aFuncType) {
+  switch (aFuncType) {
+    case SVG_FECOMPONENTTRANSFER_TYPE_IDENTITY:
+      return mozilla::wr::ComponentTransferFuncType::Identity;
+    case SVG_FECOMPONENTTRANSFER_TYPE_TABLE:
+      return mozilla::wr::ComponentTransferFuncType::Table;
+    case SVG_FECOMPONENTTRANSFER_TYPE_DISCRETE:
+      return mozilla::wr::ComponentTransferFuncType::Discrete;
+    case SVG_FECOMPONENTTRANSFER_TYPE_LINEAR:
+      return mozilla::wr::ComponentTransferFuncType::Linear;
+    case SVG_FECOMPONENTTRANSFER_TYPE_GAMMA:
+      return mozilla::wr::ComponentTransferFuncType::Gamma;
+    default:
+      MOZ_ASSERT(false, "unknown func type?");
+  }
+  MOZ_ASSERT(false, "unknown func type?");
+  return mozilla::wr::ComponentTransferFuncType::Identity;
+}
+
 bool nsFilterInstance::BuildWebRenderFilters(nsIFrame* aFilteredFrame,
-                                             nsTArray<wr::FilterOp>& aWrFilters,
+                                             WrFiltersHolder& aWrFilters,
                                              Maybe<nsRect>& aPostFilterClip) {
-  aWrFilters.Clear();
+  aWrFilters.filters.Clear();
+  aWrFilters.filter_datas.Clear();
+  aWrFilters.values.Clear();
 
   auto& filterChain = aFilteredFrame->StyleEffects()->mFilters;
   UniquePtr<UserSpaceMetrics> metrics =
       UserSpaceMetricsForFrame(aFilteredFrame);
 
   // TODO: simply using an identity matrix here, was pulling the scale from a
   // gfx context for the non-wr path.
   gfxMatrix scaleMatrix;
@@ -157,29 +179,29 @@ bool nsFilterInstance::BuildWebRenderFil
       }
     } else if (primitive.InputPrimitiveIndex(0) != int32_t(i - 1)) {
       return false;
     }
 
     bool previousSrgb = srgb;
     bool primNeedsSrgb = primitive.InputColorSpace(0) == gfx::ColorSpace::SRGB;
     if (srgb && !primNeedsSrgb) {
-      aWrFilters.AppendElement(wr::FilterOp::SrgbToLinear());
+      aWrFilters.filters.AppendElement(wr::FilterOp::SrgbToLinear());
     } else if (!srgb && primNeedsSrgb) {
-      aWrFilters.AppendElement(wr::FilterOp::LinearToSrgb());
+      aWrFilters.filters.AppendElement(wr::FilterOp::LinearToSrgb());
     }
     srgb = primitive.OutputColorSpace() == gfx::ColorSpace::SRGB;
 
     const PrimitiveAttributes& attr = primitive.Attributes();
 
     bool filterIsNoop = false;
 
     if (attr.is<OpacityAttributes>()) {
       float opacity = attr.as<OpacityAttributes>().mOpacity;
-      aWrFilters.AppendElement(wr::FilterOp::Opacity(
+      aWrFilters.filters.AppendElement(wr::FilterOp::Opacity(
           wr::PropertyBinding<float>::Value(opacity), opacity));
     } else if (attr.is<ColorMatrixAttributes>()) {
       const ColorMatrixAttributes& attributes =
           attr.as<ColorMatrixAttributes>();
 
       float transposed[20];
       if (!gfx::ComputeColorMatrix(attributes, transposed)) {
         filterIsNoop = true;
@@ -202,17 +224,17 @@ bool nsFilterInstance::BuildWebRenderFil
 
       float matrix[20] = {
           transposed[0], transposed[5], transposed[10], transposed[15],
           transposed[1], transposed[6], transposed[11], transposed[16],
           transposed[2], transposed[7], transposed[12], transposed[17],
           transposed[3], transposed[8], transposed[13], transposed[18],
           transposed[4], transposed[9], transposed[14], transposed[19]};
 
-      aWrFilters.AppendElement(wr::FilterOp::ColorMatrix(matrix));
+      aWrFilters.filters.AppendElement(wr::FilterOp::ColorMatrix(matrix));
     } else if (attr.is<GaussianBlurAttributes>()) {
       if (finalClip) {
         // There's a clip that needs to apply before the blur filter, but
         // WebRender only lets us apply the clip at the end of the filter
         // chain. Clipping after a blur is not equivalent to clipping before
         // a blur, so bail out.
         return false;
       }
@@ -221,17 +243,17 @@ bool nsFilterInstance::BuildWebRenderFil
 
       const Size& stdDev = blur.mStdDeviation;
       if (stdDev.width != stdDev.height) {
         return false;
       }
 
       float radius = stdDev.width;
       if (radius != 0.0) {
-        aWrFilters.AppendElement(wr::FilterOp::Blur(radius));
+        aWrFilters.filters.AppendElement(wr::FilterOp::Blur(radius));
       } 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;
       }
@@ -250,47 +272,93 @@ bool nsFilterInstance::BuildWebRenderFil
       if (!primNeedsSrgb) {
         color = Color(gsRGBToLinearRGBMap[uint8_t(color.r * 255)],
                       gsRGBToLinearRGBMap[uint8_t(color.g * 255)],
                       gsRGBToLinearRGBMap[uint8_t(color.b * 255)], color.a);
       }
       wr::FilterOp filterOp = wr::FilterOp::DropShadow(
           offset, radius, wr::ToColorF(ToDeviceColor(color)));
 
-      aWrFilters.AppendElement(filterOp);
+      aWrFilters.filters.AppendElement(filterOp);
+    } else if (attr.is<ComponentTransferAttributes>()) {
+      const ComponentTransferAttributes& attributes =
+          attr.as<ComponentTransferAttributes>();
+
+      size_t numValues = attributes.mValues[0].Length() +
+          attributes.mValues[1].Length() + attributes.mValues[2].Length() +
+          attributes.mValues[3].Length();
+      if (numValues > 1024) {
+        // Depending on how the wr shaders are implemented we may need to
+        // limit the total number of values.
+        return false;
+      }
+
+      wr::FilterOp filterOp = {wr::FilterOp::Tag::ComponentTransfer};
+      wr::WrFilterData filterData;
+      aWrFilters.values.AppendElement(nsTArray<float>());
+      nsTArray<float>* values = &aWrFilters.values[aWrFilters.values.Length()-1];
+      values->SetCapacity(numValues);
+
+      filterData.funcR_type = FuncTypeToWr(attributes.mTypes[0]);
+      size_t R_startindex = values->Length();
+      values->AppendElements(attributes.mValues[0]);
+      filterData.R_values_count = attributes.mValues[0].Length();
+
+      filterData.funcG_type = FuncTypeToWr(attributes.mTypes[1]);
+      size_t G_startindex = values->Length();
+      values->AppendElements(attributes.mValues[1]);
+      filterData.G_values_count = attributes.mValues[1].Length();
+
+      filterData.funcB_type = FuncTypeToWr(attributes.mTypes[2]);
+      size_t B_startindex = values->Length();
+      values->AppendElements(attributes.mValues[2]);
+      filterData.B_values_count = attributes.mValues[2].Length();
+
+      filterData.funcA_type = FuncTypeToWr(attributes.mTypes[3]);
+      size_t A_startindex = values->Length();
+      values->AppendElements(attributes.mValues[3]);
+      filterData.A_values_count = attributes.mValues[3].Length();
+
+      filterData.R_values = filterData.R_values_count > 0 ? &((*values)[R_startindex]) : nullptr;
+      filterData.G_values = filterData.G_values_count > 0 ? &((*values)[G_startindex]) : nullptr;
+      filterData.B_values = filterData.B_values_count > 0 ? &((*values)[B_startindex]) : nullptr;
+      filterData.A_values = filterData.A_values_count > 0 ? &((*values)[A_startindex]) : nullptr;
+
+      aWrFilters.filters.AppendElement(filterOp);
+      aWrFilters.filter_datas.AppendElement(filterData);
     } else {
       return false;
     }
 
-    if (filterIsNoop && aWrFilters.Length() > 0 &&
-        (aWrFilters.LastElement().tag == wr::FilterOp::Tag::SrgbToLinear ||
-         aWrFilters.LastElement().tag == wr::FilterOp::Tag::LinearToSrgb)) {
+    if (filterIsNoop && aWrFilters.filters.Length() > 0 &&
+        (aWrFilters.filters.LastElement().tag == wr::FilterOp::Tag::SrgbToLinear ||
+         aWrFilters.filters.LastElement().tag == wr::FilterOp::Tag::LinearToSrgb)) {
       // We pushed a color space conversion filter in prevision of applying
       // another filter which turned out to be a no-op, so the conversion is
       // unnecessary. Remove it from the filter list.
       // This is both an optimization and a way to pass the wptest
       // css/filter-effects/filter-scale-001.html for which the needless
       // sRGB->linear->no-op->sRGB roundtrip introduces a slight error and we
       // cannot add fuzziness to the test.
-      Unused << aWrFilters.PopLastElement();
+      Unused << aWrFilters.filters.PopLastElement();
       srgb = previousSrgb;
     }
 
     if (!filterIsNoop) {
       if (finalClip.isNothing()) {
         finalClip = Some(primitive.PrimitiveSubregion());
       } else {
         finalClip =
             Some(primitive.PrimitiveSubregion().Intersect(finalClip.value()));
       }
     }
   }
 
   if (!srgb) {
-    aWrFilters.AppendElement(wr::FilterOp::LinearToSrgb());
+    aWrFilters.filters.AppendElement(wr::FilterOp::LinearToSrgb());
   }
 
   if (finalClip) {
     aPostFilterClip = Some(instance.FilterSpaceToFrameSpace(finalClip.value()));
   }
   return true;
 }
 
--- a/layout/svg/nsFilterInstance.h
+++ b/layout/svg/nsFilterInstance.h
@@ -118,17 +118,17 @@ class nsFilterInstance {
                                     const gfxRect* aOverrideBBox = nullptr,
                                     const nsRect* aPreFilterBounds = nullptr);
 
   /**
    * Try to build WebRender filters for a frame if the filters applied to it are
    * supported.
    */
   static bool BuildWebRenderFilters(nsIFrame* aFilteredFrame,
-                                    nsTArray<mozilla::wr::FilterOp>& aWrFilters,
+                                    WrFiltersHolder& aWrFilters,
                                     mozilla::Maybe<nsRect>& aPostFilterClip);
 
  private:
   /**
    * @param aTargetFrame The frame of the filtered element under consideration,
    *   may be null.
    * @param aTargetContent The filtered element itself.
    * @param aMetrics The metrics to resolve SVG lengths against.
--- a/layout/svg/nsSVGIntegrationUtils.cpp
+++ b/layout/svg/nsSVGIntegrationUtils.cpp
@@ -1081,17 +1081,17 @@ void nsSVGIntegrationUtils::PaintFilter(
                                      offsets.offsetToUserSpaceInDevPx);
   nsRegion dirtyRegion = aParams.dirtyRect - offsets.offsetToBoundingBox;
 
   nsFilterInstance::PaintFilteredFrame(frame, &context, &callback, &dirtyRegion,
                                        aParams.imgParams, opacity);
 }
 
 bool nsSVGIntegrationUtils::BuildWebRenderFilters(
-    nsIFrame* aFilteredFrame, nsTArray<mozilla::wr::FilterOp>& aWrFilters,
+    nsIFrame* aFilteredFrame, WrFiltersHolder& aWrFilters,
     Maybe<nsRect>& aPostFilterClip) {
   return nsFilterInstance::BuildWebRenderFilters(aFilteredFrame, aWrFilters,
                                                  aPostFilterClip);
 }
 
 class PaintFrameCallback : public gfxDrawingCallback {
  public:
   PaintFrameCallback(nsIFrame* aFrame, const nsSize aPaintServerSize,
--- a/layout/svg/nsSVGIntegrationUtils.h
+++ b/layout/svg/nsSVGIntegrationUtils.h
@@ -29,16 +29,23 @@ class DrawTarget;
 namespace layers {
 class LayerManager;
 }  // namespace layers
 }  // namespace mozilla
 
 struct nsPoint;
 struct nsSize;
 
+struct WrFiltersHolder {
+  nsTArray<mozilla::wr::FilterOp> filters;
+  nsTArray<mozilla::wr::WrFilterData> filter_datas;
+  // This exists just to own the values long enough for them to be copied into rust.
+  nsTArray<nsTArray<float>> values;
+};
+
 /**
  * Integration of SVG effects (clipPath clipping, masking and filters) into
  * regular display list based painting and hit-testing.
  */
 class nsSVGIntegrationUtils final {
   typedef mozilla::gfx::DrawTarget DrawTarget;
   typedef mozilla::gfx::IntRect IntRect;
   typedef mozilla::image::imgDrawingParams imgDrawingParams;
@@ -191,17 +198,17 @@ class nsSVGIntegrationUtils final {
    */
   static void PaintFilter(const PaintFramesParams& aParams);
 
   /**
    * Try to build WebRender filters for a frame if the filters applied to it are
    * supported.
    */
   static bool BuildWebRenderFilters(nsIFrame* aFilteredFrame,
-                                    nsTArray<mozilla::wr::FilterOp>& aWrFilters,
+                                    WrFiltersHolder& aWrFilters,
                                     mozilla::Maybe<nsRect>& aPostFilterClip);
 
   /**
    * @param aRenderingContext the target rendering context in which the paint
    * server will be rendered
    * @param aTarget the target frame onto which the paint server will be
    * rendered
    * @param aPaintServer a first-continuation frame to use as the source