From 694ce1cf8d80cacf84f538d2851f0d9a07ccc63b Mon Sep 17 00:00:00 2001 From: kearwood Date: Mon, 4 Jul 2016 13:11:07 -0700 Subject: [PATCH] Bug 1284324 - Implement Canvas Layer mirrors r=bas - When a canvas layer is set to mirror, it copies the texture from the canvas rather than changing the texture factory with Morph(). - This is useful when a canvas content will be sent to multiple devices simultaneously, such as a VR HMD and a 2d monitor mirror. - This is used by the WebVR 1.0 API, in Bug 1250244 MozReview-Commit-ID: JfMSockO2uz --HG-- extra : rebase_source : ba9633f7cb9a622efb8389d834b4232205c72755 --- dom/canvas/CanvasRenderingContext2D.cpp | 8 +++++++- dom/canvas/CanvasRenderingContext2D.h | 3 ++- dom/canvas/ImageBitmapRenderingContext.cpp | 8 +++++++- dom/canvas/ImageBitmapRenderingContext.h | 3 ++- dom/canvas/WebGLContext.cpp | 11 +++++++---- dom/canvas/WebGLContext.h | 3 ++- dom/canvas/nsICanvasRenderingContextInternal.h | 3 ++- gfx/layers/CopyableCanvasLayer.cpp | 2 ++ gfx/layers/CopyableCanvasLayer.h | 1 + gfx/layers/Layers.h | 5 +++++ gfx/layers/client/CanvasClient.cpp | 5 +++++ gfx/layers/client/ClientCanvasLayer.cpp | 2 +- 12 files changed, 43 insertions(+), 11 deletions(-) diff --git a/dom/canvas/CanvasRenderingContext2D.cpp b/dom/canvas/CanvasRenderingContext2D.cpp index bbe0024d94f6..c56d7675803d 100644 --- a/dom/canvas/CanvasRenderingContext2D.cpp +++ b/dom/canvas/CanvasRenderingContext2D.cpp @@ -5811,8 +5811,14 @@ CanvasRenderingContext2D::GetBufferProvider(LayerManager* aManager) already_AddRefed CanvasRenderingContext2D::GetCanvasLayer(nsDisplayListBuilder* aBuilder, Layer *aOldLayer, - LayerManager *aManager) + LayerManager *aManager, + bool aMirror /* = false */) { + if (aMirror) { + // Not supported for CanvasRenderingContext2D + return nullptr; + } + if (mOpaque || mIsSkiaGL) { // If we're opaque then make sure we have a surface so we paint black // instead of transparent. diff --git a/dom/canvas/CanvasRenderingContext2D.h b/dom/canvas/CanvasRenderingContext2D.h index cc4ad773b1b4..11ac93037ce5 100644 --- a/dom/canvas/CanvasRenderingContext2D.h +++ b/dom/canvas/CanvasRenderingContext2D.h @@ -466,7 +466,8 @@ public: mozilla::layers::PersistentBufferProvider* GetBufferProvider(mozilla::layers::LayerManager* aManager); already_AddRefed GetCanvasLayer(nsDisplayListBuilder* aBuilder, Layer* aOldLayer, - LayerManager* aManager) override; + LayerManager* aManager, + bool aMirror = false) override; virtual bool ShouldForceInactiveLayer(LayerManager* aManager) override; void MarkContextClean() override; void MarkContextCleanForFrameCapture() override; diff --git a/dom/canvas/ImageBitmapRenderingContext.cpp b/dom/canvas/ImageBitmapRenderingContext.cpp index c2c6a0b514e1..25f639e5078b 100644 --- a/dom/canvas/ImageBitmapRenderingContext.cpp +++ b/dom/canvas/ImageBitmapRenderingContext.cpp @@ -214,8 +214,14 @@ ImageBitmapRenderingContext::Reset() already_AddRefed ImageBitmapRenderingContext::GetCanvasLayer(nsDisplayListBuilder* aBuilder, Layer* aOldLayer, - LayerManager* aManager) + LayerManager* aManager, + bool aMirror /* = false */) { + if (aMirror) { + // Not supported for ImageBitmapRenderingContext + return nullptr; + } + if (!mImage) { // No DidTransactionCallback will be received, so mark the context clean // now so future invalidations will be dispatched. diff --git a/dom/canvas/ImageBitmapRenderingContext.h b/dom/canvas/ImageBitmapRenderingContext.h index 56874db8dadb..b6cad4fa9795 100644 --- a/dom/canvas/ImageBitmapRenderingContext.h +++ b/dom/canvas/ImageBitmapRenderingContext.h @@ -70,7 +70,8 @@ public: NS_IMETHOD Reset() override; virtual already_AddRefed GetCanvasLayer(nsDisplayListBuilder* aBuilder, Layer* aOldLayer, - LayerManager* aManager) override; + LayerManager* aManager, + bool aMirror = false) override; virtual void MarkContextClean() override; NS_IMETHOD Redraw(const gfxRect& aDirty) override; diff --git a/dom/canvas/WebGLContext.cpp b/dom/canvas/WebGLContext.cpp index e4b2d6fd81ff..419a5ed4ef4e 100644 --- a/dom/canvas/WebGLContext.cpp +++ b/dom/canvas/WebGLContext.cpp @@ -1265,6 +1265,7 @@ WebGLContext::UpdateLastUseIndex() } static uint8_t gWebGLLayerUserData; +static uint8_t gWebGLMirrorLayerUserData; class WebGLContextUserData : public LayerUserData { @@ -1307,13 +1308,14 @@ private: already_AddRefed WebGLContext::GetCanvasLayer(nsDisplayListBuilder* builder, Layer* oldLayer, - LayerManager* manager) + LayerManager* manager, + bool aMirror /*= false*/) { if (IsContextLost()) return nullptr; if (!mResetLayer && oldLayer && - oldLayer->HasUserData(&gWebGLLayerUserData)) { + oldLayer->HasUserData(aMirror ? &gWebGLMirrorLayerUserData : &gWebGLLayerUserData)) { RefPtr ret = oldLayer; return ret.forget(); } @@ -1325,7 +1327,7 @@ WebGLContext::GetCanvasLayer(nsDisplayListBuilder* builder, } WebGLContextUserData* userData = nullptr; - if (builder->IsPaintingToWindow() && mCanvasElement) { + if (builder->IsPaintingToWindow() && mCanvasElement && !aMirror) { // Make the layer tell us whenever a transaction finishes (including // the current transaction), so we can clear our invalidation state and // start invalidating again. We need to do this for the layer that is @@ -1345,13 +1347,14 @@ WebGLContext::GetCanvasLayer(nsDisplayListBuilder* builder, WebGLContextUserData::PreTransactionCallback, userData); } - canvasLayer->SetUserData(&gWebGLLayerUserData, userData); + canvasLayer->SetUserData(aMirror ? &gWebGLMirrorLayerUserData : &gWebGLLayerUserData, userData); CanvasLayer::Data data; data.mGLContext = gl; data.mSize = nsIntSize(mWidth, mHeight); data.mHasAlpha = gl->Caps().alpha; data.mIsGLAlphaPremult = IsPremultAlpha() || !data.mHasAlpha; + data.mIsMirror = aMirror; canvasLayer->Initialize(data); uint32_t flags = gl->Caps().alpha ? 0 : Layer::CONTENT_OPAQUE; diff --git a/dom/canvas/WebGLContext.h b/dom/canvas/WebGLContext.h index 2322852be13b..d1f53ce620d9 100644 --- a/dom/canvas/WebGLContext.h +++ b/dom/canvas/WebGLContext.h @@ -333,7 +333,8 @@ public: already_AddRefed GetCanvasLayer(nsDisplayListBuilder* builder, Layer* oldLayer, - LayerManager* manager) override; + LayerManager* manager, + bool aMirror = false) override; // Note that 'clean' here refers to its invalidation state, not the // contents of the buffer. diff --git a/dom/canvas/nsICanvasRenderingContextInternal.h b/dom/canvas/nsICanvasRenderingContextInternal.h index f5eac32b5488..9ef0a3a61fe0 100644 --- a/dom/canvas/nsICanvasRenderingContextInternal.h +++ b/dom/canvas/nsICanvasRenderingContextInternal.h @@ -135,7 +135,8 @@ public: // one for the given layer manager if not available. virtual already_AddRefed GetCanvasLayer(nsDisplayListBuilder* builder, Layer *oldLayer, - LayerManager *manager) = 0; + LayerManager *manager, + bool aMirror = false) = 0; // Return true if the canvas should be forced to be "inactive" to ensure // it can be drawn to the screen even if it's too large to be blitted by diff --git a/gfx/layers/CopyableCanvasLayer.cpp b/gfx/layers/CopyableCanvasLayer.cpp index 491b5e3bbc6f..69ca999c3b34 100644 --- a/gfx/layers/CopyableCanvasLayer.cpp +++ b/gfx/layers/CopyableCanvasLayer.cpp @@ -37,6 +37,7 @@ CopyableCanvasLayer::CopyableCanvasLayer(LayerManager* aLayerManager, void *aImp , mGLFrontbuffer(nullptr) , mIsAlphaPremultiplied(true) , mOriginPos(gl::OriginPos::TopLeft) + , mIsMirror(false) { MOZ_COUNT_CTOR(CopyableCanvasLayer); } @@ -55,6 +56,7 @@ CopyableCanvasLayer::Initialize(const Data& aData) mGLContext = aData.mGLContext; mIsAlphaPremultiplied = aData.mIsGLAlphaPremult; mOriginPos = gl::OriginPos::BottomLeft; + mIsMirror = aData.mIsMirror; MOZ_ASSERT(mGLContext->IsOffscreen(), "canvas gl context isn't offscreen"); diff --git a/gfx/layers/CopyableCanvasLayer.h b/gfx/layers/CopyableCanvasLayer.h index 5ed3c4f43815..6cefa8150d5e 100644 --- a/gfx/layers/CopyableCanvasLayer.h +++ b/gfx/layers/CopyableCanvasLayer.h @@ -58,6 +58,7 @@ protected: bool mIsAlphaPremultiplied; gl::OriginPos mOriginPos; + bool mIsMirror; RefPtr mCachedTempSurface; diff --git a/gfx/layers/Layers.h b/gfx/layers/Layers.h index c20a09956d8b..6f91017c53ca 100644 --- a/gfx/layers/Layers.h +++ b/gfx/layers/Layers.h @@ -2375,6 +2375,7 @@ public: , mSize(0,0) , mHasAlpha(false) , mIsGLAlphaPremult(true) + , mIsMirror(false) { } // One of these three must be specified for Canvas2D, but never more than one @@ -2393,6 +2394,10 @@ public: // Whether mGLContext contains data that is alpha-premultiplied. bool mIsGLAlphaPremult; + + // Whether the canvas front buffer is already being rendered somewhere else. + // When true, do not swap buffers or Morph() to another factory on mGLContext + bool mIsMirror; }; /** diff --git a/gfx/layers/client/CanvasClient.cpp b/gfx/layers/client/CanvasClient.cpp index b8a0f2006a07..2c167ffe27c0 100644 --- a/gfx/layers/client/CanvasClient.cpp +++ b/gfx/layers/client/CanvasClient.cpp @@ -411,6 +411,11 @@ CanvasClientSharedSurface::UpdateRenderer(gfx::IntSize aSize, Renderer& aRendere gfxCriticalError() << "Invalid canvas front buffer"; return; } + } else if (layer && layer->mIsMirror) { + mShSurfClient = CloneSurface(gl->Screen()->Front()->Surf(), layer->mFactory.get()); + if (!mShSurfClient) { + return; + } } else { mShSurfClient = gl->Screen()->Front(); if (mShSurfClient && mShSurfClient->GetAllocator() && diff --git a/gfx/layers/client/ClientCanvasLayer.cpp b/gfx/layers/client/ClientCanvasLayer.cpp index a2c4197bfd77..6ded430dbee9 100644 --- a/gfx/layers/client/ClientCanvasLayer.cpp +++ b/gfx/layers/client/ClientCanvasLayer.cpp @@ -67,7 +67,7 @@ ClientCanvasLayer::Initialize(const Data& aData) UniquePtr factory = GLScreenBuffer::CreateFactory(mGLContext, caps, forwarder, mFlags); - if (mGLFrontbuffer) { + if (mGLFrontbuffer || aData.mIsMirror) { // We're using a source other than the one in the default screen. // (SkiaGL) mFactory = Move(factory);