Bug 1531766 - don't clear Skia DTs backing Canvas2D if it is already clearing. r=nical
☠☠ backed out by 93ee0fb34c18 ☠ ☠
authorLee Salzman <lsalzman@mozilla.com>
Fri, 01 Mar 2019 10:45:37 -0500
changeset 519972 c5e9d61ee3f40adb25a61d8cb571762eb2752606
parent 519971 e41148de8adf5c5e19859d215c19280769bc8c19
child 519973 93ee0fb34c186f8b1f30a41a7f3deec499fa5dc8
push id10862
push userffxbld-merge
push dateMon, 11 Mar 2019 13:01:11 +0000
treeherdermozilla-beta@a2e7f5c935da [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnical
bugs1531766
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 1531766 - don't clear Skia DTs backing Canvas2D if it is already clearing. r=nical
dom/canvas/CanvasRenderingContext2D.cpp
dom/canvas/CanvasRenderingContext2D.h
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -217,35 +217,35 @@ class CanvasLinearGradient : public Canv
 
   // Beginning of linear gradient.
   Point mBegin;
   // End of linear gradient.
   Point mEnd;
 };
 
 bool CanvasRenderingContext2D::PatternIsOpaque(
-    CanvasRenderingContext2D::Style aStyle) const {
+    CanvasRenderingContext2D::Style aStyle, bool* aIsColor) const {
   const ContextState& state = CurrentState();
-  if (state.globalAlpha < 1.0) {
-    return false;
-  }
-
-  if (state.patternStyles[aStyle] && state.patternStyles[aStyle]->mSurface) {
-    return IsOpaque(state.patternStyles[aStyle]->mSurface->GetFormat());
-  }
-
-  // TODO: for gradient patterns we could check that all stops are opaque
-  // colors.
-
-  if (!state.gradientStyles[aStyle]) {
-    // it's a color pattern.
-    return Color::FromABGR(state.colorStyles[aStyle]).a >= 1.0;
-  }
-
-  return false;
+  bool opaque = false;
+  bool color = false;
+  if (state.globalAlpha >= 1.0) {
+    if (state.patternStyles[aStyle] && state.patternStyles[aStyle]->mSurface) {
+      opaque = IsOpaque(state.patternStyles[aStyle]->mSurface->GetFormat());
+    } else if (!state.gradientStyles[aStyle]) {
+      // TODO: for gradient patterns we could check that all stops are opaque
+      // colors.
+      // it's a color pattern.
+      opaque = Color::FromABGR(state.colorStyles[aStyle]).a >= 1.0;
+      color = true;
+    }
+  }
+  if (aIsColor) {
+    *aIsColor = color;
+  }
+  return opaque;
 }
 
 // This class is named 'GeneralCanvasPattern' instead of just
 // 'GeneralPattern' to keep Windows PGO builds from confusing the
 // GeneralPattern class in gfxContext.cpp with this one.
 class CanvasGeneralPattern {
  public:
   typedef CanvasRenderingContext2D::Style Style;
@@ -1162,17 +1162,18 @@ void CanvasRenderingContext2D::RestoreCl
         mTarget->PushClip(clipOrTransform.clip);
       } else {
         mTarget->SetTransform(clipOrTransform.transform);
       }
     }
   }
 }
 
