summaryrefslogtreecommitdiffstats
path: root/gfx/layers/composite/ContainerLayerComposite.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/layers/composite/ContainerLayerComposite.cpp')
-rw-r--r--gfx/layers/composite/ContainerLayerComposite.cpp820
1 files changed, 820 insertions, 0 deletions
diff --git a/gfx/layers/composite/ContainerLayerComposite.cpp b/gfx/layers/composite/ContainerLayerComposite.cpp
new file mode 100644
index 0000000000..34b91714e0
--- /dev/null
+++ b/gfx/layers/composite/ContainerLayerComposite.cpp
@@ -0,0 +1,820 @@
+/* -*- 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 "ContainerLayerComposite.h"
+#include <algorithm> // for min
+#include "FrameMetrics.h" // for FrameMetrics
+#include "Units.h" // for LayerRect, LayerPixel, etc
+#include "CompositableHost.h" // for CompositableHost
+#include "gfxEnv.h" // for gfxEnv
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/RefPtr.h" // for RefPtr
+#include "mozilla/StaticPrefs_apz.h"
+#include "mozilla/StaticPrefs_layers.h"
+#include "mozilla/UniquePtr.h" // for UniquePtr
+#include "mozilla/gfx/BaseRect.h" // for BaseRect
+#include "mozilla/gfx/Matrix.h" // for Matrix4x4
+#include "mozilla/gfx/Point.h" // for Point, IntPoint
+#include "mozilla/gfx/Rect.h" // for IntRect, Rect
+#include "mozilla/layers/APZSampler.h" // for APZSampler
+#include "mozilla/layers/BSPTree.h"
+#include "mozilla/layers/Compositor.h" // for Compositor, etc
+#include "mozilla/layers/CompositorBridgeParent.h" // for CompositorBridgeParent
+#include "mozilla/layers/CompositorTypes.h" // for DiagnosticFlags::CONTAINER
+#include "mozilla/layers/Effects.h" // for Effect, EffectChain, etc
+#include "mozilla/layers/TextureHost.h" // for CompositingRenderTarget
+#include "mozilla/layers/APZUtils.h" // for AsyncTransform
+#include "mozilla/layers/LayerManagerCompositeUtils.h"
+#include "mozilla/layers/LayerMetricsWrapper.h" // for LayerMetricsWrapper
+#include "mozilla/layers/LayersHelpers.h"
+#include "mozilla/mozalloc.h" // for operator delete, etc
+#include "mozilla/RefPtr.h" // for nsRefPtr
+#include "nsDebug.h" // for NS_ASSERTION
+#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
+#include "nsISupportsUtils.h" // for NS_ADDREF, NS_RELEASE
+#include "nsRegion.h" // for nsIntRegion
+#include "nsTArray.h" // for AutoTArray
+#include <stack>
+#include "TextRenderer.h" // for TextRenderer
+#include <vector>
+#include "GeckoProfiler.h" // for GeckoProfiler
+
+static mozilla::LazyLogModule sGfxCullLog("gfx.culling");
+#define CULLING_LOG(...) MOZ_LOG(sGfxCullLog, LogLevel::Debug, (__VA_ARGS__))
+
+#define DUMP(...) \
+ do { \
+ if (gfxEnv::DumpDebug()) { \
+ printf_stderr(__VA_ARGS__); \
+ } \
+ } while (0)
+#define XYWH(k) (k).X(), (k).Y(), (k).Width(), (k).Height()
+#define XY(k) (k).X(), (k).Y()
+#define WH(k) (k).Width(), (k).Height()
+
+namespace mozilla {
+namespace layers {
+
+using namespace gfx;
+
+static void DrawLayerInfo(const RenderTargetIntRect& aClipRect,
+ LayerManagerComposite* aManager, Layer* aLayer) {
+ if (aLayer->GetType() == Layer::LayerType::TYPE_CONTAINER) {
+ // XXX - should figure out a way to render this, but for now this
+ // is hard to do, since it will often get superimposed over the first
+ // child of the layer, which is bad.
+ return;
+ }
+
+ std::stringstream ss;
+ aLayer->PrintInfo(ss, "");
+
+ LayerIntRegion visibleRegion = aLayer->GetVisibleRegion();
+
+ uint32_t maxWidth =
+ std::min<uint32_t>(visibleRegion.GetBounds().Width(), 500);
+
+ IntPoint topLeft = visibleRegion.GetBounds().ToUnknownRect().TopLeft();
+ aManager->GetTextRenderer()->RenderText(
+ aManager->GetCompositor(), ss.str().c_str(), topLeft,
+ aLayer->GetEffectiveTransform(), 16, maxWidth);
+}
+
+static void PrintUniformityInfo(Layer* aLayer) {
+#if defined(MOZ_GECKO_PROFILER)
+ if (!profiler_thread_is_being_profiled()) {
+ return;
+ }
+
+ // Don't want to print a log for smaller layers
+ if (aLayer->GetLocalVisibleRegion().GetBounds().Width() < 300 ||
+ aLayer->GetLocalVisibleRegion().GetBounds().Height() < 300) {
+ return;
+ }
+
+ Matrix4x4 transform = aLayer->AsHostLayer()->GetShadowBaseTransform();
+ if (!transform.Is2D()) {
+ return;
+ }
+
+ Point translation = transform.As2D().GetTranslation();
+
+ // Contains the translation applied to a 2d layer so we can track the layer
+ // position at each frame.
+ struct LayerTranslationMarker {
+ static constexpr Span<const char> MarkerTypeName() {
+ return MakeStringSpan("LayerTranslation");
+ }
+ static void StreamJSONMarkerData(
+ baseprofiler::SpliceableJSONWriter& aWriter,
+ ProfileBufferRawPointer<layers::Layer> aLayer, gfx::Point aPoint) {
+ const size_t bufferSize = 32;
+ char buffer[bufferSize];
+ SprintfLiteral(buffer, "%p", aLayer.mRawPointer);
+
+ aWriter.StringProperty("layer", buffer);
+ aWriter.IntProperty("x", aPoint.x);
+ aWriter.IntProperty("y", aPoint.y);
+ }
+ static MarkerSchema MarkerTypeDisplay() {
+ using MS = MarkerSchema;
+ MS schema{MS::Location::markerChart, MS::Location::markerTable};
+ schema.AddKeyLabelFormat("layer", "Layer", MS::Format::string);
+ schema.AddKeyLabelFormat("x", "X", MS::Format::integer);
+ schema.AddKeyLabelFormat("y", "Y", MS::Format::integer);
+ return schema;
+ }
+ };
+
+ profiler_add_marker("LayerTranslation", geckoprofiler::category::GRAPHICS, {},
+ LayerTranslationMarker{},
+ WrapProfileBufferRawPointer(aLayer), translation);
+#endif
+}
+
+static Maybe<gfx::Polygon> SelectLayerGeometry(
+ const Maybe<gfx::Polygon>& aParentGeometry,
+ const Maybe<gfx::Polygon>& aChildGeometry) {
+ // Both the parent and the child layer were split.
+ if (aParentGeometry && aChildGeometry) {
+ return Some(aParentGeometry->ClipPolygon(*aChildGeometry));
+ }
+
+ // The parent layer was split.
+ if (aParentGeometry) {
+ return aParentGeometry;
+ }
+
+ // The child layer was split.
+ if (aChildGeometry) {
+ return aChildGeometry;
+ }
+
+ // No split.
+ return Nothing();
+}
+
+void TransformLayerGeometry(Layer* aLayer, Maybe<gfx::Polygon>& aGeometry) {
+ Layer* parent = aLayer;
+ gfx::Matrix4x4 transform;
+
+ // Collect all parent transforms.
+ while (parent != nullptr && !parent->Is3DContextLeaf()) {
+ transform = transform * parent->GetLocalTransform();
+ parent = parent->GetParent();
+ }
+
+ // Transform the geometry to the parent 3D context leaf coordinate space.
+ transform = transform.ProjectTo2D();
+
+ if (!transform.IsSingular()) {
+ aGeometry->TransformToScreenSpace(transform.Inverse(), transform);
+ } else {
+ // Discard the geometry since the result might not be correct.
+ aGeometry.reset();
+ }
+}
+
+template <class ContainerT>
+static gfx::IntRect ContainerVisibleRect(ContainerT* aContainer) {
+ gfx::IntRect surfaceRect =
+ aContainer->GetLocalVisibleRegion().GetBounds().ToUnknownRect();
+ return surfaceRect;
+}
+
+/* all of the per-layer prepared data we need to maintain */
+struct PreparedLayer {
+ PreparedLayer(Layer* aLayer, RenderTargetIntRect aClipRect,
+ Maybe<gfx::Polygon>&& aGeometry)
+ : mLayer(aLayer), mClipRect(aClipRect), mGeometry(std::move(aGeometry)) {}
+
+ RefPtr<Layer> mLayer;
+ RenderTargetIntRect mClipRect;
+ Maybe<Polygon> mGeometry;
+};
+
+/* all of the prepared data that we need in RenderLayer() */
+struct PreparedData {
+ RefPtr<CompositingRenderTarget> mTmpTarget;
+ AutoTArray<PreparedLayer, 12> mLayers;
+ bool mNeedsSurfaceCopy;
+};
+
+// ContainerPrepare is shared between RefLayer and ContainerLayer
+template <class ContainerT>
+void ContainerPrepare(ContainerT* aContainer, LayerManagerComposite* aManager,
+ const RenderTargetIntRect& aClipRect) {
+ // We can end up calling prepare multiple times if we duplicated
+ // layers due to preserve-3d plane splitting. The results
+ // should be identical, so we only need to do it once.
+ if (aContainer->mPrepared) {
+ return;
+ }
+ aContainer->mPrepared = MakeUnique<PreparedData>();
+ aContainer->mPrepared->mNeedsSurfaceCopy = false;
+
+ const ContainerLayerComposite::SortMode sortMode =
+ aManager->GetCompositor()->SupportsLayerGeometry()
+ ? ContainerLayerComposite::SortMode::WITH_GEOMETRY
+ : ContainerLayerComposite::SortMode::WITHOUT_GEOMETRY;
+
+ nsTArray<LayerPolygon> polygons =
+ aContainer->SortChildrenBy3DZOrder(sortMode);
+
+ for (LayerPolygon& layer : polygons) {
+ LayerComposite* layerToRender =
+ static_cast<LayerComposite*>(layer.layer->ImplData());
+
+ RenderTargetIntRect clipRect =
+ layerToRender->GetLayer()->CalculateScissorRect(aClipRect);
+
+ if (layerToRender->GetLayer()->IsBackfaceHidden()) {
+ continue;
+ }
+
+ // We don't want to skip container layers because otherwise their mPrepared
+ // may be null which is not allowed.
+ if (!layerToRender->GetLayer()->AsContainerLayer()) {
+ if (!layerToRender->GetLayer()->IsVisible()) {
+ CULLING_LOG("Sublayer %p has no effective visible region\n",
+ layerToRender->GetLayer());
+ continue;
+ }
+
+ if (clipRect.IsEmpty()) {
+ CULLING_LOG("Sublayer %p has an empty world clip rect\n",
+ layerToRender->GetLayer());
+ continue;
+ }
+ }
+
+ CULLING_LOG("Preparing sublayer %p\n", layerToRender->GetLayer());
+
+ layerToRender->Prepare(clipRect);
+ aContainer->mPrepared->mLayers.AppendElement(PreparedLayer(
+ layerToRender->GetLayer(), clipRect, std::move(layer.geometry)));
+ }
+
+ CULLING_LOG("Preparing container layer %p\n", aContainer->GetLayer());
+
+ /**
+ * Setup our temporary surface for rendering the contents of this container.
+ */
+
+ gfx::IntRect surfaceRect = ContainerVisibleRect(aContainer);
+ if (surfaceRect.IsEmpty()) {
+ return;
+ }
+
+ bool surfaceCopyNeeded;
+ // DefaultComputeSupportsComponentAlphaChildren can mutate aContainer so call
+ // it unconditionally
+ aContainer->DefaultComputeSupportsComponentAlphaChildren(&surfaceCopyNeeded);
+ if (aContainer->UseIntermediateSurface()) {
+ if (!surfaceCopyNeeded) {
+ RefPtr<CompositingRenderTarget> surface = nullptr;
+
+ RefPtr<CompositingRenderTarget>& lastSurf =
+ aContainer->mLastIntermediateSurface;
+ if (lastSurf && !aContainer->mChildrenChanged &&
+ lastSurf->GetRect().IsEqualEdges(surfaceRect)) {
+ surface = lastSurf;
+ }
+
+ if (!surface) {
+ // If we don't need a copy we can render to the intermediate now to
+ // avoid unecessary render target switching. This brings a big perf
+ // boost on mobile gpus.
+ surface = CreateOrRecycleTarget(aContainer, aManager);
+
+ MOZ_PERFORMANCE_WARNING(
+ "gfx",
+ "[%p] Container layer requires intermediate surface rendering\n",
+ aContainer);
+ RenderIntermediate(aContainer, aManager, aClipRect.ToUnknownRect(),
+ surface);
+ aContainer->SetChildrenChanged(false);
+ }
+
+ aContainer->mPrepared->mTmpTarget = surface;
+ } else {
+ MOZ_PERFORMANCE_WARNING(
+ "gfx", "[%p] Container layer requires intermediate surface copy\n",
+ aContainer);
+ aContainer->mPrepared->mNeedsSurfaceCopy = true;
+ aContainer->mLastIntermediateSurface = nullptr;
+ }
+ } else {
+ aContainer->mLastIntermediateSurface = nullptr;
+ }
+}
+
+template <class ContainerT>
+void RenderMinimap(ContainerT* aContainer, const RefPtr<APZSampler>& aSampler,
+ LayerManagerComposite* aManager,
+ const RenderTargetIntRect& aClipRect, Layer* aLayer) {
+ Compositor* compositor = aManager->GetCompositor();
+ MOZ_ASSERT(aSampler);
+
+ if (aLayer->GetScrollMetadataCount() < 1) {
+ return;
+ }
+
+ LayerMetricsWrapper wrapper(aLayer, 0);
+ if (!wrapper.GetApzc()) {
+ return;
+ }
+ const FrameMetrics& fm = wrapper.Metrics();
+ MOZ_ASSERT(fm.IsScrollable());
+
+ ParentLayerPoint scrollOffset =
+ aSampler->GetCurrentAsyncScrollOffset(wrapper);
+
+ // Options
+ const int verticalPadding = 10;
+ const int horizontalPadding = 5;
+ gfx::DeviceColor backgroundColor(0.3f, 0.3f, 0.3f, 0.3f);
+ gfx::DeviceColor tileActiveColor(1, 1, 1, 0.4f);
+ gfx::DeviceColor tileBorderColor(0, 0, 0, 0.1f);
+ gfx::DeviceColor pageBorderColor(0, 0, 0);
+ gfx::DeviceColor criticalDisplayPortColor(1.f, 1.f, 0);
+ gfx::DeviceColor displayPortColor(0, 1.f, 0);
+ gfx::DeviceColor layoutPortColor(1.f, 0, 0);
+ gfx::DeviceColor visualPortColor(0, 0, 1.f, 0.3f);
+
+ // Rects
+ ParentLayerRect compositionBounds = fm.GetCompositionBounds();
+ LayerRect scrollRect = fm.GetScrollableRect() * fm.LayersPixelsPerCSSPixel();
+ LayerRect visualRect =
+ ParentLayerRect(scrollOffset, compositionBounds.Size()) /
+ LayerToParentLayerScale(1);
+ LayerRect dp = (fm.GetDisplayPort() + fm.GetLayoutScrollOffset()) *
+ fm.LayersPixelsPerCSSPixel();
+ Maybe<LayerRect> layoutRect;
+ Maybe<LayerRect> cdp;
+ if (fm.IsRootContent()) {
+ CSSRect viewport = aSampler->GetCurrentAsyncLayoutViewport(wrapper);
+ layoutRect = Some(viewport * fm.LayersPixelsPerCSSPixel());
+ }
+ if (!fm.GetCriticalDisplayPort().IsEmpty()) {
+ cdp = Some((fm.GetCriticalDisplayPort() + fm.GetLayoutScrollOffset()) *
+ fm.LayersPixelsPerCSSPixel());
+ }
+
+ // Don't render trivial minimap. They can show up from textboxes and other
+ // tiny frames.
+ if (visualRect.Width() < 64 && visualRect.Height() < 64) {
+ return;
+ }
+
+ // Compute a scale with an appropriate aspect ratio
+ // We allocate up to 100px of width and the height of this layer.
+ float scaleFactor;
+ float scaleFactorX;
+ float scaleFactorY;
+ Rect dest = Rect(aClipRect.ToUnknownRect());
+ if (aLayer->GetLocalClipRect()) {
+ dest = Rect(aLayer->GetLocalClipRect().value().ToUnknownRect());
+ } else {
+ dest = aContainer->GetEffectiveTransform().Inverse().TransformBounds(dest);
+ }
+ dest = dest.Intersect(compositionBounds.ToUnknownRect());
+ scaleFactorX = std::min(100.f, dest.Width() - (2 * horizontalPadding)) /
+ scrollRect.Width();
+ scaleFactorY = (dest.Height() - (2 * verticalPadding)) / scrollRect.Height();
+ scaleFactor = std::min(scaleFactorX, scaleFactorY);
+ if (scaleFactor <= 0) {
+ return;
+ }
+
+ Matrix4x4 transform = Matrix4x4::Scaling(scaleFactor, scaleFactor, 1);
+ transform.PostTranslate(horizontalPadding + dest.X(),
+ verticalPadding + dest.Y(), 0);
+
+ Rect transformedScrollRect =
+ transform.TransformBounds(scrollRect.ToUnknownRect());
+
+ IntRect clipRect =
+ RoundedOut(aContainer->GetEffectiveTransform().TransformBounds(
+ transformedScrollRect));
+
+ // Render the scrollable area.
+ compositor->FillRect(transformedScrollRect, backgroundColor, clipRect,
+ aContainer->GetEffectiveTransform());
+ compositor->SlowDrawRect(transformedScrollRect, pageBorderColor, clipRect,
+ aContainer->GetEffectiveTransform());
+
+ // Render the displayport.
+ Rect r = transform.TransformBounds(dp.ToUnknownRect());
+ compositor->FillRect(r, tileActiveColor, clipRect,
+ aContainer->GetEffectiveTransform());
+ compositor->SlowDrawRect(r, displayPortColor, clipRect,
+ aContainer->GetEffectiveTransform());
+
+ // Render the critical displayport if there is one
+ if (cdp) {
+ r = transform.TransformBounds(cdp->ToUnknownRect());
+ compositor->SlowDrawRect(r, criticalDisplayPortColor, clipRect,
+ aContainer->GetEffectiveTransform());
+ }
+
+ // Render the layout viewport if it exists (which is only in the root
+ // content APZC).
+ if (layoutRect) {
+ r = transform.TransformBounds(layoutRect->ToUnknownRect());
+ compositor->SlowDrawRect(r, layoutPortColor, clipRect,
+ aContainer->GetEffectiveTransform());
+ }
+
+ // Render the visual viewport.
+ r = transform.TransformBounds(visualRect.ToUnknownRect());
+ compositor->SlowDrawRect(r, visualPortColor, clipRect,
+ aContainer->GetEffectiveTransform(), 2);
+}
+
+template <class ContainerT>
+void RenderLayers(ContainerT* aContainer, LayerManagerComposite* aManager,
+ const RenderTargetIntRect& aClipRect,
+ const Maybe<gfx::Polygon>& aGeometry) {
+ Compositor* compositor = aManager->GetCompositor();
+
+ RefPtr<APZSampler> sampler;
+ if (CompositorBridgeParent* cbp = compositor->GetCompositorBridgeParent()) {
+ sampler = cbp->GetAPZSampler();
+ }
+
+ for (size_t i = 0u; i < aContainer->mPrepared->mLayers.Length(); i++) {
+ PreparedLayer& preparedData = aContainer->mPrepared->mLayers[i];
+
+ const gfx::IntRect clipRect = preparedData.mClipRect.ToUnknownRect();
+ LayerComposite* layerToRender =
+ static_cast<LayerComposite*>(preparedData.mLayer->ImplData());
+ const Maybe<gfx::Polygon>& childGeometry = preparedData.mGeometry;
+
+ Layer* layer = layerToRender->GetLayer();
+
+ if (layerToRender->HasStaleCompositor()) {
+ continue;
+ }
+
+ if (StaticPrefs::layers_acceleration_draw_fps()) {
+ for (const auto& metadata : layer->GetAllScrollMetadata()) {
+ if (metadata.IsApzForceDisabled()) {
+ aManager->DisabledApzWarning();
+ break;
+ }
+ }
+ }
+
+ if (layerToRender->HasLayerBeenComposited()) {
+ // Composer2D will compose this layer so skip GPU composition
+ // this time. The flag will be reset for the next composition phase
+ // at the beginning of LayerManagerComposite::Rener().
+ gfx::IntRect clearRect = layerToRender->GetClearRect();
+ if (!clearRect.IsEmpty()) {
+ // Clear layer's visible rect on FrameBuffer with transparent pixels
+ gfx::Rect fbRect(clearRect.X(), clearRect.Y(), clearRect.Width(),
+ clearRect.Height());
+ compositor->ClearRect(fbRect);
+ layerToRender->SetClearRect(gfx::IntRect(0, 0, 0, 0));
+ }
+ } else {
+ // Since we force an intermediate surface for nested 3D contexts,
+ // aGeometry and childGeometry are both in the same coordinate space.
+ Maybe<gfx::Polygon> geometry =
+ SelectLayerGeometry(aGeometry, childGeometry);
+
+ // If we are dealing with a nested 3D context, we might need to transform
+ // the geometry back to the coordinate space of the current layer before
+ // rendering the layer.
+ ContainerLayer* container = layer->AsContainerLayer();
+ const bool isLeafLayer =
+ !container || container->UseIntermediateSurface();
+
+ if (geometry && isLeafLayer) {
+ TransformLayerGeometry(layer, geometry);
+ }
+
+ layerToRender->RenderLayer(clipRect, geometry);
+ }
+
+ if (StaticPrefs::layers_uniformity_info_AtStartup()) {
+ PrintUniformityInfo(layer);
+ }
+
+ if (StaticPrefs::layers_draw_layer_info()) {
+ DrawLayerInfo(preparedData.mClipRect, aManager, layer);
+ }
+
+ // Draw a border around scrollable layers.
+ // A layer can be scrolled by multiple scroll frames. Draw a border
+ // for each.
+ // Within the list of scroll frames for a layer, the layer border for a
+ // scroll frame lower down is affected by the async transforms on scroll
+ // frames higher up, so loop from the top down, and accumulate an async
+ // transform as we go along.
+ Matrix4x4 asyncTransform;
+ if (sampler) {
+ for (uint32_t i = layer->GetScrollMetadataCount(); i > 0; --i) {
+ LayerMetricsWrapper wrapper(layer, i - 1);
+ if (wrapper.GetApzc()) {
+ MOZ_ASSERT(wrapper.Metrics().IsScrollable());
+ // Since the composition bounds are in the parent layer's coordinates,
+ // use the parent's effective transform rather than the layer's own.
+ ParentLayerRect compositionBounds =
+ wrapper.Metrics().GetCompositionBounds();
+ aManager->GetCompositor()->DrawDiagnostics(
+ DiagnosticFlags::CONTAINER, compositionBounds.ToUnknownRect(),
+ aClipRect.ToUnknownRect(),
+ asyncTransform * aContainer->GetEffectiveTransform());
+ asyncTransform =
+ sampler->GetCurrentAsyncTransformWithOverscroll(wrapper)
+ .ToUnknownMatrix() *
+ asyncTransform;
+ }
+ }
+
+ if (StaticPrefs::apz_minimap_enabled()) {
+ RenderMinimap(aContainer, sampler, aManager, aClipRect, layer);
+ }
+ }
+
+ // invariant: our GL context should be current here, I don't think we can
+ // assert it though
+ }
+}
+
+template <class ContainerT>
+RefPtr<CompositingRenderTarget> CreateOrRecycleTarget(
+ ContainerT* aContainer, LayerManagerComposite* aManager) {
+ Compositor* compositor = aManager->GetCompositor();
+ SurfaceInitMode mode = INIT_MODE_CLEAR;
+ gfx::IntRect surfaceRect = ContainerVisibleRect(aContainer);
+ if (aContainer->GetLocalVisibleRegion().GetNumRects() == 1 &&
+ (aContainer->GetContentFlags() & Layer::CONTENT_OPAQUE)) {
+ mode = INIT_MODE_NONE;
+ }
+
+ RefPtr<CompositingRenderTarget>& lastSurf =
+ aContainer->mLastIntermediateSurface;
+ if (lastSurf && lastSurf->GetRect().IsEqualEdges(surfaceRect)) {
+ if (mode == INIT_MODE_CLEAR) {
+ lastSurf->ClearOnBind();
+ }
+
+ return lastSurf;
+ } else {
+ lastSurf = compositor->CreateRenderTarget(surfaceRect, mode);
+
+ return lastSurf;
+ }
+}
+
+template <class ContainerT>
+RefPtr<CompositingRenderTarget> CreateTemporaryTargetAndCopyFromBackground(
+ ContainerT* aContainer, LayerManagerComposite* aManager) {
+ Compositor* compositor = aManager->GetCompositor();
+ gfx::IntRect visibleRect =
+ aContainer->GetLocalVisibleRegion().GetBounds().ToUnknownRect();
+ RefPtr<CompositingRenderTarget> previousTarget =
+ compositor->GetCurrentRenderTarget();
+ gfx::IntRect surfaceRect =
+ gfx::IntRect(visibleRect.X(), visibleRect.Y(), visibleRect.Width(),
+ visibleRect.Height());
+
+ gfx::IntPoint sourcePoint = gfx::IntPoint(visibleRect.X(), visibleRect.Y());
+
+ gfx::Matrix4x4 transform = aContainer->GetEffectiveTransform();
+ DebugOnly<gfx::Matrix> transform2d;
+ MOZ_ASSERT(transform.Is2D(&transform2d) &&
+ !gfx::ThebesMatrix(transform2d).HasNonIntegerTranslation());
+ sourcePoint += gfx::IntPoint::Truncate(transform._41, transform._42);
+
+ sourcePoint -= previousTarget->GetOrigin();
+
+ return compositor->CreateRenderTargetFromSource(surfaceRect, previousTarget,
+ sourcePoint);
+}
+
+template <class ContainerT>
+void RenderIntermediate(ContainerT* aContainer, LayerManagerComposite* aManager,
+ const gfx::IntRect& aClipRect,
+ RefPtr<CompositingRenderTarget> surface) {
+ Compositor* compositor = aManager->GetCompositor();
+ RefPtr<CompositingRenderTarget> previousTarget =
+ compositor->GetCurrentRenderTarget();
+
+ if (!surface) {
+ return;
+ }
+
+ compositor->SetRenderTarget(surface);
+ // pre-render all of the layers into our temporary
+ RenderLayers(aContainer, aManager,
+ RenderTargetIntRect::FromUnknownRect(aClipRect), Nothing());
+
+ // Unbind the current surface and rebind the previous one.
+ compositor->SetRenderTarget(previousTarget);
+}
+
+template <class ContainerT>
+void ContainerRender(ContainerT* aContainer, LayerManagerComposite* aManager,
+ const gfx::IntRect& aClipRect,
+ const Maybe<gfx::Polygon>& aGeometry) {
+ MOZ_ASSERT(aContainer->mPrepared);
+
+ if (aContainer->UseIntermediateSurface()) {
+ RefPtr<CompositingRenderTarget> surface;
+
+ if (aContainer->mPrepared->mNeedsSurfaceCopy) {
+ // we needed to copy the background so we waited until now to render the
+ // intermediate
+ surface =
+ CreateTemporaryTargetAndCopyFromBackground(aContainer, aManager);
+ RenderIntermediate(aContainer, aManager, aClipRect, surface);
+ } else {
+ surface = aContainer->mPrepared->mTmpTarget;
+ }
+
+ if (!surface) {
+ return;
+ }
+
+ gfx::Rect visibleRect(
+ aContainer->GetLocalVisibleRegion().GetBounds().ToUnknownRect());
+
+ RefPtr<Compositor> compositor = aManager->GetCompositor();
+#ifdef MOZ_DUMP_PAINTING
+ if (gfxEnv::DumpCompositorTextures()) {
+ RefPtr<gfx::DataSourceSurface> surf = surface->Dump(compositor);
+ if (surf) {
+ WriteSnapshotToDumpFile(aContainer, surf);
+ }
+ }
+#endif
+
+ RefPtr<ContainerT> container = aContainer;
+ RenderWithAllMasks(aContainer, compositor, aClipRect,
+ [&, surface, compositor, container](
+ EffectChain& effectChain, const IntRect& clipRect) {
+ effectChain.mPrimaryEffect =
+ new EffectRenderTarget(surface);
+
+ compositor->DrawGeometry(
+ visibleRect, clipRect, effectChain,
+ container->GetEffectiveOpacity(),
+ container->GetEffectiveTransform(), aGeometry);
+ });
+
+ } else {
+ RenderLayers(aContainer, aManager,
+ RenderTargetIntRect::FromUnknownRect(aClipRect), aGeometry);
+ }
+
+ // If it is a scrollable container layer with no child layers, and one of the
+ // APZCs attached to it has a nonempty async transform, then that transform is
+ // not applied to any visible content. Display a warning box (conditioned on
+ // the FPS display being enabled).
+ if (StaticPrefs::layers_acceleration_draw_fps() &&
+ aContainer->IsScrollableWithoutContent()) {
+ RefPtr<APZSampler> sampler =
+ aManager->GetCompositor()->GetCompositorBridgeParent()->GetAPZSampler();
+ // Since aContainer doesn't have any children we can just iterate from the
+ // top metrics on it down to the bottom using GetFirstChild and not worry
+ // about walking onto another underlying layer.
+ for (LayerMetricsWrapper i(aContainer); i; i = i.GetFirstChild()) {
+ if (sampler->HasUnusedAsyncTransform(i)) {
+ aManager->UnusedApzTransformWarning();
+ break;
+ }
+ }
+ }
+}
+
+ContainerLayerComposite::ContainerLayerComposite(
+ LayerManagerComposite* aManager)
+ : ContainerLayer(aManager, nullptr), LayerComposite(aManager) {
+ MOZ_COUNT_CTOR(ContainerLayerComposite);
+ mImplData = static_cast<LayerComposite*>(this);
+}
+
+ContainerLayerComposite::~ContainerLayerComposite() {
+ MOZ_COUNT_DTOR(ContainerLayerComposite);
+
+ // We don't Destroy() on destruction here because this destructor
+ // can be called after remote content has crashed, and it may not be
+ // safe to free the IPC resources of our children. Those resources
+ // are automatically cleaned up by IPDL-generated code.
+ //
+ // In the common case of normal shutdown, either
+ // LayerManagerComposite::Destroy(), a parent
+ // *ContainerLayerComposite::Destroy(), or Disconnect() will trigger
+ // cleanup of our resources.
+ RemoveAllChildren();
+}
+
+void ContainerLayerComposite::Destroy() {
+ if (!mDestroyed) {
+ while (mFirstChild) {
+ GetFirstChildComposite()->Destroy();
+ RemoveChild(mFirstChild);
+ }
+ mDestroyed = true;
+ }
+}
+
+LayerComposite* ContainerLayerComposite::GetFirstChildComposite() {
+ if (!mFirstChild) {
+ return nullptr;
+ }
+ return static_cast<LayerComposite*>(mFirstChild->AsHostLayer());
+}
+
+void ContainerLayerComposite::Cleanup() {
+ mPrepared = nullptr;
+
+ for (Layer* l = GetFirstChild(); l; l = l->GetNextSibling()) {
+ static_cast<LayerComposite*>(l->AsHostLayer())->Cleanup();
+ }
+}
+
+void ContainerLayerComposite::RenderLayer(
+ const gfx::IntRect& aClipRect, const Maybe<gfx::Polygon>& aGeometry) {
+ ContainerRender(this, mCompositeManager, aClipRect, aGeometry);
+}
+
+void ContainerLayerComposite::Prepare(const RenderTargetIntRect& aClipRect) {
+ ContainerPrepare(this, mCompositeManager, aClipRect);
+}
+
+void ContainerLayerComposite::CleanupResources() {
+ mLastIntermediateSurface = nullptr;
+ mPrepared = nullptr;
+
+ for (Layer* l = GetFirstChild(); l; l = l->GetNextSibling()) {
+ static_cast<LayerComposite*>(l->AsHostLayer())->CleanupResources();
+ }
+}
+
+const LayerIntRegion& ContainerLayerComposite::GetShadowVisibleRegion() {
+ if (!UseIntermediateSurface()) {
+ RecomputeShadowVisibleRegionFromChildren();
+ }
+
+ return mShadowVisibleRegion;
+}
+
+const LayerIntRegion& RefLayerComposite::GetShadowVisibleRegion() {
+ if (!UseIntermediateSurface()) {
+ RecomputeShadowVisibleRegionFromChildren();
+ }
+
+ return mShadowVisibleRegion;
+}
+
+RefLayerComposite::RefLayerComposite(LayerManagerComposite* aManager)
+ : RefLayer(aManager, nullptr), LayerComposite(aManager) {
+ mImplData = static_cast<LayerComposite*>(this);
+}
+
+RefLayerComposite::~RefLayerComposite() { Destroy(); }
+
+void RefLayerComposite::Destroy() {
+ MOZ_ASSERT(!mFirstChild);
+ mDestroyed = true;
+}
+
+LayerComposite* RefLayerComposite::GetFirstChildComposite() {
+ if (!mFirstChild) {
+ return nullptr;
+ }
+ return static_cast<LayerComposite*>(mFirstChild->AsHostLayer());
+}
+
+void RefLayerComposite::RenderLayer(const gfx::IntRect& aClipRect,
+ const Maybe<gfx::Polygon>& aGeometry) {
+ ContainerRender(this, mCompositeManager, aClipRect, aGeometry);
+}
+
+void RefLayerComposite::Prepare(const RenderTargetIntRect& aClipRect) {
+ ContainerPrepare(this, mCompositeManager, aClipRect);
+}
+
+void RefLayerComposite::Cleanup() {
+ mPrepared = nullptr;
+
+ for (Layer* l = GetFirstChild(); l; l = l->GetNextSibling()) {
+ static_cast<LayerComposite*>(l->AsHostLayer())->Cleanup();
+ }
+}
+
+void RefLayerComposite::CleanupResources() {
+ mLastIntermediateSurface = nullptr;
+ mPrepared = nullptr;
+}
+
+} // namespace layers
+} // namespace mozilla