diff --git a/gfx/layers/basic/BasicLayerManager.cpp b/gfx/layers/basic/BasicLayerManager.cpp index bdddfed01ad1..19d02584661f 100644 --- a/gfx/layers/basic/BasicLayerManager.cpp +++ b/gfx/layers/basic/BasicLayerManager.cpp @@ -100,6 +100,111 @@ ToInsideIntRect(const gfxRect& aRect) return nsIntRect(r.X(), r.Y(), r.Width(), r.Height()); } +// A context helper for BasicLayerManager::PaintLayer() that holds all the +// painting context together in a data structure so it can be easily passed +// around. It also uses ensures that the Transform and Opaque rect are restored +// to their former state on destruction. + +class PaintContext { +public: + PaintContext(gfxContext* aTarget, Layer* aLayer, + LayerManager::DrawThebesLayerCallback aCallback, + void* aCallbackData, ReadbackProcessor* aReadback) + : mTarget(aTarget) + , mTargetMatrixSR(aTarget) + , mLayer(aLayer) + , mCallback(aCallback) + , mCallbackData(aCallbackData) + , mReadback(aReadback) + , mPushedOpaqueRect(false) + {} + + ~PaintContext() + { + // Matrix is restored by mTargetMatrixSR + if (mPushedOpaqueRect) + { + ClearOpaqueRect(); + } + } + + // Applies the effective 2D transform and returns true if it is a 2D + // transform. If it's a 3D transform then it applies an identity and returns + // false. + bool Apply2DTransform() + { + const gfx3DMatrix& effectiveTransform = mLayer->GetEffectiveTransform(); + + // Will return an identity matrix for 3d transforms. + bool is2D = effectiveTransform.CanDraw2D(&mTransform); + mTarget->SetMatrix(mTransform); + + return is2D; + } + + // Set the opaque rect to match the bounds of the visible region. + void AnnotateOpaqueRect() + { + const nsIntRegion& visibleRegion = mLayer->GetEffectiveVisibleRegion(); + const nsIntRect& bounds = visibleRegion.GetBounds(); + nsRefPtr currentSurface = mTarget->CurrentSurface(); + + if (mTarget->IsCairo()) { + const gfxRect& targetOpaqueRect = currentSurface->GetOpaqueRect(); + + // Try to annotate currentSurface with a region of pixels that have been + // (or will be) painted opaque, if no such region is currently set. + if (targetOpaqueRect.IsEmpty() && visibleRegion.GetNumRects() == 1 && + (mLayer->GetContentFlags() & Layer::CONTENT_OPAQUE) && + !mTransform.HasNonAxisAlignedTransform()) { + currentSurface->SetOpaqueRect( + mTarget->UserToDevice(gfxRect(bounds.x, bounds.y, bounds.width, bounds.height))); + mPushedOpaqueRect = true; + } + } else { + DrawTarget *dt = mTarget->GetDrawTarget(); + const IntRect& targetOpaqueRect = dt->GetOpaqueRect(); + + // Try to annotate currentSurface with a region of pixels that have been + // (or will be) painted opaque, if no such region is currently set. + if (targetOpaqueRect.IsEmpty() && visibleRegion.GetNumRects() == 1 && + (mLayer->GetContentFlags() & Layer::CONTENT_OPAQUE) && + !mTransform.HasNonAxisAlignedTransform()) { + + gfx::Rect opaqueRect = dt->GetTransform().TransformBounds( + gfx::Rect(bounds.x, bounds.y, bounds.width, bounds.height)); + opaqueRect.RoundIn(); + IntRect intOpaqueRect; + if (opaqueRect.ToIntRect(&intOpaqueRect)) { + mTarget->GetDrawTarget()->SetOpaqueRect(intOpaqueRect); + mPushedOpaqueRect = true; + } + } + } + } + + // Clear the Opaque rect. Although this doesn't really restore it to it's + // previous state it will happen on the exit path of the PaintLayer() so when + // painting is complete the opaque rect qill be clear. + void ClearOpaqueRect() { + if (mTarget->IsCairo()) { + nsRefPtr currentSurface = mTarget->CurrentSurface(); + currentSurface->SetOpaqueRect(gfxRect()); + } else { + mTarget->GetDrawTarget()->SetOpaqueRect(IntRect()); + } + } + + gfxContext* mTarget; + gfxContextMatrixAutoSaveRestore mTargetMatrixSR; + Layer* mLayer; + LayerManager::DrawThebesLayerCallback mCallback; + void* mCallbackData; + ReadbackProcessor* mReadback; + gfxMatrix mTransform; + bool mPushedOpaqueRect; +}; + BasicLayerManager::BasicLayerManager(nsIWidget* aWidget) : #ifdef DEBUG mPhase(PHASE_NONE), @@ -690,7 +795,71 @@ Transform3D(gfxASurface* aSource, gfxContext* aDest, return destImage.forget(); } +void +BasicLayerManager::PaintSelfOrChildren(PaintContext& aPaintContext, + gfxContext* aGroupTarget) +{ + BasicImplData* data = ToData(aPaintContext.mLayer); + if (aPaintContext.mLayer->GetFirstChild() && + aPaintContext.mLayer->GetMaskLayer() && + HasShadowManager()) { + // 'paint' the mask so that it gets sent to the shadow layer tree + static_cast(aPaintContext.mLayer->GetMaskLayer()->ImplData()) + ->Paint(nullptr, nullptr); + } + + /* Only paint ourself, or our children - This optimization relies on this! */ + Layer* child = aPaintContext.mLayer->GetFirstChild(); + if (!child) { + if (aPaintContext.mLayer->AsThebesLayer()) { + data->PaintThebes(aGroupTarget, aPaintContext.mLayer->GetMaskLayer(), + aPaintContext.mCallback, aPaintContext.mCallbackData, + aPaintContext.mReadback); + } else { + data->Paint(aGroupTarget, aPaintContext.mLayer->GetMaskLayer()); + } + } else { + ReadbackProcessor readback; + ContainerLayer* container = + static_cast(aPaintContext.mLayer); + if (IsRetained()) { + readback.BuildUpdates(container); + } + nsAutoTArray children; + container->SortChildrenBy3DZOrder(children); + for (uint32_t i = 0; i < children.Length(); i++) { + PaintLayer(aGroupTarget, children.ElementAt(i), aPaintContext.mCallback, + aPaintContext.mCallbackData, &readback); + if (mTransactionIncomplete) + break; + } + } +} + +void +BasicLayerManager::FlushGroup(PaintContext& aPaintContext, bool aNeedsClipToVisibleRegion) +{ + // If we're doing our own double-buffering, we need to avoid drawing + // the results of an incomplete transaction to the destination surface --- + // that could cause flicker. Double-buffering is implemented using a + // temporary surface for one or more container layers, so we need to stop + // those temporary surfaces from being composited to aTarget. + // ApplyDoubleBuffering guarantees that this container layer can't + // intersect any other leaf layers, so if the transaction is not yet marked + // incomplete, the contents of this container layer are the final contents + // for the window. + if (!mTransactionIncomplete) { + if (aNeedsClipToVisibleRegion) { + gfxUtils::ClipToRegion(aPaintContext.mTarget, + aPaintContext.mLayer->GetEffectiveVisibleRegion()); + } + BasicContainerLayer* container = static_cast(aPaintContext.mLayer); + AutoSetOperator setOperator(aPaintContext.mTarget, container->GetOperator()); + PaintWithMask(aPaintContext.mTarget, aPaintContext.mLayer->GetEffectiveOpacity(), + HasShadowManager() ? nullptr : aPaintContext.mLayer->GetMaskLayer()); + } +} void BasicLayerManager::PaintLayer(gfxContext* aTarget, @@ -699,10 +868,11 @@ BasicLayerManager::PaintLayer(gfxContext* aTarget, void* aCallbackData, ReadbackProcessor* aReadback) { + PaintContext paintContext(aTarget, aLayer, aCallback, aCallbackData, aReadback); + RenderTraceScope trace("BasicLayerManager::PaintLayer", "707070"); const nsIntRect* clipRect = aLayer->GetEffectiveClipRect(); - const gfx3DMatrix& effectiveTransform = aLayer->GetEffectiveTransform(); // aLayer might not be a container layer, but if so we take care not to use // the container variable BasicContainerLayer* container = static_cast(aLayer); @@ -718,12 +888,10 @@ BasicLayerManager::PaintLayer(gfxContext* aTarget, container->UseIntermediateSurface(), "ContainerLayer with mask layer should force UseIntermediateSurface"); - // If needsSaveRestore is false, we should still save and restore - // the CTM + gfxContextAutoSaveRestore contextSR; bool needsSaveRestore = needsGroup || clipRect || needsClipToVisibleRegion; - gfxMatrix savedMatrix; if (needsSaveRestore) { - aTarget->Save(); + contextSR.SetContext(aTarget); if (clipRect) { aTarget->NewPath(); @@ -731,17 +899,9 @@ BasicLayerManager::PaintLayer(gfxContext* aTarget, aTarget->Clip(); } } - savedMatrix = aTarget->CurrentMatrix(); - gfxMatrix transform; - // Will return an identity matrix for 3d transforms, and is handled separately below. - bool is2D = effectiveTransform.CanDraw2D(&transform); + bool is2D = paintContext.Apply2DTransform(); NS_ABORT_IF_FALSE(is2D || needsGroup || !aLayer->GetFirstChild(), "Must PushGroup for 3d transforms!"); - if (is2D) { - aTarget->SetMatrix(transform); - } else { - aTarget->SetMatrix(gfxMatrix()); - } const nsIntRegion& visibleRegion = aLayer->GetEffectiveVisibleRegion(); // If needsGroup is true, we'll clip to the visible region after we've popped the group @@ -750,181 +910,68 @@ BasicLayerManager::PaintLayer(gfxContext* aTarget, // Don't need to clip to visible region again needsClipToVisibleRegion = false; } - - bool pushedTargetOpaqueRect = false; - nsRefPtr currentSurface = aTarget->CurrentSurface(); - DrawTarget *dt = aTarget->GetDrawTarget(); - const nsIntRect& bounds = visibleRegion.GetBounds(); if (is2D) { - if (aTarget->IsCairo()) { - const gfxRect& targetOpaqueRect = currentSurface->GetOpaqueRect(); - - // Try to annotate currentSurface with a region of pixels that have been - // (or will be) painted opaque, if no such region is currently set. - if (targetOpaqueRect.IsEmpty() && visibleRegion.GetNumRects() == 1 && - (aLayer->GetContentFlags() & Layer::CONTENT_OPAQUE) && - !transform.HasNonAxisAlignedTransform()) { - currentSurface->SetOpaqueRect( - aTarget->UserToDevice(gfxRect(bounds.x, bounds.y, bounds.width, bounds.height))); - pushedTargetOpaqueRect = true; - } - } else { - const IntRect& targetOpaqueRect = dt->GetOpaqueRect(); - - // Try to annotate currentSurface with a region of pixels that have been - // (or will be) painted opaque, if no such region is currently set. - if (targetOpaqueRect.IsEmpty() && visibleRegion.GetNumRects() == 1 && - (aLayer->GetContentFlags() & Layer::CONTENT_OPAQUE) && - !transform.HasNonAxisAlignedTransform()) { - - gfx::Rect opaqueRect = dt->GetTransform().TransformBounds( - gfx::Rect(bounds.x, bounds.y, bounds.width, bounds.height)); - opaqueRect.RoundIn(); - IntRect intOpaqueRect; - if (opaqueRect.ToIntRect(&intOpaqueRect)) { - aTarget->GetDrawTarget()->SetOpaqueRect(intOpaqueRect); - pushedTargetOpaqueRect = true; - } - } - } + paintContext.AnnotateOpaqueRect(); } - nsRefPtr groupTarget; - nsRefPtr untransformedSurface; bool clipIsEmpty = !aTarget || aTarget->GetClipExtents().IsEmpty(); - if (!is2D && !clipIsEmpty) { - untransformedSurface = - gfxPlatform::GetPlatform()->CreateOffscreenSurface(gfxIntSize(bounds.width, bounds.height), + if (clipIsEmpty) { + PaintSelfOrChildren(paintContext, aTarget); + return; + } + + if (is2D) { + if (needsGroup) { + nsRefPtr groupTarget = PushGroupForLayer(aTarget, aLayer, aLayer->GetEffectiveVisibleRegion(), + &needsClipToVisibleRegion); + PaintSelfOrChildren(paintContext, groupTarget); + PopGroupToSourceWithCachedSurface(aTarget, groupTarget); + FlushGroup(paintContext, needsClipToVisibleRegion); + } else { + PaintSelfOrChildren(paintContext, aTarget); + } + } else { + const nsIntRect& bounds = visibleRegion.GetBounds(); + nsRefPtr untransformedSurface = + gfxPlatform::GetPlatform()->CreateOffscreenSurface(gfxIntSize(bounds.width, bounds.height), gfxASurface::CONTENT_COLOR_ALPHA); if (!untransformedSurface) { - if (pushedTargetOpaqueRect) { - if (aTarget->IsCairo()) { - currentSurface->SetOpaqueRect(gfxRect(0, 0, 0, 0)); - } else { - dt->SetOpaqueRect(IntRect()); - } - } - NS_ASSERTION(needsSaveRestore, "Should always need to restore with 3d transforms!"); - aTarget->Restore(); return; } untransformedSurface->SetDeviceOffset(gfxPoint(-bounds.x, -bounds.y)); - groupTarget = new gfxContext(untransformedSurface); - } else if (needsGroup && !clipIsEmpty) { - groupTarget = PushGroupForLayer(aTarget, aLayer, aLayer->GetEffectiveVisibleRegion(), - &needsClipToVisibleRegion); - } else { - groupTarget = aTarget; - } + nsRefPtr groupTarget = new gfxContext(untransformedSurface); - if (aLayer->GetFirstChild() && - aLayer->GetMaskLayer() && - HasShadowManager()) { - // 'paint' the mask so that it gets sent to the shadow layer tree - static_cast(aLayer->GetMaskLayer()->ImplData()) - ->Paint(nullptr, nullptr); - } + PaintSelfOrChildren(paintContext, groupTarget); - /* Only paint ourself, or our children - This optimization relies on this! */ - Layer* child = aLayer->GetFirstChild(); - if (!child) { -#ifdef MOZ_LAYERS_HAVE_LOG - MOZ_LAYERS_LOG(("%s (0x%p) is covered: %i\n", __FUNCTION__, - (void*)aLayer, data->IsHidden())); -#endif - if (aLayer->AsThebesLayer()) { - data->PaintThebes(groupTarget, - aLayer->GetMaskLayer(), - aCallback, aCallbackData, - aReadback); - } else { - data->Paint(groupTarget, aLayer->GetMaskLayer()); - } - } else { - ReadbackProcessor readback; - ContainerLayer* container = static_cast(aLayer); - if (IsRetained()) { - readback.BuildUpdates(container); - } - - nsAutoTArray children; - container->SortChildrenBy3DZOrder(children); - - for (uint32_t i = 0; i < children.Length(); i++) { - PaintLayer(groupTarget, children.ElementAt(i), aCallback, aCallbackData, &readback); - if (mTransactionIncomplete) - break; - } - } - - if (needsGroup) { - bool blitComplete = false; - if (is2D) { - PopGroupToSourceWithCachedSurface(aTarget, groupTarget); - } else { - // Temporary fast fix for bug 725886 - // Revert these changes when 725886 is ready - if (!clipIsEmpty) { - NS_ABORT_IF_FALSE(untransformedSurface, - "We should always allocate an untransformed surface with 3d transforms!"); - gfxPoint offset; - bool dontBlit = needsClipToVisibleRegion || mTransactionIncomplete || - aLayer->GetEffectiveOpacity() != 1.0f; + // Temporary fast fix for bug 725886 + // Revert these changes when 725886 is ready + NS_ABORT_IF_FALSE(untransformedSurface, + "We should always allocate an untransformed surface with 3d transforms!"); + gfxPoint offset; + bool dontBlit = needsClipToVisibleRegion || mTransactionIncomplete || + aLayer->GetEffectiveOpacity() != 1.0f; #ifdef DEBUG - if (aLayer->GetDebugColorIndex() != 0) { - gfxRGBA color((aLayer->GetDebugColorIndex() & 1) ? 1.0 : 0.0, - (aLayer->GetDebugColorIndex() & 2) ? 1.0 : 0.0, - (aLayer->GetDebugColorIndex() & 4) ? 1.0 : 0.0, - 1.0); + if (aLayer->GetDebugColorIndex() != 0) { + gfxRGBA color((aLayer->GetDebugColorIndex() & 1) ? 1.0 : 0.0, + (aLayer->GetDebugColorIndex() & 2) ? 1.0 : 0.0, + (aLayer->GetDebugColorIndex() & 4) ? 1.0 : 0.0, + 1.0); - nsRefPtr temp = new gfxContext(untransformedSurface); - temp->SetColor(color); - temp->Paint(); - } + nsRefPtr temp = new gfxContext(untransformedSurface); + temp->SetColor(color); + temp->Paint(); + } #endif + const gfx3DMatrix& effectiveTransform = aLayer->GetEffectiveTransform(); + nsRefPtr result = + Transform3D(untransformedSurface, aTarget, bounds, + effectiveTransform, offset, dontBlit); - nsRefPtr result = - Transform3D(untransformedSurface, aTarget, bounds, - effectiveTransform, offset, dontBlit); - - blitComplete = !result; - if (result) { - aTarget->SetSource(result, offset); - } - } + if (result) { + aTarget->SetSource(result, offset); + FlushGroup(paintContext, needsClipToVisibleRegion); } - // If we're doing our own double-buffering, we need to avoid drawing - // the results of an incomplete transaction to the destination surface --- - // that could cause flicker. Double-buffering is implemented using a - // temporary surface for one or more container layers, so we need to stop - // those temporary surfaces from being composited to aTarget. - // ApplyDoubleBuffering guarantees that this container layer can't - // intersect any other leaf layers, so if the transaction is not yet marked - // incomplete, the contents of this container layer are the final contents - // for the window. - if (!mTransactionIncomplete && !blitComplete) { - if (needsClipToVisibleRegion) { - gfxUtils::ClipToRegion(aTarget, aLayer->GetEffectiveVisibleRegion()); - } - AutoSetOperator setOperator(aTarget, container->GetOperator()); - PaintWithMask(aTarget, aLayer->GetEffectiveOpacity(), - HasShadowManager() ? nullptr : aLayer->GetMaskLayer()); - } - } - - if (pushedTargetOpaqueRect) { - if (aTarget->IsCairo()) { - currentSurface->SetOpaqueRect(gfxRect(0, 0, 0, 0)); - } else { - dt->SetOpaqueRect(IntRect()); - } - } - - if (needsSaveRestore) { - aTarget->Restore(); - } else { - aTarget->SetMatrix(savedMatrix); } } diff --git a/gfx/layers/basic/BasicLayers.h b/gfx/layers/basic/BasicLayers.h index cacf2118b11f..2df307a82b9a 100644 --- a/gfx/layers/basic/BasicLayers.h +++ b/gfx/layers/basic/BasicLayers.h @@ -28,6 +28,7 @@ class ShadowCanvasLayer; class ShadowColorLayer; class ReadbackProcessor; class ImageFactory; +class PaintContext; /** * This is a cairo/Thebes-only, main-thread-only implementation of layers. @@ -159,6 +160,15 @@ protected: }; TransactionPhase mPhase; + // This is the main body of the PaintLayer routine which will if it has + // children, recurse into PaintLayer() otherwise it will paint using the + // underlying Paint() method of the Layer. It will not do both. + void PaintSelfOrChildren(PaintContext& aPaintContext, gfxContext* aGroupTarget); + + // Paint the group onto the underlying target. This is used by PaintLayer to + // flush the group to the underlying target. + void FlushGroup(PaintContext& aPaintContext, bool aNeedsClipToVisibleRegion); + // Paints aLayer to mTarget. void PaintLayer(gfxContext* aTarget, Layer* aLayer,