Bug 1292192 - Simplify CanvasRenderingContext2D::SwitchRenderingMode. r=gw280
authorNicolas Silva <nsilva@mozilla.com>
Tue, 09 Aug 2016 18:18:58 +0200
changeset 350121 65be64c62ed11d8e7c4a57ca30ad5859b7a70dd9
parent 350120 ddd470e0e85a3e7702a51fd4f379927ff22835b2
child 350122 9beaa75894056714aefe9737fbafc19afa3e8df7
push id6570
push userraliiev@mozilla.com
push dateMon, 14 Nov 2016 12:26:13 +0000
treeherdermozilla-beta@f455459b2ae5 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgw280
bugs1292192
milestone51.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 1292192 - Simplify CanvasRenderingContext2D::SwitchRenderingMode. r=gw280
dom/canvas/CanvasRenderingContext2D.cpp
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -1321,75 +1321,52 @@ CanvasRenderingContext2D::RedrawUser(con
 }
 
 bool CanvasRenderingContext2D::SwitchRenderingMode(RenderingMode aRenderingMode)
 {
   if (!IsTargetValid() || mRenderingMode == aRenderingMode) {
     return false;
   }
 
+  MOZ_ASSERT(mBufferProvider);
+
 #ifdef USE_SKIA_GPU
   // Do not attempt to switch into GL mode if the platform doesn't allow it.
   if ((aRenderingMode == RenderingMode::OpenGLBackendMode) &&
       !gfxPlatform::GetPlatform()->UseAcceleratedCanvas()) {
       return false;
   }
 #endif
 
-  RefPtr<SourceSurface> snapshot;
-  Matrix transform;
   RefPtr<PersistentBufferProvider> oldBufferProvider = mBufferProvider;
-  RefPtr<DrawTarget> oldTarget = mTarget;
-
-  AutoReturnSnapshot autoReturn(nullptr);
-
-  if (mTarget) {
-    snapshot = mTarget->Snapshot();
-    transform = mTarget->GetTransform();
-  } else {
-    MOZ_ASSERT(mBufferProvider);
-    // When mBufferProvider is true but we have no mTarget, our current state's
-    // transform is always valid. See ReturnTarget().
-    transform = CurrentState().transform;
-    snapshot = mBufferProvider->BorrowSnapshot();
-    autoReturn.mBufferProvider = mBufferProvider;
-    autoReturn.mSnapshot = &snapshot;
-  }
-
+
+  // Return the old target to the buffer provider.
+  // We need to do this before calling EnsureTarget.
+  ReturnTarget();
   mTarget = nullptr;
   mBufferProvider = nullptr;
   mResetLayer = true;
 
-  // Recreate target using the new rendering mode
+  // Borrowing the snapshot must be done after ReturnTarget.
+  RefPtr<SourceSurface> snapshot = oldBufferProvider->BorrowSnapshot();
+
+  // Recreate mTarget using the new rendering mode
   RenderingMode attemptedMode = EnsureTarget(nullptr, aRenderingMode);
   if (!IsTargetValid()) {
-    if (oldBufferProvider && oldTarget) {
-      oldBufferProvider->ReturnDrawTarget(oldTarget.forget());
-    }
+    oldBufferProvider->ReturnSnapshot(snapshot.forget());
     return false;
   }
 
   // We succeeded, so update mRenderingMode to reflect reality
   mRenderingMode = attemptedMode;
 
   // Restore the content from the old DrawTarget
-  gfx::Rect r(0, 0, mWidth, mHeight);
-  mTarget->DrawSurface(snapshot, r, r);
-
-  // Restore the clips and transform
-  for (uint32_t i = 0; i < CurrentState().clipsPushed.Length(); i++) {
-    mTarget->PushClip(CurrentState().clipsPushed[i]);
-  }
-
-  mTarget->SetTransform(transform);
-
-  if (oldBufferProvider && oldTarget) {
-    oldBufferProvider->ReturnDrawTarget(oldTarget.forget());
-  }
-
+  // Clips and transform were already restored in EnsureTarget.
+  mTarget->CopySurface(snapshot, IntRect(0, 0, mWidth, mHeight), IntPoint());
+  oldBufferProvider->ReturnSnapshot(snapshot.forget());
   return true;
 }
 
 void CanvasRenderingContext2D::Demote()
 {
   if (SwitchRenderingMode(RenderingMode::SoftwareBackendMode)) {
     RemoveDemotableContext(this);
   }
@@ -1541,43 +1518,37 @@ CanvasRenderingContext2D::EnsureTarget(c
   RenderingMode mode = (aRenderingMode == RenderingMode::DefaultBackendMode) ? mRenderingMode : aRenderingMode;
 
   if (mTarget && mode == mRenderingMode) {
     return mRenderingMode;
   }
 
   ScheduleStableStateCallback();
 
+  // we'll do a few extra things at the end of this method if we changed the
+  // buffer provider.
+  RefPtr<PersistentBufferProvider> oldBufferProvider = mBufferProvider;
+
   if (mBufferProvider && mode == mRenderingMode) {
     gfx::Rect rect(0, 0, mWidth, mHeight);
     if (aCoveredRect && CurrentState().transform.TransformBounds(*aCoveredRect).Contains(rect)) {
       mTarget = mBufferProvider->BorrowDrawTarget(IntRect());
     } else {
       mTarget = mBufferProvider->BorrowDrawTarget(IntRect(0, 0, mWidth, mHeight));
     }
 
-    if (mTarget) {
-      // Restore clip and transform.
-      for (uint32_t i = 0; i < mStyleStack.Length(); i++) {
-        mTarget->SetTransform(mStyleStack[i].transform);
-        for (uint32_t c = 0; c < mStyleStack[i].clipsPushed.Length(); c++) {
-          mTarget->PushClip(mStyleStack[i].clipsPushed[c]);
-        }
-      }
-      return mRenderingMode;
-    } else {
-      mBufferProvider = nullptr;
-    }
+    mode = mRenderingMode;
   }
 
   mIsSkiaGL = false;
 
    // Check that the dimensions are sane
   IntSize size(mWidth, mHeight);
-  if (size.width <= gfxPrefs::MaxCanvasSize() &&
+  if (!mTarget &&
+      size.width <= gfxPrefs::MaxCanvasSize() &&
       size.height <= gfxPrefs::MaxCanvasSize() &&
       size.width >= 0 && size.height >= 0) {
     SurfaceFormat format = GetSurfaceFormat();
     nsIDocument* ownerDoc = nullptr;
     if (mCanvasElement) {
       ownerDoc = mCanvasElement->OwnerDoc();
     }
 
@@ -1622,56 +1593,72 @@ CanvasRenderingContext2D::EnsureTarget(c
       mTarget = mBufferProvider->BorrowDrawTarget(IntRect(IntPoint(), IntSize(mWidth, mHeight)));
     } else if (!mTarget) {
       mTarget = gfxPlatform::GetPlatform()->CreateOffscreenCanvasDrawTarget(size, format);
       mode = RenderingMode::SoftwareBackendMode;
     }
   }
 
   if (mTarget) {
-    static bool registered = false;
-    if (!registered) {
-      registered = true;
-      RegisterStrongMemoryReporter(new Canvas2dPixelsReporter());
+    // We changed the buffer provider.
+    // XXX - It would make more sense to track the allocation in
+    // PeristentBufferProvider, rather than here.
+    if (mBufferProvider != oldBufferProvider) {
+      static bool registered = false;
+      if (!registered) {
+        registered = true;
+        RegisterStrongMemoryReporter(new Canvas2dPixelsReporter());
+      }
+
+      gCanvasAzureMemoryUsed += mWidth * mHeight * 4;
+      JSContext* context = nsContentUtils::GetCurrentJSContext();
+      if (context) {
+        JS_updateMallocCounter(context, mWidth * mHeight * 4);
+      }
+
+      mTarget->ClearRect(gfx::Rect(Point(0, 0), Size(mWidth, mHeight)));
+
+      // Force a full layer transaction since we didn't have a layer before
+      // and now we might need one.
+      if (mCanvasElement) {
+        mCanvasElement->InvalidateCanvas();
+      }
+      // Calling Redraw() tells our invalidation machinery that the entire
+      // canvas is already invalid, which can speed up future drawing.
+      Redraw();
     }
 
-    gCanvasAzureMemoryUsed += mWidth * mHeight * 4;
-    JSContext* context = nsContentUtils::GetCurrentJSContext();
-    if (context) {
-      JS_updateMallocCounter(context, mWidth * mHeight * 4);
-    }
-
-    mTarget->ClearRect(gfx::Rect(Point(0, 0), Size(mWidth, mHeight)));
     if (mTarget->GetBackendType() == gfx::BackendType::CAIRO) {
       // Cairo doesn't play well with huge clips. When given a very big clip it
       // will try to allocate big mask surface without taking the target
       // size into account which can cause OOM. See bug 1034593.
       // This limits the clip extents to the size of the canvas.
       // A fix in Cairo would probably be preferable, but requires somewhat
       // invasive changes.
       mTarget->PushClipRect(gfx::Rect(Point(0, 0), Size(mWidth, mHeight)));
     }
-    // Force a full layer transaction since we didn't have a layer before
-    // and now we might need one.
-    if (mCanvasElement) {
-      mCanvasElement->InvalidateCanvas();
+
+    // Restore clip and transform.
+    for (uint32_t i = 0; i < mStyleStack.Length(); i++) {
+      mTarget->SetTransform(mStyleStack[i].transform);
+      for (uint32_t c = 0; c < mStyleStack[i].clipsPushed.Length(); c++) {
+        mTarget->PushClip(mStyleStack[i].clipsPushed[c]);
+      }
     }
-    // Calling Redraw() tells our invalidation machinery that the entire
-    // canvas is already invalid, which can speed up future drawing.
-    Redraw();
   } else {
     EnsureErrorTarget();
     mTarget = sErrorTarget;
     mBufferProvider = nullptr;
   }
 
   // Drop a note in the debug builds if we ever use accelerated Skia canvas.
   if (mIsSkiaGL && mTarget && mTarget->GetType() == DrawTargetType::HARDWARE_RASTER) {
     gfxWarningOnce() << "Using SkiaGL canvas.";
   }
+
   return mode;
 }
 
 int32_t
 CanvasRenderingContext2D::GetWidth() const
 {
   return mWidth;
 }
@@ -1765,16 +1752,23 @@ CanvasRenderingContext2D::ReturnTarget()
 {
   if (mTarget && mBufferProvider && mTarget != sErrorTarget) {
     CurrentState().transform = mTarget->GetTransform();
     for (uint32_t i = 0; i < mStyleStack.Length(); i++) {
       for (uint32_t c = 0; c < mStyleStack[i].clipsPushed.Length(); c++) {
         mTarget->PopClip();
       }
     }
+
+    if (mTarget->GetBackendType() == gfx::BackendType::CAIRO) {
+      // With the cairo backend we pushed an extra clip rect which we have to
+      // balance out here. See the comment in EnsureDrawTarget.
+      mTarget->PopClip();
+    }
+
     mBufferProvider->ReturnDrawTarget(mTarget.forget());
   }
 }
 
 NS_IMETHODIMP
 CanvasRenderingContext2D::InitializeWithDrawTarget(nsIDocShell* aShell,
                                                    gfx::DrawTarget* aTarget)
 {