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

View File

@ -5811,8 +5811,14 @@ CanvasRenderingContext2D::GetBufferProvider(LayerManager* aManager)
already_AddRefed<Layer>
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.

View File

@ -466,7 +466,8 @@ public:
mozilla::layers::PersistentBufferProvider* GetBufferProvider(mozilla::layers::LayerManager* aManager);
already_AddRefed<Layer> 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;

View File

@ -214,8 +214,14 @@ ImageBitmapRenderingContext::Reset()
already_AddRefed<Layer>
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.

View File

@ -70,7 +70,8 @@ public:
NS_IMETHOD Reset() override;
virtual already_AddRefed<Layer> 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;

View File

@ -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<layers::Layer>
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<layers::Layer> 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;

View File

@ -333,7 +333,8 @@ public:
already_AddRefed<Layer>
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.

View File

@ -135,7 +135,8 @@ public:
// one for the given layer manager if not available.
virtual already_AddRefed<Layer> 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

View File

@ -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");

View File

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

View File

@ -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;
};
/**

View File

@ -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() &&

View File

@ -67,7 +67,7 @@ ClientCanvasLayer::Initialize(const Data& aData)
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.
// (SkiaGL)
mFactory = Move(factory);