void ClientCanvasLayer::RenderLayer() { PROFILER_LABEL("ClientCanvasLayer", "RenderLayer", js::ProfileEntry::Category::GRAPHICS); RenderMaskLayers(this); if (!mCanvasClient) { TextureFlags flags = TextureFlags::IMMEDIATE_UPLOAD; if (mOriginPos == gl::OriginPos::BottomLeft) { flags |= TextureFlags::ORIGIN_BOTTOM_LEFT; } if (!mGLContext) { // We don't support locking for buffer surfaces currently flags |= TextureFlags::IMMEDIATE_UPLOAD; } if (!mIsAlphaPremultiplied) { flags |= TextureFlags::NON_PREMULTIPLIED; } mCanvasClient = CanvasClient::CreateCanvasClient(GetCanvasClientType(), ClientManager()->AsShadowForwarder(), flags); if (!mCanvasClient) { return; } if (HasShadow()) { if (mAsyncRenderer) { static_cast<CanvasClientBridge*>(mCanvasClient.get())->SetLayer(this); } else { mCanvasClient->Connect(); ClientManager()->AsShadowForwarder()->Attach(mCanvasClient, this); } } } if (mCanvasClient && mAsyncRenderer) { mCanvasClient->UpdateAsync(mAsyncRenderer); } if (!IsDirty()) { return; } Painted(); FirePreTransactionCallback(); if (mBufferProvider && mBufferProvider->GetTextureClient()) { mCanvasClient->UpdateFromTexture(mBufferProvider->GetTextureClient()); } else { mCanvasClient->Update(gfx::IntSize(mBounds.width, mBounds.height), this); } FireDidTransactionCallback(); ClientManager()->Hold(this); mCanvasClient->Updated(); }
void ClientThebesLayer::RenderLayer() { if (GetMaskLayer()) { ToClientLayer(GetMaskLayer())->RenderLayer(); } if (!mContentClient) { mContentClient = ContentClient::CreateContentClient(ClientManager()->AsShadowForwarder()); if (!mContentClient) { return; } mContentClient->Connect(); ClientManager()->AsShadowForwarder()->Attach(mContentClient, this); MOZ_ASSERT(mContentClient->GetForwarder()); } mContentClient->BeginPaint(); PaintThebes(); mContentClient->EndPaint(); // It is very important that this is called after EndPaint, because destroying // textures is a three stage process: // 1. We are done with the buffer and move it to ContentClient::mOldTextures, // that happens in DestroyBuffers which is may be called indirectly from // PaintThebes. // 2. The content client calls RemoveTextureClient on the texture clients in // mOldTextures and forgets them. They then become invalid. The compositable // client keeps a record of IDs. This happens in EndPaint. // 3. An IPC message is sent to destroy the corresponding texture host. That // happens from OnTransaction. // It is important that these steps happen in order. mContentClient->OnTransaction(); }
void ClientThebesLayer::PaintThebes() { PROFILER_LABEL("ClientThebesLayer", "PaintThebes"); NS_ASSERTION(ClientManager()->InDrawing(), "Can only draw in drawing phase"); //TODO: This is going to copy back pixels that we might end up // drawing over anyway. It would be nice if we could avoid // this duplication. mContentClient->SyncFrontBufferToBackBuffer(); bool canUseOpaqueSurface = CanUseOpaqueSurface(); ContentType contentType = canUseOpaqueSurface ? GFX_CONTENT_COLOR : GFX_CONTENT_COLOR_ALPHA; { uint32_t flags = 0; #ifndef MOZ_WIDGET_ANDROID if (ClientManager()->CompositorMightResample()) { flags |= ThebesLayerBuffer::PAINT_WILL_RESAMPLE; } if (!(flags & ThebesLayerBuffer::PAINT_WILL_RESAMPLE)) { if (MayResample()) { flags |= ThebesLayerBuffer::PAINT_WILL_RESAMPLE; } } #endif PaintState state = mContentClient->BeginPaintBuffer(this, contentType, flags); mValidRegion.Sub(mValidRegion, state.mRegionToInvalidate); if (state.mContext) { // The area that became invalid and is visible needs to be repainted // (this could be the whole visible area if our buffer switched // from RGB to RGBA, because we might need to repaint with // subpixel AA) state.mRegionToInvalidate.And(state.mRegionToInvalidate, GetEffectiveVisibleRegion()); nsIntRegion extendedDrawRegion = state.mRegionToDraw; SetAntialiasingFlags(this, state.mContext); PaintBuffer(state.mContext, state.mRegionToDraw, extendedDrawRegion, state.mRegionToInvalidate, state.mDidSelfCopy, state.mClip); MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) PaintThebes", this)); Mutated(); } else { // It's possible that state.mRegionToInvalidate is nonempty here, // if we are shrinking the valid region to nothing. So use mRegionToDraw // instead. NS_WARN_IF_FALSE(state.mRegionToDraw.IsEmpty(), "No context when we have something to draw, resource exhaustion?"); } } }
void ClientCanvasLayer::RenderLayer() { PROFILER_LABEL("ClientCanvasLayer", "RenderLayer", js::ProfileEntry::Category::GRAPHICS); if (GetMaskLayer()) { ToClientLayer(GetMaskLayer())->RenderLayer(); } if (!IsDirty()) { return; } if (!mCanvasClient) { TextureFlags flags = TextureFlags::IMMEDIATE_UPLOAD; if (mNeedsYFlip) { flags |= TextureFlags::NEEDS_Y_FLIP; } if (!mGLContext) { // We don't support locking for buffer surfaces currently flags |= TextureFlags::IMMEDIATE_UPLOAD; } else { // GLContext's SurfaceStream handles ownership itself, // and doesn't require layers to do any deallocation. flags |= TextureFlags::DEALLOCATE_CLIENT; } if (!mIsAlphaPremultiplied) { flags |= TextureFlags::NON_PREMULTIPLIED; } mCanvasClient = CanvasClient::CreateCanvasClient(GetCanvasClientType(), ClientManager()->AsShadowForwarder(), flags); if (!mCanvasClient) { return; } if (HasShadow()) { mCanvasClient->Connect(); ClientManager()->AsShadowForwarder()->Attach(mCanvasClient, this); } } FirePreTransactionCallback(); mCanvasClient->Update(gfx::IntSize(mBounds.width, mBounds.height), this); FireDidTransactionCallback(); ClientManager()->Hold(this); mCanvasClient->Updated(); mCanvasClient->OnTransaction(); }
void ClientThebesLayer::PaintThebes() { PROFILER_LABEL("ClientThebesLayer", "PaintThebes"); NS_ASSERTION(ClientManager()->InDrawing(), "Can only draw in drawing phase"); { mContentClient->PrepareFrame(); uint32_t flags = 0; #ifndef MOZ_WIDGET_ANDROID if (ClientManager()->CompositorMightResample()) { flags |= RotatedContentBuffer::PAINT_WILL_RESAMPLE; } if (!(flags & RotatedContentBuffer::PAINT_WILL_RESAMPLE)) { if (MayResample()) { flags |= RotatedContentBuffer::PAINT_WILL_RESAMPLE; } } #endif PaintState state = mContentClient->BeginPaintBuffer(this, flags); mValidRegion.Sub(mValidRegion, state.mRegionToInvalidate); if (DrawTarget* target = mContentClient->BorrowDrawTargetForPainting(this, state)) { // The area that became invalid and is visible needs to be repainted // (this could be the whole visible area if our buffer switched // from RGB to RGBA, because we might need to repaint with // subpixel AA) state.mRegionToInvalidate.And(state.mRegionToInvalidate, GetEffectiveVisibleRegion()); SetAntialiasingFlags(this, target); nsRefPtr<gfxContext> ctx = gfxContext::ContextForDrawTarget(target); PaintBuffer(ctx, state.mRegionToDraw, state.mRegionToDraw, state.mRegionToInvalidate, state.mDidSelfCopy, state.mClip); MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) PaintThebes", this)); Mutated(); ctx = nullptr; mContentClient->ReturnDrawTargetToBuffer(target); } else { // It's possible that state.mRegionToInvalidate is nonempty here, // if we are shrinking the valid region to nothing. So use mRegionToDraw // instead. NS_WARN_IF_FALSE(state.mRegionToDraw.IsEmpty(), "No context when we have something to draw, resource exhaustion?"); } } }
void ClientPaintedLayer::PaintThebes(nsTArray<ReadbackProcessor::Update>* aReadbackUpdates) { AUTO_PROFILER_LABEL("ClientPaintedLayer::PaintThebes", GRAPHICS); NS_ASSERTION(ClientManager()->InDrawing(), "Can only draw in drawing phase"); mContentClient->BeginPaint(); uint32_t flags = GetPaintFlags(); PaintState state = mContentClient->BeginPaintBuffer(this, flags); if (!UpdatePaintRegion(state)) { return; } bool didUpdate = false; RotatedContentBuffer::DrawIterator iter; while (DrawTarget* target = mContentClient->BorrowDrawTargetForPainting(state, &iter)) { if (!target || !target->IsValid()) { if (target) { mContentClient->ReturnDrawTargetToBuffer(target); } continue; } SetAntialiasingFlags(this, target); RefPtr<gfxContext> ctx = gfxContext::CreatePreservingTransformOrNull(target); MOZ_ASSERT(ctx); // already checked the target above ClientManager()->GetPaintedLayerCallback()(this, ctx, iter.mDrawRegion, iter.mDrawRegion, state.mClip, state.mRegionToInvalidate, ClientManager()->GetPaintedLayerCallbackData()); ctx = nullptr; mContentClient->ReturnDrawTargetToBuffer(target); didUpdate = true; } mContentClient->EndPaint(aReadbackUpdates); if (didUpdate) { UpdateContentClient(state); } }
void ClientCanvasLayer::Initialize(const Data& aData) { CopyableCanvasLayer::Initialize(aData); mCanvasClient = nullptr; if (mGLContext) { GLScreenBuffer* screen = mGLContext->Screen(); SurfaceStreamType streamType = SurfaceStream::ChooseGLStreamType(SurfaceStream::OffMainThread, screen->PreserveBuffer()); SurfaceFactory_GL* factory = nullptr; if (!mForceReadback) { if (ClientManager()->AsShadowForwarder()->GetCompositorBackendType() == mozilla::layers::LayersBackend::LAYERS_OPENGL) { if (mGLContext->GetContextType() == GLContextType::EGL) { bool isCrossProcess = !(XRE_GetProcessType() == GeckoProcessType_Default); if (!isCrossProcess) { // [Basic/OGL Layers, OMTC] WebGL layer init. factory = SurfaceFactory_EGLImage::Create(mGLContext, screen->Caps()); } else { // [Basic/OGL Layers, OOPC] WebGL layer init. (Out Of Process Compositing) #ifdef MOZ_WIDGET_GONK factory = new SurfaceFactory_Gralloc(mGLContext, screen->Caps(), ClientManager()->AsShadowForwarder()); #else // we could do readback here maybe NS_NOTREACHED("isCrossProcess but not on native B2G!"); #endif } } else { // [Basic Layers, OMTC] WebGL layer init. // Well, this *should* work... #ifdef XP_MACOSX factory = new SurfaceFactory_IOSurface(mGLContext, screen->Caps()); #else factory = new SurfaceFactory_GLTexture(mGLContext, nullptr, screen->Caps()); #endif } } } if (factory) { screen->Morph(factory, streamType); } } }
void ClientCanvasLayer::RenderLayer() { PROFILER_LABEL("ClientCanvasLayer", "Paint"); if (!IsDirty()) { return; } if (GetMaskLayer()) { ToClientLayer(GetMaskLayer())->RenderLayer(); } if (!mCanvasClient) { TextureFlags flags = TEXTURE_IMMEDIATE_UPLOAD; if (mNeedsYFlip) { flags |= TEXTURE_NEEDS_Y_FLIP; } bool isCrossProcess = !(XRE_GetProcessType() == GeckoProcessType_Default); //Append TEXTURE_DEALLOCATE_CLIENT flag for streaming buffer under OOPC case if (isCrossProcess && mGLContext) { GLScreenBuffer* screen = mGLContext->Screen(); if (screen && screen->Stream()) { flags |= TEXTURE_DEALLOCATE_CLIENT; } } mCanvasClient = CanvasClient::CreateCanvasClient(GetCanvasClientType(), ClientManager(), flags); if (!mCanvasClient) { return; } if (HasShadow()) { mCanvasClient->Connect(); ClientManager()->Attach(mCanvasClient, this); } } FirePreTransactionCallback(); mCanvasClient->Update(gfx::IntSize(mBounds.width, mBounds.height), this); FireDidTransactionCallback(); ClientManager()->Hold(this); mCanvasClient->Updated(); mCanvasClient->OnTransaction(); }
bool ClientTiledPaintedLayer::UseProgressiveDraw() { if (!gfxPlatform::GetPlatform()->UseProgressivePaint()) { // pref is disabled, so never do progressive return false; } if (!mContentClient->GetTiledBuffer()->SupportsProgressiveUpdate()) { return false; } if (ClientManager()->HasShadowTarget()) { // This condition is true when we are in a reftest scenario. We don't want // to draw progressively here because it can cause intermittent reftest // failures because the harness won't wait for all the tiles to be drawn. return false; } if (mPaintData.mCriticalDisplayPort.IsEmpty()) { // This catches three scenarios: // 1) This layer doesn't have a scrolling ancestor // 2) This layer is subject to OMTA transforms // 3) Low-precision painting is disabled // In all of these cases, we don't want to draw this layer progressively. return false; } if (GetIsFixedPosition() || GetParent()->GetIsFixedPosition()) { // This layer is fixed-position and so even if it does have a scrolling // ancestor it will likely be entirely on-screen all the time, so we // should draw it all at once return false; } if (ClientManager()->AsyncPanZoomEnabled()) { LayerMetricsWrapper scrollAncestor; GetAncestorLayers(&scrollAncestor, nullptr, nullptr); MOZ_ASSERT(scrollAncestor); // because mPaintData.mCriticalDisplayPort is non-empty const FrameMetrics& parentMetrics = scrollAncestor.Metrics(); if (!IsScrollingOnCompositor(parentMetrics)) { return false; } } return true; }
void SimpleClientTiledThebesLayer::RenderLayer() { LayerManager::DrawThebesLayerCallback callback = ClientManager()->GetThebesLayerCallback(); void *data = ClientManager()->GetThebesLayerCallbackData(); if (!callback) { ClientManager()->SetTransactionIncomplete(); return; } // First time? Create a content client. if (!mContentClient) { mContentClient = new SimpleTiledContentClient(this, ClientManager()); mContentClient->Connect(); ClientManager()->AsShadowForwarder()->Attach(mContentClient, this); MOZ_ASSERT(mContentClient->GetForwarder()); } // If the format changed, nothing is valid if (mContentClient->mTiledBuffer.HasFormatChanged()) { mValidRegion = nsIntRegion(); } nsIntRegion invalidRegion = mVisibleRegion; invalidRegion.Sub(invalidRegion, mValidRegion); if (invalidRegion.IsEmpty()) { return; } // Only paint the mask layer on the first transaction. if (GetMaskLayer() && !ClientManager()->IsRepeatTransaction()) { ToClientLayer(GetMaskLayer())->RenderLayer(); } // SimpleTiledContentClient doesn't support progressive updates or the low // precision buffer yet. MOZ_ASSERT(!gfxPrefs::UseProgressiveTilePainting() && !gfxPrefs::UseLowPrecisionBuffer()); mValidRegion = mVisibleRegion; NS_ASSERTION(!ClientManager()->IsRepeatTransaction(), "Didn't paint our mask layer"); mContentClient->mTiledBuffer.PaintThebes(mValidRegion, invalidRegion, callback, data); ClientManager()->Hold(this); mContentClient->UseTiledLayerBuffer(); }
void ClientThebesLayer::PaintBuffer(gfxContext* aContext, const nsIntRegion& aRegionToDraw, const nsIntRegion& aExtendedRegionToDraw, const nsIntRegion& aRegionToInvalidate, bool aDidSelfCopy, DrawRegionClip aClip) { ContentClientRemote* contentClientRemote = static_cast<ContentClientRemote*>(mContentClient.get()); MOZ_ASSERT(contentClientRemote->GetIPDLActor()); // NB: this just throws away the entire valid region if there are // too many rects. mValidRegion.SimplifyInward(8); if (!ClientManager()->GetThebesLayerCallback()) { ClientManager()->SetTransactionIncomplete(); return; } ClientManager()->GetThebesLayerCallback()(this, aContext, aExtendedRegionToDraw, aClip, aRegionToInvalidate, ClientManager()->GetThebesLayerCallbackData()); // Everything that's visible has been validated. Do this instead of just // OR-ing with aRegionToDraw, since that can lead to a very complex region // here (OR doesn't automatically simplify to the simplest possible // representation of a region.) nsIntRegion tmp; tmp.Or(mVisibleRegion, aExtendedRegionToDraw); mValidRegion.Or(mValidRegion, tmp); // Hold(this) ensures this layer is kept alive through the current transaction // The ContentClient assumes this layer is kept alive (e.g., in CreateBuffer, // DestroyThebesBuffer), so deleting this Hold for whatever reason will break things. ClientManager()->Hold(this); contentClientRemote->Updated(aRegionToDraw, mVisibleRegion, aDidSelfCopy); }
void ClientThebesLayer::RenderLayer() { if (GetMaskLayer()) { ToClientLayer(GetMaskLayer())->RenderLayer(); } if (!mContentClient) { mContentClient = ContentClient::CreateContentClient(ClientManager()); if (!mContentClient) { return; } mContentClient->Connect(); ClientManager()->Attach(mContentClient, this); MOZ_ASSERT(mContentClient->GetForwarder()); } mContentClient->BeginPaint(); PaintThebes(); mContentClient->EndPaint(); }
uint32_t ClientPaintedLayer::GetPaintFlags() { uint32_t flags = RotatedContentBuffer::PAINT_CAN_DRAW_ROTATED; #ifndef MOZ_IGNORE_PAINT_WILL_RESAMPLE if (ClientManager()->CompositorMightResample()) { flags |= RotatedContentBuffer::PAINT_WILL_RESAMPLE; } if (!(flags & RotatedContentBuffer::PAINT_WILL_RESAMPLE)) { if (MayResample()) { flags |= RotatedContentBuffer::PAINT_WILL_RESAMPLE; } } #endif return flags; }
bool ClientTiledPaintedLayer::RenderHighPrecision(nsIntRegion& aInvalidRegion, const nsIntRegion& aVisibleRegion, LayerManager::DrawPaintedLayerCallback aCallback, void* aCallbackData) { // If we have no high-precision stuff to draw, or we have started drawing low-precision // already, then we shouldn't do anything there. if (aInvalidRegion.IsEmpty() || mPaintData.mLowPrecisionPaintCount != 0) { return false; } // Only draw progressively when the resolution is unchanged, and we're not // in a reftest scenario (that's what the HasShadowManager() check is for). if (gfxPlatform::GetPlatform()->UseProgressivePaint() && !ClientManager()->HasShadowTarget() && mContentClient->mTiledBuffer.GetFrameResolution() == mPaintData.mResolution) { // Store the old valid region, then clear it before painting. // We clip the old valid region to the visible region, as it only gets // used to decide stale content (currently valid and previously visible) nsIntRegion oldValidRegion = mContentClient->mTiledBuffer.GetValidRegion(); oldValidRegion.And(oldValidRegion, aVisibleRegion); if (!mPaintData.mCriticalDisplayPort.IsEmpty()) { oldValidRegion.And(oldValidRegion, LayerIntRect::ToUntyped(mPaintData.mCriticalDisplayPort)); } TILING_LOG("TILING %p: Progressive update with old valid region %s\n", this, Stringify(oldValidRegion).c_str()); return mContentClient->mTiledBuffer.ProgressiveUpdate(mValidRegion, aInvalidRegion, oldValidRegion, &mPaintData, aCallback, aCallbackData); } // Otherwise do a non-progressive paint mValidRegion = aVisibleRegion; if (!mPaintData.mCriticalDisplayPort.IsEmpty()) { mValidRegion.And(mValidRegion, LayerIntRect::ToUntyped(mPaintData.mCriticalDisplayPort)); } TILING_LOG("TILING %p: Non-progressive paint invalid region %s\n", this, Stringify(aInvalidRegion).c_str()); TILING_LOG("TILING %p: Non-progressive paint new valid region %s\n", this, Stringify(mValidRegion).c_str()); mContentClient->mTiledBuffer.SetFrameResolution(mPaintData.mResolution); mContentClient->mTiledBuffer.PaintThebes(mValidRegion, aInvalidRegion, aCallback, aCallbackData); return true; }
void ClientCanvasLayer::Initialize(const Data& aData) { CopyableCanvasLayer::Initialize(aData); mCanvasClient = nullptr; if (!mGLContext) return; GLScreenBuffer* screen = mGLContext->Screen(); SurfaceCaps caps; if (mGLFrontbuffer) { // The screen caps are irrelevant if we're using a separate frontbuffer. caps = mGLFrontbuffer->mHasAlpha ? SurfaceCaps::ForRGBA() : SurfaceCaps::ForRGB(); } else { MOZ_ASSERT(screen); caps = screen->mCaps; } MOZ_ASSERT(caps.alpha == aData.mHasAlpha); auto forwarder = ClientManager()->AsShadowForwarder(); mFlags = TextureFlags::ORIGIN_BOTTOM_LEFT; if (!aData.mIsGLAlphaPremult) { mFlags |= TextureFlags::NON_PREMULTIPLIED; } UniquePtr<SurfaceFactory> factory = GLScreenBuffer::CreateFactory(mGLContext, caps, forwarder, mFlags); if (mGLFrontbuffer) { // We're using a source other than the one in the default screen. // (SkiaGL) mFactory = Move(factory); if (!mFactory) { // Absolutely must have a factory here, so create a basic one mFactory = MakeUnique<SurfaceFactory_Basic>(mGLContext, caps, mFlags); } } else { if (factory) screen->Morph(Move(factory)); } }
void ClientCanvasLayer::Initialize(const Data& aData) { CopyableCanvasLayer::Initialize(aData); mCanvasClient = nullptr; if (mGLContext) { GLScreenBuffer* screen = mGLContext->Screen(); SurfaceCaps caps = screen->Caps(); if (mStream) { // The screen caps are irrelevant if we're using a separate stream caps = GetContentFlags() & CONTENT_OPAQUE ? SurfaceCaps::ForRGB() : SurfaceCaps::ForRGBA(); } SurfaceStreamType streamType = SurfaceStream::ChooseGLStreamType(SurfaceStream::OffMainThread, screen->PreserveBuffer()); SurfaceFactory_GL* factory = nullptr; if (!gfxPrefs::WebGLForceLayersReadback()) { if (ClientManager()->AsShadowForwarder()->GetCompositorBackendType() == mozilla::layers::LayersBackend::LAYERS_OPENGL) { if (mGLContext->GetContextType() == GLContextType::EGL) { bool isCrossProcess = !(XRE_GetProcessType() == GeckoProcessType_Default); if (!isCrossProcess) { // [Basic/OGL Layers, OMTC] WebGL layer init. factory = SurfaceFactory_EGLImage::Create(mGLContext, caps); } else { // [Basic/OGL Layers, OOPC] WebGL layer init. (Out Of Process Compositing) #ifdef MOZ_WIDGET_GONK factory = new SurfaceFactory_Gralloc(mGLContext, caps, ClientManager()->AsShadowForwarder()); #else // we could do readback here maybe NS_NOTREACHED("isCrossProcess but not on native B2G!"); #endif } } else { // [Basic Layers, OMTC] WebGL layer init. // Well, this *should* work... #ifdef XP_MACOSX factory = new SurfaceFactory_IOSurface(mGLContext, caps); #else factory = new SurfaceFactory_GLTexture(mGLContext, nullptr, caps); #endif } } } if (mStream) { // We're using a stream other than the one in the default screen mFactory = factory; if (!mFactory) { // Absolutely must have a factory here, so create a basic one mFactory = new SurfaceFactory_Basic(mGLContext, caps); } gfx::IntSize size = gfx::IntSize(aData.mSize.width, aData.mSize.height); mTextureSurface = SharedSurface_GLTexture::Create(mGLContext, mGLContext, mGLContext->GetGLFormats(), size, caps.alpha, aData.mTexID); SharedSurface* producer = mStream->SwapProducer(mFactory, size); if (!producer) { // Fallback to basic factory delete mFactory; mFactory = new SurfaceFactory_Basic(mGLContext, caps); producer = mStream->SwapProducer(mFactory, size); MOZ_ASSERT(producer, "Failed to create initial canvas surface with basic factory"); } } else if (factory) { screen->Morph(factory, streamType); } } }
void ClientThebesLayer::PaintThebes() { PROFILER_LABEL("ClientThebesLayer", "PaintThebes"); NS_ASSERTION(ClientManager()->InDrawing(), "Can only draw in drawing phase"); uint32_t flags = RotatedContentBuffer::PAINT_CAN_DRAW_ROTATED; #ifndef MOZ_WIDGET_ANDROID if (ClientManager()->CompositorMightResample()) { flags |= RotatedContentBuffer::PAINT_WILL_RESAMPLE; } if (!(flags & RotatedContentBuffer::PAINT_WILL_RESAMPLE)) { if (MayResample()) { flags |= RotatedContentBuffer::PAINT_WILL_RESAMPLE; } } #endif PaintState state = mContentClient->BeginPaintBuffer(this, flags); mValidRegion.Sub(mValidRegion, state.mRegionToInvalidate); if (!state.mRegionToDraw.IsEmpty() && !ClientManager()->GetThebesLayerCallback()) { ClientManager()->SetTransactionIncomplete(); return; } // The area that became invalid and is visible needs to be repainted // (this could be the whole visible area if our buffer switched // from RGB to RGBA, because we might need to repaint with // subpixel AA) state.mRegionToInvalidate.And(state.mRegionToInvalidate, GetEffectiveVisibleRegion()); bool didUpdate = false; RotatedContentBuffer::DrawIterator iter; while (DrawTarget* target = mContentClient->BorrowDrawTargetForPainting(state, &iter)) { SetAntialiasingFlags(this, target); nsRefPtr<gfxContext> ctx = gfxContext::ContextForDrawTarget(target); ClientManager()->GetThebesLayerCallback()(this, ctx, iter.mDrawRegion, state.mClip, state.mRegionToInvalidate, ClientManager()->GetThebesLayerCallbackData()); ctx = nullptr; mContentClient->ReturnDrawTargetToBuffer(target); didUpdate = true; } if (didUpdate) { Mutated(); mValidRegion.Or(mValidRegion, state.mRegionToDraw); ContentClientRemote* contentClientRemote = static_cast<ContentClientRemote*>(mContentClient.get()); MOZ_ASSERT(contentClientRemote->GetIPDLActor()); // Hold(this) ensures this layer is kept alive through the current transaction // The ContentClient assumes this layer is kept alive (e.g., in CreateBuffer), // so deleting this Hold for whatever reason will break things. ClientManager()->Hold(this); contentClientRemote->Updated(state.mRegionToDraw, mVisibleRegion, state.mDidSelfCopy); } }
/*** * If we can, let's paint this ClientPaintedLayer's contents off the main thread. * The essential idea is that we ask the ContentClient for a DrawTarget and record * the moz2d commands. On the Paint Thread, we replay those commands to the * destination draw target. There are a couple of lifetime issues here though: * * 1) TextureClient owns the underlying buffer and DrawTarget. Because of this * we have to keep the TextureClient and DrawTarget alive but trick the * TextureClient into thinking it's already returned the DrawTarget * since we iterate through different Rects to get DrawTargets*. If * the TextureClient goes away, the DrawTarget and thus buffer can too. * 2) When ContentClient::EndPaint happens, it flushes the DrawTarget. We have * to Reflush on the Paint Thread * 3) DrawTarget API is NOT thread safe. We get around this by recording * on the main thread and painting on the paint thread. Logically, * ClientLayerManager will force a flushed paint and block the main thread * if we have another transaction. Thus we have a gap between when the main * thread records, the paint thread paints, and we block the main thread * from trying to paint again. The underlying API however is NOT thread safe. * 4) We have both "sync" and "async" OMTP. Sync OMTP means we paint on the main thread * but block the main thread while the paint thread paints. Async OMTP doesn't block * the main thread. Sync OMTP is only meant to be used as a debugging tool. */ bool ClientPaintedLayer::PaintOffMainThread() { mContentClient->BeginAsyncPaint(); uint32_t flags = GetPaintFlags(); PaintState state = mContentClient->BeginPaintBuffer(this, flags); if (!UpdatePaintRegion(state)) { return false; } bool didUpdate = false; RotatedContentBuffer::DrawIterator iter; // Debug Protip: Change to BorrowDrawTargetForPainting if using sync OMTP. while (RefPtr<CapturedPaintState> captureState = mContentClient->BorrowDrawTargetForRecording(state, &iter)) { DrawTarget* target = captureState->mTarget; if (!target || !target->IsValid()) { if (target) { mContentClient->ReturnDrawTargetToBuffer(target); } continue; } RefPtr<DrawTargetCapture> captureDT = Factory::CreateCaptureDrawTarget(target->GetBackendType(), target->GetSize(), target->GetFormat()); captureDT->SetTransform(captureState->mTargetTransform); SetAntialiasingFlags(this, captureDT); RefPtr<gfxContext> ctx = gfxContext::CreatePreservingTransformOrNull(captureDT); MOZ_ASSERT(ctx); // already checked the target above ClientManager()->GetPaintedLayerCallback()(this, ctx, iter.mDrawRegion, iter.mDrawRegion, state.mClip, state.mRegionToInvalidate, ClientManager()->GetPaintedLayerCallbackData()); ctx = nullptr; captureState->mCapture = captureDT.forget(); PaintThread::Get()->PaintContents(captureState, RotatedContentBuffer::PrepareDrawTargetForPainting); mContentClient->ReturnDrawTargetToBuffer(target); didUpdate = true; } mContentClient->EndPaint(nullptr); if (didUpdate) { UpdateContentClient(state); } return true; }
void ClientTiledThebesLayer::BeginPaint() { if (ClientManager()->IsRepeatTransaction()) { return; } mPaintData.mLowPrecisionPaintCount = 0; mPaintData.mPaintFinished = false; mPaintData.mCompositionBounds.SetEmpty(); mPaintData.mCriticalDisplayPort.SetEmpty(); if (!GetBaseTransform().Is2D()) { // Give up if there is a complex CSS transform on the layer. We might // eventually support these but for now it's too complicated to handle // given that it's a pretty rare scenario. return; } // Get the metrics of the nearest scrollable layer and the nearest layer // with a displayport. ContainerLayer* scrollAncestor = nullptr; ContainerLayer* displayPortAncestor = nullptr; for (ContainerLayer* ancestor = GetParent(); ancestor; ancestor = ancestor->GetParent()) { const FrameMetrics& metrics = ancestor->GetFrameMetrics(); if (!scrollAncestor && metrics.GetScrollId() != FrameMetrics::NULL_SCROLL_ID) { scrollAncestor = ancestor; } if (!metrics.mDisplayPort.IsEmpty()) { displayPortAncestor = ancestor; // Any layer that has a displayport must be scrollable, so we can break // here. break; } } if (!displayPortAncestor || !scrollAncestor) { // No displayport or scroll ancestor, so we can't do progressive rendering. #if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_B2G) // Both Android and b2g are guaranteed to have a displayport set, so this // should never happen. NS_WARNING("Tiled Thebes layer with no scrollable container ancestor"); #endif return; } TILING_PRLOG(("TILING 0x%p: Found scrollAncestor 0x%p and displayPortAncestor 0x%p\n", this, scrollAncestor, displayPortAncestor)); const FrameMetrics& scrollMetrics = scrollAncestor->GetFrameMetrics(); const FrameMetrics& displayportMetrics = displayPortAncestor->GetFrameMetrics(); // Calculate the transform required to convert ParentLayer space of our // display port ancestor to the Layer space of this layer. gfx3DMatrix transformToDisplayPort = GetTransformToAncestorsParentLayer(this, displayPortAncestor); mPaintData.mTransformDisplayPortToLayer = transformToDisplayPort.Inverse(); // Note that below we use GetZoomToParent() in a number of places. Because this // code runs on the client side, the mTransformScale field of the FrameMetrics // will not have been set. This can result in incorrect values being returned // by GetZoomToParent() when we have CSS transforms set on some of these layers. // This code should be audited and updated as part of fixing bug 993525. // Compute the critical display port that applies to this layer in the // LayoutDevice space of this layer. ParentLayerRect criticalDisplayPort = (displayportMetrics.mCriticalDisplayPort * displayportMetrics.GetZoomToParent()) + displayportMetrics.mCompositionBounds.TopLeft(); mPaintData.mCriticalDisplayPort = RoundedOut( ApplyParentLayerToLayerTransform(mPaintData.mTransformDisplayPortToLayer, criticalDisplayPort)); TILING_PRLOG_OBJ(("TILING 0x%p: Critical displayport %s\n", this, tmpstr.get()), mPaintData.mCriticalDisplayPort); // Compute the viewport that applies to this layer in the LayoutDevice // space of this layer. ParentLayerRect viewport = (displayportMetrics.mViewport * displayportMetrics.GetZoomToParent()) + displayportMetrics.mCompositionBounds.TopLeft(); mPaintData.mViewport = ApplyParentLayerToLayerTransform( mPaintData.mTransformDisplayPortToLayer, viewport); TILING_PRLOG_OBJ(("TILING 0x%p: Viewport %s\n", this, tmpstr.get()), mPaintData.mViewport); // Store the resolution from the displayport ancestor layer. Because this is Gecko-side, // before any async transforms have occurred, we can use the zoom for this. mPaintData.mResolution = displayportMetrics.GetZoomToParent(); TILING_PRLOG(("TILING 0x%p: Resolution %f\n", this, mPaintData.mResolution.scale)); // Store the applicable composition bounds in this layer's Layer units. gfx3DMatrix transformToCompBounds = GetTransformToAncestorsParentLayer(this, scrollAncestor); mPaintData.mCompositionBounds = ApplyParentLayerToLayerTransform( transformToCompBounds.Inverse(), ParentLayerRect(scrollMetrics.mCompositionBounds)); TILING_PRLOG_OBJ(("TILING 0x%p: Composition bounds %s\n", this, tmpstr.get()), mPaintData.mCompositionBounds); // Calculate the scroll offset since the last transaction mPaintData.mScrollOffset = displayportMetrics.GetScrollOffset() * displayportMetrics.GetZoomToParent(); TILING_PRLOG_OBJ(("TILING 0x%p: Scroll offset %s\n", this, tmpstr.get()), mPaintData.mScrollOffset); }
void ClientTiledThebesLayer::RenderLayer() { LayerManager::DrawThebesLayerCallback callback = ClientManager()->GetThebesLayerCallback(); void *data = ClientManager()->GetThebesLayerCallbackData(); if (!callback) { ClientManager()->SetTransactionIncomplete(); return; } if (!mContentClient) { mContentClient = new TiledContentClient(this, ClientManager()); mContentClient->Connect(); ClientManager()->AsShadowForwarder()->Attach(mContentClient, this); MOZ_ASSERT(mContentClient->GetForwarder()); } if (mContentClient->mTiledBuffer.HasFormatChanged()) { mValidRegion = nsIntRegion(); } TILING_PRLOG_OBJ(("TILING 0x%p: Initial visible region %s\n", this, tmpstr.get()), mVisibleRegion); TILING_PRLOG_OBJ(("TILING 0x%p: Initial valid region %s\n", this, tmpstr.get()), mValidRegion); nsIntRegion invalidRegion = mVisibleRegion; invalidRegion.Sub(invalidRegion, mValidRegion); if (invalidRegion.IsEmpty()) { EndPaint(true); return; } // Only paint the mask layer on the first transaction. if (GetMaskLayer() && !ClientManager()->IsRepeatTransaction()) { ToClientLayer(GetMaskLayer())->RenderLayer(); } bool isFixed = GetIsFixedPosition() || GetParent()->GetIsFixedPosition(); // Fast path for no progressive updates, no low-precision updates and no // critical display-port set, or no display-port set, or this is a fixed // position layer/contained in a fixed position layer const FrameMetrics& parentMetrics = GetParent()->GetFrameMetrics(); if ((!gfxPrefs::UseProgressiveTilePainting() && !gfxPrefs::UseLowPrecisionBuffer() && parentMetrics.mCriticalDisplayPort.IsEmpty()) || parentMetrics.mDisplayPort.IsEmpty() || isFixed) { mValidRegion = mVisibleRegion; NS_ASSERTION(!ClientManager()->IsRepeatTransaction(), "Didn't paint our mask layer"); mContentClient->mTiledBuffer.PaintThebes(mValidRegion, invalidRegion, callback, data); ClientManager()->Hold(this); mContentClient->UseTiledLayerBuffer(TiledContentClient::TILED_BUFFER); return; } // Calculate everything we need to perform the paint. BeginPaint(); if (mPaintData.mPaintFinished) { return; } TILING_PRLOG_OBJ(("TILING 0x%p: Valid region %s\n", this, tmpstr.get()), mValidRegion); TILING_PRLOG_OBJ(("TILING 0x%p: Visible region %s\n", this, tmpstr.get()), mVisibleRegion); // Make sure that tiles that fall outside of the visible region are // discarded on the first update. if (!ClientManager()->IsRepeatTransaction()) { mValidRegion.And(mValidRegion, mVisibleRegion); if (!mPaintData.mCriticalDisplayPort.IsEmpty()) { // Make sure that tiles that fall outside of the critical displayport are // discarded on the first update. mValidRegion.And(mValidRegion, LayerIntRect::ToUntyped(mPaintData.mCriticalDisplayPort)); } } nsIntRegion lowPrecisionInvalidRegion; if (!mPaintData.mCriticalDisplayPort.IsEmpty()) { if (gfxPrefs::UseLowPrecisionBuffer()) { // Calculate the invalid region for the low precision buffer lowPrecisionInvalidRegion.Sub(mVisibleRegion, mLowPrecisionValidRegion); // Remove the valid region from the low precision valid region (we don't // validate this part of the low precision buffer). lowPrecisionInvalidRegion.Sub(lowPrecisionInvalidRegion, mValidRegion); } // Clip the invalid region to the critical display-port invalidRegion.And(invalidRegion, LayerIntRect::ToUntyped(mPaintData.mCriticalDisplayPort)); if (invalidRegion.IsEmpty() && lowPrecisionInvalidRegion.IsEmpty()) { EndPaint(true); return; } } TILING_PRLOG_OBJ(("TILING 0x%p: Invalid region %s\n", this, tmpstr.get()), invalidRegion); if (!invalidRegion.IsEmpty() && mPaintData.mLowPrecisionPaintCount == 0) { bool updatedBuffer = false; // Only draw progressively when the resolution is unchanged. if (gfxPrefs::UseProgressiveTilePainting() && !ClientManager()->HasShadowTarget() && mContentClient->mTiledBuffer.GetFrameResolution() == mPaintData.mResolution) { // Store the old valid region, then clear it before painting. // We clip the old valid region to the visible region, as it only gets // used to decide stale content (currently valid and previously visible) nsIntRegion oldValidRegion = mContentClient->mTiledBuffer.GetValidRegion(); oldValidRegion.And(oldValidRegion, mVisibleRegion); if (!mPaintData.mCriticalDisplayPort.IsEmpty()) { oldValidRegion.And(oldValidRegion, LayerIntRect::ToUntyped(mPaintData.mCriticalDisplayPort)); } TILING_PRLOG_OBJ(("TILING 0x%p: Progressive update with old valid region %s\n", this, tmpstr.get()), oldValidRegion); updatedBuffer = mContentClient->mTiledBuffer.ProgressiveUpdate(mValidRegion, invalidRegion, oldValidRegion, &mPaintData, callback, data); } else { updatedBuffer = true; mValidRegion = mVisibleRegion; if (!mPaintData.mCriticalDisplayPort.IsEmpty()) { mValidRegion.And(mValidRegion, LayerIntRect::ToUntyped(mPaintData.mCriticalDisplayPort)); } TILING_PRLOG_OBJ(("TILING 0x%p: Painting: valid region %s\n", this, tmpstr.get()), mValidRegion); TILING_PRLOG_OBJ(("TILING 0x%p: and invalid region %s\n", this, tmpstr.get()), invalidRegion); mContentClient->mTiledBuffer.SetFrameResolution(mPaintData.mResolution); mContentClient->mTiledBuffer.PaintThebes(mValidRegion, invalidRegion, callback, data); } if (updatedBuffer) { ClientManager()->Hold(this); mContentClient->UseTiledLayerBuffer(TiledContentClient::TILED_BUFFER); // If there are low precision updates, mark the paint as unfinished and // request a repeat transaction. if (!lowPrecisionInvalidRegion.IsEmpty() && mPaintData.mPaintFinished) { ClientManager()->SetRepeatTransaction(); mPaintData.mLowPrecisionPaintCount = 1; mPaintData.mPaintFinished = false; } // Return so that low precision updates aren't performed in the same // transaction as high-precision updates. EndPaint(false); return; } } TILING_PRLOG_OBJ(("TILING 0x%p: Low-precision valid region is %s\n", this, tmpstr.get()), mLowPrecisionValidRegion); TILING_PRLOG_OBJ(("TILING 0x%p: Low-precision invalid region is %s\n", this, tmpstr.get()), lowPrecisionInvalidRegion); // Render the low precision buffer, if there's area to invalidate and the // visible region is larger than the critical display port. bool updatedLowPrecision = false; if (!lowPrecisionInvalidRegion.IsEmpty() && !nsIntRegion(LayerIntRect::ToUntyped(mPaintData.mCriticalDisplayPort)).Contains(mVisibleRegion)) { nsIntRegion oldValidRegion = mContentClient->mLowPrecisionTiledBuffer.GetValidRegion(); oldValidRegion.And(oldValidRegion, mVisibleRegion); // If the frame resolution or format have changed, invalidate the buffer if (mContentClient->mLowPrecisionTiledBuffer.GetFrameResolution() != mPaintData.mResolution || mContentClient->mLowPrecisionTiledBuffer.HasFormatChanged()) { if (!mLowPrecisionValidRegion.IsEmpty()) { updatedLowPrecision = true; } oldValidRegion.SetEmpty(); mLowPrecisionValidRegion.SetEmpty(); mContentClient->mLowPrecisionTiledBuffer.SetFrameResolution(mPaintData.mResolution); lowPrecisionInvalidRegion = mVisibleRegion; } // Invalidate previously valid content that is no longer visible if (mPaintData.mLowPrecisionPaintCount == 1) { mLowPrecisionValidRegion.And(mLowPrecisionValidRegion, mVisibleRegion); } mPaintData.mLowPrecisionPaintCount++; // Remove the valid high-precision region from the invalid low-precision // region. We don't want to spend time drawing things twice. lowPrecisionInvalidRegion.Sub(lowPrecisionInvalidRegion, mValidRegion); if (!lowPrecisionInvalidRegion.IsEmpty()) { updatedLowPrecision = mContentClient->mLowPrecisionTiledBuffer .ProgressiveUpdate(mLowPrecisionValidRegion, lowPrecisionInvalidRegion, oldValidRegion, &mPaintData, callback, data); } } else if (!mLowPrecisionValidRegion.IsEmpty()) { // Clear the low precision tiled buffer updatedLowPrecision = true; mLowPrecisionValidRegion.SetEmpty(); mContentClient->mLowPrecisionTiledBuffer.ResetPaintedAndValidState(); } // We send a Painted callback if we clear the valid region of the low // precision buffer, so that the shadow buffer's valid region can be updated // and the associated resources can be freed. if (updatedLowPrecision) { ClientManager()->Hold(this); mContentClient->UseTiledLayerBuffer(TiledContentClient::LOW_PRECISION_TILED_BUFFER); } EndPaint(false); }
void ClientTiledPaintedLayer::RenderLayer() { LayerManager::DrawPaintedLayerCallback callback = ClientManager()->GetPaintedLayerCallback(); void *data = ClientManager()->GetPaintedLayerCallbackData(); if (!callback) { ClientManager()->SetTransactionIncomplete(); return; } if (!mContentClient) { mContentClient = new TiledContentClient(this, ClientManager()); mContentClient->Connect(); ClientManager()->AsShadowForwarder()->Attach(mContentClient, this); MOZ_ASSERT(mContentClient->GetForwarder()); } if (mContentClient->mTiledBuffer.HasFormatChanged()) { mValidRegion = nsIntRegion(); mContentClient->mTiledBuffer.ResetPaintedAndValidState(); } TILING_LOG("TILING %p: Initial visible region %s\n", this, Stringify(mVisibleRegion).c_str()); TILING_LOG("TILING %p: Initial valid region %s\n", this, Stringify(mValidRegion).c_str()); TILING_LOG("TILING %p: Initial low-precision valid region %s\n", this, Stringify(mLowPrecisionValidRegion).c_str()); nsIntRegion neededRegion = mVisibleRegion; #ifndef MOZ_IGNORE_PAINT_WILL_RESAMPLE // This is handled by PadDrawTargetOutFromRegion in TiledContentClient for mobile if (MayResample()) { // If we're resampling then bilinear filtering can read up to 1 pixel // outside of our texture coords. Make the visible region a single rect, // and pad it out by 1 pixel (restricted to tile boundaries) so that // we always have valid content or transparent pixels to sample from. nsIntRect bounds = neededRegion.GetBounds(); nsIntRect wholeTiles = bounds; wholeTiles.Inflate(nsIntSize( gfxPlatform::GetPlatform()->GetTileWidth(), gfxPlatform::GetPlatform()->GetTileHeight())); nsIntRect padded = bounds; padded.Inflate(1); padded.IntersectRect(padded, wholeTiles); neededRegion = padded; } #endif nsIntRegion invalidRegion; invalidRegion.Sub(neededRegion, mValidRegion); if (invalidRegion.IsEmpty()) { EndPaint(); return; } if (!ClientManager()->IsRepeatTransaction()) { // Only paint the mask layer on the first transaction. if (GetMaskLayer()) { ToClientLayer(GetMaskLayer())->RenderLayer(); } // In some cases we can take a fast path and just be done with it. if (UseFastPath()) { TILING_LOG("TILING %p: Taking fast-path\n", this); mValidRegion = neededRegion; mContentClient->mTiledBuffer.PaintThebes(mValidRegion, invalidRegion, callback, data); ClientManager()->Hold(this); mContentClient->UseTiledLayerBuffer(TiledContentClient::TILED_BUFFER); return; } // For more complex cases we need to calculate a bunch of metrics before we // can do the paint. BeginPaint(); if (mPaintData.mPaintFinished) { return; } // Make sure that tiles that fall outside of the visible region or outside of the // critical displayport are discarded on the first update. Also make sure that we // only draw stuff inside the critical displayport on the first update. mValidRegion.And(mValidRegion, neededRegion); if (!mPaintData.mCriticalDisplayPort.IsEmpty()) { mValidRegion.And(mValidRegion, LayerIntRect::ToUntyped(mPaintData.mCriticalDisplayPort)); invalidRegion.And(invalidRegion, LayerIntRect::ToUntyped(mPaintData.mCriticalDisplayPort)); } TILING_LOG("TILING %p: First-transaction valid region %s\n", this, Stringify(mValidRegion).c_str()); TILING_LOG("TILING %p: First-transaction invalid region %s\n", this, Stringify(invalidRegion).c_str()); } else { if (!mPaintData.mCriticalDisplayPort.IsEmpty()) { invalidRegion.And(invalidRegion, LayerIntRect::ToUntyped(mPaintData.mCriticalDisplayPort)); } TILING_LOG("TILING %p: Repeat-transaction invalid region %s\n", this, Stringify(invalidRegion).c_str()); } nsIntRegion lowPrecisionInvalidRegion; if (gfxPrefs::UseLowPrecisionBuffer()) { // Calculate the invalid region for the low precision buffer. Make sure // to remove the valid high-precision area so we don't double-paint it. lowPrecisionInvalidRegion.Sub(neededRegion, mLowPrecisionValidRegion); lowPrecisionInvalidRegion.Sub(lowPrecisionInvalidRegion, mValidRegion); } TILING_LOG("TILING %p: Low-precision invalid region %s\n", this, Stringify(lowPrecisionInvalidRegion).c_str()); bool updatedHighPrecision = RenderHighPrecision(invalidRegion, neededRegion, callback, data); if (updatedHighPrecision) { ClientManager()->Hold(this); mContentClient->UseTiledLayerBuffer(TiledContentClient::TILED_BUFFER); if (!mPaintData.mPaintFinished) { // There is still more high-res stuff to paint, so we're not // done yet. A subsequent transaction will take care of this. ClientManager()->SetRepeatTransaction(); return; } } // If there is nothing to draw in low-precision, then we're done. if (lowPrecisionInvalidRegion.IsEmpty()) { EndPaint(); return; } if (updatedHighPrecision) { // If there are low precision updates, but we just did some high-precision // updates, then mark the paint as unfinished and request a repeat transaction. // This is so that we don't perform low-precision updates in the same transaction // as high-precision updates. TILING_LOG("TILING %p: Scheduling repeat transaction for low-precision painting\n", this); ClientManager()->SetRepeatTransaction(); mPaintData.mLowPrecisionPaintCount = 1; mPaintData.mPaintFinished = false; return; } bool updatedLowPrecision = RenderLowPrecision(lowPrecisionInvalidRegion, neededRegion, callback, data); if (updatedLowPrecision) { ClientManager()->Hold(this); mContentClient->UseTiledLayerBuffer(TiledContentClient::LOW_PRECISION_TILED_BUFFER); if (!mPaintData.mPaintFinished) { // There is still more low-res stuff to paint, so we're not // done yet. A subsequent transaction will take care of this. ClientManager()->SetRepeatTransaction(); return; } } // If we get here, we've done all the high- and low-precision // paints we wanted to do, so we can finish the paint and chill. EndPaint(); }
void ClientCanvasLayer::Initialize(const Data& aData) { CopyableCanvasLayer::Initialize(aData); mCanvasClient = nullptr; if (!mGLContext) return; GLScreenBuffer* screen = mGLContext->Screen(); SurfaceCaps caps; if (mGLFrontbuffer) { // The screen caps are irrelevant if we're using a separate frontbuffer. caps = mGLFrontbuffer->mHasAlpha ? SurfaceCaps::ForRGBA() : SurfaceCaps::ForRGB(); } else { MOZ_ASSERT(screen); caps = screen->mCaps; } MOZ_ASSERT(caps.alpha == aData.mHasAlpha); auto forwarder = ClientManager()->AsShadowForwarder(); mFlags = TextureFlags::ORIGIN_BOTTOM_LEFT; if (!aData.mIsGLAlphaPremult) { mFlags |= TextureFlags::NON_PREMULTIPLIED; } UniquePtr<SurfaceFactory> factory; if (!gfxPrefs::WebGLForceLayersReadback()) { switch (forwarder->GetCompositorBackendType()) { case mozilla::layers::LayersBackend::LAYERS_OPENGL: { #if defined(XP_MACOSX) factory = SurfaceFactory_IOSurface::Create(mGLContext, caps, forwarder, mFlags); #elif defined(MOZ_WIDGET_GONK) factory = MakeUnique<SurfaceFactory_Gralloc>(mGLContext, caps, forwarder, mFlags); #else if (mGLContext->GetContextType() == GLContextType::EGL) { bool isCrossProcess = (XRE_GetProcessType() != GeckoProcessType_Default); if (!isCrossProcess) { factory = SurfaceFactory_EGLImage::Create(mGLContext, caps, forwarder, mFlags); } } #endif break; } case mozilla::layers::LayersBackend::LAYERS_D3D11: { #ifdef XP_WIN if (mGLContext->IsANGLE() && DoesD3D11TextureSharingWork(gfxWindowsPlatform::GetPlatform()->GetD3D11Device())) { factory = SurfaceFactory_ANGLEShareHandle::Create(mGLContext, caps, forwarder, mFlags); } #endif break; } default: break; } } if (mGLFrontbuffer) { // We're using a source other than the one in the default screen. // (SkiaGL) mFactory = Move(factory); if (!mFactory) { // Absolutely must have a factory here, so create a basic one mFactory = MakeUnique<SurfaceFactory_Basic>(mGLContext, caps, mFlags); } } else { if (factory) screen->Morph(Move(factory)); } }
void ClientTiledThebesLayer::BeginPaint() { if (ClientManager()->IsRepeatTransaction()) { return; } mPaintData.mLowPrecisionPaintCount = 0; mPaintData.mPaintFinished = false; // Calculate the transform required to convert screen space into layer space mPaintData.mTransformScreenToLayer = GetEffectiveTransform(); // XXX Not sure if this code for intermediate surfaces is correct. // It rarely gets hit though, and shouldn't have terrible consequences // even if it is wrong. for (ContainerLayer* parent = GetParent(); parent; parent = parent->GetParent()) { if (parent->UseIntermediateSurface()) { mPaintData.mTransformScreenToLayer.PreMultiply(parent->GetEffectiveTransform()); } } mPaintData.mTransformScreenToLayer.Invert(); // Compute the critical display port in layer space. mPaintData.mLayerCriticalDisplayPort.SetEmpty(); const gfx::Rect& criticalDisplayPort = GetParent()->GetFrameMetrics().mCriticalDisplayPort; if (!criticalDisplayPort.IsEmpty()) { gfxRect transformedCriticalDisplayPort = mPaintData.mTransformScreenToLayer.TransformBounds( gfxRect(criticalDisplayPort.x, criticalDisplayPort.y, criticalDisplayPort.width, criticalDisplayPort.height)); transformedCriticalDisplayPort.RoundOut(); mPaintData.mLayerCriticalDisplayPort = nsIntRect(transformedCriticalDisplayPort.x, transformedCriticalDisplayPort.y, transformedCriticalDisplayPort.width, transformedCriticalDisplayPort.height); } // Calculate the frame resolution. mPaintData.mResolution.SizeTo(1, 1); for (ContainerLayer* parent = GetParent(); parent; parent = parent->GetParent()) { const FrameMetrics& metrics = parent->GetFrameMetrics(); mPaintData.mResolution.width *= metrics.mResolution.width; mPaintData.mResolution.height *= metrics.mResolution.height; } // Calculate the scroll offset since the last transaction, and the // composition bounds. mPaintData.mCompositionBounds.SetEmpty(); mPaintData.mScrollOffset.MoveTo(0, 0); Layer* primaryScrollable = ClientManager()->GetPrimaryScrollableLayer(); if (primaryScrollable) { const FrameMetrics& metrics = primaryScrollable->AsContainerLayer()->GetFrameMetrics(); mPaintData.mScrollOffset = metrics.mScrollOffset; gfxRect transformedViewport = mPaintData.mTransformScreenToLayer.TransformBounds( gfxRect(metrics.mCompositionBounds.x, metrics.mCompositionBounds.y, metrics.mCompositionBounds.width, metrics.mCompositionBounds.height)); transformedViewport.RoundOut(); mPaintData.mCompositionBounds = nsIntRect(transformedViewport.x, transformedViewport.y, transformedViewport.width, transformedViewport.height); } }
void ClientCanvasLayer::Initialize(const Data& aData) { CopyableCanvasLayer::Initialize(aData); mCanvasClient = nullptr; if (!mGLContext) return; GLScreenBuffer* screen = mGLContext->Screen(); SurfaceCaps caps; if (mGLFrontbuffer) { // The screen caps are irrelevant if we're using a separate frontbuffer. caps = mGLFrontbuffer->mHasAlpha ? SurfaceCaps::ForRGBA() : SurfaceCaps::ForRGB(); } else { MOZ_ASSERT(screen); caps = screen->mCaps; } MOZ_ASSERT(caps.alpha == aData.mHasAlpha); UniquePtr<SurfaceFactory> factory; if (!gfxPrefs::WebGLForceLayersReadback()) { switch (ClientManager()->AsShadowForwarder()->GetCompositorBackendType()) { case mozilla::layers::LayersBackend::LAYERS_OPENGL: { if (mGLContext->GetContextType() == GLContextType::EGL) { #ifdef MOZ_WIDGET_GONK TextureFlags flags = TextureFlags::DEALLOCATE_CLIENT | TextureFlags::ORIGIN_BOTTOM_LEFT; if (!aData.mIsGLAlphaPremult) { flags |= TextureFlags::NON_PREMULTIPLIED; } factory = MakeUnique<SurfaceFactory_Gralloc>(mGLContext, caps, flags, ClientManager()->AsShadowForwarder()); #else bool isCrossProcess = !(XRE_GetProcessType() == GeckoProcessType_Default); if (!isCrossProcess) { // [Basic/OGL Layers, OMTC] WebGL layer init. factory = SurfaceFactory_EGLImage::Create(mGLContext, caps); } else { // we could do readback here maybe NS_NOTREACHED("isCrossProcess but not on native B2G!"); } #endif } else { // [Basic Layers, OMTC] WebGL layer init. // Well, this *should* work... #ifdef XP_MACOSX factory = SurfaceFactory_IOSurface::Create(mGLContext, caps); #else GLContext* nullConsGL = nullptr; // Bug 1050044. factory = MakeUnique<SurfaceFactory_GLTexture>(mGLContext, nullConsGL, caps); #endif } break; } case mozilla::layers::LayersBackend::LAYERS_D3D11: { #ifdef XP_WIN if (mGLContext->IsANGLE() && DoesD3D11TextureSharingWork(gfxWindowsPlatform::GetPlatform()->GetD3D11Device())) { factory = SurfaceFactory_ANGLEShareHandle::Create(mGLContext, caps); } #endif break; } default: break; } } if (mGLFrontbuffer) { // We're using a source other than the one in the default screen. // (SkiaGL) mFactory = Move(factory); if (!mFactory) { // Absolutely must have a factory here, so create a basic one mFactory = MakeUnique<SurfaceFactory_Basic>(mGLContext, caps); } } else { if (factory) screen->Morph(Move(factory)); } }
void ClientTiledThebesLayer::BeginPaint() { if (ClientManager()->IsRepeatTransaction()) { return; } mPaintData.mLowPrecisionPaintCount = 0; mPaintData.mPaintFinished = false; // Get the metrics of the nearest scroll container. ContainerLayer* scrollParent = nullptr; for (ContainerLayer* parent = GetParent(); parent; parent = parent->GetParent()) { const FrameMetrics& metrics = parent->GetFrameMetrics(); if (metrics.mScrollId != FrameMetrics::NULL_SCROLL_ID) { scrollParent = parent; break; } } if (!scrollParent) { // XXX I don't think this can happen, but if it does, warn and set the // composition bounds to empty so that progressive updates are disabled. NS_WARNING("Tiled Thebes layer with no scrollable container parent"); mPaintData.mCompositionBounds.SetEmpty(); return; } const FrameMetrics& metrics = scrollParent->GetFrameMetrics(); // Calculate the transform required to convert screen space into transformed // layout device space. gfx::Matrix4x4 effectiveTransform = GetEffectiveTransform(); for (ContainerLayer* parent = GetParent(); parent; parent = parent->GetParent()) { if (parent->UseIntermediateSurface()) { effectiveTransform = effectiveTransform * parent->GetEffectiveTransform(); } } gfx3DMatrix layoutToScreen; gfx::To3DMatrix(effectiveTransform, layoutToScreen); layoutToScreen.ScalePost(metrics.mCumulativeResolution.scale, metrics.mCumulativeResolution.scale, 1.f); mPaintData.mTransformScreenToLayout = layoutToScreen.Inverse(); // Compute the critical display port in layer space. mPaintData.mLayoutCriticalDisplayPort.SetEmpty(); if (!metrics.mCriticalDisplayPort.IsEmpty()) { // Convert the display port to screen space first so that we can transform // it into layout device space. const ScreenRect& criticalDisplayPort = metrics.mCriticalDisplayPort * metrics.mZoom; LayoutDeviceRect transformedCriticalDisplayPort = ApplyScreenToLayoutTransform(mPaintData.mTransformScreenToLayout, criticalDisplayPort); mPaintData.mLayoutCriticalDisplayPort = LayoutDeviceIntRect::ToUntyped(RoundedOut(transformedCriticalDisplayPort)); } // Calculate the frame resolution. Because this is Gecko-side, before any // async transforms have occurred, we can use mZoom for this. mPaintData.mResolution = metrics.mZoom; // Calculate the scroll offset since the last transaction, and the // composition bounds. mPaintData.mCompositionBounds.SetEmpty(); mPaintData.mScrollOffset.MoveTo(0, 0); Layer* primaryScrollable = ClientManager()->GetPrimaryScrollableLayer(); if (primaryScrollable) { const FrameMetrics& metrics = primaryScrollable->AsContainerLayer()->GetFrameMetrics(); mPaintData.mScrollOffset = metrics.mScrollOffset * metrics.mZoom; mPaintData.mCompositionBounds = ApplyScreenToLayoutTransform(mPaintData.mTransformScreenToLayout, ScreenRect(metrics.mCompositionBounds)); } }
void SimpleClientTiledThebesLayer::RenderLayer() { LayerManager::DrawThebesLayerCallback callback = ClientManager()->GetThebesLayerCallback(); void *data = ClientManager()->GetThebesLayerCallbackData(); if (!callback) { ClientManager()->SetTransactionIncomplete(); return; } // First time? Create a content client. if (!mContentClient) { mContentClient = new SimpleTiledContentClient(this, ClientManager()); mContentClient->Connect(); ClientManager()->AsShadowForwarder()->Attach(mContentClient, this); MOZ_ASSERT(mContentClient->GetForwarder()); } // If the format changed, nothing is valid if (mContentClient->mTiledBuffer.HasFormatChanged()) { mValidRegion = nsIntRegion(); } nsIntRegion invalidRegion = mVisibleRegion; invalidRegion.Sub(invalidRegion, mValidRegion); if (invalidRegion.IsEmpty()) { EndPaint(true); return; } const FrameMetrics& parentMetrics = GetParent()->GetFrameMetrics(); nsIntRegion wantToPaintRegion = mVisibleRegion; // Only paint the mask layer on the first transaction. if (GetMaskLayer() && !ClientManager()->IsRepeatTransaction()) { ToClientLayer(GetMaskLayer())->RenderLayer(); } // Fast path for no progressive updates, no low-precision updates and no // critical display-port set, or no display-port set. if (parentMetrics.mCriticalDisplayPort.IsEmpty() || parentMetrics.mDisplayPort.IsEmpty()) { mValidRegion = wantToPaintRegion; NS_ASSERTION(!ClientManager()->IsRepeatTransaction(), "Didn't paint our mask layer"); mContentClient->mTiledBuffer.PaintThebes(mValidRegion, invalidRegion, callback, data); ClientManager()->Hold(this); mContentClient->UseTiledLayerBuffer(); return; } // Calculate everything we need to perform the paint. BeginPaint(); if (mPaintData.mPaintFinished) { return; } // Make sure that tiles that fall outside of the visible region are // discarded on the first update. if (!ClientManager()->IsRepeatTransaction()) { mValidRegion.And(mValidRegion, wantToPaintRegion); if (!mPaintData.mLayoutCriticalDisplayPort.IsEmpty()) { // Make sure that tiles that fall outside of the critical displayport are // discarded on the first update. mValidRegion.And(mValidRegion, mPaintData.mLayoutCriticalDisplayPort); } } nsIntRegion lowPrecisionInvalidRegion; if (!mPaintData.mLayoutCriticalDisplayPort.IsEmpty()) { // Clip the invalid region to the critical display-port invalidRegion.And(invalidRegion, mPaintData.mLayoutCriticalDisplayPort); if (invalidRegion.IsEmpty() && lowPrecisionInvalidRegion.IsEmpty()) { EndPaint(true); return; } } if (!invalidRegion.IsEmpty()) { mValidRegion = wantToPaintRegion; if (!mPaintData.mLayoutCriticalDisplayPort.IsEmpty()) { mValidRegion.And(mValidRegion, mPaintData.mLayoutCriticalDisplayPort); } mContentClient->mTiledBuffer.SetFrameResolution(mPaintData.mResolution); mContentClient->mTiledBuffer.PaintThebes(mValidRegion, invalidRegion, callback, data); ClientManager()->Hold(this); mContentClient->UseTiledLayerBuffer(); EndPaint(false); return; } EndPaint(false); }
void ClientCanvasLayer::Initialize(const Data& aData) { CopyableCanvasLayer::Initialize(aData); mCanvasClient = nullptr; if (mGLContext) { GLScreenBuffer* screen = mGLContext->Screen(); SurfaceCaps caps; if (mStream) { // The screen caps are irrelevant if we're using a separate stream caps = aData.mHasAlpha ? SurfaceCaps::ForRGBA() : SurfaceCaps::ForRGB(); } else { caps = screen->mCaps; } MOZ_ASSERT(caps.alpha == aData.mHasAlpha); SurfaceStreamType streamType = SurfaceStream::ChooseGLStreamType(SurfaceStream::OffMainThread, screen->PreserveBuffer()); UniquePtr<SurfaceFactory> factory; if (!gfxPrefs::WebGLForceLayersReadback()) { switch (ClientManager()->AsShadowForwarder()->GetCompositorBackendType()) { case mozilla::layers::LayersBackend::LAYERS_OPENGL: { if (mGLContext->GetContextType() == GLContextType::EGL) { #ifdef MOZ_WIDGET_GONK factory = MakeUnique<SurfaceFactory_Gralloc>(mGLContext, caps, ClientManager()->AsShadowForwarder()); #else bool isCrossProcess = !(XRE_GetProcessType() == GeckoProcessType_Default); if (!isCrossProcess) { // [Basic/OGL Layers, OMTC] WebGL layer init. factory = SurfaceFactory_EGLImage::Create(mGLContext, caps); } else { // we could do readback here maybe NS_NOTREACHED("isCrossProcess but not on native B2G!"); } #endif } else { // [Basic Layers, OMTC] WebGL layer init. // Well, this *should* work... #ifdef XP_MACOSX factory = SurfaceFactory_IOSurface::Create(mGLContext, caps); #else GLContext* nullConsGL = nullptr; // Bug 1050044. factory = MakeUnique<SurfaceFactory_GLTexture>(mGLContext, nullConsGL, caps); #endif } break; } case mozilla::layers::LayersBackend::LAYERS_D3D10: case mozilla::layers::LayersBackend::LAYERS_D3D11: { #ifdef XP_WIN if (mGLContext->IsANGLE()) { factory = SurfaceFactory_ANGLEShareHandle::Create(mGLContext, caps); } #endif break; } default: break; } } if (mStream) { // We're using a stream other than the one in the default screen mFactory = Move(factory); if (!mFactory) { // Absolutely must have a factory here, so create a basic one mFactory = MakeUnique<SurfaceFactory_Basic>(mGLContext, caps); } gfx::IntSize size = gfx::IntSize(aData.mSize.width, aData.mSize.height); mTextureSurface = SharedSurface_GLTexture::Create(mGLContext, mGLContext, mGLContext->GetGLFormats(), size, caps.alpha, aData.mTexID); SharedSurface* producer = mStream->SwapProducer(mFactory.get(), size); if (!producer) { // Fallback to basic factory mFactory = MakeUnique<SurfaceFactory_Basic>(mGLContext, caps); producer = mStream->SwapProducer(mFactory.get(), size); MOZ_ASSERT(producer, "Failed to create initial canvas surface with basic factory"); } } else if (factory) { screen->Morph(Move(factory), streamType); } } }
virtual void SetVisibleRegion(const nsIntRegion& aRegion) { NS_ASSERTION(ClientManager()->InConstruction(), "Can only set properties in construction phase"); ImageLayer::SetVisibleRegion(aRegion); }
ServerApp::ServerApp(const sf::Uint16 port, const sf::Uint16 max_players) { m_port = port; m_ClMan = ClientManager(); m_ClMan.SetMaxPlayers(max_players); }