diff --git a/dom/canvas/CanvasRenderingContext2D.cpp b/dom/canvas/CanvasRenderingContext2D.cpp index 522263f23253..c2d6886e5950 100644 --- a/dom/canvas/CanvasRenderingContext2D.cpp +++ b/dom/canvas/CanvasRenderingContext2D.cpp @@ -87,6 +87,7 @@ #include "mozilla/gfx/PatternHelpers.h" #include "mozilla/ipc/DocumentRendererParent.h" #include "mozilla/ipc/PDocumentRendererParent.h" +#include "mozilla/layers/PersistentBufferProvider.h" #include "mozilla/MathAlgorithms.h" #include "mozilla/Preferences.h" #include "mozilla/Telemetry.h" @@ -820,6 +821,7 @@ public: // Since SkiaGL default to store drawing command until flush // We will have to flush it before present. context->mTarget->Flush(); + context->ReturnTarget(); } static void DidTransactionCallback(void* aData) @@ -1034,7 +1036,9 @@ CanvasRenderingContext2D::Reset() gCanvasAzureMemoryUsed -= mWidth * mHeight * 4; } + ReturnTarget(); mTarget = nullptr; + mBufferProvider = nullptr; // reset hit regions mHitRegionsOptions.ClearAndRetainStorage(); @@ -1186,9 +1190,18 @@ bool CanvasRenderingContext2D::SwitchRenderingMode(RenderingMode aRenderingMode) } #endif - RefPtr snapshot = mTarget->Snapshot(); - RefPtr oldTarget = mTarget; + RefPtr snapshot; + Matrix transform; + + if (mTarget) { + snapshot = mTarget->Snapshot(); + transform = mTarget->GetTransform(); + } else { + MOZ_ASSERT(mBufferProvider); + snapshot = mBufferProvider->GetSnapshot(); + } mTarget = nullptr; + mBufferProvider = nullptr; mResetLayer = true; // Recreate target using the new rendering mode @@ -1208,7 +1221,7 @@ bool CanvasRenderingContext2D::SwitchRenderingMode(RenderingMode aRenderingMode) mTarget->PushClip(CurrentState().clipsPushed[i]); } - mTarget->SetTransform(oldTarget->GetTransform()); + mTarget->SetTransform(transform); return true; } @@ -1332,6 +1345,15 @@ CanvasRenderingContext2D::EnsureTarget(RenderingMode aRenderingMode) return mRenderingMode; } + if (mBufferProvider && mode == mRenderingMode) { + mTarget = mBufferProvider->GetDT(IntRect(IntPoint(), IntSize(mWidth, mHeight))); + if (mTarget) { + return mRenderingMode; + } else { + mBufferProvider = nullptr; + } + } + // Check that the dimensions are sane IntSize size(mWidth, mHeight); if (size.width <= gfxPrefs::MaxCanvasSize() && @@ -1350,11 +1372,12 @@ CanvasRenderingContext2D::EnsureTarget(RenderingMode aRenderingMode) nsContentUtils::PersistentLayerManagerForDocument(ownerDoc); } - if (layerManager) { + if (layerManager) { if (mode == RenderingMode::OpenGLBackendMode && gfxPlatform::GetPlatform()->UseAcceleratedSkiaCanvas() && CheckSizeForSkiaGL(size)) { DemoteOldestContextIfNecessary(); + mBufferProvider = nullptr; #if USE_SKIA_GPU SkiaGLGlue* glue = gfxPlatform::GetPlatform()->GetSkiaGLGlue(); @@ -1369,17 +1392,19 @@ CanvasRenderingContext2D::EnsureTarget(RenderingMode aRenderingMode) } } #endif - if (!mTarget) { - mTarget = layerManager->CreateDrawTarget(size, format); - } - } else { - mTarget = layerManager->CreateDrawTarget(size, format); - mode = RenderingMode::SoftwareBackendMode; } - } else { - mTarget = gfxPlatform::GetPlatform()->CreateOffscreenCanvasDrawTarget(size, format); - mode = RenderingMode::SoftwareBackendMode; - } + + if (!mBufferProvider) { + mBufferProvider = layerManager->CreatePersistentBufferProvider(size, format); + } + } + + if (mBufferProvider) { + mTarget = mBufferProvider->GetDT(IntRect(IntPoint(), IntSize(mWidth, mHeight))); + } else if (!mTarget) { + mTarget = gfxPlatform::GetPlatform()->CreateOffscreenCanvasDrawTarget(size, format); + mode = RenderingMode::SoftwareBackendMode; + } } if (mTarget) { @@ -1496,6 +1521,17 @@ CanvasRenderingContext2D::ClearTarget() } } +void +CanvasRenderingContext2D::ReturnTarget() +{ + if (mTarget && mBufferProvider) { + CurrentState().transform = mTarget->GetTransform(); + DrawTarget* oldDT = mTarget; + mTarget = nullptr; + mBufferProvider->ReturnAndUseDT(oldDT); + } +} + NS_IMETHODIMP CanvasRenderingContext2D::InitializeWithSurface(nsIDocShell *shell, gfxASurface *surface, @@ -2382,9 +2418,7 @@ void CanvasRenderingContext2D::ClearRect(double x, double y, double w, double h) { - if (!mTarget) { - return; - } + EnsureTarget(); mTarget->ClearRect(mgfx::Rect(x, y, w, h)); @@ -2545,6 +2579,7 @@ CanvasRenderingContext2D::BeginPath() void CanvasRenderingContext2D::Fill(const CanvasWindingRule& winding) { + EnsureTarget(); EnsureUserSpacePath(winding); if (!mPath) { @@ -2590,6 +2625,7 @@ void CanvasRenderingContext2D::Fill(const CanvasPath& path, const CanvasWindingR void CanvasRenderingContext2D::Stroke() { + EnsureTarget(); EnsureUserSpacePath(); if (!mPath) { @@ -2725,6 +2761,8 @@ bool CanvasRenderingContext2D::DrawCustomFocusRing(mozilla::dom::Element& aEleme void CanvasRenderingContext2D::Clip(const CanvasWindingRule& winding) { + EnsureTarget(); + EnsureUserSpacePath(winding); if (!mPath) { @@ -5156,6 +5194,7 @@ CanvasRenderingContext2D::PutImageData_explicit(int32_t x, int32_t y, uint32_t w bool hasDirtyRect, int32_t dirtyX, int32_t dirtyY, int32_t dirtyWidth, int32_t dirtyHeight) { + EnsureTarget(); if (mDrawObserver) { mDrawObserver->DidDrawCall(CanvasDrawObserver::DrawCallType::PutImageData); } @@ -5347,8 +5386,11 @@ static uint8_t g2DContextLayerUserData; uint32_t CanvasRenderingContext2D::SkiaGLTex() const { - MOZ_ASSERT(IsTargetValid()); - return (uint32_t)(uintptr_t)mTarget->GetNativeSurface(NativeSurfaceType::OPENGL_TEXTURE); + if (!mTarget) { + return 0; + } + MOZ_ASSERT(IsTargetValid()); + return (uint32_t)(uintptr_t)mTarget->GetNativeSurface(NativeSurfaceType::OPENGL_TEXTURE); } void CanvasRenderingContext2D::RemoveDrawObserver() @@ -5359,6 +5401,26 @@ void CanvasRenderingContext2D::RemoveDrawObserver() } } +PersistentBufferProvider* +CanvasRenderingContext2D::GetBufferProvider(LayerManager* aManager) +{ + if (mBufferProvider) { + return mBufferProvider; + } + + if (!mTarget) { + return nullptr; + } + + mBufferProvider = aManager->CreatePersistentBufferProvider(mTarget->GetSize(), mTarget->GetFormat()); + + RefPtr surf = mTarget->Snapshot(); + + mTarget = mBufferProvider->GetDT(IntRect(IntPoint(), mTarget->GetSize())); + mTarget->CopySurface(surf, IntRect(IntPoint(), mTarget->GetSize()), IntPoint()); + + return mBufferProvider; +} already_AddRefed CanvasRenderingContext2D::GetCanvasLayer(nsDisplayListBuilder* aBuilder, @@ -5375,15 +5437,13 @@ CanvasRenderingContext2D::GetCanvasLayer(nsDisplayListBuilder* aBuilder, // we have nothing to paint and there is no need to create a surface just // to paint nothing. Also, EnsureTarget() can cause creation of a persistent // layer manager which must NOT happen during a paint. - if (!mTarget || !IsTargetValid()) { + if ((!mBufferProvider && !mTarget) || !IsTargetValid()) { // No DidTransactionCallback will be received, so mark the context clean // now so future invalidations will be dispatched. MarkContextClean(); return nullptr; } - mTarget->Flush(); - if (!mResetLayer && aOldLayer) { CanvasRenderingContext2DUserData* userData = static_cast( @@ -5399,7 +5459,8 @@ CanvasRenderingContext2D::GetCanvasLayer(nsDisplayListBuilder* aBuilder, data.mGLContext = glue->GetGLContext(); data.mFrontbufferGLTex = skiaGLTex; } else { - data.mDrawTarget = mTarget; + PersistentBufferProvider *provider = GetBufferProvider(aManager); + data.mBufferProvider = provider; } if (userData && userData->IsForContext(this) && aOldLayer->IsDataValid(data)) { @@ -5437,18 +5498,19 @@ CanvasRenderingContext2D::GetCanvasLayer(nsDisplayListBuilder* aBuilder, data.mSize = nsIntSize(mWidth, mHeight); data.mHasAlpha = !mOpaque; + canvasLayer->SetPreTransactionCallback( + CanvasRenderingContext2DUserData::PreTransactionCallback, userData); + GLuint skiaGLTex = SkiaGLTex(); if (skiaGLTex) { - canvasLayer->SetPreTransactionCallback( - CanvasRenderingContext2DUserData::PreTransactionCallback, userData); - SkiaGLGlue* glue = gfxPlatform::GetPlatform()->GetSkiaGLGlue(); MOZ_ASSERT(glue); data.mGLContext = glue->GetGLContext(); data.mFrontbufferGLTex = skiaGLTex; } else { - data.mDrawTarget = mTarget; + PersistentBufferProvider *provider = GetBufferProvider(aManager); + data.mBufferProvider = provider; } canvasLayer->Initialize(data); diff --git a/dom/canvas/CanvasRenderingContext2D.h b/dom/canvas/CanvasRenderingContext2D.h index 136addccf0d0..8ebc7417c89b 100644 --- a/dom/canvas/CanvasRenderingContext2D.h +++ b/dom/canvas/CanvasRenderingContext2D.h @@ -27,6 +27,7 @@ #include "mozilla/EnumeratedArray.h" #include "FilterSupport.h" #include "nsSVGEffects.h" +#include "Layers.h" class nsGlobalWindow; class nsXULElement; @@ -456,6 +457,7 @@ public: NS_IMETHOD SetIsOpaque(bool isOpaque) override; bool GetIsOpaque() override { return mOpaque; } NS_IMETHOD Reset() override; + mozilla::layers::PersistentBufferProvider* GetBufferProvider(mozilla::layers::LayerManager* aManager); already_AddRefed GetCanvasLayer(nsDisplayListBuilder* aBuilder, CanvasLayer *aOldLayer, LayerManager *aManager) override; @@ -637,11 +639,17 @@ protected: */ void ClearTarget(); + /* + * Returns the target to the buffer provider. i.e. this will queue a frame for + * rendering. + */ + void ReturnTarget(); + /** * Check if the target is valid after calling EnsureTarget. */ bool IsTargetValid() const { - return mTarget != sErrorTarget && mTarget != nullptr; + return (sErrorTarget == nullptr || mTarget != sErrorTarget) && (mBufferProvider != nullptr || mTarget); } /** @@ -715,6 +723,8 @@ protected: // sErrorTarget. mozilla::RefPtr mTarget; + mozilla::RefPtr mBufferProvider; + uint32_t SkiaGLTex() const; // This observes our draw calls at the beginning of the canvas diff --git a/gfx/layers/PersistentBufferProvider.h b/gfx/layers/PersistentBufferProvider.h index d8a36d855f4c..2eb7dde9dee6 100644 --- a/gfx/layers/PersistentBufferProvider.h +++ b/gfx/layers/PersistentBufferProvider.h @@ -48,10 +48,8 @@ public: */ virtual bool ReturnAndUseDT(gfx::DrawTarget* aDT) = 0; -protected: - friend class CopyableCanvasLayer; - virtual TemporaryRef GetSnapshot() = 0; +protected: }; class PersistentBufferProviderBasic : public PersistentBufferProvider