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
This commit is contained in:
kearwood
2016-07-04 13:11:07 -07:00
parent 5117963506
commit b5b6726ccc
12 changed files with 43 additions and 11 deletions

View File

@@ -5811,8 +5811,14 @@ CanvasRenderingContext2D::GetBufferProvider(LayerManager* aManager)
already_AddRefed<Layer> already_AddRefed<Layer>
CanvasRenderingContext2D::GetCanvasLayer(nsDisplayListBuilder* aBuilder, CanvasRenderingContext2D::GetCanvasLayer(nsDisplayListBuilder* aBuilder,
Layer *aOldLayer, Layer *aOldLayer,
LayerManager *aManager) LayerManager *aManager,
bool aMirror /* = false */)
{ {
if (aMirror) {
// Not supported for CanvasRenderingContext2D
return nullptr;
}
if (mOpaque || mIsSkiaGL) { if (mOpaque || mIsSkiaGL) {
// If we're opaque then make sure we have a surface so we paint black // If we're opaque then make sure we have a surface so we paint black
// instead of transparent. // instead of transparent.

View File

@@ -466,7 +466,8 @@ public:
mozilla::layers::PersistentBufferProvider* GetBufferProvider(mozilla::layers::LayerManager* aManager); mozilla::layers::PersistentBufferProvider* GetBufferProvider(mozilla::layers::LayerManager* aManager);
already_AddRefed<Layer> GetCanvasLayer(nsDisplayListBuilder* aBuilder, already_AddRefed<Layer> GetCanvasLayer(nsDisplayListBuilder* aBuilder,
Layer* aOldLayer, Layer* aOldLayer,
LayerManager* aManager) override; LayerManager* aManager,
bool aMirror = false) override;
virtual bool ShouldForceInactiveLayer(LayerManager* aManager) override; virtual bool ShouldForceInactiveLayer(LayerManager* aManager) override;
void MarkContextClean() override; void MarkContextClean() override;
void MarkContextCleanForFrameCapture() override; void MarkContextCleanForFrameCapture() override;

View File

@@ -214,8 +214,14 @@ ImageBitmapRenderingContext::Reset()
already_AddRefed<Layer> already_AddRefed<Layer>
ImageBitmapRenderingContext::GetCanvasLayer(nsDisplayListBuilder* aBuilder, ImageBitmapRenderingContext::GetCanvasLayer(nsDisplayListBuilder* aBuilder,
Layer* aOldLayer, Layer* aOldLayer,
LayerManager* aManager) LayerManager* aManager,
bool aMirror /* = false */)
{ {
if (aMirror) {
// Not supported for ImageBitmapRenderingContext
return nullptr;
}
if (!mImage) { if (!mImage) {
// No DidTransactionCallback will be received, so mark the context clean // No DidTransactionCallback will be received, so mark the context clean
// now so future invalidations will be dispatched. // now so future invalidations will be dispatched.

View File

@@ -70,7 +70,8 @@ public:
NS_IMETHOD Reset() override; NS_IMETHOD Reset() override;
virtual already_AddRefed<Layer> GetCanvasLayer(nsDisplayListBuilder* aBuilder, virtual already_AddRefed<Layer> GetCanvasLayer(nsDisplayListBuilder* aBuilder,
Layer* aOldLayer, Layer* aOldLayer,
LayerManager* aManager) override; LayerManager* aManager,
bool aMirror = false) override;
virtual void MarkContextClean() override; virtual void MarkContextClean() override;
NS_IMETHOD Redraw(const gfxRect& aDirty) override; NS_IMETHOD Redraw(const gfxRect& aDirty) override;

View File

@@ -1265,6 +1265,7 @@ WebGLContext::UpdateLastUseIndex()
} }
static uint8_t gWebGLLayerUserData; static uint8_t gWebGLLayerUserData;
static uint8_t gWebGLMirrorLayerUserData;
class WebGLContextUserData : public LayerUserData class WebGLContextUserData : public LayerUserData
{ {
@@ -1307,13 +1308,14 @@ private:
already_AddRefed<layers::Layer> already_AddRefed<layers::Layer>
WebGLContext::GetCanvasLayer(nsDisplayListBuilder* builder, WebGLContext::GetCanvasLayer(nsDisplayListBuilder* builder,
Layer* oldLayer, Layer* oldLayer,
LayerManager* manager) LayerManager* manager,
bool aMirror /*= false*/)
{ {
if (IsContextLost()) if (IsContextLost())
return nullptr; return nullptr;
if (!mResetLayer && oldLayer && if (!mResetLayer && oldLayer &&
oldLayer->HasUserData(&gWebGLLayerUserData)) { oldLayer->HasUserData(aMirror ? &gWebGLMirrorLayerUserData : &gWebGLLayerUserData)) {
RefPtr<layers::Layer> ret = oldLayer; RefPtr<layers::Layer> ret = oldLayer;
return ret.forget(); return ret.forget();
} }
@@ -1325,7 +1327,7 @@ WebGLContext::GetCanvasLayer(nsDisplayListBuilder* builder,
} }
WebGLContextUserData* userData = nullptr; WebGLContextUserData* userData = nullptr;
if (builder->IsPaintingToWindow() && mCanvasElement) { if (builder->IsPaintingToWindow() && mCanvasElement && !aMirror) {
// Make the layer tell us whenever a transaction finishes (including // Make the layer tell us whenever a transaction finishes (including
// the current transaction), so we can clear our invalidation state and // the current transaction), so we can clear our invalidation state and
// start invalidating again. We need to do this for the layer that is // start invalidating again. We need to do this for the layer that is
@@ -1345,13 +1347,14 @@ WebGLContext::GetCanvasLayer(nsDisplayListBuilder* builder,
WebGLContextUserData::PreTransactionCallback, userData); WebGLContextUserData::PreTransactionCallback, userData);
} }
canvasLayer->SetUserData(&gWebGLLayerUserData, userData); canvasLayer->SetUserData(aMirror ? &gWebGLMirrorLayerUserData : &gWebGLLayerUserData, userData);
CanvasLayer::Data data; CanvasLayer::Data data;
data.mGLContext = gl; data.mGLContext = gl;
data.mSize = nsIntSize(mWidth, mHeight); data.mSize = nsIntSize(mWidth, mHeight);
data.mHasAlpha = gl->Caps().alpha; data.mHasAlpha = gl->Caps().alpha;
data.mIsGLAlphaPremult = IsPremultAlpha() || !data.mHasAlpha; data.mIsGLAlphaPremult = IsPremultAlpha() || !data.mHasAlpha;
data.mIsMirror = aMirror;
canvasLayer->Initialize(data); canvasLayer->Initialize(data);
uint32_t flags = gl->Caps().alpha ? 0 : Layer::CONTENT_OPAQUE; uint32_t flags = gl->Caps().alpha ? 0 : Layer::CONTENT_OPAQUE;

View File

@@ -333,7 +333,8 @@ public:
already_AddRefed<Layer> already_AddRefed<Layer>
GetCanvasLayer(nsDisplayListBuilder* builder, Layer* oldLayer, 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 // Note that 'clean' here refers to its invalidation state, not the
// contents of the buffer. // contents of the buffer.

View File

@@ -135,7 +135,8 @@ public:
// one for the given layer manager if not available. // one for the given layer manager if not available.
virtual already_AddRefed<Layer> GetCanvasLayer(nsDisplayListBuilder* builder, virtual already_AddRefed<Layer> GetCanvasLayer(nsDisplayListBuilder* builder,
Layer *oldLayer, Layer *oldLayer,
LayerManager *manager) = 0; LayerManager *manager,
bool aMirror = false) = 0;
// Return true if the canvas should be forced to be "inactive" to ensure // 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 // it can be drawn to the screen even if it's too large to be blitted by

View File

@@ -37,6 +37,7 @@ CopyableCanvasLayer::CopyableCanvasLayer(LayerManager* aLayerManager, void *aImp
, mGLFrontbuffer(nullptr) , mGLFrontbuffer(nullptr)
, mIsAlphaPremultiplied(true) , mIsAlphaPremultiplied(true)
, mOriginPos(gl::OriginPos::TopLeft) , mOriginPos(gl::OriginPos::TopLeft)
, mIsMirror(false)
{ {
MOZ_COUNT_CTOR(CopyableCanvasLayer); MOZ_COUNT_CTOR(CopyableCanvasLayer);
} }
@@ -55,6 +56,7 @@ CopyableCanvasLayer::Initialize(const Data& aData)
mGLContext = aData.mGLContext; mGLContext = aData.mGLContext;
mIsAlphaPremultiplied = aData.mIsGLAlphaPremult; mIsAlphaPremultiplied = aData.mIsGLAlphaPremult;
mOriginPos = gl::OriginPos::BottomLeft; mOriginPos = gl::OriginPos::BottomLeft;
mIsMirror = aData.mIsMirror;
MOZ_ASSERT(mGLContext->IsOffscreen(), "canvas gl context isn't offscreen"); MOZ_ASSERT(mGLContext->IsOffscreen(), "canvas gl context isn't offscreen");

View File

@@ -58,6 +58,7 @@ protected:
bool mIsAlphaPremultiplied; bool mIsAlphaPremultiplied;
gl::OriginPos mOriginPos; gl::OriginPos mOriginPos;
bool mIsMirror;
RefPtr<gfx::DataSourceSurface> mCachedTempSurface; RefPtr<gfx::DataSourceSurface> mCachedTempSurface;

View File

@@ -2375,6 +2375,7 @@ public:
, mSize(0,0) , mSize(0,0)
, mHasAlpha(false) , mHasAlpha(false)
, mIsGLAlphaPremult(true) , mIsGLAlphaPremult(true)
, mIsMirror(false)
{ } { }
// One of these three must be specified for Canvas2D, but never more than one // 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. // Whether mGLContext contains data that is alpha-premultiplied.
bool mIsGLAlphaPremult; 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;
}; };
/** /**

View File

@@ -411,6 +411,11 @@ CanvasClientSharedSurface::UpdateRenderer(gfx::IntSize aSize, Renderer& aRendere
gfxCriticalError() << "Invalid canvas front buffer"; gfxCriticalError() << "Invalid canvas front buffer";
return; return;
} }
} else if (layer && layer->mIsMirror) {
mShSurfClient = CloneSurface(gl->Screen()->Front()->Surf(), layer->mFactory.get());
if (!mShSurfClient) {
return;
}
} else { } else {
mShSurfClient = gl->Screen()->Front(); mShSurfClient = gl->Screen()->Front();
if (mShSurfClient && mShSurfClient->GetAllocator() && if (mShSurfClient && mShSurfClient->GetAllocator() &&

View File

@@ -67,7 +67,7 @@ ClientCanvasLayer::Initialize(const Data& aData)
UniquePtr<SurfaceFactory> factory = GLScreenBuffer::CreateFactory(mGLContext, caps, forwarder, mFlags); UniquePtr<SurfaceFactory> 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. // We're using a source other than the one in the default screen.
// (SkiaGL) // (SkiaGL)
mFactory = Move(factory); mFactory = Move(factory);