From 906120d7755ad6167460b446c55a9adf12a79b02 Mon Sep 17 00:00:00 2001 From: vincentliu Date: Fri, 4 Mar 2016 15:22:39 +0800 Subject: [PATCH] Bug 801176 - part2-v5: Let Canvas 2d context works on Workers. r=roc --- dom/canvas/CanvasRenderingContext2D.cpp | 53 ++++++++++++----------- dom/canvas/OffscreenCanvas.cpp | 46 +++++++++++--------- dom/html/HTMLCanvasElement.cpp | 1 + gfx/layers/AsyncCanvasRenderer.cpp | 17 ++++++++ gfx/layers/AsyncCanvasRenderer.h | 16 +++++++ gfx/layers/Effects.cpp | 6 +++ gfx/layers/GLImages.cpp | 5 +-- gfx/layers/client/CanvasClient.cpp | 74 +++++++++++++++++++++++++-------- gfx/layers/client/CanvasClient.h | 10 ++++- 9 files changed, 162 insertions(+), 66 deletions(-) --- dom/canvas/CanvasRenderingContext2D.cpp | 53 +++++++++--------- dom/canvas/OffscreenCanvas.cpp | 46 ++++++++------- dom/html/HTMLCanvasElement.cpp | 1 + gfx/layers/AsyncCanvasRenderer.cpp | 17 ++++++ gfx/layers/AsyncCanvasRenderer.h | 16 ++++++ gfx/layers/Effects.cpp | 6 ++ gfx/layers/GLImages.cpp | 5 +- gfx/layers/client/CanvasClient.cpp | 74 +++++++++++++++++++------ gfx/layers/client/CanvasClient.h | 10 +++- 9 files changed, 162 insertions(+), 66 deletions(-) diff --git a/dom/canvas/CanvasRenderingContext2D.cpp b/dom/canvas/CanvasRenderingContext2D.cpp index ed22b2b4d213..9ce7f0f57ef6 100644 --- a/dom/canvas/CanvasRenderingContext2D.cpp +++ b/dom/canvas/CanvasRenderingContext2D.cpp @@ -975,7 +975,8 @@ CanvasRenderingContext2D::CanvasRenderingContext2D() sNumLivingContexts++; // The default is to use OpenGL mode - if (gfxPlatform::GetPlatform()->UseAcceleratedCanvas()) { + if (NS_IsMainThread() && + gfxPlatform::GetPlatform()->UseAcceleratedCanvas()) { mDrawObserver = new CanvasDrawObserver(this); } else { mRenderingMode = RenderingMode::SoftwareBackendMode; @@ -1418,32 +1419,36 @@ CanvasRenderingContext2D::EnsureTarget(RenderingMode aRenderingMode) nsContentUtils::PersistentLayerManagerForDocument(ownerDoc); } - if (layerManager) { - if (mode == RenderingMode::OpenGLBackendMode && - gfxPlatform::GetPlatform()->UseAcceleratedCanvas() && - CheckSizeForSkiaGL(size)) { - DemoteOldestContextIfNecessary(); - mBufferProvider = nullptr; + if (mode == RenderingMode::OpenGLBackendMode && + gfxPlatform::GetPlatform()->UseAcceleratedCanvas() && + CheckSizeForSkiaGL(size)) { + DemoteOldestContextIfNecessary(); + mBufferProvider = nullptr; #if USE_SKIA_GPU - SkiaGLGlue* glue = gfxPlatform::GetPlatform()->GetSkiaGLGlue(); + SkiaGLGlue* glue = gfxPlatform::GetPlatform()->GetSkiaGLGlue(); - if (glue && glue->GetGrContext() && glue->GetGLContext()) { - mTarget = Factory::CreateDrawTargetSkiaWithGrContext(glue->GetGrContext(), size, format); - if (mTarget) { - AddDemotableContext(this); + if (glue && glue->GetGrContext() && glue->GetGLContext()) { + mTarget = Factory::CreateDrawTargetSkiaWithGrContext(glue->GetGrContext(), size, format); + if (mTarget) { + AddDemotableContext(this); + if (NS_IsMainThread()) { mBufferProvider = new PersistentBufferProviderBasic(mTarget); - mIsSkiaGL = true; - } else { - gfxCriticalNote << "Failed to create a SkiaGL DrawTarget, falling back to software\n"; - mode = RenderingMode::SoftwareBackendMode; } + mIsSkiaGL = true; + } else { + gfxCriticalNote << "Failed to create a SkiaGL DrawTarget, falling back to software\n"; + mode = RenderingMode::SoftwareBackendMode; } -#endif } +#endif + } - if (!mBufferProvider) { + if (!mBufferProvider) { + if (layerManager) { mBufferProvider = layerManager->CreatePersistentBufferProvider(size, format); + } else { + mBufferProvider = new PersistentBufferProviderBasic(size, format, gfxPlatform::GetPlatform()->GetPreferredCanvasBackend()); } } @@ -5646,17 +5651,15 @@ void CanvasRenderingContext2D::RemoveDrawObserver() PersistentBufferProvider* CanvasRenderingContext2D::GetBufferProvider(LayerManager* aManager) { + if (!mTarget) { + EnsureTarget(); + } + if (mBufferProvider) { return mBufferProvider; - } - - if (!mTarget) { + } else { return nullptr; } - - mBufferProvider = new PersistentBufferProviderBasic(mTarget); - - return mBufferProvider; } already_AddRefed diff --git a/dom/canvas/OffscreenCanvas.cpp b/dom/canvas/OffscreenCanvas.cpp index 857da02aeacc..dff3b7f51b14 100644 --- a/dom/canvas/OffscreenCanvas.cpp +++ b/dom/canvas/OffscreenCanvas.cpp @@ -113,7 +113,6 @@ OffscreenCanvas::GetContext(JSContext* aCx, return nullptr; } - // We only support WebGL in workers for now CanvasContextType contextType; if (!CanvasUtils::GetCanvasContextType(aContextId, &contextType)) { aRv.Throw(NS_ERROR_NOT_IMPLEMENTED); @@ -122,6 +121,7 @@ OffscreenCanvas::GetContext(JSContext* aCx, if (!(contextType == CanvasContextType::WebGL1 || contextType == CanvasContextType::WebGL2 || + contextType == CanvasContextType::Canvas2D || contextType == CanvasContextType::ImageBitmap)) { aRv.Throw(NS_ERROR_NOT_IMPLEMENTED); @@ -138,33 +138,39 @@ OffscreenCanvas::GetContext(JSContext* aCx, return nullptr; } - if (mCanvasRenderer) { + if (mCanvasRenderer && ImageBridgeChild::IsCreated()) { + mCanvasRenderer->mContext = mCurrentContext; + TextureFlags flags = TextureFlags::ORIGIN_BOTTOM_LEFT; + if (contextType == CanvasContextType::WebGL1 || contextType == CanvasContextType::WebGL2) { WebGLContext* webGL = static_cast(mCurrentContext.get()); gl::GLContext* gl = webGL->GL(); - mCanvasRenderer->mContext = mCurrentContext; mCanvasRenderer->SetActiveThread(); mCanvasRenderer->mGLContext = gl; mCanvasRenderer->SetIsAlphaPremultiplied(webGL->IsPremultAlpha() || !gl->Caps().alpha); - if (ImageBridgeChild::IsCreated()) { - TextureFlags flags = TextureFlags::ORIGIN_BOTTOM_LEFT; - mCanvasClient = ImageBridgeChild::GetSingleton()-> - CreateCanvasClient(CanvasClient::CanvasClientTypeShSurf, flags).take(); - mCanvasRenderer->SetCanvasClient(mCanvasClient); + mCanvasClient = ImageBridgeChild::GetSingleton()-> + CreateCanvasClient(CanvasClient::CanvasClientTypeShSurf, flags).take(); - gl::GLScreenBuffer* screen = gl->Screen(); - gl::SurfaceCaps caps = screen->mCaps; - auto forwarder = mCanvasClient->GetForwarder(); + gl::GLScreenBuffer* screen = gl->Screen(); + gl::SurfaceCaps caps = screen->mCaps; + auto forwarder = mCanvasClient->GetForwarder(); - UniquePtr factory = - gl::GLScreenBuffer::CreateFactory(gl, caps, forwarder, flags); + UniquePtr factory = + gl::GLScreenBuffer::CreateFactory(gl, caps, forwarder, flags); - if (factory) - screen->Morph(Move(factory)); - } + if (factory) + screen->Morph(Move(factory)); + } else if (contextType == CanvasContextType::Canvas2D) { + CanvasRenderingContext2D* context2D = static_cast(mCurrentContext.get()); + mCanvasRenderer->mGLContext = nullptr; + mCanvasRenderer->mBufferProvider = context2D->GetBufferProvider(nullptr); + + mCanvasClient = ImageBridgeChild::GetSingleton()-> + CreateCanvasClient(CanvasClient::CanvasClientSurface, flags).take(); } + mCanvasRenderer->SetCanvasClient(mCanvasClient); } return result; @@ -200,11 +206,11 @@ OffscreenCanvas::CommitFrameToCompositor() mAttrDirty = false; } - if (mCurrentContext) { - static_cast(mCurrentContext.get())->PresentScreenBuffer(); - } + if (mCurrentContext && mCanvasRenderer) { + if (mCanvasRenderer->mGLContext) { + static_cast(mCurrentContext.get())->PresentScreenBuffer(); + } - if (mCanvasRenderer && mCanvasRenderer->mGLContext) { mCanvasRenderer->NotifyElementAboutInvalidation(); ImageBridgeChild::GetSingleton()-> UpdateAsyncCanvasRenderer(mCanvasRenderer); diff --git a/dom/html/HTMLCanvasElement.cpp b/dom/html/HTMLCanvasElement.cpp index b3c7d6a91361..299098605b97 100644 --- a/dom/html/HTMLCanvasElement.cpp +++ b/dom/html/HTMLCanvasElement.cpp @@ -785,6 +785,7 @@ HTMLCanvasElement::TransferControlToOffscreen(ErrorResult& aRv) RefPtr renderer = GetAsyncCanvasRenderer(); renderer->SetWidth(sz.width); renderer->SetHeight(sz.height); + renderer->SetOpaque(GetIsOpaque()); nsCOMPtr global = do_QueryInterface(OwnerDoc()->GetInnerWindow()); diff --git a/gfx/layers/AsyncCanvasRenderer.cpp b/gfx/layers/AsyncCanvasRenderer.cpp index b25d60c15a30..36e745f3cf00 100644 --- a/gfx/layers/AsyncCanvasRenderer.cpp +++ b/gfx/layers/AsyncCanvasRenderer.cpp @@ -18,6 +18,7 @@ #include "mozilla/ReentrantMonitor.h" #include "nsIRunnable.h" #include "nsThreadUtils.h" +#include "PersistentBufferProvider.h" namespace mozilla { namespace layers { @@ -234,6 +235,22 @@ AsyncCanvasRenderer::UpdateTarget() return surface.forget(); } +void +AsyncCanvasRenderer::UpdateTarget(TextureClient* aTexture) +{ + if (!mBufferProvider) { + return; + } + + RefPtr surface = mBufferProvider->GetSnapshot(); + + if (surface) { + NS_ASSERTION(surface, "Must have surface to draw!"); + aTexture->UpdateFromSurface(surface); + surface = nullptr; + } +} + already_AddRefed AsyncCanvasRenderer::GetSurface() { diff --git a/gfx/layers/AsyncCanvasRenderer.h b/gfx/layers/AsyncCanvasRenderer.h index 6a84b0703528..24e14b7d0434 100644 --- a/gfx/layers/AsyncCanvasRenderer.h +++ b/gfx/layers/AsyncCanvasRenderer.h @@ -21,6 +21,7 @@ namespace mozilla { namespace gfx { class DataSourceSurface; +class DrawTarget; } namespace gl { @@ -34,6 +35,7 @@ class HTMLCanvasElement; namespace layers { class CanvasClient; +class PersistentBufferProvider; class TextureClient; /** @@ -74,11 +76,23 @@ public: mHeight = aHeight; } + bool GetOpaque() + { + return mOpaque; + } + + void SetOpaque(bool aOpaque) + { + mOpaque = aOpaque; + } + void SetIsAlphaPremultiplied(bool aIsAlphaPremultiplied) { mIsAlphaPremultiplied = aIsAlphaPremultiplied; } + void UpdateTarget(TextureClient* aTexture); + // Active thread means the thread which spawns GLContext. void SetActiveThread(); void ResetActiveThread(); @@ -130,6 +144,7 @@ public: // canvas' surface texture destructor will deref and destroy it too early // Only accessed in active thread. RefPtr mGLContext; + PersistentBufferProvider* mBufferProvider; private: virtual ~AsyncCanvasRenderer(); @@ -138,6 +153,7 @@ private: already_AddRefed UpdateTarget(); bool mIsAlphaPremultiplied; + bool mOpaque; uint32_t mWidth; uint32_t mHeight; diff --git a/gfx/layers/Effects.cpp b/gfx/layers/Effects.cpp index cf768afe08f1..923a45acb677 100644 --- a/gfx/layers/Effects.cpp +++ b/gfx/layers/Effects.cpp @@ -9,6 +9,9 @@ #include "nsPrintfCString.h" // for nsPrintfCString #include "nsString.h" // for nsAutoCString +namespace mozilla { +namespace layers { + using namespace mozilla::layers; void @@ -76,3 +79,6 @@ EffectVRDistortion::PrintInfo(std::stringstream& aStream, const char* aPrefix) aStream << nsPrintfCString("EffectVRDistortion (0x%p) [hmd=%p] [render-target=%p] [texture=%p]", this, mHMD.get(), mRenderTarget.get(), mTexture).get(); } + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/GLImages.cpp b/gfx/layers/GLImages.cpp index 755a6804217b..a7ca8d294eb2 100644 --- a/gfx/layers/GLImages.cpp +++ b/gfx/layers/GLImages.cpp @@ -8,12 +8,11 @@ #include "GLReadTexImageHelper.h" #include "GLLibraryEGL.h" -using namespace mozilla; -using namespace mozilla::gl; - namespace mozilla { namespace layers { +using namespace mozilla; +using namespace mozilla::gl; static RefPtr sSnapshotContext; EGLImageImage::EGLImageImage(EGLImage aImage, EGLSync aSync, diff --git a/gfx/layers/client/CanvasClient.cpp b/gfx/layers/client/CanvasClient.cpp index a11c8c35f4ab..86c0601c2326 100644 --- a/gfx/layers/client/CanvasClient.cpp +++ b/gfx/layers/client/CanvasClient.cpp @@ -26,6 +26,7 @@ #include "nsXULAppAPI.h" // for XRE_GetProcessType, etc #include "TextureClientSharedSurface.h" #include "VRManagerChild.h" +#include "gfxUtils.h" using namespace mozilla::gfx; using namespace mozilla::gl; @@ -69,38 +70,69 @@ CanvasClientBridge::UpdateAsync(AsyncCanvasRenderer* aRenderer) void CanvasClient2D::Update(gfx::IntSize aSize, ClientCanvasLayer* aLayer) +{ + Renderer renderer; + renderer.construct(aLayer); + UpdateRenderer(aSize, renderer); +} + +void +CanvasClient2D::UpdateAsync(AsyncCanvasRenderer* aRenderer) +{ + Renderer renderer; + renderer.construct(aRenderer); + UpdateRenderer(aRenderer->GetSize(), renderer); +} + +void +CanvasClient2D::UpdateRenderer(gfx::IntSize aSize, Renderer& aRenderer) { AutoRemoveTexture autoRemove(this); + ClientCanvasLayer* layer = nullptr; + AsyncCanvasRenderer* asyncRenderer = nullptr; + if (aRenderer.constructed()) { + layer = aRenderer.ref(); + } else { + asyncRenderer = aRenderer.ref(); + } + if (mBuffer && (mBuffer->IsImmutable() || mBuffer->GetSize() != aSize)) { autoRemove.mTexture = mBuffer; mBuffer = nullptr; } - bool bufferCreated = false; + mBufferCreated = false; if (!mBuffer) { - bool isOpaque = (aLayer->GetContentFlags() & Layer::CONTENT_OPAQUE); - gfxContentType contentType = isOpaque - ? gfxContentType::COLOR - : gfxContentType::COLOR_ALPHA; - gfx::SurfaceFormat surfaceFormat - = gfxPlatform::GetPlatform()->Optimal2DFormatForContent(contentType); + bool isOpaque; + gfxContentType contentType; + if (layer) { + isOpaque = (layer->GetContentFlags() & Layer::CONTENT_OPAQUE); + } else { + isOpaque = (asyncRenderer->GetOpaque() & Layer::CONTENT_OPAQUE); + } + contentType = isOpaque + ? gfxContentType::COLOR + : gfxContentType::COLOR_ALPHA; + gfx::SurfaceFormat surfaceFormat = + gfxPlatform::GetPlatform()->Optimal2DFormatForContent(contentType); TextureFlags flags = TextureFlags::DEFAULT; if (mTextureFlags & TextureFlags::ORIGIN_BOTTOM_LEFT) { flags |= TextureFlags::ORIGIN_BOTTOM_LEFT; } - mBuffer = CreateTextureClientForCanvas(surfaceFormat, aSize, flags, aLayer); + flags = TextureFlags::NO_FLAGS; + mBuffer = CreateTextureClientForCanvas(surfaceFormat, aSize, flags, layer); if (!mBuffer) { NS_WARNING("Failed to allocate the TextureClient"); return; } MOZ_ASSERT(mBuffer->CanExposeDrawTarget()); - bufferCreated = true; + mBufferCreated = true; } - bool updated = false; + mUpdated = false; { TextureClientAutoLock autoLock(mBuffer, OpenMode::OPEN_WRITE_ONLY); if (!autoLock.Succeeded()) { @@ -108,19 +140,27 @@ CanvasClient2D::Update(gfx::IntSize aSize, ClientCanvasLayer* aLayer) return; } - RefPtr target = mBuffer->BorrowDrawTarget(); - if (target) { - aLayer->UpdateTarget(target); - updated = true; + if (layer) { + RefPtr target = mBuffer->BorrowDrawTarget(); + if (target) { + layer->UpdateTarget(target); + } + } else { + asyncRenderer->UpdateTarget(mBuffer); } + mUpdated = true; } +} - if (bufferCreated && !AddTextureClient(mBuffer)) { +void +CanvasClient2D::Updated() +{ + if (mBufferCreated && !AddTextureClient(mBuffer)) { mBuffer = nullptr; return; } - if (updated) { + if (mUpdated) { AutoTArray textures; CompositableForwarder::TimedTextureClient* t = textures.AppendElement(); t->mTextureClient = mBuffer; @@ -138,7 +178,7 @@ CanvasClient2D::CreateTextureClientForCanvas(gfx::SurfaceFormat aFormat, TextureFlags aFlags, ClientCanvasLayer* aLayer) { - if (aLayer->IsGLLayer()) { + if (aLayer && aLayer->IsGLLayer()) { // We want a cairo backend here as we don't want to be copying into // an accelerated backend and we like LockBits to work. This is currently // the most effective way to make this work. diff --git a/gfx/layers/client/CanvasClient.h b/gfx/layers/client/CanvasClient.h index 7a8a17485156..bd48998dc46d 100644 --- a/gfx/layers/client/CanvasClient.h +++ b/gfx/layers/client/CanvasClient.h @@ -88,7 +88,9 @@ class CanvasClient2D : public CanvasClient public: CanvasClient2D(CompositableForwarder* aLayerForwarder, TextureFlags aFlags) - : CanvasClient(aLayerForwarder, aFlags) + : CanvasClient(aLayerForwarder, aFlags), + mBufferCreated(false), + mUpdated(false) { } @@ -103,6 +105,8 @@ public: } virtual void Update(gfx::IntSize aSize, ClientCanvasLayer* aLayer) override; + virtual void UpdateAsync(AsyncCanvasRenderer* aRenderer) override; + void UpdateRenderer(gfx::IntSize aSize, Renderer& aRenderer); virtual bool AddTextureClient(TextureClient* aTexture) override { @@ -110,6 +114,8 @@ public: return CanvasClient::AddTextureClient(aTexture); } + virtual void Updated() override; + virtual void OnDetach() override { mBuffer = nullptr; @@ -123,6 +129,8 @@ private: ClientCanvasLayer* aLayer); RefPtr mBuffer; + bool mBufferCreated; + bool mUpdated; }; // Used for GL canvases where we don't need to do any readback, i.e., with a