-bool CanvasRenderingContext2D::EnsureTarget(const gfx::Rect* aCoveredRect) {
+bool CanvasRenderingContext2D::EnsureTarget(
+    const gfx::Rect* aCoveredRect, bool aWillClear) {
   if (AlreadyShutDown()) {
     gfxCriticalError() << "Attempt to render into a Canvas2d after shutdown.";
     SetErrorState();
     return false;
   }
 
   if (mTarget) {
     return true;
@@ -1236,17 +1237,17 @@ bool CanvasRenderingContext2D::EnsureTar
     SetErrorState();
     return false;
   }
 
   MOZ_ASSERT(newTarget);
   MOZ_ASSERT(newProvider);
 
   bool needsClear = !canDiscardContent;
-  if (newTarget->GetBackendType() == gfx::BackendType::SKIA) {
+  if (newTarget->GetBackendType() == gfx::BackendType::SKIA && (needsClear || !aWillClear)) {
     // Skia expects the unused X channel to contains 0xFF even for opaque
     // operations so we can't skip clearing in that case, even if we are going
     // to cover the entire canvas in the next drawing operation.
     newTarget->ClearRect(canvasRect);
     needsClear = false;
   }
 
   // Try to copy data from the previous buffer provider if there is one.
@@ -2341,17 +2342,17 @@ void CanvasRenderingContext2D::ClearRect
                                          double aH) {
   // Do not allow zeros - it's a no-op at that point per spec.
   if (!ValidateRect(aX, aY, aW, aH, false)) {
     return;
   }
 
   gfx::Rect clearRect(aX, aY, aW, aH);
 
-  EnsureTarget(&clearRect);
+  EnsureTarget(&clearRect, RenderingMode::DefaultBackendMode, true);
   if (!IsTargetValid()) {
     return;
   }
 
   mTarget->ClearRect(clearRect);
 
   RedrawUser(gfxRect(aX, aY, aW, aH));
 }
@@ -2408,21 +2409,24 @@ void CanvasRenderingContext2D::FillRect(
           aH = 0;
         }
       }
     }
   }
   state = nullptr;
 
   CompositionOp op = UsedOperation();
+  bool isColor;
   bool discardContent =
-      PatternIsOpaque(Style::FILL) &&
+      PatternIsOpaque(Style::FILL, &isColor) &&
       (op == CompositionOp::OP_OVER || op == CompositionOp::OP_SOURCE);
   const gfx::Rect fillRect(aX, aY, aW, aH);
-  EnsureTarget(discardContent ? &fillRect : nullptr);
+  EnsureTarget(discardContent ? &fillRect : nullptr,
+               RenderingMode::DefaultBackendMode,
+               discardContent && isColor);
   if (!IsTargetValid()) {
     return;
   }
 
   gfx::Rect bounds;
   const bool needBounds = NeedToCalculateBounds();
   if (!IsTargetValid()) {
     return;
--- a/dom/canvas/CanvasRenderingContext2D.h
+++ b/dom/canvas/CanvasRenderingContext2D.h
@@ -602,17 +602,19 @@ class CanvasRenderingContext2D final : p
   /**
    * Create the backing surfacing, if it doesn't exist. If there is an error
    * in creating the target then it will put sErrorTarget in place. If there
    * is in turn an error in creating the sErrorTarget then they would both
    * be null so IsTargetValid() would still return null.
    *
    * Returns true on success.
    */
-  bool EnsureTarget(const gfx::Rect* aCoveredRect = nullptr);
+  bool EnsureTarget(
+      const gfx::Rect* aCoveredRect = nullptr,
+      bool aWillClear = false);
 
   void RestoreClipsAndTransformToTarget();
 
   bool TrySharedTarget(RefPtr<gfx::DrawTarget>& aOutDT,
                        RefPtr<layers::PersistentBufferProvider>& aOutProvider);
 
   bool TryBasicTarget(RefPtr<gfx::DrawTarget>& aOutDT,
                       RefPtr<layers::PersistentBufferProvider>& aOutProvider);
@@ -659,19 +661,19 @@ class CanvasRenderingContext2D final : p
    * Returns the surface format this canvas should be allocated using. Takes
    * into account mOpaque, platform requirements, etc.
    */
   mozilla::gfx::SurfaceFormat GetSurfaceFormat() const;
 
   /**
    * Returns true if we know for sure that the pattern for a given style is
    * opaque. Usefull to know if we can discard the content below in certain
-   * situations.
+   * situations. Optionally checks if the pattern is a color pattern.
    */
-  bool PatternIsOpaque(Style aStyle) const;
+  bool PatternIsOpaque(Style aStyle, bool* aIsColor = nullptr) const;
 
   nsLayoutUtils::SurfaceFromElementResult CachedSurfaceFromElement(
       Element* aElement);
 
   void DrawImage(const CanvasImageSource& aImgElt, double aSx, double aSy,
                  double aSw, double aSh, double aDx, double aDy, double aDw,
                  double aDh, uint8_t aOptional_argc,
                  mozilla::ErrorResult& aError);