summaryrefslogtreecommitdiffstats
path: root/gfx/layers/composite
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
commit2aa4a82499d4becd2284cdb482213d541b8804dd (patch)
treeb80bf8bf13c3766139fbacc530efd0dd9d54394c /gfx/layers/composite
parentInitial commit. (diff)
downloadfirefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz
firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'gfx/layers/composite')
-rw-r--r--gfx/layers/composite/AsyncCompositionManager.cpp1314
-rw-r--r--gfx/layers/composite/AsyncCompositionManager.h273
-rw-r--r--gfx/layers/composite/CanvasLayerComposite.cpp141
-rw-r--r--gfx/layers/composite/CanvasLayerComposite.h72
-rw-r--r--gfx/layers/composite/ColorLayerComposite.cpp63
-rw-r--r--gfx/layers/composite/ColorLayerComposite.h62
-rw-r--r--gfx/layers/composite/CompositableHost.cpp167
-rw-r--r--gfx/layers/composite/CompositableHost.h278
-rw-r--r--gfx/layers/composite/ConsolasFontData.h457
-rw-r--r--gfx/layers/composite/ContainerLayerComposite.cpp820
-rw-r--r--gfx/layers/composite/ContainerLayerComposite.h197
-rw-r--r--gfx/layers/composite/ContentHost.cpp460
-rw-r--r--gfx/layers/composite/ContentHost.h236
-rw-r--r--gfx/layers/composite/Diagnostics.cpp96
-rw-r--r--gfx/layers/composite/Diagnostics.h100
-rw-r--r--gfx/layers/composite/FPSCounter.cpp343
-rw-r--r--gfx/layers/composite/FPSCounter.h102
-rw-r--r--gfx/layers/composite/FontData.h304
-rw-r--r--gfx/layers/composite/FrameUniformityData.cpp145
-rw-r--r--gfx/layers/composite/FrameUniformityData.h80
-rw-r--r--gfx/layers/composite/GPUVideoTextureHost.cpp295
-rw-r--r--gfx/layers/composite/GPUVideoTextureHost.h98
-rw-r--r--gfx/layers/composite/ImageComposite.cpp385
-rw-r--r--gfx/layers/composite/ImageComposite.h139
-rw-r--r--gfx/layers/composite/ImageHost.cpp454
-rw-r--r--gfx/layers/composite/ImageHost.h138
-rw-r--r--gfx/layers/composite/ImageLayerComposite.cpp209
-rw-r--r--gfx/layers/composite/ImageLayerComposite.h78
-rw-r--r--gfx/layers/composite/LayerManagerComposite.cpp1780
-rw-r--r--gfx/layers/composite/LayerManagerComposite.h729
-rw-r--r--gfx/layers/composite/LayerManagerCompositeUtils.h160
-rw-r--r--gfx/layers/composite/PaintCounter.cpp79
-rw-r--r--gfx/layers/composite/PaintCounter.h53
-rw-r--r--gfx/layers/composite/PaintedLayerComposite.cpp169
-rw-r--r--gfx/layers/composite/PaintedLayerComposite.h86
-rw-r--r--gfx/layers/composite/TextRenderer.cpp245
-rw-r--r--gfx/layers/composite/TextRenderer.h95
-rw-r--r--gfx/layers/composite/TextureHost.cpp1371
-rw-r--r--gfx/layers/composite/TextureHost.h1082
-rw-r--r--gfx/layers/composite/TiledContentHost.cpp658
-rw-r--r--gfx/layers/composite/TiledContentHost.h263
-rw-r--r--gfx/layers/composite/X11TextureHost.cpp103
-rw-r--r--gfx/layers/composite/X11TextureHost.h65
43 files changed, 14444 insertions, 0 deletions
diff --git a/gfx/layers/composite/AsyncCompositionManager.cpp b/gfx/layers/composite/AsyncCompositionManager.cpp
new file mode 100644
index 0000000000..3c155d0df0
--- /dev/null
+++ b/gfx/layers/composite/AsyncCompositionManager.cpp
@@ -0,0 +1,1314 @@
+/* -*- 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 "mozilla/layers/AsyncCompositionManager.h"
+#include <stdint.h> // for uint32_t
+#include "LayerManagerComposite.h" // for LayerManagerComposite, etc
+#include "Layers.h" // for Layer, ContainerLayer, etc
+#include "gfxPoint.h" // for gfxPoint, gfxSize
+#include "mozilla/ServoBindings.h" // for Servo_AnimationValue_GetOpacity, etc
+#include "mozilla/ScopeExit.h" // for MakeScopeExit
+#include "mozilla/StaticPrefs_apz.h"
+#include "mozilla/StaticPrefs_gfx.h"
+#include "mozilla/WidgetUtils.h" // for ComputeTransformForRotation
+#include "mozilla/gfx/BaseRect.h" // for BaseRect
+#include "mozilla/gfx/Point.h" // for RoundedToInt, PointTyped
+#include "mozilla/gfx/Rect.h" // for RoundedToInt, RectTyped
+#include "mozilla/gfx/ScaleFactor.h" // for ScaleFactor
+#include "mozilla/layers/AnimationHelper.h"
+#include "mozilla/layers/APZSampler.h" // for APZSampler
+#include "mozilla/layers/APZUtils.h" // for CompleteAsyncTransform
+#include "mozilla/layers/Compositor.h" // for Compositor
+#include "mozilla/layers/CompositorAnimationStorage.h" // for CompositorAnimationStorage
+#include "mozilla/layers/CompositorBridgeParent.h" // for CompositorBridgeParent, etc
+#include "mozilla/layers/CompositorThread.h"
+#include "mozilla/layers/LayerAnimationUtils.h" // for TimingFunctionToComputedTimingFunction
+#include "mozilla/layers/LayerMetricsWrapper.h" // for LayerMetricsWrapper
+#include "mozilla/layers/SampleTime.h"
+#include "nsCoord.h" // for NSAppUnitsToFloatPixels, etc
+#include "nsDebug.h" // for NS_ASSERTION, etc
+#include "nsDeviceContext.h" // for nsDeviceContext
+#include "nsDisplayList.h" // for nsDisplayTransform, etc
+#include "nsMathUtils.h" // for NS_round
+#include "nsPoint.h" // for nsPoint
+#include "nsRect.h" // for mozilla::gfx::IntRect
+#include "nsRegion.h" // for nsIntRegion
+#include "nsTArray.h" // for nsTArray, nsTArray_Impl, etc
+#include "nsTArrayForwardDeclare.h" // for nsTArray
+#include "UnitTransforms.h" // for TransformTo
+#if defined(MOZ_WIDGET_ANDROID)
+# include <android/log.h>
+# include "mozilla/layers/UiCompositorControllerParent.h"
+# include "mozilla/widget/AndroidCompositorWidget.h"
+#endif
+#include "GeckoProfiler.h"
+#include "FrameUniformityData.h"
+#include "TreeTraversal.h" // for ForEachNode, BreadthFirstSearch
+#include "VsyncSource.h"
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::gfx;
+
+static bool IsSameDimension(hal::ScreenOrientation o1,
+ hal::ScreenOrientation o2) {
+ bool isO1portrait = (o1 == hal::eScreenOrientation_PortraitPrimary ||
+ o1 == hal::eScreenOrientation_PortraitSecondary);
+ bool isO2portrait = (o2 == hal::eScreenOrientation_PortraitPrimary ||
+ o2 == hal::eScreenOrientation_PortraitSecondary);
+ return !(isO1portrait ^ isO2portrait);
+}
+
+static bool ContentMightReflowOnOrientationChange(const IntRect& rect) {
+ return rect.Width() != rect.Height();
+}
+
+AsyncCompositionManager::AsyncCompositionManager(
+ CompositorBridgeParent* aParent, HostLayerManager* aManager)
+ : mLayerManager(aManager),
+ mIsFirstPaint(true),
+ mLayersUpdated(false),
+ mReadyForCompose(true),
+ mCompositorBridge(aParent) {
+ MOZ_ASSERT(mCompositorBridge);
+}
+
+AsyncCompositionManager::~AsyncCompositionManager() = default;
+
+void AsyncCompositionManager::ResolveRefLayers(
+ CompositorBridgeParent* aCompositor, bool* aHasRemoteContent,
+ bool* aResolvePlugins) {
+ if (aHasRemoteContent) {
+ *aHasRemoteContent = false;
+ }
+
+#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
+ // If valid *aResolvePlugins indicates if we need to update plugin geometry
+ // when we walk the tree.
+ bool resolvePlugins = (aCompositor && aResolvePlugins && *aResolvePlugins);
+#endif
+
+ if (!mLayerManager->GetRoot()) {
+ // Updated the return value since this result controls completing
+ // composition.
+ if (aResolvePlugins) {
+ *aResolvePlugins = false;
+ }
+ return;
+ }
+
+ mReadyForCompose = true;
+ bool hasRemoteContent = false;
+ bool didResolvePlugins = false;
+
+ ForEachNode<ForwardIterator>(mLayerManager->GetRoot(), [&](Layer* layer) {
+ RefLayer* refLayer = layer->AsRefLayer();
+ if (!refLayer) {
+ return;
+ }
+
+ hasRemoteContent = true;
+ const CompositorBridgeParent::LayerTreeState* state =
+ CompositorBridgeParent::GetIndirectShadowTree(
+ refLayer->GetReferentId());
+ if (!state) {
+ return;
+ }
+
+ Layer* referent = state->mRoot;
+ if (!referent) {
+ return;
+ }
+
+ if (!refLayer->GetLocalVisibleRegion().IsEmpty()) {
+ hal::ScreenOrientation chromeOrientation = mTargetConfig.orientation();
+ hal::ScreenOrientation contentOrientation =
+ state->mTargetConfig.orientation();
+ if (!IsSameDimension(chromeOrientation, contentOrientation) &&
+ ContentMightReflowOnOrientationChange(
+ mTargetConfig.naturalBounds())) {
+ mReadyForCompose = false;
+ }
+ }
+
+ refLayer->ConnectReferentLayer(referent);
+
+#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
+ if (resolvePlugins) {
+ didResolvePlugins |=
+ aCompositor->UpdatePluginWindowState(refLayer->GetReferentId());
+ }
+#endif
+ });
+
+ if (aHasRemoteContent) {
+ *aHasRemoteContent = hasRemoteContent;
+ }
+ if (aResolvePlugins) {
+ *aResolvePlugins = didResolvePlugins;
+ }
+}
+
+void AsyncCompositionManager::DetachRefLayers() {
+ if (!mLayerManager->GetRoot()) {
+ return;
+ }
+
+ mReadyForCompose = false;
+
+ ForEachNodePostOrder<ForwardIterator>(
+ mLayerManager->GetRoot(), [&](Layer* layer) {
+ RefLayer* refLayer = layer->AsRefLayer();
+ if (!refLayer) {
+ return;
+ }
+
+ const CompositorBridgeParent::LayerTreeState* state =
+ CompositorBridgeParent::GetIndirectShadowTree(
+ refLayer->GetReferentId());
+ if (!state) {
+ return;
+ }
+
+ Layer* referent = state->mRoot;
+ if (referent) {
+ refLayer->DetachReferentLayer(referent);
+ }
+ });
+}
+
+void AsyncCompositionManager::ComputeRotation() {
+ if (!mTargetConfig.naturalBounds().IsEmpty()) {
+ mWorldTransform = ComputeTransformForRotation(mTargetConfig.naturalBounds(),
+ mTargetConfig.rotation());
+ }
+}
+
+static void GetBaseTransform(Layer* aLayer, Matrix4x4* aTransform) {
+ // Start with the animated transform if there is one
+ *aTransform = (aLayer->AsHostLayer()->GetShadowTransformSetByAnimation()
+ ? aLayer->GetLocalTransform()
+ : aLayer->GetTransform());
+}
+
+static void TransformClipRect(
+ Layer* aLayer, const ParentLayerToParentLayerMatrix4x4& aTransform) {
+ MOZ_ASSERT(aTransform.Is2D());
+ const Maybe<ParentLayerIntRect>& clipRect =
+ aLayer->AsHostLayer()->GetShadowClipRect();
+ if (clipRect) {
+ ParentLayerIntRect transformed = TransformBy(aTransform, *clipRect);
+ aLayer->AsHostLayer()->SetShadowClipRect(Some(transformed));
+ }
+}
+
+// Similar to TransformFixedClip(), but only transforms the fixed part of the
+// clip.
+static void TransformFixedClip(
+ Layer* aLayer, const ParentLayerToParentLayerMatrix4x4& aTransform,
+ AsyncCompositionManager::ClipParts& aClipParts) {
+ MOZ_ASSERT(aTransform.Is2D());
+ if (aClipParts.mFixedClip) {
+ *aClipParts.mFixedClip = TransformBy(aTransform, *aClipParts.mFixedClip);
+ aLayer->AsHostLayer()->SetShadowClipRect(aClipParts.Intersect());
+ }
+}
+
+/**
+ * Set the given transform as the shadow transform on the layer, assuming
+ * that the given transform already has the pre- and post-scales applied.
+ * That is, this function cancels out the pre- and post-scales from aTransform
+ * before setting it as the shadow transform on the layer, so that when
+ * the layer's effective transform is computed, the pre- and post-scales will
+ * only be applied once.
+ */
+static void SetShadowTransform(Layer* aLayer,
+ LayerToParentLayerMatrix4x4 aTransform) {
+ if (ContainerLayer* c = aLayer->AsContainerLayer()) {
+ aTransform.PreScale(1.0f / c->GetPreXScale(), 1.0f / c->GetPreYScale(), 1);
+ }
+ aTransform.PostScale(1.0f / aLayer->GetPostXScale(),
+ 1.0f / aLayer->GetPostYScale(), 1);
+ aLayer->AsHostLayer()->SetShadowBaseTransform(aTransform.ToUnknownMatrix());
+}
+
+static void TranslateShadowLayer(
+ Layer* aLayer, const ParentLayerPoint& aTranslation, bool aAdjustClipRect,
+ AsyncCompositionManager::ClipPartsCache* aClipPartsCache) {
+ // This layer might also be a scrollable layer and have an async transform.
+ // To make sure we don't clobber that, we start with the shadow transform.
+ // (i.e. GetLocalTransform() instead of GetTransform()).
+ // Note that the shadow transform is reset on every frame of composition so
+ // we don't have to worry about the adjustments compounding over successive
+ // frames.
+ LayerToParentLayerMatrix4x4 layerTransform = aLayer->GetLocalTransformTyped();
+
+ // Apply the translation to the layer transform.
+ layerTransform.PostTranslate(aTranslation);
+
+ SetShadowTransform(aLayer, layerTransform);
+ aLayer->AsHostLayer()->SetShadowTransformSetByAnimation(false);
+
+ if (aAdjustClipRect) {
+ auto transform =
+ ParentLayerToParentLayerMatrix4x4::Translation(aTranslation);
+ // If we're passed a clip parts cache, only transform the fixed part of
+ // the clip.
+ if (aClipPartsCache) {
+ auto iter = aClipPartsCache->find(aLayer);
+ MOZ_ASSERT(iter != aClipPartsCache->end());
+ TransformFixedClip(aLayer, transform, iter->second);
+ } else {
+ TransformClipRect(aLayer, transform);
+ }
+
+ // If a fixed- or sticky-position layer has a mask layer, that mask should
+ // move along with the layer, so apply the translation to the mask layer
+ // too.
+ if (Layer* maskLayer = aLayer->GetMaskLayer()) {
+ TranslateShadowLayer(maskLayer, aTranslation, false, aClipPartsCache);
+ }
+ }
+}
+
+static void AccumulateLayerTransforms(Layer* aLayer, Layer* aAncestor,
+ Matrix4x4& aMatrix) {
+ // Accumulate the transforms between this layer and the subtree root layer.
+ for (Layer* l = aLayer; l && l != aAncestor; l = l->GetParent()) {
+ Matrix4x4 transform;
+ GetBaseTransform(l, &transform);
+ aMatrix *= transform;
+ }
+}
+
+/**
+ * Finds the metrics on |aLayer| with scroll id |aScrollId|, and returns a
+ * LayerMetricsWrapper representing the (layer, metrics) pair, or the null
+ * LayerMetricsWrapper if no matching metrics could be found.
+ */
+static LayerMetricsWrapper FindMetricsWithScrollId(
+ Layer* aLayer, ScrollableLayerGuid::ViewID aScrollId) {
+ for (uint64_t i = 0; i < aLayer->GetScrollMetadataCount(); ++i) {
+ if (aLayer->GetFrameMetrics(i).GetScrollId() == aScrollId) {
+ return LayerMetricsWrapper(aLayer, i);
+ }
+ }
+ return LayerMetricsWrapper();
+}
+
+/**
+ * Checks whether the (layer, metrics) pair (aTransformedLayer,
+ * aTransformedMetrics) is on the path from |aFixedLayer| to the metrics with
+ * scroll id |aFixedWithRespectTo|, inclusive.
+ */
+static bool AsyncTransformShouldBeUnapplied(
+ Layer* aFixedLayer, ScrollableLayerGuid::ViewID aFixedWithRespectTo,
+ Layer* aTransformedLayer, ScrollableLayerGuid::ViewID aTransformedMetrics) {
+ LayerMetricsWrapper transformed =
+ FindMetricsWithScrollId(aTransformedLayer, aTransformedMetrics);
+ if (!transformed.IsValid()) {
+ return false;
+ }
+ // It's important to start at the bottom, because the fixed layer itself
+ // could have the transformed metrics, and they can be at the bottom.
+ LayerMetricsWrapper current(aFixedLayer,
+ LayerMetricsWrapper::StartAt::BOTTOM);
+ bool encounteredTransformedLayer = false;
+ // The transformed layer is on the path from |aFixedLayer| to the fixed-to
+ // layer if as we walk up the (layer, metrics) tree starting from
+ // |aFixedLayer|, we *first* encounter the transformed layer, and *then* (or
+ // at the same time) the fixed-to layer.
+ while (current) {
+ if (!encounteredTransformedLayer && current == transformed) {
+ encounteredTransformedLayer = true;
+ }
+ if (current.Metrics().GetScrollId() == aFixedWithRespectTo) {
+ return encounteredTransformedLayer;
+ }
+ current = current.GetParent();
+ // It's possible that we reach a layers id boundary before we reach an
+ // ancestor with the scroll id |aFixedWithRespectTo| (this could happen
+ // e.g. if the scroll frame with that scroll id uses containerless
+ // scrolling). In such a case, stop the walk, as a new layers id could
+ // have a different layer with scroll id |aFixedWithRespectTo| which we
+ // don't intend to match.
+ if (current && current.AsRefLayer() != nullptr) {
+ break;
+ }
+ }
+ return false;
+}
+
+// If |aLayer| is fixed or sticky, returns the scroll id of the scroll frame
+// that it's fixed or sticky to. Otherwise, returns Nothing().
+static Maybe<ScrollableLayerGuid::ViewID> IsFixedOrSticky(Layer* aLayer) {
+ bool isRootOfFixedSubtree = aLayer->GetIsFixedPosition() &&
+ !aLayer->GetParent()->GetIsFixedPosition();
+ if (isRootOfFixedSubtree) {
+ return Some(aLayer->GetFixedPositionScrollContainerId());
+ }
+ if (aLayer->GetIsStickyPosition()) {
+ return Some(aLayer->GetStickyScrollContainerId());
+ }
+ return Nothing();
+}
+
+void AsyncCompositionManager::AlignFixedAndStickyLayers(
+ Layer* aTransformedSubtreeRoot, Layer* aStartTraversalAt,
+ SideBits aStuckSides, ScrollableLayerGuid::ViewID aTransformScrollId,
+ const LayerToParentLayerMatrix4x4& aPreviousTransformForRoot,
+ const LayerToParentLayerMatrix4x4& aCurrentTransformForRoot,
+ const ScreenMargin& aFixedLayerMargins, ClipPartsCache& aClipPartsCache,
+ const ScreenMargin& aGeckoFixedLayerMargins) {
+ Layer* layer = aStartTraversalAt;
+ bool needsAsyncTransformUnapplied = false;
+ if (Maybe<ScrollableLayerGuid::ViewID> fixedTo = IsFixedOrSticky(layer)) {
+ needsAsyncTransformUnapplied = AsyncTransformShouldBeUnapplied(
+ layer, *fixedTo, aTransformedSubtreeRoot, aTransformScrollId);
+ }
+
+ // We want to process all the fixed and sticky descendants of
+ // aTransformedSubtreeRoot. Once we do encounter such a descendant, we don't
+ // need to recurse any deeper because the adjustment to the fixed or sticky
+ // layer will apply to its subtree.
+ if (!needsAsyncTransformUnapplied) {
+ for (Layer* child = layer->GetFirstChild(); child;
+ child = child->GetNextSibling()) {
+ AlignFixedAndStickyLayers(aTransformedSubtreeRoot, child, aStuckSides,
+ aTransformScrollId, aPreviousTransformForRoot,
+ aCurrentTransformForRoot, aFixedLayerMargins,
+ aClipPartsCache, aGeckoFixedLayerMargins);
+ }
+ return;
+ }
+
+ AdjustFixedOrStickyLayer(aTransformedSubtreeRoot, layer, aStuckSides,
+ aTransformScrollId, aPreviousTransformForRoot,
+ aCurrentTransformForRoot, aFixedLayerMargins,
+ aClipPartsCache, aGeckoFixedLayerMargins);
+}
+
+// Determine the amount of overlap between the 1D vector |aTranslation|
+// and the interval [aMin, aMax].
+static gfxFloat IntervalOverlap(gfxFloat aTranslation, gfxFloat aMin,
+ gfxFloat aMax) {
+ if (aTranslation > 0) {
+ return std::max(0.0, std::min(aMax, aTranslation) - std::max(aMin, 0.0));
+ }
+
+ return std::min(0.0, std::max(aMin, aTranslation) - std::min(aMax, 0.0));
+}
+
+void AsyncCompositionManager::AdjustFixedOrStickyLayer(
+ Layer* aTransformedSubtreeRoot, Layer* aFixedOrSticky, SideBits aStuckSides,
+ ScrollableLayerGuid::ViewID aTransformScrollId,
+ const LayerToParentLayerMatrix4x4& aPreviousTransformForRoot,
+ const LayerToParentLayerMatrix4x4& aCurrentTransformForRoot,
+ const ScreenMargin& aFixedLayerMargins, ClipPartsCache& aClipPartsCache,
+ const ScreenMargin& aGeckoFixedLayerMargins) {
+ Layer* layer = aFixedOrSticky;
+
+ // Insert a translation so that the position of the anchor point is the same
+ // before and after the change to the transform of aTransformedSubtreeRoot.
+
+ // Accumulate the transforms between this layer and the subtree root layer.
+ Matrix4x4 ancestorTransform;
+ if (layer != aTransformedSubtreeRoot) {
+ AccumulateLayerTransforms(layer->GetParent(), aTransformedSubtreeRoot,
+ ancestorTransform);
+ }
+ ancestorTransform.NudgeToIntegersFixedEpsilon();
+
+ // A transform creates a containing block for fixed-position descendants,
+ // so there shouldn't be a transform in between the fixed layer and
+ // the subtree root layer.
+ if (layer->GetIsFixedPosition()) {
+ MOZ_ASSERT(ancestorTransform.IsIdentity());
+ }
+
+ // Calculate the cumulative transforms between the subtree root with the
+ // old transform and the current transform.
+ // For coordinate purposes, we'll treat the subtree root layer as the
+ // "parent" layer, even though it could be a farther ancestor.
+ auto oldCumulativeTransform = ViewAs<LayerToParentLayerMatrix4x4>(
+ ancestorTransform * aPreviousTransformForRoot.ToUnknownMatrix());
+ auto newCumulativeTransform = ViewAs<LayerToParentLayerMatrix4x4>(
+ ancestorTransform * aCurrentTransformForRoot.ToUnknownMatrix());
+
+ // We're going to be inverting |newCumulativeTransform|. If it's singular,
+ // there's nothing we can do.
+ if (newCumulativeTransform.IsSingular()) {
+ return;
+ }
+
+ // Since we create container layers for fixed layers, there shouldn't
+ // a local CSS or OMTA transform on the fixed layer, either (any local
+ // transform would go onto a descendant layer inside the container
+ // layer).
+#ifdef DEBUG
+ Matrix4x4 localTransform;
+ GetBaseTransform(layer, &localTransform);
+ localTransform.NudgeToIntegersFixedEpsilon();
+ MOZ_ASSERT(localTransform.IsIdentity());
+#endif
+
+ // Now work out the translation necessary to make sure the layer doesn't
+ // move given the new sub-tree root transform.
+
+ // Get the layer's fixed anchor point, in the layer's local coordinate space
+ // (before any transform is applied).
+ LayerPoint anchor = layer->GetFixedPositionAnchor();
+
+ SideBits sideBits = layer->GetFixedPositionSides();
+ if (layer->GetIsStickyPosition()) {
+ // For sticky items, it may be that only some of the sides are actively
+ // stuck. Only take into account those sides.
+ sideBits &= aStuckSides;
+ }
+
+ // Offset the layer's anchor point to make sure fixed position content
+ // respects content document fixed position margins.
+ ScreenPoint offset = apz::ComputeFixedMarginsOffset(
+ aFixedLayerMargins, sideBits,
+ // For sticky layers, we don't need to factor aGeckoFixedLayerMargins
+ // because Gecko doesn't shift the position of sticky elements for dynamic
+ // toolbar movements.
+ layer->GetIsStickyPosition() ? ScreenMargin() : aGeckoFixedLayerMargins);
+
+ // Fixed margins only apply to layers fixed to the root, so we can view
+ // the offset in layer space.
+ LayerPoint offsetAnchor =
+ anchor + ViewAs<LayerPixel>(
+ offset, PixelCastJustification::ScreenIsParentLayerForRoot);
+
+ // Additionally transform the anchor to compensate for the change
+ // from the old transform to the new transform. We do
+ // this by using the old transform to take the offset anchor back into
+ // subtree root space, and then the inverse of the new transform
+ // to bring it back to layer space.
+ ParentLayerPoint offsetAnchorInSubtreeRootSpace =
+ oldCumulativeTransform.TransformPoint(offsetAnchor);
+ LayerPoint transformedAnchor =
+ newCumulativeTransform.Inverse().TransformPoint(
+ offsetAnchorInSubtreeRootSpace);
+
+ // We want to translate the layer by the difference between
+ // |transformedAnchor| and |anchor|.
+ LayerPoint translation = transformedAnchor - anchor;
+
+ // A fixed layer will "consume" (be unadjusted by) the entire translation
+ // calculated above. A sticky layer may consume all, part, or none of it,
+ // depending on where we are relative to its sticky scroll range.
+ // The remainder of the translation (the unconsumed portion) needs to
+ // be propagated to descendant fixed/sticky layers.
+ LayerPoint unconsumedTranslation;
+
+ if (layer->GetIsStickyPosition()) {
+ // For sticky positioned layers, the difference between the two rectangles
+ // defines a pair of translation intervals in each dimension through which
+ // the layer should not move relative to the scroll container. To
+ // accomplish this, we limit each dimension of the |translation| to that
+ // part of it which overlaps those intervals.
+ const LayerRectAbsolute& stickyOuter = layer->GetStickyScrollRangeOuter();
+ const LayerRectAbsolute& stickyInner = layer->GetStickyScrollRangeInner();
+
+ LayerPoint originalTranslation = translation;
+ translation.y =
+ IntervalOverlap(translation.y, stickyOuter.Y(), stickyOuter.YMost()) -
+ IntervalOverlap(translation.y, stickyInner.Y(), stickyInner.YMost());
+ translation.x =
+ IntervalOverlap(translation.x, stickyOuter.X(), stickyOuter.XMost()) -
+ IntervalOverlap(translation.x, stickyInner.X(), stickyInner.XMost());
+ unconsumedTranslation = translation - originalTranslation;
+ }
+
+ // Finally, apply the translation to the layer transform. Note that in cases
+ // where the async transform on |aTransformedSubtreeRoot| affects this layer's
+ // clip rect, we need to apply the same translation to said clip rect, so
+ // that the effective transform on the clip rect takes it back to where it was
+ // originally, had there been no async scroll.
+ TranslateShadowLayer(
+ layer,
+ ViewAs<ParentLayerPixel>(translation,
+ PixelCastJustification::NoTransformOnLayer),
+ true, &aClipPartsCache);
+
+ // Propragate the unconsumed portion of the translation to descendant
+ // fixed/sticky layers.
+ if (unconsumedTranslation != LayerPoint()) {
+ // Take the computations we performed to derive |translation| from
+ // |aCurrentTransformForRoot|, and perform them in reverse, keeping other
+ // quantities fixed, to come up with a new transform |newTransform| that
+ // would produce |unconsumedTranslation|.
+ LayerPoint newTransformedAnchor = unconsumedTranslation + anchor;
+ ParentLayerPoint newTransformedAnchorInSubtreeRootSpace =
+ oldCumulativeTransform.TransformPoint(newTransformedAnchor);
+ LayerToParentLayerMatrix4x4 newTransform = aPreviousTransformForRoot;
+ newTransform.PostTranslate(newTransformedAnchorInSubtreeRootSpace -
+ offsetAnchorInSubtreeRootSpace);
+
+ // Propagate this new transform to our descendants as the new value of
+ // |aCurrentTransformForRoot|. This allows them to consume the unconsumed
+ // translation.
+ for (Layer* child = layer->GetFirstChild(); child;
+ child = child->GetNextSibling()) {
+ AlignFixedAndStickyLayers(aTransformedSubtreeRoot, child, aStuckSides,
+ aTransformScrollId, aPreviousTransformForRoot,
+ newTransform, aFixedLayerMargins,
+ aClipPartsCache, aGeckoFixedLayerMargins);
+ }
+ }
+}
+
+bool AsyncCompositionManager::SampleAnimations(Layer* aLayer,
+ TimeStamp aCurrentFrameTime) {
+ CompositorAnimationStorage* storage =
+ mCompositorBridge->GetAnimationStorage();
+ MOZ_ASSERT(storage);
+
+ return storage->SampleAnimations(aLayer, mCompositorBridge,
+ mPreviousFrameTimeStamp, aCurrentFrameTime);
+}
+
+void AsyncCompositionManager::RecordShadowTransforms(Layer* aLayer) {
+ MOZ_ASSERT(StaticPrefs::gfx_vsync_collect_scroll_transforms());
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+
+ ForEachNodePostOrder<ForwardIterator>(aLayer, [this](Layer* layer) {
+ for (uint32_t i = 0; i < layer->GetScrollMetadataCount(); i++) {
+ if (!layer->GetFrameMetrics(i).IsScrollable()) {
+ continue;
+ }
+ gfx::Matrix4x4 shadowTransform =
+ layer->AsHostLayer()->GetShadowBaseTransform();
+ if (!shadowTransform.Is2D()) {
+ continue;
+ }
+
+ Matrix transform = shadowTransform.As2D();
+ if (transform.IsTranslation() && !shadowTransform.IsIdentity()) {
+ Point translation = transform.GetTranslation();
+ mLayerTransformRecorder.RecordTransform(layer, translation);
+ return;
+ }
+ }
+ });
+}
+
+static AsyncTransformComponentMatrix AdjustForClip(
+ const AsyncTransformComponentMatrix& asyncTransform, Layer* aLayer) {
+ AsyncTransformComponentMatrix result = asyncTransform;
+
+ // Container layers start at the origin, but they are clipped to where they
+ // actually have content on the screen. The tree transform is meant to apply
+ // to the clipped area. If the tree transform includes a scale component,
+ // then applying it to container as-is will produce incorrect results. To
+ // avoid this, translate the layer so that the clip rect starts at the origin,
+ // apply the tree transform, and translate back.
+ if (const Maybe<ParentLayerIntRect>& shadowClipRect =
+ aLayer->AsHostLayer()->GetShadowClipRect()) {
+ if (shadowClipRect->TopLeft() !=
+ ParentLayerIntPoint()) { // avoid a gratuitous change of basis
+ result.ChangeBasis(shadowClipRect->X(), shadowClipRect->Y(), 0);
+ }
+ }
+ return result;
+}
+
+static void ExpandRootClipRect(Layer* aLayer,
+ const ScreenMargin& aFixedLayerMargins) {
+ // For Fennec we want to expand the root scrollable layer clip rect based on
+ // the fixed position margins. In particular, we want this while the dynamic
+ // toolbar is in the process of sliding offscreen and the area of the
+ // LayerView visible to the user is larger than the viewport size that Gecko
+ // knows about (and therefore larger than the clip rect). We could also just
+ // clear the clip rect on aLayer entirely but this seems more precise.
+ Maybe<ParentLayerIntRect> rootClipRect =
+ aLayer->AsHostLayer()->GetShadowClipRect();
+ if (rootClipRect && aFixedLayerMargins != ScreenMargin()) {
+#ifndef MOZ_WIDGET_ANDROID
+ // We should never enter here on anything other than Fennec, since
+ // aFixedLayerMargins should be empty everywhere else.
+ MOZ_ASSERT(false);
+#endif
+ ParentLayerRect rect(rootClipRect.value());
+ rect.Deflate(ViewAs<ParentLayerPixel>(
+ aFixedLayerMargins,
+ PixelCastJustification::ScreenIsParentLayerForRoot));
+ aLayer->AsHostLayer()->SetShadowClipRect(Some(RoundedOut(rect)));
+ }
+}
+
+#ifdef MOZ_WIDGET_ANDROID
+static void MoveScrollbarForLayerMargin(
+ Layer* aRoot, ScrollableLayerGuid::ViewID aRootScrollId,
+ const ScreenMargin& aFixedLayerMargins) {
+ // See bug 1223928 comment 9 - once we can detect the RCD with just the
+ // isRootContent flag on the metrics, we can probably move this code into
+ // ApplyAsyncTransformToScrollbar rather than having it as a separate
+ // adjustment on the layer tree.
+ Layer* scrollbar =
+ BreadthFirstSearch<ReverseIterator>(aRoot, [aRootScrollId](Layer* aNode) {
+ return (aNode->GetScrollbarData().IsThumb() &&
+ aNode->GetScrollbarData().mDirection.isSome() &&
+ *aNode->GetScrollbarData().mDirection ==
+ ScrollDirection::eHorizontal &&
+ aNode->GetScrollbarData().mTargetViewId == aRootScrollId);
+ });
+ if (scrollbar) {
+ // Shift the horizontal scrollbar down into the new space exposed by the
+ // dynamic toolbar hiding. Technically we should also scale the vertical
+ // scrollbar a bit to expand into the new space but it's not as noticeable
+ // and it would add a lot more complexity, so we're going with the "it's not
+ // worth it" justification.
+ TranslateShadowLayer(scrollbar,
+ ParentLayerPoint(0, -aFixedLayerMargins.bottom), true,
+ nullptr);
+ if (scrollbar->GetParent()) {
+ // The layer that has the HORIZONTAL direction sits inside another
+ // ContainerLayer. This ContainerLayer also has a clip rect that causes
+ // the scrollbar to get clipped. We need to expand that clip rect to
+ // prevent that from happening. This is kind of ugly in that we're
+ // assuming a particular layer tree structure but short of adding more
+ // flags to the layer there doesn't appear to be a good way to do this.
+ ExpandRootClipRect(scrollbar->GetParent(), aFixedLayerMargins);
+ }
+ }
+}
+#endif
+
+bool AsyncCompositionManager::ApplyAsyncContentTransformToTree(
+ Layer* aLayer, bool* aOutFoundRoot) {
+ bool appliedTransform = false;
+ std::stack<Maybe<ParentLayerIntRect>> stackDeferredClips;
+ std::stack<LayersId> layersIds;
+ layersIds.push(mCompositorBridge->RootLayerTreeId());
+
+ // Maps layers to their ClipParts. The parts are not stored individually
+ // on the layer, but during AlignFixedAndStickyLayers we need access to
+ // the individual parts for descendant layers.
+ ClipPartsCache clipPartsCache;
+
+ Layer* zoomContainer = nullptr;
+ Maybe<LayerMetricsWrapper> zoomedMetrics;
+
+ ForEachNode<ForwardIterator>(
+ aLayer,
+ [&](Layer* layer) {
+ if (layer->AsRefLayer()) {
+ layersIds.push(layer->AsRefLayer()->GetReferentId());
+ }
+
+ stackDeferredClips.push(Maybe<ParentLayerIntRect>());
+
+ // If we encounter the async zoom container, find the corresponding
+ // APZC and stash it into |zoomedMetrics|.
+ // (We stash it in the form of a LayerMetricsWrapper because
+ // APZSampler requires going through that rather than using the APZC
+ // directly.)
+ // We do this on the way down the tree (i.e. here in the pre-action)
+ // so that by the time we encounter the layers with the RCD-RSF's
+ // scroll metadata (which will be descendants of the async zoom
+ // container), we can check for it and know we should only apply the
+ // scroll portion of the async transform to those layers (as the zoom
+ // portion will go on the async zoom container).
+ if (Maybe<ScrollableLayerGuid::ViewID> zoomedScrollId =
+ layer->IsAsyncZoomContainer()) {
+ zoomContainer = layer;
+ ForEachNode<ForwardIterator>(
+ LayerMetricsWrapper(layer),
+ [zoomedScrollId, &zoomedMetrics](LayerMetricsWrapper aWrapper) {
+ // Do not descend into layer subtrees with a different layers
+ // id.
+ if (aWrapper.AsRefLayer()) {
+ return TraversalFlag::Skip;
+ }
+
+ if (aWrapper.Metrics().GetScrollId() == *zoomedScrollId) {
+ zoomedMetrics = Some(aWrapper);
+ MOZ_ASSERT(zoomedMetrics->GetApzc());
+ return TraversalFlag::Abort;
+ }
+
+ return TraversalFlag::Continue;
+ });
+ }
+ },
+ [&](Layer* layer) {
+ Maybe<ParentLayerIntRect> clipDeferredFromChildren =
+ stackDeferredClips.top();
+ stackDeferredClips.pop();
+ MOZ_ASSERT(!layersIds.empty());
+ LayersId currentLayersId = layersIds.top();
+ LayerToParentLayerMatrix4x4 oldTransform =
+ layer->GetTransformTyped() * AsyncTransformMatrix();
+
+ AsyncTransformComponentMatrix combinedAsyncTransform;
+ bool hasAsyncTransform = false;
+ // Only set on the root layer for Android.
+ ScreenMargin fixedLayerMargins;
+
+ // Each layer has multiple clips:
+ // - Its local clip, which is fixed to the layer contents, i.e. it
+ // moves with those async transforms which the layer contents move
+ // with.
+ // - Its scrolled clip, which moves with all async transforms.
+ // - For each ScrollMetadata on the layer, a scroll clip. This
+ // includes the composition bounds and any other clips induced by
+ // layout. This moves with async transforms from ScrollMetadatas
+ // above it.
+ // In this function, these clips are combined into two shadow clip
+ // parts:
+ // - The fixed clip, which consists of the local clip only, initially
+ // transformed by all async transforms.
+ // - The scrolled clip, which consists of the other clips, transformed
+ // by the appropriate transforms.
+ // These two parts are kept separate for now, because for fixed layers,
+ // we need to adjust the fixed clip (to cancel out some async
+ // transforms). The parts are kept in a cache which is cleared at the
+ // beginning of every composite. The final shadow clip for the layer is
+ // the intersection of the (possibly adjusted) fixed clip and the
+ // scrolled clip.
+ ClipParts& clipParts = clipPartsCache[layer];
+ clipParts.mFixedClip = layer->GetClipRect();
+ clipParts.mScrolledClip = layer->GetScrolledClipRect();
+
+ // If we are a perspective transform ContainerLayer, apply the clip
+ // deferred from our child (if there is any) before we iterate over our
+ // frame metrics, because this clip is subject to all async transforms
+ // of this layer. Since this clip came from the a scroll clip on the
+ // child, it becomes part of our scrolled clip.
+ clipParts.mScrolledClip = IntersectMaybeRects(clipDeferredFromChildren,
+ clipParts.mScrolledClip);
+
+ // The transform of a mask layer is relative to the masked layer's
+ // parent layer. So whenever we apply an async transform to a layer, we
+ // need to apply that same transform to the layer's own mask layer. A
+ // layer can also have "ancestor" mask layers for any rounded clips from
+ // its ancestor scroll frames. A scroll frame mask layer only needs to
+ // be async transformed for async scrolls of this scroll frame's
+ // ancestor scroll frames, not for async scrolls of this scroll frame
+ // itself. In the loop below, we iterate over scroll frames from inside
+ // to outside. At each iteration, this array contains the layer's
+ // ancestor mask layers of all scroll frames inside the current one.
+ nsTArray<Layer*> ancestorMaskLayers;
+
+ // The layer's scrolled clip can have an ancestor mask layer as well,
+ // which is moved by all async scrolls on this layer.
+ if (const Maybe<LayerClip>& scrolledClip = layer->GetScrolledClip()) {
+ if (scrolledClip->GetMaskLayerIndex()) {
+ ancestorMaskLayers.AppendElement(layer->GetAncestorMaskLayerAt(
+ *scrolledClip->GetMaskLayerIndex()));
+ }
+ }
+
+ if (RefPtr<APZSampler> sampler = mCompositorBridge->GetAPZSampler()) {
+ for (uint32_t i = 0; i < layer->GetScrollMetadataCount(); i++) {
+ LayerMetricsWrapper wrapper(layer, i);
+ if (!wrapper.GetApzc()) {
+ continue;
+ }
+
+ const FrameMetrics& metrics = wrapper.Metrics();
+ MOZ_ASSERT(metrics.IsScrollable());
+
+ hasAsyncTransform = true;
+
+ AsyncTransformComponents asyncTransformComponents =
+ (zoomedMetrics &&
+ sampler->GetGuid(*zoomedMetrics) == sampler->GetGuid(wrapper))
+ ? AsyncTransformComponents{AsyncTransformComponent::eLayout}
+ : LayoutAndVisual;
+ AsyncTransform asyncTransformWithoutOverscroll =
+ sampler->GetCurrentAsyncTransform(wrapper,
+ asyncTransformComponents);
+ Maybe<CompositionPayload> payload =
+ sampler->NotifyScrollSampling(wrapper);
+ // The scroll latency should be measured between composition and the
+ // first scrolling event. Otherwise we observe metrics with <16ms
+ // latency even when frame.delay is enabled.
+ if (payload.isSome()) {
+ mLayerManager->RegisterPayload(*payload);
+ }
+
+ AsyncTransformComponentMatrix overscrollTransform =
+ sampler->GetOverscrollTransform(wrapper);
+ AsyncTransformComponentMatrix asyncTransform =
+ AsyncTransformComponentMatrix(asyncTransformWithoutOverscroll) *
+ overscrollTransform;
+
+ if (!layer->IsScrollableWithoutContent()) {
+ sampler->MarkAsyncTransformAppliedToContent(wrapper);
+ }
+
+ const ScrollMetadata& scrollMetadata = wrapper.Metadata();
+
+#if defined(MOZ_WIDGET_ANDROID)
+ // If we find a metrics which is the root content doc, use that. If
+ // not, use the root layer. Since this function recurses on children
+ // first we should only end up using the root layer if the entire
+ // tree was devoid of a root content metrics. This is a temporary
+ // solution; in the long term we should not need the root content
+ // metrics at all. See bug 1201529 comment 6 for details.
+ if (!(*aOutFoundRoot)) {
+ *aOutFoundRoot =
+ metrics.IsRootContent() || /* RCD */
+ (layer->GetParent() == nullptr && /* rootmost metrics */
+ i + 1 >= layer->GetScrollMetadataCount());
+ if (*aOutFoundRoot) {
+ mRootScrollableId = metrics.GetScrollId();
+ Compositor* compositor = mLayerManager->GetCompositor();
+ if (CompositorBridgeParent* bridge =
+ compositor->GetCompositorBridgeParent()) {
+ LayersId rootLayerTreeId = bridge->RootLayerTreeId();
+ GeckoViewMetrics gvMetrics =
+ sampler->GetGeckoViewMetrics(wrapper);
+ if (mIsFirstPaint || GeckoViewMetricsHaveUpdated(gvMetrics)) {
+ if (RefPtr<UiCompositorControllerParent> uiController =
+ UiCompositorControllerParent::
+ GetFromRootLayerTreeId(rootLayerTreeId)) {
+ uiController->NotifyUpdateScreenMetrics(gvMetrics);
+ }
+ mLastMetrics = gvMetrics;
+ }
+ if (mIsFirstPaint) {
+ if (RefPtr<UiCompositorControllerParent> uiController =
+ UiCompositorControllerParent::
+ GetFromRootLayerTreeId(rootLayerTreeId)) {
+ uiController->NotifyFirstPaint();
+ }
+ mIsFirstPaint = false;
+ }
+ if (mLayersUpdated) {
+ LayersId rootLayerTreeId = bridge->RootLayerTreeId();
+ if (RefPtr<UiCompositorControllerParent> uiController =
+ UiCompositorControllerParent::
+ GetFromRootLayerTreeId(rootLayerTreeId)) {
+ uiController->NotifyLayersUpdated();
+ }
+ mLayersUpdated = false;
+ }
+ }
+ fixedLayerMargins = GetFixedLayerMargins();
+ }
+ }
+#else
+ *aOutFoundRoot = false;
+ // Non-Android platforms still care about this flag being cleared
+ // after the first call to TransformShadowTree().
+ mIsFirstPaint = false;
+#endif
+
+ // Transform the current local clips by this APZC's async transform.
+ MOZ_ASSERT(asyncTransform.Is2D());
+ if (clipParts.mFixedClip) {
+ *clipParts.mFixedClip =
+ TransformBy(asyncTransform, *clipParts.mFixedClip);
+ }
+ if (clipParts.mScrolledClip) {
+ *clipParts.mScrolledClip =
+ TransformBy(asyncTransform, *clipParts.mScrolledClip);
+ }
+ // Note: we don't set the layer's shadow clip rect property yet;
+ // AlignFixedAndStickyLayers will use the clip parts from the clip
+ // parts cache.
+
+ combinedAsyncTransform *= asyncTransform;
+
+ // For the purpose of aligning fixed and sticky layers, we disregard
+ // the overscroll transform as well as any OMTA transform when
+ // computing the 'aCurrentTransformForRoot' parameter. This ensures
+ // that the overscroll and OMTA transforms are not unapplied, and
+ // therefore that the visual effects apply to fixed and sticky
+ // layers. We do this by using GetTransform() as the base transform
+ // rather than GetLocalTransform(), which would include those
+ // factors.
+ LayerToParentLayerMatrix4x4 transformWithoutOverscrollOrOmta =
+ layer->GetTransformTyped() *
+ CompleteAsyncTransform(AdjustForClip(asyncTransform, layer));
+ // See bug 1630274 for why we pass eNone here; fixing that bug will
+ // probably end up changing this to be more correct.
+ AlignFixedAndStickyLayers(layer, layer, SideBits::eNone,
+ metrics.GetScrollId(), oldTransform,
+ transformWithoutOverscrollOrOmta,
+ fixedLayerMargins, clipPartsCache,
+ sampler->GetGeckoFixedLayerMargins());
+
+ // Combine the local clip with the ancestor scrollframe clip. This
+ // is not included in the async transform above, since the ancestor
+ // clip should not move with this APZC.
+ if (scrollMetadata.HasScrollClip()) {
+ ParentLayerIntRect clip =
+ scrollMetadata.ScrollClip().GetClipRect();
+ if (layer->GetParent() &&
+ layer->GetParent()->GetTransformIsPerspective()) {
+ // If our parent layer has a perspective transform, we want to
+ // apply our scroll clip to it instead of to this layer (see bug
+ // 1168263). A layer with a perspective transform shouldn't have
+ // multiple children with FrameMetrics, nor a child with
+ // multiple FrameMetrics. (A child with multiple FrameMetrics
+ // would mean that there's *another* scrollable element between
+ // the one with the CSS perspective and the transformed element.
+ // But you'd have to use preserve-3d on the inner scrollable
+ // element in order to have the perspective apply to the
+ // transformed child, and preserve-3d is not supported on
+ // scrollable elements, so this case can't occur.)
+ MOZ_ASSERT(!stackDeferredClips.top());
+ stackDeferredClips.top().emplace(clip);
+ } else {
+ clipParts.mScrolledClip =
+ IntersectMaybeRects(Some(clip), clipParts.mScrolledClip);
+ }
+ }
+
+ // Do the same for the ancestor mask layers: ancestorMaskLayers
+ // contains the ancestor mask layers for scroll frames *inside* the
+ // current scroll frame, so these are the ones we need to shift by
+ // our async transform.
+ for (Layer* ancestorMaskLayer : ancestorMaskLayers) {
+ SetShadowTransform(
+ ancestorMaskLayer,
+ ancestorMaskLayer->GetLocalTransformTyped() * asyncTransform);
+ }
+
+ // Append the ancestor mask layer for this scroll frame to
+ // ancestorMaskLayers.
+ if (scrollMetadata.HasScrollClip()) {
+ const LayerClip& scrollClip = scrollMetadata.ScrollClip();
+ if (scrollClip.GetMaskLayerIndex()) {
+ size_t maskLayerIndex = scrollClip.GetMaskLayerIndex().value();
+ Layer* ancestorMaskLayer =
+ layer->GetAncestorMaskLayerAt(maskLayerIndex);
+ ancestorMaskLayers.AppendElement(ancestorMaskLayer);
+ }
+ }
+ }
+
+ if (Maybe<ScrollableLayerGuid::ViewID> zoomedScrollId =
+ layer->IsAsyncZoomContainer()) {
+ if (zoomedMetrics) {
+ AsyncTransform zoomTransform = sampler->GetCurrentAsyncTransform(
+ *zoomedMetrics, {AsyncTransformComponent::eVisual});
+ hasAsyncTransform = true;
+ combinedAsyncTransform *=
+ AsyncTransformComponentMatrix(zoomTransform);
+ } else {
+ // TODO: Is this normal? It happens on some pages, such as
+ // about:config on mobile, for just one frame or so, before the
+ // scroll metadata for zoomedScrollId appears in the layer tree.
+ }
+ }
+
+ auto IsFixedToZoomContainer = [&](Layer* aFixedLayer) {
+ if (!zoomedMetrics) {
+ return false;
+ }
+ ScrollableLayerGuid::ViewID targetId =
+ aFixedLayer->GetFixedPositionScrollContainerId();
+ MOZ_ASSERT(targetId != ScrollableLayerGuid::NULL_SCROLL_ID);
+ ScrollableLayerGuid rootContent = sampler->GetGuid(*zoomedMetrics);
+ return rootContent.mScrollId == targetId &&
+ rootContent.mLayersId == currentLayersId;
+ };
+
+ auto SidesStuckToZoomContainer = [&](Layer* aLayer) -> SideBits {
+ SideBits result = SideBits::eNone;
+ if (!zoomedMetrics) {
+ return result;
+ }
+ if (!aLayer->GetIsStickyPosition()) {
+ return result;
+ }
+
+ ScrollableLayerGuid::ViewID targetId =
+ aLayer->GetStickyScrollContainerId();
+ if (targetId == ScrollableLayerGuid::NULL_SCROLL_ID) {
+ return result;
+ }
+
+ ScrollableLayerGuid rootContent = sampler->GetGuid(*zoomedMetrics);
+ if (rootContent.mScrollId != targetId ||
+ rootContent.mLayersId != currentLayersId) {
+ return result;
+ }
+
+ ParentLayerPoint translation =
+ sampler
+ ->GetCurrentAsyncTransform(
+ *zoomedMetrics, {AsyncTransformComponent::eLayout})
+ .mTranslation;
+
+ if (apz::IsStuckAtBottom(translation.y,
+ aLayer->GetStickyScrollRangeInner(),
+ aLayer->GetStickyScrollRangeOuter())) {
+ result |= SideBits::eBottom;
+ }
+ if (apz::IsStuckAtTop(translation.y,
+ aLayer->GetStickyScrollRangeInner(),
+ aLayer->GetStickyScrollRangeOuter())) {
+ result |= SideBits::eTop;
+ }
+ return result;
+ };
+
+ // Layers fixed to the RCD-RSF no longer need
+ // AdjustFixedOrStickyLayer() to scroll them by the eVisual transform,
+ // as that's now applied to the async zoom container itself. However,
+ // we still need to adjust them by the fixed layer margins to
+ // account for dynamic toolbar transitions. This is also handled by
+ // AdjustFixedOrStickyLayer(), so we now call it with empty transforms
+ // to get it to perform just the fixed margins adjustment.
+ SideBits stuckSides = SidesStuckToZoomContainer(layer);
+ if (zoomedMetrics && ((layer->GetIsFixedPosition() &&
+ !layer->GetParent()->GetIsFixedPosition() &&
+ IsFixedToZoomContainer(layer)) ||
+ stuckSides != SideBits::eNone)) {
+ LayerToParentLayerMatrix4x4 emptyTransform;
+ ScreenMargin marginsForFixedLayer = GetFixedLayerMargins();
+ AdjustFixedOrStickyLayer(zoomContainer, layer, stuckSides,
+ sampler->GetGuid(*zoomedMetrics).mScrollId,
+ emptyTransform, emptyTransform,
+ marginsForFixedLayer, clipPartsCache,
+ sampler->GetGeckoFixedLayerMargins());
+ }
+ }
+
+ bool clipChanged = (hasAsyncTransform || clipDeferredFromChildren ||
+ layer->GetScrolledClipRect());
+ if (clipChanged) {
+ // Intersect the two clip parts and apply them to the layer.
+ // During ApplyAsyncContentTransformTree on an ancestor layer,
+ // AlignFixedAndStickyLayers may overwrite this with a new clip it
+ // computes from the clip parts, but if that doesn't happen, this
+ // is the layer's final clip rect.
+ layer->AsHostLayer()->SetShadowClipRect(clipParts.Intersect());
+ }
+
+ if (hasAsyncTransform) {
+ // Apply the APZ transform on top of GetLocalTransform() here (rather
+ // than GetTransform()) in case the OMTA code in SampleAnimations
+ // already set a shadow transform; in that case we want to apply ours
+ // on top of that one rather than clobber it.
+ SetShadowTransform(layer,
+ layer->GetLocalTransformTyped() *
+ AdjustForClip(combinedAsyncTransform, layer));
+
+ // Do the same for the layer's own mask layer, if it has one.
+ if (Layer* maskLayer = layer->GetMaskLayer()) {
+ SetShadowTransform(maskLayer, maskLayer->GetLocalTransformTyped() *
+ combinedAsyncTransform);
+ }
+
+ appliedTransform = true;
+ }
+
+ ExpandRootClipRect(layer, fixedLayerMargins);
+
+ if (layer->GetScrollbarData().mScrollbarLayerType ==
+ layers::ScrollbarLayerType::Thumb) {
+ ApplyAsyncTransformToScrollbar(layer);
+ }
+
+ if (layer->AsRefLayer()) {
+ MOZ_ASSERT(layersIds.size() > 1);
+ layersIds.pop();
+ }
+ });
+
+ return appliedTransform;
+}
+
+#if defined(MOZ_WIDGET_ANDROID)
+bool AsyncCompositionManager::GeckoViewMetricsHaveUpdated(
+ const GeckoViewMetrics& aMetrics) {
+ return RoundedToInt(mLastMetrics.mVisualScrollOffset) !=
+ RoundedToInt(aMetrics.mVisualScrollOffset) ||
+ mLastMetrics.mZoom != aMetrics.mZoom;
+}
+#endif
+
+static bool LayerIsScrollbarTarget(const LayerMetricsWrapper& aTarget,
+ Layer* aScrollbar) {
+ if (!aTarget.GetApzc()) {
+ return false;
+ }
+ const FrameMetrics& metrics = aTarget.Metrics();
+ MOZ_ASSERT(metrics.IsScrollable());
+ if (metrics.GetScrollId() != aScrollbar->GetScrollbarData().mTargetViewId) {
+ return false;
+ }
+ return !metrics.IsScrollInfoLayer();
+}
+
+static void ApplyAsyncTransformToScrollbarForContent(
+ const RefPtr<APZSampler>& aSampler, Layer* aScrollbar,
+ const LayerMetricsWrapper& aContent, bool aScrollbarIsDescendant) {
+ AsyncTransformComponentMatrix clipTransform;
+
+ MOZ_ASSERT(aSampler);
+ LayerToParentLayerMatrix4x4 transform =
+ aSampler->ComputeTransformForScrollThumb(
+ aScrollbar->GetLocalTransformTyped(), aContent,
+ aScrollbar->GetScrollbarData(), aScrollbarIsDescendant,
+ &clipTransform);
+
+ if (aScrollbarIsDescendant) {
+ // We also need to make a corresponding change on the clip rect of all the
+ // layers on the ancestor chain from the scrollbar layer up to but not
+ // including the layer with the async transform. Otherwise the scrollbar
+ // shifts but gets clipped and so appears to flicker.
+ for (Layer* ancestor = aScrollbar; ancestor != aContent.GetLayer();
+ ancestor = ancestor->GetParent()) {
+ TransformClipRect(ancestor, clipTransform);
+ }
+ }
+
+ SetShadowTransform(aScrollbar, transform);
+}
+
+static LayerMetricsWrapper FindScrolledLayerForScrollbar(Layer* aScrollbar,
+ bool* aOutIsAncestor) {
+ // First check if the scrolled layer is an ancestor of the scrollbar layer.
+ LayerMetricsWrapper root(aScrollbar->Manager()->GetRoot());
+ LayerMetricsWrapper prevAncestor(aScrollbar);
+ LayerMetricsWrapper scrolledLayer;
+
+ for (LayerMetricsWrapper ancestor(aScrollbar); ancestor;
+ ancestor = ancestor.GetParent()) {
+ // Don't walk into remote layer trees; the scrollbar will always be in
+ // the same layer space.
+ if (ancestor.AsRefLayer()) {
+ root = prevAncestor;
+ break;
+ }
+ prevAncestor = ancestor;
+
+ if (LayerIsScrollbarTarget(ancestor, aScrollbar)) {
+ *aOutIsAncestor = true;
+ return ancestor;
+ }
+ }
+
+ // Search the entire layer space of the scrollbar.
+ ForEachNode<ForwardIterator>(root, [&root, &scrolledLayer, &aScrollbar](
+ LayerMetricsWrapper aLayerMetrics) {
+ // Do not recurse into RefLayers, since our initial aSubtreeRoot is the
+ // root (or RefLayer root) of a single layer space to search.
+ if (root != aLayerMetrics && aLayerMetrics.AsRefLayer()) {
+ return TraversalFlag::Skip;
+ }
+ if (LayerIsScrollbarTarget(aLayerMetrics, aScrollbar)) {
+ scrolledLayer = aLayerMetrics;
+ return TraversalFlag::Abort;
+ }
+ return TraversalFlag::Continue;
+ });
+ return scrolledLayer;
+}
+
+void AsyncCompositionManager::ApplyAsyncTransformToScrollbar(Layer* aLayer) {
+ // If this layer corresponds to a scrollbar, then there should be a layer that
+ // is a previous sibling or a parent that has a matching ViewID on its
+ // FrameMetrics. That is the content that this scrollbar is for. We pick up
+ // the transient async transform from that layer and use it to update the
+ // scrollbar position. Note that it is possible that the content layer is no
+ // longer there; in this case we don't need to do anything because there can't
+ // be an async transform on the content.
+ bool isAncestor = false;
+ const LayerMetricsWrapper& scrollTarget =
+ FindScrolledLayerForScrollbar(aLayer, &isAncestor);
+ if (scrollTarget) {
+ ApplyAsyncTransformToScrollbarForContent(mCompositorBridge->GetAPZSampler(),
+ aLayer, scrollTarget, isAncestor);
+ }
+}
+
+void AsyncCompositionManager::GetFrameUniformity(
+ FrameUniformityData* aOutData) {
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+ mLayerTransformRecorder.EndTest(aOutData);
+}
+
+bool AsyncCompositionManager::TransformShadowTree(
+ const SampleTime& aCurrentFrame, TimeDuration aVsyncRate,
+ CompositorBridgeParentBase::TransformsToSkip aSkip) {
+ AUTO_PROFILER_LABEL("AsyncCompositionManager::TransformShadowTree", GRAPHICS);
+
+ Layer* root = mLayerManager->GetRoot();
+ if (!root) {
+ return false;
+ }
+
+ // First, compute and set the shadow transforms from OMT animations.
+ // NB: we must sample animations *before* sampling pan/zoom
+ // transforms.
+ bool wantNextFrame = SampleAnimations(root, aCurrentFrame.Time());
+
+ // Advance animations to the next expected vsync timestamp, if we can
+ // get it.
+ SampleTime nextFrame = aCurrentFrame;
+
+ MOZ_ASSERT(aVsyncRate != TimeDuration::Forever());
+ if (aVsyncRate != TimeDuration::Forever()) {
+ nextFrame = nextFrame + aVsyncRate;
+ }
+
+ // Reset the previous time stamp if we don't already have any running
+ // animations to avoid using the time which is far behind for newly
+ // started animations.
+ mPreviousFrameTimeStamp = wantNextFrame ? aCurrentFrame.Time() : TimeStamp();
+
+ if (!(aSkip & CompositorBridgeParentBase::TransformsToSkip::APZ)) {
+ bool apzAnimating = false;
+ if (RefPtr<APZSampler> apz = mCompositorBridge->GetAPZSampler()) {
+ apzAnimating = apz->AdvanceAnimations(nextFrame);
+ }
+ wantNextFrame |= apzAnimating;
+
+ // Apply an async content transform to any layer that has
+ // an async pan zoom controller.
+ bool foundRoot = false;
+ if (ApplyAsyncContentTransformToTree(root, &foundRoot)) {
+#if defined(MOZ_WIDGET_ANDROID)
+ MOZ_ASSERT(foundRoot);
+ if (foundRoot && GetFixedLayerMargins() != ScreenMargin()) {
+ MoveScrollbarForLayerMargin(root, mRootScrollableId,
+ GetFixedLayerMargins());
+ }
+#endif
+ }
+ }
+
+ HostLayer* rootComposite = root->AsHostLayer();
+
+ gfx::Matrix4x4 trans = rootComposite->GetShadowBaseTransform();
+ trans *= gfx::Matrix4x4::From2D(mWorldTransform);
+ rootComposite->SetShadowBaseTransform(trans);
+
+ if (StaticPrefs::gfx_vsync_collect_scroll_transforms()) {
+ RecordShadowTransforms(root);
+ }
+
+ return wantNextFrame;
+}
+
+void AsyncCompositionManager::SetFixedLayerMargins(ScreenIntCoord aTop,
+ ScreenIntCoord aBottom) {
+ mFixedLayerMargins.top = aTop;
+ mFixedLayerMargins.bottom = aBottom;
+}
+ScreenMargin AsyncCompositionManager::GetFixedLayerMargins() const {
+ ScreenMargin result = mFixedLayerMargins;
+ if (StaticPrefs::apz_fixed_margin_override_enabled()) {
+ result.top = StaticPrefs::apz_fixed_margin_override_top();
+ result.bottom = StaticPrefs::apz_fixed_margin_override_bottom();
+ }
+ return result;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/composite/AsyncCompositionManager.h b/gfx/layers/composite/AsyncCompositionManager.h
new file mode 100644
index 0000000000..474f653f99
--- /dev/null
+++ b/gfx/layers/composite/AsyncCompositionManager.h
@@ -0,0 +1,273 @@
+/* -*- 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/. */
+
+#ifndef GFX_ASYNCCOMPOSITIONMANAGER_H
+#define GFX_ASYNCCOMPOSITIONMANAGER_H
+
+#include "Units.h" // for ScreenPoint, etc
+#include "FrameMetrics.h" // for FrameMetrics
+#include "mozilla/layers/APZUtils.h" // for GeckoViewMetrics
+#include "mozilla/layers/CompositorBridgeParent.h" // for TransformsToSkip
+#include "mozilla/layers/LayerManagerComposite.h" // for LayerManagerComposite
+#include "mozilla/Attributes.h" // for final, etc
+#include "mozilla/RefPtr.h" // for RefCounted
+#include "mozilla/TimeStamp.h" // for TimeStamp
+#include "mozilla/gfx/BasePoint.h" // for BasePoint
+#include "mozilla/gfx/Matrix.h" // for Matrix4x4
+#include "mozilla/HalScreenConfiguration.h" // For ScreenOrientation
+#include "mozilla/layers/FrameUniformityData.h" // For FrameUniformityData
+#include "mozilla/layers/LayersMessages.h" // for TargetConfig
+#include "mozilla/RefPtr.h" // for nsRefPtr
+#include "nsISupportsImpl.h" // for LayerManager::AddRef, etc
+
+namespace mozilla {
+namespace layers {
+
+class Layer;
+class LayerManagerComposite;
+class AutoResolveRefLayers;
+class CompositorBridgeParent;
+class SampleTime;
+
+/**
+ * Manage async composition effects. This class is only used with OMTC and only
+ * lives on the compositor thread. It is a layer on top of the layer manager
+ * (LayerManagerComposite) which deals with elements of composition which are
+ * usually dealt with by dom or layout when main thread rendering, but which can
+ * short circuit that stuff to directly affect layers as they are composited,
+ * for example, off-main thread animation, async video, async pan/zoom.
+ */
+class AsyncCompositionManager final {
+ friend class AutoResolveRefLayers;
+ ~AsyncCompositionManager();
+
+ public:
+ NS_INLINE_DECL_REFCOUNTING(AsyncCompositionManager)
+
+ AsyncCompositionManager(CompositorBridgeParent* aParent,
+ HostLayerManager* aManager);
+
+ /**
+ * This forces the is-first-paint flag to true. This is intended to
+ * be called by the widget code when it loses its viewport information
+ * (or for whatever reason wants to refresh the viewport information).
+ * The information refresh happens because the compositor will call
+ * AndroidDynamicToolbarAnimator::FirstPaint() on the next frame of
+ * composition.
+ */
+ void ForceIsFirstPaint() { mIsFirstPaint = true; }
+
+ // Sample transforms for layer trees. Return true to request
+ // another animation frame.
+ bool TransformShadowTree(
+ const SampleTime& aCurrentFrame, TimeDuration aVsyncRate,
+ CompositorBridgeParentBase::TransformsToSkip aSkip =
+ CompositorBridgeParentBase::TransformsToSkip::NoneOfThem);
+
+ // Calculates the correct rotation and applies the transform to
+ // our layer manager
+ void ComputeRotation();
+
+ // Call after updating our layer tree.
+ void Updated(bool isFirstPaint, const TargetConfig& aTargetConfig) {
+ mIsFirstPaint |= isFirstPaint;
+ mLayersUpdated = true;
+ mTargetConfig = aTargetConfig;
+ }
+
+ bool RequiresReorientation(hal::ScreenOrientation aOrientation) const {
+ return mTargetConfig.orientation() != aOrientation;
+ }
+
+ // True if the underlying layer tree is ready to be composited.
+ bool ReadyForCompose() { return mReadyForCompose; }
+
+ // Returns true if the next composition will be the first for a
+ // particular document.
+ bool IsFirstPaint() { return mIsFirstPaint; }
+
+ // GetFrameUniformity will return the frame uniformity for each layer attached
+ // to an APZ from the recorded data in RecordShadowTransform
+ void GetFrameUniformity(FrameUniformityData* aFrameUniformityData);
+
+ // Stores the clip rect of a layer in two parts: a fixed part and a scrolled
+ // part. When a layer is fixed, the clip needs to be adjusted to account for
+ // async transforms. Only the fixed part needs to be adjusted, so we need
+ // to store the two parts separately.
+ struct ClipParts {
+ Maybe<ParentLayerIntRect> mFixedClip;
+ Maybe<ParentLayerIntRect> mScrolledClip;
+
+ Maybe<ParentLayerIntRect> Intersect() const {
+ return IntersectMaybeRects(mFixedClip, mScrolledClip);
+ }
+ };
+
+ typedef std::map<Layer*, ClipParts> ClipPartsCache;
+
+ private:
+ // Return true if an AsyncPanZoomController content transform was
+ // applied for |aLayer|. |*aOutFoundRoot| is set to true on Android only, if
+ // one of the metrics on one of the layers was determined to be the "root"
+ // and its state was synced to the Java front-end. |aOutFoundRoot| must be
+ // non-null.
+ bool ApplyAsyncContentTransformToTree(Layer* aLayer, bool* aOutFoundRoot);
+ /**
+ * Update the shadow transform for aLayer assuming that is a scrollbar,
+ * so that it stays in sync with the content that is being scrolled by APZ.
+ */
+ void ApplyAsyncTransformToScrollbar(Layer* aLayer);
+
+ /**
+ * Adds a translation to the transform of any fixed position (whose parent
+ * layer is not fixed) or sticky position layer descendant of
+ * |aTransformedSubtreeRoot|. The translation is chosen so that the layer's
+ * anchor point relative to |aTransformedSubtreeRoot|'s parent layer is the
+ * same as it was when |aTransformedSubtreeRoot|'s GetLocalTransform() was
+ * |aPreviousTransformForRoot|. |aCurrentTransformForRoot| is
+ * |aTransformedSubtreeRoot|'s current GetLocalTransform() modulo any
+ * overscroll-related transform, which we don't want to adjust for.
+ * For sticky position layers, the translation is further intersected with
+ * the layer's sticky scroll ranges.
+ * This function will also adjust layers so that the given content document
+ * fixed position margins will be respected during asynchronous panning and
+ * zooming.
+ * |aTransformScrollId| is the scroll id of the scroll frame that scrolls
+ * |aTransformedSubtreeRoot|.
+ * |aClipPartsCache| maps layers to separate fixed and scrolled
+ * clips, so we can only adjust the fixed portion.
+ * This function has a recursive implementation; aStartTraversalAt specifies
+ * where to start the current recursion of the traversal. For the initial
+ * call, it should be the same as aTrasnformedSubtreeRoot.
+ */
+ void AlignFixedAndStickyLayers(
+ Layer* aTransformedSubtreeRoot, Layer* aStartTraversalAt,
+ SideBits aStuckSides, ScrollableLayerGuid::ViewID aTransformScrollId,
+ const LayerToParentLayerMatrix4x4& aPreviousTransformForRoot,
+ const LayerToParentLayerMatrix4x4& aCurrentTransformForRoot,
+ const ScreenMargin& aCompositorFixedLayerMargins,
+ ClipPartsCache& aClipPartsCache,
+ const ScreenMargin& aGeckoFixedLayerMargins);
+
+ /**
+ * Helper function for AlignFixedAndStickyLayers() to perform a transform
+ * adjustment for a single fixed or sticky layer, rather than all such
+ * layers rooted at a subtree. May also be called directly.
+ */
+ void AdjustFixedOrStickyLayer(
+ Layer* aTransformedSubtreeRoot, Layer* aFixedOrSticky,
+ SideBits aStuckSides, ScrollableLayerGuid::ViewID aTransformScrollId,
+ const LayerToParentLayerMatrix4x4& aPreviousTransformForRoot,
+ const LayerToParentLayerMatrix4x4& aCurrentTransformForRoot,
+ const ScreenMargin& aCompositorFixedLayerMargins,
+ ClipPartsCache& aClipPartsCache,
+ const ScreenMargin& aGeckoFixedLayerMargins);
+
+ /**
+ * DRAWING PHASE ONLY
+ *
+ * For reach RefLayer in our layer tree, look up its referent and connect it
+ * to the layer tree, if found.
+ * aHasRemoteContent - indicates if the layer tree contains a remote reflayer.
+ * May be null.
+ * aResolvePlugins - incoming value indicates if plugin windows should be
+ * updated through a call on aCompositor's UpdatePluginWindowState. Applies
+ * to linux and windows only, may be null. On return value indicates
+ * if any updates occured.
+ */
+ void ResolveRefLayers(CompositorBridgeParent* aCompositor,
+ bool* aHasRemoteContent, bool* aResolvePlugins);
+
+ /**
+ * Detaches all referents resolved by ResolveRefLayers.
+ * Assumes that mLayerManager->GetRoot() and mTargetConfig have not changed
+ * since ResolveRefLayers was called.
+ */
+ void DetachRefLayers();
+
+ // Records the shadow transforms for the tree of layers rooted at the given
+ // layer
+ void RecordShadowTransforms(Layer* aLayer);
+
+ bool SampleAnimations(Layer* aLayer, TimeStamp aCurrentFrameTime);
+
+ TargetConfig mTargetConfig;
+ CSSRect mContentRect;
+
+ RefPtr<HostLayerManager> mLayerManager;
+ // When this flag is set, the next composition will be the first for a
+ // particular document (i.e. the document displayed on the screen will
+ // change). This happens when loading a new page or switching tabs. We notify
+ // the front-end (e.g. Java on Android) about this so that it take the new
+ // page size and zoom into account when providing us with the next view
+ // transform.
+ bool mIsFirstPaint;
+
+ // This flag is set during a layers update, so that the first composition
+ // after a layers update has it set. It is cleared after that first
+ // composition.
+ bool mLayersUpdated;
+
+ bool mReadyForCompose;
+
+ gfx::Matrix mWorldTransform;
+ LayerTransformRecorder mLayerTransformRecorder;
+
+ TimeStamp mPreviousFrameTimeStamp;
+
+ MOZ_NON_OWNING_REF CompositorBridgeParent* mCompositorBridge;
+
+ public:
+ void SetFixedLayerMargins(ScreenIntCoord aTop, ScreenIntCoord aBottom);
+ ScreenMargin GetFixedLayerMargins() const;
+
+ private:
+ ScreenMargin mFixedLayerMargins;
+
+#ifdef MOZ_WIDGET_ANDROID
+ private:
+ // This calculates whether GeckoView metrics should be sent to Java.
+ bool GeckoViewMetricsHaveUpdated(const GeckoViewMetrics& aMetrics);
+ // This holds the most recent GeckoView metrics sent to Java, and is used
+ // to send new updates when it changes.
+ GeckoViewMetrics mLastMetrics;
+ // The following two fields are only needed on Fennec with C++ APZ, because
+ // then we need to reposition the gecko scrollbar to deal with the
+ // dynamic toolbar shifting content around.
+ ScrollableLayerGuid::ViewID mRootScrollableId;
+#endif
+};
+
+class MOZ_STACK_CLASS AutoResolveRefLayers {
+ public:
+ explicit AutoResolveRefLayers(AsyncCompositionManager* aManager,
+ CompositorBridgeParent* aCompositor = nullptr,
+ bool* aHasRemoteContent = nullptr,
+ bool* aResolvePlugins = nullptr)
+ : mManager(aManager) {
+ if (mManager) {
+ mManager->ResolveRefLayers(aCompositor, aHasRemoteContent,
+ aResolvePlugins);
+ }
+ }
+
+ ~AutoResolveRefLayers() {
+ if (mManager) {
+ mManager->DetachRefLayers();
+ }
+ }
+
+ private:
+ AsyncCompositionManager* mManager;
+
+ AutoResolveRefLayers(const AutoResolveRefLayers&) = delete;
+ AutoResolveRefLayers& operator=(const AutoResolveRefLayers&) = delete;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/composite/CanvasLayerComposite.cpp b/gfx/layers/composite/CanvasLayerComposite.cpp
new file mode 100644
index 0000000000..9b1d4aa78b
--- /dev/null
+++ b/gfx/layers/composite/CanvasLayerComposite.cpp
@@ -0,0 +1,141 @@
+/* -*- 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 "CanvasLayerComposite.h"
+#include "composite/CompositableHost.h" // for CompositableHost
+#include "gfx2DGlue.h" // for ToFilter
+#include "gfxEnv.h" // for gfxEnv, etc
+#include "mozilla/gfx/Matrix.h" // for Matrix4x4
+#include "mozilla/gfx/Point.h" // for Point
+#include "mozilla/gfx/Rect.h" // for Rect
+#include "mozilla/layers/Compositor.h" // for Compositor
+#include "mozilla/layers/Effects.h" // for EffectChain
+#include "mozilla/layers/LayerManagerCompositeUtils.h"
+#include "mozilla/mozalloc.h" // for operator delete
+#include "nsAString.h"
+#include "mozilla/RefPtr.h" // for nsRefPtr
+#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
+#include "nsString.h" // for nsAutoCString
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::gfx;
+
+CanvasLayerComposite::CanvasLayerComposite(LayerManagerComposite* aManager)
+ : CanvasLayer(aManager, nullptr),
+ LayerComposite(aManager),
+ mCompositableHost(nullptr) {
+ MOZ_COUNT_CTOR(CanvasLayerComposite);
+ mImplData = static_cast<LayerComposite*>(this);
+}
+
+CanvasLayerComposite::~CanvasLayerComposite() {
+ MOZ_COUNT_DTOR(CanvasLayerComposite);
+
+ CleanupResources();
+}
+
+bool CanvasLayerComposite::SetCompositableHost(CompositableHost* aHost) {
+ switch (aHost->GetType()) {
+ case CompositableType::IMAGE: {
+ if (mCompositableHost && aHost != mCompositableHost) {
+ mCompositableHost->Detach(this);
+ }
+ mCompositableHost = aHost;
+ return true;
+ }
+ default:
+ return false;
+ }
+}
+
+Layer* CanvasLayerComposite::GetLayer() { return this; }
+
+void CanvasLayerComposite::SetLayerManager(HostLayerManager* aManager) {
+ LayerComposite::SetLayerManager(aManager);
+ mManager = aManager;
+ if (mCompositableHost && mCompositor) {
+ mCompositableHost->SetTextureSourceProvider(mCompositor);
+ }
+}
+
+void CanvasLayerComposite::RenderLayer(const IntRect& aClipRect,
+ const Maybe<gfx::Polygon>& aGeometry) {
+ if (!mCompositableHost || !mCompositableHost->IsAttached()) {
+ return;
+ }
+
+ mCompositor->MakeCurrent();
+
+#ifdef MOZ_DUMP_PAINTING
+ if (gfxEnv::DumpCompositorTextures()) {
+ RefPtr<gfx::DataSourceSurface> surf = mCompositableHost->GetAsSurface();
+ if (surf) {
+ WriteSnapshotToDumpFile(this, surf);
+ }
+ }
+#endif
+
+ RenderWithAllMasks(this, mCompositor, aClipRect,
+ [&](EffectChain& effectChain, const IntRect& clipRect) {
+ mCompositableHost->Composite(
+ mCompositor, this, effectChain,
+ GetEffectiveOpacity(), GetEffectiveTransform(),
+ GetSamplingFilter(), clipRect);
+ });
+
+ mCompositableHost->BumpFlashCounter();
+}
+
+CompositableHost* CanvasLayerComposite::GetCompositableHost() {
+ if (mCompositableHost && mCompositableHost->IsAttached()) {
+ return mCompositableHost.get();
+ }
+
+ return nullptr;
+}
+
+void CanvasLayerComposite::CleanupResources() {
+ if (mCompositableHost) {
+ mCompositableHost->Detach(this);
+ }
+ mCompositableHost = nullptr;
+}
+
+gfx::SamplingFilter CanvasLayerComposite::GetSamplingFilter() {
+ gfx::SamplingFilter filter = mSamplingFilter;
+#ifdef ANDROID
+ // Bug 691354
+ // Using the LINEAR filter we get unexplained artifacts.
+ // Use NEAREST when no scaling is required.
+ Matrix matrix;
+ bool is2D = GetEffectiveTransform().Is2D(&matrix);
+ if (is2D && !ThebesMatrix(matrix).HasNonTranslationOrFlip()) {
+ filter = SamplingFilter::POINT;
+ }
+#endif
+ return filter;
+}
+
+void CanvasLayerComposite::GenEffectChain(EffectChain& aEffect) {
+ aEffect.mLayerRef = this;
+ aEffect.mPrimaryEffect = mCompositableHost->GenEffect(GetSamplingFilter());
+}
+
+void CanvasLayerComposite::PrintInfo(std::stringstream& aStream,
+ const char* aPrefix) {
+ CanvasLayer::PrintInfo(aStream, aPrefix);
+ aStream << "\n";
+ if (mCompositableHost && mCompositableHost->IsAttached()) {
+ nsAutoCString pfx(aPrefix);
+ pfx += " ";
+ mCompositableHost->PrintInfo(aStream, pfx.get());
+ }
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/composite/CanvasLayerComposite.h b/gfx/layers/composite/CanvasLayerComposite.h
new file mode 100644
index 0000000000..dbe29fbb6d
--- /dev/null
+++ b/gfx/layers/composite/CanvasLayerComposite.h
@@ -0,0 +1,72 @@
+/* -*- 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/. */
+
+#ifndef GFX_CanvasLayerComposite_H
+#define GFX_CanvasLayerComposite_H
+
+#include "Layers.h" // for CanvasLayer, etc
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/RefPtr.h" // for RefPtr
+#include "mozilla/layers/LayerManagerComposite.h" // for LayerComposite, etc
+#include "mozilla/layers/LayersTypes.h" // for LayerRenderState, etc
+#include "nsRect.h" // for mozilla::gfx::IntRect
+#include "nscore.h" // for nsACString
+
+namespace mozilla {
+namespace layers {
+
+class CompositableHost;
+// Canvas layers use ImageHosts (but CanvasClients) because compositing a
+// canvas is identical to compositing an image.
+class ImageHost;
+
+class CanvasLayerComposite : public CanvasLayer, public LayerComposite {
+ public:
+ explicit CanvasLayerComposite(LayerManagerComposite* aManager);
+
+ protected:
+ virtual ~CanvasLayerComposite();
+
+ public:
+ bool SetCompositableHost(CompositableHost* aHost) override;
+
+ void Disconnect() override { Destroy(); }
+
+ void SetLayerManager(HostLayerManager* aManager) override;
+
+ Layer* GetLayer() override;
+ void RenderLayer(const gfx::IntRect& aClipRect,
+ const Maybe<gfx::Polygon>& aGeometry) override;
+
+ void CleanupResources() override;
+
+ void GenEffectChain(EffectChain& aEffect) override;
+
+ CompositableHost* GetCompositableHost() override;
+
+ HostLayer* AsHostLayer() override { return this; }
+
+ const char* Name() const override { return "CanvasLayerComposite"; }
+
+ protected:
+ RefPtr<CanvasRenderer> CreateCanvasRendererInternal() override {
+ MOZ_CRASH("Incompatible surface type");
+ return nullptr;
+ }
+
+ void PrintInfo(std::stringstream& aStream, const char* aPrefix) override;
+
+ private:
+ gfx::SamplingFilter GetSamplingFilter();
+
+ private:
+ RefPtr<CompositableHost> mCompositableHost;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* GFX_CanvasLayerComposite_H */
diff --git a/gfx/layers/composite/ColorLayerComposite.cpp b/gfx/layers/composite/ColorLayerComposite.cpp
new file mode 100644
index 0000000000..4b17a216e8
--- /dev/null
+++ b/gfx/layers/composite/ColorLayerComposite.cpp
@@ -0,0 +1,63 @@
+/* -*- 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 "ColorLayerComposite.h"
+#include "mozilla/RefPtr.h" // for RefPtr
+#include "mozilla/gfx/Matrix.h" // for Matrix4x4
+#include "mozilla/gfx/Point.h" // for Point
+#include "mozilla/gfx/Rect.h" // for Rect
+#include "mozilla/gfx/Types.h" // for Color
+#include "mozilla/layers/Compositor.h" // for Compositor
+#include "mozilla/layers/CompositorTypes.h" // for DiagnosticFlags::COLOR
+#include "mozilla/layers/Effects.h" // for Effect, EffectChain, etc
+#include "mozilla/layers/LayerManagerCompositeUtils.h"
+#include "mozilla/mozalloc.h" // for operator delete, etc
+#include "UnitTransforms.h" // for ViewAs
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::gfx;
+
+void ColorLayerComposite::RenderLayer(const gfx::IntRect& aClipRect,
+ const Maybe<gfx::Polygon>& aGeometry) {
+ const Matrix4x4& transform = GetEffectiveTransform();
+
+#ifdef MOZ_GFX_OPTIMIZE_MOBILE
+ // On desktop we want to draw a single rectangle to avoid possible
+ // seams if we're resampling. On mobile we'd prefer to use the accurate
+ // region for better performance.
+ LayerIntRegion drawRegion = GetLocalVisibleRegion();
+#else
+ LayerIntRegion drawRegion = ViewAs<LayerPixel>(GetBounds());
+#endif
+
+ for (auto iter = drawRegion.RectIter(); !iter.Done(); iter.Next()) {
+ const LayerIntRect& rect = iter.Get();
+ Rect graphicsRect(rect.X(), rect.Y(), rect.Width(), rect.Height());
+
+ RenderWithAllMasks(this, mCompositor, aClipRect,
+ [&](EffectChain& effectChain, const IntRect& clipRect) {
+ GenEffectChain(effectChain);
+
+ mCompositor->DrawGeometry(
+ graphicsRect, clipRect, effectChain,
+ GetEffectiveOpacity(), transform, aGeometry);
+ });
+ }
+
+ Rect rect(GetBounds());
+ mCompositor->DrawDiagnostics(DiagnosticFlags::COLOR, rect, aClipRect,
+ transform);
+}
+
+void ColorLayerComposite::GenEffectChain(EffectChain& aEffect) {
+ aEffect.mLayerRef = this;
+ aEffect.mPrimaryEffect = new EffectSolidColor(GetColor());
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/composite/ColorLayerComposite.h b/gfx/layers/composite/ColorLayerComposite.h
new file mode 100644
index 0000000000..b499112486
--- /dev/null
+++ b/gfx/layers/composite/ColorLayerComposite.h
@@ -0,0 +1,62 @@
+/* -*- 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/. */
+
+#ifndef GFX_ColorLayerComposite_H
+#define GFX_ColorLayerComposite_H
+
+#include "Layers.h" // for ColorLayer, etc
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/layers/LayerManagerComposite.h" // for LayerComposite, etc
+#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
+
+namespace mozilla {
+namespace layers {
+
+class CompositableHost;
+
+class ColorLayerComposite : public ColorLayer, public LayerComposite {
+ public:
+ explicit ColorLayerComposite(LayerManagerComposite* aManager)
+ : ColorLayer(aManager, nullptr), LayerComposite(aManager) {
+ MOZ_COUNT_CTOR(ColorLayerComposite);
+ mImplData = static_cast<LayerComposite*>(this);
+ }
+
+ protected:
+ virtual ~ColorLayerComposite() {
+ MOZ_COUNT_DTOR(ColorLayerComposite);
+ Destroy();
+ }
+
+ public:
+ // LayerComposite Implementation
+ Layer* GetLayer() override { return this; }
+
+ void SetLayerManager(HostLayerManager* aManager) override {
+ LayerComposite::SetLayerManager(aManager);
+ mManager = aManager;
+ }
+
+ void Destroy() override { mDestroyed = true; }
+
+ void RenderLayer(const gfx::IntRect& aClipRect,
+ const Maybe<gfx::Polygon>& aGeometry) override;
+
+ void CleanupResources() override{};
+
+ void GenEffectChain(EffectChain& aEffect) override;
+
+ CompositableHost* GetCompositableHost() override { return nullptr; }
+
+ HostLayer* AsHostLayer() override { return this; }
+
+ const char* Name() const override { return "ColorLayerComposite"; }
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* GFX_ColorLayerComposite_H */
diff --git a/gfx/layers/composite/CompositableHost.cpp b/gfx/layers/composite/CompositableHost.cpp
new file mode 100644
index 0000000000..056f26de04
--- /dev/null
+++ b/gfx/layers/composite/CompositableHost.cpp
@@ -0,0 +1,167 @@
+/* -*- 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 "CompositableHost.h"
+#include <map> // for _Rb_tree_iterator, map, etc
+#include <utility> // for pair
+#include "ContentHost.h" // for ContentHostDoubleBuffered, etc
+#include "Effects.h" // for EffectMask, Effect, etc
+#include "gfxUtils.h"
+#include "ImageHost.h" // for ImageHostBuffered, etc
+#include "Layers.h"
+#include "TiledContentHost.h" // for TiledContentHost
+#include "mozilla/gfx/gfxVars.h"
+#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor
+#include "mozilla/layers/TextureHost.h" // for TextureHost, etc
+#include "mozilla/layers/WebRenderImageHost.h"
+#include "mozilla/RefPtr.h" // for nsRefPtr
+#include "nsDebug.h" // for NS_WARNING
+#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
+#include "gfxPlatform.h" // for gfxPlatform
+#include "IPDLActor.h"
+
+namespace mozilla {
+
+using namespace gfx;
+
+namespace layers {
+
+class Compositor;
+
+CompositableHost::CompositableHost(const TextureInfo& aTextureInfo)
+ : mTextureInfo(aTextureInfo),
+ mCompositorBridgeID(0),
+ mLayer(nullptr),
+ mFlashCounter(0),
+ mAttached(false),
+ mKeepAttached(false) {
+ MOZ_COUNT_CTOR(CompositableHost);
+}
+
+CompositableHost::~CompositableHost() { MOZ_COUNT_DTOR(CompositableHost); }
+
+void CompositableHost::UseTextureHost(const nsTArray<TimedTexture>& aTextures) {
+ if (mTextureSourceProvider) {
+ for (auto& texture : aTextures) {
+ texture.mTexture->SetTextureSourceProvider(mTextureSourceProvider);
+ }
+ }
+}
+
+void CompositableHost::UseComponentAlphaTextures(TextureHost* aTextureOnBlack,
+ TextureHost* aTextureOnWhite) {
+ MOZ_ASSERT(aTextureOnBlack && aTextureOnWhite);
+ if (mTextureSourceProvider) {
+ aTextureOnBlack->SetTextureSourceProvider(mTextureSourceProvider);
+ aTextureOnWhite->SetTextureSourceProvider(mTextureSourceProvider);
+ }
+}
+
+void CompositableHost::RemoveTextureHost(TextureHost* aTexture) {}
+
+void CompositableHost::SetTextureSourceProvider(
+ TextureSourceProvider* aProvider) {
+ MOZ_ASSERT(aProvider);
+ mTextureSourceProvider = aProvider;
+}
+
+bool CompositableHost::AddMaskEffect(EffectChain& aEffects,
+ const gfx::Matrix4x4& aTransform) {
+ CompositableTextureSourceRef source;
+ RefPtr<TextureHost> host = GetAsTextureHost();
+
+ if (!host) {
+ NS_WARNING("Using compositable with no valid TextureHost as mask");
+ return false;
+ }
+
+ if (!host->Lock()) {
+ NS_WARNING("Failed to lock the mask texture");
+ return false;
+ }
+
+ if (!host->BindTextureSource(source)) {
+ NS_WARNING(
+ "The TextureHost was successfully locked but can't provide a "
+ "TextureSource");
+ host->Unlock();
+ return false;
+ }
+ MOZ_ASSERT(source);
+
+ RefPtr<EffectMask> effect =
+ new EffectMask(source, source->GetSize(), aTransform);
+ aEffects.mSecondaryEffects[EffectTypes::MASK] = effect;
+ return true;
+}
+
+void CompositableHost::RemoveMaskEffect() {
+ RefPtr<TextureHost> host = GetAsTextureHost();
+ if (host) {
+ host->Unlock();
+ }
+}
+
+/* static */
+already_AddRefed<CompositableHost> CompositableHost::Create(
+ const TextureInfo& aTextureInfo, bool aUseWebRender) {
+ RefPtr<CompositableHost> result;
+ switch (aTextureInfo.mCompositableType) {
+ case CompositableType::IMAGE_BRIDGE:
+ NS_ERROR("Cannot create an image bridge compositable this way");
+ break;
+ case CompositableType::CONTENT_TILED:
+ result = new TiledContentHost(aTextureInfo);
+ break;
+ case CompositableType::IMAGE:
+ if (aUseWebRender) {
+ result = new WebRenderImageHost(aTextureInfo);
+ } else {
+ result = new ImageHost(aTextureInfo);
+ }
+ break;
+ case CompositableType::CONTENT_SINGLE:
+ if (aUseWebRender) {
+ result = new WebRenderImageHost(aTextureInfo);
+ } else {
+ result = new ContentHostSingleBuffered(aTextureInfo);
+ }
+ break;
+ case CompositableType::CONTENT_DOUBLE:
+ MOZ_ASSERT(!aUseWebRender);
+ result = new ContentHostDoubleBuffered(aTextureInfo);
+ break;
+ default:
+ NS_ERROR("Unknown CompositableType");
+ }
+ return result.forget();
+}
+
+void CompositableHost::DumpTextureHost(std::stringstream& aStream,
+ TextureHost* aTexture) {
+ if (!aTexture) {
+ return;
+ }
+ RefPtr<gfx::DataSourceSurface> dSurf = aTexture->GetAsSurface();
+ if (!dSurf) {
+ return;
+ }
+ aStream << gfxUtils::GetAsDataURI(dSurf).get();
+}
+
+HostLayerManager* CompositableHost::GetLayerManager() const {
+ if (!mLayer || !mLayer->Manager()) {
+ return nullptr;
+ }
+ return mLayer->Manager()->AsHostLayerManager();
+}
+
+TextureSourceProvider* CompositableHost::GetTextureSourceProvider() const {
+ return mTextureSourceProvider;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/composite/CompositableHost.h b/gfx/layers/composite/CompositableHost.h
new file mode 100644
index 0000000000..6f749d76b8
--- /dev/null
+++ b/gfx/layers/composite/CompositableHost.h
@@ -0,0 +1,278 @@
+/* -*- 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/. */
+
+#ifndef MOZILLA_GFX_BUFFERHOST_H
+#define MOZILLA_GFX_BUFFERHOST_H
+
+#include <stdint.h> // for uint64_t
+#include <stdio.h> // for FILE
+#include "gfxRect.h" // for gfxRect
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/RefPtr.h" // for RefPtr, RefCounted, etc
+#include "mozilla/gfx/MatrixFwd.h" // for Matrix4x4
+#include "mozilla/gfx/Point.h" // for Point
+#include "mozilla/gfx/Polygon.h" // for Polygon
+#include "mozilla/gfx/Rect.h" // for Rect
+#include "mozilla/gfx/Types.h" // for SamplingFilter
+#include "mozilla/ipc/ProtocolUtils.h"
+#include "mozilla/layers/Compositor.h" // for Compositor
+#include "mozilla/layers/CompositorTypes.h" // for TextureInfo, etc
+#include "mozilla/layers/Effects.h" // for Texture Effect
+#include "mozilla/layers/LayersTypes.h" // for LayerRenderState, etc
+#include "mozilla/layers/LayersMessages.h"
+#include "mozilla/layers/TextureHost.h" // for TextureHost
+#include "mozilla/mozalloc.h" // for operator delete
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsRegion.h" // for nsIntRegion
+#include "nscore.h" // for nsACString
+#include "Units.h" // for CSSToScreenScale
+
+namespace mozilla {
+namespace gfx {
+class DataSourceSurface;
+} // namespace gfx
+
+namespace layers {
+
+class Layer;
+class LayerComposite;
+class ImageHost;
+class Compositor;
+class ThebesBufferData;
+class TiledContentHost;
+class CompositableParentManager;
+class WebRenderImageHost;
+class ContentHost;
+class ContentHostTexture;
+class HostLayerManager;
+struct EffectChain;
+
+struct ImageCompositeNotificationInfo {
+ base::ProcessId mImageBridgeProcessId;
+ ImageCompositeNotification mNotification;
+};
+
+struct AsyncCompositableRef {
+ AsyncCompositableRef() : mProcessId(mozilla::ipc::kInvalidProcessId) {}
+ AsyncCompositableRef(base::ProcessId aProcessId,
+ const CompositableHandle& aHandle)
+ : mProcessId(aProcessId), mHandle(aHandle) {}
+ explicit operator bool() const { return !!mHandle; }
+ base::ProcessId mProcessId;
+ CompositableHandle mHandle;
+};
+
+/**
+ * The compositor-side counterpart to CompositableClient. Responsible for
+ * updating textures and data about textures from IPC and how textures are
+ * composited (tiling, double buffering, etc.).
+ *
+ * Update (for images/canvases) and UpdateThebes (for Thebes) are called during
+ * the layers transaction to update the Compositbale's textures from the
+ * content side. The actual update (and any syncronous upload) is done by the
+ * TextureHost, but it is coordinated by the CompositableHost.
+ *
+ * Composite is called by the owning layer when it is composited.
+ * CompositableHost will use its TextureHost(s) and call Compositor::DrawQuad to
+ * do the actual rendering.
+ */
+class CompositableHost {
+ protected:
+ virtual ~CompositableHost();
+
+ public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CompositableHost)
+ explicit CompositableHost(const TextureInfo& aTextureInfo);
+
+ static already_AddRefed<CompositableHost> Create(
+ const TextureInfo& aTextureInfo, bool aUseWebRender);
+
+ virtual CompositableType GetType() = 0;
+
+ // If base class overrides, it should still call the parent implementation
+ virtual void SetTextureSourceProvider(TextureSourceProvider* aProvider);
+
+ // composite the contents of this buffer host to the compositor's surface
+ virtual void Composite(Compositor* aCompositor, LayerComposite* aLayer,
+ EffectChain& aEffectChain, float aOpacity,
+ const gfx::Matrix4x4& aTransform,
+ const gfx::SamplingFilter aSamplingFilter,
+ const gfx::IntRect& aClipRect,
+ const nsIntRegion* aVisibleRegion = nullptr,
+ const Maybe<gfx::Polygon>& aGeometry = Nothing()) = 0;
+
+ /**
+ * Update the content host.
+ * aUpdated is the region which should be updated.
+ */
+ virtual bool UpdateThebes(const ThebesBufferData& aData,
+ const nsIntRegion& aUpdated,
+ const nsIntRegion& aOldValidRegionBack) {
+ NS_ERROR("should be implemented or not used");
+ return false;
+ }
+
+ /**
+ * Returns the front buffer.
+ * *aPictureRect (if non-null, and the returned TextureHost is non-null)
+ * is set to the picture rect.
+ */
+ virtual TextureHost* GetAsTextureHost(gfx::IntRect* aPictureRect = nullptr) {
+ return nullptr;
+ }
+
+ virtual gfx::IntSize GetImageSize() {
+ MOZ_ASSERT(false, "Should have been overridden");
+ return gfx::IntSize();
+ }
+
+ const TextureInfo& GetTextureInfo() const { return mTextureInfo; }
+
+ /**
+ * Adds a mask effect using this texture as the mask, if possible.
+ * @return true if the effect was added, false otherwise.
+ */
+ bool AddMaskEffect(EffectChain& aEffects, const gfx::Matrix4x4& aTransform);
+
+ void RemoveMaskEffect();
+
+ TextureSourceProvider* GetTextureSourceProvider() const;
+
+ Layer* GetLayer() const { return mLayer; }
+ void SetLayer(Layer* aLayer) { mLayer = aLayer; }
+
+ virtual ContentHost* AsContentHost() { return nullptr; }
+ virtual ContentHostTexture* AsContentHostTexture() { return nullptr; }
+ virtual ImageHost* AsImageHost() { return nullptr; }
+ virtual TiledContentHost* AsTiledContentHost() { return nullptr; }
+ virtual WebRenderImageHost* AsWebRenderImageHost() { return nullptr; }
+
+ typedef uint32_t AttachFlags;
+ static const AttachFlags NO_FLAGS = 0;
+ static const AttachFlags ALLOW_REATTACH = 1;
+ static const AttachFlags KEEP_ATTACHED = 2;
+ static const AttachFlags FORCE_DETACH = 2;
+
+ virtual void Attach(Layer* aLayer, TextureSourceProvider* aProvider,
+ AttachFlags aFlags = NO_FLAGS) {
+ MOZ_ASSERT(aProvider);
+ NS_ASSERTION(aFlags & ALLOW_REATTACH || !mAttached,
+ "Re-attaching compositables must be explicitly authorised");
+ SetTextureSourceProvider(aProvider);
+ SetLayer(aLayer);
+ mAttached = true;
+ mKeepAttached = aFlags & KEEP_ATTACHED;
+ }
+ // Detach this compositable host from its layer.
+ // If we are used for async video, then it is not safe to blindly detach since
+ // we might be re-attached to a different layer. aLayer is the layer which the
+ // caller expects us to be attached to, we will only detach if we are in fact
+ // attached to that layer. If we are part of a normal layer, then we will be
+ // detached in any case. if aLayer is null, then we will only detach if we are
+ // not async.
+ // Only force detach if the IPDL tree is being shutdown.
+ virtual void Detach(Layer* aLayer = nullptr, AttachFlags aFlags = NO_FLAGS) {
+ if (!mKeepAttached || aLayer == mLayer || aFlags & FORCE_DETACH) {
+ SetLayer(nullptr);
+ mAttached = false;
+ mKeepAttached = false;
+ }
+ }
+ bool IsAttached() { return mAttached; }
+
+ virtual void Dump(std::stringstream& aStream, const char* aPrefix = "",
+ bool aDumpHtml = false) {}
+ static void DumpTextureHost(std::stringstream& aStream,
+ TextureHost* aTexture);
+
+ virtual already_AddRefed<gfx::DataSourceSurface> GetAsSurface() {
+ return nullptr;
+ }
+
+ virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix) = 0;
+
+ struct TimedTexture {
+ CompositableTextureHostRef mTexture;
+ TimeStamp mTimeStamp;
+ gfx::IntRect mPictureRect;
+ int32_t mFrameID;
+ int32_t mProducerID;
+ };
+ virtual void UseTextureHost(const nsTArray<TimedTexture>& aTextures);
+ virtual void UseComponentAlphaTextures(TextureHost* aTextureOnBlack,
+ TextureHost* aTextureOnWhite);
+ virtual void RemoveTextureHost(TextureHost* aTexture);
+
+ // Called every time this is composited
+ void BumpFlashCounter() {
+ mFlashCounter = mFlashCounter >= DIAGNOSTIC_FLASH_COUNTER_MAX
+ ? DIAGNOSTIC_FLASH_COUNTER_MAX
+ : mFlashCounter + 1;
+ }
+
+ uint64_t GetCompositorBridgeID() const { return mCompositorBridgeID; }
+
+ const AsyncCompositableRef& GetAsyncRef() const { return mAsyncRef; }
+ void SetAsyncRef(const AsyncCompositableRef& aRef) { mAsyncRef = aRef; }
+
+ void SetCompositorBridgeID(uint64_t aID) { mCompositorBridgeID = aID; }
+
+ virtual bool Lock() { return false; }
+
+ virtual void Unlock() {}
+
+ virtual already_AddRefed<TexturedEffect> GenEffect(
+ const gfx::SamplingFilter aSamplingFilter) {
+ return nullptr;
+ }
+
+ /// Called when shutting down the layer tree.
+ /// This is a good place to clear all potential gpu resources before the
+ /// widget is is destroyed.
+ virtual void CleanupResources() {}
+
+ virtual void BindTextureSource() {}
+
+ virtual uint32_t GetDroppedFrames() { return 0; }
+
+ protected:
+ HostLayerManager* GetLayerManager() const;
+
+ protected:
+ TextureInfo mTextureInfo;
+ AsyncCompositableRef mAsyncRef;
+ uint64_t mCompositorBridgeID;
+ RefPtr<TextureSourceProvider> mTextureSourceProvider;
+ Layer* mLayer;
+ uint32_t mFlashCounter; // used when the pref "layers.flash-borders" is true.
+ bool mAttached;
+ bool mKeepAttached;
+};
+
+class AutoLockCompositableHost final {
+ public:
+ explicit AutoLockCompositableHost(CompositableHost* aHost) : mHost(aHost) {
+ mSucceeded = (mHost && mHost->Lock());
+ }
+
+ ~AutoLockCompositableHost() {
+ if (mSucceeded && mHost) {
+ mHost->Unlock();
+ }
+ }
+
+ bool Failed() const { return !mSucceeded; }
+
+ private:
+ RefPtr<CompositableHost> mHost;
+ bool mSucceeded;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/composite/ConsolasFontData.h b/gfx/layers/composite/ConsolasFontData.h
new file mode 100644
index 0000000000..a01c417f14
--- /dev/null
+++ b/gfx/layers/composite/ConsolasFontData.h
@@ -0,0 +1,457 @@
+/* -*- 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/. */
+
+// This is explicitly not guarded as we want only 1 file to include this and
+// it's good if things break if someone else does.
+
+namespace mozilla {
+namespace layers {
+namespace consolas_font {
+const unsigned char sFontPNG[] = {
+ 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd,
+ 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0,
+ 0x8, 0x0, 0x0, 0x0, 0x0, 0x79, 0x19, 0xf7, 0xba, 0x0, 0x0, 0x0,
+ 0x4, 0x67, 0x41, 0x4d, 0x41, 0x0, 0x0, 0xb1, 0x8f, 0xb, 0xfc, 0x61,
+ 0x5, 0x0, 0x0, 0x0, 0x20, 0x63, 0x48, 0x52, 0x4d, 0x0, 0x0, 0x7a,
+ 0x26, 0x0, 0x0, 0x80, 0x84, 0x0, 0x0, 0xfa, 0x0, 0x0, 0x0, 0x80,
+ 0xe8, 0x0, 0x0, 0x75, 0x30, 0x0, 0x0, 0xea, 0x60, 0x0, 0x0, 0x3a,
+ 0x98, 0x0, 0x0, 0x17, 0x70, 0x9c, 0xba, 0x51, 0x3c, 0x0, 0x0, 0x0,
+ 0x2, 0x62, 0x4b, 0x47, 0x44, 0x0, 0xff, 0x87, 0x8f, 0xcc, 0xbf, 0x0,
+ 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0x19, 0xd6, 0x0,
+ 0x0, 0x19, 0xd6, 0x1, 0x18, 0xd1, 0xca, 0xed, 0x0, 0x0, 0x0, 0x7,
+ 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe1, 0x4, 0x2, 0x12, 0x27, 0x25, 0xc0,
+ 0x26, 0x1d, 0xf0, 0x0, 0x0, 0x12, 0x53, 0x49, 0x44, 0x41, 0x54, 0x78,
+ 0xda, 0xed, 0x9d, 0x3d, 0x9a, 0xe4, 0xaa, 0xe, 0x86, 0xb5, 0x41, 0xf6,
+ 0xc1, 0x22, 0x58, 0x2, 0x2b, 0xf0, 0x6, 0x9c, 0x3b, 0x27, 0x76, 0x4a,
+ 0xea, 0x90, 0xd0, 0x19, 0x3b, 0xf0, 0x1, 0x5c, 0x75, 0xee, 0x3d, 0xa5,
+ 0x4f, 0x65, 0xe8, 0xb2, 0x87, 0x9e, 0x6e, 0xeb, 0x99, 0xe9, 0xc0, 0xe5,
+ 0xc2, 0xf2, 0x5b, 0xfc, 0x4a, 0x42, 0xd0, 0xf6, 0xcb, 0x85, 0x7a, 0x2b,
+ 0xd0, 0x5b, 0x6e, 0x0, 0x7f, 0xe2, 0x21, 0xd1, 0xc5, 0xab, 0x9f, 0x50,
+ 0x7d, 0xb1, 0x5, 0x80, 0xb7, 0x13, 0xba, 0x6c, 0xed, 0x36, 0x59, 0xdf,
+ 0xa2, 0x5e, 0xa0, 0x95, 0x5f, 0x1c, 0xd5, 0x50, 0x9e, 0xc1, 0xef, 0x9e,
+ 0x86, 0xb9, 0x45, 0x9f, 0x52, 0x16, 0x78, 0x80, 0x41, 0x17, 0x9b, 0x0,
+ 0x38, 0xb2, 0xf0, 0x2b, 0xb4, 0xd, 0x4, 0x54, 0xf4, 0x76, 0xdd, 0x6,
+ 0xa0, 0xa2, 0x1f, 0x34, 0x71, 0x60, 0x4e, 0x39, 0x35, 0x6e, 0x51, 0xb9,
+ 0xd7, 0xf, 0x16, 0x4a, 0x62, 0xd0, 0x83, 0x67, 0xac, 0x4f, 0x56, 0x14,
+ 0xdd, 0xef, 0x49, 0x5f, 0x6, 0xc0, 0x51, 0x40, 0xb7, 0xa7, 0xcf, 0x6,
+ 0x76, 0x79, 0xcc, 0x2f, 0xc4, 0x4b, 0x4a, 0xf5, 0x28, 0xff, 0xe3, 0x3a,
+ 0xea, 0x54, 0x5d, 0xc, 0x2a, 0x3f, 0x55, 0x24, 0xc, 0x20, 0x8, 0x6f,
+ 0x3a, 0x1, 0x6d, 0x9a, 0x0, 0x78, 0x1, 0x80, 0x81, 0x0, 0xc2, 0x40,
+ 0x21, 0xd0, 0xf8, 0xfa, 0x41, 0x24, 0x5a, 0xd3, 0x9b, 0xb2, 0xf6, 0x38,
+ 0xea, 0xa8, 0x87, 0x19, 0x94, 0x93, 0xf8, 0x26, 0x0, 0xa8, 0xf6, 0x6,
+ 0xc2, 0x4d, 0xc0, 0x42, 0x5c, 0x85, 0xa5, 0xf0, 0x41, 0x25, 0x0, 0x81,
+ 0xb8, 0xf7, 0x5b, 0xe0, 0xbd, 0x5a, 0xa0, 0x87, 0xf0, 0xeb, 0xab, 0x6,
+ 0x8a, 0xaf, 0x8a, 0xd4, 0xa2, 0xc0, 0x7, 0x3, 0x4d, 0x13, 0x7e, 0x70,
+ 0x74, 0xf0, 0x7d, 0x56, 0xdc, 0x60, 0xb6, 0xdc, 0x66, 0xc6, 0x2b, 0x0,
+ 0x48, 0x77, 0x1b, 0x13, 0x66, 0x9a, 0x99, 0x8e, 0x3a, 0x41, 0x81, 0x7a,
+ 0x4, 0xd4, 0x0, 0xd2, 0x6b, 0x2a, 0xaa, 0xd0, 0xfb, 0xff, 0xc4, 0x91,
+ 0x13, 0x3e, 0x89, 0xa4, 0x3e, 0x4, 0xd0, 0xa4, 0x48, 0xc2, 0xe5, 0x41,
+ 0x9d, 0x8b, 0x36, 0x11, 0x50, 0x68, 0xd4, 0x98, 0x61, 0xd, 0x4d, 0x7d,
+ 0x86, 0x5a, 0x57, 0xb1, 0xc3, 0xe7, 0x62, 0xe5, 0x8a, 0x4e, 0xc7, 0xa3,
+ 0xfc, 0x7b, 0x0, 0xee, 0xf0, 0xfb, 0xff, 0xca, 0x1a, 0x68, 0x8, 0x23,
+ 0xcd, 0x60, 0xf0, 0x4d, 0xdd, 0x31, 0x21, 0x32, 0x6a, 0x72, 0x4a, 0x2f,
+ 0xec, 0x66, 0x9b, 0x3a, 0xf5, 0x85, 0x4e, 0x1, 0x60, 0x3f, 0x3, 0xb0,
+ 0xb6, 0x8c, 0xf6, 0xf6, 0xd1, 0x5, 0x0, 0x66, 0x69, 0x78, 0x30, 0xe0,
+ 0xfa, 0xa0, 0x13, 0x61, 0xab, 0x58, 0x39, 0x4b, 0x19, 0x38, 0xea, 0x1f,
+ 0xfd, 0x6, 0x80, 0xfa, 0xc, 0xc0, 0xe6, 0x1b, 0x26, 0x70, 0xeb, 0x4c,
+ 0x53, 0x48, 0xc3, 0x17, 0xfb, 0x4a, 0xdc, 0x46, 0x93, 0x0, 0xb0, 0x89,
+ 0xc3, 0x42, 0x8b, 0xa3, 0x32, 0x76, 0xbe, 0xbc, 0x8e, 0xcf, 0xa3, 0xc0,
+ 0x71, 0xe3, 0xfd, 0x57, 0xe4, 0x3e, 0x60, 0xfb, 0xb0, 0xf, 0x98, 0xab,
+ 0x66, 0x12, 0x4f, 0x9, 0xe9, 0x87, 0xb0, 0x60, 0x5e, 0x47, 0x5a, 0x29,
+ 0x5, 0x3a, 0xea, 0x34, 0x11, 0xf4, 0xb4, 0x8c, 0xaf, 0x2a, 0xce, 0x94,
+ 0x5a, 0x86, 0x6e, 0xa9, 0x1, 0x8b, 0x38, 0xa, 0xf8, 0xf, 0x47, 0x81,
+ 0x59, 0x2c, 0x19, 0x49, 0x9e, 0x1b, 0x28, 0xe, 0x60, 0xcd, 0x6d, 0x3,
+ 0x8c, 0x83, 0x43, 0x7e, 0x73, 0x3, 0x5e, 0x74, 0xca, 0xf7, 0x27, 0xc,
+ 0x35, 0xf3, 0xd8, 0x5d, 0xf4, 0x55, 0xf3, 0x80, 0xad, 0x5e, 0x87, 0xf7,
+ 0xcf, 0x98, 0xc5, 0x8f, 0xe0, 0x13, 0x42, 0xe3, 0x73, 0x17, 0xa1, 0xa6,
+ 0xce, 0x35, 0xc3, 0xf8, 0x1f, 0x58, 0xd, 0x86, 0x8a, 0xdf, 0xe1, 0x33,
+ 0x19, 0xe1, 0x9c, 0x37, 0x54, 0x75, 0x24, 0x3f, 0xc3, 0x1e, 0x0, 0xd7,
+ 0xe, 0xf6, 0xe3, 0xd5, 0xe0, 0x5f, 0x24, 0x78, 0x8e, 0x5c, 0xf3, 0xcd,
+ 0x1f, 0x2, 0xe0, 0xeb, 0x72, 0x3, 0xe8, 0xad, 0x40, 0x6f, 0xb9, 0x1,
+ 0xf4, 0x56, 0xa0, 0xb7, 0xdc, 0x0, 0x7a, 0x2b, 0xd0, 0x5b, 0x6e, 0x0,
+ 0x5b, 0xe3, 0xba, 0xff, 0x5c, 0x99, 0xec, 0xd5, 0x2e, 0x93, 0x63, 0x0,
+ 0x31, 0x6c, 0x69, 0x41, 0xdd, 0x7, 0x41, 0x44, 0xcb, 0x98, 0x25, 0x2d,
+ 0x86, 0x7, 0xc4, 0xc5, 0x61, 0xfb, 0xc6, 0xa8, 0x48, 0x81, 0x65, 0xaf,
+ 0x4f, 0xc5, 0xa8, 0x63, 0xbb, 0x52, 0x76, 0x72, 0x4c, 0x34, 0xa9, 0x6,
+ 0x3, 0xc4, 0x89, 0x32, 0x81, 0x65, 0x7f, 0x5a, 0x9, 0x5b, 0xd, 0xc0,
+ 0xe4, 0x17, 0x42, 0x0, 0x2c, 0x69, 0x8b, 0xcc, 0xa8, 0x4e, 0x5b, 0xab,
+ 0x8e, 0xd, 0x2, 0xb4, 0x97, 0x3c, 0x22, 0xe2, 0x82, 0x19, 0xfa, 0x44,
+ 0x41, 0xdc, 0x55, 0xf6, 0x9, 0x58, 0xbe, 0x84, 0x36, 0xca, 0x41, 0x0,
+ 0xeb, 0x9c, 0xa1, 0xe1, 0xbe, 0x6c, 0x39, 0x5e, 0x10, 0xd2, 0x16, 0x8a,
+ 0xc5, 0x2, 0x35, 0x81, 0x26, 0xa3, 0xe8, 0x57, 0xc4, 0x3, 0xd3, 0xe7,
+ 0x52, 0x16, 0xf1, 0x86, 0x2f, 0xe5, 0x43, 0x94, 0x8d, 0xbc, 0x41, 0x4,
+ 0x70, 0x68, 0x11, 0xa0, 0xd4, 0xb2, 0x2, 0x79, 0x68, 0xa1, 0xbf, 0x1c,
+ 0x80, 0x26, 0x5e, 0xf1, 0xb2, 0x85, 0x2f, 0x28, 0x82, 0x36, 0xe, 0x11,
+ 0x80, 0x47, 0xb6, 0xab, 0xe0, 0x9c, 0x52, 0x87, 0x75, 0xb8, 0x14, 0x99,
+ 0x9e, 0xc9, 0x35, 0x9, 0x61, 0xa6, 0x31, 0x34, 0x58, 0x67, 0x5c, 0x96,
+ 0x96, 0x4e, 0x3d, 0x20, 0x3b, 0x46, 0x2, 0x30, 0x91, 0x50, 0xdb, 0x25,
+ 0x0, 0x51, 0xd1, 0x82, 0x4a, 0x22, 0x32, 0x55, 0x0, 0x60, 0x5b, 0x7f,
+ 0xba, 0xba, 0xea, 0x9d, 0x43, 0xe5, 0xf6, 0x96, 0x5e, 0x63, 0xc0, 0x2e,
+ 0xd6, 0xfc, 0xcc, 0xd0, 0x54, 0x3, 0xc, 0xec, 0xeb, 0x62, 0x8, 0xa9,
+ 0xb0, 0xa3, 0x5f, 0x44, 0x9e, 0x8, 0x5d, 0x5e, 0x3, 0xe0, 0x18, 0x98,
+ 0xfb, 0xb3, 0x59, 0x6a, 0xbc, 0x2, 0x0, 0x23, 0xf7, 0xf5, 0xf6, 0xb0,
+ 0x15, 0x7f, 0xc5, 0x33, 0x34, 0xb5, 0xb9, 0xcc, 0xd2, 0xfd, 0x6, 0x5f,
+ 0x46, 0x3d, 0xef, 0xee, 0xcf, 0xb3, 0xd0, 0x90, 0x8a, 0x1, 0x98, 0x37,
+ 0x5e, 0x24, 0x7d, 0x68, 0x5e, 0xff, 0xa, 0x80, 0x34, 0x6c, 0x34, 0xd9,
+ 0x6d, 0x85, 0x96, 0x21, 0xcc, 0x3d, 0x6, 0xd2, 0x6e, 0x0, 0xc8, 0x26,
+ 0x9b, 0x9e, 0x6b, 0xf9, 0xa4, 0xd5, 0x8, 0x1e, 0xa9, 0xc9, 0xb9, 0x49,
+ 0xd7, 0xc, 0x83, 0xb2, 0x48, 0xf3, 0x80, 0xb5, 0xad, 0xa9, 0xa7, 0x9f,
+ 0x1a, 0xd5, 0x75, 0x2f, 0xfd, 0x70, 0xd9, 0x31, 0x6, 0x66, 0xc8, 0x56,
+ 0x78, 0x51, 0xc9, 0x25, 0x57, 0xee, 0x1f, 0xf, 0x9b, 0xe4, 0x97, 0x16,
+ 0x43, 0x2d, 0x8e, 0xab, 0x42, 0xc, 0x5d, 0xd4, 0x72, 0xff, 0x74, 0xce,
+ 0xfc, 0x2b, 0x86, 0x9a, 0xe, 0xa9, 0x1d, 0x40, 0x74, 0xb6, 0xc1, 0x6f,
+ 0x25, 0x17, 0xd3, 0xe0, 0x0, 0xbf, 0x52, 0xda, 0x1, 0x4, 0x3d, 0x5c,
+ 0x3d, 0x43, 0xfe, 0xde, 0x0, 0x7e, 0x98, 0xdc, 0x0, 0x7a, 0x2b, 0xd0,
+ 0x5b, 0x6e, 0x0, 0xbd, 0x15, 0xe8, 0x2d, 0x37, 0x80, 0xde, 0xa, 0xf4,
+ 0x96, 0x1b, 0x40, 0x6f, 0x5, 0x7a, 0xcb, 0xd, 0x20, 0xff, 0x49, 0x2b,
+ 0x4d, 0xbe, 0x5e, 0xf1, 0x36, 0xb, 0x9a, 0xb0, 0x87, 0x11, 0x5c, 0x5f,
+ 0xed, 0x2e, 0x7c, 0x99, 0xe0, 0x54, 0x5a, 0xdd, 0x81, 0xf5, 0xd0, 0x84,
+ 0xed, 0xf9, 0x1d, 0x0, 0x4, 0xb8, 0x60, 0x77, 0x92, 0x45, 0x6c, 0x84,
+ 0xd7, 0x83, 0xb4, 0x2c, 0x4d, 0x8b, 0x61, 0x37, 0x82, 0xf5, 0x63, 0xb6,
+ 0xe7, 0xab, 0xa6, 0x70, 0xec, 0xcb, 0x0, 0x68, 0x1a, 0x20, 0x0, 0x6c,
+ 0xe, 0xf1, 0x6f, 0x2c, 0x8d, 0x11, 0xd8, 0xe0, 0x8a, 0x3b, 0x87, 0x87,
+ 0xb3, 0xae, 0x99, 0x49, 0xc4, 0xa6, 0x15, 0xc9, 0x1f, 0x21, 0xfa, 0x29,
+ 0xe6, 0x86, 0xa8, 0x42, 0xe, 0x20, 0xbd, 0xa9, 0x6b, 0x0, 0x60, 0x90,
+ 0x5, 0xf6, 0x21, 0x23, 0x30, 0x96, 0x95, 0x28, 0x59, 0xc5, 0xc0, 0xec,
+ 0xdb, 0x51, 0xe0, 0xca, 0x7a, 0x10, 0x4c, 0x99, 0xd2, 0xf5, 0xf4, 0xd8,
+ 0x63, 0xf3, 0xb7, 0xc, 0x20, 0x2a, 0xbd, 0x61, 0x0, 0xda, 0x8e, 0x40,
+ 0x3d, 0xa2, 0xd9, 0x5a, 0x6c, 0x69, 0x41, 0x15, 0x20, 0xef, 0x1, 0xb2,
+ 0xc0, 0x34, 0xb7, 0x47, 0x37, 0xa2, 0x20, 0xea, 0xfc, 0xa2, 0x50, 0x57,
+ 0xe9, 0xba, 0x25, 0x3, 0x2a, 0xc0, 0xa3, 0x49, 0x1e, 0x91, 0xa1, 0x52,
+ 0x3b, 0x9d, 0xd8, 0x7, 0xb0, 0x5f, 0xf4, 0xd1, 0xd6, 0x15, 0x22, 0x30,
+ 0xc2, 0x26, 0x9d, 0xb, 0xd2, 0xfc, 0xf6, 0xd4, 0x5, 0xa6, 0x3e, 0x0,
+ 0x56, 0xb3, 0x59, 0xd0, 0x1a, 0x5e, 0x5f, 0xd, 0xee, 0x48, 0x1e, 0x9d,
+ 0xf2, 0x51, 0xdb, 0xa0, 0x25, 0xbf, 0xa3, 0x13, 0x40, 0x79, 0xde, 0x46,
+ 0x3, 0xed, 0xbe, 0x3b, 0x34, 0x3c, 0x28, 0x54, 0x8c, 0x27, 0x3d, 0x23,
+ 0xe3, 0x64, 0x1c, 0xb5, 0x1a, 0x8e, 0xad, 0xb6, 0x87, 0xa2, 0x9b, 0x42,
+ 0xba, 0x39, 0x80, 0x67, 0x9c, 0xbf, 0x64, 0x72, 0x7, 0x9b, 0xa0, 0x36,
+ 0x61, 0x43, 0x19, 0xde, 0x65, 0x56, 0x9a, 0xbf, 0x11, 0xe2, 0x85, 0x75,
+ 0x9b, 0x7d, 0x19, 0x49, 0x34, 0xd8, 0xea, 0x5e, 0x5b, 0x3, 0xa6, 0x7c,
+ 0x97, 0x22, 0x83, 0xc7, 0x23, 0xc5, 0x15, 0x2c, 0x6e, 0xa8, 0x11, 0xd5,
+ 0x0, 0x58, 0x1, 0x76, 0xbf, 0xe5, 0x84, 0x7b, 0x54, 0xbc, 0x3b, 0x2a,
+ 0x58, 0xdc, 0xfd, 0x3a, 0x21, 0x7a, 0x7d, 0x80, 0xa1, 0xe5, 0xd5, 0x7d,
+ 0x40, 0xa1, 0x5, 0xee, 0x9b, 0x26, 0x37, 0x2a, 0xe0, 0xbd, 0x73, 0xa4,
+ 0xa6, 0x91, 0xa0, 0x5b, 0x13, 0x42, 0x4c, 0x65, 0xb8, 0x89, 0x8f, 0x2,
+ 0x79, 0xf6, 0xa5, 0x70, 0x50, 0xbc, 0xe4, 0x77, 0x18, 0xa4, 0xd7, 0x99,
+ 0xf0, 0x9e, 0xa4, 0x2a, 0x91, 0x1, 0x94, 0xbd, 0x4e, 0xe8, 0xa7, 0x28,
+ 0x1, 0xfd, 0x40, 0x13, 0x85, 0xd5, 0x5b, 0xb3, 0xe7, 0x42, 0xf3, 0xa1,
+ 0x33, 0x95, 0x6f, 0x26, 0x38, 0x98, 0x8, 0x9e, 0xa4, 0xbc, 0xd3, 0x4e,
+ 0xf8, 0x3d, 0xfd, 0x47, 0xf3, 0x0, 0x51, 0x44, 0xbb, 0x7a, 0x95, 0xc1,
+ 0xfd, 0x83, 0xfb, 0xa5, 0xb7, 0x31, 0x87, 0xae, 0xce, 0x73, 0x1, 0x7c,
+ 0x2f, 0x89, 0x6e, 0x68, 0xd8, 0x4a, 0xf6, 0x3, 0x1, 0x4, 0x35, 0x2c,
+ 0x9f, 0x97, 0xf2, 0x17, 0x3, 0xb8, 0x48, 0x6e, 0x0, 0xbd, 0x15, 0xe8,
+ 0x2d, 0x37, 0x80, 0xde, 0xa, 0xf4, 0x96, 0x1b, 0x40, 0x6f, 0x5, 0x7a,
+ 0xcb, 0xd, 0xa0, 0xb7, 0x2, 0xbd, 0xe5, 0xd, 0x80, 0xc9, 0x4e, 0x78,
+ 0xea, 0xe5, 0xd, 0xfd, 0x9c, 0x20, 0x91, 0x37, 0x0, 0xac, 0x10, 0x27,
+ 0x3a, 0x92, 0x75, 0xd0, 0x90, 0x31, 0x40, 0xf3, 0x43, 0x31, 0x4b, 0x40,
+ 0x1b, 0xe2, 0xa8, 0x89, 0x5b, 0xf3, 0x8a, 0x7f, 0x61, 0x0, 0xab, 0xdb,
+ 0xdd, 0x4f, 0x61, 0xab, 0xaf, 0x6f, 0x6b, 0x5a, 0xcd, 0x93, 0x39, 0x9a,
+ 0x3e, 0xbf, 0x1, 0xb0, 0x6, 0x8f, 0xe2, 0xed, 0x8b, 0xd, 0x6d, 0x53,
+ 0xdc, 0x50, 0x10, 0x71, 0xfe, 0x88, 0x1c, 0xdc, 0xa7, 0x50, 0x5c, 0x59,
+ 0x5a, 0x26, 0x5b, 0x6e, 0x71, 0xa, 0xbb, 0xa9, 0x10, 0xd9, 0x21, 0xb0,
+ 0xe1, 0x4a, 0xba, 0xbe, 0xaa, 0x52, 0x7e, 0x45, 0xa4, 0xa8, 0x91, 0x9d,
+ 0x13, 0xb, 0x28, 0x58, 0x17, 0x13, 0xdc, 0xc8, 0x3f, 0x98, 0x9, 0x1a,
+ 0xa7, 0x4a, 0x11, 0x60, 0x25, 0x3f, 0x92, 0x41, 0xd5, 0xa2, 0xa4, 0xae,
+ 0x9, 0x8a, 0xdb, 0xa, 0x25, 0x33, 0xbd, 0x74, 0x5d, 0xef, 0x3f, 0xde,
+ 0xa1, 0x49, 0x2c, 0xff, 0x12, 0x46, 0x5a, 0x66, 0x3, 0x0, 0x2b, 0xcd,
+ 0x8b, 0xb5, 0x66, 0xe2, 0x6f, 0x64, 0x9, 0x86, 0xed, 0xd3, 0xee, 0x7b,
+ 0x59, 0xf9, 0x75, 0x58, 0x3b, 0xf7, 0xdc, 0x3d, 0x20, 0x9, 0x54, 0x23,
+ 0x80, 0x8a, 0xbd, 0x12, 0x4f, 0x0, 0xd9, 0xaf, 0x0, 0x39, 0x39, 0xa7,
+ 0x79, 0x13, 0x70, 0x94, 0x93, 0x62, 0xd0, 0xc2, 0x1, 0xa4, 0x27, 0x22,
+ 0x3f, 0x7, 0x51, 0x58, 0x6, 0x68, 0x5e, 0x87, 0xa, 0xed, 0x0, 0x40,
+ 0x4e, 0x4, 0x97, 0xba, 0x1e, 0xe7, 0x80, 0xeb, 0x31, 0x87, 0x74, 0xf3,
+ 0x98, 0x6e, 0x97, 0x11, 0xa6, 0xf, 0xaa, 0xa2, 0xc5, 0x67, 0x9c, 0xb2,
+ 0x23, 0xb5, 0x2c, 0x6e, 0x81, 0xb0, 0x6a, 0xa6, 0xc5, 0x2a, 0x9e, 0x15,
+ 0x21, 0xf7, 0xd, 0x33, 0x68, 0xbb, 0x82, 0xcb, 0xf0, 0x3d, 0x0, 0xf0,
+ 0xa9, 0xe4, 0xab, 0x14, 0xae, 0x97, 0x8d, 0x17, 0xb8, 0x57, 0xe2, 0x0,
+ 0xb2, 0xbf, 0x13, 0xd4, 0x1, 0x8d, 0x54, 0x34, 0x76, 0xa2, 0x4d, 0x5b,
+ 0x9e, 0x32, 0x69, 0x4c, 0x10, 0x57, 0x50, 0xef, 0x52, 0xd, 0xc8, 0xbd,
+ 0xe9, 0xab, 0x59, 0xfc, 0xa8, 0x6, 0xb0, 0xde, 0x44, 0xf8, 0xa5, 0xa5,
+ 0x9a, 0xe1, 0xd2, 0x8f, 0x97, 0x83, 0x5a, 0xab, 0x0, 0x38, 0x6c, 0x55,
+ 0x9d, 0x60, 0xcc, 0xbe, 0xb5, 0xb4, 0xe5, 0x64, 0x47, 0xaf, 0x75, 0x2b,
+ 0x9b, 0xd6, 0x2d, 0x68, 0xd8, 0x82, 0x1f, 0x41, 0xf0, 0x8b, 0x9e, 0xd6,
+ 0x7, 0xec, 0x19, 0x60, 0x5c, 0x15, 0x0, 0xa9, 0xf, 0x58, 0x60, 0xbb,
+ 0xb0, 0x3, 0xad, 0xe9, 0xcf, 0xeb, 0x4f, 0x9d, 0x7e, 0xfc, 0x34, 0x1a,
+ 0x6b, 0xde, 0x68, 0x68, 0xf7, 0xe, 0xb3, 0x4e, 0x40, 0x63, 0x2b, 0xfa,
+ 0x73, 0x14, 0x60, 0x5d, 0x4c, 0x23, 0x80, 0x58, 0x9a, 0x75, 0xd, 0x0,
+ 0xc9, 0xb3, 0x92, 0xa3, 0xe2, 0x1, 0x1, 0x63, 0x53, 0x8f, 0x1, 0x7c,
+ 0x7a, 0xbb, 0xeb, 0x3, 0x24, 0x31, 0xcc, 0xf3, 0x0, 0x94, 0x4a, 0x2b,
+ 0x8d, 0x30, 0x9, 0x19, 0x9c, 0x7, 0x18, 0xb8, 0xf, 0x90, 0x34, 0x9c,
+ 0xf0, 0x38, 0x71, 0x5b, 0x47, 0xaa, 0x92, 0xba, 0x6a, 0x1e, 0x20, 0x84,
+ 0x69, 0x44, 0x85, 0x53, 0x93, 0x6c, 0xf3, 0xbc, 0x3a, 0xbe, 0xad, 0x6f,
+ 0xaf, 0xd1, 0xbc, 0x62, 0x17, 0x57, 0x2a, 0x9a, 0x3a, 0x87, 0xe2, 0x30,
+ 0x0, 0xbe, 0x47, 0x22, 0xb4, 0x97, 0x57, 0x9e, 0x8, 0x9, 0xef, 0x98,
+ 0x23, 0x53, 0x48, 0x7d, 0x30, 0x13, 0xc4, 0x59, 0x49, 0x67, 0x3a, 0xd1,
+ 0x36, 0x7b, 0xf1, 0x8a, 0x62, 0xad, 0x70, 0x48, 0x34, 0xaf, 0x6, 0x63,
+ 0xd9, 0x8b, 0xe7, 0x4f, 0xca, 0xb1, 0xd4, 0x5f, 0xda, 0x97, 0xc3, 0xbe,
+ 0xd4, 0xc4, 0x5f, 0xc, 0x60, 0x8b, 0xb3, 0x6b, 0x49, 0x33, 0xf7, 0xcd,
+ 0xe5, 0x36, 0x88, 0xf4, 0x56, 0xa0, 0xb7, 0xdc, 0x0, 0x7a, 0x2b, 0xd0,
+ 0x5b, 0x6e, 0x0, 0xbd, 0x15, 0xe8, 0x2d, 0x37, 0x80, 0xde, 0xa, 0xf4,
+ 0x96, 0x1b, 0xc0, 0x9e, 0xc4, 0xda, 0xf3, 0xf5, 0xd7, 0xf4, 0xd6, 0xe,
+ 0xcf, 0x97, 0x6b, 0xc5, 0x29, 0x30, 0xf0, 0xf, 0xa4, 0x2c, 0xd9, 0xd6,
+ 0x86, 0xfc, 0xc7, 0xb3, 0xdb, 0xf3, 0x25, 0xf4, 0xa5, 0x39, 0xe7, 0x17,
+ 0x5a, 0x41, 0x39, 0x49, 0x60, 0xc0, 0x2d, 0xbe, 0xff, 0x15, 0xc0, 0x6e,
+ 0x81, 0x1, 0x6b, 0xca, 0x29, 0x2d, 0xcb, 0x95, 0x0, 0x0, 0x2d, 0xb3,
+ 0xa9, 0xc4, 0x1c, 0xf3, 0xb0, 0xd5, 0x20, 0x19, 0x3e, 0xd2, 0x9a, 0xdf,
+ 0x73, 0xbb, 0x42, 0x28, 0xf9, 0x53, 0xc0, 0x97, 0xd2, 0xfa, 0x3e, 0xfb,
+ 0x17, 0xd8, 0x24, 0x3c, 0x2d, 0xfb, 0xd, 0xca, 0xb9, 0x92, 0x96, 0xcf,
+ 0x46, 0xe1, 0x98, 0xe6, 0x3a, 0x0, 0xef, 0xb2, 0xcb, 0x3b, 0xc, 0x60,
+ 0x45, 0x8b, 0x24, 0x11, 0x80, 0x56, 0xd9, 0x37, 0x4, 0x0, 0x90, 0x7,
+ 0x5f, 0xda, 0x4d, 0x68, 0x6, 0x59, 0x9c, 0xc2, 0x16, 0x35, 0xb3, 0x39,
+ 0x3e, 0xef, 0x3f, 0xda, 0x93, 0x72, 0x2a, 0x0, 0xd3, 0x50, 0x4a, 0xa0,
+ 0x91, 0x82, 0x9a, 0x0, 0x80, 0x9c, 0x86, 0x9a, 0x7f, 0x69, 0x37, 0x38,
+ 0x43, 0x8b, 0x53, 0x40, 0x36, 0x47, 0xf1, 0x7e, 0xe, 0x40, 0x3b, 0x87,
+ 0xad, 0xa7, 0xad, 0x0, 0x2c, 0x8e, 0xfc, 0x95, 0x0, 0x2c, 0xe9, 0xb,
+ 0x2b, 0xfb, 0x30, 0x64, 0xab, 0x6b, 0xe0, 0x5f, 0x1a, 0xcb, 0x23, 0x81,
+ 0x2d, 0xb9, 0x0, 0xe0, 0xb1, 0xcb, 0xe2, 0xfd, 0x1c, 0x80, 0x98, 0x15,
+ 0xbe, 0x15, 0x0, 0xf6, 0xf5, 0x88, 0x0, 0x52, 0x85, 0xd1, 0x1b, 0x2,
+ 0xe0, 0xc9, 0xf2, 0x2f, 0x59, 0x19, 0xc0, 0x9c, 0xd3, 0xe5, 0x84, 0xda,
+ 0xfb, 0x39, 0x0, 0x13, 0x42, 0x18, 0xcf, 0x0, 0x30, 0x40, 0x6f, 0x94,
+ 0x50, 0x8a, 0xa3, 0x9c, 0xa6, 0x7, 0x2, 0xc8, 0xb9, 0x5f, 0xd8, 0xf5,
+ 0x41, 0x6, 0x40, 0x28, 0x1b, 0x7f, 0x3, 0x80, 0xf3, 0xfa, 0x0, 0x8b,
+ 0x52, 0x43, 0xc9, 0x0, 0xd6, 0x34, 0x10, 0x42, 0x0, 0x33, 0x71, 0x9b,
+ 0x79, 0x71, 0x75, 0x21, 0x97, 0x59, 0x1a, 0x4d, 0x90, 0x81, 0xe6, 0x79,
+ 0xff, 0x51, 0x7e, 0xf8, 0xaf, 0x1, 0x40, 0x69, 0x7e, 0xf6, 0x51, 0x60,
+ 0xa9, 0x2d, 0xe5, 0xb1, 0x49, 0x5, 0x2, 0xd8, 0x14, 0xf7, 0x75, 0x2d,
+ 0x65, 0x44, 0xb3, 0x78, 0x14, 0x0, 0xf2, 0xbc, 0xff, 0xd0, 0x2c, 0xde,
+ 0x3c, 0xf, 0x70, 0x26, 0x75, 0x99, 0x20, 0x6c, 0x7b, 0x9f, 0x7, 0xf0,
+ 0x46, 0x10, 0x8a, 0xc7, 0x84, 0x85, 0x4e, 0x3c, 0x0, 0x30, 0xf7, 0x5b,
+ 0xd1, 0xc7, 0xa1, 0x83, 0x89, 0x8a, 0xc3, 0x0, 0xba, 0xde, 0xe0, 0x9b,
+ 0x19, 0xe1, 0xfe, 0x6a, 0x0, 0x56, 0xb0, 0xc3, 0x7b, 0x1c, 0xfe, 0xbf,
+ 0x2b, 0xa2, 0x79, 0x23, 0x8, 0x78, 0xe7, 0xc6, 0xc3, 0xb7, 0x66, 0x21,
+ 0x80, 0x88, 0xbc, 0xa0, 0x25, 0x62, 0x85, 0xcf, 0x33, 0xc4, 0x4d, 0x21,
+ 0xc2, 0xfd, 0xaf, 0x5f, 0x3f, 0xba, 0x1, 0x48, 0x6b, 0xf8, 0xff, 0x69,
+ 0xd2, 0xf8, 0xe0, 0xf0, 0xe9, 0x49, 0x53, 0x7f, 0xbb, 0xa4, 0x7a, 0x34,
+ 0x8c, 0x87, 0x13, 0xa1, 0xde, 0x5a, 0x5e, 0x29, 0x5e, 0xc1, 0xa4, 0x5c,
+ 0xbf, 0x7, 0x40, 0xcd, 0x91, 0x83, 0x3f, 0x1c, 0xc0, 0xb1, 0xdc, 0x0,
+ 0x7a, 0x2b, 0xd0, 0x5b, 0x6e, 0x0, 0xbd, 0x15, 0xe8, 0x2d, 0x37, 0x80,
+ 0xde, 0xa, 0xf4, 0x96, 0x1b, 0x40, 0x6f, 0x5, 0xae, 0x13, 0x27, 0x66,
+ 0x45, 0xf8, 0x2f, 0x0, 0xd1, 0xae, 0xee, 0x60, 0x82, 0xfe, 0x67, 0xbe,
+ 0xa0, 0x89, 0x5d, 0xdf, 0xfd, 0xb, 0xcc, 0x36, 0x67, 0xcb, 0xca, 0xd1,
+ 0xbe, 0x4e, 0x49, 0x1f, 0x51, 0x98, 0x2c, 0x18, 0x73, 0xb1, 0x79, 0x5,
+ 0xe3, 0x98, 0x46, 0xce, 0x66, 0x13, 0x44, 0xe4, 0xeb, 0x73, 0x39, 0x7f,
+ 0x51, 0xe, 0x21, 0xad, 0x0, 0x50, 0x72, 0x9c, 0x80, 0xd5, 0xe7, 0x54,
+ 0x2, 0xfc, 0xd8, 0xf5, 0x35, 0x7b, 0x5, 0x14, 0x7, 0xf0, 0x5c, 0x56,
+ 0x73, 0x63, 0x66, 0x7e, 0x21, 0x1e, 0x43, 0x3b, 0x8, 0xcb, 0xe1, 0x2d,
+ 0x47, 0xca, 0x47, 0x42, 0xf1, 0x83, 0x1b, 0x4c, 0x53, 0x23, 0xda, 0x34,
+ 0x8f, 0x83, 0x24, 0x9f, 0x0, 0x4a, 0x4c, 0x24, 0x70, 0x38, 0x48, 0xf1,
+ 0x70, 0x30, 0x53, 0x82, 0x4, 0x60, 0xf, 0x5b, 0x77, 0x4c, 0xf1, 0x87,
+ 0x41, 0x84, 0x3, 0xc8, 0xc7, 0x3c, 0xa2, 0x74, 0x3c, 0xd9, 0xb8, 0x23,
+ 0x1d, 0x23, 0x8b, 0x1c, 0x32, 0x4d, 0x0, 0x40, 0x7c, 0xe3, 0x46, 0xe2,
+ 0x31, 0x81, 0x4d, 0x0, 0xf6, 0xbd, 0x12, 0x3c, 0xf9, 0x50, 0x6, 0xe0,
+ 0xc2, 0x66, 0xf9, 0x1b, 0x69, 0x82, 0x39, 0xd1, 0x73, 0x15, 0x18, 0xf1,
+ 0x3b, 0xe1, 0x4c, 0xcf, 0xd, 0x0, 0xe2, 0x8, 0x8, 0xe6, 0xc, 0xf,
+ 0x18, 0x41, 0x1b, 0x80, 0xb2, 0xeb, 0x1f, 0x45, 0xbf, 0x87, 0x6c, 0xb0,
+ 0x0, 0x36, 0xb7, 0x45, 0x8, 0x72, 0xb7, 0x34, 0xb, 0x15, 0x0, 0x67,
+ 0x17, 0xaf, 0x7, 0x20, 0xa4, 0xa0, 0x16, 0xcf, 0xb, 0x6e, 0x3, 0x90,
+ 0xf, 0x3f, 0xf0, 0x28, 0xf8, 0x3b, 0xa4, 0x5e, 0x6, 0x1, 0xc8, 0x26,
+ 0x51, 0x7c, 0xf2, 0xb6, 0x10, 0xfd, 0xef, 0xb0, 0xfb, 0xa7, 0x1e, 0x80,
+ 0x13, 0x3c, 0x68, 0x71, 0xc2, 0xaa, 0xb4, 0x1, 0x48, 0xf, 0x88, 0x23,
+ 0xb7, 0xdb, 0x7b, 0xa, 0xda, 0x10, 0x4a, 0xa3, 0xef, 0xa5, 0xa3, 0xaa,
+ 0x2d, 0xae, 0x0, 0x52, 0xaa, 0xf3, 0x96, 0x3e, 0x40, 0xc9, 0xc7, 0x96,
+ 0xc2, 0x73, 0x84, 0xdf, 0x0, 0xe0, 0xf7, 0x5b, 0xf2, 0x20, 0x8f, 0x78,
+ 0x48, 0x15, 0xda, 0x13, 0xca, 0x3d, 0xa5, 0xc8, 0xe1, 0x57, 0x12, 0xd2,
+ 0xd4, 0x48, 0xe9, 0xf5, 0x5b, 0x0, 0x4c, 0x92, 0x7, 0x0, 0x66, 0x7a,
+ 0x82, 0x8a, 0xac, 0x8f, 0x61, 0x8a, 0x77, 0x1b, 0x78, 0x37, 0x59, 0xf1,
+ 0xe, 0xf, 0x60, 0x7f, 0x81, 0x2b, 0xa3, 0x0, 0xf2, 0x67, 0x60, 0x0,
+ 0x52, 0x76, 0xa1, 0x26, 0x0, 0x70, 0xdc, 0x35, 0x65, 0xc4, 0xaf, 0x56,
+ 0x44, 0x93, 0x81, 0x79, 0x3, 0xcb, 0x7e, 0x42, 0x8e, 0x45, 0x8a, 0xf,
+ 0x28, 0xaa, 0x44, 0xaa, 0x6, 0x9f, 0x6e, 0xd5, 0xd2, 0x44, 0xa8, 0x61,
+ 0x18, 0xe4, 0x55, 0x31, 0xef, 0x3b, 0x14, 0xec, 0xea, 0x18, 0xc0, 0x7e,
+ 0xc0, 0x30, 0xba, 0x5f, 0xe3, 0xb0, 0x81, 0x34, 0x32, 0x46, 0xe, 0x60,
+ 0xd7, 0x4, 0x92, 0x14, 0x9a, 0xde, 0xa7, 0x13, 0x21, 0x59, 0x42, 0x6b,
+ 0x38, 0x7f, 0x37, 0x87, 0x1, 0x94, 0xcf, 0x1, 0xfc, 0xe5, 0x52, 0xb6,
+ 0x99, 0xfd, 0x6e, 0x0, 0x75, 0xab, 0xc1, 0x1f, 0x2b, 0x31, 0x84, 0xdf,
+ 0x5d, 0x3, 0xea, 0xe4, 0x6, 0xd0, 0x5b, 0x81, 0xde, 0x72, 0x3, 0xe8,
+ 0xad, 0x40, 0x6f, 0xb9, 0x1, 0xf4, 0x56, 0xa0, 0xb7, 0xdc, 0x0, 0xb6,
+ 0x51, 0x8e, 0x85, 0x3b, 0xce, 0xbf, 0x50, 0x21, 0x53, 0xe3, 0x99, 0x5a,
+ 0xd3, 0x71, 0x60, 0xd3, 0x49, 0x5f, 0x7a, 0x0, 0x10, 0xd7, 0xc, 0xde,
+ 0x9, 0x36, 0xc8, 0xc5, 0x36, 0x71, 0x69, 0x3c, 0x9e, 0xd0, 0xb6, 0x1d,
+ 0x65, 0xf7, 0xc1, 0x97, 0x1e, 0x0, 0x36, 0x39, 0xa0, 0x5a, 0x38, 0x70,
+ 0x50, 0xab, 0xb6, 0xc7, 0x2d, 0xae, 0xe5, 0xe7, 0xf9, 0xf3, 0x0, 0x46,
+ 0x31, 0xbf, 0x37, 0x6, 0xe0, 0x68, 0xd5, 0x47, 0x1, 0xa8, 0x35, 0xe2,
+ 0x61, 0x52, 0x98, 0xfc, 0x2e, 0xde, 0x41, 0x73, 0x74, 0xc4, 0x5f, 0xc8,
+ 0x5, 0x69, 0x98, 0xd5, 0xdc, 0x55, 0x1c, 0x9c, 0x5c, 0xbc, 0x22, 0x52,
+ 0x2f, 0x0, 0x1, 0xc4, 0x7c, 0x50, 0xb7, 0xe8, 0x33, 0xa8, 0x17, 0x21,
+ 0x7b, 0x7d, 0x2, 0x90, 0x3b, 0x1f, 0x40, 0x78, 0x92, 0x33, 0x46, 0xc1,
+ 0x14, 0xda, 0x86, 0x24, 0xeb, 0xea, 0xb, 0x80, 0xf4, 0x48, 0xa1, 0x8a,
+ 0x42, 0x0, 0xc5, 0x95, 0x61, 0xf, 0xb7, 0xa2, 0x1c, 0x4a, 0x4e, 0x8,
+ 0x83, 0x3c, 0x52, 0x96, 0x94, 0xf, 0x7c, 0x7, 0x48, 0x76, 0x17, 0xb8,
+ 0x10, 0x78, 0x87, 0x5a, 0x72, 0x1d, 0x19, 0x94, 0xa9, 0x6a, 0xae, 0x49,
+ 0xde, 0x9e, 0x1, 0x78, 0x89, 0x13, 0x4, 0x50, 0xaa, 0xd5, 0x7a, 0xca,
+ 0xa9, 0xdc, 0x21, 0x18, 0x94, 0x90, 0xca, 0x3f, 0x63, 0xbd, 0xff, 0x23,
+ 0x42, 0x5a, 0xe5, 0xf5, 0x71, 0x48, 0x2b, 0xcf, 0xeb, 0x44, 0x69, 0x35,
+ 0x3c, 0xd4, 0xa5, 0xd1, 0xd1, 0xc2, 0x6c, 0xe0, 0xd2, 0xa3, 0xa7, 0xe3,
+ 0x80, 0xab, 0xae, 0xc5, 0x5b, 0x60, 0x36, 0x8d, 0xfb, 0xb9, 0xf0, 0x38,
+ 0xaa, 0x83, 0x9f, 0x61, 0x22, 0x6e, 0x4, 0xe1, 0x0, 0xa4, 0x91, 0xf0,
+ 0x52, 0x0, 0x39, 0x29, 0x21, 0xae, 0x1, 0x1, 0xd6, 0x0, 0x83, 0xfb,
+ 0x6a, 0x9, 0x40, 0x2c, 0x35, 0xa0, 0x32, 0x95, 0x96, 0x34, 0x12, 0x5e,
+ 0xa, 0xe0, 0x91, 0x64, 0xd, 0x3, 0x0, 0x1b, 0x12, 0x6, 0x6c, 0xff,
+ 0x2f, 0x4d, 0xc0, 0x83, 0x9a, 0xa4, 0xea, 0xd2, 0x7c, 0x3c, 0x53, 0x69,
+ 0x9, 0x74, 0x2f, 0x4, 0x90, 0xcf, 0x1d, 0x50, 0xb0, 0x9, 0x8c, 0xce,
+ 0xc2, 0xfd, 0x87, 0xd9, 0x1, 0x80, 0x9c, 0xc9, 0x76, 0x22, 0x98, 0x78,
+ 0x49, 0x4d, 0xce, 0x1d, 0xc6, 0x8b, 0xd3, 0x3, 0xa3, 0xb0, 0xb1, 0xe9,
+ 0x42, 0x0, 0xc5, 0x5, 0x8, 0xb2, 0xf2, 0x95, 0xd1, 0xe, 0x85, 0x38,
+ 0xa7, 0xa1, 0x1, 0x6d, 0x90, 0x5c, 0xd3, 0xe5, 0x9, 0x1d, 0x92, 0x32,
+ 0x95, 0x3c, 0x42, 0x75, 0x0, 0x84, 0x91, 0xf0, 0x52, 0x0, 0x72, 0x16,
+ 0xa1, 0x18, 0xda, 0xbe, 0x21, 0x9f, 0x83, 0x50, 0x31, 0x54, 0x3d, 0x0,
+ 0x2c, 0x96, 0xcf, 0x6c, 0xd6, 0x30, 0x37, 0x0, 0x8, 0x4f, 0xb9, 0x84,
+ 0xd5, 0x75, 0x72, 0x90, 0x54, 0xb5, 0x1e, 0xc0, 0xf3, 0xb0, 0x9a, 0x2f,
+ 0x4f, 0xca, 0xbf, 0x1f, 0x80, 0xb5, 0x62, 0x10, 0xf9, 0x9f, 0xb8, 0xa7,
+ 0x7c, 0x2b, 0xf7, 0xd8, 0x47, 0x0, 0x7e, 0x87, 0xdc, 0x0, 0x7a, 0x2b,
+ 0xd0, 0x5b, 0x6e, 0x0, 0xbd, 0x15, 0xe8, 0x2d, 0x37, 0x80, 0xde, 0xa,
+ 0xf4, 0x96, 0x1b, 0x40, 0xfe, 0xb3, 0x58, 0x65, 0xaa, 0x8d, 0xf7, 0xb2,
+ 0x9, 0x3e, 0x88, 0x9b, 0xf5, 0xd, 0x38, 0x13, 0x54, 0x2a, 0xc7, 0xe3,
+ 0x64, 0xf9, 0x57, 0x2, 0xc8, 0xa7, 0x82, 0xda, 0x8a, 0x7d, 0xc6, 0xbb,
+ 0xc8, 0x16, 0x68, 0x27, 0x45, 0xa4, 0x44, 0x14, 0x27, 0x27, 0x95, 0xe3,
+ 0x73, 0x56, 0xdd, 0x3f, 0xb, 0xc0, 0x36, 0x9d, 0x7b, 0xf9, 0xc6, 0x4,
+ 0xef, 0xa4, 0x0, 0xfb, 0xc5, 0xb5, 0x94, 0x73, 0x9c, 0xf7, 0xe2, 0x64,
+ 0x0, 0xc2, 0x2, 0x26, 0x96, 0xa9, 0x3d, 0x48, 0xce, 0x81, 0xed, 0xf6,
+ 0x21, 0xdf, 0xd, 0x8a, 0xf1, 0xc2, 0xa, 0xa1, 0x0, 0x98, 0x11, 0x33,
+ 0x8, 0x20, 0xb8, 0x98, 0x9e, 0x3a, 0x3b, 0x50, 0xfe, 0x26, 0xf8, 0x11,
+ 0x66, 0xe7, 0x8e, 0x53, 0x0, 0x53, 0x5a, 0xc7, 0xe6, 0x3c, 0x2c, 0x7c,
+ 0xd9, 0xb3, 0x2a, 0x82, 0x87, 0x6c, 0x58, 0x7c, 0x59, 0xc, 0xca, 0xb2,
+ 0xc2, 0x1a, 0x31, 0x3, 0xc0, 0x47, 0x2b, 0x42, 0x0, 0x8e, 0xf2, 0x89,
+ 0x14, 0xc0, 0xd0, 0x6f, 0xf3, 0x7, 0x40, 0x9f, 0xdd, 0x5f, 0x70, 0xec,
+ 0x17, 0x10, 0x23, 0x2d, 0xf3, 0x69, 0xd4, 0x2b, 0x48, 0x41, 0x90, 0xed,
+ 0xf6, 0xb, 0xb0, 0xe7, 0xc7, 0x30, 0x43, 0x0, 0x79, 0x51, 0x69, 0x30,
+ 0x0, 0x8f, 0x33, 0x5d, 0x9, 0x0, 0x6c, 0xde, 0x32, 0x1, 0x53, 0x6b,
+ 0x40, 0x7d, 0x34, 0x8d, 0x61, 0x51, 0x87, 0xad, 0x5b, 0xae, 0x1, 0x65,
+ 0x33, 0xb, 0x3c, 0x33, 0xdc, 0xb, 0xd, 0x58, 0x6e, 0xbb, 0xf8, 0x48,
+ 0xcf, 0x59, 0x48, 0xea, 0x2e, 0x0, 0x58, 0x2, 0x4d, 0x10, 0x0, 0xd4,
+ 0x27, 0x14, 0xeb, 0xd9, 0xb1, 0x63, 0x56, 0xee, 0x3, 0xd2, 0x77, 0xc3,
+ 0x4, 0x5d, 0x57, 0xe1, 0x24, 0x0, 0x24, 0x5a, 0xe3, 0x21, 0x80, 0x90,
+ 0x2d, 0x74, 0x8, 0x0, 0xd4, 0x67, 0x7e, 0x9c, 0x53, 0x70, 0x34, 0xa2,
+ 0xca, 0x0, 0x66, 0xe1, 0xf0, 0xf9, 0xf3, 0x0, 0xcc, 0xe2, 0x11, 0x3,
+ 0x27, 0x0, 0xd8, 0x53, 0x31, 0xcd, 0x1f, 0x0, 0x50, 0x1a, 0x5b, 0x1b,
+ 0xcf, 0x3, 0x10, 0x1c, 0x36, 0xda, 0x9e, 0x2, 0x60, 0x77, 0x3b, 0xda,
+ 0x43, 0x2f, 0xee, 0x1b, 0x0, 0x64, 0x47, 0x7, 0x3c, 0xfb, 0x12, 0x80,
+ 0x1c, 0x4f, 0x41, 0x82, 0x43, 0x5a, 0x0, 0x90, 0x7b, 0xf0, 0xad, 0xb2,
+ 0x9c, 0x56, 0x0, 0xe9, 0x92, 0x9e, 0x6c, 0x9d, 0x59, 0x5c, 0xda, 0xa0,
+ 0x42, 0x30, 0x3f, 0x9a, 0xfc, 0x40, 0xd9, 0x17, 0x27, 0x1, 0xd8, 0x14,
+ 0x1a, 0xd6, 0x60, 0x39, 0xcd, 0x0, 0x3e, 0xcd, 0x23, 0xb4, 0x96, 0x5e,
+ 0xf4, 0xb8, 0xd, 0x55, 0x89, 0xe9, 0x63, 0x2b, 0xae, 0xb1, 0xe9, 0x8a,
+ 0x0, 0xf6, 0x61, 0x64, 0x68, 0x8b, 0xef, 0x41, 0x32, 0x85, 0x30, 0x57,
+ 0x1e, 0x7a, 0xd4, 0x43, 0xe4, 0x59, 0xb7, 0x11, 0xce, 0xf5, 0x69, 0x7e,
+ 0x82, 0xb4, 0xfd, 0xf0, 0x7b, 0xc8, 0x9b, 0x65, 0x47, 0x9a, 0xdc, 0xcf,
+ 0x27, 0x9c, 0xa3, 0xb0, 0xe2, 0xc0, 0x9e, 0xef, 0x22, 0xb7, 0x41, 0xa4,
+ 0xb7, 0x2, 0xbd, 0xe5, 0x6, 0xd0, 0x5b, 0x81, 0xde, 0x72, 0x3, 0xe8,
+ 0xad, 0x40, 0x6f, 0xb9, 0x1, 0xf4, 0x56, 0xa0, 0xb7, 0xdc, 0x0, 0xae,
+ 0x7f, 0xc4, 0x6a, 0xfe, 0xa0, 0x9f, 0xe3, 0x3b, 0x2, 0x8, 0xe7, 0xac,
+ 0x28, 0x2f, 0x3, 0x20, 0xdb, 0xd5, 0x7d, 0x9a, 0xc5, 0xcf, 0x3c, 0xf7,
+ 0x85, 0x8b, 0xc1, 0xc1, 0x2d, 0x10, 0xb3, 0x3, 0xd7, 0x83, 0x1b, 0x4b,
+ 0xfe, 0x7a, 0x50, 0x7a, 0x7c, 0xfc, 0x79, 0xb9, 0x5c, 0x56, 0xce, 0xec,
+ 0xb, 0x8f, 0xb, 0xe1, 0xf5, 0xb, 0x6b, 0xf9, 0x80, 0x1f, 0x80, 0x28,
+ 0x95, 0xc3, 0x1, 0xec, 0x76, 0x75, 0x6e, 0x99, 0x91, 0xe2, 0xf0, 0x5d,
+ 0xf1, 0xb, 0xa8, 0x88, 0xef, 0x77, 0xfc, 0x76, 0x21, 0x41, 0xb3, 0x68,
+ 0xcc, 0xcc, 0xaa, 0x78, 0xa6, 0x90, 0x7d, 0x66, 0x9e, 0x7a, 0x7d, 0x70,
+ 0x49, 0x21, 0xa2, 0xaa, 0xcb, 0x41, 0x0, 0x70, 0x7c, 0xbe, 0x14, 0x87,
+ 0xef, 0x72, 0xd2, 0x9d, 0x1, 0xf9, 0x11, 0xb2, 0x7f, 0x15, 0xba, 0xb,
+ 0xc, 0xa, 0x1f, 0x94, 0x2c, 0x39, 0xc5, 0x96, 0x37, 0x30, 0x7d, 0x6,
+ 0x29, 0xf3, 0x54, 0x4e, 0x22, 0x83, 0x42, 0xe9, 0x1f, 0xe5, 0x1c, 0xfb,
+ 0x5, 0x84, 0xf8, 0x7c, 0x31, 0xe, 0xbf, 0xdc, 0xa, 0xe, 0x65, 0x49,
+ 0x35, 0x1d, 0x3f, 0x4c, 0xe8, 0x3, 0x24, 0x0, 0xa3, 0x94, 0x78, 0xc9,
+ 0x2f, 0xe9, 0xc9, 0x60, 0xb3, 0xce, 0x4c, 0xa, 0x5, 0x7b, 0xb, 0xe5,
+ 0x20, 0x0, 0x30, 0x3e, 0x5f, 0xa, 0x43, 0x17, 0x63, 0xeb, 0x73, 0xcc,
+ 0x2e, 0xd, 0x28, 0xc6, 0xb7, 0xd, 0x40, 0x36, 0x45, 0xcd, 0xdc, 0x10,
+ 0xe5, 0x29, 0x1b, 0x4b, 0x37, 0x54, 0x96, 0x21, 0x6c, 0xa3, 0x86, 0xe5,
+ 0x88, 0x0, 0x6, 0xf6, 0xfd, 0x46, 0x0, 0x49, 0xc7, 0x11, 0xfe, 0x14,
+ 0x8d, 0x0, 0x72, 0x8, 0xbd, 0x41, 0x5c, 0x46, 0x95, 0x1a, 0x2a, 0x78,
+ 0x78, 0xee, 0x7c, 0x50, 0x74, 0x3, 0x2e, 0x47, 0x0, 0xc0, 0xed, 0xe7,
+ 0x62, 0x1c, 0xfe, 0xbb, 0xdc, 0x24, 0x2b, 0xf4, 0xeb, 0xe2, 0xdd, 0x8,
+ 0xf9, 0xb9, 0xa8, 0xfc, 0x6c, 0x88, 0x45, 0xdf, 0x48, 0x15, 0xdd, 0xa7,
+ 0xff, 0xfc, 0xe1, 0xa9, 0x17, 0x54, 0x28, 0xf3, 0x9d, 0x50, 0xe, 0x0,
+ 0x80, 0xe3, 0xf3, 0x4b, 0x1c, 0x7e, 0x3, 0x80, 0x71, 0x9a, 0x97, 0x1,
+ 0x3e, 0x31, 0x87, 0xb3, 0xf3, 0x9f, 0x68, 0x20, 0x3, 0xe3, 0xfc, 0x8b,
+ 0x15, 0x71, 0x86, 0x57, 0xd3, 0x77, 0x50, 0x9f, 0x91, 0x8f, 0xa4, 0x40,
+ 0x56, 0x57, 0xa2, 0x8a, 0xcd, 0x6d, 0xb4, 0xdb, 0xe1, 0x41, 0x7c, 0x7e,
+ 0x89, 0xc3, 0x6f, 0x68, 0x2, 0x65, 0xb, 0x90, 0x41, 0xf3, 0x83, 0x9c,
+ 0xe3, 0x1c, 0x6c, 0x77, 0x93, 0xe2, 0xfc, 0xf3, 0x49, 0xe8, 0xa0, 0x94,
+ 0x7c, 0x7e, 0x87, 0xe7, 0xce, 0x34, 0xff, 0x38, 0x49, 0xc2, 0xd6, 0x96,
+ 0x3, 0x0, 0x2c, 0x42, 0x43, 0x9, 0xb1, 0x6d, 0x47, 0xa6, 0x1c, 0x2a,
+ 0x8f, 0x3f, 0xb9, 0x3a, 0xed, 0x90, 0xad, 0xb1, 0xe9, 0xd3, 0xfb, 0x77,
+ 0xfc, 0xfa, 0x96, 0xd4, 0xde, 0x12, 0xc3, 0x58, 0xe5, 0x8d, 0xf8, 0xb1,
+ 0x0, 0xd2, 0x7c, 0x4d, 0xd5, 0x18, 0xf5, 0x69, 0x5b, 0xdf, 0xbd, 0xe2,
+ 0xfa, 0xb7, 0xbe, 0x7f, 0x5a, 0x34, 0xd4, 0x79, 0x23, 0x6e, 0x7b, 0x40,
+ 0x6f, 0x5, 0x7a, 0xcb, 0xd, 0xa0, 0xb7, 0x2, 0xbd, 0xe5, 0x6, 0xd0,
+ 0x5b, 0x81, 0xde, 0xf2, 0x15, 0x0, 0xf1, 0x94, 0xe4, 0x1, 0xdf, 0x44,
+ 0xbe, 0x74, 0xd4, 0xd6, 0xb5, 0x3b, 0x6a, 0xbf, 0x11, 0x0, 0xaf, 0xe0,
+ 0xab, 0x6, 0xe1, 0x6a, 0x53, 0x38, 0xd, 0x9, 0xa1, 0xc5, 0xa7, 0xca,
+ 0x68, 0xf4, 0xb1, 0x49, 0x4c, 0x96, 0x95, 0x14, 0xac, 0xec, 0x18, 0x40,
+ 0x74, 0xc7, 0x47, 0xbc, 0xfe, 0x7, 0x80, 0x68, 0xaf, 0x5a, 0xcc, 0x49,
+ 0x41, 0x35, 0x69, 0x95, 0x6f, 0x3f, 0x1, 0xe0, 0x48, 0x32, 0xf2, 0x9,
+ 0x4d, 0xa0, 0xc2, 0xfe, 0x50, 0x1, 0xc0, 0xe7, 0x44, 0x1, 0xea, 0x84,
+ 0x56, 0x16, 0x6b, 0x56, 0x43, 0xf4, 0x88, 0xe7, 0xc7, 0x9, 0x83, 0x84,
+ 0xed, 0xea, 0x22, 0x80, 0x86, 0x3a, 0x2d, 0x3, 0x50, 0xc5, 0x8c, 0x71,
+ 0xc2, 0x52, 0xb9, 0xca, 0x23, 0x43, 0xe2, 0xc1, 0x8a, 0x9d, 0x0, 0xac,
+ 0x8e, 0x54, 0x8, 0x27, 0x4, 0x67, 0xa1, 0x53, 0x1d, 0x20, 0x80, 0x1c,
+ 0xcf, 0xaf, 0xe0, 0xad, 0x63, 0x2b, 0x0, 0x98, 0x8d, 0x26, 0xce, 0xb0,
+ 0x21, 0x49, 0x0, 0xec, 0x49, 0xd1, 0x79, 0x5b, 0x4e, 0xc, 0xae, 0xed,
+ 0x61, 0xa8, 0x68, 0xd1, 0x1, 0x6e, 0xdc, 0xf0, 0xa3, 0xe4, 0x55, 0x11,
+ 0x1, 0x38, 0xd2, 0xec, 0x93, 0xa8, 0x8a, 0xdb, 0x89, 0x39, 0x34, 0xc4,
+ 0x26, 0x70, 0x7c, 0x4e, 0x6a, 0x9d, 0x54, 0xa6, 0x33, 0xd8, 0x13, 0x29,
+ 0x21, 0xd3, 0x81, 0x15, 0xf, 0x19, 0x11, 0x1, 0x64, 0x33, 0xdf, 0xeb,
+ 0xb5, 0x99, 0xdc, 0x94, 0xb8, 0xb0, 0xdf, 0x54, 0x2, 0x30, 0xa9, 0xfc,
+ 0xbb, 0xb5, 0x65, 0xe0, 0x13, 0xa4, 0xca, 0x9a, 0x43, 0xc5, 0xae, 0xe,
+ 0x2b, 0x69, 0x4, 0xa7, 0x2, 0xbc, 0x7, 0x30, 0x0, 0x64, 0xde, 0xee,
+ 0xf9, 0x52, 0x5e, 0x3f, 0x91, 0x0, 0x78, 0x9b, 0x6, 0xdf, 0x73, 0x42,
+ 0x2b, 0xab, 0x12, 0xe9, 0x90, 0x7c, 0x40, 0xc1, 0x89, 0x9d, 0x60, 0xe4,
+ 0xf6, 0x4f, 0xb1, 0x9, 0xac, 0xcd, 0x59, 0x38, 0xa4, 0x7d, 0x31, 0x55,
+ 0x7d, 0x32, 0x65, 0xe3, 0xb1, 0xb0, 0x57, 0xb3, 0xd3, 0x30, 0x68, 0xf3,
+ 0x59, 0xa9, 0xa6, 0xa1, 0x23, 0xb0, 0x78, 0xe7, 0x49, 0xd5, 0x34, 0xa0,
+ 0xe7, 0x30, 0x28, 0x4e, 0x85, 0x47, 0x78, 0x98, 0xb8, 0x2c, 0x42, 0x9d,
+ 0xf1, 0x35, 0xa7, 0xee, 0x1e, 0xcc, 0x4, 0x71, 0x2f, 0x28, 0x3, 0x38,
+ 0x2b, 0x2a, 0xbe, 0x71, 0x1e, 0x80, 0x9e, 0x6b, 0xad, 0xa2, 0xe3, 0x6d,
+ 0x93, 0xef, 0x1, 0x2c, 0x64, 0x16, 0xbc, 0xaf, 0xf, 0x2, 0x8, 0x43,
+ 0x15, 0xf2, 0xb3, 0x25, 0x3a, 0xb8, 0xf5, 0x57, 0x93, 0xa9, 0xaa, 0x44,
+ 0x6f, 0x2b, 0xed, 0x48, 0x6d, 0xab, 0x41, 0xdd, 0x23, 0x85, 0x4e, 0xd0,
+ 0xc3, 0x27, 0xe6, 0x89, 0xf7, 0xad, 0x36, 0x42, 0xef, 0x95, 0x60, 0x10,
+ 0xf9, 0xeb, 0x92, 0x48, 0x55, 0x0, 0xf8, 0x5, 0x72, 0x3, 0xe8, 0xad,
+ 0x40, 0x6f, 0xb9, 0x1, 0xf4, 0x56, 0xa0, 0xb7, 0xdc, 0x0, 0x7a, 0x2b,
+ 0xd0, 0x5b, 0x6e, 0x0, 0xbd, 0x15, 0xe8, 0x2d, 0x37, 0x80, 0xde, 0xa,
+ 0xf4, 0x96, 0x1b, 0x40, 0x6f, 0x5, 0x7a, 0xcb, 0xd, 0xa0, 0xb7, 0x2,
+ 0xbd, 0xe5, 0x6, 0xd0, 0x5b, 0x81, 0xde, 0x72, 0x3, 0xe8, 0xad, 0x40,
+ 0x6f, 0xf9, 0xf5, 0x0, 0xfe, 0x1, 0x35, 0x69, 0x45, 0xec, 0xa0, 0xf0,
+ 0xdf, 0x8c, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61,
+ 0x74, 0x65, 0x3a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x0, 0x32, 0x30,
+ 0x31, 0x37, 0x2d, 0x30, 0x34, 0x2d, 0x30, 0x32, 0x54, 0x31, 0x38, 0x3a,
+ 0x33, 0x39, 0x3a, 0x33, 0x37, 0x2b, 0x30, 0x30, 0x3a, 0x30, 0x30, 0x98,
+ 0xbb, 0xf, 0xb3, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64,
+ 0x61, 0x74, 0x65, 0x3a, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0, 0x32,
+ 0x30, 0x31, 0x37, 0x2d, 0x30, 0x34, 0x2d, 0x30, 0x32, 0x54, 0x31, 0x38,
+ 0x3a, 0x33, 0x39, 0x3a, 0x33, 0x37, 0x2b, 0x30, 0x30, 0x3a, 0x30, 0x30,
+ 0xe9, 0xe6, 0xb7, 0xf, 0x0, 0x0, 0x0, 0x11, 0x74, 0x45, 0x58, 0x74,
+ 0x70, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x0, 0x55, 0x6e, 0x64, 0x65,
+ 0x66, 0x69, 0x6e, 0x65, 0x64, 0xdb, 0xa5, 0x33, 0x1b, 0x0, 0x0, 0x0,
+ 0x4a, 0x74, 0x45, 0x58, 0x74, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75,
+ 0x72, 0x65, 0x0, 0x65, 0x34, 0x39, 0x30, 0x38, 0x64, 0x62, 0x62, 0x30,
+ 0x62, 0x64, 0x38, 0x62, 0x36, 0x62, 0x66, 0x64, 0x32, 0x38, 0x39, 0x33,
+ 0x34, 0x39, 0x38, 0x39, 0x35, 0x39, 0x39, 0x62, 0x66, 0x32, 0x66, 0x61,
+ 0x39, 0x38, 0x63, 0x63, 0x33, 0x64, 0x35, 0x36, 0x35, 0x31, 0x39, 0x66,
+ 0x36, 0x66, 0x36, 0x35, 0x31, 0x64, 0x61, 0x32, 0x65, 0x34, 0x36, 0x30,
+ 0x34, 0x38, 0x38, 0x38, 0x33, 0x34, 0x35, 0xfb, 0xa7, 0xec, 0xfc, 0x0,
+ 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82};
+} // namespace consolas_font
+const unsigned int sGlyphWidth = 9;
+const unsigned int sTextureWidth = 256;
+const unsigned int sTextureHeight = 256;
+const unsigned int sCellWidth = 24;
+const unsigned int sCellHeight = 24;
+const unsigned int sFirstChar = 32;
+
+const FontBitmapInfo sFixedWidthCompositorFont = {
+ mozilla::Some(9),
+ mozilla::Nothing(),
+ 256,
+ 256,
+ 24,
+ 24,
+ 32,
+ consolas_font::sFontPNG,
+ sizeof(consolas_font::sFontPNG)};
+
+} // namespace layers
+} // namespace mozilla
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
diff --git a/gfx/layers/composite/ContainerLayerComposite.h b/gfx/layers/composite/ContainerLayerComposite.h
new file mode 100644
index 0000000000..68ad26db2f
--- /dev/null
+++ b/gfx/layers/composite/ContainerLayerComposite.h
@@ -0,0 +1,197 @@
+/* -*- 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/. */
+
+#ifndef GFX_ContainerLayerComposite_H
+#define GFX_ContainerLayerComposite_H
+
+#include "Layers.h" // for Layer (ptr only), etc
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/UniquePtr.h" // for UniquePtr
+#include "mozilla/layers/LayerManagerComposite.h"
+#include "mozilla/gfx/Rect.h"
+
+namespace mozilla {
+namespace layers {
+
+class APZSampler;
+class CompositableHost;
+class CompositingRenderTarget;
+struct PreparedData;
+
+class ContainerLayerComposite : public ContainerLayer, public LayerComposite {
+ template <class ContainerT>
+ friend void ContainerPrepare(ContainerT* aContainer,
+ LayerManagerComposite* aManager,
+ const RenderTargetIntRect& aClipRect);
+ template <class ContainerT>
+ friend void ContainerRender(ContainerT* aContainer,
+ LayerManagerComposite* aManager,
+ const RenderTargetIntRect& aClipRect,
+ const Maybe<gfx::Polygon>& aGeometry);
+ template <class ContainerT>
+ friend void RenderLayers(ContainerT* aContainer,
+ LayerManagerComposite* aManager,
+ const RenderTargetIntRect& aClipRect,
+ const Maybe<gfx::Polygon>& aGeometry);
+ template <class ContainerT>
+ friend void RenderIntermediate(ContainerT* aContainer,
+ LayerManagerComposite* aManager,
+ const gfx::IntRect& aClipRect,
+ RefPtr<CompositingRenderTarget> surface);
+ template <class ContainerT>
+ friend RefPtr<CompositingRenderTarget>
+ CreateTemporaryTargetAndCopyFromBackground(
+ ContainerT* aContainer, LayerManagerComposite* aManager,
+ const RenderTargetIntRect& aClipRect);
+ template <class ContainerT>
+ friend RefPtr<CompositingRenderTarget> CreateOrRecycleTarget(
+ ContainerT* aContainer, LayerManagerComposite* aManager,
+ const RenderTargetIntRect& aClipRect);
+
+ template <class ContainerT>
+ void RenderMinimap(ContainerT* aContainer, const RefPtr<APZSampler>& aSampler,
+ LayerManagerComposite* aManager,
+ const RenderTargetIntRect& aClipRect, Layer* aLayer);
+
+ public:
+ explicit ContainerLayerComposite(LayerManagerComposite* aManager);
+
+ protected:
+ virtual ~ContainerLayerComposite();
+
+ public:
+ // LayerComposite Implementation
+ Layer* GetLayer() override { return this; }
+
+ void SetLayerManager(HostLayerManager* aManager) override {
+ LayerComposite::SetLayerManager(aManager);
+ mManager = aManager;
+ mLastIntermediateSurface = nullptr;
+ }
+
+ void Destroy() override;
+
+ LayerComposite* GetFirstChildComposite() override;
+
+ void Cleanup() override;
+
+ void RenderLayer(const gfx::IntRect& aClipRect,
+ const Maybe<gfx::Polygon>& aGeometry) override;
+
+ void Prepare(const RenderTargetIntRect& aClipRect) override;
+
+ void ComputeEffectiveTransforms(
+ const gfx::Matrix4x4& aTransformToSurface) override {
+ DefaultComputeEffectiveTransforms(aTransformToSurface);
+ }
+
+ const LayerIntRegion& GetShadowVisibleRegion() override;
+
+ void CleanupResources() override;
+
+ HostLayer* AsHostLayer() override { return this; }
+
+ // container layers don't use a compositable
+ CompositableHost* GetCompositableHost() override { return nullptr; }
+
+ // If the layer has a pres shell resolution, add a post-scale
+ // to the layer's transform equal to the pres shell resolution we're
+ // scaling to. This cancels out the post scale of '1 / resolution'
+ // added by Layout. TODO: It would be nice to get rid of both of these
+ // post-scales.
+ float GetPostXScale() const override {
+ return mSimpleAttrs.GetPostXScale() * mPresShellResolution;
+ }
+ float GetPostYScale() const override {
+ return mSimpleAttrs.GetPostYScale() * mPresShellResolution;
+ }
+
+ const char* Name() const override { return "ContainerLayerComposite"; }
+ UniquePtr<PreparedData> mPrepared;
+
+ RefPtr<CompositingRenderTarget> mLastIntermediateSurface;
+};
+
+class RefLayerComposite : public RefLayer, public LayerComposite {
+ template <class ContainerT>
+ friend void ContainerPrepare(ContainerT* aContainer,
+ LayerManagerComposite* aManager,
+ const RenderTargetIntRect& aClipRect);
+ template <class ContainerT>
+ friend void ContainerRender(ContainerT* aContainer,
+ LayerManagerComposite* aManager,
+ const gfx::IntRect& aClipRect,
+ const Maybe<gfx::Polygon>& aGeometry);
+ template <class ContainerT>
+ friend void RenderLayers(ContainerT* aContainer,
+ LayerManagerComposite* aManager,
+ const gfx::IntRect& aClipRect,
+ const Maybe<gfx::Polygon>& aGeometry);
+ template <class ContainerT>
+ friend void RenderIntermediate(ContainerT* aContainer,
+ LayerManagerComposite* aManager,
+ const gfx::IntRect& aClipRect,
+ RefPtr<CompositingRenderTarget> surface);
+ template <class ContainerT>
+ friend RefPtr<CompositingRenderTarget>
+ CreateTemporaryTargetAndCopyFromBackground(ContainerT* aContainer,
+ LayerManagerComposite* aManager,
+ const gfx::IntRect& aClipRect);
+ template <class ContainerT>
+ friend RefPtr<CompositingRenderTarget> CreateTemporaryTarget(
+ ContainerT* aContainer, LayerManagerComposite* aManager,
+ const gfx::IntRect& aClipRect);
+
+ public:
+ explicit RefLayerComposite(LayerManagerComposite* aManager);
+
+ protected:
+ virtual ~RefLayerComposite();
+
+ public:
+ /** LayerOGL implementation */
+ Layer* GetLayer() override { return this; }
+
+ void SetLayerManager(HostLayerManager* aManager) override {
+ LayerComposite::SetLayerManager(aManager);
+ mManager = aManager;
+ mLastIntermediateSurface = nullptr;
+ }
+
+ void Destroy() override;
+
+ LayerComposite* GetFirstChildComposite() override;
+
+ void RenderLayer(const gfx::IntRect& aClipRect,
+ const Maybe<gfx::Polygon>& aGeometry) override;
+
+ void Prepare(const RenderTargetIntRect& aClipRect) override;
+
+ void ComputeEffectiveTransforms(
+ const gfx::Matrix4x4& aTransformToSurface) override {
+ DefaultComputeEffectiveTransforms(aTransformToSurface);
+ }
+
+ const LayerIntRegion& GetShadowVisibleRegion() override;
+
+ void Cleanup() override;
+
+ void CleanupResources() override;
+
+ HostLayer* AsHostLayer() override { return this; }
+
+ // ref layers don't use a compositable
+ CompositableHost* GetCompositableHost() override { return nullptr; }
+
+ const char* Name() const override { return "RefLayerComposite"; }
+ UniquePtr<PreparedData> mPrepared;
+ RefPtr<CompositingRenderTarget> mLastIntermediateSurface;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* GFX_ContainerLayerComposite_H */
diff --git a/gfx/layers/composite/ContentHost.cpp b/gfx/layers/composite/ContentHost.cpp
new file mode 100644
index 0000000000..d4be5b0dc7
--- /dev/null
+++ b/gfx/layers/composite/ContentHost.cpp
@@ -0,0 +1,460 @@
+/* -*- 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 "mozilla/layers/ContentHost.h"
+#include "gfx2DGlue.h" // for ContentForFormat
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/gfx/BaseRect.h" // for BaseRect
+#include "mozilla/layers/Compositor.h" // for Compositor
+#include "mozilla/layers/Effects.h" // for TexturedEffect, Effect, etc
+#include "mozilla/layers/LayersMessages.h" // for ThebesBufferData
+#include "nsAString.h"
+#include "nsPrintfCString.h" // for nsPrintfCString
+#include "nsString.h" // for nsAutoCString
+#include "mozilla/layers/TextureHostOGL.h" // for TextureHostOGL
+
+namespace mozilla {
+using namespace gfx;
+
+namespace layers {
+
+ContentHostBase::ContentHostBase(const TextureInfo& aTextureInfo)
+ : ContentHost(aTextureInfo), mInitialised(false) {}
+
+ContentHostBase::~ContentHostBase() = default;
+
+void ContentHostTexture::Composite(
+ Compositor* aCompositor, LayerComposite* aLayer, EffectChain& aEffectChain,
+ float aOpacity, const gfx::Matrix4x4& aTransform,
+ const SamplingFilter aSamplingFilter, const IntRect& aClipRect,
+ const nsIntRegion* aVisibleRegion, const Maybe<gfx::Polygon>& aGeometry) {
+ NS_ASSERTION(aVisibleRegion, "Requires a visible region");
+
+ AutoLockCompositableHost lock(this);
+ if (lock.Failed()) {
+ return;
+ }
+
+ if (!mTextureHost->BindTextureSource(mTextureSource)) {
+ return;
+ }
+ MOZ_ASSERT(mTextureSource.get());
+
+ if (!mTextureHostOnWhite) {
+ mTextureSourceOnWhite = nullptr;
+ }
+ if (mTextureHostOnWhite &&
+ !mTextureHostOnWhite->BindTextureSource(mTextureSourceOnWhite)) {
+ return;
+ }
+
+ RefPtr<TexturedEffect> effect = CreateTexturedEffect(
+ mTextureSource.get(), mTextureSourceOnWhite.get(), aSamplingFilter, true);
+ if (!effect) {
+ return;
+ }
+
+ aEffectChain.mPrimaryEffect = effect;
+
+ nsIntRegion tmpRegion;
+ const nsIntRegion* renderRegion;
+#ifndef MOZ_IGNORE_PAINT_WILL_RESAMPLE
+ if (PaintWillResample()) {
+ // If we're resampling, then the texture image will contain exactly the
+ // entire visible region's bounds, and we should draw it all in one quad
+ // to avoid unexpected aliasing.
+ tmpRegion = aVisibleRegion->GetBounds();
+ renderRegion = &tmpRegion;
+ } else {
+ renderRegion = aVisibleRegion;
+ }
+#else
+ renderRegion = aVisibleRegion;
+#endif
+
+ nsIntRegion region(*renderRegion);
+ nsIntPoint origin = GetOriginOffset();
+ // translate into TexImage space, buffer origin might not be at texture (0,0)
+ region.MoveBy(-origin);
+
+ // Figure out the intersecting draw region
+ gfx::IntSize texSize = mTextureSource->GetSize();
+ IntRect textureRect = IntRect(0, 0, texSize.width, texSize.height);
+ textureRect.MoveBy(region.GetBounds().TopLeft());
+ nsIntRegion subregion;
+ subregion.And(region, textureRect);
+ if (subregion.IsEmpty()) {
+ // Region is empty, nothing to draw
+ return;
+ }
+
+ nsIntRegion screenRects;
+ nsIntRegion regionRects;
+
+ // Collect texture/screen coordinates for drawing
+ for (auto iter = subregion.RectIter(); !iter.Done(); iter.Next()) {
+ IntRect regionRect = iter.Get();
+ IntRect screenRect = iter.Get();
+ screenRect.MoveBy(origin);
+
+ screenRects.Or(screenRects, screenRect);
+ regionRects.Or(regionRects, regionRect);
+ }
+
+ BigImageIterator* bigImgIter = mTextureSource->AsBigImageIterator();
+ BigImageIterator* iterOnWhite = nullptr;
+ if (bigImgIter) {
+ bigImgIter->BeginBigImageIteration();
+ }
+
+ if (mTextureSourceOnWhite) {
+ iterOnWhite = mTextureSourceOnWhite->AsBigImageIterator();
+ MOZ_ASSERT(!bigImgIter ||
+ bigImgIter->GetTileCount() == iterOnWhite->GetTileCount(),
+ "Tile count mismatch on component alpha texture");
+ if (iterOnWhite) {
+ iterOnWhite->BeginBigImageIteration();
+ }
+ }
+
+ bool usingTiles = (bigImgIter && bigImgIter->GetTileCount() > 1);
+ do {
+ if (iterOnWhite && bigImgIter) {
+ MOZ_ASSERT(iterOnWhite->GetTileRect() == bigImgIter->GetTileRect(),
+ "component alpha textures should be the same size.");
+ }
+
+ IntRect texRect = bigImgIter ? bigImgIter->GetTileRect()
+ : IntRect(0, 0, texSize.width, texSize.height);
+
+ // Draw texture. If we're using tiles, we do repeating manually, as texture
+ // repeat would cause each individual tile to repeat instead of the
+ // compound texture as a whole. This involves drawing at most 4 sections,
+ // 2 for each axis that has texture repeat.
+ for (int y = 0; y < (usingTiles ? 2 : 1); y++) {
+ for (int x = 0; x < (usingTiles ? 2 : 1); x++) {
+ IntRect currentTileRect(texRect);
+ currentTileRect.MoveBy(x * texSize.width, y * texSize.height);
+
+ for (auto screenIter = screenRects.RectIter(),
+ regionIter = regionRects.RectIter();
+ !screenIter.Done() && !regionIter.Done();
+ screenIter.Next(), regionIter.Next()) {
+ const IntRect& screenRect = screenIter.Get();
+ const IntRect& regionRect = regionIter.Get();
+ IntRect tileScreenRect(screenRect);
+ IntRect tileRegionRect(regionRect);
+
+ // When we're using tiles, find the intersection between the tile
+ // rect and this region rect. Tiling is then handled by the
+ // outer for-loops and modifying the tile rect.
+ if (usingTiles) {
+ tileScreenRect.MoveBy(-origin);
+ tileScreenRect = tileScreenRect.Intersect(currentTileRect);
+ tileScreenRect.MoveBy(origin);
+
+ if (tileScreenRect.IsEmpty()) continue;
+
+ tileRegionRect = regionRect.Intersect(currentTileRect);
+ tileRegionRect.MoveBy(-currentTileRect.TopLeft());
+ }
+ gfx::Rect rect(tileScreenRect.X(), tileScreenRect.Y(),
+ tileScreenRect.Width(), tileScreenRect.Height());
+
+ effect->mTextureCoords =
+ Rect(Float(tileRegionRect.X()) / texRect.Width(),
+ Float(tileRegionRect.Y()) / texRect.Height(),
+ Float(tileRegionRect.Width()) / texRect.Width(),
+ Float(tileRegionRect.Height()) / texRect.Height());
+
+ aCompositor->DrawGeometry(rect, aClipRect, aEffectChain, aOpacity,
+ aTransform, aGeometry);
+
+ if (usingTiles) {
+ DiagnosticFlags diagnostics =
+ DiagnosticFlags::CONTENT | DiagnosticFlags::BIGIMAGE;
+ if (iterOnWhite) {
+ diagnostics |= DiagnosticFlags::COMPONENT_ALPHA;
+ }
+ aCompositor->DrawDiagnostics(diagnostics, rect, aClipRect,
+ aTransform, mFlashCounter);
+ }
+ }
+ }
+ }
+
+ if (iterOnWhite) {
+ iterOnWhite->NextTile();
+ }
+ } while (usingTiles && bigImgIter->NextTile());
+
+ if (bigImgIter) {
+ bigImgIter->EndBigImageIteration();
+ }
+ if (iterOnWhite) {
+ iterOnWhite->EndBigImageIteration();
+ }
+
+ DiagnosticFlags diagnostics = DiagnosticFlags::CONTENT;
+ if (iterOnWhite) {
+ diagnostics |= DiagnosticFlags::COMPONENT_ALPHA;
+ }
+ aCompositor->DrawDiagnostics(diagnostics, nsIntRegion(mBufferRect), aClipRect,
+ aTransform, mFlashCounter);
+}
+
+RefPtr<TextureSource> ContentHostTexture::AcquireTextureSource() {
+ if (!mTextureHost || !mTextureHost->AcquireTextureSource(mTextureSource)) {
+ return nullptr;
+ }
+ return mTextureSource.get();
+}
+
+RefPtr<TextureSource> ContentHostTexture::AcquireTextureSourceOnWhite() {
+ if (!mTextureHostOnWhite ||
+ !mTextureHostOnWhite->AcquireTextureSource(mTextureSourceOnWhite)) {
+ return nullptr;
+ }
+ return mTextureSourceOnWhite.get();
+}
+
+void ContentHostTexture::UseTextureHost(
+ const nsTArray<TimedTexture>& aTextures) {
+ ContentHostBase::UseTextureHost(aTextures);
+ MOZ_ASSERT(aTextures.Length() == 1);
+ const TimedTexture& t = aTextures[0];
+ MOZ_ASSERT(t.mPictureRect.IsEqualInterior(
+ nsIntRect(nsIntPoint(0, 0), nsIntSize(t.mTexture->GetSize()))),
+ "Only default picture rect supported");
+
+ if (t.mTexture != mTextureHost) {
+ mReceivedNewHost = true;
+ }
+
+ mTextureHost = t.mTexture;
+ mTextureHostOnWhite = nullptr;
+ mTextureSourceOnWhite = nullptr;
+ if (mTextureHost) {
+ mTextureHost->PrepareTextureSource(mTextureSource);
+ }
+}
+
+void ContentHostTexture::UseComponentAlphaTextures(
+ TextureHost* aTextureOnBlack, TextureHost* aTextureOnWhite) {
+ ContentHostBase::UseComponentAlphaTextures(aTextureOnBlack, aTextureOnWhite);
+ mTextureHost = aTextureOnBlack;
+ mTextureHostOnWhite = aTextureOnWhite;
+ if (mTextureHost) {
+ mTextureHost->PrepareTextureSource(mTextureSource);
+ }
+ if (mTextureHostOnWhite) {
+ mTextureHostOnWhite->PrepareTextureSource(mTextureSourceOnWhite);
+ }
+}
+
+void ContentHostTexture::SetTextureSourceProvider(
+ TextureSourceProvider* aProvider) {
+ ContentHostBase::SetTextureSourceProvider(aProvider);
+ if (mTextureHost) {
+ mTextureHost->SetTextureSourceProvider(aProvider);
+ }
+ if (mTextureHostOnWhite) {
+ mTextureHostOnWhite->SetTextureSourceProvider(aProvider);
+ }
+}
+
+void ContentHostTexture::Dump(std::stringstream& aStream, const char* aPrefix,
+ bool aDumpHtml) {
+#ifdef MOZ_DUMP_PAINTING
+ if (aDumpHtml) {
+ aStream << "<ul>";
+ }
+ if (mTextureHost) {
+ aStream << aPrefix;
+ if (aDumpHtml) {
+ aStream << "<li> <a href=";
+ } else {
+ aStream << "Front buffer: ";
+ }
+ DumpTextureHost(aStream, mTextureHost);
+ if (aDumpHtml) {
+ aStream << "> Front buffer </a></li> ";
+ } else {
+ aStream << "\n";
+ }
+ }
+ if (mTextureHostOnWhite) {
+ aStream << aPrefix;
+ if (aDumpHtml) {
+ aStream << "<li> <a href=";
+ } else {
+ aStream << "Front buffer on white: ";
+ }
+ DumpTextureHost(aStream, mTextureHostOnWhite);
+ if (aDumpHtml) {
+ aStream << "> Front buffer on white </a> </li> ";
+ } else {
+ aStream << "\n";
+ }
+ }
+ if (aDumpHtml) {
+ aStream << "</ul>";
+ }
+#endif
+}
+
+static inline void AddWrappedRegion(const nsIntRegion& aInput,
+ nsIntRegion& aOutput, const IntSize& aSize,
+ const nsIntPoint& aShift) {
+ nsIntRegion tempRegion;
+ tempRegion.And(IntRect(aShift, aSize), aInput);
+ tempRegion.MoveBy(-aShift);
+ aOutput.Or(aOutput, tempRegion);
+}
+
+bool ContentHostSingleBuffered::UpdateThebes(
+ const ThebesBufferData& aData, const nsIntRegion& aUpdated,
+ const nsIntRegion& aOldValidRegionBack) {
+ if (!mTextureHost) {
+ mInitialised = false;
+ return true; // FIXME should we return false? Returning true for now
+ } // to preserve existing behavior of NOT causing IPC errors.
+
+ // updated is in screen coordinates. Convert it to buffer coordinates.
+ nsIntRegion destRegion(aUpdated);
+
+ if (mReceivedNewHost) {
+ destRegion.Or(destRegion, aOldValidRegionBack);
+ mReceivedNewHost = false;
+ }
+ destRegion.MoveBy(-aData.rect().TopLeft());
+
+ if (!aData.rect().Contains(aUpdated.GetBounds()) ||
+ aData.rotation().x > aData.rect().Width() ||
+ aData.rotation().y > aData.rect().Height()) {
+ NS_ERROR("Invalid update data");
+ return false;
+ }
+
+ // destRegion is now in logical coordinates relative to the buffer, but we
+ // need to account for rotation. We do that by moving the region to the
+ // rotation offset and then wrapping any pixels that extend off the
+ // bottom/right edges.
+
+ // Shift to the rotation point
+ destRegion.MoveBy(aData.rotation());
+
+ IntSize bufferSize = aData.rect().Size();
+
+ // Select only the pixels that are still within the buffer.
+ nsIntRegion finalRegion;
+ finalRegion.And(IntRect(IntPoint(), bufferSize), destRegion);
+
+ // For each of the overlap areas (right, bottom-right, bottom), select those
+ // pixels and wrap them around to the opposite edge of the buffer rect.
+ AddWrappedRegion(destRegion, finalRegion, bufferSize,
+ nsIntPoint(aData.rect().Width(), 0));
+ AddWrappedRegion(destRegion, finalRegion, bufferSize,
+ nsIntPoint(aData.rect().Width(), aData.rect().Height()));
+ AddWrappedRegion(destRegion, finalRegion, bufferSize,
+ nsIntPoint(0, aData.rect().Height()));
+
+ MOZ_ASSERT(IntRect(0, 0, aData.rect().Width(), aData.rect().Height())
+ .Contains(finalRegion.GetBounds()));
+
+ mTextureHost->Updated(&finalRegion);
+ if (mTextureHostOnWhite) {
+ mTextureHostOnWhite->Updated(&finalRegion);
+ }
+ mInitialised = true;
+
+ mBufferRect = aData.rect();
+ mBufferRotation = aData.rotation();
+
+ return true;
+}
+
+bool ContentHostDoubleBuffered::UpdateThebes(
+ const ThebesBufferData& aData, const nsIntRegion& aUpdated,
+ const nsIntRegion& aOldValidRegionBack) {
+ if (!mTextureHost) {
+ mInitialised = false;
+ return true;
+ }
+
+ // We don't need to calculate an update region because we assume that if we
+ // are using double buffering then we have render-to-texture and thus no
+ // upload to do.
+ mTextureHost->Updated();
+ if (mTextureHostOnWhite) {
+ mTextureHostOnWhite->Updated();
+ }
+ mInitialised = true;
+
+ mBufferRect = aData.rect();
+ mBufferRotation = aData.rotation();
+
+ // Save the current valid region of our front buffer, because if
+ // we're double buffering, it's going to be the valid region for the
+ // next back buffer sent back to the renderer.
+ //
+ // NB: we rely here on the fact that mValidRegion is initialized to
+ // empty, and that the first time Swap() is called we don't have a
+ // valid front buffer that we're going to return to content.
+ mValidRegionForNextBackBuffer = aOldValidRegionBack;
+
+ return true;
+}
+
+void ContentHostTexture::PrintInfo(std::stringstream& aStream,
+ const char* aPrefix) {
+ aStream << aPrefix;
+ aStream << nsPrintfCString("ContentHost (0x%p)", this).get()
+ << " [buffer-rect=" << mBufferRect << "]"
+ << " [buffer-rotation=" << mBufferRotation << "]";
+ if (PaintWillResample()) {
+ aStream << " [paint-will-resample]";
+ }
+
+ if (mTextureHost) {
+ nsAutoCString pfx(aPrefix);
+ pfx += " ";
+
+ aStream << "\n";
+ mTextureHost->PrintInfo(aStream, pfx.get());
+ }
+}
+
+already_AddRefed<TexturedEffect> ContentHostTexture::GenEffect(
+ const gfx::SamplingFilter aSamplingFilter) {
+ if (!mTextureHost) {
+ return nullptr;
+ }
+ if (!mTextureHost->BindTextureSource(mTextureSource)) {
+ return nullptr;
+ }
+ if (!mTextureHostOnWhite) {
+ mTextureSourceOnWhite = nullptr;
+ }
+ if (mTextureHostOnWhite &&
+ !mTextureHostOnWhite->BindTextureSource(mTextureSourceOnWhite)) {
+ return nullptr;
+ }
+ return CreateTexturedEffect(mTextureSource.get(), mTextureSourceOnWhite.get(),
+ aSamplingFilter, true);
+}
+
+already_AddRefed<gfx::DataSourceSurface> ContentHostTexture::GetAsSurface() {
+ if (!mTextureHost) {
+ return nullptr;
+ }
+
+ return mTextureHost->GetAsSurface();
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/composite/ContentHost.h b/gfx/layers/composite/ContentHost.h
new file mode 100644
index 0000000000..6833ebd74f
--- /dev/null
+++ b/gfx/layers/composite/ContentHost.h
@@ -0,0 +1,236 @@
+/* -*- 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/. */
+
+#ifndef GFX_CONTENTHOST_H
+#define GFX_CONTENTHOST_H
+
+#include <stdint.h> // for uint32_t
+#include <stdio.h> // for FILE
+#include "mozilla-config.h" // for MOZ_DUMP_PAINTING
+#include "CompositableHost.h" // for CompositableHost, etc
+#include "RotatedBuffer.h" // for RotatedBuffer, etc
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/RefPtr.h" // for RefPtr
+#include "mozilla/gfx/BasePoint.h" // for BasePoint
+#include "mozilla/gfx/MatrixFwd.h" // for Matrix4x4
+#include "mozilla/gfx/Point.h" // for Point
+#include "mozilla/gfx/Polygon.h" // for Polygon
+#include "mozilla/gfx/Rect.h" // for Rect
+#include "mozilla/gfx/Types.h" // for SamplingFilter
+#include "mozilla/layers/CompositorTypes.h" // for TextureInfo, etc
+#include "mozilla/layers/ContentClient.h" // for ContentClient
+#include "mozilla/layers/ISurfaceAllocator.h" // for ISurfaceAllocator
+#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor
+#include "mozilla/layers/LayersTypes.h" // for etc
+#include "mozilla/layers/TextureHost.h" // for TextureHost
+#include "mozilla/mozalloc.h" // for operator delete
+#include "mozilla/UniquePtr.h" // for UniquePtr
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
+#include "nsPoint.h" // for nsIntPoint
+#include "nsRect.h" // for mozilla::gfx::IntRect
+#include "nsRegion.h" // for nsIntRegion
+#include "nsTArray.h" // for nsTArray
+#include "nscore.h" // for nsACString
+
+namespace mozilla {
+namespace layers {
+class Compositor;
+class ThebesBufferData;
+struct EffectChain;
+
+struct TexturedEffect;
+
+/**
+ * ContentHosts are used for compositing Painted layers, always matched by a
+ * ContentClient of the same type.
+ *
+ * ContentHosts support only UpdateThebes(), not Update().
+ */
+class ContentHost : public CompositableHost {
+ public:
+ virtual bool UpdateThebes(
+ const ThebesBufferData& aData, const nsIntRegion& aUpdated,
+ const nsIntRegion& aOldValidRegionBack) override = 0;
+
+ virtual void SetPaintWillResample(bool aResample) {
+ mPaintWillResample = aResample;
+ }
+ bool PaintWillResample() { return mPaintWillResample; }
+
+ // We use this to allow TiledContentHost to invalidate regions where
+ // tiles are fading in.
+ virtual void AddAnimationInvalidation(nsIntRegion& aRegion) {}
+
+ virtual gfx::IntRect GetBufferRect() {
+ MOZ_ASSERT_UNREACHABLE("Must be implemented in derived class");
+ return gfx::IntRect();
+ }
+
+ ContentHost* AsContentHost() override { return this; }
+
+ protected:
+ explicit ContentHost(const TextureInfo& aTextureInfo)
+ : CompositableHost(aTextureInfo), mPaintWillResample(false) {}
+
+ bool mPaintWillResample;
+};
+
+/**
+ * Base class for non-tiled ContentHosts.
+ *
+ * Ownership of the SurfaceDescriptor and the resources it represents is passed
+ * from the ContentClient to the ContentHost when the TextureClient/Hosts are
+ * created, that is recevied here by SetTextureHosts which assigns one or two
+ * texture hosts (for single and double buffering) to the ContentHost.
+ *
+ * It is the responsibility of the ContentHost to destroy its resources when
+ * they are recreated or the ContentHost dies.
+ */
+class ContentHostBase : public ContentHost {
+ public:
+ typedef ContentClient::ContentType ContentType;
+ typedef ContentClient::PaintState PaintState;
+
+ explicit ContentHostBase(const TextureInfo& aTextureInfo);
+ virtual ~ContentHostBase();
+
+ gfx::IntRect GetBufferRect() override { return mBufferRect; }
+
+ virtual nsIntPoint GetOriginOffset() {
+ return mBufferRect.TopLeft() - mBufferRotation;
+ }
+
+ gfx::IntPoint GetBufferRotation() { return mBufferRotation.ToUnknownPoint(); }
+
+ protected:
+ gfx::IntRect mBufferRect;
+ nsIntPoint mBufferRotation;
+ bool mInitialised;
+};
+
+/**
+ * Shared ContentHostBase implementation for content hosts that
+ * use up to two TextureHosts.
+ */
+class ContentHostTexture : public ContentHostBase {
+ public:
+ explicit ContentHostTexture(const TextureInfo& aTextureInfo)
+ : ContentHostBase(aTextureInfo),
+ mLocked(false),
+ mReceivedNewHost(false) {}
+
+ void Composite(Compositor* aCompositor, LayerComposite* aLayer,
+ EffectChain& aEffectChain, float aOpacity,
+ const gfx::Matrix4x4& aTransform,
+ const gfx::SamplingFilter aSamplingFilter,
+ const gfx::IntRect& aClipRect,
+ const nsIntRegion* aVisibleRegion = nullptr,
+ const Maybe<gfx::Polygon>& aGeometry = Nothing()) override;
+
+ void SetTextureSourceProvider(TextureSourceProvider* aProvider) override;
+
+ already_AddRefed<gfx::DataSourceSurface> GetAsSurface() override;
+
+ void Dump(std::stringstream& aStream, const char* aPrefix = "",
+ bool aDumpHtml = false) override;
+
+ void PrintInfo(std::stringstream& aStream, const char* aPrefix) override;
+
+ void UseTextureHost(const nsTArray<TimedTexture>& aTextures) override;
+ void UseComponentAlphaTextures(TextureHost* aTextureOnBlack,
+ TextureHost* aTextureOnWhite) override;
+
+ bool Lock() override {
+ MOZ_ASSERT(!mLocked);
+ if (!mTextureHost) {
+ return false;
+ }
+ if (!mTextureHost->Lock()) {
+ return false;
+ }
+
+ if (mTextureHostOnWhite && !mTextureHostOnWhite->Lock()) {
+ return false;
+ }
+
+ mLocked = true;
+ return true;
+ }
+ void Unlock() override {
+ MOZ_ASSERT(mLocked);
+ mTextureHost->Unlock();
+ if (mTextureHostOnWhite) {
+ mTextureHostOnWhite->Unlock();
+ }
+ mLocked = false;
+ }
+
+ bool HasComponentAlpha() const { return !!mTextureHostOnWhite; }
+
+ RefPtr<TextureSource> AcquireTextureSource();
+ RefPtr<TextureSource> AcquireTextureSourceOnWhite();
+
+ ContentHostTexture* AsContentHostTexture() override { return this; }
+
+ already_AddRefed<TexturedEffect> GenEffect(
+ const gfx::SamplingFilter aSamplingFilter) override;
+
+ protected:
+ CompositableTextureHostRef mTextureHost;
+ CompositableTextureHostRef mTextureHostOnWhite;
+ CompositableTextureSourceRef mTextureSource;
+ CompositableTextureSourceRef mTextureSourceOnWhite;
+ bool mLocked;
+ bool mReceivedNewHost;
+};
+
+/**
+ * Double buffering is implemented by swapping the front and back TextureHosts.
+ * We assume that whenever we use double buffering, then we have
+ * render-to-texture and thus no texture upload to do.
+ */
+class ContentHostDoubleBuffered : public ContentHostTexture {
+ public:
+ explicit ContentHostDoubleBuffered(const TextureInfo& aTextureInfo)
+ : ContentHostTexture(aTextureInfo) {}
+
+ virtual ~ContentHostDoubleBuffered() = default;
+
+ CompositableType GetType() override {
+ return CompositableType::CONTENT_DOUBLE;
+ }
+
+ virtual bool UpdateThebes(const ThebesBufferData& aData,
+ const nsIntRegion& aUpdated,
+ const nsIntRegion& aOldValidRegionBack) override;
+
+ protected:
+ nsIntRegion mValidRegionForNextBackBuffer;
+};
+
+/**
+ * Single buffered, therefore we must synchronously upload the image from the
+ * TextureHost in the layers transaction (i.e., in UpdateThebes).
+ */
+class ContentHostSingleBuffered : public ContentHostTexture {
+ public:
+ explicit ContentHostSingleBuffered(const TextureInfo& aTextureInfo)
+ : ContentHostTexture(aTextureInfo) {}
+ virtual ~ContentHostSingleBuffered() = default;
+
+ CompositableType GetType() override {
+ return CompositableType::CONTENT_SINGLE;
+ }
+
+ bool UpdateThebes(const ThebesBufferData& aData, const nsIntRegion& aUpdated,
+ const nsIntRegion& aOldValidRegionBack) override;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/composite/Diagnostics.cpp b/gfx/layers/composite/Diagnostics.cpp
new file mode 100644
index 0000000000..0be1f978c8
--- /dev/null
+++ b/gfx/layers/composite/Diagnostics.cpp
@@ -0,0 +1,96 @@
+/* -*- 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 "Diagnostics.h"
+#include "mozilla/layers/LayersMessages.h"
+#include "nsPrintfCString.h"
+
+namespace mozilla {
+namespace layers {
+
+float TimedMetric::Average() const {
+ // We take at most 2 seconds of history.
+ TimeStamp latest = TimeStamp::Now();
+ float total = 0.0f;
+ size_t count = 0;
+ for (auto iter = mHistory.rbegin(); iter != mHistory.rend(); iter++) {
+ if ((latest - iter->second).ToSeconds() > 2.0f) {
+ break;
+ }
+ total += iter->first;
+ count++;
+ }
+
+ if (!count) {
+ return 0.0f;
+ }
+ return total / float(count);
+}
+
+Diagnostics::Diagnostics()
+ : mCompositeFps("Compositor"), mTransactionFps("LayerTransactions") {}
+
+void Diagnostics::RecordPaintTimes(const PaintTiming& aPaintTimes) {
+ mDlbMs.Add(aPaintTimes.dlMs());
+ mDlb2Ms.Add(aPaintTimes.dl2Ms());
+ mFlbMs.Add(aPaintTimes.flbMs());
+ mRasterMs.Add(aPaintTimes.rasterMs());
+ mSerializeMs.Add(aPaintTimes.serializeMs());
+ mSendMs.Add(aPaintTimes.sendMs());
+}
+
+std::string Diagnostics::GetFrameOverlayString(const GPUStats& aStats) {
+ TimeStamp now = TimeStamp::Now();
+ unsigned fps = unsigned(mCompositeFps.AddFrameAndGetFps(now));
+ unsigned txnFps = unsigned(mTransactionFps.GetFPS(now));
+
+ float pixelFillRatio =
+ aStats.mInvalidPixels
+ ? float(aStats.mPixelsFilled) / float(aStats.mInvalidPixels)
+ : 0.0f;
+ float screenFillRatio = aStats.mScreenPixels ? float(aStats.mPixelsFilled) /
+ float(aStats.mScreenPixels)
+ : 0.0f;
+
+ if (aStats.mDrawTime) {
+ mGPUDrawMs.Add(aStats.mDrawTime.value());
+ }
+
+ std::string gpuTimeString;
+ if (mGPUDrawMs.Empty()) {
+ gpuTimeString = "N/A";
+ } else {
+ gpuTimeString = nsPrintfCString("%0.1fms", mGPUDrawMs.Average()).get();
+ }
+
+ // DL = nsDisplayListBuilder, p = partial, f = full
+ // FLB = FrameLayerBuilder
+ // R = ClientLayerManager::EndTransaction
+ // CP = ShadowLayerForwarder::EndTransaction (txn build)
+ // TX = LayerTransactionChild::SendUpdate (IPDL serialize+send)
+ // UP = LayerTransactionParent::RecvUpdate (IPDL deserialize, update, APZ
+ // update)
+ // CC_BUILD = Container prepare/composite frame building
+ // CC_EXEC = Container render/composite drawing
+ nsPrintfCString line1("FPS: %d (TXN: %d)", fps, txnFps);
+ nsPrintfCString line2(
+ "[CC] Build: %0.1fms Exec: %0.1fms GPU: %s Fill Ratio: %0.1f/%0.1f",
+ mPrepareMs.Average(), mCompositeMs.Average(), gpuTimeString.c_str(),
+ pixelFillRatio, screenFillRatio);
+ nsPrintfCString line3(
+ "[Content] DL p: %0.1f DL f: %0.1fms FLB: %0.1fms Raster: %0.1fms",
+ mDlbMs.Average(), mDlb2Ms.Average(), mFlbMs.Average(),
+ mRasterMs.Average());
+ nsPrintfCString line4("[IPDL] Build: %0.1fms Send: %0.1fms Update: %0.1fms",
+ mSerializeMs.Average(), mSendMs.Average(),
+ mUpdateMs.Average());
+
+ return std::string(line1.get()) + "\n" + std::string(line2.get()) + "\n" +
+ std::string(line3.get()) + "\n" + std::string(line4.get());
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/composite/Diagnostics.h b/gfx/layers/composite/Diagnostics.h
new file mode 100644
index 0000000000..4619ff6aa5
--- /dev/null
+++ b/gfx/layers/composite/Diagnostics.h
@@ -0,0 +1,100 @@
+/* -*- 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/. */
+
+#ifndef mozilla_gfx_layers_composite_Diagnostics_h
+#define mozilla_gfx_layers_composite_Diagnostics_h
+
+#include "FPSCounter.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/StaticPrefs_layers.h"
+#include "mozilla/TimeStamp.h"
+#include <deque>
+#include <string>
+#include <utility>
+
+namespace mozilla {
+namespace layers {
+
+class PaintTiming;
+
+class TimedMetric {
+ typedef std::pair<float, TimeStamp> Entry;
+
+ public:
+ void Add(float aValue) {
+ if (mHistory.size() > kMaxHistory) {
+ mHistory.pop_front();
+ }
+ mHistory.push_back(Entry(aValue, TimeStamp::Now()));
+ }
+
+ float Average() const;
+ bool Empty() const { return mHistory.empty(); }
+
+ private:
+ static const size_t kMaxHistory = 60;
+
+ std::deque<Entry> mHistory;
+};
+
+// These statistics are collected by layers backends, preferably by the GPU
+struct GPUStats {
+ GPUStats() : mInvalidPixels(0), mScreenPixels(0), mPixelsFilled(0) {}
+
+ uint32_t mInvalidPixels;
+ uint32_t mScreenPixels;
+ uint32_t mPixelsFilled;
+ Maybe<float> mDrawTime;
+};
+
+// Collects various diagnostics about layers performance.
+class Diagnostics {
+ public:
+ Diagnostics();
+
+ void RecordPaintTimes(const PaintTiming& aPaintTimes);
+ void RecordUpdateTime(float aValue) { mUpdateMs.Add(aValue); }
+ void RecordPrepareTime(float aValue) { mPrepareMs.Add(aValue); }
+ void RecordCompositeTime(float aValue) { mCompositeMs.Add(aValue); }
+ void AddTxnFrame() { mTransactionFps.AddFrame(TimeStamp::Now()); }
+
+ std::string GetFrameOverlayString(const GPUStats& aStats);
+
+ class Record {
+ public:
+ explicit Record(TimeStamp aStart = TimeStamp()) {
+ if (StaticPrefs::layers_acceleration_draw_fps()) {
+ mStart = aStart.IsNull() ? TimeStamp::Now() : aStart;
+ }
+ }
+ bool Recording() const { return !mStart.IsNull(); }
+ float Duration() const {
+ return (TimeStamp::Now() - mStart).ToMilliseconds();
+ }
+
+ private:
+ TimeStamp mStart;
+ };
+
+ private:
+ FPSCounter mCompositeFps;
+ FPSCounter mTransactionFps;
+ TimedMetric mDlbMs;
+ TimedMetric mDlb2Ms;
+ TimedMetric mFlbMs;
+ TimedMetric mRasterMs;
+ TimedMetric mSerializeMs;
+ TimedMetric mSendMs;
+ TimedMetric mUpdateMs;
+ TimedMetric mPrepareMs;
+ TimedMetric mCompositeMs;
+ TimedMetric mGPUDrawMs;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_gfx_layers_composite_Diagnostics_h
diff --git a/gfx/layers/composite/FPSCounter.cpp b/gfx/layers/composite/FPSCounter.cpp
new file mode 100644
index 0000000000..cf3d0725ed
--- /dev/null
+++ b/gfx/layers/composite/FPSCounter.cpp
@@ -0,0 +1,343 @@
+/* -*- 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 <stddef.h> // for size_t
+#include "Units.h" // for ScreenIntRect
+#include "gfxRect.h" // for gfxRect
+#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/StaticPrefs_layers.h"
+#include "mozilla/TimeStamp.h" // for TimeStamp, TimeDuration
+#include "nsPoint.h" // for nsIntPoint
+#include "nsRect.h" // for mozilla::gfx::IntRect
+#include "nsIFile.h" // for nsIFile
+#include "nsDirectoryServiceDefs.h" // for NS_OS_TMP_DIR
+#include "mozilla/Sprintf.h"
+#include "FPSCounter.h"
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::gfx;
+
+FPSCounter::FPSCounter(const char* aName)
+ : mWriteIndex(0), mIteratorIndex(-1), mFPSName(aName) {
+ Init();
+}
+
+FPSCounter::~FPSCounter() = default;
+
+void FPSCounter::Init() {
+ for (int i = 0; i < kMaxFrames; i++) {
+ mFrameTimestamps.AppendElement(TimeStamp());
+ }
+ mLastInterval = TimeStamp::Now();
+}
+
+// Returns true if we captured a full interval of data
+bool FPSCounter::CapturedFullInterval(TimeStamp aTimestamp) {
+ TimeDuration duration = aTimestamp - mLastInterval;
+ return duration.ToSeconds() >= kFpsDumpInterval;
+}
+
+void FPSCounter::AddFrame(TimeStamp aTimestamp) {
+ NS_ASSERTION(mWriteIndex < kMaxFrames,
+ "We probably have a bug with the circular buffer");
+ NS_ASSERTION(mWriteIndex >= 0,
+ "Circular Buffer index should never be negative");
+
+ int index = mWriteIndex++;
+ if (mWriteIndex == kMaxFrames) {
+ mWriteIndex = 0;
+ }
+
+ mFrameTimestamps[index] = aTimestamp;
+
+ if (CapturedFullInterval(aTimestamp)) {
+ PrintFPS();
+ WriteFrameTimeStamps();
+ mLastInterval = aTimestamp;
+ }
+}
+
+double FPSCounter::AddFrameAndGetFps(TimeStamp aTimestamp) {
+ AddFrame(aTimestamp);
+ return GetFPS(aTimestamp);
+}
+
+int FPSCounter::GetLatestReadIndex() {
+ if (mWriteIndex == 0) {
+ return kMaxFrames - 1;
+ }
+
+ return mWriteIndex - 1;
+}
+
+TimeStamp FPSCounter::GetLatestTimeStamp() {
+ TimeStamp timestamp = mFrameTimestamps[GetLatestReadIndex()];
+ MOZ_ASSERT(!timestamp.IsNull(), "Cannot use null timestamps");
+ return timestamp;
+}
+
+// Returns true if we iterated over a full interval of data
+bool FPSCounter::IteratedFullInterval(TimeStamp aTimestamp, double aDuration) {
+ MOZ_ASSERT(mIteratorIndex >= 0, "Cannot be negative");
+ MOZ_ASSERT(mIteratorIndex < kMaxFrames,
+ "Iterator index cannot be greater than kMaxFrames");
+
+ TimeStamp currentStamp = mFrameTimestamps[mIteratorIndex];
+ TimeDuration duration = aTimestamp - currentStamp;
+ return duration.ToSeconds() >= aDuration;
+}
+
+void FPSCounter::ResetReverseIterator() {
+ mIteratorIndex = GetLatestReadIndex();
+}
+
+/***
+ * Returns true if we have another timestamp that is valid and
+ * is within the given duration that we're interested in.
+ * Duration is in seconds
+ */
+bool FPSCounter::HasNext(TimeStamp aTimestamp, double aDuration) {
+ // Order of evaluation here has to stay the same
+ // otherwise IteratedFullInterval reads from mFrameTimestamps which cannot
+ // be null
+ return (mIteratorIndex != mWriteIndex) // Didn't loop around the buffer
+ && !mFrameTimestamps[mIteratorIndex].IsNull() // valid data
+ && !IteratedFullInterval(aTimestamp, aDuration);
+}
+
+TimeStamp FPSCounter::GetNextTimeStamp() {
+ TimeStamp timestamp = mFrameTimestamps[mIteratorIndex--];
+ MOZ_ASSERT(!timestamp.IsNull(), "Reading Invalid Timestamp Data");
+
+ if (mIteratorIndex == -1) {
+ mIteratorIndex = kMaxFrames - 1;
+ }
+ return timestamp;
+}
+
+/**
+ * GetFPS calculates how many frames we've already composited from the current
+ * frame timestamp and we iterate from the latest timestamp we recorded,
+ * going back in time. When we hit a frame that is longer than the 1 second
+ * from the current composited frame, we return how many frames we've counted.
+ * Just a visualization:
+ *
+ * aTimestamp
+ * Frames: 1 2 3 4 5 6 7 8 9 10 11 12
+ * Time -------------------------->
+ *
+ * GetFPS iterates from aTimestamp, which is the current frame.
+ * Then starting at frame 12, going back to frame 11, 10, etc, we calculate
+ * the duration of the recorded frame timestamp from aTimestamp.
+ * Once duration is greater than 1 second, we return how many frames
+ * we composited.
+ */
+double FPSCounter::GetFPS(TimeStamp aTimestamp) {
+ int frameCount = 0;
+ int duration = 1.0; // Only care about the last 1s of data
+
+ ResetReverseIterator();
+ while (HasNext(aTimestamp, duration)) {
+ GetNextTimeStamp();
+ frameCount++;
+ }
+
+ return frameCount;
+}
+
+// Iterate the same way we do in GetFPS()
+int FPSCounter::BuildHistogram(std::map<int, int>& aFpsData) {
+ TimeStamp currentIntervalStart = GetLatestTimeStamp();
+ TimeStamp currentTimeStamp = GetLatestTimeStamp();
+ TimeStamp startTimeStamp = GetLatestTimeStamp();
+
+ int frameCount = 0;
+ int totalFrameCount = 0;
+
+ ResetReverseIterator();
+ while (HasNext(startTimeStamp)) {
+ currentTimeStamp = GetNextTimeStamp();
+ TimeDuration interval = currentIntervalStart - currentTimeStamp;
+
+ if (interval.ToSeconds() >= 1.0) {
+ currentIntervalStart = currentTimeStamp;
+ aFpsData[frameCount]++;
+ frameCount = 0;
+ }
+
+ frameCount++;
+ totalFrameCount++;
+ }
+
+ TimeDuration totalTime = currentIntervalStart - currentTimeStamp;
+ printf_stderr("Discarded %d frames over %f ms in histogram for %s\n",
+ frameCount, totalTime.ToMilliseconds(), mFPSName);
+ return totalFrameCount;
+}
+
+// Iterate the same way we do in GetFPS()
+void FPSCounter::WriteFrameTimeStamps(PRFileDesc* fd) {
+ const int bufferSize = 256;
+ char buffer[bufferSize];
+ int writtenCount = SprintfLiteral(buffer, "FPS Data for: %s\n", mFPSName);
+ MOZ_ASSERT(writtenCount < bufferSize);
+ if (writtenCount >= bufferSize) {
+ return;
+ }
+ PR_Write(fd, buffer, writtenCount);
+
+ ResetReverseIterator();
+ TimeStamp startTimeStamp = GetLatestTimeStamp();
+
+ MOZ_ASSERT(HasNext(startTimeStamp));
+ TimeStamp previousSample = GetNextTimeStamp();
+
+ MOZ_ASSERT(HasNext(startTimeStamp));
+ TimeStamp nextTimeStamp = GetNextTimeStamp();
+
+ while (HasNext(startTimeStamp)) {
+ TimeDuration duration = previousSample - nextTimeStamp;
+ writtenCount = SprintfLiteral(buffer, "%f,\n", duration.ToMilliseconds());
+ MOZ_ASSERT(writtenCount < bufferSize);
+ if (writtenCount >= bufferSize) {
+ continue;
+ }
+ PR_Write(fd, buffer, writtenCount);
+
+ previousSample = nextTimeStamp;
+ nextTimeStamp = GetNextTimeStamp();
+ }
+}
+
+double FPSCounter::GetMean(std::map<int, int> aHistogram) {
+ double average = 0.0;
+ double samples = 0.0;
+
+ for (std::map<int, int>::iterator iter = aHistogram.begin();
+ iter != aHistogram.end(); ++iter) {
+ int fps = iter->first;
+ int count = iter->second;
+
+ average += fps * count;
+ samples += count;
+ }
+
+ return average / samples;
+}
+
+double FPSCounter::GetStdDev(std::map<int, int> aHistogram) {
+ double sumOfDifferences = 0;
+ double average = GetMean(aHistogram);
+ double samples = 0.0;
+
+ for (std::map<int, int>::iterator iter = aHistogram.begin();
+ iter != aHistogram.end(); ++iter) {
+ int fps = iter->first;
+ int count = iter->second;
+
+ double diff = ((double)fps) - average;
+ diff *= diff;
+
+ for (int i = 0; i < count; i++) {
+ sumOfDifferences += diff;
+ }
+ samples += count;
+ }
+
+ double stdDev = sumOfDifferences / samples;
+ return sqrt(stdDev);
+}
+
+void FPSCounter::PrintFPS() {
+ if (!StaticPrefs::layers_acceleration_draw_fps_print_histogram()) {
+ return;
+ }
+
+ std::map<int, int> histogram;
+ int totalFrames = BuildHistogram(histogram);
+
+ TimeDuration measurementInterval =
+ mFrameTimestamps[GetLatestReadIndex()] - mLastInterval;
+ printf_stderr("FPS for %s. Total Frames: %d Time Interval: %f seconds\n",
+ mFPSName, totalFrames,
+ measurementInterval.ToSecondsSigDigits());
+
+ PrintHistogram(histogram);
+}
+
+void FPSCounter::PrintHistogram(std::map<int, int>& aHistogram) {
+ if (aHistogram.empty()) {
+ return;
+ }
+
+ int length = 0;
+ const int kBufferLength = 512;
+ int availableSpace = kBufferLength;
+ char buffer[kBufferLength];
+
+ for (std::map<int, int>::iterator iter = aHistogram.begin();
+ iter != aHistogram.end(); iter++) {
+ int fps = iter->first;
+ int count = iter->second;
+
+ int lengthRequired =
+ snprintf(buffer + length, availableSpace, "FPS: %d = %d. ", fps, count);
+ // Ran out of buffer space. Oh well - just print what we have.
+ if (lengthRequired > availableSpace) {
+ break;
+ }
+ length += lengthRequired;
+ availableSpace -= lengthRequired;
+ }
+
+ printf_stderr("%s\n", buffer);
+ printf_stderr("Mean: %f , std dev %f\n", GetMean(aHistogram),
+ GetStdDev(aHistogram));
+}
+
+// Write FPS timestamp data to a file only if
+// draw-fps.write-to-file is true
+nsresult FPSCounter::WriteFrameTimeStamps() {
+ if (!StaticPrefs::layers_acceleration_draw_fps_write_to_file()) {
+ return NS_OK;
+ }
+
+ MOZ_ASSERT(mWriteIndex == 0);
+
+ nsCOMPtr<nsIFile> resultFile;
+ nsresult rv =
+ NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(resultFile));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!strncmp(mFPSName, "Compositor", strlen(mFPSName))) {
+ resultFile->Append(u"fps.txt"_ns);
+ } else {
+ resultFile->Append(u"txn.txt"_ns);
+ }
+
+ PRFileDesc* fd = nullptr;
+ int mode = 644;
+ int openFlags = PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE;
+ rv = resultFile->OpenNSPRFileDesc(openFlags, mode, &fd);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ WriteFrameTimeStamps(fd);
+ PR_Close(fd);
+
+ printf_stderr("Wrote FPS data to file: %s\n",
+ resultFile->HumanReadablePath().get());
+ return NS_OK;
+}
+
+} // end namespace layers
+} // end namespace mozilla
diff --git a/gfx/layers/composite/FPSCounter.h b/gfx/layers/composite/FPSCounter.h
new file mode 100644
index 0000000000..c61b13a7b5
--- /dev/null
+++ b/gfx/layers/composite/FPSCounter.h
@@ -0,0 +1,102 @@
+/* -*- 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/. */
+
+#ifndef mozilla_layers_opengl_FPSCounter_h_
+#define mozilla_layers_opengl_FPSCounter_h_
+
+#include <algorithm> // for min
+#include <stddef.h> // for size_t
+#include <map> // for std::map
+#include "GLDefs.h" // for GLuint
+#include "mozilla/RefPtr.h" // for already_AddRefed, RefCounted
+#include "mozilla/TimeStamp.h" // for TimeStamp, TimeDuration
+#include "nsTArray.h" // for AutoTArray, nsTArray_Impl, etc
+#include "prio.h" // for NSPR file i/o
+
+namespace mozilla {
+namespace layers {
+
+class DataTextureSource;
+class Compositor;
+
+// Dump the FPS histogram every 10 seconds or kMaxFrameFPS
+const int kFpsDumpInterval = 10;
+
+// On desktop, we can have 240 hz monitors, so 10 seconds
+// times 240 frames = 2400
+const int kMaxFrames = 2400;
+
+/**
+ * The FPSCounter tracks how often we composite or have a layer transaction.
+ * At each composite / layer transaction, we record the timestamp.
+ * After kFpsDumpInterval number of composites / transactions, we calculate
+ * the average and standard deviation of frames composited. We dump a histogram,
+ * which allows for more statistically significant measurements. We also dump
+ * absolute frame composite times to a file on the device.
+ * The FPS counters displayed on screen are based on how many frames we
+ * composited within the last ~1 second. The more accurate measurement is to
+ * grab the histogram from stderr or grab the FPS timestamp dumps written to
+ * file.
+ *
+ * To enable dumping to file, enable
+ * layers.acceleration.draw-fps.write-to-file pref.
+
+ double AddFrameAndGetFps(TimeStamp aCurrentFrame) {
+ AddFrame(aCurrentFrame);
+ return EstimateFps(aCurrentFrame);
+ }
+ * To enable printing histogram data to logcat,
+ * enable layers.acceleration.draw-fps.print-histogram
+ *
+ * Use the HasNext(), GetNextTimeStamp() like an iterator to read the data,
+ * backwards in time. This abstracts away the mechanics of reading the data.
+ */
+class FPSCounter final {
+ public:
+ explicit FPSCounter(const char* aName);
+ ~FPSCounter();
+
+ void AddFrame(TimeStamp aTimestamp);
+ double AddFrameAndGetFps(TimeStamp aTimestamp);
+ double GetFPS(TimeStamp aTimestamp);
+
+ private:
+ void Init();
+ bool CapturedFullInterval(TimeStamp aTimestamp);
+
+ // Used while iterating backwards over the data
+ void ResetReverseIterator();
+ bool HasNext(TimeStamp aTimestamp, double aDuration = kFpsDumpInterval);
+ TimeStamp GetNextTimeStamp();
+ int GetLatestReadIndex();
+ TimeStamp GetLatestTimeStamp();
+ void WriteFrameTimeStamps(PRFileDesc* fd);
+ bool IteratedFullInterval(TimeStamp aTimestamp, double aDuration);
+
+ void PrintFPS();
+ int BuildHistogram(std::map<int, int>& aHistogram);
+ void PrintHistogram(std::map<int, int>& aHistogram);
+ double GetMean(std::map<int, int> aHistogram);
+ double GetStdDev(std::map<int, int> aHistogram);
+ nsresult WriteFrameTimeStamps();
+
+ /***
+ * mFrameTimestamps is a psuedo circular buffer
+ * Since we have a constant write time and don't
+ * read at an offset except our latest write
+ * we don't need an explicit read pointer.
+ */
+ AutoTArray<TimeStamp, kMaxFrames> mFrameTimestamps;
+ int mWriteIndex; // points to next open write slot
+ int mIteratorIndex; // used only when iterating
+ const char* mFPSName;
+ TimeStamp mLastInterval;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_opengl_FPSCounter_h_
diff --git a/gfx/layers/composite/FontData.h b/gfx/layers/composite/FontData.h
new file mode 100644
index 0000000000..aa315f2519
--- /dev/null
+++ b/gfx/layers/composite/FontData.h
@@ -0,0 +1,304 @@
+/* -*- 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/. */
+
+// This is explicitly not guarded as we want only 1 file to include this and
+// it's good if things break if someone else does.
+
+namespace mozilla {
+namespace layers {
+namespace normal_font {
+const unsigned char sFontPNG[] = {
+ 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd,
+ 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0,
+ 0x8, 0x0, 0x0, 0x0, 0x0, 0x79, 0x19, 0xf7, 0xba, 0x0, 0x0, 0xb,
+ 0xfe, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0xed, 0x5d, 0xd1, 0xb6, 0xe3,
+ 0x20, 0x8, 0x74, 0x72, 0xf2, 0xff, 0xbf, 0x3c, 0xfb, 0xd0, 0xbb, 0xbd,
+ 0x51, 0x8, 0x48, 0xd4, 0xc4, 0xde, 0xda, 0xb3, 0x67, 0x77, 0x6d, 0x13,
+ 0x63, 0x10, 0x81, 0x41, 0x40, 0x30, 0x7d, 0xf7, 0x67, 0x4b, 0x8b, 0x0,
+ 0xdf, 0xfd, 0xd9, 0x2f, 0xdc, 0x3, 0xc6, 0xda, 0x76, 0x67, 0x29, 0xa5,
+ 0x94, 0xb8, 0x38, 0x20, 0x42, 0x33, 0xc0, 0x68, 0xaa, 0x37, 0x64, 0x14,
+ 0x47, 0xc2, 0x67, 0x13, 0x80, 0x5, 0xbf, 0x91, 0xb0, 0x5f, 0x9f, 0x73,
+ 0x71, 0x40, 0x39, 0x61, 0x1c, 0xcd, 0xff, 0x39, 0x7d, 0x8, 0x1e, 0x29,
+ 0x72, 0x3f, 0x1, 0x90, 0x12, 0x8a, 0xf9, 0xc, 0x52, 0x20, 0x9f, 0x51,
+ 0x2, 0x8e, 0xd0, 0x99, 0x8d, 0x3, 0x7a, 0x2f, 0x2, 0x7a, 0x4, 0xa4,
+ 0x90, 0xba, 0x8, 0x68, 0x1, 0x28, 0x32, 0x1a, 0x87, 0x6f, 0x50, 0x71,
+ 0x7f, 0x5f, 0x29, 0xf, 0x85, 0xa3, 0x18, 0x5b, 0x10, 0xc, 0x10, 0x80,
+ 0xf2, 0x1d, 0x79, 0x4e, 0x5e, 0x8f, 0xfc, 0xaf, 0x1, 0xb0, 0x60, 0x50,
+ 0x4, 0x17, 0x40, 0xc6, 0xd3, 0xf4, 0xd5, 0x2e, 0x3a, 0xdb, 0x1, 0x5d,
+ 0x85, 0x78, 0x8a, 0xbd, 0x7e, 0x33, 0xc3, 0x24, 0xe4, 0x52, 0x70, 0xff,
+ 0xbc, 0xf5, 0x8f, 0xf0, 0x82, 0xe2, 0x5c, 0x1c, 0xd0, 0x9b, 0x83, 0x1c,
+ 0x7a, 0x50, 0xb4, 0x33, 0xa9, 0xf9, 0xf5, 0x58, 0x0, 0xb, 0xe, 0x2f,
+ 0x2, 0x2c, 0x2, 0x7c, 0x3d, 0x1, 0xea, 0x8c, 0xb9, 0xc, 0x4f, 0x16,
+ 0x70, 0x14, 0x21, 0xc5, 0x8e, 0x60, 0xfb, 0x1, 0xe, 0x80, 0xf5, 0xc2,
+ 0x52, 0x8c, 0xb6, 0x9, 0x52, 0xdc, 0xfb, 0xc6, 0x35, 0x4, 0x40, 0xa,
+ 0x81, 0x8d, 0x66, 0xbd, 0xce, 0xc9, 0x8, 0x80, 0x6f, 0x93, 0x1, 0x6d,
+ 0xef, 0xcf, 0xf1, 0xf4, 0xc2, 0xc0, 0xf9, 0xd9, 0xdb, 0xe7, 0x7f, 0x30,
+ 0x5, 0x50, 0x98, 0x6b, 0x48, 0xe8, 0x9, 0xaf, 0x77, 0x14, 0xf8, 0xbe,
+ 0xf5, 0x7d, 0xa0, 0xfa, 0xb, 0x18, 0xa2, 0x6f, 0x3e, 0x9e, 0xc2, 0x5c,
+ 0x65, 0x5f, 0xe3, 0x75, 0xa7, 0x30, 0x87, 0x3d, 0xa, 0xd8, 0x42, 0x8b,
+ 0x8a, 0x3f, 0x81, 0xb6, 0x7f, 0xa1, 0xa6, 0x8f, 0x80, 0x7, 0xa2, 0x5d,
+ 0x8, 0xd2, 0xd1, 0xfb, 0x7f, 0x4a, 0x4a, 0x6e, 0xfe, 0xc, 0x3c, 0xab,
+ 0xa6, 0x9e, 0x31, 0x84, 0x4c, 0x9e, 0xa3, 0xfb, 0x45, 0x47, 0x7f, 0xc5,
+ 0xc2, 0x2, 0xcb, 0x1f, 0xb0, 0x38, 0x60, 0x11, 0x60, 0x7a, 0x2, 0xe0,
+ 0x6f, 0x11, 0x0, 0x1e, 0x5a, 0x85, 0x3, 0x97, 0x5f, 0xde, 0x1, 0x94,
+ 0x5f, 0x9c, 0x93, 0x2b, 0xf2, 0x73, 0x77, 0x68, 0xd1, 0xce, 0x1, 0x60,
+ 0x6e, 0x3b, 0x82, 0x44, 0x26, 0x5c, 0xe1, 0x6d, 0x5f, 0xd3, 0xb2, 0x43,
+ 0x91, 0xb4, 0xcd, 0xb0, 0x27, 0x97, 0x0, 0x12, 0x4, 0x53, 0xc, 0x54,
+ 0x25, 0x44, 0x6f, 0x4d, 0x95, 0x77, 0xe7, 0x13, 0x80, 0xd2, 0x30, 0xf4,
+ 0x2d, 0x1f, 0xf0, 0xb4, 0x45, 0x47, 0x11, 0x13, 0xb7, 0xda, 0x49, 0x7b,
+ 0xf7, 0x29, 0x22, 0x12, 0xc1, 0x23, 0xa2, 0x3, 0x1b, 0xec, 0xd, 0x30,
+ 0xc7, 0x7f, 0x3f, 0x4b, 0x82, 0x9d, 0x18, 0xe0, 0x2, 0x1, 0xca, 0xdd,
+ 0xd9, 0x32, 0xe4, 0xe2, 0x35, 0xb6, 0xdf, 0xab, 0xd0, 0x86, 0xaf, 0x59,
+ 0xf0, 0x80, 0x4b, 0xcc, 0xe0, 0xd6, 0xe1, 0x7e, 0x41, 0x4, 0xb8, 0xf,
+ 0x11, 0xf8, 0x9a, 0x37, 0x1a, 0x9c, 0xc1, 0x27, 0x6d, 0xd1, 0xe, 0x5e,
+ 0x22, 0x80, 0x21, 0x99, 0xcc, 0x89, 0xc, 0xee, 0x72, 0x28, 0xbe, 0x1d,
+ 0x10, 0x17, 0xd3, 0x60, 0x4a, 0x7c, 0x7, 0xee, 0x10, 0x0, 0x81, 0x71,
+ 0x73, 0x1c, 0xdd, 0x86, 0xe8, 0xf, 0x86, 0x1a, 0xfb, 0x50, 0xc8, 0x77,
+ 0xe3, 0xe3, 0xb7, 0xfb, 0x57, 0xdd, 0x5c, 0x78, 0x76, 0x86, 0xd5, 0x99,
+ 0x8d, 0xe1, 0xee, 0x1, 0x2d, 0x7f, 0xc0, 0x82, 0xc3, 0x8b, 0x0, 0xdf,
+ 0xfd, 0xd9, 0x15, 0xcb, 0x1a, 0x22, 0xb6, 0x94, 0x56, 0xbb, 0x94, 0x62,
+ 0xa5, 0x62, 0x80, 0x68, 0x9a, 0xbf, 0xf7, 0xdd, 0xf9, 0xd2, 0xed, 0x82,
+ 0x2c, 0x52, 0x54, 0x40, 0x15, 0xc8, 0xb7, 0xcb, 0xd0, 0x88, 0xb0, 0xc6,
+ 0x6d, 0x6c, 0x5c, 0x5e, 0xcf, 0xe2, 0xe, 0xbf, 0xff, 0x36, 0x59, 0xe,
+ 0x7b, 0x3c, 0x9b, 0x7b, 0x3, 0x3b, 0x68, 0xfd, 0x96, 0x94, 0x92, 0x8a,
+ 0x0, 0x12, 0x33, 0x5c, 0xbc, 0xb4, 0x42, 0xe9, 0x81, 0xa1, 0xf0, 0xb,
+ 0x96, 0x70, 0x4f, 0x2e, 0xa9, 0xd4, 0x64, 0xeb, 0x79, 0x33, 0x80, 0xa4,
+ 0xf0, 0x63, 0xc6, 0x72, 0x16, 0x76, 0xdb, 0x2f, 0x30, 0x98, 0x83, 0x5,
+ 0x29, 0x4d, 0x1b, 0x7b, 0x86, 0xc2, 0xd6, 0x7d, 0x88, 0x63, 0x93, 0x8c,
+ 0xa7, 0x6, 0x63, 0x4, 0x90, 0x8f, 0x28, 0xd7, 0xac, 0x39, 0x0, 0xfa,
+ 0xbd, 0x83, 0x85, 0x43, 0x25, 0x6, 0xc6, 0xfc, 0x68, 0xf1, 0x73, 0x7,
+ 0xd5, 0xde, 0x24, 0x60, 0xfe, 0xcf, 0x57, 0xc8, 0x9, 0x1, 0xc6, 0x58,
+ 0xde, 0x5f, 0x22, 0xd9, 0xe3, 0xc5, 0x12, 0x40, 0x63, 0xb0, 0xb4, 0x97,
+ 0x14, 0xc7, 0xce, 0xc6, 0xfc, 0x85, 0xfb, 0x33, 0x96, 0xa4, 0xdb, 0x9f,
+ 0x92, 0xae, 0x60, 0xe9, 0x69, 0x57, 0x6f, 0x2b, 0x5a, 0xd1, 0x6e, 0x33,
+ 0xa5, 0xa6, 0xeb, 0x5b, 0xad, 0x84, 0x47, 0xc1, 0xd7, 0x74, 0xf8, 0x6d,
+ 0xfb, 0xf2, 0xf7, 0x5f, 0x70, 0x78, 0xa1, 0xc1, 0x45, 0x80, 0x45, 0x80,
+ 0xaf, 0xf7, 0x7, 0x28, 0xd1, 0x9c, 0xb4, 0xc0, 0x45, 0x91, 0x4c, 0xaa,
+ 0xda, 0xd, 0x87, 0x6f, 0xdc, 0xfb, 0x99, 0x3f, 0x12, 0x34, 0xf1, 0xbb,
+ 0x7b, 0xfd, 0xff, 0x76, 0xfe, 0x6f, 0xb9, 0xbd, 0xc8, 0x23, 0x1, 0xde,
+ 0x9b, 0x78, 0xbf, 0xf0, 0x4e, 0xdb, 0xeb, 0xcb, 0x9e, 0x50, 0x98, 0x5e,
+ 0x25, 0xe0, 0x37, 0xd1, 0x1b, 0x63, 0xda, 0x47, 0xcb, 0x2c, 0xb5, 0x6e,
+ 0xff, 0x79, 0x63, 0xdb, 0x90, 0x7a, 0x7f, 0xb9, 0xfd, 0xfe, 0xbf, 0x12,
+ 0x82, 0xbc, 0x2e, 0xf7, 0xf6, 0xb4, 0x83, 0xf7, 0x8f, 0x54, 0xde, 0x25,
+ 0x5a, 0x67, 0xf6, 0xe5, 0xf6, 0x42, 0x63, 0xc7, 0x98, 0x87, 0x32, 0xe4,
+ 0xe3, 0xd3, 0x6c, 0x21, 0x22, 0x42, 0xa2, 0xfd, 0x8d, 0x48, 0x6d, 0x8f,
+ 0x4c, 0x4, 0xed, 0x29, 0x42, 0x41, 0x1, 0xfc, 0xbc, 0x84, 0xfe, 0xaf,
+ 0xdc, 0x60, 0xee, 0xcd, 0xee, 0xa, 0x53, 0x6a, 0x3e, 0xbe, 0xd0, 0x9a,
+ 0xad, 0xf0, 0x50, 0x1c, 0x1e, 0xc2, 0xe0, 0x4b, 0x81, 0x2e, 0xb, 0x50,
+ 0x44, 0x30, 0xb8, 0x70, 0x18, 0x85, 0x2c, 0x9c, 0xd8, 0x44, 0x66, 0xd7,
+ 0xfd, 0xf6, 0x2d, 0xfd, 0x6c, 0x60, 0x57, 0xf7, 0x49, 0x28, 0x33, 0x16,
+ 0x61, 0x10, 0x84, 0x6e, 0x40, 0x8d, 0x60, 0xf3, 0x78, 0xfe, 0x78, 0x79,
+ 0xf1, 0xfc, 0xfd, 0xcd, 0x84, 0xb9, 0xfa, 0x3c, 0xf4, 0x51, 0xae, 0x69,
+ 0xc2, 0x5e, 0xe3, 0x5e, 0xcd, 0x8, 0x22, 0x22, 0x53, 0xa8, 0x6f, 0x13,
+ 0x14, 0xe3, 0x63, 0x15, 0xd5, 0xb5, 0xe7, 0x1f, 0x63, 0x79, 0xbe, 0xd2,
+ 0x12, 0xfc, 0x55, 0x0, 0x5f, 0x8a, 0x8b, 0x97, 0x3f, 0x60, 0xa1, 0xc1,
+ 0x45, 0x80, 0x45, 0x80, 0x2f, 0xf7, 0x7, 0x54, 0xe1, 0xfd, 0xf3, 0x8d,
+ 0x82, 0x12, 0xfe, 0x97, 0x96, 0xa4, 0x5, 0xcf, 0x7f, 0x77, 0xc5, 0x60,
+ 0xf8, 0x7, 0xe4, 0xe3, 0x2d, 0xbf, 0x3e, 0x82, 0x7b, 0xd1, 0x7b, 0x1d,
+ 0xde, 0x17, 0xe, 0x88, 0x77, 0xdb, 0xdb, 0xcd, 0x67, 0x79, 0x7d, 0xd1,
+ 0x13, 0xe5, 0xde, 0x20, 0xa5, 0xbb, 0xc1, 0xc1, 0x16, 0x96, 0xf7, 0xc0,
+ 0x89, 0xd3, 0x3d, 0x8b, 0xf, 0xf0, 0xf3, 0x67, 0xdb, 0x94, 0x2f, 0x8e,
+ 0x86, 0x99, 0x48, 0x99, 0x8, 0xc4, 0x4e, 0xb6, 0x22, 0xf7, 0xf1, 0x32,
+ 0x20, 0x6, 0xcf, 0x47, 0x18, 0x3a, 0x74, 0x6a, 0x89, 0x79, 0x78, 0xbf,
+ 0xf8, 0x9d, 0x68, 0xe3, 0x80, 0x17, 0x58, 0x3d, 0x40, 0x56, 0x5, 0xba,
+ 0x34, 0x6, 0xcf, 0xc6, 0x84, 0x60, 0x45, 0x3d, 0x38, 0xb0, 0xc5, 0x3f,
+ 0x50, 0xb1, 0x1e, 0x22, 0xc1, 0xf7, 0x21, 0x77, 0x87, 0xcc, 0x37, 0xb8,
+ 0xb4, 0x4, 0x1a, 0x53, 0x1e, 0x30, 0xb3, 0xc1, 0xbd, 0x55, 0xe1, 0xf5,
+ 0x23, 0x5, 0xd0, 0xba, 0xe6, 0xf9, 0x2b, 0xfd, 0x6b, 0x16, 0x29, 0x38,
+ 0x74, 0x7a, 0xf6, 0x3a, 0xbc, 0x7e, 0xe0, 0x33, 0x57, 0x6, 0x8, 0x4,
+ 0x4f, 0xd7, 0x89, 0x6d, 0x5f, 0x31, 0x14, 0xaa, 0xea, 0x4f, 0x9e, 0x19,
+ 0x23, 0x82, 0xad, 0x17, 0x54, 0xc8, 0x80, 0xbe, 0x19, 0x1e, 0x93, 0x9b,
+ 0xc2, 0x3, 0xec, 0x9c, 0xc1, 0x2c, 0x60, 0x8e, 0xae, 0x32, 0x81, 0x86,
+ 0xcb, 0x21, 0xb2, 0xd0, 0xe0, 0x22, 0xc0, 0xf, 0x1, 0xf0, 0xed, 0x4,
+ 0xf0, 0xec, 0x88, 0xba, 0xd2, 0x4f, 0xd7, 0x3b, 0x40, 0xe7, 0x76, 0x7c,
+ 0x9, 0xc, 0x16, 0x83, 0x51, 0x31, 0xdb, 0x23, 0x3c, 0xbf, 0x4d, 0x6,
+ 0x88, 0x3, 0xb, 0x0, 0xf3, 0x77, 0x79, 0x79, 0x79, 0x9e, 0x81, 0xe8,
+ 0xce, 0xea, 0xa0, 0x66, 0x86, 0x4d, 0xd3, 0x39, 0x1, 0xc7, 0x3e, 0x61,
+ 0x8f, 0x7f, 0x57, 0x67, 0xcc, 0xa, 0x77, 0x57, 0x32, 0x3c, 0x60, 0xdc,
+ 0x4f, 0x25, 0x40, 0x43, 0x44, 0x2c, 0x4, 0xb9, 0xc4, 0x45, 0x8f, 0x38,
+ 0x42, 0x58, 0x7b, 0xfc, 0x15, 0xc9, 0xd3, 0x74, 0xd9, 0xb4, 0xad, 0xd4,
+ 0x35, 0x3b, 0x2f, 0x2a, 0x25, 0xe1, 0x3e, 0x66, 0x9, 0xb6, 0xaa, 0x85,
+ 0xd1, 0x6a, 0x65, 0x78, 0x41, 0x45, 0x36, 0x3e, 0x63, 0xf8, 0x89, 0x19,
+ 0x5d, 0xcd, 0xd7, 0x8a, 0xa2, 0xaa, 0x70, 0x27, 0x1, 0x8d, 0x9a, 0xef,
+ 0x82, 0x8b, 0x65, 0x20, 0x7, 0x10, 0x5, 0x8d, 0x8b, 0x9c, 0x94, 0x12,
+ 0xee, 0x97, 0x29, 0x2e, 0x74, 0xf2, 0xfe, 0xa2, 0x29, 0x31, 0xe7, 0x3e,
+ 0x95, 0xeb, 0xd4, 0xe3, 0xd3, 0xc8, 0x1f, 0x1c, 0x73, 0xed, 0x47, 0x60,
+ 0x81, 0xc9, 0x4c, 0xef, 0x7, 0x38, 0x20, 0x96, 0x1a, 0xb, 0xfe, 0x39,
+ 0x2, 0x2c, 0x38, 0xbc, 0x8, 0xb0, 0x8, 0xf0, 0xa7, 0x8, 0x70, 0xbb,
+ 0x54, 0xb7, 0x4f, 0x33, 0x88, 0xfa, 0xb3, 0x3f, 0xef, 0x98, 0x1d, 0x17,
+ 0xa, 0x79, 0xa5, 0xc6, 0x52, 0x51, 0x4e, 0x4f, 0xc1, 0xcb, 0x79, 0x81,
+ 0x3, 0x17, 0xef, 0xcb, 0x19, 0xc1, 0x69, 0xf3, 0xdd, 0x86, 0x71, 0x7d,
+ 0x51, 0x8f, 0x12, 0x23, 0xeb, 0x50, 0xed, 0x1a, 0x5e, 0x36, 0x2b, 0xe,
+ 0x78, 0x78, 0xff, 0x82, 0x59, 0xb, 0xaf, 0x58, 0x39, 0xda, 0xd6, 0xa8,
+ 0xc5, 0x15, 0xaa, 0x4b, 0x8c, 0x41, 0x2a, 0x6, 0x4d, 0x71, 0x58, 0xfb,
+ 0xc5, 0xda, 0xde, 0x6a, 0x13, 0x8d, 0x41, 0x5a, 0x15, 0x26, 0xb6, 0xb,
+ 0x6f, 0x0, 0x44, 0x5f, 0xb8, 0xd, 0xd9, 0x4, 0x93, 0x9d, 0x29, 0x18,
+ 0xd8, 0x3a, 0xd3, 0xf2, 0x82, 0x16, 0xf0, 0xce, 0xf8, 0x8c, 0x4f, 0x11,
+ 0x4c, 0x8a, 0xd9, 0x45, 0x57, 0x5b, 0x8d, 0xdb, 0x2d, 0x5c, 0xc8, 0x16,
+ 0x35, 0xab, 0xce, 0x99, 0xf0, 0x88, 0xe0, 0xc0, 0xf0, 0x9a, 0xa2, 0x12,
+ 0xef, 0x9b, 0x54, 0xd6, 0xfc, 0x1, 0xc9, 0xf4, 0xf, 0xb8, 0x27, 0x82,
+ 0xd2, 0x3c, 0x42, 0x62, 0x6c, 0x7c, 0xc0, 0x9e, 0x3c, 0x17, 0xa7, 0xeb,
+ 0x24, 0x55, 0xda, 0xf4, 0xae, 0xe7, 0x69, 0xb3, 0xa7, 0xc0, 0xad, 0x61,
+ 0xcf, 0x9, 0xd, 0xa1, 0xb1, 0x3c, 0xcf, 0xd9, 0x2d, 0x41, 0xb7, 0x1e,
+ 0xb, 0xbd, 0xf8, 0x0, 0x4e, 0x44, 0xee, 0x85, 0x6, 0x3b, 0x31, 0x5,
+ 0xc6, 0xdd, 0xff, 0x11, 0x1c, 0x30, 0xb2, 0x32, 0xcf, 0xf3, 0x4, 0x78,
+ 0x78, 0x4, 0xfb, 0x15, 0x3d, 0x1b, 0x14, 0x34, 0x66, 0x21, 0x20, 0x50,
+ 0xad, 0x54, 0x86, 0xf3, 0x2, 0x5, 0xe7, 0x8a, 0x8d, 0xa7, 0xbf, 0x5a,
+ 0xa9, 0xb3, 0xd1, 0x74, 0xfe, 0xce, 0x73, 0x46, 0x59, 0x6a, 0x2e, 0x87,
+ 0xa7, 0x6e, 0x2a, 0xaf, 0x53, 0x80, 0xd0, 0x41, 0x93, 0xfb, 0x18, 0x6b,
+ 0x23, 0x9f, 0x81, 0xb2, 0xd6, 0x17, 0x12, 0xa3, 0x24, 0x66, 0xcb, 0x74,
+ 0x98, 0xd9, 0xe3, 0xf1, 0xc3, 0xdb, 0x82, 0xb1, 0xcf, 0x32, 0xd8, 0x7c,
+ 0xaa, 0x52, 0xc8, 0x7b, 0xdc, 0xa1, 0x31, 0xb6, 0x54, 0xfa, 0xeb, 0x78,
+ 0x2, 0xbc, 0x32, 0xec, 0x99, 0x64, 0x6c, 0x32, 0x4e, 0x57, 0x3a, 0xaf,
+ 0x50, 0xec, 0xa, 0x7, 0x8c, 0x3e, 0x81, 0x13, 0xc4, 0xfb, 0xaf, 0x54,
+ 0xe6, 0x27, 0x9c, 0x3c, 0xfd, 0xb2, 0xa2, 0xdb, 0x98, 0xc8, 0x4f, 0xb2,
+ 0x6, 0x7b, 0x57, 0xf7, 0xd8, 0xaa, 0x41, 0x7e, 0x37, 0xa1, 0x8, 0xc7,
+ 0x75, 0xad, 0xa0, 0x61, 0x8b, 0x2, 0x6e, 0x98, 0x9f, 0x8b, 0xc6, 0x11,
+ 0xe4, 0x20, 0xe1, 0xb0, 0x53, 0x3c, 0x78, 0x99, 0x6a, 0x8a, 0x15, 0x4,
+ 0x7c, 0xa9, 0xe5, 0x5f, 0x19, 0x30, 0xdc, 0xe, 0xe8, 0xae, 0x2, 0x59,
+ 0x1, 0xf2, 0x3d, 0xbc, 0x7a, 0x2c, 0xe9, 0xd0, 0xe8, 0xa2, 0x65, 0x13,
+ 0x83, 0x5c, 0x60, 0x80, 0xf, 0xfb, 0x80, 0xe3, 0xef, 0xf8, 0xd3, 0x40,
+ 0x6b, 0xf9, 0x3, 0x16, 0x1, 0x3e, 0x90, 0xeb, 0x8f, 0xba, 0x75, 0x9b,
+ 0x60, 0x38, 0x68, 0xbc, 0x20, 0xfa, 0x80, 0xcc, 0x6d, 0xed, 0x9f, 0x3d,
+ 0xee, 0x9d, 0x3f, 0xd8, 0x38, 0x3e, 0x69, 0x46, 0xbc, 0x77, 0xa7, 0xd1,
+ 0x7, 0x7b, 0x50, 0x3b, 0xc, 0x0, 0xe7, 0x4, 0x70, 0x2d, 0xa7, 0xbe,
+ 0x72, 0x53, 0x3f, 0xc4, 0x17, 0xae, 0x7d, 0x13, 0xb5, 0x2c, 0x72, 0xcb,
+ 0x11, 0x2c, 0xfc, 0x1, 0xd7, 0xd5, 0xc4, 0x18, 0x3b, 0x80, 0xe0, 0x7b,
+ 0xcb, 0xd0, 0x3, 0x6b, 0x48, 0x66, 0x78, 0xaa, 0xb0, 0x4, 0xf3, 0xfd,
+ 0x78, 0xf7, 0x7c, 0x1, 0x3a, 0x33, 0x46, 0xaf, 0xde, 0x7f, 0xf, 0x45,
+ 0x6d, 0x55, 0xb3, 0x7f, 0x7b, 0xb4, 0xc, 0x9c, 0x88, 0x3b, 0xb5, 0x0,
+ 0xba, 0x40, 0x2b, 0x9e, 0x1e, 0xd2, 0x78, 0x1, 0xe, 0xca, 0xd3, 0xef,
+ 0x52, 0xaa, 0x5e, 0x2, 0x1d, 0xe8, 0x91, 0xbd, 0x82, 0x5f, 0xe, 0x84,
+ 0x87, 0x3f, 0xa9, 0x36, 0x17, 0xfb, 0x72, 0xfe, 0xf9, 0x3e, 0x60, 0xf9,
+ 0x9a, 0xf9, 0x1a, 0x57, 0x8a, 0xe7, 0x57, 0x84, 0x48, 0x5c, 0x77, 0x88,
+ 0xc, 0x10, 0x60, 0x1c, 0xc1, 0x42, 0xfe, 0xfb, 0xe0, 0xaa, 0x3f, 0xe0,
+ 0x3, 0xc, 0xb7, 0xae, 0x5a, 0xc0, 0x96, 0xa9, 0xb, 0xb, 0x2c, 0x2,
+ 0x2c, 0x2, 0x2c, 0x2, 0x2c, 0x2, 0x7c, 0xd3, 0x67, 0xff, 0xb6, 0x17,
+ 0xbe, 0x52, 0x43, 0xc4, 0x2f, 0xd9, 0x27, 0x2e, 0x86, 0xb1, 0x97, 0x57,
+ 0xe6, 0xed, 0x81, 0x96, 0xe2, 0x6e, 0x87, 0x56, 0xf6, 0xc6, 0x40, 0xcd,
+ 0xe6, 0x72, 0x92, 0x25, 0xc, 0x3, 0xdb, 0xf5, 0xce, 0x79, 0x7f, 0x17,
+ 0x28, 0x78, 0x5, 0x9, 0x9d, 0x76, 0xe2, 0x9f, 0x3c, 0xd, 0x27, 0x7d,
+ 0x1c, 0x45, 0x82, 0x41, 0x4d, 0xe9, 0x30, 0xfb, 0xfd, 0x81, 0x63, 0xb0,
+ 0x69, 0x23, 0xb4, 0x14, 0xfd, 0xb9, 0x4, 0xa0, 0x8e, 0xce, 0x2c, 0xe0,
+ 0x1a, 0xf2, 0x89, 0xb8, 0xd1, 0xe6, 0xc8, 0xa2, 0xb1, 0x65, 0xd7, 0x71,
+ 0xbc, 0x6d, 0x45, 0x77, 0xef, 0xe1, 0x45, 0x81, 0x82, 0x27, 0x50, 0x54,
+ 0x7b, 0x87, 0x13, 0x41, 0x51, 0x75, 0xa4, 0x26, 0x4f, 0xfc, 0x37, 0x49,
+ 0xd6, 0x5b, 0xa8, 0xa8, 0x8b, 0xdd, 0x78, 0xe0, 0xa2, 0xe5, 0x4e, 0x50,
+ 0xc7, 0xe, 0xb6, 0xe1, 0xdf, 0x54, 0xd4, 0x14, 0x35, 0xdf, 0x5f, 0x1c,
+ 0x4f, 0xa0, 0xbc, 0x2d, 0xfd, 0x18, 0x21, 0x1b, 0xdf, 0x9b, 0x24, 0x64,
+ 0x99, 0x72, 0xe3, 0x48, 0x6e, 0x2f, 0xbb, 0xdc, 0xb9, 0xc0, 0x5d, 0x71,
+ 0x3c, 0x19, 0x63, 0xb5, 0x16, 0x50, 0x17, 0x9d, 0xa1, 0x5, 0xf0, 0xa,
+ 0x6a, 0x39, 0xed, 0x41, 0xf5, 0xdf, 0x44, 0xb4, 0x80, 0x26, 0xd6, 0x58,
+ 0xdf, 0xa1, 0xd3, 0x5f, 0xd, 0x1, 0x9c, 0x73, 0x85, 0x9d, 0x3e, 0xa3,
+ 0x6a, 0x30, 0x35, 0xd6, 0x2c, 0x6d, 0xb6, 0x3, 0x7a, 0x7b, 0x8, 0xbc,
+ 0x83, 0x93, 0xbe, 0xcf, 0x14, 0x9e, 0xcb, 0x5, 0xb3, 0x4d, 0x3e, 0xbe,
+ 0xf1, 0xd8, 0x60, 0xb9, 0xc4, 0x16, 0x1, 0x16, 0x1, 0xfa, 0x20, 0xec,
+ 0xcf, 0x25, 0x80, 0x13, 0xb6, 0xe8, 0xd4, 0x92, 0xab, 0x88, 0x33, 0xb,
+ 0xa6, 0x7f, 0x47, 0xb, 0x2, 0x94, 0xf9, 0xe6, 0x79, 0xfb, 0xff, 0xb3,
+ 0x71, 0x2a, 0x4, 0xa5, 0x61, 0xe5, 0x5, 0x36, 0x22, 0x18, 0x5c, 0x1e,
+ 0xb, 0x71, 0x80, 0x63, 0xfb, 0xeb, 0xd7, 0x83, 0x67, 0x6d, 0xc7, 0xf4,
+ 0xaa, 0x58, 0x2, 0x22, 0x82, 0x83, 0xee, 0x84, 0xb4, 0xac, 0xa, 0x36,
+ 0x5e, 0x2f, 0xec, 0x2e, 0xd5, 0xb6, 0x3f, 0x10, 0x80, 0x88, 0x15, 0x77,
+ 0xe7, 0xc3, 0x87, 0x11, 0xaa, 0xee, 0x0, 0x83, 0x49, 0x49, 0x24, 0x3,
+ 0xa0, 0x8a, 0x50, 0x59, 0x14, 0xc6, 0xaa, 0x80, 0xff, 0xd1, 0x39, 0x8a,
+ 0xd2, 0xab, 0xf0, 0x26, 0x88, 0x3c, 0xc9, 0x12, 0xe, 0xe2, 0x10, 0x5d,
+ 0xfc, 0xdb, 0x3e, 0x34, 0xff, 0x3, 0x66, 0xdd, 0xc9, 0xb6, 0x2b, 0x1c,
+ 0x85, 0xce, 0xdb, 0xdb, 0x8, 0x49, 0x10, 0xf7, 0x81, 0x5, 0x5, 0xf8,
+ 0x23, 0xc9, 0x90, 0xb5, 0x7f, 0x4f, 0x8d, 0x4b, 0xc5, 0xef, 0x8f, 0xdb,
+ 0x1, 0xcd, 0xa6, 0x67, 0x9d, 0xd4, 0x36, 0xfc, 0xb2, 0x59, 0x73, 0x1b,
+ 0xb4, 0x4c, 0x87, 0x1a, 0xef, 0x9a, 0x58, 0xb3, 0x95, 0x33, 0x4e, 0x4f,
+ 0xa1, 0xdd, 0x4f, 0x84, 0x38, 0x5b, 0x5e, 0x9f, 0x23, 0x21, 0x6, 0xb5,
+ 0xb2, 0x2e, 0x2c, 0xce, 0xd9, 0xcd, 0x24, 0x57, 0xf9, 0xfb, 0x60, 0x30,
+ 0xa4, 0x79, 0x18, 0xa6, 0xca, 0x5d, 0xcd, 0x17, 0xc8, 0xf3, 0x5b, 0x63,
+ 0xb7, 0xe7, 0x1b, 0xe4, 0x3e, 0xc7, 0x3b, 0x38, 0x60, 0x6a, 0x40, 0xbe,
+ 0xfc, 0x1, 0xaa, 0xd4, 0xe, 0xc6, 0x4b, 0x77, 0x57, 0x1a, 0x5e, 0xd5,
+ 0xda, 0x5e, 0x37, 0xff, 0x27, 0x0, 0x1d, 0x1d, 0x22, 0x4c, 0x19, 0x11,
+ 0x7f, 0x8d, 0xc8, 0x33, 0x5d, 0x5b, 0xd1, 0x7b, 0x7e, 0x76, 0x6c, 0x73,
+ 0xcb, 0xcd, 0x83, 0x96, 0xc0, 0x47, 0xe5, 0x51, 0xa9, 0xc9, 0xf3, 0xdd,
+ 0xdb, 0xb9, 0x11, 0xa8, 0x9d, 0x67, 0x78, 0x7e, 0x80, 0xa1, 0xdb, 0x6e,
+ 0x1b, 0xdc, 0xae, 0xb1, 0x74, 0xe7, 0x76, 0xa1, 0xea, 0xd4, 0xf3, 0xc,
+ 0x3, 0x60, 0xa0, 0x2f, 0x77, 0x6d, 0x77, 0x71, 0x59, 0xd1, 0x9a, 0x66,
+ 0x8d, 0x6c, 0xf2, 0xb0, 0xaf, 0xde, 0xed, 0xb9, 0x3f, 0xfb, 0x5d, 0x62,
+ 0x26, 0x55, 0x7, 0xf3, 0x46, 0xdb, 0xed, 0x4, 0xe8, 0x7d, 0xcc, 0x89,
+ 0xd6, 0xe6, 0x75, 0x7, 0xc0, 0x58, 0x19, 0xb0, 0xcb, 0x53, 0x5e, 0x47,
+ 0xb4, 0xe7, 0x5d, 0x12, 0xf, 0x38, 0x44, 0x90, 0x66, 0xa2, 0xc8, 0xaf,
+ 0xef, 0x6c, 0xe8, 0xbf, 0x98, 0xd6, 0xe, 0x78, 0xc4, 0x66, 0x9b, 0xc9,
+ 0x50, 0x7c, 0x64, 0x6f, 0x70, 0x26, 0x2d, 0xf9, 0x8, 0x1, 0x66, 0xe2,
+ 0x80, 0xe5, 0xf, 0xd0, 0x25, 0xa3, 0x3, 0x6f, 0xe1, 0x41, 0xf0, 0xd6,
+ 0x7c, 0xef, 0x26, 0x88, 0x1f, 0xd2, 0x31, 0x5b, 0xd, 0x9c, 0x75, 0xfc,
+ 0x5, 0x25, 0xe6, 0x76, 0xfd, 0x5, 0x51, 0x19, 0x11, 0x84, 0xf8, 0x33,
+ 0x9d, 0xe1, 0xf2, 0x9, 0x58, 0x40, 0x98, 0xda, 0x22, 0xb0, 0x50, 0xc4,
+ 0xcb, 0x37, 0xb6, 0x35, 0x86, 0x65, 0x51, 0x75, 0xa8, 0xb7, 0x65, 0x7a,
+ 0x3a, 0x9c, 0x3d, 0xa9, 0xdb, 0xe9, 0x96, 0xcf, 0xa4, 0xb5, 0x5d, 0xe5,
+ 0x93, 0x69, 0xd1, 0xb1, 0x8c, 0x3c, 0x6e, 0x93, 0xab, 0x4e, 0xdd, 0xcc,
+ 0x3c, 0xc4, 0xef, 0xb7, 0xb6, 0xb, 0x21, 0xf5, 0x33, 0xa0, 0x7e, 0xa6,
+ 0x41, 0x99, 0xb0, 0x60, 0x47, 0xa8, 0xdc, 0xbf, 0x31, 0xa2, 0x9d, 0x27,
+ 0x30, 0xb6, 0x40, 0xdf, 0x99, 0x53, 0xe6, 0xc5, 0x1, 0x4a, 0x69, 0xb3,
+ 0xc1, 0x86, 0xda, 0xf0, 0xf7, 0xf, 0xd5, 0xeb, 0xdf, 0xa8, 0x64, 0x7b,
+ 0x73, 0xf0, 0xf6, 0xee, 0xd3, 0x96, 0xf0, 0xf1, 0x64, 0xf1, 0xfb, 0x97,
+ 0x80, 0xac, 0xae, 0x3f, 0x58, 0x15, 0x2b, 0x1e, 0xa4, 0xc3, 0xf6, 0xf5,
+ 0xa6, 0x8e, 0x41, 0xca, 0xc4, 0xdf, 0xb, 0x9a, 0xdb, 0x49, 0xd4, 0x34,
+ 0x91, 0xd7, 0x67, 0x87, 0x98, 0xc4, 0xda, 0x72, 0xbe, 0x69, 0x95, 0x8c,
+ 0x4, 0x9f, 0xb6, 0x3, 0x5e, 0x23, 0xb0, 0xc2, 0x90, 0xa2, 0x6d, 0xbb,
+ 0xe0, 0xc4, 0xab, 0x82, 0xe2, 0x3a, 0x7c, 0xfd, 0x51, 0x38, 0xbc, 0x8,
+ 0xb0, 0x8, 0xb0, 0x8, 0xf0, 0xa7, 0x9, 0x80, 0x4f, 0x23, 0x40, 0xe7,
+ 0x0, 0x11, 0xc4, 0x83, 0x63, 0x1b, 0x1c, 0x40, 0x3d, 0x22, 0x44, 0x9a,
+ 0x3, 0x44, 0x10, 0x33, 0xc5, 0x9b, 0x1d, 0x40, 0xb0, 0xcc, 0xf6, 0xd0,
+ 0xdd, 0xcb, 0xe, 0xf8, 0x39, 0x61, 0xa2, 0xc9, 0xf0, 0xaa, 0x68, 0x67,
+ 0xb1, 0x79, 0x9d, 0xfb, 0x6f, 0x8e, 0x10, 0xd1, 0x22, 0x8b, 0x7b, 0xb7,
+ 0x8f, 0x5f, 0xf6, 0xee, 0xbf, 0x59, 0x8, 0xb6, 0xee, 0x4e, 0x5f, 0xd9,
+ 0xcd, 0xe6, 0x6c, 0x4b, 0xc0, 0x11, 0xfa, 0x1d, 0xda, 0xa8, 0x97, 0xe3,
+ 0xd1, 0x76, 0x2b, 0x1, 0x40, 0xe9, 0xa3, 0xeb, 0xdd, 0xb6, 0xe1, 0x6a,
+ 0x7, 0x96, 0x6a, 0x34, 0x84, 0x46, 0xf3, 0x80, 0x60, 0x2, 0xcc, 0xc3,
+ 0x4, 0xa0, 0x88, 0xef, 0xef, 0xdf, 0x4e, 0x19, 0x4, 0x9f, 0x4b, 0xb,
+ 0x3c, 0x61, 0x7, 0x4c, 0x65, 0x7b, 0x3c, 0x1, 0x86, 0xa6, 0xa, 0xa2,
+ 0x7b, 0x82, 0x0, 0x53, 0x71, 0xc0, 0x72, 0x89, 0x2d, 0x7f, 0xc0, 0x22,
+ 0x80, 0x62, 0x9a, 0xc5, 0x2, 0x44, 0x7a, 0x27, 0x94, 0xdc, 0x19, 0x20,
+ 0xf2, 0x3a, 0x78, 0xd9, 0xc6, 0xfb, 0xbd, 0xfd, 0x5, 0x63, 0xfd, 0x3,
+ 0xe2, 0x7a, 0x67, 0x7a, 0x3a, 0x63, 0xab, 0x9, 0x5, 0xaf, 0xf3, 0xb4,
+ 0x4d, 0x46, 0xf8, 0x24, 0xa7, 0x66, 0x44, 0x6b, 0x5b, 0x24, 0x7e, 0xe6,
+ 0x17, 0x88, 0xf8, 0x81, 0x58, 0x5b, 0xa9, 0x17, 0x21, 0xba, 0x3f, 0xfe,
+ 0xfe, 0x8a, 0x16, 0x2f, 0xf7, 0x92, 0x46, 0xfa, 0x7, 0xb4, 0x19, 0xba,
+ 0x3f, 0xd9, 0xd8, 0x40, 0x83, 0xe9, 0x86, 0xbd, 0x5a, 0xc5, 0x67, 0xd4,
+ 0xcf, 0xcc, 0xb4, 0xb, 0x0, 0x94, 0xbf, 0xdf, 0xbe, 0x3d, 0xae, 0xd6,
+ 0x96, 0x7b, 0xd0, 0x36, 0x9e, 0xc0, 0xe, 0xe8, 0x1e, 0x1f, 0x62, 0xf7,
+ 0x58, 0xfc, 0xfe, 0x8, 0x16, 0xf0, 0x78, 0xa2, 0xaf, 0x7b, 0x0, 0xae,
+ 0x84, 0x70, 0xaa, 0xe6, 0xf6, 0xf6, 0x12, 0x2b, 0xe3, 0x2b, 0xae, 0x2f,
+ 0xea, 0x33, 0x6, 0xdb, 0x22, 0x20, 0xa, 0x91, 0xf0, 0x81, 0xbf, 0xf0,
+ 0xf1, 0x2, 0x70, 0xc6, 0x97, 0xd0, 0x98, 0x5b, 0xa6, 0x38, 0x25, 0x34,
+ 0x3e, 0xfe, 0xf5, 0x13, 0x43, 0xbf, 0x7f, 0xbd, 0x3f, 0xe0, 0x1f, 0x5f,
+ 0x4e, 0x4b, 0x19, 0x56, 0xcb, 0xb5, 0x20, 0x0, 0x0, 0x0, 0x0, 0x49,
+ 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82};
+const unsigned short sGlyphWidths[256] = {
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 4, 3, 5, 7, 7, 12,
+ 9, 2, 4, 4, 5, 8, 4, 4, 4, 4, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 4, 4, 8, 8, 8, 7, 13, 9, 9, 9, 9, 9, 8, 10, 9, 3, 6, 9,
+ 7, 11, 9, 10, 9, 10, 9, 9, 7, 9, 9, 13, 7, 9, 7, 4, 4, 4, 5,
+ 7, 4, 7, 7, 7, 7, 7, 3, 7, 7, 3, 3, 7, 3, 11, 7, 7, 7, 7,
+ 4, 7, 4, 7, 5, 9, 7, 7, 7, 4, 3, 4, 8, 10, 7, 10, 3, 7, 4,
+ 13, 7, 7, 4, 14, 9, 4, 13, 10, 7, 10, 10, 3, 3, 4, 4, 5, 7, 13,
+ 4, 13, 7, 4, 12, 10, 7, 9, 4, 3, 7, 7, 7, 7, 3, 7, 4, 10, 4,
+ 7, 8, 4, 10, 7, 5, 7, 4, 4, 4, 7, 7, 4, 4, 4, 5, 7, 11, 11,
+ 11, 8, 9, 9, 9, 9, 9, 9, 13, 9, 9, 9, 9, 9, 3, 3, 3, 3, 9,
+ 9, 10, 10, 10, 10, 10, 8, 10, 9, 9, 9, 9, 9, 9, 9, 7, 7, 7, 7,
+ 7, 7, 12, 7, 7, 7, 7, 7, 3, 3, 3, 3, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7};
+} // namespace normal_font
+
+const FontBitmapInfo sDefaultCompositorFont = {
+ mozilla::Nothing(),
+ mozilla::Some(&normal_font::sGlyphWidths[0]),
+ 256,
+ 256,
+ 16,
+ 16,
+ 0,
+ normal_font::sFontPNG,
+ sizeof(normal_font::sFontPNG)};
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/composite/FrameUniformityData.cpp b/gfx/layers/composite/FrameUniformityData.cpp
new file mode 100644
index 0000000000..397ec0e1dc
--- /dev/null
+++ b/gfx/layers/composite/FrameUniformityData.cpp
@@ -0,0 +1,145 @@
+/* -*- 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 "FrameUniformityData.h"
+
+#include <map>
+
+#include "Units.h"
+#include "gfxPoint.h"
+#include "mozilla/TimeStamp.h"
+#include "mozilla/dom/APZTestDataBinding.h"
+#include "mozilla/dom/ToJSValue.h"
+#include "nsTArray.h"
+
+namespace mozilla {
+namespace layers {
+
+using namespace gfx;
+
+Point LayerTransforms::GetAverage() {
+ MOZ_ASSERT(!mTransforms.IsEmpty());
+
+ Point current = mTransforms[0];
+ Point average;
+ size_t length = mTransforms.Length();
+
+ for (size_t i = 1; i < length; i++) {
+ Point nextTransform = mTransforms[i];
+ Point movement = nextTransform - current;
+ average += Point(std::fabs(movement.x), std::fabs(movement.y));
+ current = nextTransform;
+ }
+
+ average = average / (float)length;
+ return average;
+}
+
+Point LayerTransforms::GetStdDev() {
+ Point average = GetAverage();
+ Point stdDev;
+ Point current = mTransforms[0];
+
+ for (size_t i = 1; i < mTransforms.Length(); i++) {
+ Point next = mTransforms[i];
+ Point move = next - current;
+ move.x = fabs(move.x);
+ move.y = fabs(move.y);
+
+ Point diff = move - average;
+ diff.x = diff.x * diff.x;
+ diff.y = diff.y * diff.y;
+ stdDev += diff;
+
+ current = next;
+ }
+
+ stdDev = stdDev / mTransforms.Length();
+ stdDev.x = sqrt(stdDev.x);
+ stdDev.y = sqrt(stdDev.y);
+ return stdDev;
+}
+
+bool LayerTransforms::Sanitize() {
+ // Remove leading and trailing zeros to isolate the composites that actually
+ // changed the transform
+ for (size_t i = 1; i < mTransforms.Length(); i++) {
+ if (mTransforms[i] != mTransforms[i - 1]) {
+ mTransforms.RemoveElementsAt(0, i - 1);
+ break;
+ }
+ }
+ for (size_t i = mTransforms.Length() - 1; i > 0; i--) {
+ if (mTransforms[i - 1] != mTransforms[i]) {
+ mTransforms.SetLength(i + 1);
+ break;
+ }
+ }
+ return !mTransforms.IsEmpty();
+}
+
+LayerTransformRecorder::~LayerTransformRecorder() { Reset(); }
+
+void LayerTransformRecorder::RecordTransform(Layer* aLayer,
+ const Point& aTransform) {
+ LayerTransforms* layerTransforms = GetLayerTransforms((uintptr_t)aLayer);
+ layerTransforms->mTransforms.AppendElement(aTransform);
+}
+
+void LayerTransformRecorder::EndTest(FrameUniformityData* aOutData) {
+ for (const auto& [layer, transforms] : mFrameTransforms) {
+ (void)transforms; // suppress unused variable warning
+
+ float uniformity = CalculateFrameUniformity(layer);
+
+ std::pair<uintptr_t, float> result(layer, uniformity);
+ aOutData->mUniformities.insert(result);
+ }
+
+ Reset();
+}
+
+LayerTransforms* LayerTransformRecorder::GetLayerTransforms(uintptr_t aLayer) {
+ auto [iter, inserted] =
+ mFrameTransforms.insert(FrameTransformMap::value_type{aLayer, nullptr});
+ if (inserted) {
+ iter->second = MakeUnique<LayerTransforms>();
+ }
+ return iter->second.get();
+}
+
+void LayerTransformRecorder::Reset() { mFrameTransforms.clear(); }
+
+float LayerTransformRecorder::CalculateFrameUniformity(uintptr_t aLayer) {
+ LayerTransforms* layerTransform = GetLayerTransforms(aLayer);
+ float yUniformity = -1;
+ if (layerTransform->Sanitize()) {
+ Point stdDev = layerTransform->GetStdDev();
+ yUniformity = stdDev.y;
+ }
+ return yUniformity;
+}
+
+bool FrameUniformityData::ToJS(JS::MutableHandleValue aOutValue,
+ JSContext* aContext) {
+ dom::FrameUniformityResults results;
+ dom::Sequence<dom::FrameUniformity>& layers =
+ results.mLayerUniformities.Construct();
+
+ for (const auto& [layerAddr, uniformity] : mUniformities) {
+ // FIXME: Make this infallible after bug 968520 is done.
+ MOZ_ALWAYS_TRUE(layers.AppendElement(fallible));
+ dom::FrameUniformity& entry = layers.LastElement();
+
+ entry.mLayerAddress.Construct() = layerAddr;
+ entry.mFrameUniformity.Construct() = uniformity;
+ }
+
+ return dom::ToJSValue(aContext, results, aOutValue);
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/composite/FrameUniformityData.h b/gfx/layers/composite/FrameUniformityData.h
new file mode 100644
index 0000000000..8cb639e60c
--- /dev/null
+++ b/gfx/layers/composite/FrameUniformityData.h
@@ -0,0 +1,80 @@
+/* -*- 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/. */
+
+#ifndef mozilla_layers_FrameUniformityData_h_
+#define mozilla_layers_FrameUniformityData_h_
+
+#include "ipc/IPCMessageUtils.h"
+#include "js/TypeDecls.h"
+#include "ipc/IPCMessageUtilsSpecializations.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/gfx/Point.h"
+#include "nsTArray.h"
+
+namespace mozilla {
+namespace layers {
+class Layer;
+
+class FrameUniformityData {
+ friend struct IPC::ParamTraits<FrameUniformityData>;
+
+ public:
+ bool ToJS(JS::MutableHandleValue aOutValue, JSContext* aContext);
+ // Contains the calculated frame uniformities
+ std::map<uintptr_t, float> mUniformities;
+};
+
+struct LayerTransforms {
+ LayerTransforms() = default;
+
+ gfx::Point GetAverage();
+ gfx::Point GetStdDev();
+ bool Sanitize();
+
+ // 60 fps * 5 seconds worth of data
+ AutoTArray<gfx::Point, 300> mTransforms;
+};
+
+class LayerTransformRecorder {
+ public:
+ LayerTransformRecorder() = default;
+ ~LayerTransformRecorder();
+
+ void RecordTransform(Layer* aLayer, const gfx::Point& aTransform);
+ void Reset();
+ void EndTest(FrameUniformityData* aOutData);
+
+ private:
+ float CalculateFrameUniformity(uintptr_t aLayer);
+ LayerTransforms* GetLayerTransforms(uintptr_t aLayer);
+ using FrameTransformMap =
+ std::map<uintptr_t, mozilla::UniquePtr<LayerTransforms>>;
+ FrameTransformMap mFrameTransforms;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+namespace IPC {
+template <>
+struct ParamTraits<mozilla::layers::FrameUniformityData> {
+ typedef mozilla::layers::FrameUniformityData paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam) {
+ WriteParam(aMsg, aParam.mUniformities);
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter,
+ paramType* aResult) {
+ return ParamTraitsStd<std::map<uintptr_t, float>>::Read(
+ aMsg, aIter, &aResult->mUniformities);
+ }
+};
+
+} // namespace IPC
+
+#endif // mozilla_layers_FrameUniformityData_h_
diff --git a/gfx/layers/composite/GPUVideoTextureHost.cpp b/gfx/layers/composite/GPUVideoTextureHost.cpp
new file mode 100644
index 0000000000..ed6be1972d
--- /dev/null
+++ b/gfx/layers/composite/GPUVideoTextureHost.cpp
@@ -0,0 +1,295 @@
+/* -*- 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 "GPUVideoTextureHost.h"
+
+#include "ImageContainer.h"
+#include "mozilla/RemoteDecoderManagerParent.h"
+#include "mozilla/layers/ImageBridgeParent.h"
+#include "mozilla/layers/VideoBridgeParent.h"
+#include "mozilla/webrender/RenderTextureHostWrapper.h"
+#include "mozilla/webrender/RenderThread.h"
+
+namespace mozilla {
+namespace layers {
+
+GPUVideoTextureHost::GPUVideoTextureHost(
+ TextureFlags aFlags, const SurfaceDescriptorGPUVideo& aDescriptor)
+ : TextureHost(aFlags), mDescriptor(aDescriptor) {
+ MOZ_COUNT_CTOR(GPUVideoTextureHost);
+}
+
+GPUVideoTextureHost::~GPUVideoTextureHost() {
+ MOZ_COUNT_DTOR(GPUVideoTextureHost);
+}
+
+GPUVideoTextureHost* GPUVideoTextureHost::CreateFromDescriptor(
+ TextureFlags aFlags, const SurfaceDescriptorGPUVideo& aDescriptor) {
+ return new GPUVideoTextureHost(aFlags, aDescriptor);
+}
+
+TextureHost* GPUVideoTextureHost::EnsureWrappedTextureHost() {
+ if (mWrappedTextureHost) {
+ return mWrappedTextureHost;
+ }
+
+ const auto& sd =
+ static_cast<const SurfaceDescriptorRemoteDecoder&>(mDescriptor);
+ VideoBridgeParent* parent = VideoBridgeParent::GetSingleton(sd.source());
+ if (!parent) {
+ // The VideoBridge went away. This can happen if the RDD process
+ // crashes.
+ return nullptr;
+ }
+ mWrappedTextureHost = parent->LookupTexture(sd.handle());
+
+ if (!mWrappedTextureHost) {
+ // The TextureHost hasn't been registered yet. This is due to a race
+ // between the ImageBridge (content) and the VideoBridge (RDD) and the
+ // ImageBridge won. See bug
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=1630733#c14 for more
+ // details.
+ return nullptr;
+ }
+
+ if (mWrappedTextureHost->AsBufferTextureHost()) {
+ // TODO(miko): This code path is taken when WebRenderTextureHost wraps
+ // GPUVideoTextureHost, which wraps BufferTextureHost.
+ // Because this creates additional copies of the texture data, we should not
+ // do this.
+ mWrappedTextureHost->AsBufferTextureHost()->DisableExternalTextures();
+ }
+
+ if (mExternalImageId.isSome()) {
+ // External image id is allocated by mWrappedTextureHost.
+ mWrappedTextureHost->EnsureRenderTexture(Nothing());
+ MOZ_ASSERT(mWrappedTextureHost->mExternalImageId.isSome());
+ auto wrappedId = mWrappedTextureHost->mExternalImageId.ref();
+
+ RefPtr<wr::RenderTextureHost> texture =
+ new wr::RenderTextureHostWrapper(wrappedId);
+ wr::RenderThread::Get()->RegisterExternalImage(
+ wr::AsUint64(mExternalImageId.ref()), texture.forget());
+ }
+
+ if (mPendingSourceProvider) {
+ RefPtr<TextureSourceProvider> provider = mPendingSourceProvider.forget();
+ mWrappedTextureHost->SetTextureSourceProvider(provider);
+ }
+ if (mPendingUpdatedInternal) {
+ mWrappedTextureHost->UpdatedInternal(mPendingIntRegion.ptrOr(nullptr));
+ mPendingIntRegion.reset();
+ mPendingUpdatedInternal = false;
+ }
+ if (mPendingPrepareTextureSource) {
+ mWrappedTextureHost->PrepareTextureSource(*mPendingPrepareTextureSource);
+ mPendingPrepareTextureSource.reset();
+ }
+
+ return mWrappedTextureHost;
+}
+
+bool GPUVideoTextureHost::IsValid() { return !!EnsureWrappedTextureHost(); }
+
+bool GPUVideoTextureHost::Lock() {
+ if (!EnsureWrappedTextureHost()) {
+ return false;
+ }
+ return EnsureWrappedTextureHost()->Lock();
+}
+
+void GPUVideoTextureHost::Unlock() {
+ if (!EnsureWrappedTextureHost()) {
+ return;
+ }
+ EnsureWrappedTextureHost()->Unlock();
+}
+
+void GPUVideoTextureHost::PrepareTextureSource(
+ CompositableTextureSourceRef& aTexture) {
+ if (!EnsureWrappedTextureHost()) {
+ mPendingPrepareTextureSource = Some(aTexture);
+ return;
+ }
+ EnsureWrappedTextureHost()->PrepareTextureSource(aTexture);
+}
+
+bool GPUVideoTextureHost::BindTextureSource(
+ CompositableTextureSourceRef& aTexture) {
+ MOZ_ASSERT(EnsureWrappedTextureHost(), "Image isn't valid yet");
+ if (!EnsureWrappedTextureHost()) {
+ return false;
+ }
+ return EnsureWrappedTextureHost()->BindTextureSource(aTexture);
+}
+
+bool GPUVideoTextureHost::AcquireTextureSource(
+ CompositableTextureSourceRef& aTexture) {
+ MOZ_ASSERT(EnsureWrappedTextureHost(), "Image isn't valid yet");
+ if (!EnsureWrappedTextureHost()) {
+ return false;
+ }
+ return EnsureWrappedTextureHost()->AcquireTextureSource(aTexture);
+}
+
+void GPUVideoTextureHost::SetTextureSourceProvider(
+ TextureSourceProvider* aProvider) {
+ if (!EnsureWrappedTextureHost()) {
+ mPendingSourceProvider = aProvider;
+ return;
+ }
+ EnsureWrappedTextureHost()->SetTextureSourceProvider(aProvider);
+}
+
+gfx::YUVColorSpace GPUVideoTextureHost::GetYUVColorSpace() const {
+ MOZ_ASSERT(mWrappedTextureHost, "Image isn't valid yet");
+ if (!mWrappedTextureHost) {
+ return gfx::YUVColorSpace::UNKNOWN;
+ }
+ return mWrappedTextureHost->GetYUVColorSpace();
+}
+
+gfx::ColorDepth GPUVideoTextureHost::GetColorDepth() const {
+ MOZ_ASSERT(mWrappedTextureHost, "Image isn't valid yet");
+ if (!mWrappedTextureHost) {
+ return TextureHost::GetColorDepth();
+ }
+ return mWrappedTextureHost->GetColorDepth();
+}
+
+gfx::ColorRange GPUVideoTextureHost::GetColorRange() const {
+ MOZ_ASSERT(mWrappedTextureHost, "Image isn't valid yet");
+ if (!mWrappedTextureHost) {
+ return TextureHost::GetColorRange();
+ }
+ return mWrappedTextureHost->GetColorRange();
+}
+
+gfx::IntSize GPUVideoTextureHost::GetSize() const {
+ MOZ_ASSERT(mWrappedTextureHost, "Image isn't valid yet");
+ if (!mWrappedTextureHost) {
+ return gfx::IntSize();
+ }
+ return mWrappedTextureHost->GetSize();
+}
+
+gfx::SurfaceFormat GPUVideoTextureHost::GetFormat() const {
+ MOZ_ASSERT(mWrappedTextureHost, "Image isn't valid yet");
+ if (!mWrappedTextureHost) {
+ return gfx::SurfaceFormat::UNKNOWN;
+ }
+ return mWrappedTextureHost->GetFormat();
+}
+
+bool GPUVideoTextureHost::HasIntermediateBuffer() const {
+ MOZ_ASSERT(mWrappedTextureHost, "Image isn't valid yet");
+ if (!mWrappedTextureHost) {
+ return false;
+ }
+ return mWrappedTextureHost->HasIntermediateBuffer();
+}
+
+void GPUVideoTextureHost::UpdatedInternal(const nsIntRegion* Region) {
+ if (!EnsureWrappedTextureHost()) {
+ mPendingUpdatedInternal = true;
+ if (Region) {
+ mPendingIntRegion = Some(*Region);
+ } else {
+ mPendingIntRegion.reset();
+ }
+ return;
+ }
+ EnsureWrappedTextureHost()->UpdatedInternal(Region);
+}
+
+void GPUVideoTextureHost::CreateRenderTexture(
+ const wr::ExternalImageId& aExternalImageId) {
+ MOZ_ASSERT(mExternalImageId.isSome());
+
+ // When mWrappedTextureHost already exist, call CreateRenderTexture() here.
+ // In other cases, EnsureWrappedTextureHost() handles CreateRenderTexture().
+
+ if (mWrappedTextureHost) {
+ // External image id is allocated by mWrappedTextureHost.
+ mWrappedTextureHost->EnsureRenderTexture(Nothing());
+ MOZ_ASSERT(mWrappedTextureHost->mExternalImageId.isSome());
+ auto wrappedId = mWrappedTextureHost->mExternalImageId.ref();
+
+ RefPtr<wr::RenderTextureHost> texture =
+ new wr::RenderTextureHostWrapper(wrappedId);
+ wr::RenderThread::Get()->RegisterExternalImage(
+ wr::AsUint64(mExternalImageId.ref()), texture.forget());
+ return;
+ }
+
+ EnsureWrappedTextureHost();
+}
+
+void GPUVideoTextureHost::MaybeDestroyRenderTexture() {
+ if (mExternalImageId.isNothing() || !mWrappedTextureHost) {
+ // RenderTextureHost was not created
+ return;
+ }
+ // When GPUVideoTextureHost created RenderTextureHost, delete it here.
+ TextureHost::DestroyRenderTexture(mExternalImageId.ref());
+}
+
+uint32_t GPUVideoTextureHost::NumSubTextures() {
+ if (!EnsureWrappedTextureHost()) {
+ return 0;
+ }
+ return EnsureWrappedTextureHost()->NumSubTextures();
+}
+
+void GPUVideoTextureHost::PushResourceUpdates(
+ wr::TransactionBuilder& aResources, ResourceUpdateOp aOp,
+ const Range<wr::ImageKey>& aImageKeys, const wr::ExternalImageId& aExtID) {
+ MOZ_ASSERT(EnsureWrappedTextureHost(), "Image isn't valid yet");
+ if (!EnsureWrappedTextureHost()) {
+ return;
+ }
+ EnsureWrappedTextureHost()->PushResourceUpdates(aResources, aOp, aImageKeys,
+ aExtID);
+}
+
+void GPUVideoTextureHost::PushDisplayItems(
+ wr::DisplayListBuilder& aBuilder, const wr::LayoutRect& aBounds,
+ const wr::LayoutRect& aClip, wr::ImageRendering aFilter,
+ const Range<wr::ImageKey>& aImageKeys, PushDisplayItemFlagSet aFlags) {
+ MOZ_ASSERT(EnsureWrappedTextureHost(), "Image isn't valid yet");
+ MOZ_ASSERT(aImageKeys.length() > 0);
+ if (!EnsureWrappedTextureHost()) {
+ return;
+ }
+
+ EnsureWrappedTextureHost()->PushDisplayItems(aBuilder, aBounds, aClip,
+ aFilter, aImageKeys, aFlags);
+}
+
+bool GPUVideoTextureHost::SupportsExternalCompositing() {
+ if (!EnsureWrappedTextureHost()) {
+ return false;
+ }
+ return EnsureWrappedTextureHost()->SupportsExternalCompositing();
+}
+
+void GPUVideoTextureHost::UnbindTextureSource() {
+ if (EnsureWrappedTextureHost()) {
+ EnsureWrappedTextureHost()->UnbindTextureSource();
+ }
+ // Handle read unlock
+ TextureHost::UnbindTextureSource();
+}
+
+void GPUVideoTextureHost::NotifyNotUsed() {
+ if (EnsureWrappedTextureHost()) {
+ EnsureWrappedTextureHost()->NotifyNotUsed();
+ }
+ TextureHost::NotifyNotUsed();
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/composite/GPUVideoTextureHost.h b/gfx/layers/composite/GPUVideoTextureHost.h
new file mode 100644
index 0000000000..1346d61ccd
--- /dev/null
+++ b/gfx/layers/composite/GPUVideoTextureHost.h
@@ -0,0 +1,98 @@
+/* -*- 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/. */
+
+#ifndef MOZILLA_GFX_GPUVIDEOTEXTUREHOST_H
+#define MOZILLA_GFX_GPUVIDEOTEXTUREHOST_H
+
+#include "mozilla/layers/TextureHost.h"
+
+namespace mozilla {
+namespace layers {
+
+class GPUVideoTextureHost : public TextureHost {
+ public:
+ static GPUVideoTextureHost* CreateFromDescriptor(
+ TextureFlags aFlags, const SurfaceDescriptorGPUVideo& aDescriptor);
+
+ virtual ~GPUVideoTextureHost();
+
+ void DeallocateDeviceData() override {}
+
+ virtual void SetTextureSourceProvider(
+ TextureSourceProvider* aProvider) override;
+
+ bool Lock() override;
+
+ void Unlock() override;
+
+ gfx::SurfaceFormat GetFormat() const override;
+
+ void PrepareTextureSource(CompositableTextureSourceRef& aTexture) override;
+
+ bool BindTextureSource(CompositableTextureSourceRef& aTexture) override;
+ bool AcquireTextureSource(CompositableTextureSourceRef& aTexture) override;
+
+ already_AddRefed<gfx::DataSourceSurface> GetAsSurface() override {
+ return nullptr; // XXX - implement this (for MOZ_DUMP_PAINTING)
+ }
+
+ gfx::YUVColorSpace GetYUVColorSpace() const override;
+ gfx::ColorDepth GetColorDepth() const override;
+ gfx::ColorRange GetColorRange() const override;
+
+ gfx::IntSize GetSize() const override;
+
+ bool IsValid() override;
+
+#ifdef MOZ_LAYERS_HAVE_LOG
+ const char* Name() override { return "GPUVideoTextureHost"; }
+#endif
+
+ bool HasIntermediateBuffer() const override;
+
+ void CreateRenderTexture(
+ const wr::ExternalImageId& aExternalImageId) override;
+
+ void MaybeDestroyRenderTexture() override;
+
+ uint32_t NumSubTextures() override;
+
+ void PushResourceUpdates(wr::TransactionBuilder& aResources,
+ ResourceUpdateOp aOp,
+ const Range<wr::ImageKey>& aImageKeys,
+ const wr::ExternalImageId& aExtID) override;
+
+ void PushDisplayItems(wr::DisplayListBuilder& aBuilder,
+ const wr::LayoutRect& aBounds,
+ const wr::LayoutRect& aClip, wr::ImageRendering aFilter,
+ const Range<wr::ImageKey>& aImageKeys,
+ PushDisplayItemFlagSet aFlags) override;
+
+ bool SupportsExternalCompositing() override;
+
+ void UnbindTextureSource() override;
+ void NotifyNotUsed() override;
+
+ protected:
+ GPUVideoTextureHost(TextureFlags aFlags,
+ const SurfaceDescriptorGPUVideo& aDescriptor);
+
+ TextureHost* EnsureWrappedTextureHost();
+
+ void UpdatedInternal(const nsIntRegion* Region) override;
+
+ RefPtr<TextureHost> mWrappedTextureHost;
+ RefPtr<TextureSourceProvider> mPendingSourceProvider;
+ bool mPendingUpdatedInternal = false;
+ Maybe<nsIntRegion> mPendingIntRegion;
+ Maybe<CompositableTextureSourceRef> mPendingPrepareTextureSource;
+ SurfaceDescriptorGPUVideo mDescriptor;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // MOZILLA_GFX_GPUVIDEOTEXTUREHOST_H
diff --git a/gfx/layers/composite/ImageComposite.cpp b/gfx/layers/composite/ImageComposite.cpp
new file mode 100644
index 0000000000..20e4ee2b5c
--- /dev/null
+++ b/gfx/layers/composite/ImageComposite.cpp
@@ -0,0 +1,385 @@
+/* -*- 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 "ImageComposite.h"
+
+#include <inttypes.h>
+
+#include "GeckoProfiler.h"
+#include "gfxPlatform.h"
+
+namespace mozilla {
+
+using namespace gfx;
+
+namespace layers {
+
+/* static */ const float ImageComposite::BIAS_TIME_MS = 1.0f;
+
+ImageComposite::ImageComposite() = default;
+
+ImageComposite::~ImageComposite() = default;
+
+TimeStamp ImageComposite::GetBiasedTime(const TimeStamp& aInput) const {
+ switch (mBias) {
+ case ImageComposite::BIAS_NEGATIVE:
+ return aInput - TimeDuration::FromMilliseconds(BIAS_TIME_MS);
+ case ImageComposite::BIAS_POSITIVE:
+ return aInput + TimeDuration::FromMilliseconds(BIAS_TIME_MS);
+ default:
+ return aInput;
+ }
+}
+
+void ImageComposite::UpdateBias(size_t aImageIndex, bool aFrameChanged) {
+ MOZ_ASSERT(aImageIndex < ImagesCount());
+
+ TimeStamp compositionTime = GetCompositionTime();
+ TimeStamp compositedImageTime = mImages[aImageIndex].mTimeStamp;
+ TimeStamp nextImageTime = aImageIndex + 1 < ImagesCount()
+ ? mImages[aImageIndex + 1].mTimeStamp
+ : TimeStamp();
+
+#if MOZ_GECKO_PROFILER
+ if (profiler_can_accept_markers() && compositedImageTime && nextImageTime) {
+ TimeDuration offsetCurrent = compositedImageTime - compositionTime;
+ TimeDuration offsetNext = nextImageTime - compositionTime;
+ nsPrintfCString str("current %.2lfms, next %.2lfms",
+ offsetCurrent.ToMilliseconds(),
+ offsetNext.ToMilliseconds());
+ PROFILER_MARKER_TEXT("Video frame offsets", GRAPHICS, {}, str);
+ }
+#endif
+
+ if (compositedImageTime.IsNull()) {
+ mBias = ImageComposite::BIAS_NONE;
+ return;
+ }
+ TimeDuration threshold = TimeDuration::FromMilliseconds(1.5);
+ if (compositionTime - compositedImageTime < threshold &&
+ compositionTime - compositedImageTime > -threshold) {
+ // The chosen frame's time is very close to the composition time (probably
+ // just before the current composition time, but due to previously set
+ // negative bias, it could be just after the current composition time too).
+ // If the inter-frame time is almost exactly equal to (a multiple of)
+ // the inter-composition time, then we're in a dangerous situation because
+ // jitter might cause frames to fall one side or the other of the
+ // composition times, causing many frames to be skipped or duplicated.
+ // Try to prevent that by adding a negative bias to the frame times during
+ // the next composite; that should ensure the next frame's time is treated
+ // as falling just before a composite time.
+ mBias = ImageComposite::BIAS_NEGATIVE;
+ return;
+ }
+ if (!nextImageTime.IsNull() && nextImageTime - compositionTime < threshold &&
+ nextImageTime - compositionTime > -threshold) {
+ // The next frame's time is very close to our composition time (probably
+ // just after the current composition time, but due to previously set
+ // positive bias, it could be just before the current composition time too).
+ // We're in a dangerous situation because jitter might cause frames to
+ // fall one side or the other of the composition times, causing many frames
+ // to be skipped or duplicated.
+ // Specifically, the next composite is at risk of picking the "next + 1"
+ // frame rather than the "next" frame, which would cause the "next" frame to
+ // be skipped. Try to prevent that by adding a positive bias to the frame
+ // times during the next composite; if the inter-frame time is almost
+ // exactly equal to the inter-composition time, that should ensure that the
+ // next + 1 frame falls just *after* the next composition time, and the next
+ // composite should then pick the next frame rather than the next + 1 frame.
+ mBias = ImageComposite::BIAS_POSITIVE;
+ return;
+ }
+ if (aFrameChanged) {
+ // The current and next video frames are a sufficient distance from the
+ // composition time and we can reliably pick the right frame without bias.
+ // Reset the bias.
+ // We only do this when the frame changed. Otherwise, when playing a 30fps
+ // video on a 60fps display, we'd keep resetting the bias during the "middle
+ // frames".
+ mBias = ImageComposite::BIAS_NONE;
+ }
+}
+
+int ImageComposite::ChooseImageIndex() {
+ // ChooseImageIndex is called for all images in the layer when it is visible.
+ // Change to this behaviour would break dropped frames counting calculation:
+ // We rely on this assumption to determine if during successive runs an
+ // image is returned that isn't the one following immediately the previous one
+ if (mImages.IsEmpty()) {
+ return -1;
+ }
+
+ TimeStamp compositionTime = GetCompositionTime();
+ auto compositionOpportunityId = GetCompositionOpportunityId();
+ if (compositionTime &&
+ compositionOpportunityId != mLastChooseImageIndexComposition) {
+ // We are inside a composition, in the first call to ChooseImageIndex during
+ // this composition.
+ // Find the newest frame whose biased timestamp is at or before
+ // `compositionTime`.
+ uint32_t imageIndex = 0;
+ while (imageIndex + 1 < mImages.Length() &&
+ mImages[imageIndex + 1].mTextureHost->IsValid() &&
+ GetBiasedTime(mImages[imageIndex + 1].mTimeStamp) <=
+ compositionTime) {
+ ++imageIndex;
+ }
+
+ if (!mImages[imageIndex].mTextureHost->IsValid()) {
+ // Still not ready to be shown.
+ return -1;
+ }
+
+ bool wasVisibleAtPreviousComposition =
+ compositionOpportunityId == mLastChooseImageIndexComposition.Next();
+
+ bool frameChanged =
+ UpdateCompositedFrame(imageIndex, wasVisibleAtPreviousComposition);
+ UpdateBias(imageIndex, frameChanged);
+
+ mLastChooseImageIndexComposition = compositionOpportunityId;
+
+ return imageIndex;
+ }
+
+ // We've been called before during this composition, or we're not in a
+ // composition. Just return the last image we picked (if it's one of the
+ // current images).
+ for (uint32_t i = 0; i < mImages.Length(); ++i) {
+ if (mImages[i].mFrameID == mLastFrameID &&
+ mImages[i].mProducerID == mLastProducerID) {
+ return i;
+ }
+ }
+
+ return 0;
+}
+
+const ImageComposite::TimedImage* ImageComposite::ChooseImage() {
+ int index = ChooseImageIndex();
+ return index >= 0 ? &mImages[index] : nullptr;
+}
+
+void ImageComposite::RemoveImagesWithTextureHost(TextureHost* aTexture) {
+ for (int32_t i = mImages.Length() - 1; i >= 0; --i) {
+ if (mImages[i].mTextureHost == aTexture) {
+ aTexture->UnbindTextureSource();
+ mImages.RemoveElementAt(i);
+ }
+ }
+}
+
+void ImageComposite::ClearImages() { mImages.Clear(); }
+
+void ImageComposite::SetImages(nsTArray<TimedImage>&& aNewImages) {
+ if (!aNewImages.IsEmpty()) {
+ DetectTimeStampJitter(&aNewImages[0]);
+
+ // Frames older than the first frame in aNewImages that we haven't shown yet
+ // will never be shown.
+ CountSkippedFrames(&aNewImages[0]);
+
+#if MOZ_GECKO_PROFILER
+ if (profiler_can_accept_markers()) {
+ int len = aNewImages.Length();
+ const auto& first = aNewImages[0];
+ const auto& last = aNewImages.LastElement();
+ nsPrintfCString str("%d %s, frameID %" PRId32 " (prod %" PRId32
+ ") to frameID %" PRId32 " (prod %" PRId32 ")",
+ len, len == 1 ? "image" : "images", first.mFrameID,
+ first.mProducerID, last.mFrameID, last.mProducerID);
+ PROFILER_MARKER_TEXT("ImageComposite::SetImages", GRAPHICS, {}, str);
+ }
+#endif
+ }
+ mImages = std::move(aNewImages);
+}
+
+// Returns whether the frame changed.
+bool ImageComposite::UpdateCompositedFrame(
+ int aImageIndex, bool aWasVisibleAtPreviousComposition) {
+ MOZ_RELEASE_ASSERT(aImageIndex >= 0);
+ MOZ_RELEASE_ASSERT(aImageIndex < static_cast<int>(mImages.Length()));
+ const TimedImage& image = mImages[aImageIndex];
+
+ auto compositionOpportunityId = GetCompositionOpportunityId();
+ TimeStamp compositionTime = GetCompositionTime();
+ MOZ_RELEASE_ASSERT(compositionTime,
+ "Should only be called during a composition");
+
+#if MOZ_GECKO_PROFILER
+ nsCString descr;
+ if (profiler_can_accept_markers()) {
+ nsCString relativeTimeString;
+ if (image.mTimeStamp) {
+ relativeTimeString.AppendPrintf(
+ " [relative timestamp %.1lfms]",
+ (image.mTimeStamp - compositionTime).ToMilliseconds());
+ }
+ int remainingImages = mImages.Length() - 1 - aImageIndex;
+ static const char* kBiasStrings[] = {"NONE", "NEGATIVE", "POSITIVE"};
+ descr.AppendPrintf(
+ "frameID %" PRId32 " (producerID %" PRId32 ") [composite %" PRIu64
+ "] [bias %s] [%d remaining %s]%s",
+ image.mFrameID, image.mProducerID, compositionOpportunityId.mId,
+ kBiasStrings[mBias], remainingImages,
+ remainingImages == 1 ? "image" : "images", relativeTimeString.get());
+ if (mLastProducerID != image.mProducerID) {
+ descr.AppendPrintf(", previous producerID: %" PRId32, mLastProducerID);
+ } else if (mLastFrameID != image.mFrameID) {
+ descr.AppendPrintf(", previous frameID: %" PRId32, mLastFrameID);
+ } else {
+ descr.AppendLiteral(", no change");
+ }
+ }
+ PROFILER_MARKER_TEXT("UpdateCompositedFrame", GRAPHICS, {}, descr);
+#endif
+
+ if (mLastFrameID == image.mFrameID && mLastProducerID == image.mProducerID) {
+ // The frame didn't change.
+ return false;
+ }
+
+ CountSkippedFrames(&image);
+
+ int32_t dropped = mSkippedFramesSinceLastComposite;
+ mSkippedFramesSinceLastComposite = 0;
+
+ if (!aWasVisibleAtPreviousComposition) {
+ // This video was not part of the on-screen scene during the previous
+ // composition opportunity, for example it may have been scrolled off-screen
+ // or in a background tab, or compositing might have been paused.
+ // Ignore any skipped frames and don't count them as dropped.
+ dropped = 0;
+ }
+
+ if (dropped > 0) {
+ mDroppedFrames += dropped;
+#if MOZ_GECKO_PROFILER
+ if (profiler_can_accept_markers()) {
+ const char* frameOrFrames = dropped == 1 ? "frame" : "frames";
+ nsPrintfCString text("%" PRId32 " %s dropped: %" PRId32 " -> %" PRId32
+ " (producer %" PRId32 ")",
+ dropped, frameOrFrames, mLastFrameID, image.mFrameID,
+ mLastProducerID);
+ PROFILER_MARKER_TEXT("Video frames dropped", GRAPHICS, {}, text);
+ }
+#endif
+ }
+
+ mLastFrameID = image.mFrameID;
+ mLastProducerID = image.mProducerID;
+ mLastFrameUpdateComposition = compositionOpportunityId;
+
+ return true;
+}
+
+void ImageComposite::OnFinishRendering(int aImageIndex,
+ const TimedImage* aImage,
+ base::ProcessId aProcessId,
+ const CompositableHandle& aHandle) {
+ if (mLastFrameUpdateComposition != GetCompositionOpportunityId()) {
+ // The frame did not change in this composition.
+ return;
+ }
+
+ if (aHandle) {
+ ImageCompositeNotificationInfo info;
+ info.mImageBridgeProcessId = aProcessId;
+ info.mNotification = ImageCompositeNotification(
+ aHandle, aImage->mTimeStamp, GetCompositionTime(), mLastFrameID,
+ mLastProducerID);
+ AppendImageCompositeNotification(info);
+ }
+}
+
+const ImageComposite::TimedImage* ImageComposite::GetImage(
+ size_t aIndex) const {
+ if (aIndex >= mImages.Length()) {
+ return nullptr;
+ }
+ return &mImages[aIndex];
+}
+
+void ImageComposite::CountSkippedFrames(const TimedImage* aImage) {
+ if (aImage->mProducerID != mLastProducerID) {
+ // Switched producers.
+ return;
+ }
+
+ if (mImages.IsEmpty() || aImage->mFrameID <= mLastFrameID + 1) {
+ // No frames were skipped.
+ return;
+ }
+
+ uint32_t targetFrameRate = gfxPlatform::TargetFrameRate();
+ if (targetFrameRate == 0) {
+ // Can't know whether we could have reasonably displayed all video frames.
+ return;
+ }
+
+ double targetFrameDurationMS = 1000.0 / targetFrameRate;
+
+ // Count how many images in mImages were skipped between mLastFrameID and
+ // aImage.mFrameID. Only count frames for which we can estimate a duration by
+ // looking at the next frame's timestamp, and only if the video frame rate is
+ // no faster than the target frame rate.
+ int32_t skipped = 0;
+ for (size_t i = 0; i + 1 < mImages.Length(); i++) {
+ const auto& img = mImages[i];
+ if (img.mProducerID != aImage->mProducerID ||
+ img.mFrameID <= mLastFrameID || img.mFrameID >= aImage->mFrameID) {
+ continue;
+ }
+
+ // We skipped img! Estimate img's time duration.
+ const auto& next = mImages[i + 1];
+ if (next.mProducerID != aImage->mProducerID) {
+ continue;
+ }
+
+ MOZ_ASSERT(next.mFrameID > img.mFrameID);
+ TimeDuration duration = next.mTimeStamp - img.mTimeStamp;
+ if (floor(duration.ToMilliseconds()) >= floor(targetFrameDurationMS)) {
+ // Count the frame.
+ skipped++;
+ }
+ }
+
+ mSkippedFramesSinceLastComposite += skipped;
+}
+
+void ImageComposite::DetectTimeStampJitter(const TimedImage* aNewImage) {
+#if MOZ_GECKO_PROFILER
+ if (!profiler_can_accept_markers() || aNewImage->mTimeStamp.IsNull()) {
+ return;
+ }
+
+ // Find aNewImage in mImages and compute its timestamp delta, if found.
+ // Ideally, a given video frame should never change its timestamp (jitter
+ // should be zero). However, we re-adjust video frame timestamps based on the
+ // audio clock. If the audio clock drifts compared to the system clock, or if
+ // there are bugs or inaccuracies in the computation of these timestamps,
+ // jitter will be non-zero.
+ Maybe<TimeDuration> jitter;
+ for (const auto& img : mImages) {
+ if (img.mProducerID == aNewImage->mProducerID &&
+ img.mFrameID == aNewImage->mFrameID) {
+ if (!img.mTimeStamp.IsNull()) {
+ jitter = Some(aNewImage->mTimeStamp - img.mTimeStamp);
+ }
+ break;
+ }
+ }
+ if (jitter) {
+ nsPrintfCString text("%.2lfms", jitter->ToMilliseconds());
+ PROFILER_MARKER_TEXT("VideoFrameTimeStampJitter", GRAPHICS, {}, text);
+ }
+#endif
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/composite/ImageComposite.h b/gfx/layers/composite/ImageComposite.h
new file mode 100644
index 0000000000..05e812c8b3
--- /dev/null
+++ b/gfx/layers/composite/ImageComposite.h
@@ -0,0 +1,139 @@
+/* -*- 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/. */
+
+#ifndef MOZILLA_GFX_IMAGECOMPOSITE_H
+#define MOZILLA_GFX_IMAGECOMPOSITE_H
+
+#include "CompositableHost.h" // for CompositableTextureHostRef
+#include "mozilla/gfx/2D.h"
+#include "mozilla/TimeStamp.h" // for TimeStamp
+#include "nsTArray.h"
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * Implements Image selection logic.
+ */
+class ImageComposite {
+ public:
+ static const float BIAS_TIME_MS;
+
+ explicit ImageComposite();
+ virtual ~ImageComposite();
+
+ int32_t GetFrameID() {
+ const TimedImage* img = ChooseImage();
+ return img ? img->mFrameID : -1;
+ }
+
+ int32_t GetProducerID() {
+ const TimedImage* img = ChooseImage();
+ return img ? img->mProducerID : -1;
+ }
+
+ int32_t GetLastFrameID() const { return mLastFrameID; }
+ int32_t GetLastProducerID() const { return mLastProducerID; }
+ uint32_t GetDroppedFramesAndReset() {
+ uint32_t dropped = mDroppedFrames;
+ mDroppedFrames = 0;
+ return dropped;
+ }
+
+ enum Bias {
+ // Don't apply bias to frame times
+ BIAS_NONE,
+ // Apply a negative bias to frame times to keep them before the vsync time
+ BIAS_NEGATIVE,
+ // Apply a positive bias to frame times to keep them after the vsync time
+ BIAS_POSITIVE,
+ };
+
+ protected:
+ virtual TimeStamp GetCompositionTime() const = 0;
+ virtual CompositionOpportunityId GetCompositionOpportunityId() const = 0;
+ virtual void AppendImageCompositeNotification(
+ const ImageCompositeNotificationInfo& aInfo) const = 0;
+
+ struct TimedImage {
+ CompositableTextureHostRef mTextureHost;
+ TimeStamp mTimeStamp;
+ gfx::IntRect mPictureRect;
+ int32_t mFrameID;
+ int32_t mProducerID;
+ };
+
+ /**
+ * ChooseImage is guaranteed to return the same TimedImage every time it's
+ * called during the same composition, up to the end of Composite() ---
+ * it depends only on mImages, mCompositor->GetCompositionTime(), and mBias.
+ * mBias is updated at the end of Composite().
+ */
+ const TimedImage* ChooseImage();
+ int ChooseImageIndex();
+ const TimedImage* GetImage(size_t aIndex) const;
+ size_t ImagesCount() const { return mImages.Length(); }
+ const nsTArray<TimedImage>& Images() const { return mImages; }
+
+ void RemoveImagesWithTextureHost(TextureHost* aTexture);
+ void ClearImages();
+ void SetImages(nsTArray<TimedImage>&& aNewImages);
+
+ protected:
+ // Send ImageComposite notifications and update the ChooseImage bias.
+ void OnFinishRendering(int aImageIndex, const TimedImage* aImage,
+ base::ProcessId aProcessId,
+ const CompositableHandle& aHandle);
+
+ int32_t mLastFrameID = -1;
+ int32_t mLastProducerID = -1;
+
+ private:
+ nsTArray<TimedImage> mImages;
+ TimeStamp GetBiasedTime(const TimeStamp& aInput) const;
+
+ // Called when we know that frames older than aImage will never get another
+ // chance to be displayed.
+ // Counts frames in mImages that were skipped, and adds the skipped count to
+ // mSkippedFramesSinceLastComposite.
+ void CountSkippedFrames(const TimedImage* aImage);
+
+ // Update mLastFrameID and mLastProducerID, and report dropped frames.
+ // Returns whether the frame changed.
+ bool UpdateCompositedFrame(int aImageIndex,
+ bool aWasVisibleAtPreviousComposition);
+
+ void UpdateBias(size_t aImageIndex, bool aFrameUpdated);
+
+ // Emit a profiler marker about video frame timestamp jitter.
+ void DetectTimeStampJitter(const TimedImage* aNewImage);
+
+ /**
+ * Bias to apply to the next frame.
+ */
+ Bias mBias = BIAS_NONE;
+
+ // Video frames that were in mImages but that will never reach the screen.
+ // This count is reset in UpdateCompositedFrame.
+ int32_t mSkippedFramesSinceLastComposite = 0;
+
+ // The number of dropped frames since the last call to
+ // GetDroppedFramesAndReset(). Not all skipped frames are considered "dropped"
+ // - for example, videos that are scrolled out of view or videos in background
+ // tabs are expected to skip frames, and those skipped frames are not counted
+ // as dropped frames.
+ uint32_t mDroppedFrames = 0;
+
+ // The composition opportunity IDs of the last calls to ChooseImageIndex and
+ // UpdateCompositedFrame, respectively.
+ CompositionOpportunityId mLastChooseImageIndexComposition;
+ CompositionOpportunityId mLastFrameUpdateComposition;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // MOZILLA_GFX_IMAGECOMPOSITE_H
diff --git a/gfx/layers/composite/ImageHost.cpp b/gfx/layers/composite/ImageHost.cpp
new file mode 100644
index 0000000000..3c23dc432b
--- /dev/null
+++ b/gfx/layers/composite/ImageHost.cpp
@@ -0,0 +1,454 @@
+/* -*- 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 "ImageHost.h"
+
+#include <utility>
+
+#include "composite/CompositableHost.h" // for CompositableHost, etc
+#include "ipc/IPCMessageUtils.h" // for null_t
+#include "mozilla/layers/Compositor.h" // for Compositor
+#include "mozilla/layers/Effects.h" // for TexturedEffect, Effect, etc
+#include "mozilla/layers/LayerManagerComposite.h" // for TexturedEffect, Effect, etc
+#include "nsAString.h"
+#include "nsDebug.h" // for NS_WARNING, NS_ASSERTION
+#include "nsPrintfCString.h" // for nsPrintfCString
+#include "nsString.h" // for nsAutoCString
+
+namespace mozilla {
+
+using namespace gfx;
+
+namespace layers {
+
+class ISurfaceAllocator;
+
+ImageHost::ImageHost(const TextureInfo& aTextureInfo)
+ : CompositableHost(aTextureInfo), ImageComposite(), mLocked(false) {}
+
+ImageHost::~ImageHost() = default;
+
+void ImageHost::UseTextureHost(const nsTArray<TimedTexture>& aTextures) {
+ MOZ_ASSERT(!mLocked);
+
+ CompositableHost::UseTextureHost(aTextures);
+ MOZ_ASSERT(aTextures.Length() >= 1);
+
+ nsTArray<TimedImage> newImages;
+
+ for (uint32_t i = 0; i < aTextures.Length(); ++i) {
+ const TimedTexture& t = aTextures[i];
+ MOZ_ASSERT(t.mTexture);
+ if (i + 1 < aTextures.Length() && t.mProducerID == mLastProducerID &&
+ t.mFrameID < mLastFrameID) {
+ // Ignore frames before a frame that we already composited. We don't
+ // ever want to display these frames. This could be important if
+ // the frame producer adjusts timestamps (e.g. to track the audio clock)
+ // and the new frame times are earlier.
+ continue;
+ }
+ TimedImage& img = *newImages.AppendElement();
+ img.mTextureHost = t.mTexture;
+ img.mTimeStamp = t.mTimeStamp;
+ img.mPictureRect = t.mPictureRect;
+ img.mFrameID = t.mFrameID;
+ img.mProducerID = t.mProducerID;
+ img.mTextureHost->SetCropRect(img.mPictureRect);
+ img.mTextureHost->Updated();
+ }
+
+ SetImages(std::move(newImages));
+
+ // If we only have one image we can upload it right away, otherwise we'll
+ // upload on-demand during composition after we have picked the proper
+ // timestamp.
+ if (ImagesCount() == 1) {
+ SetCurrentTextureHost(GetImage(0)->mTextureHost);
+ }
+
+ HostLayerManager* lm = GetLayerManager();
+
+ // Video producers generally send replacement images with the same frameID but
+ // slightly different timestamps in order to sync with the audio clock. This
+ // means that any CompositeUntil() call we made in Composite() may no longer
+ // guarantee that we'll composite until the next frame is ready. Fix that
+ // here.
+ if (lm && mLastFrameID >= 0) {
+ for (const auto& img : Images()) {
+ bool frameComesAfter =
+ img.mFrameID > mLastFrameID || img.mProducerID != mLastProducerID;
+ if (frameComesAfter && !img.mTimeStamp.IsNull()) {
+ lm->CompositeUntil(img.mTimeStamp +
+ TimeDuration::FromMilliseconds(BIAS_TIME_MS));
+ break;
+ }
+ }
+ }
+}
+
+void ImageHost::SetCurrentTextureHost(TextureHost* aTexture) {
+ if (aTexture == mCurrentTextureHost.get()) {
+ return;
+ }
+
+ bool swapTextureSources = !!mCurrentTextureHost && !!mCurrentTextureSource &&
+ mCurrentTextureHost->IsValid() &&
+ mCurrentTextureHost->HasIntermediateBuffer();
+
+ if (swapTextureSources) {
+ auto dataSource = mCurrentTextureSource->AsDataTextureSource();
+ if (dataSource) {
+ // The current textureHost has an internal buffer in the form of the
+ // DataTextureSource. Removing the ownership of the texture source
+ // will enable the next texture host we bind to the texture source to
+ // acquire it instead of creating a new one. This is desirable in
+ // ImageHost because the current texture won't be used again with the
+ // same content. It wouldn't be desirable with ContentHost for instance,
+ // because the latter reuses the texture's valid regions.
+ dataSource->SetOwner(nullptr);
+ }
+
+ RefPtr<TextureSource> tmp = mExtraTextureSource;
+ mExtraTextureSource = mCurrentTextureSource.get();
+ mCurrentTextureSource = tmp;
+ } else {
+ mExtraTextureSource = nullptr;
+ }
+
+ mCurrentTextureHost = aTexture;
+ mCurrentTextureHost->PrepareTextureSource(mCurrentTextureSource);
+}
+
+void ImageHost::CleanupResources() {
+ mExtraTextureSource = nullptr;
+ mCurrentTextureSource = nullptr;
+ mCurrentTextureHost = nullptr;
+}
+
+void ImageHost::RemoveTextureHost(TextureHost* aTexture) {
+ MOZ_ASSERT(!mLocked);
+
+ CompositableHost::RemoveTextureHost(aTexture);
+ RemoveImagesWithTextureHost(aTexture);
+}
+
+TimeStamp ImageHost::GetCompositionTime() const {
+ TimeStamp time;
+ if (HostLayerManager* lm = GetLayerManager()) {
+ time = lm->GetCompositionTime();
+ }
+ return time;
+}
+
+CompositionOpportunityId ImageHost::GetCompositionOpportunityId() const {
+ CompositionOpportunityId id;
+ if (HostLayerManager* lm = GetLayerManager()) {
+ id = lm->GetCompositionOpportunityId();
+ }
+ return id;
+}
+
+void ImageHost::AppendImageCompositeNotification(
+ const ImageCompositeNotificationInfo& aInfo) const {
+ if (HostLayerManager* lm = GetLayerManager()) {
+ lm->AppendImageCompositeNotification(aInfo);
+ }
+}
+
+TextureHost* ImageHost::GetAsTextureHost(IntRect* aPictureRect) {
+ const TimedImage* img = ChooseImage();
+ if (!img) {
+ return nullptr;
+ }
+ SetCurrentTextureHost(img->mTextureHost);
+ if (aPictureRect) {
+ *aPictureRect = img->mPictureRect;
+ }
+ return img->mTextureHost;
+}
+
+void ImageHost::Attach(Layer* aLayer, TextureSourceProvider* aProvider,
+ AttachFlags aFlags) {
+ CompositableHost::Attach(aLayer, aProvider, aFlags);
+ for (const auto& img : Images()) {
+ img.mTextureHost->SetTextureSourceProvider(aProvider);
+ img.mTextureHost->Updated();
+ }
+}
+
+void ImageHost::Composite(Compositor* aCompositor, LayerComposite* aLayer,
+ EffectChain& aEffectChain, float aOpacity,
+ const gfx::Matrix4x4& aTransform,
+ const gfx::SamplingFilter aSamplingFilter,
+ const gfx::IntRect& aClipRect,
+ const nsIntRegion* aVisibleRegion,
+ const Maybe<gfx::Polygon>& aGeometry) {
+ RenderInfo info;
+ if (!PrepareToRender(aCompositor, &info)) {
+ return;
+ }
+
+ const TimedImage* img = info.img;
+
+ {
+ AutoLockCompositableHost autoLock(this);
+ if (autoLock.Failed()) {
+ NS_WARNING("failed to lock front buffer");
+ return;
+ }
+
+ if (!mCurrentTextureHost->BindTextureSource(mCurrentTextureSource)) {
+ return;
+ }
+
+ if (!mCurrentTextureSource) {
+ // BindTextureSource above should have returned false!
+ MOZ_ASSERT(false);
+ return;
+ }
+
+ bool isAlphaPremultiplied =
+ !(mCurrentTextureHost->GetFlags() & TextureFlags::NON_PREMULTIPLIED);
+ RefPtr<TexturedEffect> effect =
+ CreateTexturedEffect(mCurrentTextureHost, mCurrentTextureSource.get(),
+ aSamplingFilter, isAlphaPremultiplied);
+ if (!effect) {
+ return;
+ }
+
+ if (!aCompositor->SupportsEffect(effect->mType)) {
+ return;
+ }
+
+ DiagnosticFlags diagnosticFlags = DiagnosticFlags::IMAGE;
+ if (effect->mType == EffectTypes::NV12) {
+ diagnosticFlags |= DiagnosticFlags::NV12;
+ } else if (effect->mType == EffectTypes::YCBCR) {
+ diagnosticFlags |= DiagnosticFlags::YCBCR;
+ }
+
+ aEffectChain.mPrimaryEffect = effect;
+ gfx::Rect pictureRect(0, 0, img->mPictureRect.Width(),
+ img->mPictureRect.Height());
+ BigImageIterator* it = mCurrentTextureSource->AsBigImageIterator();
+ if (it) {
+ // This iteration does not work if we have multiple texture sources here
+ // (e.g. 3 YCbCr textures). There's nothing preventing the different
+ // planes from having different resolutions or tile sizes. For example, a
+ // YCbCr frame could have Cb and Cr planes that are half the resolution of
+ // the Y plane, in such a way that the Y plane overflows the maximum
+ // texture size and the Cb and Cr planes do not. Then the Y plane would be
+ // split into multiple tiles and the Cb and Cr planes would just be one
+ // tile each.
+ // To handle the general case correctly, we'd have to create a grid of
+ // intersected tiles over all planes, and then draw each grid tile using
+ // the corresponding source tiles from all planes, with appropriate
+ // per-plane per-tile texture coords.
+ // DrawQuad currently assumes that all planes use the same texture coords.
+ MOZ_ASSERT(
+ it->GetTileCount() == 1 || !mCurrentTextureSource->GetNextSibling(),
+ "Can't handle multi-plane BigImages");
+
+ it->BeginBigImageIteration();
+ do {
+ IntRect tileRect = it->GetTileRect();
+ gfx::Rect rect(tileRect.X(), tileRect.Y(), tileRect.Width(),
+ tileRect.Height());
+ rect = rect.Intersect(pictureRect);
+ effect->mTextureCoords =
+ Rect(Float(rect.X() - tileRect.X()) / tileRect.Width(),
+ Float(rect.Y() - tileRect.Y()) / tileRect.Height(),
+ Float(rect.Width()) / tileRect.Width(),
+ Float(rect.Height()) / tileRect.Height());
+ if (img->mTextureHost->GetFlags() & TextureFlags::ORIGIN_BOTTOM_LEFT) {
+ effect->mTextureCoords.SetRectY(effect->mTextureCoords.YMost(),
+ -effect->mTextureCoords.Height());
+ }
+ aCompositor->DrawGeometry(rect, aClipRect, aEffectChain, aOpacity,
+ aTransform, aGeometry);
+ aCompositor->DrawDiagnostics(
+ diagnosticFlags | DiagnosticFlags::BIGIMAGE, rect, aClipRect,
+ aTransform, mFlashCounter);
+ } while (it->NextTile());
+ it->EndBigImageIteration();
+ // layer border
+ aCompositor->DrawDiagnostics(diagnosticFlags, pictureRect, aClipRect,
+ aTransform, mFlashCounter);
+ } else {
+ IntSize textureSize = mCurrentTextureSource->GetSize();
+ effect->mTextureCoords =
+ Rect(Float(img->mPictureRect.X()) / textureSize.width,
+ Float(img->mPictureRect.Y()) / textureSize.height,
+ Float(img->mPictureRect.Width()) / textureSize.width,
+ Float(img->mPictureRect.Height()) / textureSize.height);
+
+ if (img->mTextureHost->GetFlags() & TextureFlags::ORIGIN_BOTTOM_LEFT) {
+ effect->mTextureCoords.SetRectY(effect->mTextureCoords.YMost(),
+ -effect->mTextureCoords.Height());
+ }
+
+ aCompositor->DrawGeometry(pictureRect, aClipRect, aEffectChain, aOpacity,
+ aTransform, aGeometry);
+ aCompositor->DrawDiagnostics(diagnosticFlags, pictureRect, aClipRect,
+ aTransform, mFlashCounter);
+ }
+ }
+
+ FinishRendering(info);
+}
+
+bool ImageHost::PrepareToRender(TextureSourceProvider* aProvider,
+ RenderInfo* aOutInfo) {
+ HostLayerManager* lm = GetLayerManager();
+ if (!lm) {
+ return false;
+ }
+
+ int imageIndex = ChooseImageIndex();
+ if (imageIndex < 0) {
+ return false;
+ }
+
+ if (uint32_t(imageIndex) + 1 < ImagesCount()) {
+ lm->CompositeUntil(GetImage(imageIndex + 1)->mTimeStamp +
+ TimeDuration::FromMilliseconds(BIAS_TIME_MS));
+ }
+
+ const TimedImage* img = GetImage(imageIndex);
+ img->mTextureHost->SetTextureSourceProvider(aProvider);
+ SetCurrentTextureHost(img->mTextureHost);
+
+ aOutInfo->imageIndex = imageIndex;
+ aOutInfo->img = img;
+ aOutInfo->host = mCurrentTextureHost;
+ return true;
+}
+
+RefPtr<TextureSource> ImageHost::AcquireTextureSource(const RenderInfo& aInfo) {
+ MOZ_ASSERT(aInfo.host == mCurrentTextureHost);
+ if (!aInfo.host->AcquireTextureSource(mCurrentTextureSource)) {
+ return nullptr;
+ }
+ return mCurrentTextureSource.get();
+}
+
+void ImageHost::FinishRendering(const RenderInfo& aInfo) {
+ OnFinishRendering(aInfo.imageIndex, aInfo.img, mAsyncRef.mProcessId,
+ mAsyncRef.mHandle);
+}
+
+void ImageHost::SetTextureSourceProvider(TextureSourceProvider* aProvider) {
+ if (mTextureSourceProvider != aProvider) {
+ for (const auto& img : Images()) {
+ img.mTextureHost->SetTextureSourceProvider(aProvider);
+ }
+ }
+ CompositableHost::SetTextureSourceProvider(aProvider);
+}
+
+void ImageHost::PrintInfo(std::stringstream& aStream, const char* aPrefix) {
+ aStream << aPrefix;
+ aStream << nsPrintfCString("ImageHost (0x%p)", this).get();
+
+ nsAutoCString pfx(aPrefix);
+ pfx += " ";
+ for (const auto& img : Images()) {
+ aStream << "\n";
+ img.mTextureHost->PrintInfo(aStream, pfx.get());
+ aStream << " [picture-rect=" << img.mPictureRect << "]";
+ }
+}
+
+void ImageHost::Dump(std::stringstream& aStream, const char* aPrefix,
+ bool aDumpHtml) {
+ for (const auto& img : Images()) {
+ aStream << aPrefix;
+ aStream << (aDumpHtml ? "<ul><li>TextureHost: " : "TextureHost: ");
+ DumpTextureHost(aStream, img.mTextureHost);
+ aStream << (aDumpHtml ? " </li></ul> " : " ");
+ }
+}
+
+already_AddRefed<gfx::DataSourceSurface> ImageHost::GetAsSurface() {
+ const TimedImage* img = ChooseImage();
+ if (img) {
+ return img->mTextureHost->GetAsSurface();
+ }
+ return nullptr;
+}
+
+bool ImageHost::Lock() {
+ MOZ_ASSERT(!mLocked);
+ const TimedImage* img = ChooseImage();
+ if (!img) {
+ return false;
+ }
+
+ SetCurrentTextureHost(img->mTextureHost);
+
+ if (!mCurrentTextureHost->Lock()) {
+ return false;
+ }
+ mLocked = true;
+ return true;
+}
+
+void ImageHost::Unlock() {
+ MOZ_ASSERT(mLocked);
+
+ if (mCurrentTextureHost) {
+ mCurrentTextureHost->Unlock();
+ }
+ mLocked = false;
+}
+
+IntSize ImageHost::GetImageSize() {
+ const TimedImage* img = ChooseImage();
+ if (img) {
+ return IntSize(img->mPictureRect.Width(), img->mPictureRect.Height());
+ }
+ return IntSize();
+}
+
+bool ImageHost::IsOpaque() {
+ const TimedImage* img = ChooseImage();
+ if (!img) {
+ return false;
+ }
+
+ if (img->mPictureRect.Width() == 0 || img->mPictureRect.Height() == 0 ||
+ !img->mTextureHost) {
+ return false;
+ }
+
+ gfx::SurfaceFormat format = img->mTextureHost->GetFormat();
+ if (gfx::IsOpaque(format)) {
+ return true;
+ }
+ return false;
+}
+
+already_AddRefed<TexturedEffect> ImageHost::GenEffect(
+ const gfx::SamplingFilter aSamplingFilter) {
+ const TimedImage* img = ChooseImage();
+ if (!img) {
+ return nullptr;
+ }
+ SetCurrentTextureHost(img->mTextureHost);
+ if (!mCurrentTextureHost->BindTextureSource(mCurrentTextureSource)) {
+ return nullptr;
+ }
+ bool isAlphaPremultiplied = true;
+ if (mCurrentTextureHost->GetFlags() & TextureFlags::NON_PREMULTIPLIED) {
+ isAlphaPremultiplied = false;
+ }
+
+ return CreateTexturedEffect(mCurrentTextureHost, mCurrentTextureSource,
+ aSamplingFilter, isAlphaPremultiplied);
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/composite/ImageHost.h b/gfx/layers/composite/ImageHost.h
new file mode 100644
index 0000000000..5d712d5bdd
--- /dev/null
+++ b/gfx/layers/composite/ImageHost.h
@@ -0,0 +1,138 @@
+/* -*- 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/. */
+
+#ifndef MOZILLA_GFX_IMAGEHOST_H
+#define MOZILLA_GFX_IMAGEHOST_H
+
+#include <stdio.h> // for FILE
+#include "CompositableHost.h" // for CompositableHost
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/RefPtr.h" // for RefPtr
+#include "mozilla/gfx/MatrixFwd.h" // for Matrix4x4
+#include "mozilla/gfx/Point.h" // for Point
+#include "mozilla/gfx/Polygon.h" // for Polygon
+#include "mozilla/gfx/Rect.h" // for Rect
+#include "mozilla/gfx/Types.h" // for SamplingFilter
+#include "mozilla/layers/CompositorTypes.h" // for TextureInfo, etc
+#include "mozilla/layers/ImageComposite.h" // for ImageComposite
+#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor
+#include "mozilla/layers/LayersTypes.h" // for LayerRenderState, etc
+#include "mozilla/layers/TextureHost.h" // for TextureHost, etc
+#include "mozilla/mozalloc.h" // for operator delete
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsRect.h" // for mozilla::gfx::IntRect
+#include "nsRegionFwd.h" // for nsIntRegion
+#include "nscore.h" // for nsACString
+
+namespace mozilla {
+namespace layers {
+
+class Compositor;
+struct EffectChain;
+class HostLayerManager;
+
+/**
+ * ImageHost. Works with ImageClientSingle and ImageClientBuffered
+ */
+class ImageHost : public CompositableHost, public ImageComposite {
+ public:
+ explicit ImageHost(const TextureInfo& aTextureInfo);
+ virtual ~ImageHost();
+
+ CompositableType GetType() override { return mTextureInfo.mCompositableType; }
+ ImageHost* AsImageHost() override { return this; }
+
+ void Composite(Compositor* aCompositor, LayerComposite* aLayer,
+ EffectChain& aEffectChain, float aOpacity,
+ const gfx::Matrix4x4& aTransform,
+ const gfx::SamplingFilter aSamplingFilter,
+ const gfx::IntRect& aClipRect,
+ const nsIntRegion* aVisibleRegion = nullptr,
+ const Maybe<gfx::Polygon>& aGeometry = Nothing()) override;
+
+ void UseTextureHost(const nsTArray<TimedTexture>& aTextures) override;
+
+ void RemoveTextureHost(TextureHost* aTexture) override;
+
+ TextureHost* GetAsTextureHost(gfx::IntRect* aPictureRect = nullptr) override;
+
+ void Attach(Layer* aLayer, TextureSourceProvider* aProvider,
+ AttachFlags aFlags = NO_FLAGS) override;
+
+ void SetTextureSourceProvider(TextureSourceProvider* aProvider) override;
+
+ gfx::IntSize GetImageSize() override;
+
+ void PrintInfo(std::stringstream& aStream, const char* aPrefix) override;
+
+ void Dump(std::stringstream& aStream, const char* aPrefix = "",
+ bool aDumpHtml = false) override;
+
+ already_AddRefed<gfx::DataSourceSurface> GetAsSurface() override;
+
+ bool Lock() override;
+
+ void Unlock() override;
+
+ already_AddRefed<TexturedEffect> GenEffect(
+ const gfx::SamplingFilter aSamplingFilter) override;
+
+ void SetCurrentTextureHost(TextureHost* aTexture);
+
+ void CleanupResources() override;
+
+ bool IsOpaque();
+
+ uint32_t GetDroppedFrames() override { return GetDroppedFramesAndReset(); }
+
+ struct RenderInfo {
+ int imageIndex;
+ const TimedImage* img;
+ RefPtr<TextureHost> host;
+
+ RenderInfo() : imageIndex(-1), img(nullptr) {}
+ };
+
+ // Acquire rendering information for the current frame.
+ bool PrepareToRender(TextureSourceProvider* aProvider, RenderInfo* aOutInfo);
+
+ // Acquire the TextureSource for the currently prepared frame.
+ RefPtr<TextureSource> AcquireTextureSource(const RenderInfo& aInfo);
+
+ // Send ImageComposite notifications.
+ void FinishRendering(const RenderInfo& aInfo);
+
+ // This should only be called inside a lock, or during rendering. It is
+ // infallible to enforce this.
+ TextureHost* CurrentTextureHost() const {
+ MOZ_ASSERT(mCurrentTextureHost);
+ return mCurrentTextureHost;
+ }
+
+ protected:
+ // ImageComposite
+ TimeStamp GetCompositionTime() const override;
+ CompositionOpportunityId GetCompositionOpportunityId() const override;
+ void AppendImageCompositeNotification(
+ const ImageCompositeNotificationInfo& aInfo) const override;
+
+ // Use a simple RefPtr because the same texture is already held by a
+ // a CompositableTextureHostRef in the array of TimedImage.
+ // See the comment in CompositableTextureRef for more details.
+ RefPtr<TextureHost> mCurrentTextureHost;
+ CompositableTextureSourceRef mCurrentTextureSource;
+ // When doing texture uploads it's best to alternate between two (or three)
+ // texture sources so that the texture we upload to isn't being used by
+ // the GPU to composite the previous frame.
+ RefPtr<TextureSource> mExtraTextureSource;
+
+ bool mLocked;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/composite/ImageLayerComposite.cpp b/gfx/layers/composite/ImageLayerComposite.cpp
new file mode 100644
index 0000000000..9fab48e280
--- /dev/null
+++ b/gfx/layers/composite/ImageLayerComposite.cpp
@@ -0,0 +1,209 @@
+/* -*- 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 "ImageLayerComposite.h"
+#include "CompositableHost.h" // for CompositableHost
+#include "Layers.h" // for WriteSnapshotToDumpFile, etc
+#include "gfx2DGlue.h" // for ToFilter
+#include "gfxEnv.h" // for gfxEnv
+#include "gfxRect.h" // for gfxRect
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/gfx/Matrix.h" // for Matrix4x4
+#include "mozilla/gfx/Point.h" // for IntSize, Point
+#include "mozilla/gfx/Rect.h" // for Rect
+#include "mozilla/layers/Compositor.h" // for Compositor
+#include "mozilla/layers/Effects.h" // for EffectChain
+#include "mozilla/layers/ImageHost.h" // for ImageHost
+#include "mozilla/layers/LayerManagerCompositeUtils.h"
+#include "mozilla/layers/TextureHost.h" // for TextureHost, etc
+#include "mozilla/mozalloc.h" // for operator delete
+#include "nsAString.h"
+#include "mozilla/RefPtr.h" // for nsRefPtr
+#include "nsDebug.h" // for NS_ASSERTION
+#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
+#include "nsString.h" // for nsAutoCString
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::gfx;
+
+ImageLayerComposite::ImageLayerComposite(LayerManagerComposite* aManager)
+ : ImageLayer(aManager, nullptr),
+ LayerComposite(aManager),
+ mImageHost(nullptr) {
+ MOZ_COUNT_CTOR(ImageLayerComposite);
+ mImplData = static_cast<LayerComposite*>(this);
+}
+
+ImageLayerComposite::~ImageLayerComposite() {
+ MOZ_COUNT_DTOR(ImageLayerComposite);
+ MOZ_ASSERT(mDestroyed);
+
+ CleanupResources();
+}
+
+bool ImageLayerComposite::SetCompositableHost(CompositableHost* aHost) {
+ switch (aHost->GetType()) {
+ case CompositableType::IMAGE: {
+ ImageHost* newImageHost = static_cast<ImageHost*>(aHost);
+ if (mImageHost && newImageHost != mImageHost) {
+ mImageHost->Detach(this);
+ }
+ mImageHost = newImageHost;
+ return true;
+ }
+ default:
+ return false;
+ }
+}
+
+void ImageLayerComposite::Disconnect() { Destroy(); }
+
+Layer* ImageLayerComposite::GetLayer() { return this; }
+
+void ImageLayerComposite::SetLayerManager(HostLayerManager* aManager) {
+ LayerComposite::SetLayerManager(aManager);
+ mManager = aManager;
+ if (mImageHost) {
+ mImageHost->SetTextureSourceProvider(mCompositor);
+ if (aManager && mImageHost->GetAsyncRef()) {
+ mImageHost->SetCompositorBridgeID(aManager->GetCompositorBridgeID());
+ }
+ }
+}
+
+void ImageLayerComposite::RenderLayer(const IntRect& aClipRect,
+ const Maybe<gfx::Polygon>& aGeometry) {
+ if (!mImageHost || !mImageHost->IsAttached()) {
+ return;
+ }
+
+#ifdef MOZ_DUMP_PAINTING
+ if (gfxEnv::DumpCompositorTextures()) {
+ RefPtr<gfx::DataSourceSurface> surf = mImageHost->GetAsSurface();
+ if (surf) {
+ WriteSnapshotToDumpFile(this, surf);
+ }
+ }
+#endif
+
+ mCompositor->MakeCurrent();
+
+ RenderWithAllMasks(this, mCompositor, aClipRect,
+ [&](EffectChain& effectChain, const IntRect& clipRect) {
+ mImageHost->SetTextureSourceProvider(mCompositor);
+ mImageHost->Composite(mCompositor, this, effectChain,
+ GetEffectiveOpacity(),
+ GetEffectiveTransformForBuffer(),
+ GetSamplingFilter(), clipRect);
+ });
+ mImageHost->BumpFlashCounter();
+}
+
+void ImageLayerComposite::ComputeEffectiveTransforms(
+ const gfx::Matrix4x4& aTransformToSurface) {
+ gfx::Matrix4x4 local = GetLocalTransform();
+
+ // Snap image edges to pixel boundaries
+ gfxRect sourceRect(0, 0, 0, 0);
+ if (mImageHost && mImageHost->IsAttached()) {
+ IntSize size = mImageHost->GetImageSize();
+ sourceRect.SizeTo(size.width, size.height);
+ }
+ // Snap our local transform first, and snap the inherited transform as well.
+ // This makes our snapping equivalent to what would happen if our content
+ // was drawn into a PaintedLayer (gfxContext would snap using the local
+ // transform, then we'd snap again when compositing the PaintedLayer).
+ mEffectiveTransform = SnapTransform(local, sourceRect, nullptr) *
+ SnapTransformTranslation(aTransformToSurface, nullptr);
+
+ if (mScaleMode != ScaleMode::SCALE_NONE && !sourceRect.IsZeroArea()) {
+ NS_ASSERTION(mScaleMode == ScaleMode::STRETCH,
+ "No other scalemodes than stretch and none supported yet.");
+ local.PreScale(mScaleToSize.width / sourceRect.Width(),
+ mScaleToSize.height / sourceRect.Height(), 1.0);
+
+ mEffectiveTransformForBuffer =
+ SnapTransform(local, sourceRect, nullptr) *
+ SnapTransformTranslation(aTransformToSurface, nullptr);
+ } else {
+ mEffectiveTransformForBuffer = mEffectiveTransform;
+ }
+
+ ComputeEffectiveTransformForMaskLayers(aTransformToSurface);
+}
+
+bool ImageLayerComposite::IsOpaque() {
+ if (!mImageHost || !mImageHost->IsAttached()) {
+ return false;
+ }
+
+ // TODO: Handle ScaleMode::NONE where the image
+ // still covers the whole Layer.
+ if (mScaleMode == ScaleMode::STRETCH) {
+ if ((GetContentFlags() & CONTENT_OPAQUE) && !mImageHost->IsOpaque()) {
+ NS_WARNING("Must have an opaque ImageHost if we reported CONTENT_OPAQUE");
+ }
+ return mImageHost->IsOpaque();
+ }
+ return false;
+}
+
+nsIntRegion ImageLayerComposite::GetFullyRenderedRegion() {
+ if (!mImageHost || !mImageHost->IsAttached()) {
+ return GetShadowVisibleRegion().ToUnknownRegion();
+ }
+
+ if (mScaleMode == ScaleMode::STRETCH) {
+ nsIntRegion shadowVisibleRegion;
+ shadowVisibleRegion.And(GetShadowVisibleRegion().ToUnknownRegion(),
+ nsIntRegion(gfx::IntRect(0, 0, mScaleToSize.width,
+ mScaleToSize.height)));
+ return shadowVisibleRegion;
+ }
+
+ return GetShadowVisibleRegion().ToUnknownRegion();
+}
+
+CompositableHost* ImageLayerComposite::GetCompositableHost() {
+ if (mImageHost && mImageHost->IsAttached()) {
+ return mImageHost.get();
+ }
+
+ return nullptr;
+}
+
+void ImageLayerComposite::CleanupResources() {
+ if (mImageHost) {
+ mImageHost->CleanupResources();
+ mImageHost->Detach(this);
+ }
+ mImageHost = nullptr;
+}
+
+gfx::SamplingFilter ImageLayerComposite::GetSamplingFilter() {
+ return mSamplingFilter;
+}
+
+void ImageLayerComposite::GenEffectChain(EffectChain& aEffect) {
+ aEffect.mLayerRef = this;
+ aEffect.mPrimaryEffect = mImageHost->GenEffect(GetSamplingFilter());
+}
+
+void ImageLayerComposite::PrintInfo(std::stringstream& aStream,
+ const char* aPrefix) {
+ ImageLayer::PrintInfo(aStream, aPrefix);
+ if (mImageHost && mImageHost->IsAttached()) {
+ aStream << "\n";
+ nsAutoCString pfx(aPrefix);
+ pfx += " ";
+ mImageHost->PrintInfo(aStream, pfx.get());
+ }
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/composite/ImageLayerComposite.h b/gfx/layers/composite/ImageLayerComposite.h
new file mode 100644
index 0000000000..f0725c2852
--- /dev/null
+++ b/gfx/layers/composite/ImageLayerComposite.h
@@ -0,0 +1,78 @@
+/* -*- 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/. */
+
+#ifndef GFX_ImageLayerComposite_H
+#define GFX_ImageLayerComposite_H
+
+#include "GLTextureImage.h" // for TextureImage
+#include "ImageLayers.h" // for ImageLayer
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/gfx/Rect.h"
+#include "mozilla/RefPtr.h" // for RefPtr
+#include "mozilla/layers/LayerManagerComposite.h" // for LayerComposite, etc
+#include "mozilla/layers/LayersTypes.h" // for LayerRenderState, etc
+#include "nsISupportsImpl.h" // for TextureImage::AddRef, etc
+#include "nscore.h" // for nsACString
+#include "CompositableHost.h" // for CompositableHost
+
+namespace mozilla {
+namespace layers {
+
+class ImageHost;
+class Layer;
+
+class ImageLayerComposite : public ImageLayer, public LayerComposite {
+ typedef gl::TextureImage TextureImage;
+
+ public:
+ explicit ImageLayerComposite(LayerManagerComposite* aManager);
+
+ protected:
+ virtual ~ImageLayerComposite();
+
+ public:
+ void Disconnect() override;
+
+ bool SetCompositableHost(CompositableHost* aHost) override;
+
+ Layer* GetLayer() override;
+
+ void SetLayerManager(HostLayerManager* aManager) override;
+
+ void RenderLayer(const gfx::IntRect& aClipRect,
+ const Maybe<gfx::Polygon>& aGeometry) override;
+
+ void ComputeEffectiveTransforms(
+ const mozilla::gfx::Matrix4x4& aTransformToSurface) override;
+
+ void CleanupResources() override;
+
+ CompositableHost* GetCompositableHost() override;
+
+ void GenEffectChain(EffectChain& aEffect) override;
+
+ HostLayer* AsHostLayer() override { return this; }
+
+ const char* Name() const override { return "ImageLayerComposite"; }
+
+ bool IsOpaque() override;
+
+ nsIntRegion GetFullyRenderedRegion() override;
+
+ protected:
+ void PrintInfo(std::stringstream& aStream, const char* aPrefix) override;
+
+ private:
+ gfx::SamplingFilter GetSamplingFilter();
+
+ private:
+ RefPtr<ImageHost> mImageHost;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* GFX_ImageLayerComposite_H */
diff --git a/gfx/layers/composite/LayerManagerComposite.cpp b/gfx/layers/composite/LayerManagerComposite.cpp
new file mode 100644
index 0000000000..8ad4e0310d
--- /dev/null
+++ b/gfx/layers/composite/LayerManagerComposite.cpp
@@ -0,0 +1,1780 @@
+/* -*- 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 <stddef.h> // for size_t
+#include <stdint.h> // 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 <android/log.h>
+# include <android/native_window.h>
+# 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<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?
+}
+
+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<CollectedFrames> HostLayerManager::GetCollectedFrames() {
+ Maybe<CollectedFrames> 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<Diagnostics>();
+ 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 <typename Units>
+static IntRectTyped<Units> TransformRect(const IntRectTyped<Units>& aRect,
+ const Matrix& aTransform,
+ bool aRoundIn = false) {
+ if (aRect.IsEmpty()) {
+ return IntRectTyped<Units>();
+ }
+
+ 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<Units>(intRect);
+}
+
+template <typename Units>
+static IntRectTyped<Units> TransformRect(const IntRectTyped<Units>& aRect,
+ const Matrix4x4& aTransform,
+ bool aRoundIn = false) {
+ if (aRect.IsEmpty()) {
+ return IntRectTyped<Units>();
+ }
+
+ 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<Units>(intRect);
+}
+
+template <typename Units, typename MatrixType>
+static IntRectTyped<Units> TransformRectRoundIn(
+ const IntRectTyped<Units>& aRect, const MatrixType& aTransform) {
+ return TransformRect(aRect, aTransform, true);
+}
+
+template <typename Units, typename MatrixType>
+static void AddTransformedRegion(IntRegionTyped<Units>& aDest,
+ const IntRegionTyped<Units>& aSource,
+ const MatrixType& aTransform) {
+ for (auto iter = aSource.RectIter(); !iter.Done(); iter.Next()) {
+ aDest.Or(aDest, TransformRect(iter.Get(), aTransform));
+ }
+ aDest.SimplifyOutward(20);
+}
+
+template <typename Units, typename MatrixType>
+static void AddTransformedRegionRoundIn(IntRegionTyped<Units>& aDest,
+ const IntRegionTyped<Units>& 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<LayerComposite*>(mRoot->AsHostLayer());
+ PostProcessLayers(
+ mRoot, aOpaqueRegion, visible,
+ ViewAs<RenderTargetPixel>(
+ 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<RenderTargetIntRect>& aRenderTargetClip,
+ const Maybe<ParentLayerIntRect>& aClipFromAncestors,
+ bool aCanContributeOpaque) {
+ // Compute a clip that's the combination of our layer clip with the clip
+ // from our ancestors.
+ LayerComposite* composite =
+ static_cast<LayerComposite*>(aLayer->AsHostLayer());
+ 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);
+
+ Maybe<LayerIntRect> 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<LayerPixel>(
+ *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<ParentLayerToLayerMatrix4x4>(localTransform),
+ ParentLayerRect(*outsideClip), LayerRect::MaxIntRect())
+ .valueOr(LayerRect());
+ insideClipFloat.RoundOut();
+ LayerIntRect insideClipInt;
+ if (insideClipFloat.ToIntRect(&insideClipInt)) {
+ insideClip = Some(insideClipInt);
+ }
+ }
+ }
+
+ Maybe<ParentLayerIntRect> ancestorClipForChildren;
+ if (insideClip) {
+ ancestorClipForChildren = Some(ViewAs<ParentLayerPixel>(
+ *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<LayerComposite*>(child->AsHostLayer());
+ Maybe<RenderTargetIntRect> renderTargetClip = aRenderTargetClip;
+ if (childComposite->GetShadowClipRect()) {
+ RenderTargetIntRect clip = TransformBy(
+ ViewAs<ParentLayerToRenderTargetMatrix4x4>(
+ 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<LayerComposite*>(child->AsHostLayer());
+ PostProcessLayers(
+ child, localOpaque, descendantsVisibleRegion,
+ ViewAs<RenderTargetPixel>(
+ 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<LayerToParentLayerMatrix4x4>(transform), visible);
+ aVisibleRegion.OrWith(ViewAs<LayerPixel>(
+ 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<DrawTarget> 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<DrawTarget> 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<DrawTarget> 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<DrawTarget> 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<CompositingRenderTarget>
+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<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.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<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());
+}
+
+void LayerManagerComposite::PlaceNativeLayers(
+ const IntRegion& aRegion, bool aOpaque,
+ std::deque<RefPtr<NativeLayer>>* 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<RefPtr<NativeLayer>>* aLayersToRecycle,
+ IntRegion* aWindowInvalidRegion) {
+ RefPtr<NativeLayer> 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<ForwardIterator>(aLayer, [](Layer* layer) {
+ if (layer->AsHostLayer()) {
+ static_cast<LayerComposite*>(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::Packet>();
+ 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<IntRect> rootLayerClip = mRoot->GetClipRect().map(
+ [](const ParentLayerIntRect& r) { return r.ToUnknownRect(); });
+ Maybe<IntRect> 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<CompositingRenderTarget> 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<RefPtr<NativeLayer>> 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<IntRect> 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<RecordedFrame> 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<uint8_t>());
+ Unused << mScreenPixelsTarget->SendScreenPixels(
+ std::move(mem), ScreenIntSize(bufferWidth, bufferHeight), true);
+ mScreenPixelsTarget = nullptr;
+}
+#endif
+
+already_AddRefed<PaintedLayer> LayerManagerComposite::CreatePaintedLayer() {
+ if (mDestroyed) {
+ NS_WARNING("Call on destroyed layer manager");
+ return nullptr;
+ }
+ return RefPtr<PaintedLayer>(new PaintedLayerComposite(this)).forget();
+}
+
+already_AddRefed<ContainerLayer> LayerManagerComposite::CreateContainerLayer() {
+ if (mDestroyed) {
+ NS_WARNING("Call on destroyed layer manager");
+ return nullptr;
+ }
+ return RefPtr<ContainerLayer>(new ContainerLayerComposite(this)).forget();
+}
+
+already_AddRefed<ImageLayer> LayerManagerComposite::CreateImageLayer() {
+ if (mDestroyed) {
+ NS_WARNING("Call on destroyed layer manager");
+ return nullptr;
+ }
+ return RefPtr<ImageLayer>(new ImageLayerComposite(this)).forget();
+}
+
+already_AddRefed<ColorLayer> LayerManagerComposite::CreateColorLayer() {
+ if (LayerManagerComposite::mDestroyed) {
+ NS_WARNING("Call on destroyed layer manager");
+ return nullptr;
+ }
+ return RefPtr<ColorLayer>(new ColorLayerComposite(this)).forget();
+}
+
+already_AddRefed<CanvasLayer> LayerManagerComposite::CreateCanvasLayer() {
+ if (LayerManagerComposite::mDestroyed) {
+ NS_WARNING("Call on destroyed layer manager");
+ return nullptr;
+ }
+ return RefPtr<CanvasLayer>(new CanvasLayerComposite(this)).forget();
+}
+
+already_AddRefed<RefLayer> LayerManagerComposite::CreateRefLayer() {
+ if (LayerManagerComposite::mDestroyed) {
+ NS_WARNING("Call on destroyed layer manager");
+ return nullptr;
+ }
+ return RefPtr<RefLayer>(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<LayerManagerComposite*>(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<CompositingRenderTarget> 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<profiler_screenshots::RenderSource> 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<CompositingRenderTarget> previousTarget =
+ mCompositor->GetCurrentRenderTarget();
+
+ mCompositor->SetRenderTarget(mRenderSource->RenderTarget());
+ bool result = mCompositor->BlitRenderTarget(
+ static_cast<RenderSourceLMC*>(aSource)->RenderTarget(),
+ aSourceRect.Size(), aDestRect.Size());
+
+ // Restore the old render target.
+ mCompositor->SetRenderTarget(previousTarget);
+
+ return result;
+ }
+
+ protected:
+ virtual ~DownscaleTargetLMC() {}
+
+ RefPtr<RenderSourceLMC> 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<RenderSourceLMC*>(aSource)->RenderTarget(), mARB);
+ }
+ bool MapAndCopyInto(DataSourceSurface* aSurface,
+ const IntSize& aReadSize) override {
+ return mARB->MapAndCopyInto(aSurface, aReadSize);
+ }
+
+ protected:
+ virtual ~AsyncReadbackBufferLMC() {}
+
+ RefPtr<mozilla::layers::AsyncReadbackBuffer> mARB;
+ Compositor* mCompositor;
+};
+
+already_AddRefed<profiler_screenshots::RenderSource>
+WindowLMC::GetWindowContents(const gfx::IntSize& aWindowSize) {
+ RefPtr<CompositingRenderTarget> rt = mCompositor->GetWindowRenderTarget();
+ if (!rt) {
+ return nullptr;
+ }
+ return MakeAndAddRef<RenderSourceLMC>(rt);
+}
+
+already_AddRefed<profiler_screenshots::DownscaleTarget>
+WindowLMC::CreateDownscaleTarget(const gfx::IntSize& aSize) {
+ RefPtr<CompositingRenderTarget> rt =
+ mCompositor->CreateRenderTarget(IntRect({}, aSize), INIT_MODE_NONE);
+ return MakeAndAddRef<DownscaleTargetLMC>(rt, mCompositor);
+}
+
+already_AddRefed<profiler_screenshots::AsyncReadbackBuffer>
+WindowLMC::CreateAsyncReadbackBuffer(const gfx::IntSize& aSize) {
+ RefPtr<AsyncReadbackBuffer> carb =
+ mCompositor->CreateAsyncReadbackBuffer(aSize);
+ if (!carb) {
+ return nullptr;
+ }
+ return MakeAndAddRef<AsyncReadbackBufferLMC>(carb, mCompositor);
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/composite/LayerManagerComposite.h b/gfx/layers/composite/LayerManagerComposite.h
new file mode 100644
index 0000000000..18f8f0aea9
--- /dev/null
+++ b/gfx/layers/composite/LayerManagerComposite.h
@@ -0,0 +1,729 @@
+/* -*- 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/. */
+
+#ifndef GFX_LayerManagerComposite_H
+#define GFX_LayerManagerComposite_H
+
+#include <cstdint> // for uint64_t, int32_t
+#include <deque> // for deque
+#include <new> // for operator new
+#include <type_traits> // for remove_reference<>::type
+#include <utility> // for move, forward
+#include "CompositableHost.h" // for ImageCompositeNotificationInfo
+#include "Units.h" // for LayerIntRegion, ParentLayerIntRect, RenderTargetIntRect
+#include "mozilla/AlreadyAddRefed.h" // for already_AddRefed
+#include "mozilla/Assertions.h" // for MOZ_CRASH, AssertionConditionType, MOZ_ASSERT, MOZ_AS...
+#include "mozilla/Maybe.h" // for Maybe
+#include "mozilla/RefPtr.h" // for RefPtr
+#include "mozilla/TimeStamp.h" // for TimeStamp, BaseTimeDuration
+#include "mozilla/UniquePtr.h" // for UniquePtr
+#include "mozilla/gfx/2D.h" // for DrawTarget
+#include "mozilla/gfx/Matrix.h" // for Matrix4x4
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "mozilla/gfx/Polygon.h" // for Polygon
+#include "mozilla/gfx/Rect.h" // for IntRect
+#include "mozilla/gfx/Types.h" // for DeviceColor (ptr only), SurfaceFormat
+#include "mozilla/layers/CompositionRecorder.h" // for CompositionRecorder, CollectedFrames (ptr only)
+#include "mozilla/layers/Compositor.h" // for Compositor
+#include "mozilla/layers/CompositorTypes.h" // for DiagnosticTypes, TextureFactoryIdentifier
+#include "mozilla/layers/LayerManager.h" // for LayerManager::END_DEFAULT, LayerManager::EndTransacti...
+#include "mozilla/layers/LayersTypes.h" // for CompositionOpportunityId, LayersBackend, LayersBacken...
+#include "mozilla/layers/ScreenshotGrabber.h" // for ScreenshotGrabber
+#include "nsDebug.h" // for NS_WARNING
+#include "nsIThread.h" // for TimeDuration
+#include "nsRegion.h" // for nsIntRegion
+#include "nsRegionFwd.h" // for IntRegion
+#include "nsStringFwd.h" // for nsCString, nsAString
+#include "nsTArray.h" // for nsTArray
+
+class gfxContext;
+
+#ifdef XP_WIN
+# include <windows.h>
+#endif
+
+namespace mozilla {
+namespace layers {
+
+class CanvasLayer;
+class CanvasLayerComposite;
+class ColorLayer;
+class ColorLayerComposite;
+class ContainerLayer;
+class ContainerLayerComposite;
+class Diagnostics;
+struct EffectChain;
+class ImageLayer;
+class ImageLayerComposite;
+class LayerComposite;
+class NativeLayer;
+class NativeLayerRoot;
+class RefLayerComposite;
+class PaintTiming;
+class PaintedLayer;
+class PaintedLayerComposite;
+class RefLayer;
+class SurfacePoolHandle;
+class TextRenderer;
+class TextureSourceProvider;
+class CompositingRenderTarget;
+struct FPSState;
+class PaintCounter;
+class LayerMLGPU;
+class LayerManagerMLGPU;
+class UiCompositorControllerParent;
+class Layer;
+struct LayerProperties;
+
+static const int kVisualWarningDuration = 150; // ms
+
+// An implementation of LayerManager that acts as a pair with ClientLayerManager
+// and is mirrored across IPDL. This gets managed/updated by
+// LayerTransactionParent.
+class HostLayerManager : public LayerManager {
+ public:
+ HostLayerManager();
+ virtual ~HostLayerManager();
+
+ bool BeginTransactionWithTarget(gfxContext* aTarget,
+ const nsCString& aURL) override {
+ MOZ_CRASH("GFX: Use BeginTransactionWithDrawTarget");
+ }
+
+ bool EndEmptyTransaction(EndTransactionFlags aFlags = END_DEFAULT) override {
+ MOZ_CRASH("GFX: Use EndTransaction(aTimeStamp)");
+ return false;
+ }
+
+ void EndTransaction(DrawPaintedLayerCallback aCallback, void* aCallbackData,
+ EndTransactionFlags aFlags = END_DEFAULT) override {
+ MOZ_CRASH("GFX: Use EndTransaction(aTimeStamp)");
+ }
+
+ int32_t GetMaxTextureSize() const override {
+ MOZ_CRASH("GFX: Call on compositor, not LayerManagerComposite");
+ }
+
+ void GetBackendName(nsAString& name) override {
+ MOZ_CRASH("GFX: Shouldn't be called for composited layer manager");
+ }
+
+ virtual void ForcePresent() = 0;
+ virtual void AddInvalidRegion(const nsIntRegion& aRegion) = 0;
+
+ virtual void NotifyShadowTreeTransaction() {}
+ virtual void BeginTransactionWithDrawTarget(gfx::DrawTarget* aTarget,
+ const gfx::IntRect& aRect) = 0;
+ virtual Compositor* GetCompositor() const = 0;
+ virtual TextureSourceProvider* GetTextureSourceProvider() const = 0;
+ virtual void EndTransaction(const TimeStamp& aTimeStamp,
+ EndTransactionFlags aFlags = END_DEFAULT) = 0;
+ virtual void UpdateRenderBounds(const gfx::IntRect& aRect) {}
+ virtual void SetDiagnosticTypes(DiagnosticTypes aDiagnostics) {}
+ virtual void InvalidateAll() = 0;
+
+ HostLayerManager* AsHostLayerManager() override { return this; }
+ virtual LayerManagerMLGPU* AsLayerManagerMLGPU() { return nullptr; }
+
+ void ExtractImageCompositeNotifications(
+ nsTArray<ImageCompositeNotificationInfo>* aNotifications) {
+ aNotifications->AppendElements(std::move(mImageCompositeNotifications));
+ }
+
+ void AppendImageCompositeNotification(
+ const ImageCompositeNotificationInfo& aNotification) {
+ // Only send composite notifications when we're drawing to the screen,
+ // because that's what they mean.
+ // Also when we're not drawing to the screen, DidComposite will not be
+ // called to extract and send these notifications, so they might linger
+ // and contain stale ImageContainerParent pointers.
+ if (IsCompositingToScreen()) {
+ mImageCompositeNotifications.AppendElement(aNotification);
+ }
+ }
+
+ /**
+ * LayerManagerComposite provides sophisticated debug overlays
+ * that can request a next frame.
+ */
+ bool DebugOverlayWantsNextFrame() { return mDebugOverlayWantsNextFrame; }
+ void SetDebugOverlayWantsNextFrame(bool aVal) {
+ mDebugOverlayWantsNextFrame = aVal;
+ }
+
+ /**
+ * Add an on frame warning.
+ * @param severity ranges from 0 to 1. It's used to compute the warning color.
+ */
+ void VisualFrameWarning(float severity) {
+ mozilla::TimeStamp now = TimeStamp::Now();
+ if (mWarnTime.IsNull() || severity > mWarningLevel ||
+ mWarnTime + TimeDuration::FromMilliseconds(kVisualWarningDuration) <
+ now) {
+ mWarnTime = now;
+ mWarningLevel = severity;
+ }
+ }
+
+ void SetPaintTime(const TimeDuration& aPaintTime) {
+ mLastPaintTime = aPaintTime;
+ }
+
+ virtual bool AlwaysScheduleComposite() const { return false; }
+ virtual bool IsCompositingToScreen() const { return false; }
+
+ void RecordPaintTimes(const PaintTiming& aTiming);
+ void RecordUpdateTime(float aValue);
+
+ CompositionOpportunityId GetCompositionOpportunityId() const {
+ return mCompositionOpportunityId;
+ }
+
+ TimeStamp GetCompositionTime() const { return mCompositionTime; }
+ void SetCompositionTime(TimeStamp aTimeStamp) {
+ mCompositionTime = aTimeStamp;
+ if (!mCompositionTime.IsNull() && !mCompositeUntilTime.IsNull() &&
+ mCompositionTime >= mCompositeUntilTime) {
+ mCompositeUntilTime = TimeStamp();
+ }
+ }
+
+ void CompositeUntil(TimeStamp aTimeStamp) {
+ if (mCompositeUntilTime.IsNull() || mCompositeUntilTime < aTimeStamp) {
+ mCompositeUntilTime = aTimeStamp;
+ }
+ }
+ TimeStamp GetCompositeUntilTime() const { return mCompositeUntilTime; }
+
+ // We maintaining a global mapping from ID to CompositorBridgeParent for
+ // async compositables.
+ uint64_t GetCompositorBridgeID() const { return mCompositorBridgeID; }
+ void SetCompositorBridgeID(uint64_t aID) {
+ MOZ_ASSERT(mCompositorBridgeID == 0,
+ "The compositor ID must be set only once.");
+ mCompositorBridgeID = aID;
+ }
+
+ void SetCompositionRecorder(UniquePtr<CompositionRecorder> aRecorder) {
+ mCompositionRecorder = std::move(aRecorder);
+ }
+
+ /**
+ * Write the frames collected by the |CompositionRecorder| to disk.
+ *
+ * If there is not currently a |CompositionRecorder|, this is a no-op.
+ */
+ void WriteCollectedFrames();
+
+ Maybe<CollectedFrames> GetCollectedFrames();
+
+ protected:
+ bool mDebugOverlayWantsNextFrame;
+ nsTArray<ImageCompositeNotificationInfo> mImageCompositeNotifications;
+ // Testing property. If hardware composer is supported, this will return
+ // true if the last frame was deemed 'too complicated' to be rendered.
+ float mWarningLevel;
+ mozilla::TimeStamp mWarnTime;
+ UniquePtr<Diagnostics> mDiagnostics;
+ uint64_t mCompositorBridgeID;
+
+ TimeDuration mLastPaintTime;
+ TimeStamp mRenderStartTime;
+ UniquePtr<CompositionRecorder> mCompositionRecorder = nullptr;
+
+ // Render time for the current composition.
+ TimeStamp mCompositionTime;
+
+ // CompositionOpportunityId of the current composition.
+ CompositionOpportunityId mCompositionOpportunityId;
+
+ // When nonnull, during rendering, some compositable indicated that it will
+ // change its rendering at this time. In order not to miss it, we composite
+ // on every vsync until this time occurs (this is the latest such time).
+ TimeStamp mCompositeUntilTime;
+#if defined(MOZ_WIDGET_ANDROID)
+ public:
+ // Used by UiCompositorControllerParent to set itself as the target for the
+ // contents of the frame buffer after a composite.
+ // Implemented in LayerManagerComposite
+ virtual void RequestScreenPixels(UiCompositorControllerParent* aController) {}
+#endif // defined(MOZ_WIDGET_ANDROID)
+};
+
+// A layer manager implementation that uses the Compositor API
+// to render layers.
+class LayerManagerComposite final : public HostLayerManager {
+ typedef mozilla::gfx::DrawTarget DrawTarget;
+ typedef mozilla::gfx::IntSize IntSize;
+ typedef mozilla::gfx::SurfaceFormat SurfaceFormat;
+
+ public:
+ explicit LayerManagerComposite(Compositor* aCompositor);
+ virtual ~LayerManagerComposite();
+
+ void Destroy() override;
+
+ /**
+ * Sets the clipping region for this layer manager. This is important on
+ * windows because using OGL we no longer have GDI's native clipping. Therefor
+ * widget must tell us what part of the screen is being invalidated,
+ * and we should clip to this.
+ *
+ * \param aClippingRegion Region to clip to. Setting an empty region
+ * will disable clipping.
+ */
+ void SetClippingRegion(const nsIntRegion& aClippingRegion) {
+ mClippingRegion = aClippingRegion;
+ }
+
+ /**
+ * LayerManager implementation.
+ */
+ LayerManagerComposite* AsLayerManagerComposite() override { return this; }
+
+ void UpdateRenderBounds(const gfx::IntRect& aRect) override;
+
+ bool BeginTransaction(const nsCString& aURL) override;
+ void BeginTransactionWithDrawTarget(gfx::DrawTarget* aTarget,
+ const gfx::IntRect& aRect) override;
+ void EndTransaction(const TimeStamp& aTimeStamp,
+ EndTransactionFlags aFlags = END_DEFAULT) override;
+ virtual void EndTransaction(
+ DrawPaintedLayerCallback aCallback, void* aCallbackData,
+ EndTransactionFlags aFlags = END_DEFAULT) override {
+ MOZ_CRASH("GFX: Use EndTransaction(aTimeStamp)");
+ }
+
+ void SetRoot(Layer* aLayer) override;
+
+ // XXX[nrc]: never called, we should move this logic to ClientLayerManager
+ // (bug 946926).
+ bool CanUseCanvasLayerForSize(const gfx::IntSize& aSize) override;
+
+ void ClearCachedResources(Layer* aSubtree = nullptr) override;
+
+ already_AddRefed<PaintedLayer> CreatePaintedLayer() override;
+ already_AddRefed<ContainerLayer> CreateContainerLayer() override;
+ already_AddRefed<ImageLayer> CreateImageLayer() override;
+ already_AddRefed<ColorLayer> CreateColorLayer() override;
+ already_AddRefed<CanvasLayer> CreateCanvasLayer() override;
+ already_AddRefed<RefLayer> CreateRefLayer() override;
+
+ bool AreComponentAlphaLayersEnabled() override;
+
+ already_AddRefed<DrawTarget> CreateOptimalMaskDrawTarget(
+ const IntSize& aSize) override;
+
+ const char* Name() const override { return ""; }
+ bool IsCompositingToScreen() const override;
+
+ bool AlwaysScheduleComposite() const override;
+
+ /**
+ * Post-processes layers before composition. This performs the following:
+ *
+ * - Applies occlusion culling. This restricts the shadow visible region of
+ * layers that are covered with opaque content. |aOpaqueRegion| is the
+ * region already known to be covered with opaque content, in the
+ * post-transform coordinate space of aLayer.
+ *
+ * - Recomputes visible regions to account for async transforms.
+ * Each layer accumulates into |aVisibleRegion| its post-transform
+ * (including async transforms) visible region.
+ *
+ * - aRenderTargetClip is the exact clip required for aLayer, in the
+ * coordinates of the nearest render target (the same as
+ * GetEffectiveTransform).
+ *
+ * - aClipFromAncestors is the approximate combined clip from all
+ * ancestors, in the coordinate space of our parent, but maybe be an
+ * overestimate in the presence of complex transforms.
+ */
+ void PostProcessLayers(nsIntRegion& aOpaqueRegion);
+ void PostProcessLayers(Layer* aLayer, nsIntRegion& aOpaqueRegion,
+ LayerIntRegion& aVisibleRegion,
+ const Maybe<RenderTargetIntRect>& aRenderTargetClip,
+ const Maybe<ParentLayerIntRect>& aClipFromAncestors,
+ bool aCanContributeOpaque);
+
+ /**
+ * RAII helper class to add a mask effect with the compositable from
+ * aMaskLayer to the EffectChain aEffect and notify the compositable when we
+ * are done.
+ */
+ class AutoAddMaskEffect {
+ public:
+ AutoAddMaskEffect(Layer* aMaskLayer, EffectChain& aEffect);
+ ~AutoAddMaskEffect();
+
+ bool Failed() const { return mFailed; }
+
+ private:
+ CompositableHost* mCompositable;
+ bool mFailed;
+ };
+
+ /**
+ * returns true if PlatformAllocBuffer will return a buffer that supports
+ * direct texturing
+ */
+ static bool SupportsDirectTexturing();
+
+ static void PlatformSyncBeforeReplyUpdate();
+
+ void AddInvalidRegion(const nsIntRegion& aRegion) override {
+ mInvalidRegion.Or(mInvalidRegion, aRegion);
+ }
+
+ Compositor* GetCompositor() const override { return mCompositor; }
+ TextureSourceProvider* GetTextureSourceProvider() const override {
+ return mCompositor;
+ }
+
+ void NotifyShadowTreeTransaction() override;
+
+ TextRenderer* GetTextRenderer() { return mTextRenderer; }
+
+ void UnusedApzTransformWarning() { mUnusedApzTransformWarning = true; }
+ void DisabledApzWarning() { mDisabledApzWarning = true; }
+
+ bool AsyncPanZoomEnabled() const override;
+
+ public:
+ TextureFactoryIdentifier GetTextureFactoryIdentifier() override {
+ return mCompositor->GetTextureFactoryIdentifier();
+ }
+ LayersBackend GetBackendType() override {
+ return mCompositor ? mCompositor->GetBackendType()
+ : LayersBackend::LAYERS_NONE;
+ }
+ void SetDiagnosticTypes(DiagnosticTypes aDiagnostics) override {
+ mCompositor->SetDiagnosticTypes(aDiagnostics);
+ }
+
+ void InvalidateAll() override {
+ AddInvalidRegion(nsIntRegion(mRenderBounds));
+ }
+
+ void ForcePresent() override { mCompositor->ForcePresent(); }
+
+ private:
+ /** Region we're clipping our current drawing to. */
+ nsIntRegion mClippingRegion;
+ gfx::IntRect mRenderBounds;
+
+ /** Current root layer. */
+ LayerComposite* RootLayer() const;
+
+ /**
+ * Update the invalid region and render it.
+ */
+ void UpdateAndRender();
+
+ /**
+ * Render the current layer tree to the active target.
+ * Returns true if the current invalid region can be cleared, false if
+ * rendering was canceled.
+ */
+ bool Render(const nsIntRegion& aInvalidRegion,
+ const nsIntRegion& aOpaqueRegion);
+#if defined(MOZ_WIDGET_ANDROID)
+ void RenderToPresentationSurface();
+ // Shifts the content down so the toolbar does not cover it.
+ // Returns the Y shift of the content in screen pixels
+ ScreenCoord GetContentShiftForToolbar();
+ // Renders the static snapshot after the content has been rendered.
+ void RenderToolbar();
+ // Used by robocop tests to get a snapshot of the frame buffer.
+ void HandlePixelsTarget();
+#endif
+
+ /**
+ * We need to know our invalid region before we're ready to render.
+ */
+ void InvalidateDebugOverlay(nsIntRegion& aInvalidRegion,
+ const gfx::IntRect& aBounds);
+
+ /**
+ * Render debug overlays such as the FPS/FrameCounter above the frame.
+ */
+ void RenderDebugOverlay(const gfx::IntRect& aBounds);
+
+ void DrawBorder(const gfx::IntRect& aOuter, int32_t aBorderWidth,
+ const gfx::DeviceColor& aColor,
+ const gfx::Matrix4x4& aTransform);
+ void DrawTranslationWarningOverlay(const gfx::IntRect& aBounds);
+
+ void UpdateDebugOverlayNativeLayers();
+
+ RefPtr<CompositingRenderTarget> PushGroupForLayerEffects();
+ void PopGroupForLayerEffects(RefPtr<CompositingRenderTarget> aPreviousTarget,
+ gfx::IntRect aClipRect, bool aGrayscaleEffect,
+ bool aInvertEffect, float aContrastEffect);
+
+ /**
+ * Create or recycle native layers to cover aRegion or aRect.
+ * This method takes existing layers from the front of aLayersToRecycle (or
+ * creates new layers if no layers are left to recycle) and appends them to
+ * the end of mNativeLayers. The "take from front, add to back" approach keeps
+ * the layer to rect assignment stable between frames.
+ * Updates the rect and opaqueness on the layers. For layers that moved or
+ * resized, *aWindowInvalidRegion is updated to include the area impacted by
+ * the move.
+ * Any layers left in aLayersToRecycle are not needed and can be disposed of.
+ */
+ void PlaceNativeLayers(const gfx::IntRegion& aRegion, bool aOpaque,
+ std::deque<RefPtr<NativeLayer>>* aLayersToRecycle,
+ gfx::IntRegion* aWindowInvalidRegion);
+ void PlaceNativeLayer(const gfx::IntRect& aRect, bool aOpaque,
+ std::deque<RefPtr<NativeLayer>>* aLayersToRecycle,
+ gfx::IntRegion* aWindowInvalidRegion);
+
+ bool mUnusedApzTransformWarning;
+ bool mDisabledApzWarning;
+ RefPtr<Compositor> mCompositor;
+ UniquePtr<LayerProperties> mClonedLayerTreeProperties;
+
+ /**
+ * Context target, nullptr when drawing directly to our swap chain.
+ */
+ RefPtr<gfx::DrawTarget> mTarget;
+ gfx::IntRect mTargetBounds;
+
+ nsIntRegion mInvalidRegion;
+
+ bool mInTransaction;
+ bool mIsCompositorReady;
+
+ RefPtr<CompositingRenderTarget> mTwoPassTmpTarget;
+ ScreenshotGrabber mProfilerScreenshotGrabber;
+ RefPtr<TextRenderer> mTextRenderer;
+ RefPtr<NativeLayerRoot> mNativeLayerRoot;
+ RefPtr<SurfacePoolHandle> mSurfacePoolHandle;
+ std::deque<RefPtr<NativeLayer>> mNativeLayers;
+ RefPtr<NativeLayer> mGPUStatsLayer;
+ RefPtr<NativeLayer> mUnusedTransformWarningLayer;
+ RefPtr<NativeLayer> mDisabledApzWarningLayer;
+
+#ifdef USE_SKIA
+ /**
+ * Render paint and composite times above the frame.
+ */
+ void DrawPaintTimes(Compositor* aCompositor);
+ RefPtr<PaintCounter> mPaintCounter;
+#endif
+#if defined(MOZ_WIDGET_ANDROID)
+ public:
+ virtual void RequestScreenPixels(
+ UiCompositorControllerParent* aController) override {
+ mScreenPixelsTarget = aController;
+ }
+
+ private:
+ UiCompositorControllerParent* mScreenPixelsTarget;
+#endif // defined(MOZ_WIDGET_ANDROID)
+};
+
+/**
+ * Compositor layers are for use with OMTC on the compositor thread only. There
+ * must be corresponding Client layers on the content thread. For composite
+ * layers, the layer manager only maintains the layer tree.
+ */
+class HostLayer {
+ public:
+ explicit HostLayer(HostLayerManager* aManager)
+ : mCompositorManager(aManager),
+ mShadowOpacity(1.0),
+ mShadowTransformSetByAnimation(false),
+ mShadowOpacitySetByAnimation(false) {}
+
+ virtual void SetLayerManager(HostLayerManager* aManager) {
+ mCompositorManager = aManager;
+ }
+ HostLayerManager* GetLayerManager() const { return mCompositorManager; }
+
+ virtual ~HostLayer() = default;
+
+ virtual LayerComposite* GetFirstChildComposite() { return nullptr; }
+
+ virtual Layer* GetLayer() = 0;
+
+ virtual LayerMLGPU* AsLayerMLGPU() { return nullptr; }
+
+ virtual bool SetCompositableHost(CompositableHost*) {
+ // We must handle this gracefully, see bug 967824
+ NS_WARNING(
+ "called SetCompositableHost for a layer type not accepting a "
+ "compositable");
+ return false;
+ }
+ virtual CompositableHost* GetCompositableHost() = 0;
+
+ /**
+ * The following methods are
+ *
+ * CONSTRUCTION PHASE ONLY
+ *
+ * They are analogous to the Layer interface.
+ */
+ void SetShadowVisibleRegion(const LayerIntRegion& aRegion) {
+ mShadowVisibleRegion = aRegion;
+ }
+ void SetShadowVisibleRegion(LayerIntRegion&& aRegion) {
+ mShadowVisibleRegion = std::move(aRegion);
+ }
+
+ void SetShadowOpacity(float aOpacity) { mShadowOpacity = aOpacity; }
+ void SetShadowOpacitySetByAnimation(bool aSetByAnimation) {
+ mShadowOpacitySetByAnimation = aSetByAnimation;
+ }
+
+ void SetShadowClipRect(const Maybe<ParentLayerIntRect>& aRect) {
+ mShadowClipRect = aRect;
+ }
+
+ void SetShadowBaseTransform(const gfx::Matrix4x4& aMatrix) {
+ mShadowTransform = aMatrix;
+ }
+ void SetShadowTransformSetByAnimation(bool aSetByAnimation) {
+ mShadowTransformSetByAnimation = aSetByAnimation;
+ }
+
+ // These getters can be used anytime.
+ float GetShadowOpacity() { return mShadowOpacity; }
+ const Maybe<ParentLayerIntRect>& GetShadowClipRect() {
+ return mShadowClipRect;
+ }
+ virtual const LayerIntRegion& GetShadowVisibleRegion() {
+ return mShadowVisibleRegion;
+ }
+ const gfx::Matrix4x4& GetShadowBaseTransform() { return mShadowTransform; }
+ gfx::Matrix4x4 GetShadowTransform();
+ bool GetShadowTransformSetByAnimation() {
+ return mShadowTransformSetByAnimation;
+ }
+ bool GetShadowOpacitySetByAnimation() { return mShadowOpacitySetByAnimation; }
+
+ void RecomputeShadowVisibleRegionFromChildren();
+
+ protected:
+ HostLayerManager* mCompositorManager;
+
+ gfx::Matrix4x4 mShadowTransform;
+ LayerIntRegion mShadowVisibleRegion;
+ Maybe<ParentLayerIntRect> mShadowClipRect;
+ float mShadowOpacity;
+ bool mShadowTransformSetByAnimation;
+ bool mShadowOpacitySetByAnimation;
+};
+
+/**
+ * Composite layers are for use with OMTC on the compositor thread only. There
+ * must be corresponding Client layers on the content thread. For composite
+ * layers, the layer manager only maintains the layer tree, all rendering is
+ * done by a Compositor (see Compositor.h). As such, composite layers are
+ * platform-independent and can be used on any platform for which there is a
+ * Compositor implementation.
+ *
+ * The composite layer tree reflects exactly the basic layer tree. To
+ * composite to screen, the layer manager walks the layer tree calling render
+ * methods which in turn call into their CompositableHosts' Composite methods.
+ * These call Compositor::DrawQuad to do the rendering.
+ *
+ * Mostly, layers are updated during the layers transaction. This is done from
+ * CompositableClient to CompositableHost without interacting with the layer.
+ *
+ * A reference to the Compositor is stored in LayerManagerComposite.
+ */
+class LayerComposite : public HostLayer {
+ public:
+ explicit LayerComposite(LayerManagerComposite* aManager);
+
+ virtual ~LayerComposite();
+
+ void SetLayerManager(HostLayerManager* aManager) override;
+
+ LayerComposite* GetFirstChildComposite() override { return nullptr; }
+
+ /* Do NOT call this from the generic LayerComposite destructor. Only from the
+ * concrete class destructor
+ */
+ virtual void Destroy();
+ virtual void Cleanup() {}
+
+ /**
+ * Perform a first pass over the layer tree to render all of the intermediate
+ * surfaces that we can. This allows us to avoid framebuffer switches in the
+ * middle of our render which is inefficient especially on mobile GPUs. This
+ * must be called before RenderLayer.
+ */
+ virtual void Prepare(const RenderTargetIntRect& aClipRect) {}
+
+ // TODO: This should also take RenderTargetIntRect like Prepare.
+ virtual void RenderLayer(const gfx::IntRect& aClipRect,
+ const Maybe<gfx::Polygon>& aGeometry) = 0;
+
+ bool SetCompositableHost(CompositableHost*) override {
+ // We must handle this gracefully, see bug 967824
+ NS_WARNING(
+ "called SetCompositableHost for a layer type not accepting a "
+ "compositable");
+ return false;
+ }
+
+ virtual void CleanupResources() = 0;
+
+ virtual void DestroyFrontBuffer() {}
+
+ void AddBlendModeEffect(EffectChain& aEffectChain);
+
+ virtual void GenEffectChain(EffectChain& aEffect) {}
+
+ void SetLayerComposited(bool value) { mLayerComposited = value; }
+
+ void SetClearRect(const gfx::IntRect& aRect) { mClearRect = aRect; }
+
+ bool HasLayerBeenComposited() { return mLayerComposited; }
+ gfx::IntRect GetClearRect() { return mClearRect; }
+
+ // Returns false if the layer is attached to an older compositor.
+ bool HasStaleCompositor() const;
+
+ /**
+ * Return the part of the visible region that has been fully rendered.
+ * While progressive drawing is in progress this region will be
+ * a subset of the shadow visible region.
+ */
+ virtual nsIntRegion GetFullyRenderedRegion();
+
+ protected:
+ LayerManagerComposite* mCompositeManager;
+
+ RefPtr<Compositor> mCompositor;
+ bool mDestroyed;
+ bool mLayerComposited;
+ gfx::IntRect mClearRect;
+};
+
+class WindowLMC : public profiler_screenshots::Window {
+ public:
+ explicit WindowLMC(Compositor* aCompositor) : mCompositor(aCompositor) {}
+
+ already_AddRefed<profiler_screenshots::RenderSource> GetWindowContents(
+ const gfx::IntSize& aWindowSize) override;
+ already_AddRefed<profiler_screenshots::DownscaleTarget> CreateDownscaleTarget(
+ const gfx::IntSize& aSize) override;
+ already_AddRefed<profiler_screenshots::AsyncReadbackBuffer>
+ CreateAsyncReadbackBuffer(const gfx::IntSize& aSize) override;
+
+ protected:
+ Compositor* mCompositor;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* GFX_LayerManagerComposite_H */
diff --git a/gfx/layers/composite/LayerManagerCompositeUtils.h b/gfx/layers/composite/LayerManagerCompositeUtils.h
new file mode 100644
index 0000000000..d349cf99fc
--- /dev/null
+++ b/gfx/layers/composite/LayerManagerCompositeUtils.h
@@ -0,0 +1,160 @@
+/* -*- 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/. */
+
+#ifndef GFX_LayerManagerCompositeUtils_H
+#define GFX_LayerManagerCompositeUtils_H
+
+#include <cstddef> // for size_t
+#include "Layers.h" // for Layer
+#include "Units.h" // for LayerIntRegion
+#include "mozilla/RefPtr.h" // for RefPtr
+#include "mozilla/gfx/BaseRect.h" // for operator-
+#include "mozilla/gfx/Matrix.h" // for Matrix4x4
+#include "mozilla/gfx/Rect.h" // for IntRect, Rect, RoundedOut, IntRectTyped
+#include "mozilla/layers/Compositor.h" // for Compositor, INIT_MODE_CLEAR
+#include "mozilla/layers/Effects.h" // for EffectChain, EffectRenderTarget
+#include "mozilla/layers/LayerManagerComposite.h" // for LayerManagerComposite::AutoAddMaskEffect, LayerComposite, LayerManagerComposite
+#include "mozilla/layers/TextureHost.h" // for CompositingRenderTarget
+
+namespace mozilla {
+namespace layers {
+
+// Render aLayer using aCompositor and apply all mask layers of aLayer: The
+// layer's own mask layer (aLayer->GetMaskLayer()), and any ancestor mask
+// layers.
+// If more than one mask layer needs to be applied, we use intermediate surfaces
+// (CompositingRenderTargets) for rendering, applying one mask layer at a time.
+// Callers need to provide a callback function aRenderCallback that does the
+// actual rendering of the source. It needs to have the following form:
+// void (EffectChain& effectChain, const Rect& clipRect)
+// aRenderCallback is called exactly once, inside this function, unless aLayer's
+// visible region is completely clipped out (in that case, aRenderCallback won't
+// be called at all).
+// This function calls aLayer->AsHostLayer()->AddBlendModeEffect for the
+// final rendering pass.
+//
+// (This function should really live in LayerManagerComposite.cpp, but we
+// need to use templates for passing lambdas until bug 1164522 is resolved.)
+template <typename RenderCallbackType>
+void RenderWithAllMasks(Layer* aLayer, Compositor* aCompositor,
+ const gfx::IntRect& aClipRect,
+ RenderCallbackType aRenderCallback) {
+ Layer* firstMask = nullptr;
+ size_t maskLayerCount = 0;
+ size_t nextAncestorMaskLayer = 0;
+
+ size_t ancestorMaskLayerCount = aLayer->GetAncestorMaskLayerCount();
+ if (Layer* ownMask = aLayer->GetMaskLayer()) {
+ firstMask = ownMask;
+ maskLayerCount = ancestorMaskLayerCount + 1;
+ nextAncestorMaskLayer = 0;
+ } else if (ancestorMaskLayerCount > 0) {
+ firstMask = aLayer->GetAncestorMaskLayerAt(0);
+ maskLayerCount = ancestorMaskLayerCount;
+ nextAncestorMaskLayer = 1;
+ } else {
+ // no mask layers at all
+ }
+
+ if (maskLayerCount <= 1) {
+ // This is the common case. Render in one pass and return.
+ EffectChain effectChain(aLayer);
+ LayerManagerComposite::AutoAddMaskEffect autoMaskEffect(firstMask,
+ effectChain);
+ static_cast<LayerComposite*>(aLayer->AsHostLayer())
+ ->AddBlendModeEffect(effectChain);
+ aRenderCallback(effectChain, aClipRect);
+ return;
+ }
+
+ // We have multiple mask layers.
+ // We split our list of mask layers into three parts:
+ // (1) The first mask
+ // (2) The list of intermediate masks (every mask except first and last)
+ // (3) The final mask.
+ // Part (2) can be empty.
+ // For parts (1) and (2) we need to allocate intermediate surfaces to render
+ // into. The final mask gets rendered into the original render target.
+
+ // Calculate the size of the intermediate surfaces.
+ gfx::Rect visibleRect(
+ aLayer->GetLocalVisibleRegion().GetBounds().ToUnknownRect());
+ gfx::Matrix4x4 transform = aLayer->GetEffectiveTransform();
+ // TODO: Use RenderTargetIntRect and TransformBy here
+ gfx::IntRect surfaceRect = RoundedOut(
+ transform.TransformAndClipBounds(visibleRect, gfx::Rect(aClipRect)));
+ if (surfaceRect.IsEmpty()) {
+ return;
+ }
+
+ RefPtr<CompositingRenderTarget> originalTarget =
+ aCompositor->GetCurrentRenderTarget();
+
+ RefPtr<CompositingRenderTarget> firstTarget =
+ aCompositor->CreateRenderTarget(surfaceRect, INIT_MODE_CLEAR);
+ if (!firstTarget) {
+ return;
+ }
+
+ // Render the source while applying the first mask.
+ aCompositor->SetRenderTarget(firstTarget);
+ {
+ EffectChain firstEffectChain(aLayer);
+ LayerManagerComposite::AutoAddMaskEffect firstMaskEffect(firstMask,
+ firstEffectChain);
+ aRenderCallback(firstEffectChain, aClipRect - surfaceRect.TopLeft());
+ // firstTarget now contains the transformed source with the first mask and
+ // opacity already applied.
+ }
+
+ // Apply the intermediate masks.
+ gfx::IntRect intermediateClip(surfaceRect - surfaceRect.TopLeft());
+ RefPtr<CompositingRenderTarget> previousTarget = firstTarget;
+ for (size_t i = nextAncestorMaskLayer; i < ancestorMaskLayerCount - 1; i++) {
+ Layer* intermediateMask = aLayer->GetAncestorMaskLayerAt(i);
+ RefPtr<CompositingRenderTarget> intermediateTarget =
+ aCompositor->CreateRenderTarget(surfaceRect, INIT_MODE_CLEAR);
+ if (!intermediateTarget) {
+ break;
+ }
+ aCompositor->SetRenderTarget(intermediateTarget);
+ EffectChain intermediateEffectChain(aLayer);
+ LayerManagerComposite::AutoAddMaskEffect intermediateMaskEffect(
+ intermediateMask, intermediateEffectChain);
+ if (intermediateMaskEffect.Failed()) {
+ continue;
+ }
+ intermediateEffectChain.mPrimaryEffect =
+ new EffectRenderTarget(previousTarget);
+ aCompositor->DrawQuad(gfx::Rect(surfaceRect), intermediateClip,
+ intermediateEffectChain, 1.0, gfx::Matrix4x4());
+ previousTarget = intermediateTarget;
+ }
+
+ aCompositor->SetRenderTarget(originalTarget);
+
+ // Apply the final mask, rendering into originalTarget.
+ EffectChain finalEffectChain(aLayer);
+ finalEffectChain.mPrimaryEffect = new EffectRenderTarget(previousTarget);
+ Layer* finalMask = aLayer->GetAncestorMaskLayerAt(ancestorMaskLayerCount - 1);
+
+ // The blend mode needs to be applied in this final step, because this is
+ // where we're blending with the actual background (which is in
+ // originalTarget).
+ static_cast<LayerComposite*>(aLayer->AsHostLayer())
+ ->AddBlendModeEffect(finalEffectChain);
+ LayerManagerComposite::AutoAddMaskEffect autoMaskEffect(finalMask,
+ finalEffectChain);
+ if (!autoMaskEffect.Failed()) {
+ aCompositor->DrawQuad(gfx::Rect(surfaceRect), aClipRect, finalEffectChain,
+ 1.0, gfx::Matrix4x4());
+ }
+}
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* GFX_LayerManagerCompositeUtils_H */
diff --git a/gfx/layers/composite/PaintCounter.cpp b/gfx/layers/composite/PaintCounter.cpp
new file mode 100644
index 0000000000..fbf1552116
--- /dev/null
+++ b/gfx/layers/composite/PaintCounter.cpp
@@ -0,0 +1,79 @@
+/* -*- 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 "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/TimeStamp.h" // for TimeStamp, TimeDuration
+#include "mozilla/Sprintf.h"
+
+#include "mozilla/gfx/HelpersSkia.h"
+#include "skia/include/core/SkFont.h"
+#include "PaintCounter.h"
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::gfx;
+
+// Positioned below the chrome UI
+IntRect PaintCounter::mRect = IntRect(0, 175, 300, 60);
+
+PaintCounter::PaintCounter() {
+ mFormat = SurfaceFormat::B8G8R8A8;
+ mSurface = Factory::CreateDataSourceSurface(mRect.Size(), mFormat);
+ mMap.emplace(mSurface, DataSourceSurface::READ_WRITE);
+ mStride = mMap->GetStride();
+
+ mCanvas = SkCanvas::MakeRasterDirect(MakeSkiaImageInfo(mRect.Size(), mFormat),
+ mMap->GetData(), mStride);
+ mCanvas->clear(SK_ColorWHITE);
+}
+
+PaintCounter::~PaintCounter() {
+ mSurface = nullptr;
+ mTextureSource = nullptr;
+ mTexturedEffect = nullptr;
+}
+
+void PaintCounter::Draw(Compositor* aCompositor, TimeDuration aPaintTime,
+ TimeDuration aCompositeTime) {
+ char buffer[48];
+ SprintfLiteral(buffer, "P: %.2f C: %.2f", aPaintTime.ToMilliseconds(),
+ aCompositeTime.ToMilliseconds());
+
+ SkPaint paint;
+ paint.setColor(SkColorSetRGB(0, 255, 0));
+ paint.setAntiAlias(true);
+
+ SkFont font(SkTypeface::MakeDefault(), 32);
+
+ mCanvas->clear(SK_ColorTRANSPARENT);
+ mCanvas->drawString(buffer, 10, 30, font, paint);
+ mCanvas->flush();
+
+ if (!mTextureSource) {
+ mTextureSource = aCompositor->CreateDataTextureSource();
+ mTexturedEffect = CreateTexturedEffect(mFormat, mTextureSource,
+ SamplingFilter::POINT, true);
+ mTexturedEffect->mTextureCoords = Rect(0, 0, 1.0f, 1.0f);
+ }
+
+ mTextureSource->Update(mSurface);
+
+ EffectChain effectChain;
+ effectChain.mPrimaryEffect = mTexturedEffect;
+
+ gfx::Matrix4x4 identity;
+ Rect rect(mRect.X(), mRect.Y(), mRect.Width(), mRect.Height());
+ aCompositor->DrawQuad(rect, mRect, effectChain, 1.0, identity);
+}
+
+} // end namespace layers
+} // end namespace mozilla
diff --git a/gfx/layers/composite/PaintCounter.h b/gfx/layers/composite/PaintCounter.h
new file mode 100644
index 0000000000..084330c998
--- /dev/null
+++ b/gfx/layers/composite/PaintCounter.h
@@ -0,0 +1,53 @@
+/* -*- 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/. */
+
+#ifndef mozilla_layers_PaintCounter_h_
+#define mozilla_layers_PaintCounter_h_
+
+#include <map> // for std::map
+#include "mozilla/Maybe.h"
+#include "mozilla/RefPtr.h" // for already_AddRefed, RefCounted
+#include "mozilla/TimeStamp.h" // for TimeStamp, TimeDuration
+#include "skia/include/core/SkCanvas.h"
+
+namespace mozilla {
+namespace layers {
+
+class Compositor;
+
+using namespace mozilla::gfx;
+using namespace mozilla::gl;
+
+// Keeps track and paints how long a full invalidation paint takes to rasterize
+// and composite.
+class PaintCounter {
+ public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(PaintCounter)
+
+ PaintCounter();
+ void Draw(Compositor* aCompositor, TimeDuration aPaintTime,
+ TimeDuration aCompositeTime);
+ static IntRect GetPaintRect() { return PaintCounter::mRect; }
+
+ private:
+ virtual ~PaintCounter();
+
+ SurfaceFormat mFormat;
+ std::unique_ptr<SkCanvas> mCanvas;
+ IntSize mSize;
+ int mStride;
+
+ RefPtr<DataSourceSurface> mSurface;
+ RefPtr<DataTextureSource> mTextureSource;
+ RefPtr<TexturedEffect> mTexturedEffect;
+ Maybe<DataSourceSurface::ScopedMap> mMap;
+ static IntRect mRect;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_opengl_PaintCounter_h_
diff --git a/gfx/layers/composite/PaintedLayerComposite.cpp b/gfx/layers/composite/PaintedLayerComposite.cpp
new file mode 100644
index 0000000000..fe8028d12f
--- /dev/null
+++ b/gfx/layers/composite/PaintedLayerComposite.cpp
@@ -0,0 +1,169 @@
+/* -*- 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 "PaintedLayerComposite.h"
+#include "CompositableHost.h" // for TiledLayerProperties, etc
+#include "FrameMetrics.h" // for FrameMetrics
+#include "Units.h" // for CSSRect, LayerPixel, etc
+#include "gfxEnv.h" // for gfxEnv
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/gfx/Matrix.h" // for Matrix4x4
+#include "mozilla/gfx/Point.h" // for Point
+#include "mozilla/gfx/Polygon.h" // for Polygon
+#include "mozilla/gfx/Rect.h" // for RoundedToInt, Rect
+#include "mozilla/gfx/Types.h" // for SamplingFilter::LINEAR
+#include "mozilla/layers/Compositor.h" // for Compositor
+#include "mozilla/layers/ContentHost.h" // for ContentHost
+#include "mozilla/layers/Effects.h" // for EffectChain
+#include "mozilla/layers/LayerManagerCompositeUtils.h"
+#include "mozilla/mozalloc.h" // for operator delete
+#include "nsAString.h"
+#include "mozilla/RefPtr.h" // for nsRefPtr
+#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
+#include "nsMathUtils.h" // for NS_lround
+#include "nsString.h" // for nsAutoCString
+#include "TextRenderer.h"
+#include "GeckoProfiler.h"
+
+namespace mozilla {
+namespace layers {
+
+PaintedLayerComposite::PaintedLayerComposite(LayerManagerComposite* aManager)
+ : PaintedLayer(aManager, nullptr),
+ LayerComposite(aManager),
+ mBuffer(nullptr) {
+ MOZ_COUNT_CTOR(PaintedLayerComposite);
+ mImplData = static_cast<LayerComposite*>(this);
+}
+
+PaintedLayerComposite::~PaintedLayerComposite() {
+ MOZ_COUNT_DTOR(PaintedLayerComposite);
+ CleanupResources();
+}
+
+bool PaintedLayerComposite::SetCompositableHost(CompositableHost* aHost) {
+ switch (aHost->GetType()) {
+ case CompositableType::CONTENT_TILED:
+ case CompositableType::CONTENT_SINGLE:
+ case CompositableType::CONTENT_DOUBLE: {
+ ContentHost* newBuffer = static_cast<ContentHost*>(aHost);
+ if (mBuffer && newBuffer != mBuffer) {
+ mBuffer->Detach(this);
+ }
+ mBuffer = newBuffer;
+ return true;
+ }
+ default:
+ return false;
+ }
+}
+
+void PaintedLayerComposite::Disconnect() { Destroy(); }
+
+void PaintedLayerComposite::Destroy() {
+ if (!mDestroyed) {
+ CleanupResources();
+ mDestroyed = true;
+ }
+}
+
+Layer* PaintedLayerComposite::GetLayer() { return this; }
+
+void PaintedLayerComposite::SetLayerManager(HostLayerManager* aManager) {
+ LayerComposite::SetLayerManager(aManager);
+ mManager = aManager;
+ if (mBuffer && mCompositor) {
+ mBuffer->SetTextureSourceProvider(mCompositor);
+ }
+}
+
+void PaintedLayerComposite::RenderLayer(const gfx::IntRect& aClipRect,
+ const Maybe<gfx::Polygon>& aGeometry) {
+ if (!mBuffer || !mBuffer->IsAttached()) {
+ return;
+ }
+ AUTO_PROFILER_LABEL("PaintedLayerComposite::RenderLayer", GRAPHICS);
+
+ Compositor* compositor = mCompositeManager->GetCompositor();
+
+ MOZ_ASSERT(mBuffer->GetTextureSourceProvider() == compositor &&
+ mBuffer->GetLayer() == this,
+ "buffer is corrupted");
+
+ const nsIntRegion visibleRegion = GetLocalVisibleRegion().ToUnknownRegion();
+
+#ifdef MOZ_DUMP_PAINTING
+ if (gfxEnv::DumpCompositorTextures()) {
+ RefPtr<gfx::DataSourceSurface> surf = mBuffer->GetAsSurface();
+ if (surf) {
+ WriteSnapshotToDumpFile(this, surf);
+ }
+ }
+#endif
+
+ RenderWithAllMasks(
+ this, compositor, aClipRect,
+ [&](EffectChain& effectChain, const gfx::IntRect& clipRect) {
+ mBuffer->SetPaintWillResample(MayResample());
+
+ mBuffer->Composite(compositor, this, effectChain, GetEffectiveOpacity(),
+ GetEffectiveTransform(), GetSamplingFilter(),
+ clipRect, &visibleRegion, aGeometry);
+ });
+
+ mBuffer->BumpFlashCounter();
+
+ compositor->MakeCurrent();
+}
+
+CompositableHost* PaintedLayerComposite::GetCompositableHost() {
+ if (mBuffer && mBuffer->IsAttached()) {
+ return mBuffer.get();
+ }
+
+ return nullptr;
+}
+
+void PaintedLayerComposite::CleanupResources() {
+ if (mBuffer) {
+ mBuffer->Detach(this);
+ }
+ mBuffer = nullptr;
+}
+
+bool PaintedLayerComposite::IsOpaque() {
+ if (!mBuffer || !mBuffer->IsAttached()) {
+ return false;
+ }
+ return PaintedLayer::IsOpaque();
+}
+
+void PaintedLayerComposite::GenEffectChain(EffectChain& aEffect) {
+ aEffect.mLayerRef = this;
+ aEffect.mPrimaryEffect = mBuffer->GenEffect(GetSamplingFilter());
+}
+
+void PaintedLayerComposite::PrintInfo(std::stringstream& aStream,
+ const char* aPrefix) {
+ PaintedLayer::PrintInfo(aStream, aPrefix);
+ if (mBuffer && mBuffer->IsAttached()) {
+ aStream << "\n";
+ nsAutoCString pfx(aPrefix);
+ pfx += " ";
+ mBuffer->PrintInfo(aStream, pfx.get());
+ }
+}
+
+const gfx::TiledIntRegion& PaintedLayerComposite::GetInvalidRegion() {
+ if (mBuffer) {
+ nsIntRegion region = mInvalidRegion.GetRegion();
+ mBuffer->AddAnimationInvalidation(region);
+ }
+ return mInvalidRegion;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/composite/PaintedLayerComposite.h b/gfx/layers/composite/PaintedLayerComposite.h
new file mode 100644
index 0000000000..c2c22eb433
--- /dev/null
+++ b/gfx/layers/composite/PaintedLayerComposite.h
@@ -0,0 +1,86 @@
+/* -*- 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/. */
+
+#ifndef GFX_PaintedLayerComposite_H
+#define GFX_PaintedLayerComposite_H
+
+#include "Layers.h" // for Layer (ptr only), etc
+#include "mozilla/gfx/Rect.h"
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/RefPtr.h" // for RefPtr
+#include "mozilla/layers/LayerManagerComposite.h" // for LayerComposite, etc
+#include "mozilla/layers/LayersTypes.h" // for LayerRenderState, etc
+#include "nsRegion.h" // for nsIntRegion
+#include "nscore.h" // for nsACString
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * PaintedLayers use ContentHosts for their compsositable host.
+ * By using different ContentHosts, PaintedLayerComposite support tiled and
+ * non-tiled PaintedLayers and single or double buffering.
+ */
+
+class CompositableHost;
+class ContentHost;
+
+class PaintedLayerComposite : public PaintedLayer, public LayerComposite {
+ public:
+ explicit PaintedLayerComposite(LayerManagerComposite* aManager);
+
+ protected:
+ virtual ~PaintedLayerComposite();
+
+ public:
+ void Disconnect() override;
+
+ CompositableHost* GetCompositableHost() override;
+
+ void Destroy() override;
+
+ Layer* GetLayer() override;
+
+ void SetLayerManager(HostLayerManager* aManager) override;
+
+ void RenderLayer(const gfx::IntRect& aClipRect,
+ const Maybe<gfx::Polygon>& aGeometry) override;
+
+ void CleanupResources() override;
+
+ bool IsOpaque() override;
+
+ void GenEffectChain(EffectChain& aEffect) override;
+
+ bool SetCompositableHost(CompositableHost* aHost) override;
+
+ HostLayer* AsHostLayer() override { return this; }
+
+ void InvalidateRegion(const nsIntRegion& aRegion) override {
+ MOZ_CRASH("PaintedLayerComposites can't fill invalidated regions");
+ }
+
+ const gfx::TiledIntRegion& GetInvalidRegion() override;
+
+ MOZ_LAYER_DECL_NAME("PaintedLayerComposite", TYPE_PAINTED)
+
+ protected:
+ virtual void PrintInfo(std::stringstream& aStream,
+ const char* aPrefix) override;
+
+ private:
+ gfx::SamplingFilter GetSamplingFilter() {
+ return gfx::SamplingFilter::LINEAR;
+ }
+
+ private:
+ RefPtr<ContentHost> mBuffer;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* GFX_PaintedLayerComposite_H */
diff --git a/gfx/layers/composite/TextRenderer.cpp b/gfx/layers/composite/TextRenderer.cpp
new file mode 100644
index 0000000000..3bd1a7d9b3
--- /dev/null
+++ b/gfx/layers/composite/TextRenderer.cpp
@@ -0,0 +1,245 @@
+/* -*- 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 "TextRenderer.h"
+#include "FontData.h"
+#include "ConsolasFontData.h"
+#include "png.h"
+#include "mozilla/Base64.h"
+#include "mozilla/layers/Compositor.h"
+#include "mozilla/layers/TextureHost.h"
+#include "mozilla/layers/Effects.h"
+
+namespace mozilla {
+namespace layers {
+
+using namespace gfx;
+
+const Float sBackgroundOpacity = 0.8f;
+const SurfaceFormat sTextureFormat = SurfaceFormat::B8G8R8A8;
+
+static void PNGAPI info_callback(png_structp png_ptr, png_infop info_ptr) {
+ png_read_update_info(png_ptr, info_ptr);
+}
+
+static void PNGAPI row_callback(png_structp png_ptr, png_bytep new_row,
+ png_uint_32 row_num, int pass) {
+ MOZ_ASSERT(sTextureFormat == SurfaceFormat::B8G8R8A8);
+
+ TextRenderer::FontCache* cache =
+ static_cast<TextRenderer::FontCache*>(png_get_progressive_ptr(png_ptr));
+
+ uint32_t* dst =
+ (uint32_t*)(cache->mMap.mData + cache->mMap.mStride * row_num);
+
+ for (uint32_t x = 0; x < cache->mInfo->mTextureWidth; x++) {
+ // We blend to a transparent white background, this will make text readable
+ // even if it's on a dark background. Without hurting our ability to
+ // interact with the content behind the text.
+ Float alphaValue = Float(0xFF - new_row[x]) / 255.0f;
+ Float baseValue = sBackgroundOpacity * (1.0f - alphaValue);
+ // FIXME(aosmond): PNGs may have color profiles. Should we be checking for
+ // that and performing color management?
+ sRGBColor pixelColor(baseValue, baseValue, baseValue,
+ baseValue + alphaValue);
+ dst[x] = pixelColor.ToABGR();
+ }
+}
+
+TextRenderer::~TextRenderer() = default;
+
+TextRenderer::FontCache::~FontCache() { mGlyphBitmaps->Unmap(); }
+
+void TextRenderer::RenderText(Compositor* aCompositor, const std::string& aText,
+ const IntPoint& aOrigin,
+ const Matrix4x4& aTransform, uint32_t aTextSize,
+ uint32_t aTargetPixelWidth, FontType aFontType) {
+ const FontBitmapInfo* info = GetFontInfo(aFontType);
+
+ // For now we only have a bitmap font with a 24px cell size, so we just
+ // scale it up if the user wants larger text.
+ Float scaleFactor = Float(aTextSize) / Float(info->mCellHeight);
+ aTargetPixelWidth /= scaleFactor;
+
+ RefPtr<TextureSource> src =
+ RenderText(aCompositor, aText, aTargetPixelWidth, aFontType);
+ if (!src) {
+ return;
+ }
+
+ RefPtr<EffectRGB> effect = new EffectRGB(src, true, SamplingFilter::LINEAR);
+ EffectChain chain;
+ chain.mPrimaryEffect = effect;
+
+ Matrix4x4 transform = aTransform;
+ transform.PreScale(scaleFactor, scaleFactor, 1.0f);
+
+ IntRect drawRect(aOrigin, src->GetSize());
+ IntRect clip(-10000, -10000, 20000, 20000);
+ aCompositor->DrawQuad(Rect(drawRect), clip, chain, 1.0f, transform);
+}
+
+IntSize TextRenderer::ComputeSurfaceSize(const std::string& aText,
+ uint32_t aTargetPixelWidth,
+ FontType aFontType) {
+ if (!EnsureInitialized(aFontType)) {
+ return IntSize();
+ }
+
+ FontCache* cache = mFonts[aFontType].get();
+ const FontBitmapInfo* info = cache->mInfo;
+
+ uint32_t numLines = 1;
+ uint32_t maxWidth = 0;
+ uint32_t lineWidth = 0;
+ // Calculate the size of the surface needed to draw all the glyphs.
+ for (uint32_t i = 0; i < aText.length(); i++) {
+ // Insert a line break if we go past the TargetPixelWidth.
+ // XXX - this has the downside of overrunning the intended width, causing
+ // things at the edge of a window to be cut off.
+ if (aText[i] == '\n' ||
+ (aText[i] == ' ' && lineWidth > aTargetPixelWidth)) {
+ numLines++;
+ lineWidth = 0;
+ continue;
+ }
+
+ lineWidth += info->GetGlyphWidth(aText[i]);
+ maxWidth = std::max(lineWidth, maxWidth);
+ }
+
+ return IntSize(maxWidth, numLines * info->mCellHeight);
+}
+
+RefPtr<TextureSource> TextRenderer::RenderText(TextureSourceProvider* aProvider,
+ const std::string& aText,
+ uint32_t aTargetPixelWidth,
+ FontType aFontType) {
+ if (!EnsureInitialized(aFontType)) {
+ return nullptr;
+ }
+
+ IntSize size = ComputeSurfaceSize(aText, aTargetPixelWidth, aFontType);
+
+ // Create a DrawTarget to draw our glyphs to.
+ RefPtr<DrawTarget> dt =
+ Factory::CreateDrawTarget(BackendType::SKIA, size, sTextureFormat);
+
+ RenderTextToDrawTarget(dt, aText, aTargetPixelWidth, aFontType);
+ RefPtr<SourceSurface> surf = dt->Snapshot();
+ RefPtr<DataSourceSurface> dataSurf = surf->GetDataSurface();
+ RefPtr<DataTextureSource> src = aProvider->CreateDataTextureSource();
+
+ if (!src->Update(dataSurf)) {
+ // Upload failed.
+ return nullptr;
+ }
+
+ return src;
+}
+
+void TextRenderer::RenderTextToDrawTarget(DrawTarget* aDrawTarget,
+ const std::string& aText,
+ uint32_t aTargetPixelWidth,
+ FontType aFontType) {
+ if (!EnsureInitialized(aFontType)) {
+ return;
+ }
+
+ // Initialize the DrawTarget to transparent white.
+ // FIXME(aosmond): Should the background be color managed if we color manage
+ // the glyphs or is this part of a mask?
+ IntSize size = aDrawTarget->GetSize();
+ aDrawTarget->FillRect(
+ Rect(0, 0, size.width, size.height),
+ ColorPattern(DeviceColor(1.0, 1.0, 1.0, sBackgroundOpacity)),
+ DrawOptions(1.0, CompositionOp::OP_SOURCE));
+
+ IntPoint currentPos;
+
+ FontCache* cache = mFonts[aFontType].get();
+ const FontBitmapInfo* info = cache->mInfo;
+
+ const unsigned int kGlyphsPerLine = info->mTextureWidth / info->mCellWidth;
+
+ // Copy our glyphs onto the DrawTarget.
+ for (uint32_t i = 0; i < aText.length(); i++) {
+ if (aText[i] == '\n' ||
+ (aText[i] == ' ' && currentPos.x > int32_t(aTargetPixelWidth))) {
+ currentPos.y += info->mCellHeight;
+ currentPos.x = 0;
+ continue;
+ }
+
+ uint32_t index = aText[i] - info->mFirstChar;
+ uint32_t cellIndexY = index / kGlyphsPerLine;
+ uint32_t cellIndexX = index - (cellIndexY * kGlyphsPerLine);
+ uint32_t glyphWidth = info->GetGlyphWidth(aText[i]);
+ IntRect srcRect(cellIndexX * info->mCellWidth,
+ cellIndexY * info->mCellHeight, glyphWidth,
+ info->mCellHeight);
+
+ aDrawTarget->CopySurface(cache->mGlyphBitmaps, srcRect, currentPos);
+
+ currentPos.x += glyphWidth;
+ }
+}
+
+/* static */ const FontBitmapInfo* TextRenderer::GetFontInfo(FontType aType) {
+ switch (aType) {
+ case FontType::Default:
+ return &sDefaultCompositorFont;
+ case FontType::FixedWidth:
+ return &sFixedWidthCompositorFont;
+ default:
+ MOZ_ASSERT_UNREACHABLE("unknown font type");
+ return nullptr;
+ }
+}
+
+bool TextRenderer::EnsureInitialized(FontType aType) {
+ if (mFonts[aType]) {
+ return true;
+ }
+
+ const FontBitmapInfo* info = GetFontInfo(aType);
+
+ IntSize size(info->mTextureWidth, info->mTextureHeight);
+ RefPtr<DataSourceSurface> surface =
+ Factory::CreateDataSourceSurface(size, sTextureFormat);
+ if (NS_WARN_IF(!surface)) {
+ return false;
+ }
+
+ DataSourceSurface::MappedSurface map;
+ if (NS_WARN_IF(!surface->Map(DataSourceSurface::MapType::READ_WRITE, &map))) {
+ return false;
+ }
+
+ UniquePtr<FontCache> cache = MakeUnique<FontCache>();
+ cache->mGlyphBitmaps = surface;
+ cache->mMap = map;
+ cache->mInfo = info;
+
+ png_structp png_ptr = NULL;
+ png_ptr =
+ png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
+
+ png_set_progressive_read_fn(png_ptr, cache.get(), info_callback, row_callback,
+ nullptr);
+ png_infop info_ptr = NULL;
+ info_ptr = png_create_info_struct(png_ptr);
+
+ png_process_data(png_ptr, info_ptr, (uint8_t*)info->mPNG, info->mPNGLength);
+
+ png_destroy_read_struct(&png_ptr, &info_ptr, nullptr);
+
+ mFonts[aType] = std::move(cache);
+ return true;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/composite/TextRenderer.h b/gfx/layers/composite/TextRenderer.h
new file mode 100644
index 0000000000..e88fc1d8f5
--- /dev/null
+++ b/gfx/layers/composite/TextRenderer.h
@@ -0,0 +1,95 @@
+/* -*- 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/. */
+
+#ifndef GFX_TextRenderer_H
+#define GFX_TextRenderer_H
+
+#include "mozilla/EnumeratedArray.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/UniquePtr.h"
+#include "nsISupportsImpl.h"
+#include <string>
+
+namespace mozilla {
+namespace layers {
+
+class Compositor;
+class TextureSource;
+class TextureSourceProvider;
+struct FontBitmapInfo;
+
+class TextRenderer final {
+ ~TextRenderer();
+
+ public:
+ NS_INLINE_DECL_REFCOUNTING(TextRenderer)
+
+ enum class FontType { Default, FixedWidth, NumTypes };
+
+ TextRenderer() = default;
+
+ RefPtr<TextureSource> RenderText(TextureSourceProvider* aProvider,
+ const std::string& aText,
+ uint32_t aTargetPixelWidth,
+ FontType aFontType);
+
+ void RenderText(Compositor* aCompositor, const std::string& aText,
+ const gfx::IntPoint& aOrigin,
+ const gfx::Matrix4x4& aTransform, uint32_t aTextSize,
+ uint32_t aTargetPixelWidth,
+ FontType aFontType = FontType::Default);
+
+ gfx::IntSize ComputeSurfaceSize(const std::string& aText,
+ uint32_t aTargetPixelWidth,
+ FontType aFontType = FontType::Default);
+
+ void RenderTextToDrawTarget(gfx::DrawTarget* aDrawTarget,
+ const std::string& aText,
+ uint32_t aTargetPixelWidth,
+ FontType aFontType = FontType::Default);
+
+ struct FontCache {
+ ~FontCache();
+ RefPtr<gfx::DataSourceSurface> mGlyphBitmaps;
+ gfx::DataSourceSurface::MappedSurface mMap;
+ const FontBitmapInfo* mInfo;
+ };
+
+ protected:
+ // Note that this may still fail to set mGlyphBitmaps to a valid value
+ // if the underlying CreateDataSourceSurface fails for some reason.
+ bool EnsureInitialized(FontType aType);
+
+ static const FontBitmapInfo* GetFontInfo(FontType aType);
+
+ private:
+ EnumeratedArray<FontType, FontType::NumTypes, UniquePtr<FontCache>> mFonts;
+};
+
+struct FontBitmapInfo {
+ Maybe<unsigned int> mGlyphWidth;
+ Maybe<const unsigned short*> mGlyphWidths;
+ unsigned int mTextureWidth;
+ unsigned int mTextureHeight;
+ unsigned int mCellWidth;
+ unsigned int mCellHeight;
+ unsigned int mFirstChar;
+ const unsigned char* mPNG;
+ size_t mPNGLength;
+
+ unsigned int GetGlyphWidth(char aGlyph) const {
+ if (mGlyphWidth) {
+ return mGlyphWidth.value();
+ }
+ MOZ_ASSERT(unsigned(aGlyph) >= mFirstChar);
+ return mGlyphWidths.value()[unsigned(aGlyph) - mFirstChar];
+ }
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/composite/TextureHost.cpp b/gfx/layers/composite/TextureHost.cpp
new file mode 100644
index 0000000000..92ced1ee89
--- /dev/null
+++ b/gfx/layers/composite/TextureHost.cpp
@@ -0,0 +1,1371 @@
+/* -*- 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 "TextureHost.h"
+
+#include "CompositableHost.h" // for CompositableHost
+#include "LayerScope.h"
+#include "mozilla/gfx/2D.h" // for DataSourceSurface, Factory
+#include "mozilla/gfx/gfxVars.h"
+#include "mozilla/ipc/Shmem.h" // for Shmem
+#include "mozilla/layers/AsyncImagePipelineManager.h"
+#include "mozilla/layers/BufferTexture.h"
+#include "mozilla/layers/CompositableTransactionParent.h" // for CompositableParentManager
+#include "mozilla/layers/CompositorBridgeParent.h"
+#include "mozilla/layers/Compositor.h" // for Compositor
+#include "mozilla/layers/ISurfaceAllocator.h" // for ISurfaceAllocator
+#include "mozilla/layers/ImageBridgeParent.h" // for ImageBridgeParent
+#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor, etc
+#include "mozilla/layers/TextureHostBasic.h"
+#include "mozilla/layers/TextureHostOGL.h" // for TextureHostOGL
+#include "mozilla/layers/ImageDataSerializer.h"
+#include "mozilla/layers/TextureClient.h"
+#ifdef XP_DARWIN
+# include "mozilla/layers/TextureSync.h"
+#endif
+#include "mozilla/layers/GPUVideoTextureHost.h"
+#include "mozilla/layers/WebRenderTextureHost.h"
+#include "mozilla/StaticPrefs_layers.h"
+#include "mozilla/StaticPrefs_gfx.h"
+#include "mozilla/webrender/RenderBufferTextureHost.h"
+#include "mozilla/webrender/RenderBufferTextureHostSWGL.h"
+#include "mozilla/webrender/RenderExternalTextureHost.h"
+#include "mozilla/webrender/RenderThread.h"
+#include "mozilla/webrender/WebRenderAPI.h"
+#include "nsAString.h"
+#include "mozilla/RefPtr.h" // for nsRefPtr
+#include "nsPrintfCString.h" // for nsPrintfCString
+#include "mozilla/layers/PTextureParent.h"
+#include "mozilla/Unused.h"
+#include <limits>
+#include "../opengl/CompositorOGL.h"
+
+#include "gfxUtils.h"
+#include "IPDLActor.h"
+
+#ifdef MOZ_ENABLE_D3D10_LAYER
+# include "../d3d11/CompositorD3D11.h"
+#endif
+
+#ifdef MOZ_X11
+# include "mozilla/layers/X11TextureHost.h"
+#endif
+
+#ifdef XP_MACOSX
+# include "../opengl/MacIOSurfaceTextureHostOGL.h"
+#endif
+
+#ifdef XP_WIN
+# include "mozilla/layers/TextureD3D11.h"
+# include "mozilla/layers/TextureDIB.h"
+#endif
+
+#if 0
+# define RECYCLE_LOG(...) printf_stderr(__VA_ARGS__)
+#else
+# define RECYCLE_LOG(...) \
+ do { \
+ } while (0)
+#endif
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * TextureParent is the host-side IPDL glue between TextureClient and
+ * TextureHost. It is an IPDL actor just like LayerParent, CompositableParent,
+ * etc.
+ */
+class TextureParent : public ParentActor<PTextureParent> {
+ public:
+ TextureParent(HostIPCAllocator* aAllocator, uint64_t aSerial,
+ const wr::MaybeExternalImageId& aExternalImageId);
+
+ virtual ~TextureParent();
+
+ bool Init(const SurfaceDescriptor& aSharedData,
+ const ReadLockDescriptor& aReadLock,
+ const LayersBackend& aLayersBackend, const TextureFlags& aFlags);
+
+ void NotifyNotUsed(uint64_t aTransactionId);
+
+ mozilla::ipc::IPCResult RecvRecycleTexture(
+ const TextureFlags& aTextureFlags) final;
+
+ TextureHost* GetTextureHost() { return mTextureHost; }
+
+ void Destroy() override;
+
+ uint64_t GetSerial() const { return mSerial; }
+
+ HostIPCAllocator* mSurfaceAllocator;
+ RefPtr<TextureHost> mTextureHost;
+ // mSerial is unique in TextureClient's process.
+ const uint64_t mSerial;
+ wr::MaybeExternalImageId mExternalImageId;
+};
+
+static bool WrapWithWebRenderTextureHost(ISurfaceAllocator* aDeallocator,
+ LayersBackend aBackend,
+ TextureFlags aFlags) {
+ if ((aFlags & TextureFlags::SNAPSHOT) ||
+ (aBackend != LayersBackend::LAYERS_WR) ||
+ (!aDeallocator->UsesImageBridge() &&
+ !aDeallocator->AsCompositorBridgeParentBase())) {
+ return false;
+ }
+ return true;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+PTextureParent* TextureHost::CreateIPDLActor(
+ HostIPCAllocator* aAllocator, const SurfaceDescriptor& aSharedData,
+ const ReadLockDescriptor& aReadLock, LayersBackend aLayersBackend,
+ TextureFlags aFlags, uint64_t aSerial,
+ const wr::MaybeExternalImageId& aExternalImageId) {
+ TextureParent* actor =
+ new TextureParent(aAllocator, aSerial, aExternalImageId);
+ if (!actor->Init(aSharedData, aReadLock, aLayersBackend, aFlags)) {
+ actor->ActorDestroy(ipc::IProtocol::ActorDestroyReason::FailedConstructor);
+ delete actor;
+ return nullptr;
+ }
+ return actor;
+}
+
+// static
+bool TextureHost::DestroyIPDLActor(PTextureParent* actor) {
+ delete actor;
+ return true;
+}
+
+// static
+bool TextureHost::SendDeleteIPDLActor(PTextureParent* actor) {
+ return PTextureParent::Send__delete__(actor);
+}
+
+// static
+TextureHost* TextureHost::AsTextureHost(PTextureParent* actor) {
+ if (!actor) {
+ return nullptr;
+ }
+ return static_cast<TextureParent*>(actor)->mTextureHost;
+}
+
+// static
+uint64_t TextureHost::GetTextureSerial(PTextureParent* actor) {
+ if (!actor) {
+ return UINT64_MAX;
+ }
+ return static_cast<TextureParent*>(actor)->mSerial;
+}
+
+PTextureParent* TextureHost::GetIPDLActor() { return mActor; }
+
+void TextureHost::SetLastFwdTransactionId(uint64_t aTransactionId) {
+ MOZ_ASSERT(mFwdTransactionId <= aTransactionId);
+ mFwdTransactionId = aTransactionId;
+}
+
+already_AddRefed<TextureHost> CreateDummyBufferTextureHost(
+ mozilla::layers::LayersBackend aBackend,
+ mozilla::layers::TextureFlags aFlags) {
+ // Ensure that the host will delete the memory.
+ aFlags &= ~TextureFlags::DEALLOCATE_CLIENT;
+ UniquePtr<TextureData> textureData(BufferTextureData::Create(
+ gfx::IntSize(1, 1), gfx::SurfaceFormat::B8G8R8A8, gfx::BackendType::SKIA,
+ aBackend, aFlags, TextureAllocationFlags::ALLOC_DEFAULT, nullptr));
+ SurfaceDescriptor surfDesc;
+ textureData->Serialize(surfDesc);
+ const SurfaceDescriptorBuffer& bufferDesc =
+ surfDesc.get_SurfaceDescriptorBuffer();
+ const MemoryOrShmem& data = bufferDesc.data();
+ RefPtr<TextureHost> host =
+ new MemoryTextureHost(reinterpret_cast<uint8_t*>(data.get_uintptr_t()),
+ bufferDesc.desc(), aFlags);
+ return host.forget();
+}
+
+already_AddRefed<TextureHost> TextureHost::Create(
+ const SurfaceDescriptor& aDesc, const ReadLockDescriptor& aReadLock,
+ ISurfaceAllocator* aDeallocator, LayersBackend aBackend,
+ TextureFlags aFlags, wr::MaybeExternalImageId& aExternalImageId) {
+ RefPtr<TextureHost> result;
+
+ switch (aDesc.type()) {
+ case SurfaceDescriptor::TSurfaceDescriptorBuffer:
+ case SurfaceDescriptor::TSurfaceDescriptorDIB:
+ case SurfaceDescriptor::TSurfaceDescriptorFileMapping:
+ case SurfaceDescriptor::TSurfaceDescriptorGPUVideo:
+ result = CreateBackendIndependentTextureHost(aDesc, aDeallocator,
+ aBackend, aFlags);
+ break;
+
+ case SurfaceDescriptor::TEGLImageDescriptor:
+ case SurfaceDescriptor::TSurfaceTextureDescriptor:
+ case SurfaceDescriptor::TSurfaceDescriptorAndroidHardwareBuffer:
+ case SurfaceDescriptor::TSurfaceDescriptorSharedGLTexture:
+ case SurfaceDescriptor::TSurfaceDescriptorDMABuf:
+ result = CreateTextureHostOGL(aDesc, aDeallocator, aBackend, aFlags);
+ break;
+
+ case SurfaceDescriptor::TSurfaceDescriptorMacIOSurface:
+ if (aBackend == LayersBackend::LAYERS_OPENGL ||
+ aBackend == LayersBackend::LAYERS_WR) {
+ result = CreateTextureHostOGL(aDesc, aDeallocator, aBackend, aFlags);
+ break;
+ } else {
+ result = CreateTextureHostBasic(aDesc, aDeallocator, aBackend, aFlags);
+ break;
+ }
+
+#ifdef MOZ_X11
+ case SurfaceDescriptor::TSurfaceDescriptorX11: {
+ if (!aDeallocator->IsSameProcess()) {
+ NS_ERROR(
+ "A client process is trying to peek at our address space using a "
+ "X11Texture!");
+ return nullptr;
+ }
+
+ const SurfaceDescriptorX11& desc = aDesc.get_SurfaceDescriptorX11();
+ result = MakeAndAddRef<X11TextureHost>(aFlags, desc);
+ break;
+ }
+#endif
+
+#ifdef XP_WIN
+ case SurfaceDescriptor::TSurfaceDescriptorD3D10:
+ case SurfaceDescriptor::TSurfaceDescriptorDXGIYCbCr:
+ result = CreateTextureHostD3D11(aDesc, aDeallocator, aBackend, aFlags);
+ break;
+#endif
+ case SurfaceDescriptor::TSurfaceDescriptorRecorded: {
+ const SurfaceDescriptorRecorded& desc =
+ aDesc.get_SurfaceDescriptorRecorded();
+ UniquePtr<SurfaceDescriptor> realDesc =
+ aDeallocator->AsCompositorBridgeParentBase()
+ ->LookupSurfaceDescriptorForClientTexture(desc.textureId());
+ if (!realDesc) {
+ gfxCriticalNote << "Failed to get descriptor for recorded texture.";
+ // Create a dummy to prevent any crashes due to missing IPDL actors.
+ result = CreateDummyBufferTextureHost(aBackend, aFlags);
+ break;
+ }
+
+ result = TextureHost::Create(*realDesc, aReadLock, aDeallocator, aBackend,
+ aFlags, aExternalImageId);
+ return result.forget();
+ }
+ default:
+ MOZ_CRASH("GFX: Unsupported Surface type host");
+ }
+
+ if (!result) {
+ gfxCriticalNote << "TextureHost creation failure type=" << aDesc.type();
+ }
+
+ if (result && WrapWithWebRenderTextureHost(aDeallocator, aBackend, aFlags)) {
+ MOZ_ASSERT(aExternalImageId.isSome());
+ result =
+ new WebRenderTextureHost(aDesc, aFlags, result, aExternalImageId.ref());
+ }
+
+ if (result) {
+ result->DeserializeReadLock(aReadLock, aDeallocator);
+ }
+
+ return result.forget();
+}
+
+already_AddRefed<TextureHost> CreateBackendIndependentTextureHost(
+ const SurfaceDescriptor& aDesc, ISurfaceAllocator* aDeallocator,
+ LayersBackend aBackend, TextureFlags aFlags) {
+ RefPtr<TextureHost> result;
+ switch (aDesc.type()) {
+ case SurfaceDescriptor::TSurfaceDescriptorBuffer: {
+ const SurfaceDescriptorBuffer& bufferDesc =
+ aDesc.get_SurfaceDescriptorBuffer();
+ const MemoryOrShmem& data = bufferDesc.data();
+ switch (data.type()) {
+ case MemoryOrShmem::TShmem: {
+ const ipc::Shmem& shmem = data.get_Shmem();
+ const BufferDescriptor& desc = bufferDesc.desc();
+ if (!shmem.IsReadable()) {
+ // We failed to map the shmem so we can't verify its size. This
+ // should not be a fatal error, so just create the texture with
+ // nothing backing it.
+ result = new ShmemTextureHost(shmem, desc, aDeallocator, aFlags);
+ break;
+ }
+
+ size_t bufSize = shmem.Size<char>();
+ size_t reqSize = SIZE_MAX;
+ switch (desc.type()) {
+ case BufferDescriptor::TYCbCrDescriptor: {
+ const YCbCrDescriptor& ycbcr = desc.get_YCbCrDescriptor();
+ reqSize = ImageDataSerializer::ComputeYCbCrBufferSize(
+ ycbcr.ySize(), ycbcr.yStride(), ycbcr.cbCrSize(),
+ ycbcr.cbCrStride(), ycbcr.yOffset(), ycbcr.cbOffset(),
+ ycbcr.crOffset());
+ break;
+ }
+ case BufferDescriptor::TRGBDescriptor: {
+ const RGBDescriptor& rgb = desc.get_RGBDescriptor();
+ reqSize = ImageDataSerializer::ComputeRGBBufferSize(rgb.size(),
+ rgb.format());
+ break;
+ }
+ default:
+ gfxCriticalError()
+ << "Bad buffer host descriptor " << (int)desc.type();
+ MOZ_CRASH("GFX: Bad descriptor");
+ }
+
+ if (reqSize == 0 || bufSize < reqSize) {
+ NS_ERROR(
+ "A client process gave a shmem too small to fit for its "
+ "descriptor!");
+ return nullptr;
+ }
+
+ result = new ShmemTextureHost(shmem, desc, aDeallocator, aFlags);
+ break;
+ }
+ case MemoryOrShmem::Tuintptr_t: {
+ if (!aDeallocator->IsSameProcess()) {
+ NS_ERROR(
+ "A client process is trying to peek at our address space using "
+ "a MemoryTexture!");
+ return nullptr;
+ }
+
+ result = new MemoryTextureHost(
+ reinterpret_cast<uint8_t*>(data.get_uintptr_t()),
+ bufferDesc.desc(), aFlags);
+ break;
+ }
+ default:
+ gfxCriticalError()
+ << "Failed texture host for backend " << (int)data.type();
+ MOZ_CRASH("GFX: No texture host for backend");
+ }
+ break;
+ }
+ case SurfaceDescriptor::TSurfaceDescriptorGPUVideo: {
+ if (aDesc.get_SurfaceDescriptorGPUVideo().type() ==
+ SurfaceDescriptorGPUVideo::TSurfaceDescriptorPlugin) {
+ MOZ_ASSERT(aDeallocator && aDeallocator->UsesImageBridge());
+ auto ibpBase = static_cast<ImageBridgeParent*>(aDeallocator);
+ result =
+ ibpBase->LookupTextureHost(aDesc.get_SurfaceDescriptorGPUVideo());
+ if (!result) {
+ return nullptr;
+ }
+ MOZ_ASSERT(aFlags == result->GetFlags());
+ break;
+ }
+
+ MOZ_ASSERT(aDesc.get_SurfaceDescriptorGPUVideo().type() ==
+ SurfaceDescriptorGPUVideo::TSurfaceDescriptorRemoteDecoder);
+ result = GPUVideoTextureHost::CreateFromDescriptor(
+ aFlags, aDesc.get_SurfaceDescriptorGPUVideo());
+ break;
+ }
+#ifdef XP_WIN
+ case SurfaceDescriptor::TSurfaceDescriptorDIB: {
+ if (!aDeallocator->IsSameProcess()) {
+ NS_ERROR(
+ "A client process is trying to peek at our address space using a "
+ "DIBTexture!");
+ return nullptr;
+ }
+
+ result = new DIBTextureHost(aFlags, aDesc);
+ break;
+ }
+ case SurfaceDescriptor::TSurfaceDescriptorFileMapping: {
+ result = new TextureHostFileMapping(aFlags, aDesc);
+ break;
+ }
+#endif
+ default: {
+ NS_WARNING("No backend independent TextureHost for this descriptor type");
+ }
+ }
+ return result.forget();
+}
+
+TextureHost::TextureHost(TextureFlags aFlags)
+ : AtomicRefCountedWithFinalize("TextureHost"),
+ mActor(nullptr),
+ mFlags(aFlags),
+ mCompositableCount(0),
+ mFwdTransactionId(0),
+ mReadLocked(false) {}
+
+TextureHost::~TextureHost() {
+ if (mReadLocked) {
+ // If we still have a ReadLock, unlock it. At this point we don't care about
+ // the texture client being written into on the other side since it should
+ // be destroyed by now. But we will hit assertions if we don't ReadUnlock
+ // before destroying the lock itself.
+ ReadUnlock();
+ MaybeNotifyUnlocked();
+ }
+}
+
+void TextureHost::Finalize() {
+ MaybeDestroyRenderTexture();
+
+ if (!(GetFlags() & TextureFlags::DEALLOCATE_CLIENT)) {
+ DeallocateSharedData();
+ DeallocateDeviceData();
+ }
+}
+
+void TextureHost::UnbindTextureSource() {
+ if (mReadLocked) {
+ // This TextureHost is not used anymore. Since most compositor backends are
+ // working asynchronously under the hood a compositor could still be using
+ // this texture, so it is generally best to wait until the end of the next
+ // composition before calling ReadUnlock. We ask the compositor to take care
+ // of that for us.
+ if (mProvider) {
+ mProvider->UnlockAfterComposition(this);
+ } else {
+ // GetCompositor returned null which means no compositor can be using this
+ // texture. We can ReadUnlock right away.
+ ReadUnlock();
+ MaybeNotifyUnlocked();
+ }
+ }
+}
+
+void TextureHost::RecycleTexture(TextureFlags aFlags) {
+ MOZ_ASSERT(GetFlags() & TextureFlags::RECYCLE);
+ MOZ_ASSERT(aFlags & TextureFlags::RECYCLE);
+ mFlags = aFlags;
+}
+
+void TextureHost::NotifyNotUsed() {
+ if (!mActor) {
+ return;
+ }
+
+ // Do not need to call NotifyNotUsed() if TextureHost does not have
+ // TextureFlags::RECYCLE flag nor TextureFlags::WAIT_HOST_USAGE_END flag.
+ if (!(GetFlags() & TextureFlags::RECYCLE) &&
+ !(GetFlags() & TextureFlags::WAIT_HOST_USAGE_END)) {
+ return;
+ }
+
+ // The following cases do not need to defer NotifyNotUsed until next
+ // Composite.
+ // - TextureHost does not have Compositor.
+ // - Compositor is BasicCompositor.
+ // - TextureHost has intermediate buffer.
+ // end of buffer usage.
+ if (!mProvider || HasIntermediateBuffer() ||
+ !mProvider->NotifyNotUsedAfterComposition(this)) {
+ static_cast<TextureParent*>(mActor)->NotifyNotUsed(mFwdTransactionId);
+ return;
+ }
+}
+
+void TextureHost::CallNotifyNotUsed() {
+ if (!mActor) {
+ return;
+ }
+ static_cast<TextureParent*>(mActor)->NotifyNotUsed(mFwdTransactionId);
+}
+
+void TextureHost::MaybeDestroyRenderTexture() {
+ if (mExternalImageId.isNothing()) {
+ // RenderTextureHost was not created
+ return;
+ }
+ // When TextureHost created RenderTextureHost, delete it here.
+ TextureHost::DestroyRenderTexture(mExternalImageId.ref());
+}
+
+void TextureHost::DestroyRenderTexture(
+ const wr::ExternalImageId& aExternalImageId) {
+ wr::RenderThread::Get()->UnregisterExternalImage(
+ wr::AsUint64(aExternalImageId));
+}
+
+void TextureHost::EnsureRenderTexture(
+ const wr::MaybeExternalImageId& aExternalImageId) {
+ if (aExternalImageId.isNothing()) {
+ // TextureHost is wrapped by GPUVideoTextureHost.
+ if (mExternalImageId.isSome()) {
+ // RenderTextureHost was already created.
+ return;
+ }
+ mExternalImageId =
+ Some(AsyncImagePipelineManager::GetNextExternalImageId());
+ } else {
+ // TextureHost is wrapped by WebRenderTextureHost.
+ if (aExternalImageId == mExternalImageId) {
+ // The texture has already been created.
+ return;
+ }
+ MOZ_ASSERT(mExternalImageId.isNothing());
+ mExternalImageId = aExternalImageId;
+ }
+ CreateRenderTexture(mExternalImageId.ref());
+}
+
+void TextureHost::PrintInfo(std::stringstream& aStream, const char* aPrefix) {
+ aStream << aPrefix;
+ aStream << nsPrintfCString("%s (0x%p)", Name(), this).get();
+ // Note: the TextureHost needs to be locked before it is safe to call
+ // GetSize() and GetFormat() on it.
+ if (Lock()) {
+ aStream << " [size=" << GetSize() << "]"
+ << " [format=" << GetFormat() << "]";
+ Unlock();
+ }
+ aStream << " [flags=" << mFlags << "]";
+#ifdef MOZ_DUMP_PAINTING
+ if (StaticPrefs::layers_dump_texture()) {
+ nsAutoCString pfx(aPrefix);
+ pfx += " ";
+
+ aStream << "\n" << pfx.get() << "Surface: ";
+ RefPtr<gfx::DataSourceSurface> dSurf = GetAsSurface();
+ if (dSurf) {
+ aStream << gfxUtils::GetAsLZ4Base64Str(dSurf).get();
+ }
+ }
+#endif
+}
+
+void TextureHost::Updated(const nsIntRegion* aRegion) {
+ LayerScope::ContentChanged(this);
+ UpdatedInternal(aRegion);
+}
+
+TextureSource::TextureSource() : mCompositableCount(0) {}
+
+TextureSource::~TextureSource() = default;
+BufferTextureHost::BufferTextureHost(const BufferDescriptor& aDesc,
+ TextureFlags aFlags)
+ : TextureHost(aFlags),
+ mUpdateSerial(1),
+ mLocked(false),
+ mNeedsFullUpdate(false) {
+ mDescriptor = aDesc;
+ switch (mDescriptor.type()) {
+ case BufferDescriptor::TYCbCrDescriptor: {
+ const YCbCrDescriptor& ycbcr = mDescriptor.get_YCbCrDescriptor();
+ mSize = ycbcr.display().Size();
+ mFormat = gfx::SurfaceFormat::YUV;
+ mHasIntermediateBuffer = ycbcr.hasIntermediateBuffer();
+ break;
+ }
+ case BufferDescriptor::TRGBDescriptor: {
+ const RGBDescriptor& rgb = mDescriptor.get_RGBDescriptor();
+ mSize = rgb.size();
+ mFormat = rgb.format();
+ mHasIntermediateBuffer = rgb.hasIntermediateBuffer();
+ break;
+ }
+ default:
+ gfxCriticalError() << "Bad buffer host descriptor "
+ << (int)mDescriptor.type();
+ MOZ_CRASH("GFX: Bad descriptor");
+ }
+ if (aFlags & TextureFlags::COMPONENT_ALPHA) {
+ // One texture of a component alpha texture pair will start out all white.
+ // This hack allows us to easily make sure that white will be uploaded.
+ // See bug 1138934
+ mNeedsFullUpdate = true;
+ }
+
+#ifdef XP_MACOSX
+ const int kMinSize = 1024;
+ const int kMaxSize = 4096;
+ mUseExternalTextures =
+ kMaxSize >= mSize.width && mSize.width >= kMinSize &&
+ kMaxSize >= mSize.height && mSize.height >= kMinSize &&
+ StaticPrefs::gfx_webrender_enable_client_storage_AtStartup();
+#else
+ mUseExternalTextures = false;
+#endif
+}
+
+BufferTextureHost::~BufferTextureHost() = default;
+
+void BufferTextureHost::UpdatedInternal(const nsIntRegion* aRegion) {
+ ++mUpdateSerial;
+ // If the last frame wasn't uploaded yet, and we -don't- have a partial
+ // update, we still need to update the full surface.
+ if (aRegion && !mNeedsFullUpdate) {
+ mMaybeUpdatedRegion.OrWith(*aRegion);
+ } else {
+ mNeedsFullUpdate = true;
+ }
+ if (GetFlags() & TextureFlags::IMMEDIATE_UPLOAD) {
+ DebugOnly<bool> result =
+ MaybeUpload(!mNeedsFullUpdate ? &mMaybeUpdatedRegion : nullptr);
+ NS_WARNING_ASSERTION(result, "Failed to upload a texture");
+ }
+}
+
+void BufferTextureHost::SetTextureSourceProvider(
+ TextureSourceProvider* aProvider) {
+ if (mProvider == aProvider) {
+ return;
+ }
+ if (mFirstSource && mFirstSource->IsOwnedBy(this)) {
+ mFirstSource->SetOwner(nullptr);
+ }
+ if (mFirstSource) {
+ mFirstSource = nullptr;
+ mNeedsFullUpdate = true;
+ }
+ mProvider = aProvider;
+}
+
+void BufferTextureHost::DeallocateDeviceData() {
+ if (mFirstSource && mFirstSource->NumCompositableRefs() > 0) {
+ // WrappingTextureSourceYCbCrBasic wraps YUV format BufferTextureHost.
+ // When BufferTextureHost is destroyed, data of
+ // WrappingTextureSourceYCbCrBasic becomes invalid.
+ if (mFirstSource->AsWrappingTextureSourceYCbCrBasic() &&
+ mFirstSource->IsOwnedBy(this)) {
+ mFirstSource->SetOwner(nullptr);
+ mFirstSource->DeallocateDeviceData();
+ }
+ return;
+ }
+
+ if (!mFirstSource || !mFirstSource->IsOwnedBy(this)) {
+ mFirstSource = nullptr;
+ return;
+ }
+
+ mFirstSource->SetOwner(nullptr);
+
+ RefPtr<TextureSource> it = mFirstSource;
+ while (it) {
+ it->DeallocateDeviceData();
+ it = it->GetNextSibling();
+ }
+}
+
+bool BufferTextureHost::Lock() {
+ MOZ_ASSERT(!mLocked);
+ if (!UploadIfNeeded()) {
+ return false;
+ }
+ mLocked = !!mFirstSource;
+ return mLocked;
+}
+
+void BufferTextureHost::Unlock() {
+ MOZ_ASSERT(mLocked);
+ mLocked = false;
+}
+
+void BufferTextureHost::CreateRenderTexture(
+ const wr::ExternalImageId& aExternalImageId) {
+ RefPtr<wr::RenderTextureHost> texture;
+
+ if (gfx::gfxVars::UseSoftwareWebRender()) {
+ texture =
+ new wr::RenderBufferTextureHostSWGL(GetBuffer(), GetBufferDescriptor());
+ } else if (UseExternalTextures()) {
+ texture =
+ new wr::RenderExternalTextureHost(GetBuffer(), GetBufferDescriptor());
+ } else {
+ texture =
+ new wr::RenderBufferTextureHost(GetBuffer(), GetBufferDescriptor());
+ }
+
+ wr::RenderThread::Get()->RegisterExternalImage(wr::AsUint64(aExternalImageId),
+ texture.forget());
+}
+
+uint32_t BufferTextureHost::NumSubTextures() {
+ if (GetFormat() == gfx::SurfaceFormat::YUV) {
+ return 3;
+ }
+
+ return 1;
+}
+
+void BufferTextureHost::PushResourceUpdates(
+ wr::TransactionBuilder& aResources, ResourceUpdateOp aOp,
+ const Range<wr::ImageKey>& aImageKeys, const wr::ExternalImageId& aExtID) {
+ auto method = aOp == TextureHost::ADD_IMAGE
+ ? &wr::TransactionBuilder::AddExternalImage
+ : &wr::TransactionBuilder::UpdateExternalImage;
+
+ auto imageType = UseExternalTextures() || gfx::gfxVars::UseSoftwareWebRender()
+ ? wr::ExternalImageType::TextureHandle(
+ wr::ImageBufferKind::TextureRect)
+ : wr::ExternalImageType::Buffer();
+
+ if (GetFormat() != gfx::SurfaceFormat::YUV) {
+ MOZ_ASSERT(aImageKeys.length() == 1);
+
+ wr::ImageDescriptor descriptor(
+ GetSize(),
+ ImageDataSerializer::ComputeRGBStride(GetFormat(), GetSize().width),
+ GetFormat());
+ (aResources.*method)(aImageKeys[0], descriptor, aExtID, imageType, 0);
+ } else {
+ MOZ_ASSERT(aImageKeys.length() == 3);
+
+ const layers::YCbCrDescriptor& desc = mDescriptor.get_YCbCrDescriptor();
+ wr::ImageDescriptor yDescriptor(
+ desc.ySize(), desc.yStride(),
+ SurfaceFormatForColorDepth(desc.colorDepth()));
+ wr::ImageDescriptor cbcrDescriptor(
+ desc.cbCrSize(), desc.cbCrStride(),
+ SurfaceFormatForColorDepth(desc.colorDepth()));
+ (aResources.*method)(aImageKeys[0], yDescriptor, aExtID, imageType, 0);
+ (aResources.*method)(aImageKeys[1], cbcrDescriptor, aExtID, imageType, 1);
+ (aResources.*method)(aImageKeys[2], cbcrDescriptor, aExtID, imageType, 2);
+ }
+}
+
+void BufferTextureHost::PushDisplayItems(wr::DisplayListBuilder& aBuilder,
+ const wr::LayoutRect& aBounds,
+ const wr::LayoutRect& aClip,
+ wr::ImageRendering aFilter,
+ const Range<wr::ImageKey>& aImageKeys,
+ PushDisplayItemFlagSet aFlags) {
+ // SWGL should always try to bypass shaders and composite directly.
+ bool preferCompositorSurface =
+ aFlags.contains(PushDisplayItemFlag::PREFER_COMPOSITOR_SURFACE);
+ bool useExternalSurface =
+ aFlags.contains(PushDisplayItemFlag::SUPPORTS_EXTERNAL_BUFFER_TEXTURES);
+ if (GetFormat() != gfx::SurfaceFormat::YUV) {
+ MOZ_ASSERT(aImageKeys.length() == 1);
+ aBuilder.PushImage(aBounds, aClip, true, aFilter, aImageKeys[0],
+ !(mFlags & TextureFlags::NON_PREMULTIPLIED),
+ wr::ColorF{1.0f, 1.0f, 1.0f, 1.0f},
+ preferCompositorSurface, useExternalSurface);
+ } else {
+ MOZ_ASSERT(aImageKeys.length() == 3);
+ const YCbCrDescriptor& desc = mDescriptor.get_YCbCrDescriptor();
+ aBuilder.PushYCbCrPlanarImage(
+ aBounds, aClip, true, aImageKeys[0], aImageKeys[1], aImageKeys[2],
+ wr::ToWrColorDepth(desc.colorDepth()),
+ wr::ToWrYuvColorSpace(desc.yUVColorSpace()),
+ wr::ToWrColorRange(desc.colorRange()), aFilter, preferCompositorSurface,
+ useExternalSurface);
+ }
+}
+
+void TextureHost::DeserializeReadLock(const ReadLockDescriptor& aDesc,
+ ISurfaceAllocator* aAllocator) {
+ if (mReadLock) {
+ return;
+ }
+
+ mReadLock = TextureReadLock::Deserialize(aDesc, aAllocator);
+}
+
+void TextureHost::SetReadLocked() {
+ if (!mReadLock) {
+ return;
+ }
+ // If mReadLocked is true it means we haven't read unlocked yet and the
+ // content side should not have been able to write into this texture and read
+ // lock again!
+ MOZ_ASSERT(!mReadLocked);
+ mReadLocked = true;
+ if (mProvider) {
+ mProvider->MaybeUnlockBeforeNextComposition(this);
+ }
+}
+
+void TextureHost::ReadUnlock() {
+ if (mReadLock && mReadLocked) {
+ mReadLock->ReadUnlock();
+ mReadLocked = false;
+ }
+}
+
+bool TextureHost::NeedsYFlip() const {
+ return bool(mFlags & TextureFlags::ORIGIN_BOTTOM_LEFT);
+}
+
+bool BufferTextureHost::EnsureWrappingTextureSource() {
+ MOZ_ASSERT(!mHasIntermediateBuffer);
+
+ if (mFirstSource && mFirstSource->IsOwnedBy(this)) {
+ return true;
+ }
+ // We don't own it, apparently.
+ if (mFirstSource) {
+ mNeedsFullUpdate = true;
+ mFirstSource = nullptr;
+ }
+
+ if (!mProvider) {
+ return false;
+ }
+
+ if (mFormat == gfx::SurfaceFormat::YUV) {
+ mFirstSource = mProvider->CreateDataTextureSourceAroundYCbCr(this);
+ } else {
+ uint8_t* data = GetBuffer();
+ if (!data) {
+ return false;
+ }
+ RefPtr<gfx::DataSourceSurface> surf =
+ gfx::Factory::CreateWrappingDataSourceSurface(
+ data, ImageDataSerializer::ComputeRGBStride(mFormat, mSize.width),
+ mSize, mFormat);
+ if (!surf) {
+ return false;
+ }
+ mFirstSource = mProvider->CreateDataTextureSourceAround(surf);
+ }
+
+ if (!mFirstSource) {
+ // BasicCompositor::CreateDataTextureSourceAround never returns null
+ // and we don't expect to take this branch if we are using another backend.
+ // Returning false is fine but if we get into this situation it probably
+ // means something fishy is going on, like a texture being used with
+ // several compositor backends.
+ NS_WARNING("Failed to use a BufferTextureHost without intermediate buffer");
+ return false;
+ }
+
+ mFirstSource->SetUpdateSerial(mUpdateSerial);
+ mFirstSource->SetOwner(this);
+
+ return true;
+}
+
+static bool IsCompatibleTextureSource(TextureSource* aTexture,
+ const BufferDescriptor& aDescriptor,
+ TextureSourceProvider* aProvider) {
+ if (!aProvider) {
+ return false;
+ }
+
+ switch (aDescriptor.type()) {
+ case BufferDescriptor::TYCbCrDescriptor: {
+ const YCbCrDescriptor& ycbcr = aDescriptor.get_YCbCrDescriptor();
+
+ if (!aProvider->SupportsEffect(EffectTypes::YCBCR)) {
+ return aTexture->GetFormat() == gfx::SurfaceFormat::B8G8R8X8 &&
+ aTexture->GetSize() == ycbcr.ySize();
+ }
+
+ if (aTexture->GetFormat() != gfx::SurfaceFormat::A8 ||
+ aTexture->GetSize() != ycbcr.ySize()) {
+ return false;
+ }
+
+ auto cbTexture = aTexture->GetSubSource(1);
+ if (!cbTexture || cbTexture->GetFormat() != gfx::SurfaceFormat::A8 ||
+ cbTexture->GetSize() != ycbcr.cbCrSize()) {
+ return false;
+ }
+
+ auto crTexture = aTexture->GetSubSource(2);
+ if (!crTexture || crTexture->GetFormat() != gfx::SurfaceFormat::A8 ||
+ crTexture->GetSize() != ycbcr.cbCrSize()) {
+ return false;
+ }
+
+ return true;
+ }
+ case BufferDescriptor::TRGBDescriptor: {
+ const RGBDescriptor& rgb = aDescriptor.get_RGBDescriptor();
+ return aTexture->GetFormat() == rgb.format() &&
+ aTexture->GetSize() == rgb.size();
+ }
+ default: {
+ return false;
+ }
+ }
+}
+
+void BufferTextureHost::PrepareTextureSource(
+ CompositableTextureSourceRef& aTexture) {
+ // Reuse WrappingTextureSourceYCbCrBasic to reduce memory consumption.
+ if (mFormat == gfx::SurfaceFormat::YUV && !mHasIntermediateBuffer &&
+ aTexture.get() && aTexture->AsWrappingTextureSourceYCbCrBasic() &&
+ aTexture->NumCompositableRefs() <= 1 &&
+ aTexture->GetSize() == GetSize()) {
+ aTexture->AsSourceBasic()->SetBufferTextureHost(this);
+ aTexture->AsDataTextureSource()->SetOwner(this);
+ mFirstSource = aTexture->AsDataTextureSource();
+ mNeedsFullUpdate = true;
+ }
+
+ if (!mHasIntermediateBuffer) {
+ EnsureWrappingTextureSource();
+ }
+
+ if (mFirstSource && mFirstSource->IsOwnedBy(this)) {
+ // We are already attached to a TextureSource, nothing to do except tell
+ // the compositable to use it.
+ aTexture = mFirstSource.get();
+ return;
+ }
+
+ // We don't own it, apparently.
+ if (mFirstSource) {
+ mNeedsFullUpdate = true;
+ mFirstSource = nullptr;
+ }
+
+ DataTextureSource* texture =
+ aTexture.get() ? aTexture->AsDataTextureSource() : nullptr;
+
+ bool compatibleFormats =
+ texture && IsCompatibleTextureSource(texture, mDescriptor, mProvider);
+
+ bool shouldCreateTexture = !compatibleFormats ||
+ texture->NumCompositableRefs() > 1 ||
+ texture->HasOwner();
+
+ if (!shouldCreateTexture) {
+ mFirstSource = texture;
+ mFirstSource->SetOwner(this);
+ mNeedsFullUpdate = true;
+
+ // It's possible that texture belonged to a different compositor,
+ // so make sure we update it (and all of its siblings) to the
+ // current one.
+ RefPtr<TextureSource> it = mFirstSource;
+ while (it) {
+ it->SetTextureSourceProvider(mProvider);
+ it = it->GetNextSibling();
+ }
+ }
+}
+
+bool BufferTextureHost::BindTextureSource(
+ CompositableTextureSourceRef& aTexture) {
+ MOZ_ASSERT(mLocked);
+ MOZ_ASSERT(mFirstSource);
+ aTexture = mFirstSource;
+ return !!aTexture;
+}
+
+bool BufferTextureHost::AcquireTextureSource(
+ CompositableTextureSourceRef& aTexture) {
+ if (!UploadIfNeeded()) {
+ return false;
+ }
+ aTexture = mFirstSource;
+ return !!mFirstSource;
+}
+
+void BufferTextureHost::ReadUnlock() {
+ if (mFirstSource) {
+ mFirstSource->Sync(true);
+ }
+
+ TextureHost::ReadUnlock();
+}
+
+void BufferTextureHost::MaybeNotifyUnlocked() {
+#ifdef XP_DARWIN
+ auto actor = GetIPDLActor();
+ if (actor) {
+ AutoTArray<uint64_t, 1> serials;
+ serials.AppendElement(TextureHost::GetTextureSerial(actor));
+ TextureSync::SetTexturesUnlocked(actor->OtherPid(), serials);
+ }
+#endif
+}
+
+void BufferTextureHost::UnbindTextureSource() {
+ if (mFirstSource && mFirstSource->IsOwnedBy(this)) {
+ mFirstSource->Unbind();
+ }
+
+ // This texture is not used by any layer anymore.
+ // If the texture doesn't have an intermediate buffer, it means we are
+ // compositing synchronously on the CPU, so we don't need to wait until
+ // the end of the next composition to ReadUnlock (which other textures do
+ // by default).
+ // If the texture has an intermediate buffer we don't care either because
+ // texture uploads are also performed synchronously for BufferTextureHost.
+ ReadUnlock();
+ MaybeNotifyUnlocked();
+}
+
+gfx::SurfaceFormat BufferTextureHost::GetFormat() const {
+ // mFormat is the format of the data that we share with the content process.
+ // GetFormat, on the other hand, expects the format that we present to the
+ // Compositor (it is used to choose the effect type).
+ // if the compositor does not support YCbCr effects, we give it a RGBX texture
+ // instead (see BufferTextureHost::Upload)
+ if (mFormat == gfx::SurfaceFormat::YUV && mProvider &&
+ !mProvider->SupportsEffect(EffectTypes::YCBCR)) {
+ return gfx::SurfaceFormat::R8G8B8X8;
+ }
+ return mFormat;
+}
+
+gfx::YUVColorSpace BufferTextureHost::GetYUVColorSpace() const {
+ if (mFormat == gfx::SurfaceFormat::YUV) {
+ const YCbCrDescriptor& desc = mDescriptor.get_YCbCrDescriptor();
+ return desc.yUVColorSpace();
+ }
+ return gfx::YUVColorSpace::UNKNOWN;
+}
+
+gfx::ColorDepth BufferTextureHost::GetColorDepth() const {
+ if (mFormat == gfx::SurfaceFormat::YUV) {
+ const YCbCrDescriptor& desc = mDescriptor.get_YCbCrDescriptor();
+ return desc.colorDepth();
+ }
+ return gfx::ColorDepth::COLOR_8;
+}
+
+gfx::ColorRange BufferTextureHost::GetColorRange() const {
+ if (mFormat == gfx::SurfaceFormat::YUV) {
+ const YCbCrDescriptor& desc = mDescriptor.get_YCbCrDescriptor();
+ return desc.colorRange();
+ }
+ return TextureHost::GetColorRange();
+}
+
+bool BufferTextureHost::UploadIfNeeded() {
+ return MaybeUpload(!mNeedsFullUpdate ? &mMaybeUpdatedRegion : nullptr);
+}
+
+bool BufferTextureHost::MaybeUpload(nsIntRegion* aRegion) {
+ auto serial = mFirstSource ? mFirstSource->GetUpdateSerial() : 0;
+
+ if (serial == mUpdateSerial) {
+ return true;
+ }
+
+ if (serial == 0) {
+ // 0 means the source has no valid content
+ aRegion = nullptr;
+ }
+
+ if (!Upload(aRegion)) {
+ return false;
+ }
+
+ if (mHasIntermediateBuffer) {
+ // We just did the texture upload, the content side can now freely write
+ // into the shared buffer.
+ ReadUnlock();
+ MaybeNotifyUnlocked();
+ }
+
+ // We no longer have an invalid region.
+ mNeedsFullUpdate = false;
+ mMaybeUpdatedRegion.SetEmpty();
+
+ // If upload returns true we know mFirstSource is not null
+ mFirstSource->SetUpdateSerial(mUpdateSerial);
+ return true;
+}
+
+bool BufferTextureHost::Upload(nsIntRegion* aRegion) {
+ uint8_t* buf = GetBuffer();
+ if (!buf) {
+ // We don't have a buffer; a possible cause is that the IPDL actor
+ // is already dead. This inevitably happens as IPDL actors can die
+ // at any time, so we want to silently return in this case.
+ // another possible cause is that IPDL failed to map the shmem when
+ // deserializing it.
+ return false;
+ }
+ if (!mProvider) {
+ // This can happen if we send textures to a compositable that isn't yet
+ // attached to a layer.
+ return false;
+ }
+ if (!mHasIntermediateBuffer && EnsureWrappingTextureSource()) {
+ if (!mFirstSource || !mFirstSource->IsDirectMap()) {
+ return true;
+ }
+ }
+
+ if (mFormat == gfx::SurfaceFormat::UNKNOWN) {
+ NS_WARNING("BufferTextureHost: unsupported format!");
+ return false;
+ } else if (mFormat == gfx::SurfaceFormat::YUV) {
+ const YCbCrDescriptor& desc = mDescriptor.get_YCbCrDescriptor();
+
+ if (!mProvider->SupportsEffect(EffectTypes::YCBCR)) {
+ RefPtr<gfx::DataSourceSurface> surf =
+ ImageDataSerializer::DataSourceSurfaceFromYCbCrDescriptor(
+ buf, mDescriptor.get_YCbCrDescriptor());
+ if (NS_WARN_IF(!surf)) {
+ return false;
+ }
+ if (!mFirstSource) {
+ mFirstSource = mProvider->CreateDataTextureSource(
+ mFlags | TextureFlags::RGB_FROM_YCBCR);
+ mFirstSource->SetOwner(this);
+ }
+ return mFirstSource->Update(surf, aRegion);
+ }
+
+ RefPtr<DataTextureSource> srcY;
+ RefPtr<DataTextureSource> srcU;
+ RefPtr<DataTextureSource> srcV;
+ if (!mFirstSource) {
+ // We don't support BigImages for YCbCr compositing.
+ srcY = mProvider->CreateDataTextureSource(
+ mFlags | TextureFlags::DISALLOW_BIGIMAGE);
+ srcU = mProvider->CreateDataTextureSource(
+ mFlags | TextureFlags::DISALLOW_BIGIMAGE);
+ srcV = mProvider->CreateDataTextureSource(
+ mFlags | TextureFlags::DISALLOW_BIGIMAGE);
+ mFirstSource = srcY;
+ mFirstSource->SetOwner(this);
+ srcY->SetNextSibling(srcU);
+ srcU->SetNextSibling(srcV);
+ } else {
+ // mFormat never changes so if this was created as a YCbCr host and
+ // already contains a source it should already have 3 sources.
+ // BufferTextureHost only uses DataTextureSources so it is safe to assume
+ // all 3 sources are DataTextureSource.
+ MOZ_ASSERT(mFirstSource->GetNextSibling());
+ MOZ_ASSERT(mFirstSource->GetNextSibling()->GetNextSibling());
+ srcY = mFirstSource;
+ srcU = mFirstSource->GetNextSibling()->AsDataTextureSource();
+ srcV = mFirstSource->GetNextSibling()
+ ->GetNextSibling()
+ ->AsDataTextureSource();
+ }
+
+ RefPtr<gfx::DataSourceSurface> tempY =
+ gfx::Factory::CreateWrappingDataSourceSurface(
+ ImageDataSerializer::GetYChannel(buf, desc), desc.yStride(),
+ desc.ySize(), SurfaceFormatForColorDepth(desc.colorDepth()));
+ RefPtr<gfx::DataSourceSurface> tempCb =
+ gfx::Factory::CreateWrappingDataSourceSurface(
+ ImageDataSerializer::GetCbChannel(buf, desc), desc.cbCrStride(),
+ desc.cbCrSize(), SurfaceFormatForColorDepth(desc.colorDepth()));
+ RefPtr<gfx::DataSourceSurface> tempCr =
+ gfx::Factory::CreateWrappingDataSourceSurface(
+ ImageDataSerializer::GetCrChannel(buf, desc), desc.cbCrStride(),
+ desc.cbCrSize(), SurfaceFormatForColorDepth(desc.colorDepth()));
+ // We don't support partial updates for Y U V textures
+ NS_ASSERTION(!aRegion, "Unsupported partial updates for YCbCr textures");
+ if (!tempY || !tempCb || !tempCr || !srcY->Update(tempY) ||
+ !srcU->Update(tempCb) || !srcV->Update(tempCr)) {
+ NS_WARNING("failed to update the DataTextureSource");
+ return false;
+ }
+ } else {
+ // non-YCbCr case
+ nsIntRegion* regionToUpdate = aRegion;
+ if (!mFirstSource) {
+ mFirstSource = mProvider->CreateDataTextureSource(mFlags);
+ mFirstSource->SetOwner(this);
+ if (mFlags & TextureFlags::COMPONENT_ALPHA) {
+ // Update the full region the first time for component alpha textures.
+ regionToUpdate = nullptr;
+ }
+ }
+
+ RefPtr<gfx::DataSourceSurface> surf =
+ gfx::Factory::CreateWrappingDataSourceSurface(
+ GetBuffer(),
+ ImageDataSerializer::ComputeRGBStride(mFormat, mSize.width), mSize,
+ mFormat);
+ if (!surf) {
+ return false;
+ }
+
+ if (!mFirstSource->Update(surf.get(), regionToUpdate)) {
+ NS_WARNING("failed to update the DataTextureSource");
+ return false;
+ }
+ }
+ MOZ_ASSERT(mFirstSource);
+ return true;
+}
+
+already_AddRefed<gfx::DataSourceSurface> BufferTextureHost::GetAsSurface() {
+ RefPtr<gfx::DataSourceSurface> result;
+ if (mFormat == gfx::SurfaceFormat::UNKNOWN) {
+ NS_WARNING("BufferTextureHost: unsupported format!");
+ return nullptr;
+ } else if (mFormat == gfx::SurfaceFormat::YUV) {
+ result = ImageDataSerializer::DataSourceSurfaceFromYCbCrDescriptor(
+ GetBuffer(), mDescriptor.get_YCbCrDescriptor());
+ if (NS_WARN_IF(!result)) {
+ return nullptr;
+ }
+ } else {
+ result = gfx::Factory::CreateWrappingDataSourceSurface(
+ GetBuffer(),
+ ImageDataSerializer::GetRGBStride(mDescriptor.get_RGBDescriptor()),
+ mSize, mFormat);
+ }
+ return result.forget();
+}
+
+ShmemTextureHost::ShmemTextureHost(const ipc::Shmem& aShmem,
+ const BufferDescriptor& aDesc,
+ ISurfaceAllocator* aDeallocator,
+ TextureFlags aFlags)
+ : BufferTextureHost(aDesc, aFlags), mDeallocator(aDeallocator) {
+ if (aShmem.IsReadable()) {
+ mShmem = MakeUnique<ipc::Shmem>(aShmem);
+ } else {
+ // This can happen if we failed to map the shmem on this process, perhaps
+ // because it was big and we didn't have enough contiguous address space
+ // available, even though we did on the child process.
+ // As a result this texture will be in an invalid state and Lock will
+ // always fail.
+
+ gfxCriticalNote << "Failed to create a valid ShmemTextureHost";
+ }
+
+ MOZ_COUNT_CTOR(ShmemTextureHost);
+}
+
+ShmemTextureHost::~ShmemTextureHost() {
+ MOZ_ASSERT(!mShmem || (mFlags & TextureFlags::DEALLOCATE_CLIENT),
+ "Leaking our buffer");
+ DeallocateDeviceData();
+ MOZ_COUNT_DTOR(ShmemTextureHost);
+}
+
+void ShmemTextureHost::DeallocateSharedData() {
+ if (mShmem) {
+ MOZ_ASSERT(mDeallocator,
+ "Shared memory would leak without a ISurfaceAllocator");
+ mDeallocator->AsShmemAllocator()->DeallocShmem(*mShmem);
+ mShmem = nullptr;
+ }
+}
+
+void ShmemTextureHost::ForgetSharedData() {
+ if (mShmem) {
+ mShmem = nullptr;
+ }
+}
+
+void ShmemTextureHost::OnShutdown() { mShmem = nullptr; }
+
+uint8_t* ShmemTextureHost::GetBuffer() {
+ return mShmem ? mShmem->get<uint8_t>() : nullptr;
+}
+
+size_t ShmemTextureHost::GetBufferSize() {
+ return mShmem ? mShmem->Size<uint8_t>() : 0;
+}
+
+MemoryTextureHost::MemoryTextureHost(uint8_t* aBuffer,
+ const BufferDescriptor& aDesc,
+ TextureFlags aFlags)
+ : BufferTextureHost(aDesc, aFlags), mBuffer(aBuffer) {
+ MOZ_COUNT_CTOR(MemoryTextureHost);
+}
+
+MemoryTextureHost::~MemoryTextureHost() {
+ MOZ_ASSERT(!mBuffer || (mFlags & TextureFlags::DEALLOCATE_CLIENT),
+ "Leaking our buffer");
+ DeallocateDeviceData();
+ MOZ_COUNT_DTOR(MemoryTextureHost);
+}
+
+void MemoryTextureHost::DeallocateSharedData() {
+ if (mBuffer) {
+ GfxMemoryImageReporter::WillFree(mBuffer);
+ }
+ delete[] mBuffer;
+ mBuffer = nullptr;
+}
+
+void MemoryTextureHost::ForgetSharedData() { mBuffer = nullptr; }
+
+uint8_t* MemoryTextureHost::GetBuffer() { return mBuffer; }
+
+size_t MemoryTextureHost::GetBufferSize() {
+ // MemoryTextureHost just trusts that the buffer size is large enough to read
+ // anything we need to. That's because MemoryTextureHost has to trust the
+ // buffer pointer anyway, so the security model here is just that
+ // MemoryTexture's are restricted to same-process clients.
+ return std::numeric_limits<size_t>::max();
+}
+
+TextureParent::TextureParent(HostIPCAllocator* aSurfaceAllocator,
+ uint64_t aSerial,
+ const wr::MaybeExternalImageId& aExternalImageId)
+ : mSurfaceAllocator(aSurfaceAllocator),
+ mSerial(aSerial),
+ mExternalImageId(aExternalImageId) {
+ MOZ_COUNT_CTOR(TextureParent);
+}
+
+TextureParent::~TextureParent() { MOZ_COUNT_DTOR(TextureParent); }
+
+void TextureParent::NotifyNotUsed(uint64_t aTransactionId) {
+ if (!mTextureHost) {
+ return;
+ }
+ mSurfaceAllocator->NotifyNotUsed(this, aTransactionId);
+}
+
+bool TextureParent::Init(const SurfaceDescriptor& aSharedData,
+ const ReadLockDescriptor& aReadLock,
+ const LayersBackend& aBackend,
+ const TextureFlags& aFlags) {
+ mTextureHost = TextureHost::Create(aSharedData, aReadLock, mSurfaceAllocator,
+ aBackend, aFlags, mExternalImageId);
+ if (mTextureHost) {
+ mTextureHost->mActor = this;
+ }
+
+ return !!mTextureHost;
+}
+
+void TextureParent::Destroy() {
+ if (!mTextureHost) {
+ return;
+ }
+
+ if (mTextureHost->mReadLocked) {
+ // ReadUnlock here to make sure the ReadLock's shmem does not outlive the
+ // protocol that created it.
+ mTextureHost->ReadUnlock();
+ mTextureHost->MaybeNotifyUnlocked();
+ }
+
+ if (mTextureHost->GetFlags() & TextureFlags::DEALLOCATE_CLIENT) {
+ mTextureHost->ForgetSharedData();
+ }
+
+ mTextureHost->mActor = nullptr;
+ mTextureHost = nullptr;
+}
+
+void TextureHost::ReceivedDestroy(PTextureParent* aActor) {
+ static_cast<TextureParent*>(aActor)->RecvDestroy();
+}
+
+mozilla::ipc::IPCResult TextureParent::RecvRecycleTexture(
+ const TextureFlags& aTextureFlags) {
+ if (!mTextureHost) {
+ return IPC_OK();
+ }
+ mTextureHost->RecycleTexture(aTextureFlags);
+ return IPC_OK();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/composite/TextureHost.h b/gfx/layers/composite/TextureHost.h
new file mode 100644
index 0000000000..aafbde1e0d
--- /dev/null
+++ b/gfx/layers/composite/TextureHost.h
@@ -0,0 +1,1082 @@
+/* -*- 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/. */
+
+#ifndef MOZILLA_GFX_TEXTUREHOST_H
+#define MOZILLA_GFX_TEXTUREHOST_H
+
+#include <stddef.h> // for size_t
+#include <stdint.h> // for uint64_t, uint32_t, uint8_t
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/RefPtr.h" // for RefPtr, already_AddRefed, etc
+#include "mozilla/gfx/Logging.h"
+#include "mozilla/gfx/Matrix.h"
+#include "mozilla/gfx/Point.h" // for IntSize, IntPoint
+#include "mozilla/gfx/Rect.h"
+#include "mozilla/gfx/Types.h" // for SurfaceFormat, etc
+#include "mozilla/ipc/FileDescriptor.h"
+#include "mozilla/layers/CompositorTypes.h" // for TextureFlags, etc
+#include "mozilla/layers/LayersTypes.h" // for LayerRenderState, etc
+#include "mozilla/layers/LayersSurfaces.h"
+#include "mozilla/mozalloc.h" // for operator delete
+#include "mozilla/Range.h"
+#include "mozilla/UniquePtr.h" // for UniquePtr
+#include "mozilla/webrender/WebRenderTypes.h"
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsDebug.h" // for NS_WARNING
+#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
+#include "nsRect.h"
+#include "nsRegion.h" // for nsIntRegion
+#include "nsTraceRefcnt.h" // for MOZ_COUNT_CTOR, etc
+#include "nscore.h" // for nsACString
+#include "mozilla/layers/AtomicRefCountedWithFinalize.h"
+
+class MacIOSurface;
+namespace mozilla {
+namespace gfx {
+class DataSourceSurface;
+}
+
+namespace ipc {
+class Shmem;
+} // namespace ipc
+
+namespace wr {
+class DisplayListBuilder;
+class TransactionBuilder;
+} // namespace wr
+
+namespace layers {
+
+class AndroidHardwareBuffer;
+class AndroidHardwareBufferTextureHost;
+class BufferDescriptor;
+class BufferTextureHost;
+class Compositor;
+class CompositableParentManager;
+class ReadLockDescriptor;
+class CompositorBridgeParent;
+class SurfaceDescriptor;
+class HostIPCAllocator;
+class ISurfaceAllocator;
+class MacIOSurfaceTextureHostOGL;
+class SurfaceTextureHost;
+class TextureHostOGL;
+class TextureReadLock;
+class TextureSourceOGL;
+class TextureSourceD3D11;
+class TextureSourceBasic;
+class TextureSourceProvider;
+class DataTextureSource;
+class PTextureParent;
+class TextureParent;
+class WebRenderTextureHost;
+class WrappingTextureSourceYCbCrBasic;
+
+/**
+ * A view on a TextureHost where the texture is internally represented as tiles
+ * (contrast with a tiled buffer, where each texture is a tile). For iteration
+ * by the texture's buffer host. This is only useful when the underlying surface
+ * is too big to fit in one device texture, which forces us to split it in
+ * smaller parts. Tiled Compositable is a different thing.
+ */
+class BigImageIterator {
+ public:
+ virtual void BeginBigImageIteration() = 0;
+ virtual void EndBigImageIteration(){};
+ virtual gfx::IntRect GetTileRect() = 0;
+ virtual size_t GetTileCount() = 0;
+ virtual bool NextTile() = 0;
+};
+
+/**
+ * TextureSource is the interface for texture objects that can be composited
+ * by a given compositor backend. Since the drawing APIs are different
+ * between backends, the TextureSource interface is split into different
+ * interfaces (TextureSourceOGL, etc.), and TextureSource mostly provide
+ * access to these interfaces.
+ *
+ * This class is used on the compositor side.
+ */
+class TextureSource : public RefCounted<TextureSource> {
+ public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(TextureSource)
+
+ TextureSource();
+
+ virtual ~TextureSource();
+
+ virtual const char* Name() const = 0;
+
+ /**
+ * Should be overridden in order to deallocate the data that is associated
+ * with the rendering backend, such as GL textures.
+ */
+ virtual void DeallocateDeviceData() {}
+
+ /**
+ * Return the size of the texture in texels.
+ * If this is a tile iterator, GetSize must return the size of the current
+ * tile.
+ */
+ virtual gfx::IntSize GetSize() const = 0;
+
+ /**
+ * Return the pixel format of this texture
+ */
+ virtual gfx::SurfaceFormat GetFormat() const {
+ return gfx::SurfaceFormat::UNKNOWN;
+ }
+
+ /**
+ * Cast to a TextureSource for for each backend..
+ */
+ virtual TextureSourceOGL* AsSourceOGL() {
+ gfxCriticalNote << "Failed to cast " << Name()
+ << " into a TextureSourceOGL";
+ return nullptr;
+ }
+ virtual TextureSourceD3D11* AsSourceD3D11() { return nullptr; }
+ virtual TextureSourceBasic* AsSourceBasic() { return nullptr; }
+ /**
+ * Cast to a DataTextureSurce.
+ */
+ virtual DataTextureSource* AsDataTextureSource() { return nullptr; }
+ virtual WrappingTextureSourceYCbCrBasic* AsWrappingTextureSourceYCbCrBasic() {
+ return nullptr;
+ }
+
+ /**
+ * Overload this if the TextureSource supports big textures that don't fit in
+ * one device texture and must be tiled internally.
+ */
+ virtual BigImageIterator* AsBigImageIterator() { return nullptr; }
+
+ virtual void SetTextureSourceProvider(TextureSourceProvider* aProvider) {}
+
+ virtual void Unbind() {}
+
+ void SetNextSibling(TextureSource* aTexture) { mNextSibling = aTexture; }
+
+ TextureSource* GetNextSibling() const { return mNextSibling; }
+
+ /**
+ * In some rare cases we currently need to consider a group of textures as one
+ * TextureSource, that can be split in sub-TextureSources.
+ */
+ TextureSource* GetSubSource(int index) {
+ switch (index) {
+ case 0:
+ return this;
+ case 1:
+ return GetNextSibling();
+ case 2:
+ return GetNextSibling() ? GetNextSibling()->GetNextSibling() : nullptr;
+ }
+ return nullptr;
+ }
+
+ void AddCompositableRef() { ++mCompositableCount; }
+
+ void ReleaseCompositableRef() {
+ --mCompositableCount;
+ MOZ_ASSERT(mCompositableCount >= 0);
+ }
+
+ // When iterating as a BigImage, this creates temporary TextureSources
+ // wrapping individual tiles.
+ virtual RefPtr<TextureSource> ExtractCurrentTile() {
+ NS_WARNING("Implementation does not expose tile sources");
+ return nullptr;
+ }
+
+ int NumCompositableRefs() const { return mCompositableCount; }
+
+ // Some texture sources could wrap the cpu buffer to gpu directly. Then,
+ // we could get better performance of texture uploading.
+ virtual bool IsDirectMap() { return false; }
+ // The direct-map cpu buffer should be alive when gpu uses it. And it
+ // should not be updated while gpu reads it. This Sync() function
+ // implements this synchronized behavior by allowing us to check if
+ // the GPU is done with the texture, and block on it if aBlocking is
+ // true.
+ virtual bool Sync(bool aBlocking) { return true; }
+
+ protected:
+ RefPtr<TextureSource> mNextSibling;
+ int mCompositableCount;
+};
+
+/// Equivalent of a RefPtr<TextureSource>, that calls AddCompositableRef and
+/// ReleaseCompositableRef in addition to the usual AddRef and Release.
+///
+/// The semantoics of these CompositableTextureRefs are important because they
+/// are used both as a synchronization/safety mechanism, and as an optimization
+/// mechanism. They are also tricky and subtle because we use them in a very
+/// implicit way (assigning to a CompositableTextureRef is less visible than
+/// explicitly calling a method or whatnot).
+/// It is Therefore important to be careful about the way we use this tool.
+///
+/// CompositableTextureRef is a mechanism that lets us count how many
+/// compositables are using a given texture (for TextureSource and TextureHost).
+/// We use it to run specific code when a texture is not used anymore, and also
+/// we trigger fast paths on some operations when we can see that the texture's
+/// CompositableTextureRef counter is equal to 1 (the texture is not shared
+/// between compositables).
+/// This means that it is important to observe the following rules:
+/// * CompositableHosts that receive UseTexture and similar messages *must*
+/// store all of the TextureHosts they receive in CompositableTextureRef slots
+/// for as long as they may be using them.
+/// * CompositableHosts must store each texture in a *single*
+/// CompositableTextureRef slot to ensure that the counter properly reflects how
+/// many compositables are using the texture. If a compositable needs to hold
+/// two references to a given texture (for example to have a pointer to the
+/// current texture in a list of textures that may be used), it can hold its
+/// extra references with RefPtr or whichever pointer type makes sense.
+template <typename T>
+class CompositableTextureRef {
+ public:
+ CompositableTextureRef() = default;
+
+ explicit CompositableTextureRef(const CompositableTextureRef& aOther) {
+ *this = aOther;
+ }
+
+ explicit CompositableTextureRef(T* aOther) { *this = aOther; }
+
+ ~CompositableTextureRef() {
+ if (mRef) {
+ mRef->ReleaseCompositableRef();
+ }
+ }
+
+ CompositableTextureRef& operator=(const CompositableTextureRef& aOther) {
+ if (aOther.get()) {
+ aOther->AddCompositableRef();
+ }
+ if (mRef) {
+ mRef->ReleaseCompositableRef();
+ }
+ mRef = aOther.get();
+ return *this;
+ }
+
+ CompositableTextureRef& operator=(T* aOther) {
+ if (aOther) {
+ aOther->AddCompositableRef();
+ }
+ if (mRef) {
+ mRef->ReleaseCompositableRef();
+ }
+ mRef = aOther;
+ return *this;
+ }
+
+ T* get() const { return mRef; }
+ operator T*() const { return mRef; }
+ T* operator->() const { return mRef; }
+ T& operator*() const { return *mRef; }
+
+ private:
+ RefPtr<T> mRef;
+};
+
+typedef CompositableTextureRef<TextureSource> CompositableTextureSourceRef;
+typedef CompositableTextureRef<TextureHost> CompositableTextureHostRef;
+
+/**
+ * Interface for TextureSources that can be updated from a DataSourceSurface.
+ *
+ * All backend should implement at least one DataTextureSource.
+ */
+class DataTextureSource : public TextureSource {
+ public:
+ DataTextureSource() : mOwner(0), mUpdateSerial(0) {}
+
+ const char* Name() const override { return "DataTextureSource"; }
+
+ DataTextureSource* AsDataTextureSource() override { return this; }
+
+ /**
+ * Upload a (portion of) surface to the TextureSource.
+ *
+ * The DataTextureSource doesn't own aSurface, although it owns and manage
+ * the device texture it uploads to internally.
+ */
+ virtual bool Update(gfx::DataSourceSurface* aSurface,
+ nsIntRegion* aDestRegion = nullptr,
+ gfx::IntPoint* aSrcOffset = nullptr) = 0;
+
+ /**
+ * A facility to avoid reuploading when it is not necessary.
+ * The caller of Update can use GetUpdateSerial to see if the number has
+ * changed since last update, and call SetUpdateSerial after each successful
+ * update. The caller is responsible for managing the update serial except
+ * when the texture data is deallocated in which case the TextureSource should
+ * always reset the update serial to zero.
+ */
+ uint32_t GetUpdateSerial() const { return mUpdateSerial; }
+ void SetUpdateSerial(uint32_t aValue) { mUpdateSerial = aValue; }
+
+ // By default at least set the update serial to zero.
+ // overloaded versions should do that too.
+ void DeallocateDeviceData() override { SetUpdateSerial(0); }
+
+#ifdef DEBUG
+ /**
+ * Provide read access to the data as a DataSourceSurface.
+ *
+ * This is expected to be very slow and should be used for mostly debugging.
+ * XXX - implement everywhere and make it pure virtual.
+ */
+ virtual already_AddRefed<gfx::DataSourceSurface> ReadBack() {
+ return nullptr;
+ };
+#endif
+
+ void SetOwner(TextureHost* aOwner) {
+ auto newOwner = (uintptr_t)aOwner;
+ if (newOwner != mOwner) {
+ mOwner = newOwner;
+ SetUpdateSerial(0);
+ }
+ }
+
+ bool IsOwnedBy(TextureHost* aOwner) const {
+ return mOwner == (uintptr_t)aOwner;
+ }
+
+ bool HasOwner() const { return !IsOwnedBy(nullptr); }
+
+ private:
+ // We store mOwner as an integer rather than as a pointer to make it clear
+ // it is not intended to be dereferenced.
+ uintptr_t mOwner;
+ uint32_t mUpdateSerial;
+};
+
+/**
+ * TextureHost is a thin abstraction over texture data that need to be shared
+ * between the content process and the compositor process. It is the
+ * compositor-side half of a TextureClient/TextureHost pair. A corresponding
+ * TextureClient lives on the content-side.
+ *
+ * TextureHost only knows how to deserialize or synchronize generic image data
+ * (SurfaceDescriptor) and provide access to one or more TextureSource objects
+ * (these provide the necessary APIs for compositor backends to composite the
+ * image).
+ *
+ * A TextureHost implementation corresponds to one SurfaceDescriptor type, as
+ * opposed to TextureSource that corresponds to device textures.
+ * This means that for YCbCr planes, even though they are represented as
+ * 3 textures internally (3 TextureSources), we use 1 TextureHost and not 3,
+ * because the 3 planes are stored in the same buffer of shared memory, before
+ * they are uploaded separately.
+ *
+ * There is always one and only one TextureHost per TextureClient, and the
+ * TextureClient/Host pair only owns one buffer of image data through its
+ * lifetime. This means that the lifetime of the underlying shared data
+ * matches the lifetime of the TextureClient/Host pair. It also means
+ * TextureClient/Host do not implement double buffering, which is the
+ * reponsibility of the compositable (which would use two Texture pairs).
+ *
+ * The Lock/Unlock mecanism here mirrors Lock/Unlock in TextureClient.
+ *
+ */
+class TextureHost : public AtomicRefCountedWithFinalize<TextureHost> {
+ /**
+ * Called once, just before the destructor.
+ *
+ * Here goes the shut-down code that uses virtual methods.
+ * Must only be called by Release().
+ */
+ void Finalize();
+
+ friend class AtomicRefCountedWithFinalize<TextureHost>;
+
+ public:
+ explicit TextureHost(TextureFlags aFlags);
+
+ protected:
+ virtual ~TextureHost();
+
+ public:
+ /**
+ * Factory method.
+ */
+ static already_AddRefed<TextureHost> Create(
+ const SurfaceDescriptor& aDesc, const ReadLockDescriptor& aReadLock,
+ ISurfaceAllocator* aDeallocator, LayersBackend aBackend,
+ TextureFlags aFlags, wr::MaybeExternalImageId& aExternalImageId);
+
+ /**
+ * Lock the texture host for compositing.
+ */
+ virtual bool Lock() { return true; }
+ /**
+ * Unlock the texture host after compositing. Lock() and Unlock() should be
+ * called in pair.
+ */
+ virtual void Unlock() {}
+
+ /**
+ * Lock the texture host for compositing without using compositor.
+ */
+ virtual bool LockWithoutCompositor() { return true; }
+ /**
+ * Similar to Unlock(), but it should be called with LockWithoutCompositor().
+ */
+ virtual void UnlockWithoutCompositor() {}
+
+ /**
+ * Note that the texture host format can be different from its corresponding
+ * texture source's. For example a ShmemTextureHost can have the ycbcr
+ * format and produce 3 "alpha" textures sources.
+ */
+ virtual gfx::SurfaceFormat GetFormat() const = 0;
+ /**
+ * Return the format used for reading the texture.
+ * Apple's YCBCR_422 is R8G8B8X8.
+ */
+ virtual gfx::SurfaceFormat GetReadFormat() const { return GetFormat(); }
+
+ virtual gfx::YUVColorSpace GetYUVColorSpace() const {
+ return gfx::YUVColorSpace::UNKNOWN;
+ }
+
+ /**
+ * Return the color depth of the image. Used with YUV textures.
+ */
+ virtual gfx::ColorDepth GetColorDepth() const {
+ return gfx::ColorDepth::COLOR_8;
+ }
+
+ /**
+ * Return true if using full range values (0-255 if 8 bits YUV). Used with YUV
+ * textures.
+ */
+ virtual gfx::ColorRange GetColorRange() const {
+ return gfx::ColorRange::LIMITED;
+ }
+
+ /**
+ * Called during the transaction. The TextureSource may or may not be
+ * composited.
+ *
+ * Note that this is called outside of lock/unlock.
+ */
+ virtual void PrepareTextureSource(CompositableTextureSourceRef& aTexture) {}
+
+ /**
+ * Called at composition time, just before compositing the TextureSource
+ * composited.
+ *
+ * Note that this is called only withing lock/unlock.
+ */
+ virtual bool BindTextureSource(CompositableTextureSourceRef& aTexture) = 0;
+
+ /**
+ * Called when preparing the rendering pipeline for advanced-layers. This is
+ * a lockless version of BindTextureSource.
+ */
+ virtual bool AcquireTextureSource(CompositableTextureSourceRef& aTexture) {
+ return false;
+ }
+
+ /**
+ * Called when another TextureHost will take over.
+ */
+ virtual void UnbindTextureSource();
+
+ virtual bool IsValid() { return true; }
+
+ /**
+ * Is called before compositing if the shared data has changed since last
+ * composition.
+ * This method should be overload in cases like when we need to do a texture
+ * upload for example.
+ *
+ * @param aRegion The region that has been changed, if nil, it means that the
+ * entire surface should be updated.
+ */
+ void Updated(const nsIntRegion* aRegion = nullptr);
+
+ /**
+ * Sets this TextureHost's compositor. A TextureHost can change compositor
+ * on certain occasions, in particular if it belongs to an async Compositable.
+ * aCompositor can be null, in which case the TextureHost must cleanup all
+ * of its device textures.
+ *
+ * Setting mProvider from this callback implicitly causes the texture to
+ * be locked for an extra frame after being detached from a compositable.
+ */
+ virtual void SetTextureSourceProvider(TextureSourceProvider* aProvider) {}
+
+ /**
+ * Should be overridden in order to deallocate the data that is associated
+ * with the rendering backend, such as GL textures.
+ */
+ virtual void DeallocateDeviceData() {}
+
+ /**
+ * Should be overridden in order to deallocate the data that is shared with
+ * the content side, such as shared memory.
+ */
+ virtual void DeallocateSharedData() {}
+
+ /**
+ * Should be overridden in order to force the TextureHost to drop all
+ * references to it's shared data.
+ *
+ * This is important to ensure the correctness of the deallocation protocol.
+ */
+ virtual void ForgetSharedData() {}
+
+ virtual gfx::IntSize GetSize() const = 0;
+
+ /**
+ * Should be overridden if TextureHost supports crop rect.
+ */
+ virtual void SetCropRect(nsIntRect aCropRect) {}
+
+ /**
+ * Debug facility.
+ * XXX - cool kids use Moz2D. See bug 882113.
+ */
+ virtual already_AddRefed<gfx::DataSourceSurface> GetAsSurface() = 0;
+
+ /**
+ * XXX - Flags should only be set at creation time, this will be removed.
+ */
+ void SetFlags(TextureFlags aFlags) { mFlags = aFlags; }
+
+ /**
+ * XXX - Flags should only be set at creation time, this will be removed.
+ */
+ void AddFlag(TextureFlags aFlag) { mFlags |= aFlag; }
+
+ TextureFlags GetFlags() { return mFlags; }
+
+ /**
+ * Allocate and deallocate a TextureParent actor.
+ *
+ * TextureParent< is an implementation detail of TextureHost that is not
+ * exposed to the rest of the code base. CreateIPDLActor and DestroyIPDLActor
+ * are for use with the managing IPDL protocols only (so that they can
+ * implement AllocPTextureParent and DeallocPTextureParent).
+ */
+ static PTextureParent* CreateIPDLActor(
+ HostIPCAllocator* aAllocator, const SurfaceDescriptor& aSharedData,
+ const ReadLockDescriptor& aDescriptor, LayersBackend aLayersBackend,
+ TextureFlags aFlags, uint64_t aSerial,
+ const wr::MaybeExternalImageId& aExternalImageId);
+ static bool DestroyIPDLActor(PTextureParent* actor);
+
+ /**
+ * Destroy the TextureChild/Parent pair.
+ */
+ static bool SendDeleteIPDLActor(PTextureParent* actor);
+
+ static void ReceivedDestroy(PTextureParent* actor);
+
+ /**
+ * Get the TextureHost corresponding to the actor passed in parameter.
+ */
+ static TextureHost* AsTextureHost(PTextureParent* actor);
+
+ static uint64_t GetTextureSerial(PTextureParent* actor);
+
+ /**
+ * Return a pointer to the IPDLActor.
+ *
+ * This is to be used with IPDL messages only. Do not store the returned
+ * pointer.
+ */
+ PTextureParent* GetIPDLActor();
+
+ // If a texture host holds a reference to shmem, it should override this
+ // method to forget about the shmem _without_ releasing it.
+ virtual void OnShutdown() {}
+
+ // Forget buffer actor. Used only for hacky fix for bug 966446.
+ virtual void ForgetBufferActor() {}
+
+ virtual const char* Name() { return "TextureHost"; }
+ virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix);
+
+ /**
+ * Indicates whether the TextureHost implementation is backed by an
+ * in-memory buffer. The consequence of this is that locking the
+ * TextureHost does not contend with locking the texture on the client side.
+ */
+ virtual bool HasIntermediateBuffer() const { return false; }
+
+ /**
+ * Returns true if the TextureHost can be released before the rendering is
+ * completed, otherwise returns false.
+ */
+ virtual bool NeedsDeferredDeletion() const {
+ return !HasIntermediateBuffer();
+ }
+
+ void AddCompositableRef() {
+ ++mCompositableCount;
+ if (mCompositableCount == 1) {
+ PrepareForUse();
+ }
+ }
+
+ void ReleaseCompositableRef() {
+ --mCompositableCount;
+ MOZ_ASSERT(mCompositableCount >= 0);
+ if (mCompositableCount == 0) {
+ UnbindTextureSource();
+ // Send mFwdTransactionId to client side if necessary.
+ NotifyNotUsed();
+ }
+ }
+
+ int NumCompositableRefs() const { return mCompositableCount; }
+
+ void SetLastFwdTransactionId(uint64_t aTransactionId);
+
+ void DeserializeReadLock(const ReadLockDescriptor& aDesc,
+ ISurfaceAllocator* aAllocator);
+ void SetReadLocked();
+
+ TextureReadLock* GetReadLock() { return mReadLock; }
+
+ virtual BufferTextureHost* AsBufferTextureHost() { return nullptr; }
+ virtual MacIOSurfaceTextureHostOGL* AsMacIOSurfaceTextureHost() {
+ return nullptr;
+ }
+ virtual WebRenderTextureHost* AsWebRenderTextureHost() { return nullptr; }
+ virtual SurfaceTextureHost* AsSurfaceTextureHost() { return nullptr; }
+ virtual AndroidHardwareBufferTextureHost*
+ AsAndroidHardwareBufferTextureHost() {
+ return nullptr;
+ }
+
+ // Create the corresponding RenderTextureHost type of this texture, and
+ // register the RenderTextureHost into render thread.
+ virtual void CreateRenderTexture(
+ const wr::ExternalImageId& aExternalImageId) {
+ MOZ_RELEASE_ASSERT(
+ false,
+ "No CreateRenderTexture() implementation for this TextureHost type.");
+ }
+
+ void EnsureRenderTexture(const wr::MaybeExternalImageId& aExternalImageId);
+
+ // Destroy RenderTextureHost when it was created by the TextureHost.
+ // It is called in TextureHost::Finalize().
+ virtual void MaybeDestroyRenderTexture();
+
+ static void DestroyRenderTexture(const wr::ExternalImageId& aExternalImageId);
+
+ /// Returns the number of actual textures that will be used to render this.
+ /// For example in a lot of YUV cases it will be 3
+ virtual uint32_t NumSubTextures() { return 1; }
+
+ enum ResourceUpdateOp {
+ ADD_IMAGE,
+ UPDATE_IMAGE,
+ };
+
+ // Add all necessary TextureHost informations to the resource update queue.
+ virtual void PushResourceUpdates(wr::TransactionBuilder& aResources,
+ ResourceUpdateOp aOp,
+ const Range<wr::ImageKey>& aImageKeys,
+ const wr::ExternalImageId& aExtID) {
+ MOZ_ASSERT_UNREACHABLE("Unimplemented");
+ }
+
+ enum class PushDisplayItemFlag {
+ // Passed if the caller wants these display items to be promoted
+ // to compositor surfaces if possible.
+ PREFER_COMPOSITOR_SURFACE,
+
+ // Passed in the RenderCompositor supports BufferTextureHosts
+ // being used directly as external compositor surfaces.
+ SUPPORTS_EXTERNAL_BUFFER_TEXTURES,
+ };
+ using PushDisplayItemFlagSet = EnumSet<PushDisplayItemFlag>;
+
+ // Put all necessary WR commands into DisplayListBuilder for this textureHost
+ // rendering.
+ virtual void PushDisplayItems(wr::DisplayListBuilder& aBuilder,
+ const wr::LayoutRect& aBounds,
+ const wr::LayoutRect& aClip,
+ wr::ImageRendering aFilter,
+ const Range<wr::ImageKey>& aKeys,
+ PushDisplayItemFlagSet aFlags) {
+ MOZ_ASSERT_UNREACHABLE(
+ "No PushDisplayItems() implementation for this TextureHost type.");
+ }
+
+ /**
+ * Some API's can use the cross-process IOSurface directly, such as OpenVR
+ */
+ virtual MacIOSurface* GetMacIOSurface() { return nullptr; }
+
+ virtual bool IsDirectMap() { return false; }
+
+ virtual bool NeedsYFlip() const;
+
+ TextureSourceProvider* GetProvider() const { return mProvider; }
+
+ virtual void SetAcquireFence(mozilla::ipc::FileDescriptor&& aFenceFd) {}
+
+ virtual void SetReleaseFence(mozilla::ipc::FileDescriptor&& aFenceFd) {}
+
+ virtual mozilla::ipc::FileDescriptor GetAndResetReleaseFence() {
+ return mozilla::ipc::FileDescriptor();
+ }
+
+ virtual AndroidHardwareBuffer* GetAndroidHardwareBuffer() const {
+ return nullptr;
+ }
+
+ virtual bool SupportsExternalCompositing() { return false; }
+
+ protected:
+ virtual void ReadUnlock();
+
+ void RecycleTexture(TextureFlags aFlags);
+
+ virtual void MaybeNotifyUnlocked() {}
+
+ virtual void UpdatedInternal(const nsIntRegion* Region) {}
+
+ /**
+ * Called when mCompositableCount becomes from 0 to 1.
+ */
+ virtual void PrepareForUse() {}
+
+ /**
+ * Called when mCompositableCount becomes 0.
+ */
+ virtual void NotifyNotUsed();
+
+ // for Compositor.
+ void CallNotifyNotUsed();
+
+ PTextureParent* mActor;
+ RefPtr<TextureSourceProvider> mProvider;
+ RefPtr<TextureReadLock> mReadLock;
+ TextureFlags mFlags;
+ int mCompositableCount;
+ uint64_t mFwdTransactionId;
+ bool mReadLocked;
+ wr::MaybeExternalImageId mExternalImageId;
+
+ friend class Compositor;
+ friend class TextureParent;
+ friend class TiledLayerBufferComposite;
+ friend class TextureSourceProvider;
+ friend class GPUVideoTextureHost;
+ friend class WebRenderTextureHost;
+};
+
+/**
+ * TextureHost that wraps a random access buffer such as a Shmem or some raw
+ * memory.
+ *
+ * This TextureHost is backend-independent and the backend-specific bits are
+ * in the TextureSource.
+ * This class must be inherited to implement GetBuffer and DeallocSharedData
+ * (see ShmemTextureHost and MemoryTextureHost)
+ *
+ * Uploads happen when Lock is called.
+ *
+ * BufferTextureHost supports YCbCr and flavours of RGBA images (RGBX, A, etc.).
+ */
+class BufferTextureHost : public TextureHost {
+ public:
+ BufferTextureHost(const BufferDescriptor& aDescriptor, TextureFlags aFlags);
+
+ virtual ~BufferTextureHost();
+
+ virtual uint8_t* GetBuffer() = 0;
+
+ virtual size_t GetBufferSize() = 0;
+
+ bool Lock() override;
+
+ void Unlock() override;
+
+ void PrepareTextureSource(CompositableTextureSourceRef& aTexture) override;
+
+ bool BindTextureSource(CompositableTextureSourceRef& aTexture) override;
+ bool AcquireTextureSource(CompositableTextureSourceRef& aTexture) override;
+
+ void UnbindTextureSource() override;
+
+ void DeallocateDeviceData() override;
+
+ void SetTextureSourceProvider(TextureSourceProvider* aProvider) override;
+
+ /**
+ * Return the format that is exposed to the compositor when calling
+ * BindTextureSource.
+ *
+ * If the shared format is YCbCr and the compositor does not support it,
+ * GetFormat will be RGB32 (even though mFormat is SurfaceFormat::YUV).
+ */
+ gfx::SurfaceFormat GetFormat() const override;
+
+ gfx::YUVColorSpace GetYUVColorSpace() const override;
+
+ gfx::ColorDepth GetColorDepth() const override;
+
+ gfx::ColorRange GetColorRange() const override;
+
+ gfx::IntSize GetSize() const override { return mSize; }
+
+ already_AddRefed<gfx::DataSourceSurface> GetAsSurface() override;
+
+ bool HasIntermediateBuffer() const override { return mHasIntermediateBuffer; }
+
+ bool NeedsDeferredDeletion() const override {
+ return TextureHost::NeedsDeferredDeletion() || UseExternalTextures();
+ }
+
+ BufferTextureHost* AsBufferTextureHost() override { return this; }
+
+ const BufferDescriptor& GetBufferDescriptor() const { return mDescriptor; }
+
+ void CreateRenderTexture(
+ const wr::ExternalImageId& aExternalImageId) override;
+
+ uint32_t NumSubTextures() override;
+
+ void PushResourceUpdates(wr::TransactionBuilder& aResources,
+ ResourceUpdateOp aOp,
+ const Range<wr::ImageKey>& aImageKeys,
+ const wr::ExternalImageId& aExtID) override;
+
+ void PushDisplayItems(wr::DisplayListBuilder& aBuilder,
+ const wr::LayoutRect& aBounds,
+ const wr::LayoutRect& aClip, wr::ImageRendering aFilter,
+ const Range<wr::ImageKey>& aImageKeys,
+ PushDisplayItemFlagSet aFlags) override;
+
+ void ReadUnlock() override;
+ bool IsDirectMap() override {
+ return mFirstSource && mFirstSource->IsDirectMap();
+ };
+
+ bool CanUnlock() { return !mFirstSource || mFirstSource->Sync(false); }
+ void DisableExternalTextures() { mUseExternalTextures = false; }
+
+ protected:
+ bool UseExternalTextures() const { return mUseExternalTextures; }
+ bool Upload(nsIntRegion* aRegion = nullptr);
+ bool UploadIfNeeded();
+ bool MaybeUpload(nsIntRegion* aRegion);
+ bool EnsureWrappingTextureSource();
+
+ void UpdatedInternal(const nsIntRegion* aRegion = nullptr) override;
+ void MaybeNotifyUnlocked() override;
+
+ BufferDescriptor mDescriptor;
+ RefPtr<Compositor> mCompositor;
+ RefPtr<DataTextureSource> mFirstSource;
+ nsIntRegion mMaybeUpdatedRegion;
+ gfx::IntSize mSize;
+ gfx::SurfaceFormat mFormat;
+ uint32_t mUpdateSerial;
+ bool mLocked;
+ bool mNeedsFullUpdate;
+ bool mHasIntermediateBuffer;
+ bool mUseExternalTextures;
+
+ class DataTextureSourceYCbCrBasic;
+};
+
+/**
+ * TextureHost that wraps shared memory.
+ * the corresponding texture on the client side is ShmemTextureClient.
+ * This TextureHost is backend-independent.
+ */
+class ShmemTextureHost : public BufferTextureHost {
+ public:
+ ShmemTextureHost(const mozilla::ipc::Shmem& aShmem,
+ const BufferDescriptor& aDesc,
+ ISurfaceAllocator* aDeallocator, TextureFlags aFlags);
+
+ protected:
+ ~ShmemTextureHost();
+
+ public:
+ void DeallocateSharedData() override;
+
+ void ForgetSharedData() override;
+
+ uint8_t* GetBuffer() override;
+
+ size_t GetBufferSize() override;
+
+ const char* Name() override { return "ShmemTextureHost"; }
+
+ void OnShutdown() override;
+
+ protected:
+ UniquePtr<mozilla::ipc::Shmem> mShmem;
+ RefPtr<ISurfaceAllocator> mDeallocator;
+};
+
+/**
+ * TextureHost that wraps raw memory.
+ * The corresponding texture on the client side is MemoryTextureClient.
+ * Can obviously not be used in a cross process setup.
+ * This TextureHost is backend-independent.
+ */
+class MemoryTextureHost : public BufferTextureHost {
+ public:
+ MemoryTextureHost(uint8_t* aBuffer, const BufferDescriptor& aDesc,
+ TextureFlags aFlags);
+
+ protected:
+ ~MemoryTextureHost();
+
+ public:
+ void DeallocateSharedData() override;
+
+ void ForgetSharedData() override;
+
+ uint8_t* GetBuffer() override;
+
+ size_t GetBufferSize() override;
+
+ const char* Name() override { return "MemoryTextureHost"; }
+
+ protected:
+ uint8_t* mBuffer;
+};
+
+class MOZ_STACK_CLASS AutoLockTextureHost {
+ public:
+ explicit AutoLockTextureHost(TextureHost* aTexture) : mTexture(aTexture) {
+ mLocked = mTexture ? mTexture->Lock() : false;
+ }
+
+ ~AutoLockTextureHost() {
+ if (mTexture && mLocked) {
+ mTexture->Unlock();
+ }
+ }
+
+ bool Failed() { return mTexture && !mLocked; }
+
+ private:
+ RefPtr<TextureHost> mTexture;
+ bool mLocked;
+};
+
+class MOZ_STACK_CLASS AutoLockTextureHostWithoutCompositor {
+ public:
+ explicit AutoLockTextureHostWithoutCompositor(TextureHost* aTexture)
+ : mTexture(aTexture) {
+ mLocked = mTexture ? mTexture->LockWithoutCompositor() : false;
+ }
+
+ ~AutoLockTextureHostWithoutCompositor() {
+ if (mTexture && mLocked) {
+ mTexture->UnlockWithoutCompositor();
+ }
+ }
+
+ bool Failed() { return mTexture && !mLocked; }
+
+ private:
+ RefPtr<TextureHost> mTexture;
+ bool mLocked;
+};
+
+/**
+ * This can be used as an offscreen rendering target by the compositor, and
+ * subsequently can be used as a source by the compositor.
+ */
+class CompositingRenderTarget : public TextureSource {
+ public:
+ explicit CompositingRenderTarget(const gfx::IntPoint& aOrigin)
+ : mClearOnBind(false),
+ mOrigin(aOrigin),
+ mZNear(0),
+ mZFar(0),
+ mHasComplexProjection(false),
+ mEnableDepthBuffer(false) {}
+ virtual ~CompositingRenderTarget() = default;
+
+ const char* Name() const override { return "CompositingRenderTarget"; }
+
+#ifdef MOZ_DUMP_PAINTING
+ virtual already_AddRefed<gfx::DataSourceSurface> Dump(
+ Compositor* aCompositor) {
+ return nullptr;
+ }
+#endif
+
+ /**
+ * Perform a clear when recycling a non opaque surface.
+ * The clear is deferred to when the render target is bound.
+ */
+ void ClearOnBind() { mClearOnBind = true; }
+
+ const gfx::IntPoint& GetOrigin() const { return mOrigin; }
+ gfx::IntRect GetRect() { return gfx::IntRect(GetOrigin(), GetSize()); }
+
+ /**
+ * If a Projection matrix is set, then it is used for rendering to
+ * this render target instead of generating one. If no explicit
+ * projection is set, Compositors are expected to generate an
+ * orthogonal maaping that maps 0..1 to the full size of the render
+ * target.
+ */
+ bool HasComplexProjection() const { return mHasComplexProjection; }
+ void ClearProjection() { mHasComplexProjection = false; }
+ void SetProjection(const gfx::Matrix4x4& aNewMatrix, bool aEnableDepthBuffer,
+ float aZNear, float aZFar) {
+ mProjectionMatrix = aNewMatrix;
+ mEnableDepthBuffer = aEnableDepthBuffer;
+ mZNear = aZNear;
+ mZFar = aZFar;
+ mHasComplexProjection = true;
+ }
+ void GetProjection(gfx::Matrix4x4& aMatrix, bool& aEnableDepth, float& aZNear,
+ float& aZFar) {
+ MOZ_ASSERT(mHasComplexProjection);
+ aMatrix = mProjectionMatrix;
+ aEnableDepth = mEnableDepthBuffer;
+ aZNear = mZNear;
+ aZFar = mZFar;
+ }
+
+ protected:
+ bool mClearOnBind;
+
+ private:
+ gfx::IntPoint mOrigin;
+
+ gfx::Matrix4x4 mProjectionMatrix;
+ float mZNear, mZFar;
+ bool mHasComplexProjection;
+ bool mEnableDepthBuffer;
+};
+
+/**
+ * Creates a TextureHost that can be used with any of the existing backends
+ * Not all SurfaceDescriptor types are supported
+ */
+already_AddRefed<TextureHost> CreateBackendIndependentTextureHost(
+ const SurfaceDescriptor& aDesc, ISurfaceAllocator* aDeallocator,
+ LayersBackend aBackend, TextureFlags aFlags);
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/composite/TiledContentHost.cpp b/gfx/layers/composite/TiledContentHost.cpp
new file mode 100644
index 0000000000..02f7c3b846
--- /dev/null
+++ b/gfx/layers/composite/TiledContentHost.cpp
@@ -0,0 +1,658 @@
+/* -*- 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 "TiledContentHost.h"
+#include "PaintedLayerComposite.h" // for PaintedLayerComposite
+#include "mozilla/gfx/BaseSize.h" // for BaseSize
+#include "mozilla/gfx/Matrix.h" // for Matrix4x4
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "mozilla/layers/Compositor.h" // for Compositor
+// clang-format off
+//#include "mozilla/layers/CompositorBridgeParent.h" // for CompositorBridgeParent
+// clang-format on
+#include "mozilla/layers/Effects.h" // for TexturedEffect, Effect, etc
+#include "mozilla/layers/LayerMetricsWrapper.h" // for LayerMetricsWrapper
+#include "mozilla/layers/PTextureParent.h"
+#include "mozilla/layers/TextureHostOGL.h" // for TextureHostOGL
+#ifdef XP_DARWIN
+# include "mozilla/layers/TextureSync.h" // for TextureSync
+#endif
+#include "nsAString.h"
+#include "nsDebug.h" // for NS_WARNING
+#include "nsPoint.h" // for IntPoint
+#include "nsPrintfCString.h" // for nsPrintfCString
+#include "nsRect.h" // for IntRect
+#include "mozilla/StaticPrefs_layers.h"
+#include "mozilla/layers/TextureClient.h"
+
+namespace mozilla {
+using namespace gfx;
+namespace layers {
+
+class Layer;
+
+float TileHost::GetFadeInOpacity(float aOpacity) {
+ TimeStamp now = TimeStamp::Now();
+ if (!StaticPrefs::layers_tiles_fade_in_enabled() || mFadeStart.IsNull() ||
+ now < mFadeStart) {
+ return aOpacity;
+ }
+
+ float duration = StaticPrefs::layers_tiles_fade_in_duration_ms();
+ float elapsed = (now - mFadeStart).ToMilliseconds();
+ if (elapsed > duration) {
+ mFadeStart = TimeStamp();
+ return aOpacity;
+ }
+ return aOpacity * (elapsed / duration);
+}
+
+RefPtr<TextureSource> TileHost::AcquireTextureSource() const {
+ if (!mTextureHost || !mTextureHost->AcquireTextureSource(mTextureSource)) {
+ return nullptr;
+ }
+ return mTextureSource.get();
+}
+
+RefPtr<TextureSource> TileHost::AcquireTextureSourceOnWhite() const {
+ if (!mTextureHostOnWhite ||
+ !mTextureHostOnWhite->AcquireTextureSource(mTextureSourceOnWhite)) {
+ return nullptr;
+ }
+ return mTextureSourceOnWhite.get();
+}
+
+TiledLayerBufferComposite::TiledLayerBufferComposite() : mFrameResolution() {}
+
+TiledLayerBufferComposite::~TiledLayerBufferComposite() { Clear(); }
+
+void TiledLayerBufferComposite::SetTextureSourceProvider(
+ TextureSourceProvider* aProvider) {
+ MOZ_ASSERT(aProvider);
+ for (TileHost& tile : mRetainedTiles) {
+ if (tile.IsPlaceholderTile()) continue;
+ tile.mTextureHost->SetTextureSourceProvider(aProvider);
+ if (tile.mTextureHostOnWhite) {
+ tile.mTextureHostOnWhite->SetTextureSourceProvider(aProvider);
+ }
+ }
+}
+
+void TiledLayerBufferComposite::AddAnimationInvalidation(nsIntRegion& aRegion) {
+ // We need to invalidate rects where we have a tile that is in the
+ // process of fading in.
+ for (size_t i = 0; i < mRetainedTiles.Length(); i++) {
+ if (!mRetainedTiles[i].mFadeStart.IsNull()) {
+ TileCoordIntPoint coord = mTiles.TileCoord(i);
+ IntPoint offset = GetTileOffset(coord);
+ nsIntRegion tileRegion = IntRect(offset, GetScaledTileSize());
+ aRegion.OrWith(tileRegion);
+ }
+ }
+}
+
+TiledContentHost::TiledContentHost(const TextureInfo& aTextureInfo)
+ : ContentHost(aTextureInfo),
+ mTiledBuffer(TiledLayerBufferComposite()),
+ mLowPrecisionTiledBuffer(TiledLayerBufferComposite()) {
+ MOZ_COUNT_CTOR(TiledContentHost);
+}
+
+TiledContentHost::~TiledContentHost() { MOZ_COUNT_DTOR(TiledContentHost); }
+
+already_AddRefed<TexturedEffect> TiledContentHost::GenEffect(
+ const gfx::SamplingFilter aSamplingFilter) {
+ MOZ_ASSERT(mTiledBuffer.GetTileCount() == 1 &&
+ mLowPrecisionTiledBuffer.GetTileCount() == 0);
+ MOZ_ASSERT(mTiledBuffer.GetTile(0).mTextureHost);
+
+ TileHost& tile = mTiledBuffer.GetTile(0);
+ if (!tile.mTextureHost->BindTextureSource(tile.mTextureSource)) {
+ return nullptr;
+ }
+
+ return CreateTexturedEffect(tile.mTextureSource, nullptr, aSamplingFilter,
+ true);
+}
+
+void TiledContentHost::Attach(Layer* aLayer, TextureSourceProvider* aProvider,
+ AttachFlags aFlags /* = NO_FLAGS */) {
+ CompositableHost::Attach(aLayer, aProvider, aFlags);
+}
+
+void TiledContentHost::Detach(Layer* aLayer,
+ AttachFlags aFlags /* = NO_FLAGS */) {
+ if (!mKeepAttached || aLayer == mLayer || aFlags & FORCE_DETACH) {
+ // Clear the TiledLayerBuffers, which will take care of releasing the
+ // copy-on-write locks.
+ mTiledBuffer.Clear();
+ mLowPrecisionTiledBuffer.Clear();
+ }
+ CompositableHost::Detach(aLayer, aFlags);
+}
+
+bool TiledContentHost::UseTiledLayerBuffer(
+ ISurfaceAllocator* aAllocator,
+ const SurfaceDescriptorTiles& aTiledDescriptor) {
+ HostLayerManager* lm = GetLayerManager();
+ if (!lm) {
+ return false;
+ }
+
+ if (aTiledDescriptor.resolution() < 1) {
+ if (!mLowPrecisionTiledBuffer.UseTiles(aTiledDescriptor, lm, aAllocator)) {
+ return false;
+ }
+ } else {
+ if (!mTiledBuffer.UseTiles(aTiledDescriptor, lm, aAllocator)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+static void UseTileTexture(CompositableTextureHostRef& aTexture,
+ CompositableTextureSourceRef& aTextureSource,
+ const IntRect& aUpdateRect,
+ TextureSourceProvider* aProvider) {
+ MOZ_ASSERT(aTexture);
+ if (!aTexture) {
+ return;
+ }
+
+ if (aProvider) {
+ aTexture->SetTextureSourceProvider(aProvider);
+ }
+
+ if (!aUpdateRect.IsEmpty()) {
+ // For !HasIntermediateBuffer() textures, this is likely a no-op.
+ nsIntRegion region = aUpdateRect;
+ aTexture->Updated(&region);
+ }
+
+ aTexture->PrepareTextureSource(aTextureSource);
+}
+
+class TextureSourceRecycler {
+ public:
+ explicit TextureSourceRecycler(nsTArray<TileHost>&& aTileSet)
+ : mTiles(std::move(aTileSet)), mFirstPossibility(0) {}
+
+ // Attempts to recycle a texture source that is already bound to the
+ // texture host for aTile.
+ void RecycleTextureSourceForTile(TileHost& aTile) {
+ for (size_t i = mFirstPossibility; i < mTiles.Length(); i++) {
+ // Skip over existing tiles without a retained texture source
+ // and make sure we don't iterate them in the future.
+ if (!mTiles[i].mTextureSource) {
+ if (i == mFirstPossibility) {
+ mFirstPossibility++;
+ }
+ continue;
+ }
+
+ // If this tile matches, then copy across the retained texture source (if
+ // any).
+ if (aTile.mTextureHost == mTiles[i].mTextureHost) {
+ aTile.mTextureSource = std::move(mTiles[i].mTextureSource);
+ if (aTile.mTextureHostOnWhite) {
+ aTile.mTextureSourceOnWhite =
+ std::move(mTiles[i].mTextureSourceOnWhite);
+ }
+ break;
+ }
+ }
+ }
+
+ // Attempts to recycle any texture source to avoid needing to allocate
+ // a new one.
+ void RecycleTextureSource(TileHost& aTile) {
+ for (size_t i = mFirstPossibility; i < mTiles.Length(); i++) {
+ if (!mTiles[i].mTextureSource) {
+ if (i == mFirstPossibility) {
+ mFirstPossibility++;
+ }
+ continue;
+ }
+
+ if (mTiles[i].mTextureSource && mTiles[i].mTextureHost->GetFormat() ==
+ aTile.mTextureHost->GetFormat()) {
+ aTile.mTextureSource = std::move(mTiles[i].mTextureSource);
+ if (aTile.mTextureHostOnWhite) {
+ aTile.mTextureSourceOnWhite =
+ std::move(mTiles[i].mTextureSourceOnWhite);
+ }
+ break;
+ }
+ }
+ }
+
+ void RecycleTileFading(TileHost& aTile) {
+ for (size_t i = 0; i < mTiles.Length(); i++) {
+ if (mTiles[i].mTextureHost == aTile.mTextureHost) {
+ aTile.mFadeStart = mTiles[i].mFadeStart;
+ }
+ }
+ }
+
+ protected:
+ nsTArray<TileHost> mTiles;
+ size_t mFirstPossibility;
+};
+
+bool TiledLayerBufferComposite::UseTiles(const SurfaceDescriptorTiles& aTiles,
+ HostLayerManager* aLayerManager,
+ ISurfaceAllocator* aAllocator) {
+ if (mResolution != aTiles.resolution() || aTiles.tileSize() != mTileSize) {
+ Clear();
+ }
+ MOZ_ASSERT(aAllocator);
+ MOZ_ASSERT(aLayerManager);
+ if (!aAllocator || !aLayerManager) {
+ return false;
+ }
+
+ if (aTiles.resolution() == 0 || IsNaN(aTiles.resolution())) {
+ // There are divisions by mResolution so this protects the compositor
+ // process against malicious content processes and fuzzing.
+ return false;
+ }
+
+ TilesPlacement newTiles(aTiles.firstTileX(), aTiles.firstTileY(),
+ aTiles.retainedWidth(), aTiles.retainedHeight());
+
+ const nsTArray<TileDescriptor>& tileDescriptors = aTiles.tiles();
+
+ TextureSourceRecycler oldRetainedTiles(std::move(mRetainedTiles));
+ mRetainedTiles.SetLength(tileDescriptors.Length());
+
+ AutoTArray<uint64_t, 10> lockedTextureSerials;
+ base::ProcessId lockedTexturePid = 0;
+
+ // Step 1, deserialize the incoming set of tiles into mRetainedTiles, and
+ // attempt to recycle the TextureSource for any repeated tiles.
+ //
+ // Since we don't have any retained 'tile' object, we have to search for
+ // instances of the same TextureHost in the old tile set. The cost of binding
+ // a TextureHost to a TextureSource for gralloc (binding EGLImage to GL
+ // texture) can be really high, so we avoid this whenever possible.
+ for (size_t i = 0; i < tileDescriptors.Length(); i++) {
+ const TileDescriptor& tileDesc = tileDescriptors[i];
+
+ TileHost& tile = mRetainedTiles[i];
+
+ if (tileDesc.type() != TileDescriptor::TTexturedTileDescriptor) {
+ NS_WARNING_ASSERTION(
+ tileDesc.type() == TileDescriptor::TPlaceholderTileDescriptor,
+ "Unrecognised tile descriptor type");
+ continue;
+ }
+
+ const TexturedTileDescriptor& texturedDesc =
+ tileDesc.get_TexturedTileDescriptor();
+
+ tile.mTextureHost =
+ TextureHost::AsTextureHost(texturedDesc.textureParent());
+ if (texturedDesc.readLocked()) {
+ tile.mTextureHost->SetReadLocked();
+ auto actor = tile.mTextureHost->GetIPDLActor();
+ if (actor && tile.mTextureHost->IsDirectMap()) {
+ lockedTextureSerials.AppendElement(
+ TextureHost::GetTextureSerial(actor));
+
+ if (lockedTexturePid) {
+ MOZ_ASSERT(lockedTexturePid == actor->OtherPid());
+ }
+ lockedTexturePid = actor->OtherPid();
+ }
+ }
+
+ if (texturedDesc.textureOnWhiteParent().isSome()) {
+ tile.mTextureHostOnWhite =
+ TextureHost::AsTextureHost(texturedDesc.textureOnWhiteParent().ref());
+ if (texturedDesc.readLockedOnWhite()) {
+ tile.mTextureHostOnWhite->SetReadLocked();
+ auto actor = tile.mTextureHostOnWhite->GetIPDLActor();
+ if (actor && tile.mTextureHostOnWhite->IsDirectMap()) {
+ lockedTextureSerials.AppendElement(
+ TextureHost::GetTextureSerial(actor));
+ }
+ }
+ }
+
+ tile.mTileCoord = newTiles.TileCoord(i);
+
+ // If this same tile texture existed in the old tile set then this will move
+ // the texture source into our new tile.
+ oldRetainedTiles.RecycleTextureSourceForTile(tile);
+
+ // If this tile is in the process of fading, we need to keep that going
+ oldRetainedTiles.RecycleTileFading(tile);
+
+ if (aTiles.isProgressive() && texturedDesc.wasPlaceholder()) {
+ // This is a progressive paint, and the tile used to be a placeholder.
+ // We need to begin fading it in (if enabled via
+ // layers.tiles.fade-in.enabled)
+ tile.mFadeStart = TimeStamp::Now();
+
+ aLayerManager->CompositeUntil(
+ tile.mFadeStart +
+ TimeDuration::FromMilliseconds(
+ StaticPrefs::layers_tiles_fade_in_duration_ms()));
+ }
+ }
+
+#ifdef XP_DARWIN
+ if (lockedTextureSerials.Length() > 0) {
+ TextureSync::SetTexturesLocked(lockedTexturePid, lockedTextureSerials);
+ }
+#endif
+
+ // Step 2, attempt to recycle unused texture sources from the old tile set
+ // into new tiles.
+ //
+ // For gralloc, binding a new TextureHost to the existing TextureSource is the
+ // fastest way to ensure that any implicit locking on the old gralloc image is
+ // released.
+ for (TileHost& tile : mRetainedTiles) {
+ if (!tile.mTextureHost || tile.mTextureSource) {
+ continue;
+ }
+ oldRetainedTiles.RecycleTextureSource(tile);
+ }
+
+ // Step 3, handle the texture uploads, texture source binding and release the
+ // copy-on-write locks for textures with an internal buffer.
+ for (size_t i = 0; i < mRetainedTiles.Length(); i++) {
+ TileHost& tile = mRetainedTiles[i];
+ if (!tile.mTextureHost) {
+ continue;
+ }
+
+ const TileDescriptor& tileDesc = tileDescriptors[i];
+ const TexturedTileDescriptor& texturedDesc =
+ tileDesc.get_TexturedTileDescriptor();
+
+ UseTileTexture(tile.mTextureHost, tile.mTextureSource,
+ texturedDesc.updateRect(),
+ aLayerManager->GetTextureSourceProvider());
+
+ if (tile.mTextureHostOnWhite) {
+ UseTileTexture(tile.mTextureHostOnWhite, tile.mTextureSourceOnWhite,
+ texturedDesc.updateRect(),
+ aLayerManager->GetTextureSourceProvider());
+ }
+ }
+
+ mTiles = newTiles;
+ mTileSize = aTiles.tileSize();
+ mTileOrigin = aTiles.tileOrigin();
+ mValidRegion = aTiles.validRegion();
+ mResolution = aTiles.resolution();
+ mFrameResolution = CSSToParentLayerScale2D(aTiles.frameXResolution(),
+ aTiles.frameYResolution());
+
+ return true;
+}
+
+void TiledLayerBufferComposite::Clear() {
+ mRetainedTiles.Clear();
+ mTiles.mFirst = TileCoordIntPoint();
+ mTiles.mSize = TileCoordIntSize();
+ mValidRegion = nsIntRegion();
+ mResolution = 1.0;
+}
+
+void TiledContentHost::Composite(
+ Compositor* aCompositor, LayerComposite* aLayer, EffectChain& aEffectChain,
+ float aOpacity, const gfx::Matrix4x4& aTransform,
+ const gfx::SamplingFilter aSamplingFilter, const gfx::IntRect& aClipRect,
+ const nsIntRegion* aVisibleRegion /* = nullptr */,
+ const Maybe<gfx::Polygon>& aGeometry) {
+ // Reduce the opacity of the low-precision buffer to make it a
+ // little more subtle and less jarring. In particular, text
+ // rendered at low-resolution and scaled tends to look pretty
+ // heavy and this helps mitigate that. When we reduce the opacity
+ // we also make sure to draw the background color behind the
+ // reduced-opacity tile so that content underneath doesn't show
+ // through.
+ // However, in cases where the background is transparent, or the layer
+ // already has some opacity, we want to skip this behaviour. Otherwise
+ // we end up changing the expected overall transparency of the content,
+ // and it just looks wrong.
+ DeviceColor backgroundColor;
+ if (aOpacity == 1.0f && StaticPrefs::layers_low_precision_opacity() < 1.0f) {
+ // Background colors are only stored on scrollable layers. Grab
+ // the one from the nearest scrollable ancestor layer.
+ for (LayerMetricsWrapper ancestor(GetLayer(),
+ LayerMetricsWrapper::StartAt::BOTTOM);
+ ancestor; ancestor = ancestor.GetParent()) {
+ if (ancestor.Metrics().IsScrollable()) {
+ backgroundColor = ancestor.Metadata().GetBackgroundColor();
+ break;
+ }
+ }
+ }
+ float lowPrecisionOpacityReduction =
+ (aOpacity == 1.0f && backgroundColor.a == 1.0f)
+ ? StaticPrefs::layers_low_precision_opacity()
+ : 1.0f;
+
+ nsIntRegion tmpRegion;
+ const nsIntRegion* renderRegion = aVisibleRegion;
+#ifndef MOZ_IGNORE_PAINT_WILL_RESAMPLE
+ if (PaintWillResample()) {
+ // If we're resampling, then the texture image will contain exactly the
+ // entire visible region's bounds, and we should draw it all in one quad
+ // to avoid unexpected aliasing.
+ tmpRegion = aVisibleRegion->GetBounds();
+ renderRegion = &tmpRegion;
+ }
+#endif
+
+ // Render the low and high precision buffers.
+ RenderLayerBuffer(
+ mLowPrecisionTiledBuffer, aCompositor,
+ lowPrecisionOpacityReduction < 1.0f ? &backgroundColor : nullptr,
+ aEffectChain, lowPrecisionOpacityReduction * aOpacity, aSamplingFilter,
+ aClipRect, *renderRegion, aTransform, aGeometry);
+
+ RenderLayerBuffer(mTiledBuffer, aCompositor, nullptr, aEffectChain, aOpacity,
+ aSamplingFilter, aClipRect, *renderRegion, aTransform,
+ aGeometry);
+}
+
+void TiledContentHost::RenderTile(
+ TileHost& aTile, Compositor* aCompositor, EffectChain& aEffectChain,
+ float aOpacity, const gfx::Matrix4x4& aTransform,
+ const gfx::SamplingFilter aSamplingFilter, const gfx::IntRect& aClipRect,
+ const nsIntRegion& aScreenRegion, const IntPoint& aTextureOffset,
+ const IntSize& aTextureBounds, const gfx::Rect& aVisibleRect,
+ const Maybe<gfx::Polygon>& aGeometry) {
+ MOZ_ASSERT(!aTile.IsPlaceholderTile());
+
+ AutoLockTextureHost autoLock(aTile.mTextureHost);
+ AutoLockTextureHost autoLockOnWhite(aTile.mTextureHostOnWhite);
+ if (autoLock.Failed() || autoLockOnWhite.Failed()) {
+ NS_WARNING("Failed to lock tile");
+ return;
+ }
+
+ if (!aTile.mTextureHost->BindTextureSource(aTile.mTextureSource)) {
+ return;
+ }
+
+ if (aTile.mTextureHostOnWhite &&
+ !aTile.mTextureHostOnWhite->BindTextureSource(
+ aTile.mTextureSourceOnWhite)) {
+ return;
+ }
+
+ RefPtr<TexturedEffect> effect = CreateTexturedEffect(
+ aTile.mTextureSource, aTile.mTextureSourceOnWhite, aSamplingFilter, true);
+ if (!effect) {
+ return;
+ }
+
+ float opacity = aTile.GetFadeInOpacity(aOpacity);
+ aEffectChain.mPrimaryEffect = effect;
+
+ for (auto iter = aScreenRegion.RectIter(); !iter.Done(); iter.Next()) {
+ const IntRect& rect = iter.Get();
+ Rect graphicsRect(rect.X(), rect.Y(), rect.Width(), rect.Height());
+ Rect textureRect(rect.X() - aTextureOffset.x, rect.Y() - aTextureOffset.y,
+ rect.Width(), rect.Height());
+
+ effect->mTextureCoords.SetRect(
+ textureRect.X() / aTextureBounds.width,
+ textureRect.Y() / aTextureBounds.height,
+ textureRect.Width() / aTextureBounds.width,
+ textureRect.Height() / aTextureBounds.height);
+
+ aCompositor->DrawGeometry(graphicsRect, aClipRect, aEffectChain, opacity,
+ aTransform, aVisibleRect, aGeometry);
+ }
+
+ DiagnosticFlags flags = DiagnosticFlags::CONTENT | DiagnosticFlags::TILE;
+ if (aTile.mTextureHostOnWhite) {
+ flags |= DiagnosticFlags::COMPONENT_ALPHA;
+ }
+ aCompositor->DrawDiagnostics(flags, aScreenRegion, aClipRect, aTransform,
+ mFlashCounter);
+}
+
+void TiledContentHost::RenderLayerBuffer(
+ TiledLayerBufferComposite& aLayerBuffer, Compositor* aCompositor,
+ const DeviceColor* aBackgroundColor, EffectChain& aEffectChain,
+ float aOpacity, const gfx::SamplingFilter aSamplingFilter,
+ const gfx::IntRect& aClipRect, nsIntRegion aVisibleRegion,
+ gfx::Matrix4x4 aTransform, const Maybe<Polygon>& aGeometry) {
+ float resolution = aLayerBuffer.GetResolution();
+ gfx::Size layerScale(1, 1);
+
+ // We assume that the current frame resolution is the one used in our high
+ // precision layer buffer. Compensate for a changing frame resolution when
+ // rendering the low precision buffer.
+ if (aLayerBuffer.GetFrameResolution() != mTiledBuffer.GetFrameResolution()) {
+ const CSSToParentLayerScale2D& layerResolution =
+ aLayerBuffer.GetFrameResolution();
+ const CSSToParentLayerScale2D& localResolution =
+ mTiledBuffer.GetFrameResolution();
+ layerScale.width = layerResolution.xScale / localResolution.xScale;
+ layerScale.height = layerResolution.yScale / localResolution.yScale;
+ aVisibleRegion.ScaleRoundOut(layerScale.width, layerScale.height);
+ }
+
+ // Make sure we don't render at low resolution where we have valid high
+ // resolution content, to avoid overdraw and artifacts with semi-transparent
+ // layers.
+ nsIntRegion maskRegion;
+ if (resolution != mTiledBuffer.GetResolution()) {
+ maskRegion = mTiledBuffer.GetValidRegion();
+ // XXX This should be ScaleRoundIn, but there is no such function on
+ // nsIntRegion.
+ maskRegion.ScaleRoundOut(layerScale.width, layerScale.height);
+ }
+
+ // Make sure the resolution and difference in frame resolution are accounted
+ // for in the layer transform.
+ aTransform.PreScale(1 / (resolution * layerScale.width),
+ 1 / (resolution * layerScale.height), 1);
+
+ DiagnosticFlags componentAlphaDiagnostic = DiagnosticFlags::NO_DIAGNOSTIC;
+
+ nsIntRegion compositeRegion = aLayerBuffer.GetValidRegion();
+ compositeRegion.AndWith(aVisibleRegion);
+ compositeRegion.SubOut(maskRegion);
+
+ IntRect visibleRect = aVisibleRegion.GetBounds();
+
+ if (compositeRegion.IsEmpty()) {
+ return;
+ }
+
+ if (aBackgroundColor) {
+ nsIntRegion backgroundRegion = compositeRegion;
+ backgroundRegion.ScaleRoundOut(resolution, resolution);
+ EffectChain effect;
+ effect.mPrimaryEffect = new EffectSolidColor(*aBackgroundColor);
+ for (auto iter = backgroundRegion.RectIter(); !iter.Done(); iter.Next()) {
+ const IntRect& rect = iter.Get();
+ Rect graphicsRect(rect.X(), rect.Y(), rect.Width(), rect.Height());
+ aCompositor->DrawGeometry(graphicsRect, aClipRect, effect, 1.0,
+ aTransform, aGeometry);
+ }
+ }
+
+ for (size_t i = 0; i < aLayerBuffer.GetTileCount(); ++i) {
+ TileHost& tile = aLayerBuffer.GetTile(i);
+ if (tile.IsPlaceholderTile()) {
+ continue;
+ }
+
+ TileCoordIntPoint tileCoord = aLayerBuffer.GetPlacement().TileCoord(i);
+ // A sanity check that catches a lot of mistakes.
+ MOZ_ASSERT(tileCoord.x == tile.mTileCoord.x &&
+ tileCoord.y == tile.mTileCoord.y);
+
+ IntPoint tileOffset = aLayerBuffer.GetTileOffset(tileCoord);
+ nsIntRegion tileDrawRegion =
+ IntRect(tileOffset, aLayerBuffer.GetScaledTileSize());
+ tileDrawRegion.AndWith(compositeRegion);
+
+ if (tileDrawRegion.IsEmpty()) {
+ continue;
+ }
+
+ tileDrawRegion.ScaleRoundOut(resolution, resolution);
+ RenderTile(tile, aCompositor, aEffectChain, aOpacity, aTransform,
+ aSamplingFilter, aClipRect, tileDrawRegion,
+ tileOffset * resolution, aLayerBuffer.GetTileSize(),
+ gfx::Rect(visibleRect.X(), visibleRect.Y(), visibleRect.Width(),
+ visibleRect.Height()),
+ aGeometry);
+
+ if (tile.mTextureHostOnWhite) {
+ componentAlphaDiagnostic = DiagnosticFlags::COMPONENT_ALPHA;
+ }
+ }
+
+ gfx::Rect rect(visibleRect.X(), visibleRect.Y(), visibleRect.Width(),
+ visibleRect.Height());
+ aCompositor->DrawDiagnostics(
+ DiagnosticFlags::CONTENT | componentAlphaDiagnostic, rect, aClipRect,
+ aTransform, mFlashCounter);
+}
+
+void TiledContentHost::PrintInfo(std::stringstream& aStream,
+ const char* aPrefix) {
+ aStream << aPrefix;
+ aStream << nsPrintfCString("TiledContentHost (0x%p)", this).get();
+
+#if defined(MOZ_DUMP_PAINTING)
+ if (StaticPrefs::layers_dump_texture()) {
+ nsAutoCString pfx(aPrefix);
+ pfx += " ";
+
+ Dump(aStream, pfx.get(), false);
+ }
+#endif
+}
+
+void TiledContentHost::Dump(std::stringstream& aStream, const char* aPrefix,
+ bool aDumpHtml) {
+ mTiledBuffer.Dump(
+ aStream, aPrefix, aDumpHtml,
+ TextureDumpMode::
+ DoNotCompress /* compression not supported on host side */);
+}
+
+void TiledContentHost::AddAnimationInvalidation(nsIntRegion& aRegion) {
+ return mTiledBuffer.AddAnimationInvalidation(aRegion);
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/composite/TiledContentHost.h b/gfx/layers/composite/TiledContentHost.h
new file mode 100644
index 0000000000..fa754bff30
--- /dev/null
+++ b/gfx/layers/composite/TiledContentHost.h
@@ -0,0 +1,263 @@
+/* -*- 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/. */
+
+#ifndef GFX_TILEDCONTENTHOST_H
+#define GFX_TILEDCONTENTHOST_H
+
+#include <stdint.h> // for uint16_t
+#include <stdio.h> // for FILE
+#include <algorithm> // for swap
+#include "ContentHost.h" // for ContentHost
+#include "TiledLayerBuffer.h" // for TiledLayerBuffer, etc
+#include "CompositableHost.h"
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/RefPtr.h" // for RefPtr
+#include "mozilla/gfx/MatrixFwd.h" // for Matrix4x4
+#include "mozilla/gfx/Point.h" // for Point
+#include "mozilla/gfx/Rect.h" // for Rect
+#include "mozilla/gfx/Types.h" // for SamplingFilter
+#include "mozilla/layers/CompositorTypes.h" // for TextureInfo, etc
+#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor
+#include "mozilla/layers/LayersTypes.h" // for LayerRenderState, etc
+#include "mozilla/layers/TextureHost.h" // for TextureHost
+#include "mozilla/layers/TextureClient.h"
+#include "mozilla/mozalloc.h" // for operator delete
+#include "nsRegion.h" // for nsIntRegion
+#include "nscore.h" // for nsACString
+
+namespace mozilla {
+
+namespace layers {
+
+class Compositor;
+class ISurfaceAllocator;
+class Layer;
+class ThebesBufferData;
+class TextureReadLock;
+struct EffectChain;
+
+class TileHost {
+ public:
+ // Constructs a placeholder TileHost. See the comments above
+ // TiledLayerBuffer for more information on what this is used for;
+ // essentially, this is a sentinel used to represent an invalid or blank
+ // tile.
+ TileHost() = default;
+
+ // Constructs a TileHost from a TextureReadLock and TextureHost.
+ TileHost(TextureReadLock* aSharedLock, TextureHost* aTextureHost,
+ TextureHost* aTextureHostOnWhite, TextureSource* aSource,
+ TextureSource* aSourceOnWhite)
+ : mTextureHost(aTextureHost),
+ mTextureHostOnWhite(aTextureHostOnWhite),
+ mTextureSource(aSource),
+ mTextureSourceOnWhite(aSourceOnWhite) {}
+
+ TileHost(const TileHost& o) {
+ mTextureHost = o.mTextureHost;
+ mTextureHostOnWhite = o.mTextureHostOnWhite;
+ mTextureSource = o.mTextureSource;
+ mTextureSourceOnWhite = o.mTextureSourceOnWhite;
+ mTileCoord = o.mTileCoord;
+ }
+ TileHost& operator=(const TileHost& o) {
+ if (this == &o) {
+ return *this;
+ }
+ mTextureHost = o.mTextureHost;
+ mTextureHostOnWhite = o.mTextureHostOnWhite;
+ mTextureSource = o.mTextureSource;
+ mTextureSourceOnWhite = o.mTextureSourceOnWhite;
+ mTileCoord = o.mTileCoord;
+ return *this;
+ }
+
+ bool operator==(const TileHost& o) const {
+ return mTextureHost == o.mTextureHost;
+ }
+ bool operator!=(const TileHost& o) const {
+ return mTextureHost != o.mTextureHost;
+ }
+
+ bool IsPlaceholderTile() const { return mTextureHost == nullptr; }
+
+ void Dump(std::stringstream& aStream) {
+ aStream << "TileHost(...)"; // fill in as needed
+ }
+
+ void DumpTexture(std::stringstream& aStream,
+ TextureDumpMode /* aCompress, ignored for host tiles */) {
+ // TODO We should combine the OnWhite/OnBlack here an just output a single
+ // image.
+ CompositableHost::DumpTextureHost(aStream, mTextureHost);
+ }
+
+ RefPtr<TextureSource> AcquireTextureSource() const;
+ RefPtr<TextureSource> AcquireTextureSourceOnWhite() const;
+
+ /**
+ * This does a linear tween of the passed opacity (which is assumed
+ * to be between 0.0 and 1.0). The duration of the fade is controlled
+ * by the 'layers.tiles.fade-in.duration-ms' preference. It is enabled
+ * via 'layers.tiles.fade-in.enabled'
+ */
+ float GetFadeInOpacity(float aOpacity);
+
+ CompositableTextureHostRef mTextureHost;
+ CompositableTextureHostRef mTextureHostOnWhite;
+ mutable CompositableTextureSourceRef mTextureSource;
+ mutable CompositableTextureSourceRef mTextureSourceOnWhite;
+ // This is not strictly necessary but makes debugging whole lot easier.
+ TileCoordIntPoint mTileCoord;
+ TimeStamp mFadeStart;
+};
+
+class TiledLayerBufferComposite
+ : public TiledLayerBuffer<TiledLayerBufferComposite, TileHost> {
+ friend class TiledLayerBuffer<TiledLayerBufferComposite, TileHost>;
+
+ public:
+ TiledLayerBufferComposite();
+ ~TiledLayerBufferComposite();
+
+ bool UseTiles(const SurfaceDescriptorTiles& aTileDescriptors,
+ HostLayerManager* aLayerManager, ISurfaceAllocator* aAllocator);
+
+ void Clear();
+
+ TileHost GetPlaceholderTile() const { return TileHost(); }
+
+ // Stores the absolute resolution of the containing frame, calculated
+ // by the sum of the resolutions of all parent layers' FrameMetrics.
+ const CSSToParentLayerScale2D& GetFrameResolution() {
+ return mFrameResolution;
+ }
+
+ void SetTextureSourceProvider(TextureSourceProvider* aProvider);
+
+ void AddAnimationInvalidation(nsIntRegion& aRegion);
+
+ protected:
+ CSSToParentLayerScale2D mFrameResolution;
+};
+
+/**
+ * ContentHost for tiled PaintedLayers. Since tiled layers are special snow
+ * flakes, we have a unique update process. All the textures that back the
+ * tiles are added in the usual way, but Updated is called on the host side
+ * in response to a message that describes the transaction for every tile.
+ * Composition happens in the normal way.
+ *
+ * TiledContentHost has a TiledLayerBufferComposite which keeps hold of the
+ * tiles. Each tile has a reference to a texture host. During the layers
+ * transaction, we receive a list of descriptors for the client-side tile buffer
+ * tiles (UseTiledLayerBuffer). If we receive two transactions before a
+ * composition, we immediately unlock and discard the unused buffer.
+ *
+ * When the content host is composited, we first validate the TiledLayerBuffer
+ * (Upload), which calls Updated on each tile's texture host to make sure the
+ * texture data has been uploaded. For single-buffered tiles, we unlock at this
+ * point, for double-buffered tiles we unlock and discard the last composited
+ * buffer after compositing a new one. Rendering takes us to RenderTile which
+ * is similar to Composite for non-tiled ContentHosts.
+ */
+class TiledContentHost : public ContentHost {
+ public:
+ explicit TiledContentHost(const TextureInfo& aTextureInfo);
+
+ protected:
+ virtual ~TiledContentHost();
+
+ public:
+ // Generate effect for layerscope when using hwc.
+ virtual already_AddRefed<TexturedEffect> GenEffect(
+ const gfx::SamplingFilter aSamplingFilter) override;
+
+ virtual bool UpdateThebes(const ThebesBufferData& aData,
+ const nsIntRegion& aUpdated,
+ const nsIntRegion& aOldValidRegionBack) override {
+ NS_ERROR("N/A for tiled layers");
+ return false;
+ }
+
+ const nsIntRegion& GetValidLowPrecisionRegion() const {
+ return mLowPrecisionTiledBuffer.GetValidRegion();
+ }
+
+ const nsIntRegion& GetValidRegion() const {
+ return mTiledBuffer.GetValidRegion();
+ }
+
+ void SetTextureSourceProvider(TextureSourceProvider* aProvider) override {
+ CompositableHost::SetTextureSourceProvider(aProvider);
+ mTiledBuffer.SetTextureSourceProvider(aProvider);
+ mLowPrecisionTiledBuffer.SetTextureSourceProvider(aProvider);
+ }
+
+ bool UseTiledLayerBuffer(ISurfaceAllocator* aAllocator,
+ const SurfaceDescriptorTiles& aTiledDescriptor);
+
+ void Composite(Compositor* aCompositor, LayerComposite* aLayer,
+ EffectChain& aEffectChain, float aOpacity,
+ const gfx::Matrix4x4& aTransform,
+ const gfx::SamplingFilter aSamplingFilter,
+ const gfx::IntRect& aClipRect,
+ const nsIntRegion* aVisibleRegion = nullptr,
+ const Maybe<gfx::Polygon>& aGeometry = Nothing()) override;
+
+ CompositableType GetType() override {
+ return CompositableType::CONTENT_TILED;
+ }
+
+ TiledContentHost* AsTiledContentHost() override { return this; }
+
+ void Attach(Layer* aLayer, TextureSourceProvider* aProvider,
+ AttachFlags aFlags = NO_FLAGS) override;
+
+ void Detach(Layer* aLayer = nullptr, AttachFlags aFlags = NO_FLAGS) override;
+
+ void Dump(std::stringstream& aStream, const char* aPrefix = "",
+ bool aDumpHtml = false) override;
+
+ void PrintInfo(std::stringstream& aStream, const char* aPrefix) override;
+
+ void AddAnimationInvalidation(nsIntRegion& aRegion) override;
+
+ TiledLayerBufferComposite& GetLowResBuffer() {
+ return mLowPrecisionTiledBuffer;
+ }
+ TiledLayerBufferComposite& GetHighResBuffer() { return mTiledBuffer; }
+
+ private:
+ void RenderLayerBuffer(TiledLayerBufferComposite& aLayerBuffer,
+ Compositor* aCompositor,
+ const gfx::DeviceColor* aBackgroundColor,
+ EffectChain& aEffectChain, float aOpacity,
+ const gfx::SamplingFilter aSamplingFilter,
+ const gfx::IntRect& aClipRect, nsIntRegion aMaskRegion,
+ gfx::Matrix4x4 aTransform,
+ const Maybe<gfx::Polygon>& aGeometry);
+
+ // Renders a single given tile.
+ void RenderTile(
+ TileHost& aTile, Compositor* aCompositor, EffectChain& aEffectChain,
+ float aOpacity, const gfx::Matrix4x4& aTransform,
+ const gfx::SamplingFilter aSamplingFilter, const gfx::IntRect& aClipRect,
+ const nsIntRegion& aScreenRegion, const gfx::IntPoint& aTextureOffset,
+ const gfx::IntSize& aTextureBounds, const gfx::Rect& aVisibleRect,
+ const Maybe<gfx::Polygon>& aGeometry);
+
+ void EnsureTileStore() {}
+
+ TiledLayerBufferComposite mTiledBuffer;
+ TiledLayerBufferComposite mLowPrecisionTiledBuffer;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/composite/X11TextureHost.cpp b/gfx/layers/composite/X11TextureHost.cpp
new file mode 100644
index 0000000000..5bbb7a82c8
--- /dev/null
+++ b/gfx/layers/composite/X11TextureHost.cpp
@@ -0,0 +1,103 @@
+/* -*- 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 "X11TextureHost.h"
+
+#include "mozilla/layers/BasicCompositor.h"
+#include "mozilla/layers/CompositorOGL.h"
+#include "mozilla/layers/X11TextureSourceBasic.h"
+#include "mozilla/layers/X11TextureSourceOGL.h"
+#include "gfx2DGlue.h"
+#include "gfxPlatform.h"
+#include "gfxXlibSurface.h"
+
+namespace mozilla::layers {
+
+using namespace mozilla::gfx;
+
+X11TextureHost::X11TextureHost(TextureFlags aFlags,
+ const SurfaceDescriptorX11& aDescriptor)
+ : TextureHost(aFlags) {
+ mSurface = aDescriptor.OpenForeign();
+
+ if (mSurface && !(aFlags & TextureFlags::DEALLOCATE_CLIENT)) {
+ mSurface->TakePixmap();
+ }
+}
+
+bool X11TextureHost::Lock() {
+ if (!mCompositor || !mSurface) {
+ return false;
+ }
+
+ if (!mTextureSource) {
+ switch (mCompositor->GetBackendType()) {
+ case LayersBackend::LAYERS_BASIC:
+ mTextureSource = new X11TextureSourceBasic(
+ mCompositor->AsBasicCompositor(), mSurface);
+ break;
+ case LayersBackend::LAYERS_OPENGL:
+ mTextureSource =
+ new X11TextureSourceOGL(mCompositor->AsCompositorOGL(), mSurface);
+ break;
+ default:
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void X11TextureHost::SetTextureSourceProvider(
+ TextureSourceProvider* aProvider) {
+ mProvider = aProvider;
+ if (mProvider) {
+ mCompositor = mProvider->AsCompositor();
+ } else {
+ mCompositor = nullptr;
+ }
+ if (mTextureSource) {
+ mTextureSource->SetTextureSourceProvider(aProvider);
+ }
+}
+
+SurfaceFormat X11TextureHost::GetFormat() const {
+ if (!mSurface) {
+ return SurfaceFormat::UNKNOWN;
+ }
+ gfxContentType type = mSurface->GetContentType();
+ if (mCompositor->GetBackendType() == LayersBackend::LAYERS_OPENGL) {
+ return X11TextureSourceOGL::ContentTypeToSurfaceFormat(type);
+ }
+ return X11TextureSourceBasic::ContentTypeToSurfaceFormat(type);
+}
+
+IntSize X11TextureHost::GetSize() const {
+ if (!mSurface) {
+ return IntSize();
+ }
+ return mSurface->GetSize();
+}
+
+already_AddRefed<gfx::DataSourceSurface> X11TextureHost::GetAsSurface() {
+ if (!mTextureSource || !mTextureSource->AsSourceBasic()) {
+ return nullptr;
+ }
+ RefPtr<DrawTarget> tempDT =
+ gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(GetSize(),
+ GetFormat());
+ if (!tempDT) {
+ return nullptr;
+ }
+ RefPtr<SourceSurface> surf =
+ mTextureSource->AsSourceBasic()->GetSurface(tempDT);
+ if (!surf) {
+ return nullptr;
+ }
+ return surf->GetDataSurface();
+}
+
+} // namespace mozilla::layers
diff --git a/gfx/layers/composite/X11TextureHost.h b/gfx/layers/composite/X11TextureHost.h
new file mode 100644
index 0000000000..568e136ff1
--- /dev/null
+++ b/gfx/layers/composite/X11TextureHost.h
@@ -0,0 +1,65 @@
+/* -*- 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/. */
+
+#ifndef MOZILLA_GFX_X11TEXTUREHOST__H
+#define MOZILLA_GFX_X11TEXTUREHOST__H
+
+#include "mozilla/layers/TextureHost.h"
+#include "mozilla/layers/LayersSurfaces.h"
+#include "mozilla/gfx/Types.h"
+
+#include "gfxXlibSurface.h"
+
+namespace mozilla {
+namespace layers {
+
+class X11TextureSource : public TextureSource {
+ public:
+ // Called when the underlying X surface has been changed.
+ // Useful for determining whether to rebind a GLXPixmap to a texture.
+ virtual void Updated() = 0;
+
+ const char* Name() const override { return "X11TextureSource"; }
+};
+
+// TextureHost for Xlib-backed TextureSources.
+class X11TextureHost : public TextureHost {
+ public:
+ X11TextureHost(TextureFlags aFlags, const SurfaceDescriptorX11& aDescriptor);
+
+ void SetTextureSourceProvider(TextureSourceProvider* aProvider) override;
+
+ bool Lock() override;
+
+ gfx::SurfaceFormat GetFormat() const override;
+
+ gfx::IntSize GetSize() const override;
+
+ bool BindTextureSource(CompositableTextureSourceRef& aTexture) override {
+ aTexture = mTextureSource;
+ return !!aTexture;
+ }
+
+ already_AddRefed<gfx::DataSourceSurface> GetAsSurface() override;
+
+#ifdef MOZ_LAYERS_HAVE_LOG
+ const char* Name() override { return "X11TextureHost"; }
+#endif
+
+ protected:
+ void UpdatedInternal(const nsIntRegion*) override {
+ if (mTextureSource) mTextureSource->Updated();
+ }
+
+ RefPtr<Compositor> mCompositor;
+ RefPtr<X11TextureSource> mTextureSource;
+ RefPtr<gfxXlibSurface> mSurface;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // MOZILLA_GFX_X11TEXTUREHOST__H