Bug 1296657: Attempt to create an mOptSurface of the correct type for the DT we're drawing to. r=jrmuizel,tnikkel draft
authorBas Schouten <bschouten@mozilla.com>
Fri, 19 Aug 2016 17:56:36 +0200
changeset 403352 4fdb5e9da5a88a59b09634a58f07ec000832cd71
parent 399104 3d92407a320cb79fde6d1d365557aa20d5cc9db4
child 403353 02fd71f89d7d37ac5eecbb9b278731f8ff408edc
child 403354 a0f69da8f6f6ad5df8c2bff073e4bcbd1ebd0ab4
push id26889
push userbschouten@mozilla.com
push dateFri, 19 Aug 2016 15:57:40 +0000
reviewersjrmuizel, tnikkel
bugs1296657
milestone51.0a1
Bug 1296657: Attempt to create an mOptSurface of the correct type for the DT we're drawing to. r=jrmuizel,tnikkel MozReview-Commit-ID: JsqQ5js4QpH
image/VectorImage.cpp
image/VectorImage.h
image/imgFrame.cpp
image/imgFrame.h
--- a/image/VectorImage.cpp
+++ b/image/VectorImage.cpp
@@ -863,17 +863,17 @@ VectorImage::Draw(gfxContext* aContext,
   // drawing parameters, use that.
   RefPtr<gfxDrawable> svgDrawable = LookupCachedSurface(params);
   if (svgDrawable) {
     Show(svgDrawable, params);
     return DrawResult::SUCCESS;
   }
 
   // We didn't get a hit in the surface cache, so we'll need to rerasterize.
-  CreateSurfaceAndShow(params);
+  CreateSurfaceAndShow(params, aContext->GetDrawTarget()->GetBackendType());
   return DrawResult::SUCCESS;
 }
 
 already_AddRefed<gfxDrawable>
 VectorImage::LookupCachedSurface(const SVGDrawingParameters& aParams)
 {
   // If we're not allowed to use a cached surface, don't attempt a lookup.
   if (aParams.flags & FLAG_BYPASS_SURFACE_CACHE) {
@@ -906,17 +906,17 @@ VectorImage::LookupCachedSurface(const S
   }
 
   RefPtr<gfxDrawable> svgDrawable =
     new gfxSurfaceDrawable(surface, drawableRef->GetSize());
   return svgDrawable.forget();
 }
 
 void
-VectorImage::CreateSurfaceAndShow(const SVGDrawingParameters& aParams)
+VectorImage::CreateSurfaceAndShow(const SVGDrawingParameters& aParams, BackendType aBackend)
 {
   mSVGDocumentWrapper->UpdateViewportBounds(aParams.viewportSize);
   mSVGDocumentWrapper->FlushImageTransformInvalidation();
 
   RefPtr<gfxDrawingCallback> cb =
     new SVGDrawingCallback(mSVGDocumentWrapper,
                            IntRect(IntPoint(0, 0), aParams.viewportSize),
                            aParams.size,
@@ -944,17 +944,18 @@ VectorImage::CreateSurfaceAndShow(const 
   SurfaceCache::UnlockEntries(ImageKey(this));
 
   // Try to create an imgFrame, initializing the surface it contains by drawing
   // our gfxDrawable into it. (We use FILTER_NEAREST since we never scale here.)
   NotNull<RefPtr<imgFrame>> frame = WrapNotNull(new imgFrame);
   nsresult rv =
     frame->InitWithDrawable(svgDrawable, aParams.size,
                             SurfaceFormat::B8G8R8A8,
-                            SamplingFilter::POINT, aParams.flags);
+                            SamplingFilter::POINT, aParams.flags,
+                            aBackend);
 
   // If we couldn't create the frame, it was probably because it would end
   // up way too big. Generally it also wouldn't fit in the cache, but the prefs
   // could be set such that the cache isn't the limiting factor.
   if (NS_FAILED(rv)) {
     return Show(svgDrawable, aParams);
   }
 
--- a/image/VectorImage.h
+++ b/image/VectorImage.h
@@ -78,17 +78,18 @@ protected:
   virtual nsresult StopAnimation() override;
   virtual bool     ShouldAnimate() override;
 
 private:
   /// Attempt to find a cached surface matching @aParams in the SurfaceCache.
   already_AddRefed<gfxDrawable>
     LookupCachedSurface(const SVGDrawingParameters& aParams);
 
-  void CreateSurfaceAndShow(const SVGDrawingParameters& aParams);
+  void CreateSurfaceAndShow(const SVGDrawingParameters& aParams,
+                            gfx::BackendType aBackend);
   void Show(gfxDrawable* aDrawable, const SVGDrawingParameters& aParams);
 
   nsresult Init(const char* aMimeType, uint32_t aFlags);
 
   /**
    * In catastrophic circumstances like a GPU driver crash, we may lose our
    * surfaces even if they're locked. RecoverFromLossOfSurfaces discards all
    * existing surfaces, allowing us to recover.
--- a/image/imgFrame.cpp
+++ b/image/imgFrame.cpp
@@ -266,17 +266,18 @@ imgFrame::InitForDecoder(const nsIntSize
   return NS_OK;
 }
 
 nsresult
 imgFrame::InitWithDrawable(gfxDrawable* aDrawable,
                            const nsIntSize& aSize,
                            const SurfaceFormat aFormat,
                            SamplingFilter aSamplingFilter,
-                           uint32_t aImageFlags)
+                           uint32_t aImageFlags,
+                           gfx::BackendType aBackend)
 {
   // Assert for properties that should be verified by decoders,
   // warn for properties related to bad content.
   if (!AllowedImageSize(aSize.width, aSize.height)) {
     NS_WARNING("Should have legal image size");
     mAborted = true;
     return NS_ERROR_FAILURE;
   }
@@ -329,17 +330,17 @@ imgFrame::InitWithDrawable(gfxDrawable* 
   } else {
     // We can't use data surfaces for content, so we'll create an offscreen
     // surface instead.  This means if someone later calls RawAccessRef(), we
     // may have to do an expensive readback, but we warned callers about that in
     // the documentation for this method.
     MOZ_ASSERT(!mOptSurface, "Called imgFrame::InitWithDrawable() twice?");
 
     target = gfxPlatform::GetPlatform()->
-      CreateOffscreenContentDrawTarget(mFrameRect.Size(), mFormat);
+      CreateDrawTargetForBackend(aBackend, mFrameRect.Size(), mFormat);
   }
 
   if (!target || !target->IsValid()) {
     mAborted = true;
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
   // Draw using the drawable the caller provided.
@@ -386,22 +387,25 @@ imgFrame::CanOptimizeOpaqueImage()
   // This optimization is free and safe, so we always do it when we can except
   // if we have a Skia backend. Skia doesn't support RGBX so ensure we don't
   // optimize to a RGBX surface.
   return mHasNoAlpha && mFormat == SurfaceFormat::B8G8R8A8 && mImageSurface &&
          (gfxPlatform::GetPlatform()->GetDefaultContentBackend() != BackendType::SKIA);
 }
 
 nsresult
-imgFrame::Optimize()
+imgFrame::Optimize(DrawTarget* aTarget)
 {
   MOZ_ASSERT(NS_IsMainThread());
   mMonitor.AssertCurrentThreadOwns();
-  MOZ_ASSERT(mLockCount == 1,
-             "Should only optimize when holding the lock exclusively");
+  
+  if (mLockCount > 0 || !mOptimizable) {
+    // Don't optimize right now.
+    return NS_OK;
+  }
 
   // Check whether image optimization is disabled -- not thread safe!
   static bool gDisableOptimize = false;
   static bool hasCheckedOptimize = false;
   if (!hasCheckedOptimize) {
     if (PR_GetEnv("MOZ_DISABLE_IMAGE_OPTIMIZE")) {
       gDisableOptimize = true;
     }
@@ -414,42 +418,43 @@ imgFrame::Optimize()
   }
 
   // This optimization is basically free, so we perform it even if optimization is disabled.
   if (CanOptimizeOpaqueImage()) {
     mFormat = SurfaceFormat::B8G8R8X8;
     mImageSurface = CreateLockedSurface(mVBuf, mFrameRect.Size(), mFormat);
   }
 
-  if (!mOptimizable || gDisableOptimize) {
+  if (gDisableOptimize) {
     return NS_OK;
   }
 
   if (mPalettedImageData || mOptSurface) {
     return NS_OK;
   }
 
   // XXX(seth): It's currently unclear if there's any reason why we can't
   // optimize non-premult surfaces. We should look into removing this.
   if (mNonPremult) {
     return NS_OK;
   }
 
-  mOptSurface = gfxPlatform::GetPlatform()
-    ->ScreenReferenceDrawTarget()->OptimizeSourceSurface(mImageSurface);
+  mOptSurface = aTarget->OptimizeSourceSurface(mImageSurface);
   if (mOptSurface == mImageSurface) {
     mOptSurface = nullptr;
   }
 
   if (mOptSurface) {
     mVBuf = nullptr;
     mVBufPtr = nullptr;
     mImageSurface = nullptr;
   }
 
+  mOptimizable = false;
+
 #ifdef MOZ_WIDGET_ANDROID
   // On Android, free mImageSurface unconditionally if we're discardable. This
   // allows the operating system to free our volatile buffer.
   // XXX(seth): We'd eventually like to do this on all platforms, but right now
   // converting raw memory to a SourceSurface is expensive on some backends.
   mImageSurface = nullptr;
 #endif
 
@@ -541,16 +546,20 @@ bool imgFrame::Draw(gfxContext* aContext
 
   if (mPalettedImageData) {
     MOZ_ASSERT_UNREACHABLE("Directly drawing a paletted image!");
     return false;
   }
 
   MonitorAutoLock lock(mMonitor);
 
+  // Possibly convert this image into a GPU texture, this may also cause our
+  // mImageSurface to be released and the OS to release the underlying memory.
+  Optimize(aContext->GetDrawTarget());
+
   bool doPartialDecode = !AreAllPixelsWritten();
 
   RefPtr<SourceSurface> surf = GetSurfaceInternal();
   if (!surf) {
     return false;
   }
 
   gfxRect imageRect(0, 0, mImageSize.width, mImageSize.height);
@@ -742,31 +751,16 @@ void
 imgFrame::AssertImageDataLocked() const
 {
 #ifdef DEBUG
   MonitorAutoLock lock(mMonitor);
   MOZ_ASSERT(mLockCount > 0, "Image data should be locked");
 #endif
 }
 
-class UnlockImageDataRunnable : public Runnable
-{
-public:
-  explicit UnlockImageDataRunnable(imgFrame* aTarget)
-    : mTarget(aTarget)
-  {
-    MOZ_ASSERT(mTarget);
-  }
-
-  NS_IMETHOD Run() override { return mTarget->UnlockImageData(); }
-
-private:
-  RefPtr<imgFrame> mTarget;
-};
-
 nsresult
 imgFrame::UnlockImageData()
 {
   MonitorAutoLock lock(mMonitor);
 
   MOZ_ASSERT(mLockCount > 0, "Unlocking an unlocked image!");
   if (mLockCount <= 0) {
     return NS_ERROR_FAILURE;
@@ -774,27 +768,16 @@ imgFrame::UnlockImageData()
 
   MOZ_ASSERT(mLockCount > 1 || mFinished || mAborted,
              "Should have Finish()'d or aborted before unlocking");
 
   // If we're about to become unlocked, we don't need to hold on to our data
   // surface anymore. (But we don't need to do anything for paletted images,
   // which don't have surfaces.)
   if (mLockCount == 1 && !mPalettedImageData) {
-    // We can't safely optimize off-main-thread, so create a runnable to do it.
-    if (!NS_IsMainThread()) {
-      nsCOMPtr<nsIRunnable> runnable = new UnlockImageDataRunnable(this);
-      NS_DispatchToMainThread(runnable);
-      return NS_OK;
-    }
-
-    // Convert our data surface to a GPU surface if possible. We'll also try to
-    // release mImageSurface.
-    Optimize();
-
     // Allow the OS to release our data surface. Note that mImageSurface also
     // keeps our volatile buffer alive, so this doesn't actually work unless we
     // released mImageSurface in Optimize().
     mVBufPtr = nullptr;
   }
 
   mLockCount--;
 
--- a/image/imgFrame.h
+++ b/image/imgFrame.h
@@ -225,22 +225,26 @@ public:
    * Initialize this imgFrame with a new surface and draw the provided
    * gfxDrawable into it.
    *
    * This is appropriate to use when drawing content into an imgFrame, as it
    * uses the same graphics backend as normal content drawing. The downside is
    * that the underlying surface may not be stored in a volatile buffer on all
    * platforms, and raw access to the surface (using RawAccessRef()) may be much
    * more expensive than in the InitForDecoder() case.
+   *
+   * aBackend specifies the DrawTarget backend type this imgFrame is supposed
+   *          to be drawn to.
    */
   nsresult InitWithDrawable(gfxDrawable* aDrawable,
                             const nsIntSize& aSize,
                             const SurfaceFormat aFormat,
                             SamplingFilter aSamplingFilter,
-                            uint32_t aImageFlags);
+                            uint32_t aImageFlags,
+                            gfx::BackendType aBackend);
 
   DrawableFrameRef DrawableRef();
   RawAccessFrameRef RawAccessRef();
 
   /**
    * Make this imgFrame permanently available for raw access.
    *
    * This is irrevocable, and should be avoided whenever possible, since it
@@ -343,17 +347,17 @@ public:
 
 private: // methods
 
   ~imgFrame();
 
   nsresult LockImageData();
   nsresult UnlockImageData();
   bool     CanOptimizeOpaqueImage();
-  nsresult Optimize();
+  nsresult Optimize(gfx::DrawTarget* aTarget);
 
   void AssertImageDataLocked() const;
 
   bool AreAllPixelsWritten() const;
   nsresult ImageUpdatedInternal(const nsIntRect& aUpdateRect);
   void GetImageDataInternal(uint8_t** aData, uint32_t* length) const;
   uint32_t GetImageBytesPerRow() const;
   uint32_t GetImageDataLength() const;