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
--- 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;