Bug 1016539. Add support for accelerated a11y filters. r=mwoodrow
authorJeff Muizelaar <jmuizelaar@mozilla.com>
Thu, 07 Aug 2014 17:44:08 -0400
changeset 199906 9dad28d06cb5326729ded0b8c9ecf1f42d7cf5b3
parent 199905 dd013a36e5d936d00d4ce25a725728653336588b
child 199907 fd08e61066de9722826b84e2dd3dded073aa95d3
push id9784
push userryanvm@gmail.com
push dateSat, 16 Aug 2014 21:45:40 +0000
treeherderb2g-inbound@94ba78a42305 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmwoodrow
bugs1016539
milestone34.0a1
Bug 1016539. Add support for accelerated a11y filters. r=mwoodrow This adds the ability to render to a temporary surface and use that to apply a filter effect to all of the content.
gfx/layers/CompositorTypes.h
gfx/layers/Effects.cpp
gfx/layers/Effects.h
gfx/layers/LayersLogging.cpp
gfx/layers/LayersLogging.h
gfx/layers/composite/LayerManagerComposite.cpp
gfx/layers/composite/LayerManagerComposite.h
gfx/layers/opengl/CompositingRenderTargetOGL.h
gfx/layers/opengl/CompositorOGL.cpp
gfx/layers/opengl/CompositorOGL.h
gfx/layers/opengl/OGLShaderProgram.cpp
gfx/layers/opengl/OGLShaderProgram.h
gfx/thebes/gfxPrefs.h
--- a/gfx/layers/CompositorTypes.h
+++ b/gfx/layers/CompositorTypes.h
@@ -138,16 +138,17 @@ MOZ_END_ENUM_CLASS(DiagnosticFlags)
 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(DiagnosticFlags)
 
 /**
  * See gfx/layers/Effects.h
  */
 MOZ_BEGIN_ENUM_CLASS(EffectTypes, uint8_t)
   MASK,
   BLEND_MODE,
+  COLOR_MATRIX,
   MAX_SECONDARY, // sentinel for the count of secondary effect types
   RGB,
   YCBCR,
   COMPONENT_ALPHA,
   SOLID_COLOR,
   RENDER_TARGET,
   MAX  //sentinel for the count of all effect types
 MOZ_END_ENUM_CLASS(EffectTypes)
--- a/gfx/layers/Effects.cpp
+++ b/gfx/layers/Effects.cpp
@@ -56,8 +56,16 @@ EffectSolidColor::PrintInfo(std::strings
 
 void
 EffectBlendMode::PrintInfo(std::stringstream& aStream, const char* aPrefix)
 {
   aStream << aPrefix;
   aStream << nsPrintfCString("EffectBlendMode (0x%p) [blendmode=%i]", this, (int)mBlendMode).get();
 }
 
+void
+EffectColorMatrix::PrintInfo(std::stringstream& aStream, const char* aPrefix)
+{
+  aStream << aPrefix;
+  aStream << nsPrintfCString("EffectColorMatrix (0x%p)", this).get();
+  AppendToString(aStream, mColorMatrix, " [matrix=", "]");
+}
+
--- a/gfx/layers/Effects.h
+++ b/gfx/layers/Effects.h
@@ -116,18 +116,39 @@ struct EffectRenderTarget : public Textu
     : TexturedEffect(EffectTypes::RENDER_TARGET, aRenderTarget, true, gfx::Filter::LINEAR)
     , mRenderTarget(aRenderTarget)
   {}
 
   virtual const char* Name() { return "EffectRenderTarget"; }
   virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix);
 
   RefPtr<CompositingRenderTarget> mRenderTarget;
+
+protected:
+  EffectRenderTarget(EffectTypes aType, CompositingRenderTarget *aRenderTarget)
+    : TexturedEffect(aType, aRenderTarget, true, gfx::Filter::LINEAR)
+    , mRenderTarget(aRenderTarget)
+  {}
+
 };
 
