diff options
Diffstat (limited to 'gfx/layers/composite/LayerManagerComposite.cpp')
-rw-r--r-- | gfx/layers/composite/LayerManagerComposite.cpp | 1451 |
1 files changed, 1451 insertions, 0 deletions
diff --git a/gfx/layers/composite/LayerManagerComposite.cpp b/gfx/layers/composite/LayerManagerComposite.cpp new file mode 100644 index 000000000..800478d55 --- /dev/null +++ b/gfx/layers/composite/LayerManagerComposite.cpp @@ -0,0 +1,1451 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "LayerManagerComposite.h" +#include <stddef.h> // for size_t +#include <stdint.h> // for uint16_t, uint32_t +#include "CanvasLayerComposite.h" // for CanvasLayerComposite +#include "ColorLayerComposite.h" // for ColorLayerComposite +#include "Composer2D.h" // for Composer2D +#include "CompositableHost.h" // for CompositableHost +#include "ContainerLayerComposite.h" // for ContainerLayerComposite, etc +#include "FPSCounter.h" // for FPSState, FPSCounter +#include "FrameMetrics.h" // for FrameMetrics +#include "GeckoProfiler.h" // for profiler_set_frame_number, etc +#include "ImageLayerComposite.h" // for ImageLayerComposite +#include "Layers.h" // for Layer, ContainerLayer, etc +#include "LayerScope.h" // for LayerScope Tool +#include "protobuf/LayerScopePacket.pb.h" // for protobuf (LayerScope) +#include "PaintedLayerComposite.h" // for PaintedLayerComposite +#include "TiledContentHost.h" +#include "Units.h" // for ScreenIntRect +#include "UnitTransforms.h" // for ViewAs +#include "apz/src/AsyncPanZoomController.h" // for AsyncPanZoomController +#include "gfxPrefs.h" // for gfxPrefs +#ifdef XP_MACOSX +#include "gfxPlatformMac.h" +#endif +#include "gfxRect.h" // for gfxRect +#include "gfxUtils.h" // for frame color util +#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc +#include "mozilla/RefPtr.h" // for RefPtr, already_AddRefed +#include "mozilla/gfx/2D.h" // for DrawTarget +#include "mozilla/gfx/Matrix.h" // for Matrix4x4 +#include "mozilla/gfx/Point.h" // for IntSize, Point +#include "mozilla/gfx/Rect.h" // for Rect +#include "mozilla/gfx/Types.h" // for Color, SurfaceFormat +#include "mozilla/layers/Compositor.h" // for Compositor +#include "mozilla/layers/CompositorTypes.h" +#include "mozilla/layers/Effects.h" // for Effect, EffectChain, etc +#include "mozilla/layers/LayerMetricsWrapper.h" // for LayerMetricsWrapper +#include "mozilla/layers/LayersTypes.h" // for etc +#include "mozilla/widget/CompositorWidget.h" // for WidgetRenderingContext +#include "ipc/CompositorBench.h" // for CompositorBench +#include "ipc/ShadowLayerUtils.h" +#include "mozilla/mozalloc.h" // for operator new, etc +#include "nsAppRunner.h" +#include "mozilla/RefPtr.h" // for nsRefPtr +#include "nsCOMPtr.h" // for already_AddRefed +#include "nsDebug.h" // for NS_WARNING, NS_RUNTIMEABORT, etc +#include "nsISupportsImpl.h" // for Layer::AddRef, etc +#include "nsIWidget.h" // for nsIWidget +#include "nsPoint.h" // for nsIntPoint +#include "nsRect.h" // for mozilla::gfx::IntRect +#include "nsRegion.h" // for nsIntRegion, etc +#ifdef MOZ_WIDGET_ANDROID +#include <android/log.h> +#include <android/native_window.h> +#endif +#if defined(MOZ_WIDGET_ANDROID) +#include "opengl/CompositorOGL.h" +#include "GLContextEGL.h" +#include "GLContextProvider.h" +#include "ScopedGLHelpers.h" +#endif +#include "GeckoProfiler.h" +#include "TextRenderer.h" // for TextRenderer +#include "mozilla/layers/CompositorBridgeParent.h" +#include "TreeTraversal.h" // for ForEachNode + +#ifdef USE_SKIA +#include "PaintCounter.h" // For PaintCounter +#endif + +class gfxContext; + +namespace mozilla { +namespace layers { + +class ImageLayer; + +using namespace mozilla::gfx; +using namespace mozilla::gl; + +static LayerComposite* +ToLayerComposite(Layer* aLayer) +{ + return static_cast<LayerComposite*>(aLayer->ImplData()); +} + +static void ClearSubtree(Layer* aLayer) +{ + ForEachNode<ForwardIterator>( + aLayer, + [] (Layer* layer) + { + ToLayerComposite(layer)->CleanupResources(); + }); +} + +void +LayerManagerComposite::ClearCachedResources(Layer* aSubtree) +{ + MOZ_ASSERT(!aSubtree || aSubtree->Manager() == this); + Layer* subtree = aSubtree ? aSubtree : mRoot.get(); + if (!subtree) { + return; + } + + ClearSubtree(subtree); + // FIXME [bjacob] + // XXX the old LayerManagerOGL code had a mMaybeInvalidTree that it set to true here. + // Do we need that? +} + +/** + * LayerManagerComposite + */ +LayerManagerComposite::LayerManagerComposite(Compositor* aCompositor) +: mWarningLevel(0.0f) +, mUnusedApzTransformWarning(false) +, mDisabledApzWarning(false) +, mCompositor(aCompositor) +, mInTransaction(false) +, mIsCompositorReady(false) +, mDebugOverlayWantsNextFrame(false) +, mGeometryChanged(true) +, mLastFrameMissedHWC(false) +, mWindowOverlayChanged(false) +, mLastPaintTime(TimeDuration::Forever()) +, mRenderStartTime(TimeStamp::Now()) +{ + mTextRenderer = new TextRenderer(aCompositor); + MOZ_ASSERT(aCompositor); + +#ifdef USE_SKIA + mPaintCounter = nullptr; +#endif +} + +LayerManagerComposite::~LayerManagerComposite() +{ + Destroy(); +} + + +void +LayerManagerComposite::Destroy() +{ + if (!mDestroyed) { + mCompositor->GetWidget()->CleanupWindowEffects(); + if (mRoot) { + RootLayer()->Destroy(); + } + mRoot = nullptr; + mClonedLayerTreeProperties = nullptr; + mDestroyed = true; + +#ifdef USE_SKIA + mPaintCounter = nullptr; +#endif + } +} + +void +LayerManagerComposite::UpdateRenderBounds(const IntRect& aRect) +{ + mRenderBounds = aRect; +} + +bool +LayerManagerComposite::AreComponentAlphaLayersEnabled() +{ + return mCompositor->GetBackendType() != LayersBackend::LAYERS_BASIC && + mCompositor->SupportsEffect(EffectTypes::COMPONENT_ALPHA) && + LayerManager::AreComponentAlphaLayersEnabled(); +} + +bool +LayerManagerComposite::BeginTransaction() +{ + mInTransaction = true; + + if (!mCompositor->Ready()) { + return false; + } + + mIsCompositorReady = true; + return true; +} + +void +LayerManagerComposite::BeginTransactionWithDrawTarget(DrawTarget* aTarget, const IntRect& aRect) +{ + mInTransaction = true; + + if (!mCompositor->Ready()) { + return; + } + +#ifdef MOZ_LAYERS_HAVE_LOG + MOZ_LAYERS_LOG(("[----- BeginTransaction")); + Log(); +#endif + + if (mDestroyed) { + NS_WARNING("Call on destroyed layer manager"); + return; + } + + mIsCompositorReady = true; + mCompositor->SetTargetContext(aTarget, aRect); + mTarget = aTarget; + mTargetBounds = aRect; +} + +/** + * Get accumulated transform of from the context creating layer to the + * given layer. + */ +static Matrix4x4 +GetAccTransformIn3DContext(Layer* aLayer) { + Matrix4x4 transform = aLayer->GetLocalTransform(); + for (Layer* layer = aLayer->GetParent(); + layer && layer->Extend3DContext(); + layer = layer->GetParent()) { + transform = transform * layer->GetLocalTransform(); + } + return transform; +} + +void +LayerManagerComposite::PostProcessLayers(Layer* aLayer, + nsIntRegion& aOpaqueRegion, + LayerIntRegion& aVisibleRegion, + const Maybe<ParentLayerIntRect>& aClipFromAncestors) +{ + if (aLayer->Extend3DContext()) { + // For layers participating 3D rendering context, their visible + // region should be empty (invisible), so we pass through them + // without doing anything. + + // Direct children of the establisher may have a clip, becaue the + // item containing it; ex. of nsHTMLScrollFrame, may give it one. + Maybe<ParentLayerIntRect> layerClip = + aLayer->AsLayerComposite()->GetShadowClipRect(); + Maybe<ParentLayerIntRect> ancestorClipForChildren = + IntersectMaybeRects(layerClip, aClipFromAncestors); + MOZ_ASSERT(!layerClip || !aLayer->Combines3DTransformWithAncestors(), + "Only direct children of the establisher could have a clip"); + + for (Layer* child = aLayer->GetLastChild(); + child; + child = child->GetPrevSibling()) { + PostProcessLayers(child, aOpaqueRegion, aVisibleRegion, + ancestorClipForChildren); + } + return; + } + + nsIntRegion localOpaque; + // Treat layers on the path to the root of the 3D rendering context as + // a giant layer if it is a leaf. + Matrix4x4 transform = GetAccTransformIn3DContext(aLayer); + Matrix transform2d; + Maybe<IntPoint> integerTranslation; + // If aLayer has a simple transform (only an integer translation) then we + // can easily convert aOpaqueRegion into pre-transform coordinates and include + // that region. + if (transform.Is2D(&transform2d)) { + if (transform2d.IsIntegerTranslation()) { + integerTranslation = Some(IntPoint::Truncate(transform2d.GetTranslation())); + localOpaque = aOpaqueRegion; + localOpaque.MoveBy(-*integerTranslation); + } + } + + // Compute a clip that's the combination of our layer clip with the clip + // from our ancestors. + LayerComposite* composite = aLayer->AsLayerComposite(); + Maybe<ParentLayerIntRect> layerClip = composite->GetShadowClipRect(); + MOZ_ASSERT(!layerClip || !aLayer->Combines3DTransformWithAncestors(), + "The layer with a clip should not participate " + "a 3D rendering context"); + Maybe<ParentLayerIntRect> outsideClip = + IntersectMaybeRects(layerClip, aClipFromAncestors); + + // Convert the combined clip into our pre-transform coordinate space, so + // that it can later be intersected with our visible region. + // If our transform is a perspective, there's no meaningful insideClip rect + // we can compute (it would need to be a cone). + Maybe<LayerIntRect> insideClip; + if (outsideClip && !transform.HasPerspectiveComponent()) { + Matrix4x4 inverse = transform; + if (inverse.Invert()) { + Maybe<LayerRect> insideClipFloat = + UntransformBy(ViewAs<ParentLayerToLayerMatrix4x4>(inverse), + ParentLayerRect(*outsideClip), + LayerRect::MaxIntRect()); + if (insideClipFloat) { + insideClipFloat->RoundOut(); + LayerIntRect insideClipInt; + if (insideClipFloat->ToIntRect(&insideClipInt)) { + insideClip = Some(insideClipInt); + } + } + } + } + + Maybe<ParentLayerIntRect> ancestorClipForChildren; + if (insideClip) { + ancestorClipForChildren = + Some(ViewAs<ParentLayerPixel>(*insideClip, PixelCastJustification::MovingDownToChildren)); + } + + // Save the value of localOpaque, which currently stores the region obscured + // by siblings (and uncles and such), before our descendants contribute to it. + nsIntRegion obscured = localOpaque; + + // Recurse on our descendants, in front-to-back order. In this process: + // - Occlusions are computed for them, and they contribute to localOpaque. + // - They recalculate their visible regions, taking ancestorClipForChildren + // into account, and accumulate them into descendantsVisibleRegion. + LayerIntRegion descendantsVisibleRegion; + bool hasPreserve3DChild = false; + for (Layer* child = aLayer->GetLastChild(); child; child = child->GetPrevSibling()) { + PostProcessLayers(child, localOpaque, descendantsVisibleRegion, ancestorClipForChildren); + if (child->Extend3DContext()) { + hasPreserve3DChild = true; + } + } + + // Recalculate our visible region. + LayerIntRegion visible = composite->GetShadowVisibleRegion(); + + // If we have descendants, throw away the visible region stored on this + // layer, and use the region accumulated by our descendants instead. + if (aLayer->GetFirstChild() && !hasPreserve3DChild) { + visible = descendantsVisibleRegion; + } + + // Subtract any areas that we know to be opaque. + if (!obscured.IsEmpty()) { + visible.SubOut(LayerIntRegion::FromUnknownRegion(obscured)); + } + + // Clip the visible region using the combined clip. + if (insideClip) { + visible.AndWith(*insideClip); + } + composite->SetShadowVisibleRegion(visible); + + // Transform the newly calculated visible region into our parent's space, + // apply our clip to it (if any), and accumulate it into |aVisibleRegion| + // for the caller to use. + ParentLayerIntRegion visibleParentSpace = TransformBy( + ViewAs<LayerToParentLayerMatrix4x4>(transform), visible); + if (const Maybe<ParentLayerIntRect>& clipRect = composite->GetShadowClipRect()) { + visibleParentSpace.AndWith(*clipRect); + } + aVisibleRegion.OrWith(ViewAs<LayerPixel>(visibleParentSpace, + PixelCastJustification::MovingDownToChildren)); + + // If we have a simple transform, then we can add our opaque area into + // aOpaqueRegion. + if (integerTranslation && + !aLayer->HasMaskLayers() && + aLayer->IsOpaqueForVisibility()) { + if (aLayer->IsOpaque()) { + localOpaque.OrWith(composite->GetFullyRenderedRegion()); + } + localOpaque.MoveBy(*integerTranslation); + if (layerClip) { + localOpaque.AndWith(layerClip->ToUnknownRect()); + } + aOpaqueRegion.OrWith(localOpaque); + } +} + +void +LayerManagerComposite::EndTransaction(const TimeStamp& aTimeStamp, + EndTransactionFlags aFlags) +{ + NS_ASSERTION(mInTransaction, "Didn't call BeginTransaction?"); + NS_ASSERTION(!(aFlags & END_NO_COMPOSITE), + "Shouldn't get END_NO_COMPOSITE here"); + mInTransaction = false; + mRenderStartTime = TimeStamp::Now(); + + if (!mIsCompositorReady) { + return; + } + mIsCompositorReady = false; + +#ifdef MOZ_LAYERS_HAVE_LOG + MOZ_LAYERS_LOG((" ----- (beginning paint)")); + Log(); +#endif + + if (mDestroyed) { + NS_WARNING("Call on destroyed layer manager"); + return; + } + + // Set composition timestamp here because we need it in + // ComputeEffectiveTransforms (so the correct video frame size is picked) and + // also to compute invalid regions properly. + mCompositor->SetCompositionTime(aTimeStamp); + + if (mRoot && !(aFlags & END_NO_IMMEDIATE_REDRAW)) { + MOZ_ASSERT(!aTimeStamp.IsNull()); + UpdateAndRender(); + mCompositor->FlushPendingNotifyNotUsed(); + } else { + // Modified the layer tree. + mGeometryChanged = true; + } + + mCompositor->ClearTargetContext(); + mTarget = nullptr; + +#ifdef MOZ_LAYERS_HAVE_LOG + Log(); + MOZ_LAYERS_LOG(("]----- EndTransaction")); +#endif +} + +void +LayerManagerComposite::UpdateAndRender() +{ + nsIntRegion invalid; + bool didEffectiveTransforms = false; + + nsIntRegion opaque; + LayerIntRegion visible; + PostProcessLayers(mRoot, opaque, visible, Nothing()); + + if (mClonedLayerTreeProperties) { + // Effective transforms are needed by ComputeDifferences(). + mRoot->ComputeEffectiveTransforms(gfx::Matrix4x4()); + didEffectiveTransforms = true; + + // We need to compute layer tree differences even if we're not going to + // immediately use the resulting damage area, since ComputeDifferences + // is also responsible for invalidates intermediate surfaces in + // ContainerLayers. + nsIntRegion changed = mClonedLayerTreeProperties->ComputeDifferences(mRoot, nullptr, &mGeometryChanged); + + if (mTarget) { + // Since we're composing to an external target, we're not going to use + // the damage region from layers changes - we want to composite + // everything in the target bounds. Instead we accumulate the layers + // damage region for the next window composite. + mInvalidRegion.Or(mInvalidRegion, changed); + } else { + invalid = Move(changed); + } + } + + if (mTarget) { + invalid.Or(invalid, mTargetBounds); + } else { + // If we didn't have a previous layer tree, invalidate the entire render + // area. + if (!mClonedLayerTreeProperties) { + invalid.Or(invalid, mRenderBounds); + } + + // Add any additional invalid rects from the window manager or previous + // damage computed during ComposeToTarget(). + invalid.Or(invalid, mInvalidRegion); + mInvalidRegion.SetEmpty(); + } + + if (invalid.IsEmpty() && !mWindowOverlayChanged) { + // Composition requested, but nothing has changed. Don't do any work. + mClonedLayerTreeProperties = LayerProperties::CloneFrom(GetRoot()); + return; + } + + // We don't want our debug overlay to cause more frames to happen + // so we will invalidate after we've decided if something changed. + InvalidateDebugOverlay(invalid, mRenderBounds); + + if (!didEffectiveTransforms) { + // The results of our drawing always go directly into a pixel buffer, + // so we don't need to pass any global transform here. + mRoot->ComputeEffectiveTransforms(gfx::Matrix4x4()); + } + + Render(invalid, opaque); +#if defined(MOZ_WIDGET_ANDROID) + RenderToPresentationSurface(); +#endif + mGeometryChanged = false; + mWindowOverlayChanged = false; + + // Update cached layer tree information. + mClonedLayerTreeProperties = LayerProperties::CloneFrom(GetRoot()); +} + +already_AddRefed<DrawTarget> +LayerManagerComposite::CreateOptimalMaskDrawTarget(const IntSize &aSize) +{ + NS_RUNTIMEABORT("Should only be called on the drawing side"); + return nullptr; +} + +already_AddRefed<PaintedLayer> +LayerManagerComposite::CreatePaintedLayer() +{ + MOZ_ASSERT(gIsGtest, "Unless you're testing the compositor using GTest," + "this should only be called on the drawing side"); + RefPtr<PaintedLayer> layer = new PaintedLayerComposite(this); + return layer.forget(); +} + +already_AddRefed<ContainerLayer> +LayerManagerComposite::CreateContainerLayer() +{ + MOZ_ASSERT(gIsGtest, "Unless you're testing the compositor using GTest," + "this should only be called on the drawing side"); + RefPtr<ContainerLayer> layer = new ContainerLayerComposite(this); + return layer.forget(); +} + +already_AddRefed<ImageLayer> +LayerManagerComposite::CreateImageLayer() +{ + NS_RUNTIMEABORT("Should only be called on the drawing side"); + return nullptr; +} + +already_AddRefed<ColorLayer> +LayerManagerComposite::CreateColorLayer() +{ + MOZ_ASSERT(gIsGtest, "Unless you're testing the compositor using GTest," + "this should only be called on the drawing side"); + RefPtr<ColorLayer> layer = new ColorLayerComposite(this); + return layer.forget(); +} + +already_AddRefed<CanvasLayer> +LayerManagerComposite::CreateCanvasLayer() +{ + NS_RUNTIMEABORT("Should only be called on the drawing side"); + return nullptr; +} + +LayerComposite* +LayerManagerComposite::RootLayer() const +{ + if (mDestroyed) { + NS_WARNING("Call on destroyed layer manager"); + return nullptr; + } + + return ToLayerComposite(mRoot); +} + +#ifdef MOZ_PROFILING +// Only build the QR feature when profiling to avoid bloating +// our data section. +// This table was generated using qrencode and is a binary +// encoding of the qrcodes 0-255. +#include "qrcode_table.h" +#endif + +void +LayerManagerComposite::InvalidateDebugOverlay(nsIntRegion& aInvalidRegion, const IntRect& aBounds) +{ + bool drawFps = gfxPrefs::LayersDrawFPS(); + bool drawFrameCounter = gfxPrefs::DrawFrameCounter(); + bool drawFrameColorBars = gfxPrefs::CompositorDrawColorBars(); + + if (drawFps || drawFrameCounter) { + aInvalidRegion.Or(aInvalidRegion, nsIntRect(0, 0, 256, 256)); + } + if (drawFrameColorBars) { + aInvalidRegion.Or(aInvalidRegion, nsIntRect(0, 0, 10, aBounds.height)); + } + +#ifdef USE_SKIA + bool drawPaintTimes = gfxPrefs::AlwaysPaint(); + if (drawPaintTimes) { + aInvalidRegion.Or(aInvalidRegion, nsIntRect(PaintCounter::GetPaintRect())); + } +#endif +} + +#ifdef USE_SKIA +void +LayerManagerComposite::DrawPaintTimes(Compositor* aCompositor) +{ + if (!mPaintCounter) { + mPaintCounter = new PaintCounter(); + } + + TimeDuration compositeTime = TimeStamp::Now() - mRenderStartTime; + mPaintCounter->Draw(aCompositor, mLastPaintTime, compositeTime); +} +#endif + +static uint16_t sFrameCount = 0; +void +LayerManagerComposite::RenderDebugOverlay(const IntRect& aBounds) +{ + bool drawFps = gfxPrefs::LayersDrawFPS(); + bool drawFrameCounter = gfxPrefs::DrawFrameCounter(); + bool drawFrameColorBars = gfxPrefs::CompositorDrawColorBars(); + + TimeStamp now = TimeStamp::Now(); + + if (drawFps) { + if (!mFPS) { + mFPS = MakeUnique<FPSState>(); + } + + float alpha = 1; +#ifdef ANDROID + // Draw a translation delay warning overlay + int width; + int border; + if (!mWarnTime.IsNull() && (now - mWarnTime).ToMilliseconds() < kVisualWarningDuration) { + EffectChain effects; + + // Black blorder + border = 4; + width = 6; + effects.mPrimaryEffect = new EffectSolidColor(gfx::Color(0, 0, 0, 1)); + mCompositor->DrawQuad(gfx::Rect(border, border, aBounds.width - 2 * border, width), + aBounds, effects, alpha, gfx::Matrix4x4()); + mCompositor->DrawQuad(gfx::Rect(border, aBounds.height - border - width, aBounds.width - 2 * border, width), + aBounds, effects, alpha, gfx::Matrix4x4()); + mCompositor->DrawQuad(gfx::Rect(border, border + width, width, aBounds.height - 2 * border - width * 2), + aBounds, effects, alpha, gfx::Matrix4x4()); + mCompositor->DrawQuad(gfx::Rect(aBounds.width - border - width, border + width, width, aBounds.height - 2 * border - 2 * width), + aBounds, effects, alpha, gfx::Matrix4x4()); + + // Content + border = 5; + width = 4; + effects.mPrimaryEffect = new EffectSolidColor(gfx::Color(1, 1.f - mWarningLevel, 0, 1)); + mCompositor->DrawQuad(gfx::Rect(border, border, aBounds.width - 2 * border, width), + aBounds, effects, alpha, gfx::Matrix4x4()); + mCompositor->DrawQuad(gfx::Rect(border, aBounds.height - border - width, aBounds.width - 2 * border, width), + aBounds, effects, alpha, gfx::Matrix4x4()); + mCompositor->DrawQuad(gfx::Rect(border, border + width, width, aBounds.height - 2 * border - width * 2), + aBounds, effects, alpha, gfx::Matrix4x4()); + mCompositor->DrawQuad(gfx::Rect(aBounds.width - border - width, border + width, width, aBounds.height - 2 * border - 2 * width), + aBounds, effects, alpha, gfx::Matrix4x4()); + SetDebugOverlayWantsNextFrame(true); + } +#endif + + float fillRatio = mCompositor->GetFillRatio(); + mFPS->DrawFPS(now, drawFrameColorBars ? 10 : 1, 2, unsigned(fillRatio), mCompositor); + + if (mUnusedApzTransformWarning) { + // If we have an unused APZ transform on this composite, draw a 20x20 red box + // in the top-right corner + EffectChain effects; + effects.mPrimaryEffect = new EffectSolidColor(gfx::Color(1, 0, 0, 1)); + mCompositor->DrawQuad(gfx::Rect(aBounds.width - 20, 0, 20, 20), + aBounds, effects, alpha, gfx::Matrix4x4()); + + mUnusedApzTransformWarning = false; + SetDebugOverlayWantsNextFrame(true); + } + if (mDisabledApzWarning) { + // If we have a disabled APZ on this composite, draw a 20x20 yellow box + // in the top-right corner, to the left of the unused-apz-transform + // warning box + EffectChain effects; + effects.mPrimaryEffect = new EffectSolidColor(gfx::Color(1, 1, 0, 1)); + mCompositor->DrawQuad(gfx::Rect(aBounds.width - 40, 0, 20, 20), + aBounds, effects, alpha, gfx::Matrix4x4()); + + mDisabledApzWarning = false; + SetDebugOverlayWantsNextFrame(true); + } + + + // Each frame is invalidate by the previous frame for simplicity + } else { + mFPS = nullptr; + } + + if (drawFrameColorBars) { + gfx::IntRect sideRect(0, 0, 10, aBounds.height); + + EffectChain effects; + effects.mPrimaryEffect = new EffectSolidColor(gfxUtils::GetColorForFrameNumber(sFrameCount)); + mCompositor->DrawQuad(Rect(sideRect), + sideRect, + effects, + 1.0, + gfx::Matrix4x4()); + + } + +#ifdef MOZ_PROFILING + if (drawFrameCounter) { + profiler_set_frame_number(sFrameCount); + const char* qr = sQRCodeTable[sFrameCount%256]; + + int size = 21; + int padding = 2; + float opacity = 1.0; + const uint16_t bitWidth = 5; + gfx::IntRect clip(0,0, bitWidth*640, bitWidth*640); + + // Draw the white squares at once + gfx::Color bitColor(1.0, 1.0, 1.0, 1.0); + EffectChain effects; + effects.mPrimaryEffect = new EffectSolidColor(bitColor); + int totalSize = (size + padding * 2) * bitWidth; + mCompositor->DrawQuad(gfx::Rect(0, 0, totalSize, totalSize), + clip, + effects, + opacity, + gfx::Matrix4x4()); + + // Draw a black square for every bit set in qr[index] + effects.mPrimaryEffect = new EffectSolidColor(gfx::Color(0, 0, 0, 1.0)); + for (int y = 0; y < size; y++) { + for (int x = 0; x < size; x++) { + // Select the right bit from the binary encoding + int currBit = 128 >> ((x + y * 21) % 8); + int i = (x + y * 21) / 8; + if (qr[i] & currBit) { + mCompositor->DrawQuad(gfx::Rect(bitWidth * (x + padding), + bitWidth * (y + padding), + bitWidth, bitWidth), + clip, + effects, + opacity, + gfx::Matrix4x4()); + } + } + } + + } +#endif + + if (drawFrameColorBars || drawFrameCounter) { + // We intentionally overflow at 2^16. + sFrameCount++; + } + +#ifdef USE_SKIA + bool drawPaintTimes = gfxPrefs::AlwaysPaint(); + if (drawPaintTimes) { + DrawPaintTimes(mCompositor); + } +#endif +} + +RefPtr<CompositingRenderTarget> +LayerManagerComposite::PushGroupForLayerEffects() +{ + // This is currently true, so just making sure that any new use of this + // method is flagged for investigation + MOZ_ASSERT(gfxPrefs::LayersEffectInvert() || + gfxPrefs::LayersEffectGrayscale() || + gfxPrefs::LayersEffectContrast() != 0.0); + + RefPtr<CompositingRenderTarget> previousTarget = mCompositor->GetCurrentRenderTarget(); + // make our render target the same size as the destination target + // so that we don't have to change size if the drawing area changes. + IntRect rect(previousTarget->GetOrigin(), previousTarget->GetSize()); + // XXX: I'm not sure if this is true or not... + MOZ_ASSERT(rect.x == 0 && rect.y == 0); + if (!mTwoPassTmpTarget || + mTwoPassTmpTarget->GetSize() != previousTarget->GetSize() || + mTwoPassTmpTarget->GetOrigin() != previousTarget->GetOrigin()) { + mTwoPassTmpTarget = mCompositor->CreateRenderTarget(rect, INIT_MODE_NONE); + } + MOZ_ASSERT(mTwoPassTmpTarget); + mCompositor->SetRenderTarget(mTwoPassTmpTarget); + return previousTarget; +} +void +LayerManagerComposite::PopGroupForLayerEffects(RefPtr<CompositingRenderTarget> aPreviousTarget, + IntRect aClipRect, + bool aGrayscaleEffect, + bool aInvertEffect, + float aContrastEffect) +{ + MOZ_ASSERT(mTwoPassTmpTarget); + + // This is currently true, so just making sure that any new use of this + // method is flagged for investigation + MOZ_ASSERT(aInvertEffect || aGrayscaleEffect || aContrastEffect != 0.0); + + mCompositor->SetRenderTarget(aPreviousTarget); + + EffectChain effectChain(RootLayer()); + Matrix5x4 effectMatrix; + if (aGrayscaleEffect) { + // R' = G' = B' = luminance + // R' = 0.2126*R + 0.7152*G + 0.0722*B + // G' = 0.2126*R + 0.7152*G + 0.0722*B + // B' = 0.2126*R + 0.7152*G + 0.0722*B + Matrix5x4 grayscaleMatrix(0.2126f, 0.2126f, 0.2126f, 0, + 0.7152f, 0.7152f, 0.7152f, 0, + 0.0722f, 0.0722f, 0.0722f, 0, + 0, 0, 0, 1, + 0, 0, 0, 0); + effectMatrix = grayscaleMatrix; + } + + if (aInvertEffect) { + // R' = 1 - R + // G' = 1 - G + // B' = 1 - B + Matrix5x4 colorInvertMatrix(-1, 0, 0, 0, + 0, -1, 0, 0, + 0, 0, -1, 0, + 0, 0, 0, 1, + 1, 1, 1, 0); + effectMatrix = effectMatrix * colorInvertMatrix; + } + + if (aContrastEffect != 0.0) { + // Multiplying with: + // R' = (1 + c) * (R - 0.5) + 0.5 + // G' = (1 + c) * (G - 0.5) + 0.5 + // B' = (1 + c) * (B - 0.5) + 0.5 + float cP1 = aContrastEffect + 1; + float hc = 0.5*aContrastEffect; + Matrix5x4 contrastMatrix( cP1, 0, 0, 0, + 0, cP1, 0, 0, + 0, 0, cP1, 0, + 0, 0, 0, 1, + -hc, -hc, -hc, 0); + effectMatrix = effectMatrix * contrastMatrix; + } + + effectChain.mPrimaryEffect = new EffectRenderTarget(mTwoPassTmpTarget); + effectChain.mSecondaryEffects[EffectTypes::COLOR_MATRIX] = new EffectColorMatrix(effectMatrix); + + mCompositor->DrawQuad(Rect(Point(0, 0), Size(mTwoPassTmpTarget->GetSize())), aClipRect, effectChain, 1., + Matrix4x4()); +} + +// Used to clear the 'mLayerComposited' flag at the beginning of each Render(). +static void +ClearLayerFlags(Layer* aLayer) { + ForEachNode<ForwardIterator>( + aLayer, + [] (Layer* layer) + { + if (layer->AsLayerComposite()) { + layer->AsLayerComposite()->SetLayerComposited(false); + } + }); +} + +void +LayerManagerComposite::Render(const nsIntRegion& aInvalidRegion, const nsIntRegion& aOpaqueRegion) +{ + PROFILER_LABEL("LayerManagerComposite", "Render", + js::ProfileEntry::Category::GRAPHICS); + + if (mDestroyed || !mCompositor || mCompositor->IsDestroyed()) { + NS_WARNING("Call on destroyed layer manager"); + return; + } + + ClearLayerFlags(mRoot); + + // At this time, it doesn't really matter if these preferences change + // during the execution of the function; we should be safe in all + // permutations. However, may as well just get the values onces and + // then use them, just in case the consistency becomes important in + // the future. + bool invertVal = gfxPrefs::LayersEffectInvert(); + bool grayscaleVal = gfxPrefs::LayersEffectGrayscale(); + float contrastVal = gfxPrefs::LayersEffectContrast(); + bool haveLayerEffects = (invertVal || grayscaleVal || contrastVal != 0.0); + + // Set LayerScope begin/end frame + LayerScopeAutoFrame frame(PR_Now()); + + // Dump to console + if (gfxPrefs::LayersDump()) { + this->Dump(/* aSorted= */true); + } else if (profiler_feature_active("layersdump")) { + std::stringstream ss; + Dump(ss); + profiler_log(ss.str().c_str()); + } + + // Dump to LayerScope Viewer + if (LayerScope::CheckSendable()) { + // Create a LayersPacket, dump Layers into it and transfer the + // packet('s ownership) to LayerScope. + auto packet = MakeUnique<layerscope::Packet>(); + layerscope::LayersPacket* layersPacket = packet->mutable_layers(); + this->Dump(layersPacket); + LayerScope::SendLayerDump(Move(packet)); + } + + /** Our more efficient but less powerful alter ego, if one is available. */ + RefPtr<Composer2D> composer2D; + composer2D = mCompositor->GetWidget()->GetComposer2D(); + + // We can't use composert2D if we have layer effects + if (!mTarget && !haveLayerEffects && + gfxPrefs::Composer2DCompositionEnabled() && + composer2D && composer2D->HasHwc() && composer2D->TryRenderWithHwc(mRoot, + mCompositor->GetWidget()->RealWidget(), + mGeometryChanged, + mCompositor->HasImageHostOverlays())) + { + LayerScope::SetHWComposed(); + if (mFPS) { + double fps = mFPS->mCompositionFps.AddFrameAndGetFps(TimeStamp::Now()); + if (gfxPrefs::LayersDrawFPS()) { + printf_stderr("HWComposer: FPS is %g\n", fps); + } + } + mCompositor->EndFrameForExternalComposition(Matrix()); + mLastFrameMissedHWC = false; + return; + } else if (!mTarget && !haveLayerEffects) { + mLastFrameMissedHWC = !!composer2D; + } + + mozilla::widget::WidgetRenderingContext widgetContext; +#if defined(XP_MACOSX) + widgetContext.mLayerManager = this; +#elif defined(MOZ_WIDGET_ANDROID) + widgetContext.mCompositor = GetCompositor(); +#endif + + { + PROFILER_LABEL("LayerManagerComposite", "PreRender", + js::ProfileEntry::Category::GRAPHICS); + + if (!mCompositor->GetWidget()->PreRender(&widgetContext)) { + return; + } + } + + ParentLayerIntRect clipRect; + IntRect bounds(mRenderBounds.x, mRenderBounds.y, mRenderBounds.width, mRenderBounds.height); + IntRect actualBounds; + + CompositorBench(mCompositor, bounds); + + MOZ_ASSERT(mRoot->GetOpacity() == 1); +#if defined(MOZ_WIDGET_ANDROID) + LayerMetricsWrapper wrapper = GetRootContentLayer(); + if (wrapper) { + mCompositor->SetClearColor(wrapper.Metadata().GetBackgroundColor()); + } else { + mCompositor->SetClearColorToDefault(); + } +#endif + if (mRoot->GetClipRect()) { + clipRect = *mRoot->GetClipRect(); + IntRect rect(clipRect.x, clipRect.y, clipRect.width, clipRect.height); + mCompositor->BeginFrame(aInvalidRegion, &rect, bounds, aOpaqueRegion, nullptr, &actualBounds); + } else { + gfx::IntRect rect; + mCompositor->BeginFrame(aInvalidRegion, nullptr, bounds, aOpaqueRegion, &rect, &actualBounds); + clipRect = ParentLayerIntRect(rect.x, rect.y, rect.width, rect.height); + } + + if (actualBounds.IsEmpty()) { + mCompositor->GetWidget()->PostRender(&widgetContext); + return; + } + + // Allow widget to render a custom background. + mCompositor->GetWidget()->DrawWindowUnderlay( + &widgetContext, LayoutDeviceIntRect::FromUnknownRect(actualBounds)); + + RefPtr<CompositingRenderTarget> previousTarget; + if (haveLayerEffects) { + previousTarget = PushGroupForLayerEffects(); + } else { + mTwoPassTmpTarget = nullptr; + } + + // Render our layers. + RootLayer()->Prepare(ViewAs<RenderTargetPixel>(clipRect, PixelCastJustification::RenderTargetIsParentLayerForRoot)); + RootLayer()->RenderLayer(clipRect.ToUnknownRect()); + + if (!mRegionToClear.IsEmpty()) { + for (auto iter = mRegionToClear.RectIter(); !iter.Done(); iter.Next()) { + const IntRect& r = iter.Get(); + mCompositor->ClearRect(Rect(r.x, r.y, r.width, r.height)); + } + } + + if (mTwoPassTmpTarget) { + MOZ_ASSERT(haveLayerEffects); + PopGroupForLayerEffects(previousTarget, clipRect.ToUnknownRect(), + grayscaleVal, invertVal, contrastVal); + } + + // Allow widget to render a custom foreground. + mCompositor->GetWidget()->DrawWindowOverlay( + &widgetContext, LayoutDeviceIntRect::FromUnknownRect(actualBounds)); + + // Debugging + RenderDebugOverlay(actualBounds); + + { + PROFILER_LABEL("LayerManagerComposite", "EndFrame", + js::ProfileEntry::Category::GRAPHICS); + + mCompositor->EndFrame(); + + // Call after EndFrame() + mCompositor->SetDispAcquireFence(mRoot); + } + + if (composer2D) { + composer2D->Render(mCompositor->GetWidget()->RealWidget()); + } + + mCompositor->GetWidget()->PostRender(&widgetContext); + + RecordFrame(); +} + +#if defined(MOZ_WIDGET_ANDROID) +class ScopedCompositorProjMatrix { +public: + ScopedCompositorProjMatrix(CompositorOGL* aCompositor, const Matrix4x4& aProjMatrix): + mCompositor(aCompositor), + mOriginalProjMatrix(mCompositor->GetProjMatrix()) + { + mCompositor->SetProjMatrix(aProjMatrix); + } + + ~ScopedCompositorProjMatrix() + { + mCompositor->SetProjMatrix(mOriginalProjMatrix); + } +private: + CompositorOGL* const mCompositor; + const Matrix4x4 mOriginalProjMatrix; +}; + +class ScopedCompostitorSurfaceSize { +public: + ScopedCompostitorSurfaceSize(CompositorOGL* aCompositor, const gfx::IntSize& aSize) : + mCompositor(aCompositor), + mOriginalSize(mCompositor->GetDestinationSurfaceSize()) + { + mCompositor->SetDestinationSurfaceSize(aSize); + } + ~ScopedCompostitorSurfaceSize() + { + mCompositor->SetDestinationSurfaceSize(mOriginalSize); + } +private: + CompositorOGL* const mCompositor; + const gfx::IntSize mOriginalSize; +}; + +class ScopedCompositorRenderOffset { +public: + ScopedCompositorRenderOffset(CompositorOGL* aCompositor, const ScreenPoint& aOffset) : + mCompositor(aCompositor), + mOriginalOffset(mCompositor->GetScreenRenderOffset()) + { + mCompositor->SetScreenRenderOffset(aOffset); + } + ~ScopedCompositorRenderOffset() + { + mCompositor->SetScreenRenderOffset(mOriginalOffset); + } +private: + CompositorOGL* const mCompositor; + const ScreenPoint mOriginalOffset; +}; + +class ScopedContextSurfaceOverride { +public: + ScopedContextSurfaceOverride(GLContextEGL* aContext, void* aSurface) : + mContext(aContext) + { + MOZ_ASSERT(aSurface); + mContext->SetEGLSurfaceOverride(aSurface); + mContext->MakeCurrent(true); + } + ~ScopedContextSurfaceOverride() + { + mContext->SetEGLSurfaceOverride(EGL_NO_SURFACE); + mContext->MakeCurrent(true); + } +private: + GLContextEGL* const mContext; +}; + +void +LayerManagerComposite::RenderToPresentationSurface() +{ +#ifdef MOZ_WIDGET_ANDROID + nsIWidget* const widget = mCompositor->GetWidget()->RealWidget(); + auto window = static_cast<ANativeWindow*>( + widget->GetNativeData(NS_PRESENTATION_WINDOW)); + + if (!window) { + return; + } + + EGLSurface surface = widget->GetNativeData(NS_PRESENTATION_SURFACE); + + if (!surface) { + //create surface; + surface = GLContextProviderEGL::CreateEGLSurface(window); + if (!surface) { + return; + } + + widget->SetNativeData(NS_PRESENTATION_SURFACE, + reinterpret_cast<uintptr_t>(surface)); + } + + CompositorOGL* compositor = mCompositor->AsCompositorOGL(); + GLContext* gl = compositor->gl(); + GLContextEGL* egl = GLContextEGL::Cast(gl); + + if (!egl) { + return; + } + + const IntSize windowSize(ANativeWindow_getWidth(window), + ANativeWindow_getHeight(window)); + +#endif + + if ((windowSize.width <= 0) || (windowSize.height <= 0)) { + return; + } + + ScreenRotation rotation = compositor->GetScreenRotation(); + + const int actualWidth = windowSize.width; + const int actualHeight = windowSize.height; + + const gfx::IntSize originalSize = compositor->GetDestinationSurfaceSize(); + const nsIntRect originalRect = nsIntRect(0, 0, originalSize.width, originalSize.height); + + int pageWidth = originalSize.width; + int pageHeight = originalSize.height; + if (rotation == ROTATION_90 || rotation == ROTATION_270) { + pageWidth = originalSize.height; + pageHeight = originalSize.width; + } + + float scale = 1.0; + + if ((pageWidth > actualWidth) || (pageHeight > actualHeight)) { + const float scaleWidth = (float)actualWidth / (float)pageWidth; + const float scaleHeight = (float)actualHeight / (float)pageHeight; + scale = scaleWidth <= scaleHeight ? scaleWidth : scaleHeight; + } + + const gfx::IntSize actualSize(actualWidth, actualHeight); + ScopedCompostitorSurfaceSize overrideSurfaceSize(compositor, actualSize); + + const ScreenPoint offset((actualWidth - (int)(scale * pageWidth)) / 2, 0); + ScopedContextSurfaceOverride overrideSurface(egl, surface); + + Matrix viewMatrix = ComputeTransformForRotation(originalRect, + rotation); + viewMatrix.Invert(); // unrotate + viewMatrix.PostScale(scale, scale); + viewMatrix.PostTranslate(offset.x, offset.y); + Matrix4x4 matrix = Matrix4x4::From2D(viewMatrix); + + mRoot->ComputeEffectiveTransforms(matrix); + nsIntRegion opaque; + LayerIntRegion visible; + PostProcessLayers(mRoot, opaque, visible, Nothing()); + + nsIntRegion invalid; + IntRect bounds = IntRect::Truncate(0, 0, scale * pageWidth, actualHeight); + IntRect rect, actualBounds; + MOZ_ASSERT(mRoot->GetOpacity() == 1); + mCompositor->BeginFrame(invalid, nullptr, bounds, nsIntRegion(), &rect, &actualBounds); + + // The Java side of Fennec sets a scissor rect that accounts for + // chrome such as the URL bar. Override that so that the entire frame buffer + // is cleared. + ScopedScissorRect scissorRect(egl, 0, 0, actualWidth, actualHeight); + egl->fClearColor(0.0, 0.0, 0.0, 0.0); + egl->fClear(LOCAL_GL_COLOR_BUFFER_BIT); + + const IntRect clipRect = IntRect::Truncate(0, 0, actualWidth, actualHeight); + + RootLayer()->Prepare(RenderTargetIntRect::FromUnknownRect(clipRect)); + RootLayer()->RenderLayer(clipRect); + + mCompositor->EndFrame(); +} +#endif + +already_AddRefed<PaintedLayerComposite> +LayerManagerComposite::CreatePaintedLayerComposite() +{ + if (mDestroyed) { + NS_WARNING("Call on destroyed layer manager"); + return nullptr; + } + return RefPtr<PaintedLayerComposite>(new PaintedLayerComposite(this)).forget(); +} + +already_AddRefed<ContainerLayerComposite> +LayerManagerComposite::CreateContainerLayerComposite() +{ + if (mDestroyed) { + NS_WARNING("Call on destroyed layer manager"); + return nullptr; + } + return RefPtr<ContainerLayerComposite>(new ContainerLayerComposite(this)).forget(); +} + +already_AddRefed<ImageLayerComposite> +LayerManagerComposite::CreateImageLayerComposite() +{ + if (mDestroyed) { + NS_WARNING("Call on destroyed layer manager"); + return nullptr; + } + return RefPtr<ImageLayerComposite>(new ImageLayerComposite(this)).forget(); +} + +already_AddRefed<ColorLayerComposite> +LayerManagerComposite::CreateColorLayerComposite() +{ + if (LayerManagerComposite::mDestroyed) { + NS_WARNING("Call on destroyed layer manager"); + return nullptr; + } + return RefPtr<ColorLayerComposite>(new ColorLayerComposite(this)).forget(); +} + +already_AddRefed<CanvasLayerComposite> +LayerManagerComposite::CreateCanvasLayerComposite() +{ + if (LayerManagerComposite::mDestroyed) { + NS_WARNING("Call on destroyed layer manager"); + return nullptr; + } + return RefPtr<CanvasLayerComposite>(new CanvasLayerComposite(this)).forget(); +} + +already_AddRefed<RefLayerComposite> +LayerManagerComposite::CreateRefLayerComposite() +{ + if (LayerManagerComposite::mDestroyed) { + NS_WARNING("Call on destroyed layer manager"); + return nullptr; + } + return RefPtr<RefLayerComposite>(new RefLayerComposite(this)).forget(); +} + +LayerManagerComposite::AutoAddMaskEffect::AutoAddMaskEffect(Layer* aMaskLayer, + EffectChain& aEffects) + : mCompositable(nullptr), mFailed(false) +{ + if (!aMaskLayer) { + return; + } + + mCompositable = ToLayerComposite(aMaskLayer)->GetCompositableHost(); + if (!mCompositable) { + NS_WARNING("Mask layer with no compositable host"); + mFailed = true; + return; + } + + if (!mCompositable->AddMaskEffect(aEffects, aMaskLayer->GetEffectiveTransform())) { + mCompositable = nullptr; + mFailed = true; + } +} + +LayerManagerComposite::AutoAddMaskEffect::~AutoAddMaskEffect() +{ + if (!mCompositable) { + return; + } + + mCompositable->RemoveMaskEffect(); +} + +void +LayerManagerComposite::ChangeCompositor(Compositor* aNewCompositor) +{ + mCompositor = aNewCompositor; + mTextRenderer = new TextRenderer(aNewCompositor); + mTwoPassTmpTarget = nullptr; +} + +LayerComposite::LayerComposite(LayerManagerComposite *aManager) + : mCompositeManager(aManager) + , mCompositor(aManager->GetCompositor()) + , mShadowOpacity(1.0) + , mShadowTransformSetByAnimation(false) + , mShadowOpacitySetByAnimation(false) + , mDestroyed(false) + , mLayerComposited(false) +{ } + +LayerComposite::~LayerComposite() +{ +} + +void +LayerComposite::Destroy() +{ + if (!mDestroyed) { + mDestroyed = true; + CleanupResources(); + } +} + +void +LayerComposite::AddBlendModeEffect(EffectChain& aEffectChain) +{ + gfx::CompositionOp blendMode = GetLayer()->GetEffectiveMixBlendMode(); + if (blendMode == gfx::CompositionOp::OP_OVER) { + return; + } + + aEffectChain.mSecondaryEffects[EffectTypes::BLEND_MODE] = new EffectBlendMode(blendMode); + return; +} + +bool +LayerManagerComposite::CanUseCanvasLayerForSize(const IntSize &aSize) +{ + return mCompositor->CanUseCanvasLayerForSize(gfx::IntSize(aSize.width, + aSize.height)); +} + +void +LayerManagerComposite::NotifyShadowTreeTransaction() +{ + if (mFPS) { + mFPS->NotifyShadowTreeTransaction(); + } +} + +void +LayerComposite::SetLayerManager(LayerManagerComposite* aManager) +{ + mCompositeManager = aManager; + mCompositor = aManager->GetCompositor(); +} + +bool +LayerManagerComposite::AsyncPanZoomEnabled() const +{ + if (CompositorBridgeParent* bridge = mCompositor->GetCompositorBridgeParent()) { + return bridge->AsyncPanZoomEnabled(); + } + return false; +} + +nsIntRegion +LayerComposite::GetFullyRenderedRegion() { + if (TiledContentHost* tiled = GetCompositableHost() ? GetCompositableHost()->AsTiledContentHost() + : nullptr) { + nsIntRegion shadowVisibleRegion = GetShadowVisibleRegion().ToUnknownRegion(); + // Discard the region which hasn't been drawn yet when doing + // progressive drawing. Note that if the shadow visible region + // shrunk the tiled valig region may not have discarded this yet. + shadowVisibleRegion.And(shadowVisibleRegion, tiled->GetValidRegion()); + return shadowVisibleRegion; + } else { + return GetShadowVisibleRegion().ToUnknownRegion(); + } +} + +Matrix4x4 +LayerComposite::GetShadowTransform() { + Matrix4x4 transform = mShadowTransform; + Layer* layer = GetLayer(); + + transform.PostScale(layer->GetPostXScale(), layer->GetPostYScale(), 1.0f); + if (const ContainerLayer* c = layer->AsContainerLayer()) { + transform.PreScale(c->GetPreXScale(), c->GetPreYScale(), 1.0f); + } + + return transform; +} + +bool +LayerComposite::HasStaleCompositor() const +{ + return mCompositeManager->GetCompositor() != mCompositor; +} + +static bool +LayerHasCheckerboardingAPZC(Layer* aLayer, Color* aOutColor) +{ + bool answer = false; + for (LayerMetricsWrapper i(aLayer, LayerMetricsWrapper::StartAt::BOTTOM); i; i = i.GetParent()) { + if (!i.Metrics().IsScrollable()) { + continue; + } + if (i.GetApzc() && i.GetApzc()->IsCurrentlyCheckerboarding()) { + if (aOutColor) { + *aOutColor = i.Metadata().GetBackgroundColor(); + } + answer = true; + break; + } + break; + } + return answer; +} + +bool +LayerComposite::NeedToDrawCheckerboarding(gfx::Color* aOutCheckerboardingColor) +{ + return GetLayer()->Manager()->AsyncPanZoomEnabled() && + (GetLayer()->GetContentFlags() & Layer::CONTENT_OPAQUE) && + GetLayer()->IsOpaqueForVisibility() && + LayerHasCheckerboardingAPZC(GetLayer(), aOutCheckerboardingColor); +} + +#ifndef MOZ_HAVE_PLATFORM_SPECIFIC_LAYER_BUFFERS + +/*static*/ bool +LayerManagerComposite::SupportsDirectTexturing() +{ + return false; +} + +/*static*/ void +LayerManagerComposite::PlatformSyncBeforeReplyUpdate() +{ +} + +#endif // !defined(MOZ_HAVE_PLATFORM_SPECIFIC_LAYER_BUFFERS) + +} // namespace layers +} // namespace mozilla |