Bug 1531766 - don't clear Skia DTs backing Canvas2D if it is already clearing. r=nical
authorLee Salzman <lsalzman@mozilla.com>
Fri, 01 Mar 2019 12:23:02 -0500
changeset 519978 fad9ff877c1cbd2b06c03263ba9fc91d2f1fc405
parent 519977 74ab0e9aebf5cea683ca7c9b8fd77303e223c3b9
child 519979 1178c5fddda7dee4cb1b0aeee6fd9afb1c6d30d0
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.
@@ -2334,17 +2335,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, true);
   if (!IsTargetValid()) {
     return;
   }
 
   mTarget->ClearRect(clearRect);
 
   RedrawUser(gfxRect(aX, aY, aW, aH));
 }
@@ -2401,21 +2402,22 @@ 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, 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);