/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* 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 // for size_t #include // for uint16_t, uint32_t #include "CanvasLayerComposite.h" // for CanvasLayerComposite #include "ColorLayerComposite.h" // for ColorLayerComposite #include "CompositableHost.h" // for CompositableHost #include "ContainerLayerComposite.h" // for ContainerLayerComposite, etc #include "Diagnostics.h" #include "FPSCounter.h" // for FPSState, FPSCounter #include "FrameMetrics.h" // for FrameMetrics #include "GeckoProfiler.h" // for profiler_* #include "ImageLayerComposite.h" // for ImageLayerComposite #include "Layers.h" // for Layer, ContainerLayer, etc #include "LayerScope.h" // for LayerScope Tool #include "LayerTreeInvalidation.h" #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 "gfxEnv.h" // for gfxEnv #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/StaticPrefs_gfx.h" #include "mozilla/StaticPrefs_layers.h" #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/CompositorOGL.h" #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/layers/NativeLayer.h" #include "mozilla/layers/UiCompositorControllerParent.h" #include "mozilla/widget/CompositorWidget.h" // for WidgetRenderingContext #include "ipc/CompositorBench.h" // for CompositorBench #include "ipc/SurfaceDescriptor.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, etc #include "nsISupportsImpl.h" // for Layer::AddRef, etc #include "nsPoint.h" // for nsIntPoint #include "nsRect.h" // for mozilla::gfx::IntRect #include "nsRegion.h" // for nsIntRegion, etc #if defined(MOZ_WIDGET_ANDROID) # include # include # include "mozilla/jni/Utils.h" # include "mozilla/widget/AndroidCompositorWidget.h" # include "GLConsts.h" # include "GLContextEGL.h" # include "GLContextProvider.h" # include "mozilla/Unused.h" # include "ScopedGLHelpers.h" #endif #include "GeckoProfiler.h" #include "TextRenderer.h" // for TextRenderer #include "mozilla/layers/CompositorBridgeParent.h" #include "TreeTraversal.h" // for ForEachNode #include "CompositionRecorder.h" #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(aLayer->ImplData()); } static void ClearSubtree(Layer* aLayer) { ForEachNode(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? } HostLayerManager::HostLayerManager() : mDebugOverlayWantsNextFrame(false), mWarningLevel(0.0f), mCompositorBridgeID(0), mLastPaintTime(TimeDuration::Forever()), mRenderStartTime(TimeStamp::Now()) {} HostLayerManager::~HostLayerManager() = default; void HostLayerManager::RecordPaintTimes(const PaintTiming& aTiming) { mDiagnostics->RecordPaintTimes(aTiming); } void HostLayerManager::RecordUpdateTime(float aValue) { mDiagnostics->RecordUpdateTime(aValue); } void HostLayerManager::WriteCollectedFrames() { if (mCompositionRecorder) { mCompositionRecorder->WriteCollectedFrames(); mCompositionRecorder = nullptr; } } Maybe HostLayerManager::GetCollectedFrames() { Maybe maybeFrames; if (mCompositionRecorder) { maybeFrames.emplace(mCompositionRecorder->GetCollectedFrames()); mCompositionRecorder = nullptr; } return maybeFrames; } /** * LayerManagerComposite */ LayerManagerComposite::LayerManagerComposite(Compositor* aCompositor) : mUnusedApzTransformWarning(false), mDisabledApzWarning(false), mCompositor(aCompositor), mInTransaction(false), mIsCompositorReady(false) #if defined(MOZ_WIDGET_ANDROID) , mScreenPixelsTarget(nullptr) #endif // defined(MOZ_WIDGET_ANDROID) { mTextRenderer = new TextRenderer(); mDiagnostics = MakeUnique(); MOZ_ASSERT(aCompositor); mNativeLayerRoot = aCompositor->GetWidget()->GetNativeLayerRoot(); if (mNativeLayerRoot) { mSurfacePoolHandle = aCompositor->GetSurfacePoolHandle(); MOZ_RELEASE_ASSERT(mSurfacePoolHandle); } #ifdef USE_SKIA mPaintCounter = nullptr; #endif } LayerManagerComposite::~LayerManagerComposite() { Destroy(); } void LayerManagerComposite::Destroy() { if (!mDestroyed) { mCompositor->GetWidget()->CleanupWindowEffects(); if (mRoot) { RootLayer()->Destroy(); } mCompositor->CancelFrame(); mRoot = nullptr; mClonedLayerTreeProperties = nullptr; mProfilerScreenshotGrabber.Destroy(); if (mNativeLayerRoot) { if (mGPUStatsLayer) { mNativeLayerRoot->RemoveLayer(mGPUStatsLayer); mGPUStatsLayer = nullptr; } if (mUnusedTransformWarningLayer) { mNativeLayerRoot->RemoveLayer(mUnusedTransformWarningLayer); mUnusedTransformWarningLayer = nullptr; } if (mDisabledApzWarningLayer) { mNativeLayerRoot->RemoveLayer(mDisabledApzWarningLayer); mDisabledApzWarningLayer = nullptr; } for (const auto& nativeLayer : mNativeLayers) { mNativeLayerRoot->RemoveLayer(nativeLayer); } mNativeLayers.clear(); mNativeLayerRoot = 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(const nsCString& aURL) { 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; mTarget = aTarget; mTargetBounds = aRect; } template static IntRectTyped TransformRect(const IntRectTyped& aRect, const Matrix& aTransform, bool aRoundIn = false) { if (aRect.IsEmpty()) { return IntRectTyped(); } Rect rect(aRect.X(), aRect.Y(), aRect.Width(), aRect.Height()); rect = aTransform.TransformBounds(rect); if (aRoundIn) { MOZ_ASSERT(aTransform.PreservesAxisAlignedRectangles()); rect.RoundIn(); } else { rect.RoundOut(); } IntRect intRect; if (!rect.ToIntRect(&intRect)) { intRect = IntRect::MaxIntRect(); } return ViewAs(intRect); } template static IntRectTyped TransformRect(const IntRectTyped& aRect, const Matrix4x4& aTransform, bool aRoundIn = false) { if (aRect.IsEmpty()) { return IntRectTyped(); } Rect rect(aRect.X(), aRect.Y(), aRect.Width(), aRect.Height()); rect = aTransform.TransformAndClipBounds(rect, Rect::MaxIntRect()); if (aRoundIn) { rect.RoundIn(); } else { rect.RoundOut(); } IntRect intRect; if (!rect.ToIntRect(&intRect)) { intRect = IntRect::MaxIntRect(); } return ViewAs(intRect); } template static IntRectTyped TransformRectRoundIn( const IntRectTyped& aRect, const MatrixType& aTransform) { return TransformRect(aRect, aTransform, true); } template static void AddTransformedRegion(IntRegionTyped& aDest, const IntRegionTyped& aSource, const MatrixType& aTransform) { for (auto iter = aSource.RectIter(); !iter.Done(); iter.Next()) { aDest.Or(aDest, TransformRect(iter.Get(), aTransform)); } aDest.SimplifyOutward(20); } template static void AddTransformedRegionRoundIn(IntRegionTyped& aDest, const IntRegionTyped& aSource, const MatrixType& aTransform) { for (auto iter = aSource.RectIter(); !iter.Done(); iter.Next()) { aDest.Or(aDest, TransformRectRoundIn(iter.Get(), aTransform)); } } void LayerManagerComposite::PostProcessLayers(nsIntRegion& aOpaqueRegion) { LayerIntRegion visible; LayerComposite* rootComposite = static_cast(mRoot->AsHostLayer()); PostProcessLayers( mRoot, aOpaqueRegion, visible, ViewAs( rootComposite->GetShadowClipRect(), PixelCastJustification::RenderTargetIsParentLayerForRoot), Nothing(), true); } // We want to skip directly through ContainerLayers that don't have an // intermediate surface. We compute occlusions for leaves and intermediate // surfaces against the layer that they actually composite into so that we can // use the final (snapped) effective transform. static bool ShouldProcessLayer(Layer* aLayer) { if (!aLayer->AsContainerLayer()) { return true; } return aLayer->AsContainerLayer()->UseIntermediateSurface(); } void LayerManagerComposite::PostProcessLayers( Layer* aLayer, nsIntRegion& aOpaqueRegion, LayerIntRegion& aVisibleRegion, const Maybe& aRenderTargetClip, const Maybe& aClipFromAncestors, bool aCanContributeOpaque) { // Compute a clip that's the combination of our layer clip with the clip // from our ancestors. LayerComposite* composite = static_cast(aLayer->AsHostLayer()); Maybe layerClip = composite->GetShadowClipRect(); MOZ_ASSERT(!layerClip || !aLayer->Combines3DTransformWithAncestors(), "The layer with a clip should not participate " "a 3D rendering context"); Maybe outsideClip = IntersectMaybeRects(layerClip, aClipFromAncestors); Maybe insideClip; if (aLayer->Extend3DContext()) { // If we're preserve-3d just pass the clip rect down directly, and we'll do // the conversion at the preserve-3d leaf Layer. if (outsideClip) { insideClip = Some(ViewAs( *outsideClip, PixelCastJustification::MovingDownToChildren)); } } else if (outsideClip) { // 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). Matrix4x4 localTransform = aLayer->ComputeTransformToPreserve3DRoot(); if (!localTransform.HasPerspectiveComponent() && localTransform.Invert()) { LayerRect insideClipFloat = UntransformBy(ViewAs(localTransform), ParentLayerRect(*outsideClip), LayerRect::MaxIntRect()) .valueOr(LayerRect()); insideClipFloat.RoundOut(); LayerIntRect insideClipInt; if (insideClipFloat.ToIntRect(&insideClipInt)) { insideClip = Some(insideClipInt); } } } Maybe ancestorClipForChildren; if (insideClip) { ancestorClipForChildren = Some(ViewAs( *insideClip, PixelCastJustification::MovingDownToChildren)); } nsIntRegion dummy; nsIntRegion& opaqueRegion = aOpaqueRegion; if (aLayer->Extend3DContext() || aLayer->Combines3DTransformWithAncestors()) { opaqueRegion = dummy; } if (!ShouldProcessLayer(aLayer)) { MOZ_ASSERT(aLayer->AsContainerLayer() && !aLayer->AsContainerLayer()->UseIntermediateSurface()); // For layers participating 3D rendering context, their visible // region should be empty (invisible), so we pass through them // without doing anything. for (Layer* child = aLayer->GetLastChild(); child; child = child->GetPrevSibling()) { LayerComposite* childComposite = static_cast(child->AsHostLayer()); Maybe renderTargetClip = aRenderTargetClip; if (childComposite->GetShadowClipRect()) { RenderTargetIntRect clip = TransformBy( ViewAs( aLayer->GetEffectiveTransform(), PixelCastJustification::RenderTargetIsParentLayerForRoot), *childComposite->GetShadowClipRect()); renderTargetClip = IntersectMaybeRects(renderTargetClip, Some(clip)); } PostProcessLayers( child, opaqueRegion, aVisibleRegion, renderTargetClip, ancestorClipForChildren, aCanContributeOpaque & !(aLayer->GetContentFlags() & Layer::CONTENT_BACKFACE_HIDDEN)); } 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 = aLayer->GetEffectiveTransform(); Matrix transform2d; bool canTransformOpaqueRegion = false; // 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 (aCanContributeOpaque && !(aLayer->GetContentFlags() & Layer::CONTENT_BACKFACE_HIDDEN) && transform.Is2D(&transform2d) && transform2d.PreservesAxisAlignedRectangles()) { Matrix inverse = transform2d; inverse.Invert(); AddTransformedRegionRoundIn(localOpaque, opaqueRegion, inverse); canTransformOpaqueRegion = true; } // 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()) { MOZ_ASSERT(aLayer->AsContainerLayer()->UseIntermediateSurface()); LayerComposite* childComposite = static_cast(child->AsHostLayer()); PostProcessLayers( child, localOpaque, descendantsVisibleRegion, ViewAs( childComposite->GetShadowClipRect(), PixelCastJustification::RenderTargetIsParentLayerForRoot), ancestorClipForChildren, true); 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(transform), visible); aVisibleRegion.OrWith(ViewAs( visibleParentSpace, PixelCastJustification::MovingDownToChildren)); // If we have a simple transform, then we can add our opaque area into // aOpaqueRegion. if (canTransformOpaqueRegion && !aLayer->HasMaskLayers() && aLayer->IsOpaqueForVisibility()) { if (aLayer->IsOpaque()) { localOpaque.OrWith(composite->GetFullyRenderedRegion()); } nsIntRegion parentSpaceOpaque; AddTransformedRegionRoundIn(parentSpaceOpaque, localOpaque, transform2d); if (aRenderTargetClip) { parentSpaceOpaque.AndWith(aRenderTargetClip->ToUnknownRect()); } opaqueRegion.OrWith(parentSpaceOpaque); } } 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(); // Ensure we read unlock textures, even if we end up // not compositing this frame. TextureSourceProvider::AutoReadUnlockTextures unlock(mCompositor); 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. SetCompositionTime(aTimeStamp); if (mRoot && !(aFlags & END_NO_IMMEDIATE_REDRAW)) { MOZ_ASSERT(!aTimeStamp.IsNull()); UpdateAndRender(); mCompositor->FlushPendingNotifyNotUsed(); } mTarget = nullptr; #ifdef MOZ_LAYERS_HAVE_LOG Log(); MOZ_LAYERS_LOG(("]----- EndTransaction")); #endif } void LayerManagerComposite::SetRoot(Layer* aLayer) { mRoot = aLayer; } void LayerManagerComposite::UpdateAndRender() { mCompositionOpportunityId = mCompositionOpportunityId.Next(); if (gfxEnv::SkipComposition()) { mInvalidRegion.SetEmpty(); return; } // 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()); nsIntRegion opaque; PostProcessLayers(opaque); if (mClonedLayerTreeProperties) { // 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; const bool overflowed = !mClonedLayerTreeProperties->ComputeDifferences( mRoot, changed, nullptr); if (overflowed) { changed = mRenderBounds; } mInvalidRegion.Or(mInvalidRegion, changed); } nsIntRegion invalid; 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. The layers damage region has been // stored in mInvalidRegion and will be picked up by the next window // composite. invalid = mTargetBounds; } else { if (!mClonedLayerTreeProperties) { // If we didn't have a previous layer tree, invalidate the entire render // area. mInvalidRegion = mRenderBounds; } invalid = mInvalidRegion; } if (invalid.IsEmpty()) { // Composition requested, but nothing has changed. Don't do any work. mClonedLayerTreeProperties = LayerProperties::CloneFrom(GetRoot()); mProfilerScreenshotGrabber.NotifyEmptyFrame(); // Discard the current payloads. These payloads did not require a composite // (they caused no changes to anything visible), so we don't want to measure // their latency. mPayload.Clear(); 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. // Only invalidate if we're not using native layers. When using native layers, // UpdateDebugOverlayNativeLayers will repaint the appropriate layer areas. if (!mNativeLayerRoot) { InvalidateDebugOverlay(invalid, mRenderBounds); } bool rendered = Render(invalid, opaque); #if defined(MOZ_WIDGET_ANDROID) RenderToPresentationSurface(); #endif if (!mTarget && rendered) { mInvalidRegion.SetEmpty(); } // Update cached layer tree information. mClonedLayerTreeProperties = LayerProperties::CloneFrom(GetRoot()); } already_AddRefed LayerManagerComposite::CreateOptimalMaskDrawTarget( const IntSize& aSize) { MOZ_CRASH("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); } void LayerManagerComposite::InvalidateDebugOverlay(nsIntRegion& aInvalidRegion, const IntRect& aBounds) { bool drawFps = StaticPrefs::layers_acceleration_draw_fps(); bool drawFrameColorBars = StaticPrefs::gfx_draw_color_bars(); if (drawFps) { aInvalidRegion.Or(aInvalidRegion, nsIntRect(0, 0, 650, 400)); } if (drawFrameColorBars) { aInvalidRegion.Or(aInvalidRegion, nsIntRect(0, 0, 10, aBounds.Height())); } #ifdef USE_SKIA bool drawPaintTimes = StaticPrefs::gfx_content_always_paint(); 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 Rect RectWithEdges(int32_t aTop, int32_t aRight, int32_t aBottom, int32_t aLeft) { return Rect(aLeft, aTop, aRight - aLeft, aBottom - aTop); } void LayerManagerComposite::DrawBorder(const IntRect& aOuter, int32_t aBorderWidth, const DeviceColor& aColor, const Matrix4x4& aTransform) { EffectChain effects; effects.mPrimaryEffect = new EffectSolidColor(aColor); IntRect inner(aOuter); inner.Deflate(aBorderWidth); // Top and bottom border sides mCompositor->DrawQuad( RectWithEdges(aOuter.Y(), aOuter.XMost(), inner.Y(), aOuter.X()), aOuter, effects, 1, aTransform); mCompositor->DrawQuad( RectWithEdges(inner.YMost(), aOuter.XMost(), aOuter.YMost(), aOuter.X()), aOuter, effects, 1, aTransform); // Left and right border sides mCompositor->DrawQuad( RectWithEdges(inner.Y(), inner.X(), inner.YMost(), aOuter.X()), aOuter, effects, 1, aTransform); mCompositor->DrawQuad( RectWithEdges(inner.Y(), aOuter.XMost(), inner.YMost(), inner.XMost()), aOuter, effects, 1, aTransform); } void LayerManagerComposite::DrawTranslationWarningOverlay( const IntRect& aBounds) { // Black blorder IntRect blackBorderBounds(aBounds); blackBorderBounds.Deflate(4); DrawBorder(blackBorderBounds, 6, DeviceColor(0, 0, 0, 1), Matrix4x4()); // Warning border, yellow to red IntRect warnBorder(aBounds); warnBorder.Deflate(5); DrawBorder(warnBorder, 4, DeviceColor(1, 1.f - mWarningLevel, 0, 1), Matrix4x4()); } static uint16_t sFrameCount = 0; void LayerManagerComposite::RenderDebugOverlay(const IntRect& aBounds) { bool drawFps = StaticPrefs::layers_acceleration_draw_fps(); bool drawFrameColorBars = StaticPrefs::gfx_draw_color_bars(); // Don't draw diagnostic overlays if we want to snapshot the output. if (mTarget) { return; } if (drawFps) { #ifdef ANDROID // Draw a translation delay warning overlay if (!mWarnTime.IsNull() && (TimeStamp::Now() - mWarnTime).ToMilliseconds() < kVisualWarningDuration) { DrawTranslationWarningOverlay(aBounds); SetDebugOverlayWantsNextFrame(true); } #endif GPUStats stats; stats.mScreenPixels = mRenderBounds.Width() * mRenderBounds.Height(); mCompositor->GetFrameStats(&stats); std::string text = mDiagnostics->GetFrameOverlayString(stats); mTextRenderer->RenderText(mCompositor, text, IntPoint(2, 5), Matrix4x4(), 24, 600, TextRenderer::FontType::FixedWidth); float alpha = 1; 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::DeviceColor(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::DeviceColor(1, 1, 0, 1)); mCompositor->DrawQuad(gfx::Rect(aBounds.Width() - 40, 0, 20, 20), aBounds, effects, alpha, gfx::Matrix4x4()); mDisabledApzWarning = false; SetDebugOverlayWantsNextFrame(true); } } 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()); // We intentionally overflow at 2^16. sFrameCount++; } #ifdef USE_SKIA bool drawPaintTimes = StaticPrefs::gfx_content_always_paint(); if (drawPaintTimes) { DrawPaintTimes(mCompositor); } #endif } void LayerManagerComposite::UpdateDebugOverlayNativeLayers() { // Remove all debug layers first because PlaceNativeLayers might have changed // the z-order. By removing and re-adding, we keep the debug overlay layers // on top. if (mGPUStatsLayer) { mNativeLayerRoot->RemoveLayer(mGPUStatsLayer); } if (mUnusedTransformWarningLayer) { mNativeLayerRoot->RemoveLayer(mUnusedTransformWarningLayer); } if (mDisabledApzWarningLayer) { mNativeLayerRoot->RemoveLayer(mDisabledApzWarningLayer); } bool drawFps = StaticPrefs::layers_acceleration_draw_fps(); if (drawFps) { GPUStats stats; stats.mScreenPixels = mRenderBounds.Area(); mCompositor->GetFrameStats(&stats); std::string text = mDiagnostics->GetFrameOverlayString(stats); IntSize size = mTextRenderer->ComputeSurfaceSize( text, 600, TextRenderer::FontType::FixedWidth); if (!mGPUStatsLayer || mGPUStatsLayer->GetSize() != size) { mGPUStatsLayer = mNativeLayerRoot->CreateLayer(size, false, mSurfacePoolHandle); } mGPUStatsLayer->SetPosition(IntPoint(2, 5)); IntRect bounds({}, size); RefPtr dt = mGPUStatsLayer->NextSurfaceAsDrawTarget( bounds, bounds, BackendType::SKIA); mTextRenderer->RenderTextToDrawTarget(dt, text, 600, TextRenderer::FontType::FixedWidth); mGPUStatsLayer->NotifySurfaceReady(); mNativeLayerRoot->AppendLayer(mGPUStatsLayer); IntSize square(20, 20); // The two warning layers are created on demand and their content is only // drawn once. After that, they only get moved (if the window size changes) // and conditionally shown. // The drawing would be unnecessary if we had native "color layers". if (mUnusedApzTransformWarning) { // If we have an unused APZ transform on this composite, draw a 20x20 red // box in the top-right corner. if (!mUnusedTransformWarningLayer) { mUnusedTransformWarningLayer = mNativeLayerRoot->CreateLayer(square, true, mSurfacePoolHandle); RefPtr dt = mUnusedTransformWarningLayer->NextSurfaceAsDrawTarget( IntRect({}, square), IntRect({}, square), BackendType::SKIA); dt->FillRect(Rect(0, 0, 20, 20), ColorPattern(DeviceColor(1, 0, 0, 1))); mUnusedTransformWarningLayer->NotifySurfaceReady(); } mUnusedTransformWarningLayer->SetPosition( IntPoint(mRenderBounds.XMost() - 20, mRenderBounds.Y())); mNativeLayerRoot->AppendLayer(mUnusedTransformWarningLayer); 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. if (!mDisabledApzWarningLayer) { mDisabledApzWarningLayer = mNativeLayerRoot->CreateLayer(square, true, mSurfacePoolHandle); RefPtr dt = mDisabledApzWarningLayer->NextSurfaceAsDrawTarget( IntRect({}, square), IntRect({}, square), BackendType::SKIA); dt->FillRect(Rect(0, 0, 20, 20), ColorPattern(DeviceColor(1, 1, 0, 1))); mDisabledApzWarningLayer->NotifySurfaceReady(); } mDisabledApzWarningLayer->SetPosition( IntPoint(mRenderBounds.XMost() - 40, mRenderBounds.Y())); mNativeLayerRoot->AppendLayer(mDisabledApzWarningLayer); mDisabledApzWarning = false; SetDebugOverlayWantsNextFrame(true); } } else { mGPUStatsLayer = nullptr; mUnusedTransformWarningLayer = nullptr; mDisabledApzWarningLayer = nullptr; } } RefPtr LayerManagerComposite::PushGroupForLayerEffects() { // This is currently true, so just making sure that any new use of this // method is flagged for investigation MOZ_ASSERT(StaticPrefs::layers_effect_invert() || StaticPrefs::layers_effect_grayscale() || StaticPrefs::layers_effect_contrast() != 0.0); RefPtr 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.IsEqualXY(0, 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 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()); } void LayerManagerComposite::PlaceNativeLayers( const IntRegion& aRegion, bool aOpaque, std::deque>* aLayersToRecycle, IntRegion* aWindowInvalidRegion) { IntSize tileSize(StaticPrefs::layers_compositing_tiles_width(), StaticPrefs::layers_compositing_tiles_height()); IntRect regionBounds = aRegion.GetBounds(); for (int32_t y = 0; y < regionBounds.YMost(); y += tileSize.height) { for (int32_t x = 0; x < regionBounds.XMost(); x += tileSize.width) { IntRegion tileRegion; tileRegion.And(aRegion, IntRect(IntPoint(x, y), tileSize)); for (auto iter = tileRegion.RectIter(); !iter.Done(); iter.Next()) { PlaceNativeLayer(iter.Get(), aOpaque, aLayersToRecycle, aWindowInvalidRegion); } } } } void LayerManagerComposite::PlaceNativeLayer( const IntRect& aRect, bool aOpaque, std::deque>* aLayersToRecycle, IntRegion* aWindowInvalidRegion) { RefPtr layer; if (aLayersToRecycle->empty() || aLayersToRecycle->front()->GetSize() != aRect.Size() || aLayersToRecycle->front()->IsOpaque() != aOpaque) { layer = mNativeLayerRoot->CreateLayer(aRect.Size(), aOpaque, mSurfacePoolHandle); mNativeLayerRoot->AppendLayer(layer); aWindowInvalidRegion->OrWith(aRect); } else { layer = aLayersToRecycle->front(); aLayersToRecycle->pop_front(); IntRect oldRect = layer->GetRect(); if (!aRect.IsEqualInterior(oldRect)) { aWindowInvalidRegion->OrWith(oldRect); aWindowInvalidRegion->OrWith(aRect); } } layer->SetPosition(aRect.TopLeft()); mNativeLayers.push_back(layer); } // Used to clear the 'mLayerComposited' flag at the beginning of each Render(). static void ClearLayerFlags(Layer* aLayer) { ForEachNode(aLayer, [](Layer* layer) { if (layer->AsHostLayer()) { static_cast(layer->AsHostLayer()) ->SetLayerComposited(false); } }); } bool LayerManagerComposite::Render(const nsIntRegion& aInvalidRegion, const nsIntRegion& aOpaqueRegion) { AUTO_PROFILER_LABEL("LayerManagerComposite::Render", GRAPHICS); if (mDestroyed || !mCompositor || mCompositor->IsDestroyed()) { NS_WARNING("Call on destroyed layer manager"); return false; } mCompositor->RequestAllowFrameRecording(!!mCompositionRecorder); 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 = StaticPrefs::layers_effect_invert(); bool grayscaleVal = StaticPrefs::layers_effect_grayscale(); float contrastVal = StaticPrefs::layers_effect_contrast(); bool haveLayerEffects = (invertVal || grayscaleVal || contrastVal != 0.0); // Set LayerScope begin/end frame LayerScopeAutoFrame frame(PR_Now()); // If you're looking for the code to dump the layer tree, it was moved // to CompositorBridgeParent::CompositeToTarget(). // 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::LayersPacket* layersPacket = packet->mutable_layers(); this->Dump(layersPacket); LayerScope::SendLayerDump(std::move(packet)); } mozilla::widget::WidgetRenderingContext widgetContext; #if defined(XP_MACOSX) if (CompositorOGL* compositorOGL = mCompositor->AsCompositorOGL()) { widgetContext.mGL = compositorOGL->gl(); } #endif { AUTO_PROFILER_LABEL("LayerManagerComposite::Render:Prerender", GRAPHICS); if (!mCompositor->GetWidget()->PreRender(&widgetContext)) { return false; } } CompositorBench(mCompositor, mRenderBounds); MOZ_ASSERT(mRoot->GetOpacity() == 1); #if defined(MOZ_WIDGET_ANDROID) LayerMetricsWrapper wrapper = GetRootContentLayer(); if (wrapper) { mCompositor->SetClearColor(wrapper.Metadata().GetBackgroundColor()); } else { mCompositor->SetClearColorToDefault(); } #endif Maybe rootLayerClip = mRoot->GetClipRect().map( [](const ParentLayerIntRect& r) { return r.ToUnknownRect(); }); Maybe maybeBounds; bool usingNativeLayers = false; if (mTarget) { maybeBounds = mCompositor->BeginFrameForTarget( aInvalidRegion, rootLayerClip, mRenderBounds, aOpaqueRegion, mTarget, mTargetBounds); } else if (mNativeLayerRoot) { mSurfacePoolHandle->OnBeginFrame(); if (aInvalidRegion.Intersects(mRenderBounds)) { mCompositor->BeginFrameForNativeLayers(); maybeBounds = Some(mRenderBounds); usingNativeLayers = true; } } else { maybeBounds = mCompositor->BeginFrameForWindow( aInvalidRegion, rootLayerClip, mRenderBounds, aOpaqueRegion); } if (!maybeBounds) { mProfilerScreenshotGrabber.NotifyEmptyFrame(); mCompositor->GetWidget()->PostRender(&widgetContext); // Discard the current payloads. These payloads did not require a composite // (they caused no changes to anything visible), so we don't want to measure // their latency. mPayload.Clear(); return true; } IntRect bounds = *maybeBounds; IntRect clipRect = rootLayerClip.valueOr(bounds); // Prepare our layers. { Diagnostics::Record record(mRenderStartTime); RootLayer()->Prepare(RenderTargetIntRect::FromUnknownRect(clipRect)); if (record.Recording()) { mDiagnostics->RecordPrepareTime(record.Duration()); } } auto RenderOnce = [&](const IntRect& aClipRect) { RefPtr previousTarget; if (haveLayerEffects) { previousTarget = PushGroupForLayerEffects(); } else { mTwoPassTmpTarget = nullptr; } // Execute draw commands. RootLayer()->RenderLayer(aClipRect, Nothing()); if (mTwoPassTmpTarget) { MOZ_ASSERT(haveLayerEffects); PopGroupForLayerEffects(previousTarget, aClipRect, grayscaleVal, invertVal, contrastVal); } if (!mRegionToClear.IsEmpty()) { for (auto iter = mRegionToClear.RectIter(); !iter.Done(); iter.Next()) { mCompositor->ClearRect(Rect(iter.Get())); } } mCompositor->NormalDrawingDone(); }; { Diagnostics::Record record; if (usingNativeLayers) { // Update the placement of our native layers, so that transparent and // opaque parts of the window are covered by different layers and we can // update those parts separately. IntRegion opaqueRegion; opaqueRegion.And(aOpaqueRegion, mRenderBounds); // Limit the complexity of these regions. Usually, opaqueRegion should be // only one or two rects, so this SimplifyInward call will not change the // region if everything looks as expected. opaqueRegion.SimplifyInward(4); IntRegion transparentRegion; transparentRegion.Sub(mRenderBounds, opaqueRegion); std::deque> layersToRecycle = std::move(mNativeLayers); IntRegion invalidRegion = aInvalidRegion; PlaceNativeLayers(opaqueRegion, true, &layersToRecycle, &invalidRegion); PlaceNativeLayers(transparentRegion, false, &layersToRecycle, &invalidRegion); for (const auto& unusedLayer : layersToRecycle) { mNativeLayerRoot->RemoveLayer(unusedLayer); } for (const auto& nativeLayer : mNativeLayers) { Maybe maybeLayerRect = mCompositor->BeginRenderingToNativeLayer( invalidRegion, rootLayerClip, aOpaqueRegion, nativeLayer); if (!maybeLayerRect) { continue; } if (rootLayerClip) { RenderOnce(rootLayerClip->Intersect(*maybeLayerRect)); } else { RenderOnce(*maybeLayerRect); } mCompositor->EndRenderingToNativeLayer(); } } else { RenderOnce(clipRect); } if (record.Recording()) { mDiagnostics->RecordCompositeTime(record.Duration()); } } RootLayer()->Cleanup(); WindowLMC window(mCompositor); mProfilerScreenshotGrabber.MaybeGrabScreenshot(window, bounds.Size()); if (mCompositionRecorder) { bool hasContentPaint = std::any_of( mPayload.begin(), mPayload.end(), [](CompositionPayload& payload) { return payload.mType == CompositionPayloadType::eContentPaint; }); if (hasContentPaint) { if (RefPtr frame = mCompositor->RecordFrame(TimeStamp::Now())) { mCompositionRecorder->RecordFrame(frame); } } } if (usingNativeLayers) { UpdateDebugOverlayNativeLayers(); } else { #if defined(MOZ_WIDGET_ANDROID) HandlePixelsTarget(); #endif // defined(MOZ_WIDGET_ANDROID) // Debugging RenderDebugOverlay(bounds); } { AUTO_PROFILER_LABEL("LayerManagerComposite::Render:EndFrame", GRAPHICS); mCompositor->EndFrame(); if (usingNativeLayers) { mNativeLayerRoot->CommitToScreen(); } } mCompositor->GetWidget()->PostRender(&widgetContext); mProfilerScreenshotGrabber.MaybeProcessQueue(); RecordFrame(); PayloadPresented(TimeStamp::Now()); // Our payload has now been presented. mPayload.Clear(); if (usingNativeLayers) { mSurfacePoolHandle->OnEndFrame(); } mCompositor->WaitForGPU(); return true; } #if defined(MOZ_WIDGET_ANDROID) 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 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() { if (!mCompositor) { return; } widget::CompositorWidget* const widget = mCompositor->GetWidget(); if (!widget) { return; } ANativeWindow* window = widget->AsAndroid()->GetPresentationANativeWindow(); if (!window) { return; } CompositorOGL* compositor = mCompositor->AsCompositorOGL(); GLContext* gl = compositor->gl(); GLContextEGL* egl = GLContextEGL::Cast(gl); if (!egl) { return; } EGLSurface surface = widget->AsAndroid()->GetPresentationEGLSurface(); if (!surface) { // create surface; surface = egl->CreateCompatibleSurface(window); if (!surface) { return; } widget->AsAndroid()->SetPresentationEGLSurface(surface); } const IntSize windowSize(ANativeWindow_getWidth(window), ANativeWindow_getHeight(window)); 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; PostProcessLayers(opaque); nsIntRegion invalid; IntRect bounds = IntRect::Truncate(0, 0, scale * pageWidth, actualHeight); MOZ_ASSERT(mRoot->GetOpacity() == 1); Unused << mCompositor->BeginFrameForWindow(invalid, Nothing(), bounds, nsIntRegion()); // 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, Nothing()); mCompositor->EndFrame(); } // Used by robocop tests to get a snapshot of the frame buffer. void LayerManagerComposite::HandlePixelsTarget() { if (!mScreenPixelsTarget) { return; } int32_t bufferWidth = mRenderBounds.width; int32_t bufferHeight = mRenderBounds.height; ipc::Shmem mem; if (!mScreenPixelsTarget->AllocPixelBuffer( bufferWidth * bufferHeight * sizeof(uint32_t), &mem)) { // Failed to alloc shmem, Just bail out. return; } CompositorOGL* compositor = mCompositor->AsCompositorOGL(); GLContext* gl = compositor->gl(); MOZ_ASSERT(gl); gl->fReadPixels(0, 0, bufferWidth, bufferHeight, LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE, mem.get()); Unused << mScreenPixelsTarget->SendScreenPixels( std::move(mem), ScreenIntSize(bufferWidth, bufferHeight), true); mScreenPixelsTarget = nullptr; } #endif already_AddRefed LayerManagerComposite::CreatePaintedLayer() { if (mDestroyed) { NS_WARNING("Call on destroyed layer manager"); return nullptr; } return RefPtr(new PaintedLayerComposite(this)).forget(); } already_AddRefed LayerManagerComposite::CreateContainerLayer() { if (mDestroyed) { NS_WARNING("Call on destroyed layer manager"); return nullptr; } return RefPtr(new ContainerLayerComposite(this)).forget(); } already_AddRefed LayerManagerComposite::CreateImageLayer() { if (mDestroyed) { NS_WARNING("Call on destroyed layer manager"); return nullptr; } return RefPtr(new ImageLayerComposite(this)).forget(); } already_AddRefed LayerManagerComposite::CreateColorLayer() { if (LayerManagerComposite::mDestroyed) { NS_WARNING("Call on destroyed layer manager"); return nullptr; } return RefPtr(new ColorLayerComposite(this)).forget(); } already_AddRefed LayerManagerComposite::CreateCanvasLayer() { if (LayerManagerComposite::mDestroyed) { NS_WARNING("Call on destroyed layer manager"); return nullptr; } return RefPtr(new CanvasLayerComposite(this)).forget(); } already_AddRefed LayerManagerComposite::CreateRefLayer() { if (LayerManagerComposite::mDestroyed) { NS_WARNING("Call on destroyed layer manager"); return nullptr; } return RefPtr(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(); } bool LayerManagerComposite::IsCompositingToScreen() const { return !mTarget; } LayerComposite::LayerComposite(LayerManagerComposite* aManager) : HostLayer(aManager), mCompositeManager(aManager), mCompositor(aManager->GetCompositor()), mDestroyed(false), mLayerComposited(false) {} LayerComposite::~LayerComposite() = default; 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); } bool LayerManagerComposite::CanUseCanvasLayerForSize(const IntSize& aSize) { return mCompositor->CanUseCanvasLayerForSize( gfx::IntSize(aSize.width, aSize.height)); } void LayerManagerComposite::NotifyShadowTreeTransaction() { if (StaticPrefs::layers_acceleration_draw_fps()) { mDiagnostics->AddTxnFrame(); } } void LayerComposite::SetLayerManager(HostLayerManager* aManager) { HostLayer::SetLayerManager(aManager); mCompositeManager = static_cast(aManager); mCompositor = mCompositeManager->GetCompositor(); } bool LayerManagerComposite::AsyncPanZoomEnabled() const { if (CompositorBridgeParent* bridge = mCompositor->GetCompositorBridgeParent()) { return bridge->GetOptions().UseAPZ(); } return false; } bool LayerManagerComposite::AlwaysScheduleComposite() const { return !!(mCompositor->GetDiagnosticTypes() & DiagnosticTypes::FLASH_BORDERS); } 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 HostLayer::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; } // Async animations can move child layers without updating our visible region. // PostProcessLayers will recompute visible regions for layers with an // intermediate surface, but otherwise we need to do it now. static void ComputeVisibleRegionForChildren(ContainerLayer* aContainer, LayerIntRegion& aResult) { for (Layer* l = aContainer->GetFirstChild(); l; l = l->GetNextSibling()) { if (l->Extend3DContext()) { MOZ_ASSERT(l->AsContainerLayer()); ComputeVisibleRegionForChildren(l->AsContainerLayer(), aResult); } else { AddTransformedRegion(aResult, l->GetLocalVisibleRegion(), l->ComputeTransformToPreserve3DRoot()); } } } void HostLayer::RecomputeShadowVisibleRegionFromChildren() { mShadowVisibleRegion.SetEmpty(); ContainerLayer* container = GetLayer()->AsContainerLayer(); MOZ_ASSERT(container); // Layers that extend a 3d context have a local visible region // that can only be represented correctly in 3d space. Since // we can't do that, leave it empty instead to stop anyone // from trying to use it. NS_ASSERTION( !GetLayer()->Extend3DContext(), "Can't compute visible region for layers that extend a 3d context"); if (container && !GetLayer()->Extend3DContext()) { ComputeVisibleRegionForChildren(container, mShadowVisibleRegion); } } bool LayerComposite::HasStaleCompositor() const { return mCompositeManager->GetCompositor() != mCompositor; } #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) class RenderSourceLMC : public profiler_screenshots::RenderSource { public: explicit RenderSourceLMC(CompositingRenderTarget* aRT) : RenderSource(aRT->GetSize()), mRT(aRT) {} const auto& RenderTarget() { return mRT; } protected: virtual ~RenderSourceLMC() {} RefPtr mRT; }; class DownscaleTargetLMC : public profiler_screenshots::DownscaleTarget { public: explicit DownscaleTargetLMC(CompositingRenderTarget* aRT, Compositor* aCompositor) : profiler_screenshots::DownscaleTarget(aRT->GetSize()), mRenderSource(new RenderSourceLMC(aRT)), mCompositor(aCompositor) {} already_AddRefed AsRenderSource() override { return do_AddRef(mRenderSource); } bool DownscaleFrom(profiler_screenshots::RenderSource* aSource, const IntRect& aSourceRect, const IntRect& aDestRect) override { MOZ_RELEASE_ASSERT(aSourceRect.TopLeft() == IntPoint()); MOZ_RELEASE_ASSERT(aDestRect.TopLeft() == IntPoint()); RefPtr previousTarget = mCompositor->GetCurrentRenderTarget(); mCompositor->SetRenderTarget(mRenderSource->RenderTarget()); bool result = mCompositor->BlitRenderTarget( static_cast(aSource)->RenderTarget(), aSourceRect.Size(), aDestRect.Size()); // Restore the old render target. mCompositor->SetRenderTarget(previousTarget); return result; } protected: virtual ~DownscaleTargetLMC() {} RefPtr mRenderSource; Compositor* mCompositor; }; class AsyncReadbackBufferLMC : public profiler_screenshots::AsyncReadbackBuffer { public: AsyncReadbackBufferLMC(mozilla::layers::AsyncReadbackBuffer* aARB, Compositor* aCompositor) : profiler_screenshots::AsyncReadbackBuffer(aARB->GetSize()), mARB(aARB), mCompositor(aCompositor) {} void CopyFrom(profiler_screenshots::RenderSource* aSource) override { mCompositor->ReadbackRenderTarget( static_cast(aSource)->RenderTarget(), mARB); } bool MapAndCopyInto(DataSourceSurface* aSurface, const IntSize& aReadSize) override { return mARB->MapAndCopyInto(aSurface, aReadSize); } protected: virtual ~AsyncReadbackBufferLMC() {} RefPtr mARB; Compositor* mCompositor; }; already_AddRefed WindowLMC::GetWindowContents(const gfx::IntSize& aWindowSize) { RefPtr rt = mCompositor->GetWindowRenderTarget(); if (!rt) { return nullptr; } return MakeAndAddRef(rt); } already_AddRefed WindowLMC::CreateDownscaleTarget(const gfx::IntSize& aSize) { RefPtr rt = mCompositor->CreateRenderTarget(IntRect({}, aSize), INIT_MODE_NONE); return MakeAndAddRef(rt, mCompositor); } already_AddRefed WindowLMC::CreateAsyncReadbackBuffer(const gfx::IntSize& aSize) { RefPtr carb = mCompositor->CreateAsyncReadbackBuffer(aSize); if (!carb) { return nullptr; } return MakeAndAddRef(carb, mCompositor); } } // namespace layers } // namespace mozilla