+// Render to a render target rather than the screen.
+struct EffectColorMatrix : public Effect
+{
+  EffectColorMatrix(gfx::Matrix5x4 aMatrix)
+    : Effect(EffectTypes::COLOR_MATRIX)
+    , mColorMatrix(aMatrix)
+  {}
+
+  virtual const char* Name() { return "EffectColorMatrix"; }
+  virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix);
+  const gfx::Matrix5x4 mColorMatrix;
+};
+
+
 struct EffectRGB : public TexturedEffect
 {
   EffectRGB(TextureSource *aTexture,
             bool aPremultiplied,
             gfx::Filter aFilter,
             bool aFlipped = false)
     : TexturedEffect(EffectTypes::RGB, aTexture, aPremultiplied, aFilter)
   {}
--- a/gfx/layers/LayersLogging.cpp
+++ b/gfx/layers/LayersLogging.cpp
@@ -165,16 +165,32 @@ AppendToString(std::stringstream& aStrea
       m._21, m._22, m._23, m._24,
       m._31, m._32, m._33, m._34,
       m._41, m._42, m._43, m._44).get();
   }
   aStream << sfx;
 }
 
 void
+AppendToString(std::stringstream& aStream, const Matrix5x4& m,
+               const char* pfx, const char* sfx)
+{
+  aStream << pfx;
+  aStream << nsPrintfCString(
+    "[ %g %g %g %g; %g %g %g %g; %g %g %g %g; %g %g %g %g; %g %g %g %g]",
+    m._11, m._12, m._13, m._14,
+    m._21, m._22, m._23, m._24,
+    m._31, m._32, m._33, m._34,
+    m._41, m._42, m._43, m._44,
+    m._51, m._52, m._53, m._54).get();
+  aStream << sfx;
+}
+
+
+void
 AppendToString(std::stringstream& aStream, const Filter filter,
                const char* pfx, const char* sfx)
 {
   aStream << pfx;
 
   switch (filter) {
     case Filter::GOOD: aStream << "Filter::GOOD"; break;
     case Filter::LINEAR: aStream << "Filter::LINEAR"; break;
--- a/gfx/layers/LayersLogging.h
+++ b/gfx/layers/LayersLogging.h
@@ -135,16 +135,20 @@ AppendToString(std::stringstream& aStrea
   aStream << sfx;
 }
 
 void
 AppendToString(std::stringstream& aStream, const mozilla::gfx::Matrix4x4& m,
                const char* pfx="", const char* sfx="");
 
 void
+AppendToString(std::stringstream& aStream, const mozilla::gfx::Matrix5x4& m,
+               const char* pfx="", const char* sfx="");
+
+void
 AppendToString(std::stringstream& aStream, const mozilla::gfx::Filter filter,
                const char* pfx="", const char* sfx="");
 
 void
 AppendToString(std::stringstream& aStream, mozilla::layers::TextureFlags flags,
                const char* pfx="", const char* sfx="");
 
 void
--- a/gfx/layers/composite/LayerManagerComposite.cpp
+++ b/gfx/layers/composite/LayerManagerComposite.cpp
@@ -405,16 +405,68 @@ LayerManagerComposite::RenderDebugOverla
 #endif
 
   if (drawFrameColorBars || drawFrameCounter) {
     // We intentionally overflow at 2^16.
     sFrameCount++;
   }
 }
 
+RefPtr<CompositingRenderTarget>
+LayerManagerComposite::PushGroup()
+{
+  RefPtr<CompositingRenderTarget> previousTarget = mCompositor->GetCurrentRenderTarget();
+  // make our render target the same size as the destination target
+  // so that we don't have to change size if the drawing area changes.
+  IntRect rect(previousTarget->GetOrigin(), previousTarget->GetSize());
+  // XXX: I'm not sure if this is true or not...
+  MOZ_ASSERT(rect.x == 0 && rect.y == 0);
+  if (!mTwoPassTmpTarget ||
+      mTwoPassTmpTarget->GetSize() != previousTarget->GetSize() ||
+      mTwoPassTmpTarget->GetOrigin() != previousTarget->GetOrigin()) {
+    mTwoPassTmpTarget = mCompositor->CreateRenderTarget(rect, INIT_MODE_NONE);
+  }
+  mCompositor->SetRenderTarget(mTwoPassTmpTarget);
+  return previousTarget;
+}
+void LayerManagerComposite::PopGroup(RefPtr<CompositingRenderTarget> aPreviousTarget, nsIntRect aClipRect)
+{
+  mCompositor->SetRenderTarget(aPreviousTarget);
+
+  EffectChain effectChain(RootLayer());
+  Matrix5x4 matrix;
+  if (gfxPrefs::Grayscale()) {
+    matrix._11 = matrix._12 = matrix._13 = 0.2126f;
+    matrix._21 = matrix._22 = matrix._23 = 0.7152f;
+    matrix._31 = matrix._32 = matrix._33 = 0.0722f;
+  }
+
+  if (gfxPrefs::Invert()) {
+    matrix._11 = -matrix._11;
+    matrix._12 = -matrix._12;
+    matrix._13 = -matrix._13;
+    matrix._21 = -matrix._21;
+    matrix._22 = -matrix._22;
+    matrix._23 = -matrix._23;
+    matrix._31 = -matrix._31;
+    matrix._32 = -matrix._32;
+    matrix._33 = -matrix._33;
+    matrix._51 = 1;
+    matrix._52 = 1;
+    matrix._53 = 1;
+  }
+
+  effectChain.mPrimaryEffect = new EffectRenderTarget(mTwoPassTmpTarget);
+  effectChain.mSecondaryEffects[EffectTypes::COLOR_MATRIX] = new EffectColorMatrix(matrix);
+
+  gfx::Rect clipRectF(aClipRect.x, aClipRect.y, aClipRect.width, aClipRect.height);
+  mCompositor->DrawQuad(Rect(Point(0, 0), Size(mTwoPassTmpTarget->GetSize())), clipRectF, effectChain, 1.,
+                        Matrix4x4());
+}
+
 void
 LayerManagerComposite::Render()
 {
   PROFILER_LABEL("LayerManagerComposite", "Render",
     js::ProfileEntry::Category::GRAPHICS);
 
   if (mDestroyed) {
     NS_WARNING("Call on destroyed layer manager");
@@ -437,16 +489,20 @@ LayerManagerComposite::Render()
     // Create a LayersPacket, dump Layers into it and transfer the
     // packet('s ownership) to LayerScope.
     auto packet = MakeUnique<layerscope::Packet>();
     layerscope::LayersPacket* layersPacket = packet->mutable_layers();
     this->Dump(layersPacket);
     LayerScope::SendLayerDump(Move(packet));
   }
 
+  if (gfxPrefs::Invert() || gfxPrefs::Grayscale()) {
+    composer2D = nullptr;
+  }
+
   if (!mTarget && composer2D && composer2D->TryRender(mRoot, mWorldMatrix, mGeometryChanged)) {
     if (mFPS) {
       double fps = mFPS->mCompositionFps.AddFrameAndGetFps(TimeStamp::Now());
       if (gfxPrefs::LayersDrawFPS()) {
         printf_stderr("HWComposer: FPS is %g\n", fps);
       }
     }
     mCompositor->EndFrameForExternalComposition(mWorldMatrix);
@@ -496,28 +552,39 @@ LayerManagerComposite::Render()
   }
 
   // Allow widget to render a custom background.
   mCompositor->GetWidget()->DrawWindowUnderlay(this, nsIntRect(actualBounds.x,
                                                                actualBounds.y,
                                                                actualBounds.width,
                                                                actualBounds.height));
 
+  RefPtr<CompositingRenderTarget> previousTarget;
+  if (gfxPrefs::Invert() || gfxPrefs::Grayscale()) {
+    previousTarget = PushGroup();
+  } else {
+    mTwoPassTmpTarget = nullptr;
+  }
+
   // Render our layers.
   RootLayer()->Prepare(clipRect);
   RootLayer()->RenderLayer(clipRect);
 
   if (!mRegionToClear.IsEmpty()) {
     nsIntRegionRectIterator iter(mRegionToClear);
     const nsIntRect *r;
     while ((r = iter.Next())) {
       mCompositor->ClearRect(Rect(r->x, r->y, r->width, r->height));
     }
   }
 
+  if (mTwoPassTmpTarget) {
+    PopGroup(previousTarget, clipRect);
+  }
+
   // Allow widget to render a custom foreground.
   mCompositor->GetWidget()->DrawWindowOverlay(this, nsIntRect(actualBounds.x,
                                                               actualBounds.y,
                                                               actualBounds.width,
                                                               actualBounds.height));
 
   // Debugging
   RenderDebugOverlay(actualBounds);
--- a/gfx/layers/composite/LayerManagerComposite.h
+++ b/gfx/layers/composite/LayerManagerComposite.h
@@ -58,16 +58,17 @@ struct EffectChain;
 class ImageLayer;
 class ImageLayerComposite;
 class LayerComposite;
 class RefLayerComposite;
 class SurfaceDescriptor;
 class ThebesLayerComposite;
 class TiledLayerComposer;
 class TextRenderer;
+class CompositingRenderTarget;
 struct FPSState;
 
 class LayerManagerComposite : public LayerManager
 {
   typedef mozilla::gfx::DrawTarget DrawTarget;
   typedef mozilla::gfx::IntSize IntSize;
   typedef mozilla::gfx::SurfaceFormat SurfaceFormat;
 
@@ -263,16 +264,19 @@ private:
 
   /**
    * Render debug overlays such as the FPS/FrameCounter above the frame.
    */
   void RenderDebugOverlay(const gfx::Rect& aBounds);
 
   void WorldTransformRect(nsIntRect& aRect);
 
+  RefPtr<CompositingRenderTarget> PushGroup();
+  void PopGroup(RefPtr<CompositingRenderTarget> aPreviousTarget, nsIntRect aClipRect);
+
   RefPtr<Compositor> mCompositor;
   nsAutoPtr<LayerProperties> mClonedLayerTreeProperties;
 
   /**
    * Context target, nullptr when drawing directly to our swap chain.
    */
   RefPtr<gfx::DrawTarget> mTarget;
   nsIntRect mTargetBounds;
@@ -280,16 +284,17 @@ private:
   gfx::Matrix mWorldMatrix;
   nsIntRegion mInvalidRegion;
   nsAutoPtr<FPSState> mFPS;
 
   bool mInTransaction;
   bool mIsCompositorReady;
   bool mDebugOverlayWantsNextFrame;
 
+  RefPtr<CompositingRenderTarget> mTwoPassTmpTarget;
   RefPtr<TextRenderer> mTextRenderer;
   bool mGeometryChanged;
 };
 
 /**
  * Composite layers are for use with OMTC on the compositor thread only. There
  * must be corresponding Basic layers on the content thread. For composite
  * layers, the layer manager only maintains the layer tree, all rendering is
--- a/gfx/layers/opengl/CompositingRenderTargetOGL.h
+++ b/gfx/layers/opengl/CompositingRenderTargetOGL.h
@@ -132,19 +132,17 @@ public:
   TextureSourceOGL* AsSourceOGL() MOZ_OVERRIDE
   {
     // XXX - Bug 900770
     MOZ_ASSERT(false, "CompositingRenderTargetOGL should not be used as a TextureSource");
     return nullptr;
   }
   gfx::IntSize GetSize() const MOZ_OVERRIDE
   {
-    // XXX - Bug 900770
-    MOZ_ASSERT(false, "CompositingRenderTargetOGL should not be used as a TextureSource");
-    return gfx::IntSize(0, 0);
+    return mInitParams.mSize;
   }
 
   gfx::SurfaceFormat GetFormat() const MOZ_OVERRIDE
   {
     // XXX - Should it be implemented ? is the above assert true ?
     MOZ_ASSERT(false, "Not implemented");
     return gfx::SurfaceFormat::UNKNOWN;
   }
--- a/gfx/layers/opengl/CompositorOGL.cpp
+++ b/gfx/layers/opengl/CompositorOGL.cpp
@@ -892,17 +892,18 @@ CompositorOGL::CreateFBOWithTexture(cons
 
   *aFBO = fbo;
   *aTexture = tex;
 }
 
 ShaderConfigOGL
 CompositorOGL::GetShaderConfigFor(Effect *aEffect,
                                   MaskType aMask,
-                                  gfx::CompositionOp aOp) const
+                                  gfx::CompositionOp aOp,
+                                  bool aColorMatrix) const
 {
   ShaderConfigOGL config;
 
   switch(aEffect->mType) {
   case EffectTypes::SOLID_COLOR:
     config.SetRenderColor(true);
     break;
   case EffectTypes::YCBCR:
@@ -939,16 +940,17 @@ CompositorOGL::GetShaderConfigFor(Effect
         !texturedEffect->mPremultiplied) {
       // We can do these blend modes just using glBlendFunc but we need the data
       // to be premultiplied first.
       config.SetPremultiply(true);
     }
     break;
   }
   }
+  config.SetColorMatrix(aColorMatrix);
   config.SetMask2D(aMask == MaskType::Mask2d);
   config.SetMask3D(aMask == MaskType::Mask3d);
   return config;
 }
 
 ShaderProgramOGL*
 CompositorOGL::GetShaderProgramFor(const ShaderConfigOGL &aConfig)
 {
@@ -1086,22 +1088,30 @@ CompositorOGL::DrawQuad(const Rect& aRec
 
   gfx::CompositionOp blendMode = gfx::CompositionOp::OP_OVER;
   if (aEffectChain.mSecondaryEffects[EffectTypes::BLEND_MODE]) {
     EffectBlendMode *blendEffect =
       static_cast<EffectBlendMode*>(aEffectChain.mSecondaryEffects[EffectTypes::BLEND_MODE].get());
     blendMode = blendEffect->mBlendMode;
   }
 
-  ShaderConfigOGL config = GetShaderConfigFor(aEffectChain.mPrimaryEffect, maskType, blendMode);
+  bool colorMatrix = aEffectChain.mSecondaryEffects[EffectTypes::COLOR_MATRIX];
+  ShaderConfigOGL config = GetShaderConfigFor(aEffectChain.mPrimaryEffect, maskType, blendMode, colorMatrix);
   config.SetOpacity(aOpacity != 1.f);
   ShaderProgramOGL *program = GetShaderProgramFor(config);
   program->Activate();
   program->SetProjectionMatrix(mProjMatrix);
   program->SetLayerTransform(aTransform);
+
+  if (colorMatrix) {
+      EffectColorMatrix* effectColorMatrix =
+        static_cast<EffectColorMatrix*>(aEffectChain.mSecondaryEffects[EffectTypes::COLOR_MATRIX].get());
+      program->SetColorMatrix(effectColorMatrix->mColorMatrix);
+  }
+
   IntPoint offset = mCurrentRenderTarget->GetOrigin();
   program->SetRenderOffset(offset.x, offset.y);
   if (aOpacity != 1.f)
     program->SetLayerOpacity(aOpacity);
   if (config.mFeatures & ENABLE_TEXTURE_RECT) {
     TexturedEffect* texturedEffect =
         static_cast<TexturedEffect*>(aEffectChain.mPrimaryEffect.get());
     TextureSourceOGL* source = texturedEffect->mTexture->AsSourceOGL();
--- a/gfx/layers/opengl/CompositorOGL.h
+++ b/gfx/layers/opengl/CompositorOGL.h
@@ -328,17 +328,18 @@ private:
                           const gfx::Rect *aClipRectIn,
                           const gfx::Matrix& aTransform,
                           const gfx::Rect& aRenderBounds,
                           gfx::Rect *aClipRectOut = nullptr,
                           gfx::Rect *aRenderBoundsOut = nullptr) MOZ_OVERRIDE;
 
   ShaderConfigOGL GetShaderConfigFor(Effect *aEffect,
                                      MaskType aMask = MaskType::MaskNone,
-                                     gfx::CompositionOp aOp = gfx::CompositionOp::OP_OVER) const;
+                                     gfx::CompositionOp aOp = gfx::CompositionOp::OP_OVER,
+                                     bool aColorMatrix = false) const;
   ShaderProgramOGL* GetShaderProgramFor(const ShaderConfigOGL &aConfig);
 
   /**
    * Create a FBO backed by a texture.
    * Note that the texture target type will be
    * of the type returned by FBOTextureTarget; different
    * shaders are required to sample from the different
    * texture types.
--- a/gfx/layers/opengl/OGLShaderProgram.cpp
+++ b/gfx/layers/opengl/OGLShaderProgram.cpp
@@ -42,16 +42,18 @@ AddUniforms(ProgramProfileOGL& aProfile)
         "uCbTexture",
         "uCrTexture",
         "uBlackTexture",
         "uWhiteTexture",
         "uMaskTexture",
         "uRenderColor",
         "uTexCoordMultiplier",
         "uTexturePass2",
+        "uColorMatrix",
+        "uColorMatrixVector",
         nullptr
     };
 
     for (int i = 0; sKnownUniformNames[i] != nullptr; ++i) {
         aProfile.mUniforms[i].mNameString = sKnownUniformNames[i];
         aProfile.mUniforms[i].mName = (KnownUniform::KnownUniformName) i;
     }
 }
--- a/gfx/layers/opengl/OGLShaderProgram.h
+++ b/gfx/layers/opengl/OGLShaderProgram.h
@@ -63,16 +63,18 @@ public:
     CbTexture,
     CrTexture,
     BlackTexture,
     WhiteTexture,
     MaskTexture,
     RenderColor,
     TexCoordMultiplier,
     TexturePass2,
+    ColorMatrix,
+    ColorMatrixVector,
 
     KnownUniformCount
   };
 
   KnownUniform()
   {
     mName = NotAKnownUniform;
     mNameString = nullptr;
@@ -364,16 +366,22 @@ public:
   void SetRenderColor(const gfxRGBA& aColor) {
     SetUniform(KnownUniform::RenderColor, aColor);
   }
 
   void SetRenderColor(const gfx::Color& aColor) {
     SetUniform(KnownUniform::RenderColor, aColor);
   }
 
+  void SetColorMatrix(const gfx::Matrix5x4& aColorMatrix)
+  {
+    SetMatrixUniform(KnownUniform::ColorMatrix, &aColorMatrix._11);
+    SetUniform(KnownUniform::ColorMatrixVector, 4, &aColorMatrix._51);
+  }
+
   void SetTexCoordMultiplier(float aWidth, float aHeight) {
     float f[] = {aWidth, aHeight};
     SetUniform(KnownUniform::TexCoordMultiplier, 2, f);
   }
 
   // Set whether we want the component alpha shader to return the color
   // vector (pass 1, false) or the alpha vector (pass2, true). With support
   // for multiple render targets we wouldn't need two passes here.
@@ -427,17 +435,17 @@ protected:
     NS_ASSERTION(aKnownUniform >= 0 && aKnownUniform < KnownUniform::KnownUniformCount, "Invalid known uniform");
 
     KnownUniform& ku(mProfile.mUniforms[aKnownUniform]);
     if (ku.UpdateUniform(aColor.r, aColor.g, aColor.b, aColor.a)) {
       mGL->fUniform4fv(ku.mLocation, 1, ku.mValue.f16v);
     }
   }
 
-  void SetUniform(KnownUniform::KnownUniformName aKnownUniform, int aLength, float *aFloatValues)
+  void SetUniform(KnownUniform::KnownUniformName aKnownUniform, int aLength, const float *aFloatValues)
   {
     ASSERT_THIS_PROGRAM;
     NS_ASSERTION(aKnownUniform >= 0 && aKnownUniform < KnownUniform::KnownUniformCount, "Invalid known uniform");
 
     KnownUniform& ku(mProfile.mUniforms[aKnownUniform]);
     if (ku.UpdateUniform(aLength, aFloatValues)) {
       switch (aLength) {
       case 1: mGL->fUniform1fv(ku.mLocation, 1, ku.mValue.f16v); break;
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -245,16 +245,18 @@ private:
   DECL_GFX_PREF(Once, "layers.offmainthreadcomposition.testing.enabled", LayersOffMainThreadCompositionTestingEnabled, bool, false);
   DECL_GFX_PREF(Once, "layers.use-image-offscreen-surfaces",   UseImageOffscreenSurfaces, bool, false);
   DECL_GFX_PREF(Live, "layers.orientation.sync.timeout",       OrientationSyncMillis, uint32_t, (uint32_t)0);
   DECL_GFX_PREF(Once, "layers.prefer-d3d9",                    LayersPreferD3D9, bool, false);
   DECL_GFX_PREF(Once, "layers.prefer-opengl",                  LayersPreferOpenGL, bool, false);
   DECL_GFX_PREF(Once, "layers.progressive-paint",              UseProgressiveTilePainting, bool, false);
   DECL_GFX_PREF(Once, "layers.scroll-graph",                   LayersScrollGraph, bool, false);
   DECL_GFX_PREF(Once, "layers.uniformity-info",                UniformityInfo, bool, false);
+  DECL_GFX_PREF(Live, "layers.invert",                         Invert, bool, false);
+  DECL_GFX_PREF(Live, "layers.grayscale",                      Grayscale, bool, false);
 
   DECL_GFX_PREF(Live, "layout.css.scroll-behavior.damping-ratio", ScrollBehaviorDampingRatio, float, 1.0f);
   DECL_GFX_PREF(Live, "layout.css.scroll-behavior.enabled",    ScrollBehaviorEnabled, bool, false);
   DECL_GFX_PREF(Live, "layout.css.scroll-behavior.spring-constant", ScrollBehaviorSpringConstant, float, 250.0f);
   DECL_GFX_PREF(Once, "layout.css.touch_action.enabled",       TouchActionEnabled, bool, false);
   DECL_GFX_PREF(Once, "layout.frame_rate",                     LayoutFrameRate, int32_t, -1);
   DECL_GFX_PREF(Live, "layout.display-list.dump",              LayoutDumpDisplayList, bool, false);
   DECL_GFX_PREF(Once, "layout.paint_rects_separately",         LayoutPaintRectsSeparately, bool, true);