summaryrefslogtreecommitdiffstats
path: root/gfx/layers/composite
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/layers/composite')
-rw-r--r--gfx/layers/composite/AsyncCompositionManager.cpp1495
-rw-r--r--gfx/layers/composite/AsyncCompositionManager.h283
-rw-r--r--gfx/layers/composite/CanvasLayerComposite.cpp166
-rw-r--r--gfx/layers/composite/CanvasLayerComposite.h81
-rw-r--r--gfx/layers/composite/ColorLayerComposite.cpp47
-rw-r--r--gfx/layers/composite/ColorLayerComposite.h65
-rw-r--r--gfx/layers/composite/CompositableHost.cpp292
-rw-r--r--gfx/layers/composite/CompositableHost.h316
-rwxr-xr-xgfx/layers/composite/ContainerLayerComposite.cpp698
-rw-r--r--gfx/layers/composite/ContainerLayerComposite.h191
-rw-r--r--gfx/layers/composite/ContentHost.cpp491
-rw-r--r--gfx/layers/composite/ContentHost.h228
-rw-r--r--gfx/layers/composite/FPSCounter.cpp450
-rw-r--r--gfx/layers/composite/FPSCounter.h114
-rw-r--r--gfx/layers/composite/FontData.h13
-rw-r--r--gfx/layers/composite/FrameUniformityData.cpp152
-rw-r--r--gfx/layers/composite/FrameUniformityData.h73
-rw-r--r--gfx/layers/composite/GPUVideoTextureHost.cpp90
-rw-r--r--gfx/layers/composite/GPUVideoTextureHost.h53
-rw-r--r--gfx/layers/composite/ImageHost.cpp739
-rw-r--r--gfx/layers/composite/ImageHost.h201
-rw-r--r--gfx/layers/composite/ImageLayerComposite.cpp236
-rw-r--r--gfx/layers/composite/ImageLayerComposite.h79
-rw-r--r--gfx/layers/composite/LayerManagerComposite.cpp1451
-rw-r--r--gfx/layers/composite/LayerManagerComposite.h687
-rw-r--r--gfx/layers/composite/PaintCounter.cpp79
-rw-r--r--gfx/layers/composite/PaintCounter.h49
-rw-r--r--gfx/layers/composite/PaintedLayerComposite.cpp194
-rw-r--r--gfx/layers/composite/PaintedLayerComposite.h94
-rw-r--r--gfx/layers/composite/TextRenderer.cpp173
-rw-r--r--gfx/layers/composite/TextRenderer.h50
-rw-r--r--gfx/layers/composite/TextureHost.cpp1142
-rw-r--r--gfx/layers/composite/TextureHost.h900
-rw-r--r--gfx/layers/composite/TiledContentHost.cpp645
-rw-r--r--gfx/layers/composite/TiledContentHost.h292
-rw-r--r--gfx/layers/composite/X11TextureHost.cpp107
-rw-r--r--gfx/layers/composite/X11TextureHost.h72
-rw-r--r--gfx/layers/composite/qrcode_table.h259
38 files changed, 12747 insertions, 0 deletions
diff --git a/gfx/layers/composite/AsyncCompositionManager.cpp b/gfx/layers/composite/AsyncCompositionManager.cpp
new file mode 100644
index 000000000..69eafceca
--- /dev/null
+++ b/gfx/layers/composite/AsyncCompositionManager.cpp
@@ -0,0 +1,1495 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=2 et 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 "apz/src/AsyncPanZoomController.h"
+#include "FrameMetrics.h" // for FrameMetrics
+#include "LayerManagerComposite.h" // for LayerManagerComposite, etc
+#include "Layers.h" // for Layer, ContainerLayer, etc
+#include "gfxPoint.h" // for gfxPoint, gfxSize
+#include "gfxPrefs.h" // for gfxPrefs
+#include "mozilla/StyleAnimationValue.h" // for StyleAnimationValue, etc
+#include "mozilla/WidgetUtils.h" // for ComputeTransformForRotation
+#include "mozilla/dom/KeyframeEffectReadOnly.h"
+#include "mozilla/dom/AnimationEffectReadOnlyBinding.h" // for dom::FillMode
+#include "mozilla/dom/KeyframeEffectBinding.h" // for dom::IterationComposite
+#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/APZUtils.h" // for CompleteAsyncTransform
+#include "mozilla/layers/Compositor.h" // for Compositor
+#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 "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 InfallibleTArray
+#include "UnitTransforms.h" // for TransformTo
+#include "gfxPrefs.h"
+#if defined(MOZ_WIDGET_ANDROID)
+# include <android/log.h>
+# include "mozilla/widget/AndroidCompositorWidget.h"
+#endif
+#include "GeckoProfiler.h"
+#include "FrameUniformityData.h"
+#include "TreeTraversal.h" // for ForEachNode, BreadthFirstSearch
+#include "VsyncSource.h"
+
+struct nsCSSValueSharedList;
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::gfx;
+
+static bool
+IsSameDimension(dom::ScreenOrientationInternal o1, dom::ScreenOrientationInternal o2)
+{
+ bool isO1portrait = (o1 == dom::eScreenOrientation_PortraitPrimary || o1 == dom::eScreenOrientation_PortraitSecondary);
+ bool isO2portrait = (o2 == dom::eScreenOrientation_PortraitPrimary || o2 == dom::eScreenOrientation_PortraitSecondary);
+ return !(isO1portrait ^ isO2portrait);
+}
+
+static bool
+ContentMightReflowOnOrientationChange(const IntRect& rect)
+{
+ return rect.width != rect.height;
+}
+
+AsyncCompositionManager::AsyncCompositionManager(LayerManagerComposite* aManager)
+ : mLayerManager(aManager)
+ , mIsFirstPaint(true)
+ , mLayersUpdated(false)
+ , mPaintSyncId(0)
+ , mReadyForCompose(true)
+{
+}
+
+AsyncCompositionManager::~AsyncCompositionManager()
+{
+}
+
+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()) {
+ dom::ScreenOrientationInternal chromeOrientation =
+ mTargetConfig.orientation();
+ dom::ScreenOrientationInternal 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());
+ }
+}
+
+#ifdef DEBUG
+static void
+GetBaseTransform(Layer* aLayer, Matrix4x4* aTransform)
+{
+ // Start with the animated transform if there is one
+ *aTransform =
+ (aLayer->AsLayerComposite()->GetShadowTransformSetByAnimation()
+ ? aLayer->GetLocalTransform()
+ : aLayer->GetTransform());
+}
+#endif
+
+static void
+TransformClipRect(Layer* aLayer,
+ const ParentLayerToParentLayerMatrix4x4& aTransform)
+{
+ MOZ_ASSERT(aTransform.Is2D());
+ const Maybe<ParentLayerIntRect>& clipRect = aLayer->AsLayerComposite()->GetShadowClipRect();
+ if (clipRect) {
+ ParentLayerIntRect transformed = TransformBy(aTransform, *clipRect);
+ aLayer->AsLayerComposite()->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->AsLayerComposite()->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->AsLayerComposite()->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->AsLayerComposite()->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);
+ }
+ }
+}
+
+#ifdef DEBUG
+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;
+ }
+}
+#endif
+
+static LayerPoint
+GetLayerFixedMarginsOffset(Layer* aLayer,
+ const ScreenMargin& aFixedLayerMargins)
+{
+ // Work out the necessary translation, in root scrollable layer space.
+ // Because fixed layer margins are stored relative to the root scrollable
+ // layer, we can just take the difference between these values.
+ LayerPoint translation;
+ int32_t sides = aLayer->GetFixedPositionSides();
+
+ if ((sides & eSideBitsLeftRight) == eSideBitsLeftRight) {
+ translation.x += (aFixedLayerMargins.left - aFixedLayerMargins.right) / 2;
+ } else if (sides & eSideBitsRight) {
+ translation.x -= aFixedLayerMargins.right;
+ } else if (sides & eSideBitsLeft) {
+ translation.x += aFixedLayerMargins.left;
+ }
+
+ if ((sides & eSideBitsTopBottom) == eSideBitsTopBottom) {
+ translation.y += (aFixedLayerMargins.top - aFixedLayerMargins.bottom) / 2;
+ } else if (sides & eSideBitsBottom) {
+ translation.y -= aFixedLayerMargins.bottom;
+ } else if (sides & eSideBitsTop) {
+ translation.y += aFixedLayerMargins.top;
+ }
+
+ return translation;
+}
+
+static gfxFloat
+IntervalOverlap(gfxFloat aTranslation, gfxFloat aMin, gfxFloat aMax)
+{
+ // Determine the amount of overlap between the 1D vector |aTranslation|
+ // and the interval [aMin, aMax].
+ if (aTranslation > 0) {
+ return std::max(0.0, std::min(aMax, aTranslation) - std::max(aMin, 0.0));
+ } else {
+ return std::min(0.0, std::max(aMin, aTranslation) - std::min(aMax, 0.0));
+ }
+}
+
+/**
+ * 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, FrameMetrics::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,
+ FrameMetrics::ViewID aFixedWithRespectTo,
+ Layer* aTransformedLayer,
+ FrameMetrics::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<FrameMetrics::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,
+ FrameMetrics::ViewID aTransformScrollId,
+ const LayerToParentLayerMatrix4x4& aPreviousTransformForRoot,
+ const LayerToParentLayerMatrix4x4& aCurrentTransformForRoot,
+ const ScreenMargin& aFixedLayerMargins,
+ ClipPartsCache* aClipPartsCache)
+{
+ // We're going to be inverting |aCurrentTransformForRoot|.
+ // If it's singular, there's nothing we can do.
+ if (aCurrentTransformForRoot.IsSingular()) {
+ return;
+ }
+
+ Layer* layer = aStartTraversalAt;
+ bool needsAsyncTransformUnapplied = false;
+ if (Maybe<FrameMetrics::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,
+ aTransformScrollId, aPreviousTransformForRoot,
+ aCurrentTransformForRoot, aFixedLayerMargins, aClipPartsCache);
+ }
+ return;
+ }
+
+ // Insert a translation so that the position of the anchor point is the same
+ // before and after the change to the transform of aTransformedSubtreeRoot.
+
+ // 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.
+#ifdef DEBUG
+ Matrix4x4 ancestorTransform;
+ if (layer != aTransformedSubtreeRoot) {
+ AccumulateLayerTransforms(layer->GetParent(), aTransformedSubtreeRoot,
+ ancestorTransform);
+ }
+ ancestorTransform.NudgeToIntegersFixedEpsilon();
+ MOZ_ASSERT(ancestorTransform.IsIdentity());
+#endif
+
+ // 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();
+
+ // Offset the layer's anchor point to make sure fixed position content
+ // respects content document fixed position margins.
+ LayerPoint offsetAnchor = anchor + GetLayerFixedMarginsOffset(layer, aFixedLayerMargins);
+
+ // 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 =
+ aPreviousTransformForRoot.TransformPoint(offsetAnchor);
+ LayerPoint transformedAnchor = aCurrentTransformForRoot.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 LayerRect& stickyOuter = layer->GetStickyScrollRangeOuter();
+ const LayerRect& 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 =
+ aPreviousTransformForRoot.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, aTransformScrollId,
+ aPreviousTransformForRoot, newTransform, aFixedLayerMargins, aClipPartsCache);
+ }
+ }
+
+ return;
+}
+
+static void
+SampleValue(float aPortion, Animation& aAnimation,
+ const StyleAnimationValue& aStart, const StyleAnimationValue& aEnd,
+ const StyleAnimationValue& aLastValue, uint64_t aCurrentIteration,
+ Animatable* aValue, Layer* aLayer)
+{
+ NS_ASSERTION(aStart.GetUnit() == aEnd.GetUnit() ||
+ aStart.GetUnit() == StyleAnimationValue::eUnit_None ||
+ aEnd.GetUnit() == StyleAnimationValue::eUnit_None,
+ "Must have same unit");
+
+ StyleAnimationValue startValue = aStart;
+ StyleAnimationValue endValue = aEnd;
+ // Iteration composition for accumulate
+ if (static_cast<dom::IterationCompositeOperation>
+ (aAnimation.iterationComposite()) ==
+ dom::IterationCompositeOperation::Accumulate &&
+ aCurrentIteration > 0) {
+ // FIXME: Bug 1293492: Add a utility function to calculate both of
+ // below StyleAnimationValues.
+ DebugOnly<bool> accumulateResult =
+ StyleAnimationValue::Accumulate(aAnimation.property(),
+ startValue,
+ aLastValue,
+ aCurrentIteration);
+ MOZ_ASSERT(accumulateResult, "could not accumulate value");
+ accumulateResult =
+ StyleAnimationValue::Accumulate(aAnimation.property(),
+ endValue,
+ aLastValue,
+ aCurrentIteration);
+ MOZ_ASSERT(accumulateResult, "could not accumulate value");
+ }
+
+ StyleAnimationValue interpolatedValue;
+ // This should never fail because we only pass transform and opacity values
+ // to the compositor and they should never fail to interpolate.
+ DebugOnly<bool> uncomputeResult =
+ StyleAnimationValue::Interpolate(aAnimation.property(),
+ startValue, endValue,
+ aPortion, interpolatedValue);
+ MOZ_ASSERT(uncomputeResult, "could not uncompute value");
+
+ if (aAnimation.property() == eCSSProperty_opacity) {
+ *aValue = interpolatedValue.GetFloatValue();
+ return;
+ }
+
+ nsCSSValueSharedList* interpolatedList =
+ interpolatedValue.GetCSSValueSharedListValue();
+
+ TransformData& data = aAnimation.data().get_TransformData();
+ nsPoint origin = data.origin();
+ // we expect all our transform data to arrive in device pixels
+ Point3D transformOrigin = data.transformOrigin();
+ nsDisplayTransform::FrameTransformProperties props(interpolatedList,
+ transformOrigin);
+
+ // If our parent layer is a perspective layer, then the offset into reference
+ // frame coordinates is already on that layer. If not, then we need to ask
+ // for it to be added here.
+ uint32_t flags = 0;
+ if (!aLayer->GetParent() || !aLayer->GetParent()->GetTransformIsPerspective()) {
+ flags = nsDisplayTransform::OFFSET_BY_ORIGIN;
+ }
+
+ Matrix4x4 transform =
+ nsDisplayTransform::GetResultingTransformMatrix(props, origin,
+ data.appUnitsPerDevPixel(),
+ flags, &data.bounds());
+
+ InfallibleTArray<TransformFunction> functions;
+ functions.AppendElement(TransformMatrix(transform));
+ *aValue = functions;
+}
+
+static bool
+SampleAnimations(Layer* aLayer, TimeStamp aPoint)
+{
+ bool activeAnimations = false;
+
+ ForEachNode<ForwardIterator>(
+ aLayer,
+ [&activeAnimations, &aPoint] (Layer* layer)
+ {
+ AnimationArray& animations = layer->GetAnimations();
+ InfallibleTArray<AnimData>& animationData = layer->GetAnimationData();
+
+ // Process in order, since later animations override earlier ones.
+ for (size_t i = 0, iEnd = animations.Length(); i < iEnd; ++i) {
+ Animation& animation = animations[i];
+ AnimData& animData = animationData[i];
+
+ activeAnimations = true;
+
+ MOZ_ASSERT(!animation.startTime().IsNull(),
+ "Failed to resolve start time of pending animations");
+ TimeDuration elapsedDuration =
+ (aPoint - animation.startTime()).MultDouble(animation.playbackRate());
+ TimingParams timing;
+ timing.mDuration.emplace(animation.duration());
+ timing.mDelay = animation.delay();
+ timing.mIterations = animation.iterations();
+ timing.mIterationStart = animation.iterationStart();
+ timing.mDirection =
+ static_cast<dom::PlaybackDirection>(animation.direction());
+ timing.mFill = static_cast<dom::FillMode>(animation.fillMode());
+ timing.mFunction =
+ AnimationUtils::TimingFunctionToComputedTimingFunction(
+ animation.easingFunction());
+
+ ComputedTiming computedTiming =
+ dom::AnimationEffectReadOnly::GetComputedTimingAt(
+ Nullable<TimeDuration>(elapsedDuration), timing,
+ animation.playbackRate());
+
+ if (computedTiming.mProgress.IsNull()) {
+ continue;
+ }
+
+ uint32_t segmentIndex = 0;
+ size_t segmentSize = animation.segments().Length();
+ AnimationSegment* segment = animation.segments().Elements();
+ while (segment->endPortion() < computedTiming.mProgress.Value() &&
+ segmentIndex < segmentSize - 1) {
+ ++segment;
+ ++segmentIndex;
+ }
+
+ double positionInSegment =
+ (computedTiming.mProgress.Value() - segment->startPortion()) /
+ (segment->endPortion() - segment->startPortion());
+
+ double portion =
+ ComputedTimingFunction::GetPortion(animData.mFunctions[segmentIndex],
+ positionInSegment,
+ computedTiming.mBeforeFlag);
+
+ // interpolate the property
+ Animatable interpolatedValue;
+ SampleValue(portion, animation,
+ animData.mStartValues[segmentIndex],
+ animData.mEndValues[segmentIndex],
+ animData.mEndValues.LastElement(),
+ computedTiming.mCurrentIteration,
+ &interpolatedValue, layer);
+ LayerComposite* layerComposite = layer->AsLayerComposite();
+ switch (animation.property()) {
+ case eCSSProperty_opacity:
+ {
+ layerComposite->SetShadowOpacity(interpolatedValue.get_float());
+ layerComposite->SetShadowOpacitySetByAnimation(true);
+ break;
+ }
+ case eCSSProperty_transform:
+ {
+ Matrix4x4 matrix = interpolatedValue.get_ArrayOfTransformFunction()[0].get_TransformMatrix().value();
+ if (ContainerLayer* c = layer->AsContainerLayer()) {
+ matrix.PostScale(c->GetInheritedXScale(), c->GetInheritedYScale(), 1);
+ }
+ layerComposite->SetShadowBaseTransform(matrix);
+ layerComposite->SetShadowTransformSetByAnimation(true);
+ break;
+ }
+ default:
+ NS_WARNING("Unhandled animated property");
+ }
+ }
+ });
+ return activeAnimations;
+}
+
+static bool
+SampleAPZAnimations(const LayerMetricsWrapper& aLayer, TimeStamp aSampleTime)
+{
+ bool activeAnimations = false;
+
+ ForEachNodePostOrder<ForwardIterator>(aLayer,
+ [&activeAnimations, &aSampleTime](LayerMetricsWrapper aLayerMetrics)
+ {
+ if (AsyncPanZoomController* apzc = aLayerMetrics.GetApzc()) {
+ apzc->ReportCheckerboard(aSampleTime);
+ activeAnimations |= apzc->AdvanceAnimations(aSampleTime);
+ }
+ }
+ );
+
+ return activeAnimations;
+}
+
+void
+AsyncCompositionManager::RecordShadowTransforms(Layer* aLayer)
+{
+ MOZ_ASSERT(gfxPrefs::CollectScrollTransforms());
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+
+ ForEachNodePostOrder<ForwardIterator>(
+ aLayer,
+ [this] (Layer* layer)
+ {
+ for (uint32_t i = 0; i < layer->GetScrollMetadataCount(); i++) {
+ AsyncPanZoomController* apzc = layer->GetAsyncPanZoomController(i);
+ if (!apzc) {
+ continue;
+ }
+ gfx::Matrix4x4 shadowTransform = layer->AsLayerComposite()->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->AsLayerComposite()->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->AsLayerComposite()->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->AsLayerComposite()->SetShadowClipRect(Some(RoundedOut(rect)));
+ }
+}
+
+#ifdef MOZ_WIDGET_ANDROID
+static void
+MoveScrollbarForLayerMargin(Layer* aRoot, FrameMetrics::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->GetScrollbarDirection() == Layer::HORIZONTAL &&
+ aNode->GetScrollbarTargetContainerId() == 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;
+
+ // 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;
+
+ ForEachNode<ForwardIterator>(
+ aLayer,
+ [&stackDeferredClips] (Layer* layer)
+ {
+ stackDeferredClips.push(Maybe<ParentLayerIntRect>());
+ },
+ [this, &aOutFoundRoot, &stackDeferredClips, &appliedTransform, &clipPartsCache] (Layer* layer)
+ {
+ Maybe<ParentLayerIntRect> clipDeferredFromChildren = stackDeferredClips.top();
+ stackDeferredClips.pop();
+ LayerToParentLayerMatrix4x4 oldTransform = layer->GetTransformTyped() *
+ AsyncTransformMatrix();
+
+ AsyncTransformComponentMatrix combinedAsyncTransform;
+ bool hasAsyncTransform = false;
+ 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()));
+ }
+ }
+
+ for (uint32_t i = 0; i < layer->GetScrollMetadataCount(); i++) {
+ AsyncPanZoomController* controller = layer->GetAsyncPanZoomController(i);
+ if (!controller) {
+ continue;
+ }
+
+ hasAsyncTransform = true;
+
+ AsyncTransform asyncTransformWithoutOverscroll =
+ controller->GetCurrentAsyncTransform(AsyncPanZoomController::RESPECT_FORCE_DISABLE);
+ AsyncTransformComponentMatrix overscrollTransform =
+ controller->GetOverscrollTransform(AsyncPanZoomController::RESPECT_FORCE_DISABLE);
+ AsyncTransformComponentMatrix asyncTransform =
+ AsyncTransformComponentMatrix(asyncTransformWithoutOverscroll)
+ * overscrollTransform;
+
+ if (!layer->IsScrollInfoLayer()) {
+ controller->MarkAsyncTransformAppliedToContent();
+ }
+
+ const ScrollMetadata& scrollMetadata = layer->GetScrollMetadata(i);
+ const FrameMetrics& metrics = scrollMetadata.GetMetrics();
+
+#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();
+ CSSToLayerScale geckoZoom = metrics.LayersPixelsPerCSSPixel().ToScaleFactor();
+ if (mIsFirstPaint) {
+ LayerIntPoint scrollOffsetLayerPixels = RoundedToInt(metrics.GetScrollOffset() * geckoZoom);
+ mContentRect = metrics.GetScrollableRect();
+ SetFirstPaintViewport(scrollOffsetLayerPixels,
+ geckoZoom,
+ mContentRect);
+ } else {
+ ParentLayerPoint scrollOffset = controller->GetCurrentAsyncScrollOffset(
+ AsyncPanZoomController::RESPECT_FORCE_DISABLE);
+ // Compute the painted displayport in document-relative CSS pixels.
+ CSSRect displayPort(metrics.GetCriticalDisplayPort().IsEmpty() ?
+ metrics.GetDisplayPort() :
+ metrics.GetCriticalDisplayPort());
+ displayPort += metrics.GetScrollOffset();
+ SyncFrameMetrics(scrollOffset,
+ geckoZoom * asyncTransformWithoutOverscroll.mScale,
+ metrics.GetScrollableRect(), displayPort, geckoZoom, mLayersUpdated,
+ mPaintSyncId, fixedLayerMargins);
+ mFixedLayerMargins = fixedLayerMargins;
+ mLayersUpdated = false;
+ mPaintSyncId = 0;
+ }
+ mIsFirstPaint = false;
+ }
+ }
+#else
+ // 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. If we're
+ // using containerful scrolling, then the clip is not part of the scrolled
+ // frame and should not be transformed.
+ if (!scrollMetadata.UsesContainerScrolling()) {
+ 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(asyncTransformWithoutOverscroll, layer));
+
+ AlignFixedAndStickyLayers(layer, layer, metrics.GetScrollId(), oldTransform,
+ transformWithoutOverscrollOrOmta, fixedLayerMargins,
+ &clipPartsCache);
+
+ // 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);
+ }
+ }
+ }
+
+ 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->AsLayerComposite()->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->GetScrollbarDirection() != Layer::NONE) {
+ ApplyAsyncTransformToScrollbar(layer);
+ }
+ });
+
+ return appliedTransform;
+}
+
+static bool
+LayerIsScrollbarTarget(const LayerMetricsWrapper& aTarget, Layer* aScrollbar)
+{
+ AsyncPanZoomController* apzc = aTarget.GetApzc();
+ if (!apzc) {
+ return false;
+ }
+ const FrameMetrics& metrics = aTarget.Metrics();
+ if (metrics.GetScrollId() != aScrollbar->GetScrollbarTargetContainerId()) {
+ return false;
+ }
+ return !aTarget.IsScrollInfoLayer();
+}
+
+static void
+ApplyAsyncTransformToScrollbarForContent(Layer* aScrollbar,
+ const LayerMetricsWrapper& aContent,
+ bool aScrollbarIsDescendant)
+{
+ // We only apply the transform if the scroll-target layer has non-container
+ // children (i.e. when it has some possibly-visible content). This is to
+ // avoid moving scroll-bars in the situation that only a scroll information
+ // layer has been built for a scroll frame, as this would result in a
+ // disparity between scrollbars and visible content.
+ if (aContent.IsScrollInfoLayer()) {
+ return;
+ }
+
+ const FrameMetrics& metrics = aContent.Metrics();
+ AsyncPanZoomController* apzc = aContent.GetApzc();
+ MOZ_RELEASE_ASSERT(apzc);
+
+ AsyncTransformComponentMatrix asyncTransform =
+ apzc->GetCurrentAsyncTransform(AsyncPanZoomController::RESPECT_FORCE_DISABLE);
+
+ // |asyncTransform| represents the amount by which we have scrolled and
+ // zoomed since the last paint. Because the scrollbar was sized and positioned based
+ // on the painted content, we need to adjust it based on asyncTransform so that
+ // it reflects what the user is actually seeing now.
+ AsyncTransformComponentMatrix scrollbarTransform;
+ if (aScrollbar->GetScrollbarDirection() == Layer::VERTICAL) {
+ const ParentLayerCoord asyncScrollY = asyncTransform._42;
+ const float asyncZoomY = asyncTransform._22;
+
+ // The scroll thumb needs to be scaled in the direction of scrolling by the
+ // inverse of the async zoom. This is because zooming in decreases the
+ // fraction of the whole srollable rect that is in view.
+ const float yScale = 1.f / asyncZoomY;
+
+ // Note: |metrics.GetZoom()| doesn't yet include the async zoom.
+ const CSSToParentLayerScale effectiveZoom(metrics.GetZoom().yScale * asyncZoomY);
+
+ // Here we convert the scrollbar thumb ratio into a true unitless ratio by
+ // dividing out the conversion factor from the scrollframe's parent's space
+ // to the scrollframe's space.
+ const float ratio = aScrollbar->GetScrollbarThumbRatio() /
+ (metrics.GetPresShellResolution() * asyncZoomY);
+ // The scroll thumb needs to be translated in opposite direction of the
+ // async scroll. This is because scrolling down, which translates the layer
+ // content up, should result in moving the scroll thumb down.
+ ParentLayerCoord yTranslation = -asyncScrollY * ratio;
+
+ // The scroll thumb additionally needs to be translated to compensate for
+ // the scale applied above. The origin with respect to which the scale is
+ // applied is the origin of the entire scrollbar, rather than the origin of
+ // the scroll thumb (meaning, for a vertical scrollbar it's at the top of
+ // the composition bounds). This means that empty space above the thumb
+ // is scaled too, effectively translating the thumb. We undo that
+ // translation here.
+ // (One can think of the adjustment being done to the translation here as
+ // a change of basis. We have a method to help with that,
+ // Matrix4x4::ChangeBasis(), but it wouldn't necessarily make the code
+ // cleaner in this case).
+ const CSSCoord thumbOrigin = (metrics.GetScrollOffset().y * ratio);
+ const CSSCoord thumbOriginScaled = thumbOrigin * yScale;
+ const CSSCoord thumbOriginDelta = thumbOriginScaled - thumbOrigin;
+ const ParentLayerCoord thumbOriginDeltaPL = thumbOriginDelta * effectiveZoom;
+ yTranslation -= thumbOriginDeltaPL;
+
+ if (metrics.IsRootContent()) {
+ // Scrollbar for the root are painted at the same resolution as the
+ // content. Since the coordinate space we apply this transform in includes
+ // the resolution, we need to adjust for it as well here. Note that in
+ // another metrics.IsRootContent() hunk below we apply a
+ // resolution-cancelling transform which ensures the scroll thumb isn't
+ // actually rendered at a larger scale.
+ yTranslation *= metrics.GetPresShellResolution();
+ }
+
+ scrollbarTransform.PostScale(1.f, yScale, 1.f);
+ scrollbarTransform.PostTranslate(0, yTranslation, 0);
+ }
+ if (aScrollbar->GetScrollbarDirection() == Layer::HORIZONTAL) {
+ // See detailed comments under the VERTICAL case.
+
+ const ParentLayerCoord asyncScrollX = asyncTransform._41;
+ const float asyncZoomX = asyncTransform._11;
+
+ const float xScale = 1.f / asyncZoomX;
+
+ const CSSToParentLayerScale effectiveZoom(metrics.GetZoom().xScale * asyncZoomX);
+
+ const float ratio = aScrollbar->GetScrollbarThumbRatio() /
+ (metrics.GetPresShellResolution() * asyncZoomX);
+ ParentLayerCoord xTranslation = -asyncScrollX * ratio;
+
+ const CSSCoord thumbOrigin = (metrics.GetScrollOffset().x * ratio);
+ const CSSCoord thumbOriginScaled = thumbOrigin * xScale;
+ const CSSCoord thumbOriginDelta = thumbOriginScaled - thumbOrigin;
+ const ParentLayerCoord thumbOriginDeltaPL = thumbOriginDelta * effectiveZoom;
+ xTranslation -= thumbOriginDeltaPL;
+
+ if (metrics.IsRootContent()) {
+ xTranslation *= metrics.GetPresShellResolution();
+ }
+
+ scrollbarTransform.PostScale(xScale, 1.f, 1.f);
+ scrollbarTransform.PostTranslate(xTranslation, 0, 0);
+ }
+
+ LayerToParentLayerMatrix4x4 transform =
+ aScrollbar->GetLocalTransformTyped() * scrollbarTransform;
+
+ AsyncTransformComponentMatrix compensation;
+ // If the scrollbar layer is for the root then the content's resolution
+ // applies to the scrollbar as well. Since we don't actually want the scroll
+ // thumb's size to vary with the zoom (other than its length reflecting the
+ // fraction of the scrollable length that's in view, which is taken care of
+ // above), we apply a transform to cancel out this resolution.
+ if (metrics.IsRootContent()) {
+ compensation =
+ AsyncTransformComponentMatrix::Scaling(
+ metrics.GetPresShellResolution(),
+ metrics.GetPresShellResolution(),
+ 1.0f).Inverse();
+ }
+ // If the scrollbar layer is a child of the content it is a scrollbar for,
+ // then we need to adjust for any async transform (including an overscroll
+ // transform) on the content. This needs to be cancelled out because layout
+ // positions and sizes the scrollbar on the assumption that there is no async
+ // transform, and without this adjustment the scrollbar will end up in the
+ // wrong place.
+ //
+ // Note that since the async transform is applied on top of the content's
+ // regular transform, we need to make sure to unapply the async transform in
+ // the same coordinate space. This requires applying the content transform
+ // and then unapplying it after unapplying the async transform.
+ if (aScrollbarIsDescendant) {
+ AsyncTransformComponentMatrix overscroll =
+ apzc->GetOverscrollTransform(AsyncPanZoomController::RESPECT_FORCE_DISABLE);
+ Matrix4x4 asyncUntransform = (asyncTransform * overscroll).Inverse().ToUnknownMatrix();
+ Matrix4x4 contentTransform = aContent.GetTransform();
+ Matrix4x4 contentUntransform = contentTransform.Inverse();
+
+ AsyncTransformComponentMatrix asyncCompensation =
+ ViewAs<AsyncTransformComponentMatrix>(
+ contentTransform
+ * asyncUntransform
+ * contentUntransform);
+
+ compensation = compensation * asyncCompensation;
+
+ // 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, asyncCompensation);
+ }
+ }
+ transform = transform * compensation;
+
+ 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(aLayer, scrollTarget, isAncestor);
+ }
+}
+
+void
+AsyncCompositionManager::GetFrameUniformity(FrameUniformityData* aOutData)
+{
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+ mLayerTransformRecorder.EndTest(aOutData);
+}
+
+bool
+AsyncCompositionManager::TransformShadowTree(TimeStamp aCurrentFrame,
+ TimeDuration aVsyncRate,
+ TransformsToSkip aSkip)
+{
+ PROFILER_LABEL("AsyncCompositionManager", "TransformShadowTree",
+ js::ProfileEntry::Category::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.
+ // Use a previous vsync time to make main thread animations and compositor
+ // more in sync with each other.
+ // On the initial frame we use aVsyncTimestamp here so the timestamp on the
+ // second frame are the same as the initial frame, but it does not matter.
+ bool wantNextFrame = SampleAnimations(root,
+ !mPreviousFrameTimeStamp.IsNull() ?
+ mPreviousFrameTimeStamp : aCurrentFrame);
+
+ // 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 : TimeStamp();
+
+ if (!(aSkip & TransformsToSkip::APZ)) {
+ // FIXME/bug 775437: unify this interface with the ~native-fennec
+ // derived code
+ //
+ // Attempt to apply an async content transform to any layer that has
+ // an async pan zoom controller (which means that it is rendered
+ // async using Gecko). If this fails, fall back to transforming the
+ // primary scrollable layer. "Failing" here means that we don't
+ // find a frame that is async scrollable. Note that the fallback
+ // code also includes Fennec which is rendered async. Fennec uses
+ // its own platform-specific async rendering that is done partially
+ // in Gecko and partially in Java.
+ bool foundRoot = false;
+ if (ApplyAsyncContentTransformToTree(root, &foundRoot)) {
+#if defined(MOZ_WIDGET_ANDROID)
+ MOZ_ASSERT(foundRoot);
+ if (foundRoot && mFixedLayerMargins != ScreenMargin()) {
+ MoveScrollbarForLayerMargin(root, mRootScrollableId, mFixedLayerMargins);
+ }
+#endif
+ }
+
+ // Advance APZ animations to the next expected vsync timestamp, if we can
+ // get it.
+ TimeStamp nextFrame = aCurrentFrame;
+
+ MOZ_ASSERT(aVsyncRate != TimeDuration::Forever());
+ if (aVsyncRate != TimeDuration::Forever()) {
+ nextFrame += aVsyncRate;
+ }
+
+ wantNextFrame |= SampleAPZAnimations(LayerMetricsWrapper(root), nextFrame);
+ }
+
+ LayerComposite* rootComposite = root->AsLayerComposite();
+
+ gfx::Matrix4x4 trans = rootComposite->GetShadowBaseTransform();
+ trans *= gfx::Matrix4x4::From2D(mWorldTransform);
+ rootComposite->SetShadowBaseTransform(trans);
+
+ if (gfxPrefs::CollectScrollTransforms()) {
+ RecordShadowTransforms(root);
+ }
+
+ return wantNextFrame;
+}
+
+void
+AsyncCompositionManager::SetFirstPaintViewport(const LayerIntPoint& aOffset,
+ const CSSToLayerScale& aZoom,
+ const CSSRect& aCssPageRect)
+{
+#ifdef MOZ_WIDGET_ANDROID
+ widget::AndroidCompositorWidget* widget =
+ mLayerManager->GetCompositor()->GetWidget()->AsAndroid();
+ if (!widget) {
+ return;
+ }
+ widget->SetFirstPaintViewport(aOffset, aZoom, aCssPageRect);
+#endif
+}
+
+void
+AsyncCompositionManager::SyncFrameMetrics(const ParentLayerPoint& aScrollOffset,
+ const CSSToParentLayerScale& aZoom,
+ const CSSRect& aCssPageRect,
+ const CSSRect& aDisplayPort,
+ const CSSToLayerScale& aPaintedResolution,
+ bool aLayersUpdated,
+ int32_t aPaintSyncId,
+ ScreenMargin& aFixedLayerMargins)
+{
+#ifdef MOZ_WIDGET_ANDROID
+ widget::AndroidCompositorWidget* widget =
+ mLayerManager->GetCompositor()->GetWidget()->AsAndroid();
+ if (!widget) {
+ return;
+ }
+ widget->SyncFrameMetrics(
+ aScrollOffset, aZoom, aCssPageRect, aDisplayPort, aPaintedResolution,
+ aLayersUpdated, aPaintSyncId, aFixedLayerMargins);
+#endif
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/composite/AsyncCompositionManager.h b/gfx/layers/composite/AsyncCompositionManager.h
new file mode 100644
index 000000000..4ec49b1a9
--- /dev/null
+++ b/gfx/layers/composite/AsyncCompositionManager.h
@@ -0,0 +1,283 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_ASYNCCOMPOSITIONMANAGER_H
+#define GFX_ASYNCCOMPOSITIONMANAGER_H
+
+#include "Units.h" // for ScreenPoint, etc
+#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/dom/ScreenOrientation.h" // for ScreenOrientation
+#include "mozilla/gfx/BasePoint.h" // for BasePoint
+#include "mozilla/gfx/Matrix.h" // for Matrix4x4
+#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 AsyncPanZoomController;
+class Layer;
+class LayerManagerComposite;
+class AutoResolveRefLayers;
+class CompositorBridgeParent;
+
+// Represents async transforms consisting of a scale and a translation.
+struct AsyncTransform {
+ explicit AsyncTransform(LayerToParentLayerScale aScale = LayerToParentLayerScale(),
+ ParentLayerPoint aTranslation = ParentLayerPoint())
+ : mScale(aScale)
+ , mTranslation(aTranslation)
+ {}
+
+ operator AsyncTransformComponentMatrix() const
+ {
+ return AsyncTransformComponentMatrix::Scaling(mScale.scale, mScale.scale, 1)
+ .PostTranslate(mTranslation.x, mTranslation.y, 0);
+ }
+
+ bool operator==(const AsyncTransform& rhs) const {
+ return mTranslation == rhs.mTranslation && mScale == rhs.mScale;
+ }
+
+ bool operator!=(const AsyncTransform& rhs) const {
+ return !(*this == rhs);
+ }
+
+ LayerToParentLayerScale mScale;
+ ParentLayerPoint mTranslation;
+};
+
+/**
+ * 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)
+
+ explicit AsyncCompositionManager(LayerManagerComposite* 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
+ * SetFirstPaintViewport on the next frame of composition.
+ */
+ void ForceIsFirstPaint() { mIsFirstPaint = true; }
+
+ // Sample transforms for layer trees. Return true to request
+ // another animation frame.
+ enum class TransformsToSkip : uint8_t { NoneOfThem = 0, APZ = 1 };
+ bool TransformShadowTree(TimeStamp aCurrentFrame,
+ TimeDuration aVsyncRate,
+ TransformsToSkip aSkip = 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,
+ int32_t aPaintSyncId)
+ {
+ mIsFirstPaint |= isFirstPaint;
+ mLayersUpdated = true;
+ mTargetConfig = aTargetConfig;
+ if (aPaintSyncId) {
+ mPaintSyncId = aPaintSyncId;
+ }
+ }
+
+ bool RequiresReorientation(mozilla::dom::ScreenOrientationInternal 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);
+
+ void SetFirstPaintViewport(const LayerIntPoint& aOffset,
+ const CSSToLayerScale& aZoom,
+ const CSSRect& aCssPageRect);
+ void SyncFrameMetrics(const ParentLayerPoint& aScrollOffset,
+ const CSSToParentLayerScale& aZoom,
+ const CSSRect& aCssPageRect,
+ const CSSRect& aDisplayPort,
+ const CSSToLayerScale& aPaintedResolution,
+ bool aLayersUpdated,
+ int32_t aPaintSyncId,
+ ScreenMargin& aFixedLayerMargins);
+
+ /**
+ * 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| optionally 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,
+ FrameMetrics::ViewID aTransformScrollId,
+ const LayerToParentLayerMatrix4x4& aPreviousTransformForRoot,
+ const LayerToParentLayerMatrix4x4& aCurrentTransformForRoot,
+ const ScreenMargin& aFixedLayerMargins,
+ ClipPartsCache* aClipPartsCache);
+
+ /**
+ * 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);
+
+ TargetConfig mTargetConfig;
+ CSSRect mContentRect;
+
+ RefPtr<LayerManagerComposite> 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;
+
+ int32_t mPaintSyncId;
+
+ bool mReadyForCompose;
+
+ gfx::Matrix mWorldTransform;
+ LayerTransformRecorder mLayerTransformRecorder;
+
+ TimeStamp mPreviousFrameTimeStamp;
+
+#ifdef MOZ_WIDGET_ANDROID
+ // 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.
+ FrameMetrics::ViewID mRootScrollableId;
+ ScreenMargin mFixedLayerMargins;
+#endif
+};
+
+MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(AsyncCompositionManager::TransformsToSkip)
+
+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 000000000..3c8299e05
--- /dev/null
+++ b/gfx/layers/composite/CanvasLayerComposite.cpp
@@ -0,0 +1,166 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "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/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:
+ mCompositableHost = aHost;
+ return true;
+ default:
+ return false;
+ }
+
+}
+
+Layer*
+CanvasLayerComposite::GetLayer()
+{
+ return this;
+}
+
+void
+CanvasLayerComposite::SetLayerManager(LayerManagerComposite* aManager)
+{
+ LayerComposite::SetLayerManager(aManager);
+ mManager = aManager;
+ if (mCompositableHost && mCompositor) {
+ mCompositableHost->SetCompositor(mCompositor);
+ }
+}
+
+LayerRenderState
+CanvasLayerComposite::GetRenderState()
+{
+ if (mDestroyed || !mCompositableHost || !mCompositableHost->IsAttached()) {
+ return LayerRenderState();
+ }
+ return mCompositableHost->GetRenderState();
+}
+
+void
+CanvasLayerComposite::RenderLayer(const IntRect& aClipRect)
+{
+ 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(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 000000000..0042d9027
--- /dev/null
+++ b/gfx/layers/composite/CanvasLayerComposite.h
@@ -0,0 +1,81 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#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 "nsDebug.h" // for NS_RUNTIMEABORT
+#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:
+ // CanvasLayer impl
+ virtual void Initialize(const Data& aData) override
+ {
+ NS_RUNTIMEABORT("Incompatibe surface type");
+ }
+
+ virtual LayerRenderState GetRenderState() override;
+
+ virtual bool SetCompositableHost(CompositableHost* aHost) override;
+
+ virtual void Disconnect() override
+ {
+ Destroy();
+ }
+
+ virtual void SetLayerManager(LayerManagerComposite* aManager) override;
+
+ virtual Layer* GetLayer() override;
+ virtual void RenderLayer(const gfx::IntRect& aClipRect) override;
+
+ virtual void CleanupResources() override;
+
+ virtual void GenEffectChain(EffectChain& aEffect) override;
+
+ CompositableHost* GetCompositableHost() override;
+
+ virtual LayerComposite* AsLayerComposite() override { return this; }
+
+ void SetBounds(gfx::IntRect aBounds) { mBounds = aBounds; }
+
+ virtual const char* Name() const override { return "CanvasLayerComposite"; }
+
+protected:
+ virtual 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 000000000..4277a8f70
--- /dev/null
+++ b/gfx/layers/composite/ColorLayerComposite.cpp
@@ -0,0 +1,47 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "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/mozalloc.h" // for operator delete, etc
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::gfx;
+
+void
+ColorLayerComposite::RenderLayer(const IntRect& aClipRect)
+{
+ Rect rect(GetBounds());
+ const Matrix4x4& transform = GetEffectiveTransform();
+
+ RenderWithAllMasks(this, mCompositor, aClipRect,
+ [&](EffectChain& effectChain, const IntRect& clipRect) {
+ GenEffectChain(effectChain);
+ mCompositor->DrawQuad(rect, clipRect, effectChain, GetEffectiveOpacity(),
+ transform);
+ });
+
+ 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 000000000..fb019f74f
--- /dev/null
+++ b/gfx/layers/composite/ColorLayerComposite.h
@@ -0,0 +1,65 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#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:
+ ~ColorLayerComposite()
+ {
+ MOZ_COUNT_DTOR(ColorLayerComposite);
+ Destroy();
+ }
+
+public:
+ // LayerComposite Implementation
+ virtual Layer* GetLayer() override { return this; }
+
+ virtual void SetLayerManager(LayerManagerComposite* aManager) override
+ {
+ LayerComposite::SetLayerManager(aManager);
+ mManager = aManager;
+ }
+
+ virtual void Destroy() override { mDestroyed = true; }
+
+ virtual void RenderLayer(const gfx::IntRect& aClipRect) override;
+ virtual void CleanupResources() override {};
+
+ virtual void GenEffectChain(EffectChain& aEffect) override;
+
+ CompositableHost* GetCompositableHost() override { return nullptr; }
+
+ virtual LayerComposite* AsLayerComposite() override { return this; }
+
+ virtual 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 000000000..5ed3d3fe9
--- /dev/null
+++ b/gfx/layers/composite/CompositableHost.cpp
@@ -0,0 +1,292 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "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 "TiledContentHost.h" // for TiledContentHost
+#include "mozilla/layers/ImageContainerParent.h"
+#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor
+#include "mozilla/layers/TextureHost.h" // for TextureHost, etc
+#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 "mozilla/layers/PCompositableParent.h"
+#include "IPDLActor.h"
+
+namespace mozilla {
+
+using namespace gfx;
+
+namespace layers {
+
+class Compositor;
+
+/**
+ * IPDL actor used by CompositableHost to match with its corresponding
+ * CompositableClient on the content side.
+ *
+ * CompositableParent is owned by the IPDL system. It's deletion is triggered
+ * by either the CompositableChild's deletion, or by the IPDL communication
+ * going down.
+ */
+class CompositableParent : public ParentActor<PCompositableParent>
+{
+public:
+ CompositableParent(CompositableParentManager* aMgr,
+ const TextureInfo& aTextureInfo,
+ uint64_t aID = 0,
+ PImageContainerParent* aImageContainer = nullptr)
+ {
+ MOZ_COUNT_CTOR(CompositableParent);
+ mHost = CompositableHost::Create(aTextureInfo);
+ mHost->SetAsyncID(aID);
+ if (aID) {
+ CompositableMap::Set(aID, this);
+ }
+ if (aImageContainer) {
+ mHost->SetImageContainer(
+ static_cast<ImageContainerParent*>(aImageContainer));
+ }
+ }
+
+ ~CompositableParent()
+ {
+ MOZ_COUNT_DTOR(CompositableParent);
+ CompositableMap::Erase(mHost->GetAsyncID());
+ }
+
+ virtual void Destroy() override
+ {
+ if (mHost) {
+ mHost->Detach(nullptr, CompositableHost::FORCE_DETACH);
+ }
+ }
+
+ RefPtr<CompositableHost> mHost;
+};
+
+CompositableHost::CompositableHost(const TextureInfo& aTextureInfo)
+ : mTextureInfo(aTextureInfo)
+ , mAsyncID(0)
+ , mCompositorID(0)
+ , mCompositor(nullptr)
+ , mLayer(nullptr)
+ , mFlashCounter(0)
+ , mAttached(false)
+ , mKeepAttached(false)
+{
+ MOZ_COUNT_CTOR(CompositableHost);
+}
+
+CompositableHost::~CompositableHost()
+{
+ MOZ_COUNT_DTOR(CompositableHost);
+}
+
+PCompositableParent*
+CompositableHost::CreateIPDLActor(CompositableParentManager* aMgr,
+ const TextureInfo& aTextureInfo,
+ uint64_t aID,
+ PImageContainerParent* aImageContainer)
+{
+ return new CompositableParent(aMgr, aTextureInfo, aID, aImageContainer);
+}
+
+bool
+CompositableHost::DestroyIPDLActor(PCompositableParent* aActor)
+{
+ delete aActor;
+ return true;
+}
+
+CompositableHost*
+CompositableHost::FromIPDLActor(PCompositableParent* aActor)
+{
+ MOZ_ASSERT(aActor);
+ return static_cast<CompositableParent*>(aActor)->mHost;
+}
+
+void
+CompositableHost::UseTextureHost(const nsTArray<TimedTexture>& aTextures)
+{
+ if (GetCompositor()) {
+ for (auto& texture : aTextures) {
+ texture.mTexture->SetCompositor(GetCompositor());
+ }
+ }
+}
+
+void
+CompositableHost::UseComponentAlphaTextures(TextureHost* aTextureOnBlack,
+ TextureHost* aTextureOnWhite)
+{
+ MOZ_ASSERT(aTextureOnBlack && aTextureOnWhite);
+ if (GetCompositor()) {
+ aTextureOnBlack->SetCompositor(GetCompositor());
+ aTextureOnWhite->SetCompositor(GetCompositor());
+ }
+}
+
+void
+CompositableHost::RemoveTextureHost(TextureHost* aTexture)
+{}
+
+void
+CompositableHost::SetCompositor(Compositor* aCompositor)
+{
+ MOZ_ASSERT(aCompositor);
+ mCompositor = aCompositor;
+}
+
+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)
+{
+ 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:
+ result = new ImageHost(aTextureInfo);
+ break;
+ case CompositableType::CONTENT_SINGLE:
+ result = new ContentHostSingleBuffered(aTextureInfo);
+ break;
+ case CompositableType::CONTENT_DOUBLE:
+ 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();
+}
+
+void
+CompositableHost::ReceivedDestroy(PCompositableParent* aActor)
+{
+ static_cast<CompositableParent*>(aActor)->RecvDestroy();
+}
+
+namespace CompositableMap {
+
+typedef std::map<uint64_t, PCompositableParent*> CompositableMap_t;
+static CompositableMap_t* sCompositableMap = nullptr;
+bool IsCreated() {
+ return sCompositableMap != nullptr;
+}
+PCompositableParent* Get(uint64_t aID)
+{
+ if (!IsCreated() || aID == 0) {
+ return nullptr;
+ }
+ CompositableMap_t::iterator it = sCompositableMap->find(aID);
+ if (it == sCompositableMap->end()) {
+ return nullptr;
+ }
+ return it->second;
+}
+void Set(uint64_t aID, PCompositableParent* aParent)
+{
+ if (!IsCreated() || aID == 0) {
+ return;
+ }
+ (*sCompositableMap)[aID] = aParent;
+}
+void Erase(uint64_t aID)
+{
+ if (!IsCreated() || aID == 0) {
+ return;
+ }
+ CompositableMap_t::iterator it = sCompositableMap->find(aID);
+ if (it != sCompositableMap->end()) {
+ sCompositableMap->erase(it);
+ }
+}
+void Clear()
+{
+ if (!IsCreated()) {
+ return;
+ }
+ sCompositableMap->clear();
+}
+void Create()
+{
+ if (sCompositableMap == nullptr) {
+ sCompositableMap = new CompositableMap_t;
+ }
+}
+void Destroy()
+{
+ Clear();
+ delete sCompositableMap;
+ sCompositableMap = nullptr;
+}
+
+} // namespace CompositableMap
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/composite/CompositableHost.h b/gfx/layers/composite/CompositableHost.h
new file mode 100644
index 000000000..d8a967732
--- /dev/null
+++ b/gfx/layers/composite/CompositableHost.h
@@ -0,0 +1,316 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#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/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 Compositor;
+class ImageContainerParent;
+class ThebesBufferData;
+class TiledContentHost;
+class CompositableParentManager;
+class PCompositableParent;
+struct EffectChain;
+
+/**
+ * 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_REFCOUNTING(CompositableHost)
+ explicit CompositableHost(const TextureInfo& aTextureInfo);
+
+ static already_AddRefed<CompositableHost> Create(const TextureInfo& aTextureInfo);
+
+ virtual CompositableType GetType() = 0;
+
+ // If base class overrides, it should still call the parent implementation
+ virtual void SetCompositor(Compositor* aCompositor);
+
+ // composite the contents of this buffer host to the compositor's surface
+ virtual void Composite(LayerComposite* aLayer,
+ EffectChain& aEffectChain,
+ float aOpacity,
+ const gfx::Matrix4x4& aTransform,
+ const gfx::SamplingFilter aSamplingFilter,
+ const gfx::IntRect& aClipRect,
+ const nsIntRegion* aVisibleRegion = nullptr) = 0;
+
+ /**
+ * Update the content host.
+ * aUpdated is the region which should be updated
+ * aUpdatedRegionBack is the region in aNewBackResult which has been updated
+ */
+ virtual bool UpdateThebes(const ThebesBufferData& aData,
+ const nsIntRegion& aUpdated,
+ const nsIntRegion& aOldValidRegionBack,
+ nsIntRegion* aUpdatedRegionBack)
+ {
+ 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 LayerRenderState GetRenderState() = 0;
+
+ virtual gfx::IntSize GetImageSize() const
+ {
+ MOZ_ASSERT(false, "Should have been overridden");
+ return gfx::IntSize();
+ }
+
+ /**
+ * 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();
+
+ Compositor* GetCompositor() const
+ {
+ return mCompositor;
+ }
+
+ Layer* GetLayer() const { return mLayer; }
+ void SetLayer(Layer* aLayer) { mLayer = aLayer; }
+
+ virtual void SetImageContainer(ImageContainerParent* aImageContainer) {}
+
+ virtual TiledContentHost* AsTiledContentHost() { 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,
+ Compositor* aCompositor,
+ AttachFlags aFlags = NO_FLAGS)
+ {
+ MOZ_ASSERT(aCompositor, "Compositor is required");
+ NS_ASSERTION(aFlags & ALLOW_REATTACH || !mAttached,
+ "Re-attaching compositables must be explicitly authorised");
+ SetCompositor(aCompositor);
+ 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; }
+
+ static void
+ ReceivedDestroy(PCompositableParent* aActor);
+
+ 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 UseOverlaySource(OverlaySource aOverlay,
+ const gfx::IntRect& aPictureRect) { }
+
+ 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;
+ }
+
+ static PCompositableParent*
+ CreateIPDLActor(CompositableParentManager* mgr,
+ const TextureInfo& textureInfo,
+ uint64_t asyncID,
+ PImageContainerParent* aImageContainer = nullptr);
+
+ static bool DestroyIPDLActor(PCompositableParent* actor);
+
+ static CompositableHost* FromIPDLActor(PCompositableParent* actor);
+
+ uint64_t GetCompositorID() const { return mCompositorID; }
+
+ uint64_t GetAsyncID() const { return mAsyncID; }
+
+ void SetCompositorID(uint64_t aID) { mCompositorID = aID; }
+
+ void SetAsyncID(uint64_t aID) { mAsyncID = 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() {}
+
+protected:
+ TextureInfo mTextureInfo;
+ uint64_t mAsyncID;
+ uint64_t mCompositorID;
+ RefPtr<Compositor> mCompositor;
+ 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;
+};
+
+/**
+ * Global CompositableMap, to use in the compositor thread only.
+ *
+ * PCompositable and PLayer can, in the case of async textures, be managed by
+ * different top level protocols. In this case they don't share the same
+ * communication channel and we can't send an OpAttachCompositable (PCompositable,
+ * PLayer) message.
+ *
+ * In order to attach a layer and the right compositable if the the compositable
+ * is async, we store references to the async compositables in a CompositableMap
+ * that is accessed only on the compositor thread. During a layer transaction we
+ * send the message OpAttachAsyncCompositable(ID, PLayer), and on the compositor
+ * side we lookup the ID in the map and attach the corresponding compositable to
+ * the layer.
+ *
+ * CompositableMap must be global because the image bridge doesn't have any
+ * reference to whatever we have created with PLayerTransaction. So, the only way to
+ * actually connect these two worlds is to have something global that they can
+ * both query (in the same thread). The map is not allocated the map on the
+ * stack to avoid the badness of static initialization.
+ *
+ * Also, we have a compositor/PLayerTransaction protocol/etc. per layer manager, and the
+ * ImageBridge is used by all the existing compositors that have a video, so
+ * there isn't an instance or "something" that lives outside the boudaries of a
+ * given layer manager on the compositor thread except the image bridge and the
+ * thread itself.
+ */
+namespace CompositableMap {
+ void Create();
+ void Destroy();
+ PCompositableParent* Get(uint64_t aID);
+ void Set(uint64_t aID, PCompositableParent* aParent);
+ void Erase(uint64_t aID);
+ void Clear();
+} // namespace CompositableMap
+
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/composite/ContainerLayerComposite.cpp b/gfx/layers/composite/ContainerLayerComposite.cpp
new file mode 100755
index 000000000..35070cad6
--- /dev/null
+++ b/gfx/layers/composite/ContainerLayerComposite.cpp
@@ -0,0 +1,698 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ContainerLayerComposite.h"
+#include <algorithm> // for min
+#include "apz/src/AsyncPanZoomController.h" // for AsyncPanZoomController
+#include "FrameMetrics.h" // for FrameMetrics
+#include "Units.h" // for LayerRect, LayerPixel, etc
+#include "CompositableHost.h" // for CompositableHost
+#include "gfxEnv.h" // for gfxEnv
+#include "gfxPrefs.h" // for gfxPrefs
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/RefPtr.h" // for RefPtr
+#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/Compositor.h" // for Compositor, etc
+#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/AsyncCompositionManager.h" // for ViewTransform
+#include "mozilla/layers/LayerMetricsWrapper.h" // for LayerMetricsWrapper
+#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
+#ifdef MOZ_ENABLE_PROFILER_SPS
+#include "ProfilerMarkers.h" // for ProfilerMarkers
+#endif
+
+#define CULLING_LOG(...)
+// #define CULLING_LOG(...) printf_stderr("CULLING: " __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.ToUnknownRegion().GetBounds().TopLeft();
+ aManager->GetTextRenderer()->RenderText(ss.str().c_str(), topLeft,
+ aLayer->GetEffectiveTransform(), 16,
+ maxWidth);
+}
+
+template<class ContainerT>
+static gfx::IntRect ContainerVisibleRect(ContainerT* aContainer)
+{
+ gfx::IntRect surfaceRect = aContainer->GetLocalVisibleRegion().ToUnknownRegion().GetBounds();
+ return surfaceRect;
+}
+
+static void PrintUniformityInfo(Layer* aLayer)
+{
+#ifdef MOZ_ENABLE_PROFILER_SPS
+ if (!profiler_is_active()) {
+ 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->AsLayerComposite()->GetShadowBaseTransform();
+ if (!transform.Is2D()) {
+ return;
+ }
+
+ Point translation = transform.As2D().GetTranslation();
+ LayerTranslationPayload* payload = new LayerTranslationPayload(aLayer, translation);
+ PROFILER_MARKER_PAYLOAD("LayerTranslation", payload);
+#endif
+}
+
+/* all of the per-layer prepared data we need to maintain */
+struct PreparedLayer
+{
+ PreparedLayer(Layer *aLayer, RenderTargetIntRect aClipRect) :
+ mLayer(aLayer), mClipRect(aClipRect) {}
+ RefPtr<Layer> mLayer;
+ RenderTargetIntRect mClipRect;
+};
+
+/* 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)
+{
+ aContainer->mPrepared = MakeUnique<PreparedData>();
+ aContainer->mPrepared->mNeedsSurfaceCopy = false;
+
+ /**
+ * Determine which layers to draw.
+ */
+ AutoTArray<Layer*, 12> children;
+ aContainer->SortChildrenBy3DZOrder(children);
+
+ for (uint32_t i = 0; i < children.Length(); i++) {
+ LayerComposite* layerToRender = static_cast<LayerComposite*>(children.ElementAt(i)->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() &&
+ !layerToRender->NeedToDrawCheckerboarding(nullptr)) {
+ 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));
+ }
+
+ 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, LayerManagerComposite* aManager,
+ const RenderTargetIntRect& aClipRect, Layer* aLayer)
+{
+ Compositor* compositor = aManager->GetCompositor();
+
+ if (aLayer->GetScrollMetadataCount() < 1) {
+ return;
+ }
+
+ AsyncPanZoomController* controller = aLayer->GetAsyncPanZoomController(0);
+ if (!controller) {
+ return;
+ }
+
+ ParentLayerPoint scrollOffset = controller->GetCurrentAsyncScrollOffset(AsyncPanZoomController::RESPECT_FORCE_DISABLE);
+
+ // Options
+ const int verticalPadding = 10;
+ const int horizontalPadding = 5;
+ gfx::Color backgroundColor(0.3f, 0.3f, 0.3f, 0.3f);
+ gfx::Color tileActiveColor(1, 1, 1, 0.4f);
+ gfx::Color tileBorderColor(0, 0, 0, 0.1f);
+ gfx::Color pageBorderColor(0, 0, 0);
+ gfx::Color criticalDisplayPortColor(1.f, 1.f, 0);
+ gfx::Color displayPortColor(0, 1.f, 0);
+ gfx::Color viewPortColor(0, 0, 1.f, 0.3f);
+ gfx::Color visibilityColor(1.f, 0, 0);
+
+ // Rects
+ const FrameMetrics& fm = aLayer->GetFrameMetrics(0);
+ ParentLayerRect compositionBounds = fm.GetCompositionBounds();
+ LayerRect scrollRect = fm.GetScrollableRect() * fm.LayersPixelsPerCSSPixel();
+ LayerRect viewRect = ParentLayerRect(scrollOffset, compositionBounds.Size()) / LayerToParentLayerScale(1);
+ LayerRect dp = (fm.GetDisplayPort() + fm.GetScrollOffset()) * fm.LayersPixelsPerCSSPixel();
+ Maybe<LayerRect> cdp;
+ if (!fm.GetCriticalDisplayPort().IsEmpty()) {
+ cdp = Some((fm.GetCriticalDisplayPort() + fm.GetScrollOffset()) * fm.LayersPixelsPerCSSPixel());
+ }
+
+ // Don't render trivial minimap. They can show up from textboxes and other tiny frames.
+ if (viewRect.width < 64 && viewRect.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());
+
+ // If enabled, render information about visibility.
+ if (gfxPrefs::APZMinimapVisibilityEnabled()) {
+ // Retrieve the APZC scrollable layer guid, which we'll use to get the
+ // appropriate visibility information from the layer manager.
+ AsyncPanZoomController* controller = aLayer->GetAsyncPanZoomController(0);
+ MOZ_ASSERT(controller);
+
+ ScrollableLayerGuid guid = controller->GetGuid();
+
+ // Get the approximately visible region.
+ static CSSIntRegion emptyRegion;
+ CSSIntRegion* visibleRegion = aManager->GetApproximatelyVisibleRegion(guid);
+ if (!visibleRegion) {
+ visibleRegion = &emptyRegion;
+ }
+
+ // Iterate through and draw the rects in the region.
+ for (CSSIntRegion::RectIterator iterator = visibleRegion->RectIter();
+ !iterator.Done();
+ iterator.Next())
+ {
+ CSSIntRect rect = iterator.Get();
+ LayerRect scaledRect = rect * fm.LayersPixelsPerCSSPixel();
+ Rect r = transform.TransformBounds(scaledRect.ToUnknownRect());
+ compositor->FillRect(r, visibilityColor, 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 viewport.
+ r = transform.TransformBounds(viewRect.ToUnknownRect());
+ compositor->SlowDrawRect(r, viewPortColor, clipRect, aContainer->GetEffectiveTransform(), 2);
+}
+
+
+template<class ContainerT> void
+RenderLayers(ContainerT* aContainer,
+ LayerManagerComposite* aManager,
+ const RenderTargetIntRect& aClipRect)
+{
+ Compositor* compositor = aManager->GetCompositor();
+
+ for (size_t i = 0u; i < aContainer->mPrepared->mLayers.Length(); i++) {
+ PreparedLayer& preparedData = aContainer->mPrepared->mLayers[i];
+ LayerComposite* layerToRender = static_cast<LayerComposite*>(preparedData.mLayer->ImplData());
+ const RenderTargetIntRect& clipRect = preparedData.mClipRect;
+ Layer* layer = layerToRender->GetLayer();
+
+ if (layerToRender->HasStaleCompositor()) {
+ continue;
+ }
+
+ if (gfxPrefs::LayersDrawFPS()) {
+ for (const auto& metadata : layer->GetAllScrollMetadata()) {
+ if (metadata.IsApzForceDisabled()) {
+ aManager->DisabledApzWarning();
+ break;
+ }
+ }
+ }
+
+ Color color;
+ if (layerToRender->NeedToDrawCheckerboarding(&color)) {
+ if (gfxPrefs::APZHighlightCheckerboardedAreas()) {
+ color = Color(255 / 255.f, 188 / 255.f, 217 / 255.f, 1.f); // "Cotton Candy"
+ }
+ // Ideally we would want to intersect the checkerboard region from the APZ with the layer bounds
+ // and only fill in that area. However the layer bounds takes into account the base translation
+ // for the painted layer whereas the checkerboard region does not. One does not simply
+ // intersect areas in different coordinate spaces. So we do this a little more permissively
+ // and only fill in the background when we know there is checkerboard, which in theory
+ // should only occur transiently.
+ gfx::IntRect layerBounds = layer->GetLayerBounds();
+ EffectChain effectChain(layer);
+ effectChain.mPrimaryEffect = new EffectSolidColor(color);
+ aManager->GetCompositor()->DrawQuad(gfx::Rect(layerBounds.x, layerBounds.y, layerBounds.width, layerBounds.height),
+ clipRect.ToUnknownRect(),
+ effectChain, layer->GetEffectiveOpacity(),
+ layer->GetEffectiveTransform());
+ }
+
+ 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 {
+ layerToRender->RenderLayer(clipRect.ToUnknownRect());
+ }
+
+ if (gfxPrefs::UniformityInfo()) {
+ PrintUniformityInfo(layer);
+ }
+
+ if (gfxPrefs::DrawLayerInfo()) {
+ DrawLayerInfo(clipRect, 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;
+ for (uint32_t i = layer->GetScrollMetadataCount(); i > 0; --i) {
+ if (layer->GetFrameMetrics(i - 1).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 = layer->GetFrameMetrics(i - 1).GetCompositionBounds();
+ aManager->GetCompositor()->DrawDiagnostics(DiagnosticFlags::CONTAINER,
+ compositionBounds.ToUnknownRect(),
+ aClipRect.ToUnknownRect(),
+ asyncTransform * aContainer->GetEffectiveTransform());
+ if (AsyncPanZoomController* apzc = layer->GetAsyncPanZoomController(i - 1)) {
+ asyncTransform =
+ apzc->GetCurrentAsyncTransformWithOverscroll(AsyncPanZoomController::RESPECT_FORCE_DISABLE).ToUnknownMatrix()
+ * asyncTransform;
+ }
+ }
+ }
+
+ if (gfxPrefs::APZMinimap()) {
+ RenderMinimap(aContainer, 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().ToUnknownRegion().GetBounds();
+ 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 -= compositor->GetCurrentRenderTarget()->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));
+ // 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)
+{
+ 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) {
+ aContainer->mPrepared = nullptr;
+ return;
+ }
+
+ gfx::Rect visibleRect(aContainer->GetLocalVisibleRegion().ToUnknownRegion().GetBounds());
+ 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->DrawQuad(visibleRect, clipRect, effectChain,
+ container->GetEffectiveOpacity(),
+ container->GetEffectiveTransform());
+ });
+ } else {
+ RenderLayers(aContainer, aManager, RenderTargetIntRect::FromUnknownRect(aClipRect));
+ }
+ aContainer->mPrepared = nullptr;
+
+ // 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 (gfxPrefs::LayersDrawFPS() && aContainer->IsScrollInfoLayer()) {
+ // 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 (AsyncPanZoomController* apzc = i.GetApzc()) {
+ if (!apzc->GetAsyncTransformAppliedToContent()
+ && !AsyncTransformComponentMatrix(apzc->GetCurrentAsyncTransform(AsyncPanZoomController::NORMAL)).IsIdentity()) {
+ 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.
+ while (mFirstChild) {
+ RemoveChild(mFirstChild);
+ }
+}
+
+void
+ContainerLayerComposite::Destroy()
+{
+ if (!mDestroyed) {
+ while (mFirstChild) {
+ static_cast<LayerComposite*>(GetFirstChild()->ImplData())->Destroy();
+ RemoveChild(mFirstChild);
+ }
+ mDestroyed = true;
+ }
+}
+
+LayerComposite*
+ContainerLayerComposite::GetFirstChildComposite()
+{
+ if (!mFirstChild) {
+ return nullptr;
+ }
+ return static_cast<LayerComposite*>(mFirstChild->ImplData());
+}
+
+void
+ContainerLayerComposite::RenderLayer(const gfx::IntRect& aClipRect)
+{
+ ContainerRender(this, mCompositeManager, aClipRect);
+}
+
+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()) {
+ LayerComposite* layerToCleanup = static_cast<LayerComposite*>(l->ImplData());
+ layerToCleanup->CleanupResources();
+ }
+}
+
+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->ImplData());
+}
+
+void
+RefLayerComposite::RenderLayer(const gfx::IntRect& aClipRect)
+{
+ ContainerRender(this, mCompositeManager, aClipRect);
+}
+
+void
+RefLayerComposite::Prepare(const RenderTargetIntRect& aClipRect)
+{
+ ContainerPrepare(this, mCompositeManager, aClipRect);
+}
+
+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 000000000..5128b9d80
--- /dev/null
+++ b/gfx/layers/composite/ContainerLayerComposite.h
@@ -0,0 +1,191 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#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 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);
+ template<class ContainerT>
+ friend void RenderLayers(ContainerT* aContainer,
+ LayerManagerComposite* aManager,
+ const RenderTargetIntRect& aClipRect);
+ 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, LayerManagerComposite* aManager,
+ const RenderTargetIntRect& aClipRect, Layer* aLayer);
+public:
+ explicit ContainerLayerComposite(LayerManagerComposite *aManager);
+
+protected:
+ ~ContainerLayerComposite();
+
+public:
+ // LayerComposite Implementation
+ virtual Layer* GetLayer() override { return this; }
+
+ virtual void SetLayerManager(LayerManagerComposite* aManager) override
+ {
+ LayerComposite::SetLayerManager(aManager);
+ mManager = aManager;
+ mLastIntermediateSurface = nullptr;
+ }
+
+ virtual void Destroy() override;
+
+ LayerComposite* GetFirstChildComposite() override;
+
+ virtual void RenderLayer(const gfx::IntRect& aClipRect) override;
+ virtual void Prepare(const RenderTargetIntRect& aClipRect) override;
+
+ virtual void ComputeEffectiveTransforms(const gfx::Matrix4x4& aTransformToSurface) override
+ {
+ DefaultComputeEffectiveTransforms(aTransformToSurface);
+ }
+
+ virtual void CleanupResources() override;
+
+ virtual LayerComposite* AsLayerComposite() override { return this; }
+
+ // container layers don't use a compositable
+ CompositableHost* GetCompositableHost() override { return nullptr; }
+
+ // If the layer is marked as scale-to-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.
+ virtual float GetPostXScale() const override {
+ if (mScaleToResolution) {
+ return mPostXScale * mPresShellResolution;
+ }
+ return mPostXScale;
+ }
+ virtual float GetPostYScale() const override {
+ if (mScaleToResolution) {
+ return mPostYScale * mPresShellResolution;
+ }
+ return mPostYScale;
+ }
+
+ virtual 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);
+ template<class ContainerT>
+ friend void RenderLayers(ContainerT* aContainer,
+ LayerManagerComposite* aManager,
+ const gfx::IntRect& aClipRect);
+ 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:
+ ~RefLayerComposite();
+
+public:
+ /** LayerOGL implementation */
+ Layer* GetLayer() override { return this; }
+
+ virtual void SetLayerManager(LayerManagerComposite* aManager) override
+ {
+ LayerComposite::SetLayerManager(aManager);
+ mManager = aManager;
+ mLastIntermediateSurface = nullptr;
+ }
+
+ void Destroy() override;
+
+ LayerComposite* GetFirstChildComposite() override;
+
+ virtual void RenderLayer(const gfx::IntRect& aClipRect) override;
+ virtual void Prepare(const RenderTargetIntRect& aClipRect) override;
+
+ virtual void ComputeEffectiveTransforms(const gfx::Matrix4x4& aTransformToSurface) override
+ {
+ DefaultComputeEffectiveTransforms(aTransformToSurface);
+ }
+
+ virtual void CleanupResources() override;
+
+ virtual LayerComposite* AsLayerComposite() override { return this; }
+
+ // ref layers don't use a compositable
+ CompositableHost* GetCompositableHost() override { return nullptr; }
+
+ virtual 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 000000000..b1d92a6c9
--- /dev/null
+++ b/gfx/layers/composite/ContentHost.cpp
@@ -0,0 +1,491 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/layers/ContentHost.h"
+#include "LayersLogging.h" // for AppendToString
+#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()
+{
+}
+
+void
+ContentHostTexture::Composite(LayerComposite* aLayer,
+ EffectChain& aEffectChain,
+ float aOpacity,
+ const gfx::Matrix4x4& aTransform,
+ const SamplingFilter aSamplingFilter,
+ const IntRect& aClipRect,
+ const nsIntRegion* aVisibleRegion)
+{
+ 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,
+ GetRenderState());
+ 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);
+ GetCompositor()->DrawQuad(rect, aClipRect, aEffectChain, aOpacity, aTransform);
+ if (usingTiles) {
+ DiagnosticFlags diagnostics = DiagnosticFlags::CONTENT | DiagnosticFlags::BIGIMAGE;
+ if (iterOnWhite) {
+ diagnostics |= DiagnosticFlags::COMPONENT_ALPHA;
+ }
+ GetCompositor()->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;
+ }
+ GetCompositor()->DrawDiagnostics(diagnostics, nsIntRegion(mBufferRect), aClipRect,
+ aTransform, mFlashCounter);
+}
+
+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::SetCompositor(Compositor* aCompositor)
+{
+ ContentHostBase::SetCompositor(aCompositor);
+ if (mTextureHost) {
+ mTextureHost->SetCompositor(aCompositor);
+ }
+ if (mTextureHostOnWhite) {
+ mTextureHostOnWhite->SetCompositor(aCompositor);
+ }
+}
+
+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,
+ nsIntRegion* aUpdatedRegionBack)
+{
+ aUpdatedRegionBack->SetEmpty();
+
+ 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,
+ nsIntRegion* aUpdatedRegionBack)
+{
+ if (!mTextureHost) {
+ mInitialised = false;
+
+ *aUpdatedRegionBack = aUpdated;
+ 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();
+
+ *aUpdatedRegionBack = aUpdated;
+
+ // 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();
+
+ AppendToString(aStream, mBufferRect, " [buffer-rect=", "]");
+ AppendToString(aStream, mBufferRotation, " [buffer-rotation=", "]");
+ if (PaintWillResample()) {
+ aStream << " [paint-will-resample]";
+ }
+
+ if (mTextureHost) {
+ nsAutoCString pfx(aPrefix);
+ pfx += " ";
+
+ aStream << "\n";
+ mTextureHost->PrintInfo(aStream, pfx.get());
+ }
+}
+
+
+LayerRenderState
+ContentHostTexture::GetRenderState()
+{
+ if (!mTextureHost) {
+ return LayerRenderState();
+ }
+
+ LayerRenderState result = mTextureHost->GetRenderState();
+
+ if (mBufferRotation != nsIntPoint()) {
+ result.mFlags |= LayerRenderStateFlags::BUFFER_ROTATION;
+ }
+ result.SetOffset(GetOriginOffset());
+ return result;
+}
+
+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,
+ GetRenderState());
+}
+
+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 000000000..9b7498415
--- /dev/null
+++ b/gfx/layers/composite/ContentHost.h
@@ -0,0 +1,228 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#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 RotatedContentBuffer, 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/Rect.h" // for Rect
+#include "mozilla/gfx/Types.h" // for SamplingFilter
+#include "mozilla/layers/CompositorTypes.h" // for TextureInfo, etc
+#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 "nsDebug.h" // for NS_RUNTIMEABORT
+#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,
+ nsIntRegion* aUpdatedRegionBack) = 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) { }
+
+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 RotatedContentBuffer::ContentType ContentType;
+ typedef RotatedContentBuffer::PaintState PaintState;
+
+ explicit ContentHostBase(const TextureInfo& aTextureInfo);
+ virtual ~ContentHostBase();
+
+protected:
+ virtual nsIntPoint GetOriginOffset()
+ {
+ return mBufferRect.TopLeft() - mBufferRotation;
+ }
+
+
+ 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)
+ { }
+
+ virtual void Composite(LayerComposite* aLayer,
+ EffectChain& aEffectChain,
+ float aOpacity,
+ const gfx::Matrix4x4& aTransform,
+ const gfx::SamplingFilter aSamplingFilter,
+ const gfx::IntRect& aClipRect,
+ const nsIntRegion* aVisibleRegion = nullptr) override;
+
+ virtual void SetCompositor(Compositor* aCompositor) override;
+
+ virtual already_AddRefed<gfx::DataSourceSurface> GetAsSurface() override;
+
+ virtual void Dump(std::stringstream& aStream,
+ const char* aPrefix="",
+ bool aDumpHtml=false) override;
+
+ virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix) override;
+
+ virtual void UseTextureHost(const nsTArray<TimedTexture>& aTextures) override;
+ virtual void UseComponentAlphaTextures(TextureHost* aTextureOnBlack,
+ TextureHost* aTextureOnWhite) override;
+
+ virtual 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;
+ }
+ virtual void Unlock() override {
+ MOZ_ASSERT(mLocked);
+ mTextureHost->Unlock();
+ if (mTextureHostOnWhite) {
+ mTextureHostOnWhite->Unlock();
+ }
+ mLocked = false;
+ }
+
+ LayerRenderState GetRenderState() override;
+
+ virtual 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() {}
+
+ virtual CompositableType GetType() { return CompositableType::CONTENT_DOUBLE; }
+
+ virtual bool UpdateThebes(const ThebesBufferData& aData,
+ const nsIntRegion& aUpdated,
+ const nsIntRegion& aOldValidRegionBack,
+ nsIntRegion* aUpdatedRegionBack);
+
+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() {}
+
+ virtual CompositableType GetType() { return CompositableType::CONTENT_SINGLE; }
+
+ virtual bool UpdateThebes(const ThebesBufferData& aData,
+ const nsIntRegion& aUpdated,
+ const nsIntRegion& aOldValidRegionBack,
+ nsIntRegion* aUpdatedRegionBack);
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/composite/FPSCounter.cpp b/gfx/layers/composite/FPSCounter.cpp
new file mode 100644
index 000000000..02ffc4b2c
--- /dev/null
+++ b/gfx/layers/composite/FPSCounter.cpp
@@ -0,0 +1,450 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <stddef.h> // for size_t
+#include "Units.h" // for ScreenIntRect
+#include "gfxRect.h" // for gfxRect
+#include "gfxPrefs.h" // for gfxPrefs
+#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 "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() { }
+
+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 >= 0);
+ 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 >= 0);
+ 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 (!gfxPrefs::FPSPrintHistogram()) {
+ 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)
+{
+ int length = 0;
+ const int kBufferLength = 512;
+ char buffer[kBufferLength];
+
+ for (std::map<int, int>::iterator iter = aHistogram.begin();
+ iter != aHistogram.end(); iter++)
+ {
+ int fps = iter->first;
+ int count = iter->second;
+
+ length += snprintf(buffer + length, kBufferLength - length,
+ "FPS: %d = %d. ", fps, count);
+ NS_ASSERTION(length >= kBufferLength, "Buffer overrun while printing FPS histogram.");
+ }
+
+ 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 (!gfxPrefs::WriteFPSToFile()) {
+ 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(NS_LITERAL_STRING("fps.txt"));
+ } else {
+ resultFile->Append(NS_LITERAL_STRING("txn.txt"));
+ }
+
+ 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);
+
+ nsAutoCString path;
+ rv = resultFile->GetNativePath(path);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ printf_stderr("Wrote FPS data to file: %s\n", path.get());
+ return NS_OK;
+}
+
+FPSState::FPSState()
+ : mCompositionFps("Compositor")
+ , mTransactionFps("LayerTransactions")
+{
+}
+
+// Size of the builtin font.
+static const float FontHeight = 7.f;
+static const float FontWidth = 4.f;
+
+// Scale the font when drawing it to the viewport for better readability.
+static const float FontScaleX = 2.f;
+static const float FontScaleY = 3.f;
+
+static void DrawDigits(unsigned int aValue,
+ int aOffsetX, int aOffsetY,
+ Compositor* aCompositor,
+ EffectChain& aEffectChain)
+{
+ if (aValue > 999) {
+ aValue = 999;
+ }
+
+ unsigned int divisor = 100;
+ float textureWidth = FontWidth * 10;
+ gfx::Float opacity = 1;
+ gfx::Matrix4x4 transform;
+ transform.PreScale(FontScaleX, FontScaleY, 1);
+
+ for (size_t n = 0; n < 3; ++n) {
+ unsigned int digit = aValue % (divisor * 10) / divisor;
+ divisor /= 10;
+
+ RefPtr<TexturedEffect> texturedEffect = static_cast<TexturedEffect*>(aEffectChain.mPrimaryEffect.get());
+ texturedEffect->mTextureCoords = Rect(float(digit * FontWidth) / textureWidth, 0, FontWidth / textureWidth, 1.0f);
+
+ Rect drawRect = Rect(aOffsetX + n * FontWidth, aOffsetY, FontWidth, FontHeight);
+ IntRect clipRect = IntRect(0, 0, 300, 100);
+ aCompositor->DrawQuad(drawRect, clipRect, aEffectChain, opacity, transform);
+ }
+}
+
+void FPSState::DrawFPS(TimeStamp aNow,
+ int aOffsetX, int aOffsetY,
+ unsigned int aFillRatio,
+ Compositor* aCompositor)
+{
+ if (!mFPSTextureSource) {
+ const char *text =
+ " "
+ " XXX XX XXX XXX X X XXX XXX XXX XXX XXX"
+ " X X X X X X X X X X X X X X"
+ " X X X XXX XXX XXX XXX XXX X XXX XXX"
+ " X X X X X X X X X X X X X"
+ " XXX XXX XXX XXX X XXX XXX X XXX X"
+ " ";
+
+ // Convert the text encoding above to RGBA.
+ int w = FontWidth * 10;
+ int h = FontHeight;
+ uint32_t* buf = (uint32_t *) malloc(w * h * sizeof(uint32_t));
+ for (int i = 0; i < h; i++) {
+ for (int j = 0; j < w; j++) {
+ uint32_t purple = 0xfff000ff;
+ uint32_t white = 0xffffffff;
+ buf[i * w + j] = (text[i * w + j] == ' ') ? purple : white;
+ }
+ }
+
+ int bytesPerPixel = 4;
+ RefPtr<DataSourceSurface> fpsSurface = Factory::CreateWrappingDataSourceSurface(
+ reinterpret_cast<uint8_t*>(buf), w * bytesPerPixel, IntSize(w, h), SurfaceFormat::B8G8R8A8);
+ mFPSTextureSource = aCompositor->CreateDataTextureSource();
+ mFPSTextureSource->Update(fpsSurface);
+ }
+
+ EffectChain effectChain;
+ effectChain.mPrimaryEffect = CreateTexturedEffect(SurfaceFormat::B8G8R8A8,
+ mFPSTextureSource,
+ SamplingFilter::POINT,
+ true);
+
+ unsigned int fps = unsigned(mCompositionFps.AddFrameAndGetFps(aNow));
+ unsigned int txnFps = unsigned(mTransactionFps.GetFPS(aNow));
+
+ DrawDigits(fps, aOffsetX + 0, aOffsetY, aCompositor, effectChain);
+ DrawDigits(txnFps, aOffsetX + FontWidth * 4, aOffsetY, aCompositor, effectChain);
+ DrawDigits(aFillRatio, aOffsetX + FontWidth * 8, aOffsetY, aCompositor, effectChain);
+}
+
+} // 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 000000000..7a59267fc
--- /dev/null
+++ b/gfx/layers/composite/FPSCounter.h
@@ -0,0 +1,114 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#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 {
+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;
+};
+
+struct FPSState {
+ FPSState();
+ void DrawFPS(TimeStamp, int offsetX, int offsetY, unsigned, Compositor* aCompositor);
+ void NotifyShadowTreeTransaction() {
+ mTransactionFps.AddFrame(TimeStamp::Now());
+ }
+
+ FPSCounter mCompositionFps;
+ FPSCounter mTransactionFps;
+
+private:
+ RefPtr<DataTextureSource> mFPSTextureSource;
+};
+
+} // 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 000000000..f8c73cc99
--- /dev/null
+++ b/gfx/layers/composite/FontData.h
@@ -0,0 +1,13 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// 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.
+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 };
+const unsigned int sTextureWidth = 256;
+const unsigned int sTextureHeight = 256;
+const unsigned int sCellWidth = 16;
+const unsigned int sCellHeight = 16;
diff --git a/gfx/layers/composite/FrameUniformityData.cpp b/gfx/layers/composite/FrameUniformityData.cpp
new file mode 100644
index 000000000..e8bab6adb
--- /dev/null
+++ b/gfx/layers/composite/FrameUniformityData.cpp
@@ -0,0 +1,152 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "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;
+}
+
+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 (auto iter = mFrameTransforms.begin(); iter != mFrameTransforms.end(); ++iter) {
+ uintptr_t layer = iter->first;
+ float uniformity = CalculateFrameUniformity(layer);
+
+ std::pair<uintptr_t,float> result(layer, uniformity);
+ aOutData->mUniformities.insert(result);
+ }
+
+ Reset();
+}
+
+LayerTransforms*
+LayerTransformRecorder::GetLayerTransforms(uintptr_t aLayer)
+{
+ if (!mFrameTransforms.count(aLayer)) {
+ LayerTransforms* newTransform = new LayerTransforms();
+ std::pair<uintptr_t, LayerTransforms*> newLayer(aLayer, newTransform);
+ mFrameTransforms.insert(newLayer);
+ }
+
+ return mFrameTransforms.find(aLayer)->second;
+}
+
+void
+LayerTransformRecorder::Reset()
+{
+ for (auto iter = mFrameTransforms.begin(); iter != mFrameTransforms.end(); ++iter) {
+ LayerTransforms* layerTransforms = iter->second;
+ delete layerTransforms;
+ }
+
+ mFrameTransforms.clear();
+}
+
+float
+LayerTransformRecorder::CalculateFrameUniformity(uintptr_t aLayer)
+{
+ LayerTransforms* layerTransform = GetLayerTransforms(aLayer);
+ float yUniformity = -1;
+ if (!layerTransform->mTransforms.IsEmpty()) {
+ 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 (auto iter = mUniformities.begin(); iter != mUniformities.end(); ++iter) {
+ uintptr_t layerAddr = iter->first;
+ float uniformity = iter->second;
+
+ // 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 000000000..3ff1bdc4a
--- /dev/null
+++ b/gfx/layers/composite/FrameUniformityData.h
@@ -0,0 +1,73 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_FrameUniformityData_h_
+#define mozilla_layers_FrameUniformityData_h_
+
+#include "ipc/IPCMessageUtils.h"
+#include "js/TypeDecls.h"
+#include "mozilla/RefPtr.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() {}
+
+ gfx::Point GetAverage();
+ gfx::Point GetStdDev();
+
+ // 60 fps * 5 seconds worth of data
+ AutoTArray<gfx::Point, 300> mTransforms;
+};
+
+class LayerTransformRecorder {
+public:
+ LayerTransformRecorder() {}
+ ~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);
+ std::map<uintptr_t,LayerTransforms*> 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 000000000..1e539d8ac
--- /dev/null
+++ b/gfx/layers/composite/GPUVideoTextureHost.cpp
@@ -0,0 +1,90 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "GPUVideoTextureHost.h"
+#include "mozilla/dom/VideoDecoderManagerParent.h"
+#include "ImageContainer.h"
+#include "mozilla/layers/VideoBridgeParent.h"
+
+namespace mozilla {
+namespace layers {
+
+GPUVideoTextureHost::GPUVideoTextureHost(TextureFlags aFlags,
+ const SurfaceDescriptorGPUVideo& aDescriptor)
+ : TextureHost(aFlags)
+{
+ MOZ_COUNT_CTOR(GPUVideoTextureHost);
+ mWrappedTextureHost = VideoBridgeParent::GetSingleton()->LookupTexture(aDescriptor.handle());
+}
+
+GPUVideoTextureHost::~GPUVideoTextureHost()
+{
+ MOZ_COUNT_DTOR(GPUVideoTextureHost);
+}
+
+bool
+GPUVideoTextureHost::Lock()
+{
+ if (!mWrappedTextureHost) {
+ return false;
+ }
+ return mWrappedTextureHost->Lock();
+}
+
+bool
+GPUVideoTextureHost::BindTextureSource(CompositableTextureSourceRef& aTexture)
+{
+ if (!mWrappedTextureHost) {
+ return false;
+ }
+ return mWrappedTextureHost->BindTextureSource(aTexture);
+}
+
+Compositor*
+GPUVideoTextureHost::GetCompositor()
+{
+ if (!mWrappedTextureHost) {
+ return nullptr;
+ }
+ return mWrappedTextureHost->GetCompositor();
+}
+
+void
+GPUVideoTextureHost::SetCompositor(Compositor* aCompositor)
+{
+ if (mWrappedTextureHost) {
+ mWrappedTextureHost->SetCompositor(aCompositor);
+ }
+}
+
+YUVColorSpace
+GPUVideoTextureHost::GetYUVColorSpace() const
+{
+ if (mWrappedTextureHost) {
+ return mWrappedTextureHost->GetYUVColorSpace();
+ }
+ return YUVColorSpace::UNKNOWN;
+}
+
+gfx::IntSize
+GPUVideoTextureHost::GetSize() const
+{
+ if (!mWrappedTextureHost) {
+ return gfx::IntSize();
+ }
+ return mWrappedTextureHost->GetSize();
+}
+
+gfx::SurfaceFormat
+GPUVideoTextureHost::GetFormat() const
+{
+ if (!mWrappedTextureHost) {
+ return gfx::SurfaceFormat::UNKNOWN;
+ }
+ return mWrappedTextureHost->GetFormat();
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/composite/GPUVideoTextureHost.h b/gfx/layers/composite/GPUVideoTextureHost.h
new file mode 100644
index 000000000..fd6bdc3fb
--- /dev/null
+++ b/gfx/layers/composite/GPUVideoTextureHost.h
@@ -0,0 +1,53 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_GPUVIDEOTEXTUREHOST_H
+#define MOZILLA_GFX_GPUVIDEOTEXTUREHOST_H
+
+#include "mozilla/layers/TextureHost.h"
+
+namespace mozilla {
+namespace layers {
+
+class GPUVideoTextureHost : public TextureHost
+{
+public:
+ GPUVideoTextureHost(TextureFlags aFlags,
+ const SurfaceDescriptorGPUVideo& aDescriptor);
+ virtual ~GPUVideoTextureHost();
+
+ virtual void DeallocateDeviceData() override {}
+
+ virtual void SetCompositor(Compositor* aCompositor) override;
+
+ virtual Compositor* GetCompositor() override;
+
+ virtual bool Lock() override;
+
+ virtual gfx::SurfaceFormat GetFormat() const override;
+
+ virtual bool BindTextureSource(CompositableTextureSourceRef& aTexture) override;
+
+ virtual already_AddRefed<gfx::DataSourceSurface> GetAsSurface() override
+ {
+ return nullptr; // XXX - implement this (for MOZ_DUMP_PAINTING)
+ }
+
+ virtual YUVColorSpace GetYUVColorSpace() const override;
+
+ virtual gfx::IntSize GetSize() const override;
+
+#ifdef MOZ_LAYERS_HAVE_LOG
+ virtual const char* Name() override { return "GPUVideoTextureHost"; }
+#endif
+
+protected:
+ RefPtr<TextureHost> mWrappedTextureHost;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // MOZILLA_GFX_GPUVIDEOTEXTUREHOST_H
diff --git a/gfx/layers/composite/ImageHost.cpp b/gfx/layers/composite/ImageHost.cpp
new file mode 100644
index 000000000..b1d77924b
--- /dev/null
+++ b/gfx/layers/composite/ImageHost.cpp
@@ -0,0 +1,739 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ImageHost.h"
+
+#include "LayersLogging.h" // for AppendToString
+#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/ImageContainerParent.h"
+#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
+
+#define BIAS_TIME_MS 1.0
+
+namespace mozilla {
+
+using namespace gfx;
+
+namespace layers {
+
+class ISurfaceAllocator;
+
+ImageHost::ImageHost(const TextureInfo& aTextureInfo)
+ : CompositableHost(aTextureInfo)
+ , mImageContainer(nullptr)
+ , mLastFrameID(-1)
+ , mLastProducerID(-1)
+ , mBias(BIAS_NONE)
+ , mLocked(false)
+{}
+
+ImageHost::~ImageHost()
+{
+ SetImageContainer(nullptr);
+}
+
+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();
+ }
+
+ mImages.SwapElements(newImages);
+ newImages.Clear();
+
+ // 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 (mImages.Length() == 1) {
+ SetCurrentTextureHost(mImages[0].mTextureHost);
+ }
+
+ // 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 (GetCompositor() && mLastFrameID >= 0) {
+ for (size_t i = 0; i < mImages.Length(); ++i) {
+ bool frameComesAfter = mImages[i].mFrameID > mLastFrameID ||
+ mImages[i].mProducerID != mLastProducerID;
+ if (frameComesAfter && !mImages[i].mTimeStamp.IsNull()) {
+ GetCompositor()->CompositeUntil(mImages[i].mTimeStamp +
+ TimeDuration::FromMilliseconds(BIAS_TIME_MS));
+ break;
+ }
+ }
+ }
+}
+
+void
+ImageHost::SetCurrentTextureHost(TextureHost* aTexture)
+{
+ if (aTexture == mCurrentTextureHost.get()) {
+ return;
+ }
+
+ bool swapTextureSources = !!mCurrentTextureHost && !!mCurrentTextureSource
+ && 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);
+
+ for (int32_t i = mImages.Length() - 1; i >= 0; --i) {
+ if (mImages[i].mTextureHost == aTexture) {
+ aTexture->UnbindTextureSource();
+ mImages.RemoveElementAt(i);
+ }
+ }
+}
+
+void
+ImageHost::UseOverlaySource(OverlaySource aOverlay,
+ const gfx::IntRect& aPictureRect)
+{
+ if (ImageHostOverlay::IsValid(aOverlay)) {
+ if (!mImageHostOverlay) {
+ mImageHostOverlay = new ImageHostOverlay();
+ }
+ mImageHostOverlay->UseOverlaySource(aOverlay, aPictureRect);
+ } else {
+ mImageHostOverlay = nullptr;
+ }
+}
+
+static TimeStamp
+GetBiasedTime(const TimeStamp& aInput, ImageHost::Bias aBias)
+{
+ switch (aBias) {
+ case ImageHost::BIAS_NEGATIVE:
+ return aInput - TimeDuration::FromMilliseconds(BIAS_TIME_MS);
+ case ImageHost::BIAS_POSITIVE:
+ return aInput + TimeDuration::FromMilliseconds(BIAS_TIME_MS);
+ default:
+ return aInput;
+ }
+}
+
+static ImageHost::Bias
+UpdateBias(const TimeStamp& aCompositionTime,
+ const TimeStamp& aCompositedImageTime,
+ const TimeStamp& aNextImageTime, // may be null
+ ImageHost::Bias aBias)
+{
+ if (aCompositedImageTime.IsNull()) {
+ return ImageHost::BIAS_NONE;
+ }
+ TimeDuration threshold = TimeDuration::FromMilliseconds(1.0);
+ if (aCompositionTime - aCompositedImageTime < threshold &&
+ aCompositionTime - aCompositedImageTime > -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.
+ return ImageHost::BIAS_NEGATIVE;
+ }
+ if (!aNextImageTime.IsNull() &&
+ aNextImageTime - aCompositionTime < threshold &&
+ aNextImageTime - aCompositionTime > -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.
+ // 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.
+ return ImageHost::BIAS_POSITIVE;
+ }
+ return ImageHost::BIAS_NONE;
+}
+
+int ImageHost::ChooseImageIndex() const
+{
+ if (!GetCompositor() || mImages.IsEmpty()) {
+ return -1;
+ }
+ TimeStamp now = GetCompositor()->GetCompositionTime();
+
+ if (now.IsNull()) {
+ // Not in a composition, so just return the last image we composited
+ // (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 -1;
+ }
+
+ uint32_t result = 0;
+ while (result + 1 < mImages.Length() &&
+ GetBiasedTime(mImages[result + 1].mTimeStamp, mBias) <= now) {
+ ++result;
+ }
+ return result;
+}
+
+const ImageHost::TimedImage* ImageHost::ChooseImage() const
+{
+ int index = ChooseImageIndex();
+ return index >= 0 ? &mImages[index] : nullptr;
+}
+
+ImageHost::TimedImage* ImageHost::ChooseImage()
+{
+ int index = ChooseImageIndex();
+ return index >= 0 ? &mImages[index] : nullptr;
+}
+
+TextureHost*
+ImageHost::GetAsTextureHost(IntRect* aPictureRect)
+{
+ TimedImage* img = ChooseImage();
+ if (img) {
+ SetCurrentTextureHost(img->mTextureHost);
+ }
+ if (aPictureRect && img) {
+ *aPictureRect = img->mPictureRect;
+ }
+ return img ? img->mTextureHost.get() : nullptr;
+}
+
+void ImageHost::Attach(Layer* aLayer,
+ Compositor* aCompositor,
+ AttachFlags aFlags)
+{
+ CompositableHost::Attach(aLayer, aCompositor, aFlags);
+ for (auto& img : mImages) {
+ if (GetCompositor()) {
+ img.mTextureHost->SetCompositor(GetCompositor());
+ }
+ img.mTextureHost->Updated();
+ }
+}
+
+void
+ImageHost::Composite(LayerComposite* aLayer,
+ EffectChain& aEffectChain,
+ float aOpacity,
+ const gfx::Matrix4x4& aTransform,
+ const gfx::SamplingFilter aSamplingFilter,
+ const gfx::IntRect& aClipRect,
+ const nsIntRegion* aVisibleRegion)
+{
+ if (!GetCompositor()) {
+ // should only happen when a tab is dragged to another window and
+ // async-video is still sending frames but we haven't attached the
+ // set the new compositor yet.
+ return;
+ }
+
+ if (mImageHostOverlay) {
+ mImageHostOverlay->Composite(GetCompositor(),
+ mFlashCounter,
+ aLayer,
+ aEffectChain,
+ aOpacity,
+ aTransform,
+ aSamplingFilter,
+ aClipRect,
+ aVisibleRegion);
+ mBias = BIAS_NONE;
+ return;
+ }
+
+ int imageIndex = ChooseImageIndex();
+ if (imageIndex < 0) {
+ return;
+ }
+
+ if (uint32_t(imageIndex) + 1 < mImages.Length()) {
+ GetCompositor()->CompositeUntil(mImages[imageIndex + 1].mTimeStamp + TimeDuration::FromMilliseconds(BIAS_TIME_MS));
+ }
+
+ TimedImage* img = &mImages[imageIndex];
+ img->mTextureHost->SetCompositor(GetCompositor());
+ SetCurrentTextureHost(img->mTextureHost);
+
+ {
+ 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,
+ GetRenderState());
+ if (!effect) {
+ return;
+ }
+
+ if (!GetCompositor()->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;
+ }
+
+ if (mLastFrameID != img->mFrameID || mLastProducerID != img->mProducerID) {
+ if (mImageContainer) {
+ aLayer->GetLayerManager()->
+ AppendImageCompositeNotification(ImageCompositeNotification(
+ mImageContainer, nullptr,
+ img->mTimeStamp, GetCompositor()->GetCompositionTime(),
+ img->mFrameID, img->mProducerID));
+ }
+ mLastFrameID = img->mFrameID;
+ mLastProducerID = img->mProducerID;
+ }
+ 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.y = effect->mTextureCoords.YMost();
+ effect->mTextureCoords.height = -effect->mTextureCoords.height;
+ }
+ GetCompositor()->DrawQuad(rect, aClipRect, aEffectChain,
+ aOpacity, aTransform);
+ GetCompositor()->DrawDiagnostics(diagnosticFlags | DiagnosticFlags::BIGIMAGE,
+ rect, aClipRect, aTransform, mFlashCounter);
+ } while (it->NextTile());
+ it->EndBigImageIteration();
+ // layer border
+ GetCompositor()->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.y = effect->mTextureCoords.YMost();
+ effect->mTextureCoords.height = -effect->mTextureCoords.height;
+ }
+
+ GetCompositor()->DrawQuad(pictureRect, aClipRect, aEffectChain,
+ aOpacity, aTransform);
+ GetCompositor()->DrawDiagnostics(diagnosticFlags,
+ pictureRect, aClipRect,
+ aTransform, mFlashCounter);
+ }
+ }
+
+ // Update mBias last. This can change which frame ChooseImage(Index) would
+ // return, and we don't want to do that until we've finished compositing
+ // since callers of ChooseImage(Index) assume the same image will be chosen
+ // during a given composition. This must happen after autoLock's
+ // destructor!
+ mBias = UpdateBias(
+ GetCompositor()->GetCompositionTime(), mImages[imageIndex].mTimeStamp,
+ uint32_t(imageIndex + 1) < mImages.Length() ?
+ mImages[imageIndex + 1].mTimeStamp : TimeStamp(),
+ mBias);
+}
+
+void
+ImageHost::SetCompositor(Compositor* aCompositor)
+{
+ if (mCompositor != aCompositor) {
+ for (auto& img : mImages) {
+ img.mTextureHost->SetCompositor(aCompositor);
+ }
+ }
+ if (mImageHostOverlay) {
+ mImageHostOverlay->SetCompositor(aCompositor);
+ }
+ CompositableHost::SetCompositor(aCompositor);
+}
+
+void
+ImageHost::PrintInfo(std::stringstream& aStream, const char* aPrefix)
+{
+ aStream << aPrefix;
+ aStream << nsPrintfCString("ImageHost (0x%p)", this).get();
+
+ nsAutoCString pfx(aPrefix);
+ pfx += " ";
+ for (auto& img : mImages) {
+ aStream << "\n";
+ img.mTextureHost->PrintInfo(aStream, pfx.get());
+ AppendToString(aStream, img.mPictureRect, " [picture-rect=", "]");
+ }
+
+ if (mImageHostOverlay) {
+ mImageHostOverlay->PrintInfo(aStream, aPrefix);
+ }
+}
+
+void
+ImageHost::Dump(std::stringstream& aStream,
+ const char* aPrefix,
+ bool aDumpHtml)
+{
+ for (auto& img : mImages) {
+ aStream << aPrefix;
+ aStream << (aDumpHtml ? "<ul><li>TextureHost: "
+ : "TextureHost: ");
+ DumpTextureHost(aStream, img.mTextureHost);
+ aStream << (aDumpHtml ? " </li></ul> " : " ");
+ }
+}
+
+LayerRenderState
+ImageHost::GetRenderState()
+{
+ if (mImageHostOverlay) {
+ return mImageHostOverlay->GetRenderState();
+ }
+
+ TimedImage* img = ChooseImage();
+ if (img) {
+ SetCurrentTextureHost(img->mTextureHost);
+ return img->mTextureHost->GetRenderState();
+ }
+ return LayerRenderState();
+}
+
+already_AddRefed<gfx::DataSourceSurface>
+ImageHost::GetAsSurface()
+{
+ if (mImageHostOverlay) {
+ return nullptr;
+ }
+
+ TimedImage* img = ChooseImage();
+ if (img) {
+ return img->mTextureHost->GetAsSurface();
+ }
+ return nullptr;
+}
+
+bool
+ImageHost::Lock()
+{
+ MOZ_ASSERT(!mLocked);
+ 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
+{
+ if (mImageHostOverlay) {
+ return mImageHostOverlay->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)
+{
+ 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,
+ GetRenderState());
+}
+
+void
+ImageHost::SetImageContainer(ImageContainerParent* aImageContainer)
+{
+ if (mImageContainer) {
+ mImageContainer->mImageHosts.RemoveElement(this);
+ }
+ mImageContainer = aImageContainer;
+ if (mImageContainer) {
+ mImageContainer->mImageHosts.AppendElement(this);
+ }
+}
+
+ImageHostOverlay::ImageHostOverlay()
+{
+ MOZ_COUNT_CTOR(ImageHostOverlay);
+}
+
+ImageHostOverlay::~ImageHostOverlay()
+{
+ if (mCompositor) {
+ mCompositor->RemoveImageHostOverlay(this);
+ }
+ MOZ_COUNT_DTOR(ImageHostOverlay);
+}
+
+/* static */ bool
+ImageHostOverlay::IsValid(OverlaySource aOverlay)
+{
+ if ((aOverlay.handle().type() == OverlayHandle::Tint32_t) &&
+ aOverlay.handle().get_int32_t() != INVALID_OVERLAY) {
+ return true;
+ } else if (aOverlay.handle().type() == OverlayHandle::TGonkNativeHandle) {
+ return true;
+ }
+ return false;
+}
+
+void
+ImageHostOverlay::SetCompositor(Compositor* aCompositor)
+{
+ if (mCompositor && (mCompositor != aCompositor)) {
+ mCompositor->RemoveImageHostOverlay(this);
+ }
+ if (aCompositor) {
+ aCompositor->AddImageHostOverlay(this);
+ }
+ mCompositor = aCompositor;
+}
+
+void
+ImageHostOverlay::Composite(Compositor* aCompositor,
+ uint32_t aFlashCounter,
+ LayerComposite* aLayer,
+ EffectChain& aEffectChain,
+ float aOpacity,
+ const gfx::Matrix4x4& aTransform,
+ const gfx::SamplingFilter aSamplingFilter,
+ const gfx::IntRect& aClipRect,
+ const nsIntRegion* aVisibleRegion)
+{
+ MOZ_ASSERT(mCompositor == aCompositor);
+
+ if (mOverlay.handle().type() == OverlayHandle::Tnull_t) {
+ return;
+ }
+
+ Color hollow(0.0f, 0.0f, 0.0f, 0.0f);
+ aEffectChain.mPrimaryEffect = new EffectSolidColor(hollow);
+ aEffectChain.mSecondaryEffects[EffectTypes::BLEND_MODE] = new EffectBlendMode(CompositionOp::OP_SOURCE);
+
+ gfx::Rect rect;
+ gfx::Rect clipRect(aClipRect.x, aClipRect.y,
+ aClipRect.width, aClipRect.height);
+ rect.SetRect(mPictureRect.x, mPictureRect.y,
+ mPictureRect.width, mPictureRect.height);
+
+ aCompositor->DrawQuad(rect, aClipRect, aEffectChain, aOpacity, aTransform);
+ aCompositor->DrawDiagnostics(DiagnosticFlags::IMAGE | DiagnosticFlags::BIGIMAGE,
+ rect, aClipRect, aTransform, aFlashCounter);
+}
+
+LayerRenderState
+ImageHostOverlay::GetRenderState()
+{
+ LayerRenderState state;
+ return state;
+}
+
+void
+ImageHostOverlay::UseOverlaySource(OverlaySource aOverlay,
+ const nsIntRect& aPictureRect)
+{
+ mOverlay = aOverlay;
+ mPictureRect = aPictureRect;
+}
+
+IntSize
+ImageHostOverlay::GetImageSize() const
+{
+ return IntSize(mPictureRect.width, mPictureRect.height);
+}
+
+void
+ImageHostOverlay::PrintInfo(std::stringstream& aStream, const char* aPrefix)
+{
+ aStream << aPrefix;
+ aStream << nsPrintfCString("ImageHostOverlay (0x%p)", this).get();
+
+ AppendToString(aStream, mPictureRect, " [picture-rect=", "]");
+
+ if (mOverlay.handle().type() == OverlayHandle::Tint32_t) {
+ nsAutoCString pfx(aPrefix);
+ pfx += " ";
+ aStream << nsPrintfCString("Overlay: %d", mOverlay.handle().get_int32_t()).get();
+ }
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/composite/ImageHost.h b/gfx/layers/composite/ImageHost.h
new file mode 100644
index 000000000..b8d23afee
--- /dev/null
+++ b/gfx/layers/composite/ImageHost.h
@@ -0,0 +1,201 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#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/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, 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 ImageContainerParent;
+class ImageHostOverlay;
+
+/**
+ * ImageHost. Works with ImageClientSingle and ImageClientBuffered
+ */
+class ImageHost : public CompositableHost
+{
+public:
+ explicit ImageHost(const TextureInfo& aTextureInfo);
+ ~ImageHost();
+
+ virtual CompositableType GetType() override { return mTextureInfo.mCompositableType; }
+
+ virtual void Composite(LayerComposite* aLayer,
+ EffectChain& aEffectChain,
+ float aOpacity,
+ const gfx::Matrix4x4& aTransform,
+ const gfx::SamplingFilter aSamplingFilter,
+ const gfx::IntRect& aClipRect,
+ const nsIntRegion* aVisibleRegion = nullptr) override;
+
+ virtual void UseTextureHost(const nsTArray<TimedTexture>& aTextures) override;
+
+ virtual void RemoveTextureHost(TextureHost* aTexture) override;
+
+ virtual void UseOverlaySource(OverlaySource aOverlay,
+ const gfx::IntRect& aPictureRect) override;
+
+ virtual TextureHost* GetAsTextureHost(gfx::IntRect* aPictureRect = nullptr) override;
+
+ virtual void Attach(Layer* aLayer,
+ Compositor* aCompositor,
+ AttachFlags aFlags = NO_FLAGS) override;
+
+ virtual void SetCompositor(Compositor* aCompositor) override;
+
+ virtual void SetImageContainer(ImageContainerParent* aImageContainer) override;
+
+ gfx::IntSize GetImageSize() const override;
+
+ virtual LayerRenderState GetRenderState() override;
+
+ virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix) override;
+
+ virtual void Dump(std::stringstream& aStream,
+ const char* aPrefix = "",
+ bool aDumpHtml = false) override;
+
+ virtual already_AddRefed<gfx::DataSourceSurface> GetAsSurface() override;
+
+ virtual bool Lock() override;
+
+ virtual void Unlock() override;
+
+ virtual already_AddRefed<TexturedEffect> GenEffect(const gfx::SamplingFilter aSamplingFilter) override;
+
+ void SetCurrentTextureHost(TextureHost* aTexture);
+
+ virtual void CleanupResources() override;
+
+ 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; }
+
+ 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,
+ };
+
+ bool IsOpaque();
+
+protected:
+ struct TimedImage {
+ CompositableTextureHostRef mTextureHost;
+ TimeStamp mTimeStamp;
+ gfx::IntRect mPictureRect;
+ int32_t mFrameID;
+ int32_t mProducerID;
+ };
+
+ // 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;
+
+ /**
+ * 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() const;
+ TimedImage* ChooseImage();
+ int ChooseImageIndex() const;
+
+ nsTArray<TimedImage> mImages;
+ // Weak reference, will be null if mImageContainer has been destroyed.
+ ImageContainerParent* mImageContainer;
+ int32_t mLastFrameID;
+ int32_t mLastProducerID;
+ /**
+ * Bias to apply to the next frame.
+ */
+ Bias mBias;
+
+ bool mLocked;
+
+ RefPtr<ImageHostOverlay> mImageHostOverlay;
+};
+
+/**
+ * ImageHostOverlay handles OverlaySource compositing
+ */
+class ImageHostOverlay {
+protected:
+ virtual ~ImageHostOverlay();
+
+public:
+ NS_INLINE_DECL_REFCOUNTING(ImageHostOverlay)
+ ImageHostOverlay();
+
+ static bool IsValid(OverlaySource aOverlay);
+
+ void SetCompositor(Compositor* aCompositor);
+
+ virtual void Composite(Compositor* aCompositor,
+ uint32_t aFlashCounter,
+ LayerComposite* aLayer,
+ EffectChain& aEffectChain,
+ float aOpacity,
+ const gfx::Matrix4x4& aTransform,
+ const gfx::SamplingFilter aSamplingFilter,
+ const gfx::IntRect& aClipRect,
+ const nsIntRegion* aVisibleRegion);
+ virtual LayerRenderState GetRenderState();
+ virtual void UseOverlaySource(OverlaySource aOverlay,
+ const gfx::IntRect& aPictureRect);
+ virtual gfx::IntSize GetImageSize() const;
+ virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix);
+protected:
+ RefPtr<Compositor> mCompositor;
+ gfx::IntRect mPictureRect;
+ OverlaySource mOverlay;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/composite/ImageLayerComposite.cpp b/gfx/layers/composite/ImageLayerComposite.cpp
new file mode 100644
index 000000000..bac9f3790
--- /dev/null
+++ b/gfx/layers/composite/ImageLayerComposite.cpp
@@ -0,0 +1,236 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "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/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:
+ mImageHost = static_cast<ImageHost*>(aHost);
+ return true;
+ default:
+ return false;
+ }
+}
+
+void
+ImageLayerComposite::Disconnect()
+{
+ Destroy();
+}
+
+LayerRenderState
+ImageLayerComposite::GetRenderState()
+{
+ if (mImageHost && mImageHost->IsAttached()) {
+ return mImageHost->GetRenderState();
+ }
+ return LayerRenderState();
+}
+
+Layer*
+ImageLayerComposite::GetLayer()
+{
+ return this;
+}
+
+void
+ImageLayerComposite::SetLayerManager(LayerManagerComposite* aManager)
+{
+ LayerComposite::SetLayerManager(aManager);
+ mManager = aManager;
+ if (mImageHost) {
+ mImageHost->SetCompositor(mCompositor);
+ }
+}
+
+void
+ImageLayerComposite::RenderLayer(const IntRect& aClipRect)
+{
+ 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->SetCompositor(mCompositor);
+ mImageHost->Composite(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.width != 0.0 && sourceRect.height != 0.0) {
+ 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;
+ }
+
+ if (mScaleMode == ScaleMode::STRETCH) {
+ 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 000000000..445917a75
--- /dev/null
+++ b/gfx/layers/composite/ImageLayerComposite.h
@@ -0,0 +1,79 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#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:
+ virtual LayerRenderState GetRenderState() override;
+
+ virtual void Disconnect() override;
+
+ virtual bool SetCompositableHost(CompositableHost* aHost) override;
+
+ virtual Layer* GetLayer() override;
+
+ virtual void SetLayerManager(LayerManagerComposite* aManager) override;
+
+ virtual void RenderLayer(const gfx::IntRect& aClipRect) override;
+
+ virtual void ComputeEffectiveTransforms(const mozilla::gfx::Matrix4x4& aTransformToSurface) override;
+
+ virtual void CleanupResources() override;
+
+ CompositableHost* GetCompositableHost() override;
+
+ virtual void GenEffectChain(EffectChain& aEffect) override;
+
+ virtual LayerComposite* AsLayerComposite() override { return this; }
+
+ virtual const char* Name() const override { return "ImageLayerComposite"; }
+
+ virtual bool IsOpaque() override;
+
+ virtual nsIntRegion GetFullyRenderedRegion() override;
+
+protected:
+ virtual 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 000000000..800478d55
--- /dev/null
+++ b/gfx/layers/composite/LayerManagerComposite.cpp
@@ -0,0 +1,1451 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "LayerManagerComposite.h"
+#include <stddef.h> // for size_t
+#include <stdint.h> // for uint16_t, uint32_t
+#include "CanvasLayerComposite.h" // for CanvasLayerComposite
+#include "ColorLayerComposite.h" // for ColorLayerComposite
+#include "Composer2D.h" // for Composer2D
+#include "CompositableHost.h" // for CompositableHost
+#include "ContainerLayerComposite.h" // for ContainerLayerComposite, etc
+#include "FPSCounter.h" // for FPSState, FPSCounter
+#include "FrameMetrics.h" // for FrameMetrics
+#include "GeckoProfiler.h" // for profiler_set_frame_number, etc
+#include "ImageLayerComposite.h" // for ImageLayerComposite
+#include "Layers.h" // for Layer, ContainerLayer, etc
+#include "LayerScope.h" // for LayerScope Tool
+#include "protobuf/LayerScopePacket.pb.h" // for protobuf (LayerScope)
+#include "PaintedLayerComposite.h" // for PaintedLayerComposite
+#include "TiledContentHost.h"
+#include "Units.h" // for ScreenIntRect
+#include "UnitTransforms.h" // for ViewAs
+#include "apz/src/AsyncPanZoomController.h" // for AsyncPanZoomController
+#include "gfxPrefs.h" // for gfxPrefs
+#ifdef XP_MACOSX
+#include "gfxPlatformMac.h"
+#endif
+#include "gfxRect.h" // for gfxRect
+#include "gfxUtils.h" // for frame color util
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/RefPtr.h" // for RefPtr, already_AddRefed
+#include "mozilla/gfx/2D.h" // for DrawTarget
+#include "mozilla/gfx/Matrix.h" // for Matrix4x4
+#include "mozilla/gfx/Point.h" // for IntSize, Point
+#include "mozilla/gfx/Rect.h" // for Rect
+#include "mozilla/gfx/Types.h" // for Color, SurfaceFormat
+#include "mozilla/layers/Compositor.h" // for Compositor
+#include "mozilla/layers/CompositorTypes.h"
+#include "mozilla/layers/Effects.h" // for Effect, EffectChain, etc
+#include "mozilla/layers/LayerMetricsWrapper.h" // for LayerMetricsWrapper
+#include "mozilla/layers/LayersTypes.h" // for etc
+#include "mozilla/widget/CompositorWidget.h" // for WidgetRenderingContext
+#include "ipc/CompositorBench.h" // for CompositorBench
+#include "ipc/ShadowLayerUtils.h"
+#include "mozilla/mozalloc.h" // for operator new, etc
+#include "nsAppRunner.h"
+#include "mozilla/RefPtr.h" // for nsRefPtr
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsDebug.h" // for NS_WARNING, NS_RUNTIMEABORT, etc
+#include "nsISupportsImpl.h" // for Layer::AddRef, etc
+#include "nsIWidget.h" // for nsIWidget
+#include "nsPoint.h" // for nsIntPoint
+#include "nsRect.h" // for mozilla::gfx::IntRect
+#include "nsRegion.h" // for nsIntRegion, etc
+#ifdef MOZ_WIDGET_ANDROID
+#include <android/log.h>
+#include <android/native_window.h>
+#endif
+#if defined(MOZ_WIDGET_ANDROID)
+#include "opengl/CompositorOGL.h"
+#include "GLContextEGL.h"
+#include "GLContextProvider.h"
+#include "ScopedGLHelpers.h"
+#endif
+#include "GeckoProfiler.h"
+#include "TextRenderer.h" // for TextRenderer
+#include "mozilla/layers/CompositorBridgeParent.h"
+#include "TreeTraversal.h" // for ForEachNode
+
+#ifdef USE_SKIA
+#include "PaintCounter.h" // For PaintCounter
+#endif
+
+class gfxContext;
+
+namespace mozilla {
+namespace layers {
+
+class ImageLayer;
+
+using namespace mozilla::gfx;
+using namespace mozilla::gl;
+
+static LayerComposite*
+ToLayerComposite(Layer* aLayer)
+{
+ return static_cast<LayerComposite*>(aLayer->ImplData());
+}
+
+static void ClearSubtree(Layer* aLayer)
+{
+ ForEachNode<ForwardIterator>(
+ aLayer,
+ [] (Layer* layer)
+ {
+ ToLayerComposite(layer)->CleanupResources();
+ });
+}
+
+void
+LayerManagerComposite::ClearCachedResources(Layer* aSubtree)
+{
+ MOZ_ASSERT(!aSubtree || aSubtree->Manager() == this);
+ Layer* subtree = aSubtree ? aSubtree : mRoot.get();
+ if (!subtree) {
+ return;
+ }
+
+ ClearSubtree(subtree);
+ // FIXME [bjacob]
+ // XXX the old LayerManagerOGL code had a mMaybeInvalidTree that it set to true here.
+ // Do we need that?
+}
+
+/**
+ * LayerManagerComposite
+ */
+LayerManagerComposite::LayerManagerComposite(Compositor* aCompositor)
+: mWarningLevel(0.0f)
+, mUnusedApzTransformWarning(false)
+, mDisabledApzWarning(false)
+, mCompositor(aCompositor)
+, mInTransaction(false)
+, mIsCompositorReady(false)
+, mDebugOverlayWantsNextFrame(false)
+, mGeometryChanged(true)
+, mLastFrameMissedHWC(false)
+, mWindowOverlayChanged(false)
+, mLastPaintTime(TimeDuration::Forever())
+, mRenderStartTime(TimeStamp::Now())
+{
+ mTextRenderer = new TextRenderer(aCompositor);
+ MOZ_ASSERT(aCompositor);
+
+#ifdef USE_SKIA
+ mPaintCounter = nullptr;
+#endif
+}
+
+LayerManagerComposite::~LayerManagerComposite()
+{
+ Destroy();
+}
+
+
+void
+LayerManagerComposite::Destroy()
+{
+ if (!mDestroyed) {
+ mCompositor->GetWidget()->CleanupWindowEffects();
+ if (mRoot) {
+ RootLayer()->Destroy();
+ }
+ mRoot = nullptr;
+ mClonedLayerTreeProperties = nullptr;
+ mDestroyed = true;
+
+#ifdef USE_SKIA
+ mPaintCounter = nullptr;
+#endif
+ }
+}
+
+void
+LayerManagerComposite::UpdateRenderBounds(const IntRect& aRect)
+{
+ mRenderBounds = aRect;
+}
+
+bool
+LayerManagerComposite::AreComponentAlphaLayersEnabled()
+{
+ return mCompositor->GetBackendType() != LayersBackend::LAYERS_BASIC &&
+ mCompositor->SupportsEffect(EffectTypes::COMPONENT_ALPHA) &&
+ LayerManager::AreComponentAlphaLayersEnabled();
+}
+
+bool
+LayerManagerComposite::BeginTransaction()
+{
+ mInTransaction = true;
+
+ if (!mCompositor->Ready()) {
+ return false;
+ }
+
+ mIsCompositorReady = true;
+ return true;
+}
+
+void
+LayerManagerComposite::BeginTransactionWithDrawTarget(DrawTarget* aTarget, const IntRect& aRect)
+{
+ mInTransaction = true;
+
+ if (!mCompositor->Ready()) {
+ return;
+ }
+
+#ifdef MOZ_LAYERS_HAVE_LOG
+ MOZ_LAYERS_LOG(("[----- BeginTransaction"));
+ Log();
+#endif
+
+ if (mDestroyed) {
+ NS_WARNING("Call on destroyed layer manager");
+ return;
+ }
+
+ mIsCompositorReady = true;
+ mCompositor->SetTargetContext(aTarget, aRect);
+ mTarget = aTarget;
+ mTargetBounds = aRect;
+}
+
+/**
+ * Get accumulated transform of from the context creating layer to the
+ * given layer.
+ */
+static Matrix4x4
+GetAccTransformIn3DContext(Layer* aLayer) {
+ Matrix4x4 transform = aLayer->GetLocalTransform();
+ for (Layer* layer = aLayer->GetParent();
+ layer && layer->Extend3DContext();
+ layer = layer->GetParent()) {
+ transform = transform * layer->GetLocalTransform();
+ }
+ return transform;
+}
+
+void
+LayerManagerComposite::PostProcessLayers(Layer* aLayer,
+ nsIntRegion& aOpaqueRegion,
+ LayerIntRegion& aVisibleRegion,
+ const Maybe<ParentLayerIntRect>& aClipFromAncestors)
+{
+ if (aLayer->Extend3DContext()) {
+ // For layers participating 3D rendering context, their visible
+ // region should be empty (invisible), so we pass through them
+ // without doing anything.
+
+ // Direct children of the establisher may have a clip, becaue the
+ // item containing it; ex. of nsHTMLScrollFrame, may give it one.
+ Maybe<ParentLayerIntRect> layerClip =
+ aLayer->AsLayerComposite()->GetShadowClipRect();
+ Maybe<ParentLayerIntRect> ancestorClipForChildren =
+ IntersectMaybeRects(layerClip, aClipFromAncestors);
+ MOZ_ASSERT(!layerClip || !aLayer->Combines3DTransformWithAncestors(),
+ "Only direct children of the establisher could have a clip");
+
+ for (Layer* child = aLayer->GetLastChild();
+ child;
+ child = child->GetPrevSibling()) {
+ PostProcessLayers(child, aOpaqueRegion, aVisibleRegion,
+ ancestorClipForChildren);
+ }
+ return;
+ }
+
+ nsIntRegion localOpaque;
+ // Treat layers on the path to the root of the 3D rendering context as
+ // a giant layer if it is a leaf.
+ Matrix4x4 transform = GetAccTransformIn3DContext(aLayer);
+ Matrix transform2d;
+ Maybe<IntPoint> integerTranslation;
+ // If aLayer has a simple transform (only an integer translation) then we
+ // can easily convert aOpaqueRegion into pre-transform coordinates and include
+ // that region.
+ if (transform.Is2D(&transform2d)) {
+ if (transform2d.IsIntegerTranslation()) {
+ integerTranslation = Some(IntPoint::Truncate(transform2d.GetTranslation()));
+ localOpaque = aOpaqueRegion;
+ localOpaque.MoveBy(-*integerTranslation);
+ }
+ }
+
+ // Compute a clip that's the combination of our layer clip with the clip
+ // from our ancestors.
+ LayerComposite* composite = aLayer->AsLayerComposite();
+ Maybe<ParentLayerIntRect> layerClip = composite->GetShadowClipRect();
+ MOZ_ASSERT(!layerClip || !aLayer->Combines3DTransformWithAncestors(),
+ "The layer with a clip should not participate "
+ "a 3D rendering context");
+ Maybe<ParentLayerIntRect> outsideClip =
+ IntersectMaybeRects(layerClip, aClipFromAncestors);
+
+ // Convert the combined clip into our pre-transform coordinate space, so
+ // that it can later be intersected with our visible region.
+ // If our transform is a perspective, there's no meaningful insideClip rect
+ // we can compute (it would need to be a cone).
+ Maybe<LayerIntRect> insideClip;
+ if (outsideClip && !transform.HasPerspectiveComponent()) {
+ Matrix4x4 inverse = transform;
+ if (inverse.Invert()) {
+ Maybe<LayerRect> insideClipFloat =
+ UntransformBy(ViewAs<ParentLayerToLayerMatrix4x4>(inverse),
+ ParentLayerRect(*outsideClip),
+ LayerRect::MaxIntRect());
+ if (insideClipFloat) {
+ insideClipFloat->RoundOut();
+ LayerIntRect insideClipInt;
+ if (insideClipFloat->ToIntRect(&insideClipInt)) {
+ insideClip = Some(insideClipInt);
+ }
+ }
+ }
+ }
+
+ Maybe<ParentLayerIntRect> ancestorClipForChildren;
+ if (insideClip) {
+ ancestorClipForChildren =
+ Some(ViewAs<ParentLayerPixel>(*insideClip, PixelCastJustification::MovingDownToChildren));
+ }
+
+ // Save the value of localOpaque, which currently stores the region obscured
+ // by siblings (and uncles and such), before our descendants contribute to it.
+ nsIntRegion obscured = localOpaque;
+
+ // Recurse on our descendants, in front-to-back order. In this process:
+ // - Occlusions are computed for them, and they contribute to localOpaque.
+ // - They recalculate their visible regions, taking ancestorClipForChildren
+ // into account, and accumulate them into descendantsVisibleRegion.
+ LayerIntRegion descendantsVisibleRegion;
+ bool hasPreserve3DChild = false;
+ for (Layer* child = aLayer->GetLastChild(); child; child = child->GetPrevSibling()) {
+ PostProcessLayers(child, localOpaque, descendantsVisibleRegion, ancestorClipForChildren);
+ if (child->Extend3DContext()) {
+ hasPreserve3DChild = true;
+ }
+ }
+
+ // Recalculate our visible region.
+ LayerIntRegion visible = composite->GetShadowVisibleRegion();
+
+ // If we have descendants, throw away the visible region stored on this
+ // layer, and use the region accumulated by our descendants instead.
+ if (aLayer->GetFirstChild() && !hasPreserve3DChild) {
+ visible = descendantsVisibleRegion;
+ }
+
+ // Subtract any areas that we know to be opaque.
+ if (!obscured.IsEmpty()) {
+ visible.SubOut(LayerIntRegion::FromUnknownRegion(obscured));
+ }
+
+ // Clip the visible region using the combined clip.
+ if (insideClip) {
+ visible.AndWith(*insideClip);
+ }
+ composite->SetShadowVisibleRegion(visible);
+
+ // Transform the newly calculated visible region into our parent's space,
+ // apply our clip to it (if any), and accumulate it into |aVisibleRegion|
+ // for the caller to use.
+ ParentLayerIntRegion visibleParentSpace = TransformBy(
+ ViewAs<LayerToParentLayerMatrix4x4>(transform), visible);
+ if (const Maybe<ParentLayerIntRect>& clipRect = composite->GetShadowClipRect()) {
+ visibleParentSpace.AndWith(*clipRect);
+ }
+ aVisibleRegion.OrWith(ViewAs<LayerPixel>(visibleParentSpace,
+ PixelCastJustification::MovingDownToChildren));
+
+ // If we have a simple transform, then we can add our opaque area into
+ // aOpaqueRegion.
+ if (integerTranslation &&
+ !aLayer->HasMaskLayers() &&
+ aLayer->IsOpaqueForVisibility()) {
+ if (aLayer->IsOpaque()) {
+ localOpaque.OrWith(composite->GetFullyRenderedRegion());
+ }
+ localOpaque.MoveBy(*integerTranslation);
+ if (layerClip) {
+ localOpaque.AndWith(layerClip->ToUnknownRect());
+ }
+ aOpaqueRegion.OrWith(localOpaque);
+ }
+}
+
+void
+LayerManagerComposite::EndTransaction(const TimeStamp& aTimeStamp,
+ EndTransactionFlags aFlags)
+{
+ NS_ASSERTION(mInTransaction, "Didn't call BeginTransaction?");
+ NS_ASSERTION(!(aFlags & END_NO_COMPOSITE),
+ "Shouldn't get END_NO_COMPOSITE here");
+ mInTransaction = false;
+ mRenderStartTime = TimeStamp::Now();
+
+ if (!mIsCompositorReady) {
+ return;
+ }
+ mIsCompositorReady = false;
+
+#ifdef MOZ_LAYERS_HAVE_LOG
+ MOZ_LAYERS_LOG((" ----- (beginning paint)"));
+ Log();
+#endif
+
+ if (mDestroyed) {
+ NS_WARNING("Call on destroyed layer manager");
+ return;
+ }
+
+ // Set composition timestamp here because we need it in
+ // ComputeEffectiveTransforms (so the correct video frame size is picked) and
+ // also to compute invalid regions properly.
+ mCompositor->SetCompositionTime(aTimeStamp);
+
+ if (mRoot && !(aFlags & END_NO_IMMEDIATE_REDRAW)) {
+ MOZ_ASSERT(!aTimeStamp.IsNull());
+ UpdateAndRender();
+ mCompositor->FlushPendingNotifyNotUsed();
+ } else {
+ // Modified the layer tree.
+ mGeometryChanged = true;
+ }
+
+ mCompositor->ClearTargetContext();
+ mTarget = nullptr;
+
+#ifdef MOZ_LAYERS_HAVE_LOG
+ Log();
+ MOZ_LAYERS_LOG(("]----- EndTransaction"));
+#endif
+}
+
+void
+LayerManagerComposite::UpdateAndRender()
+{
+ nsIntRegion invalid;
+ bool didEffectiveTransforms = false;
+
+ nsIntRegion opaque;
+ LayerIntRegion visible;
+ PostProcessLayers(mRoot, opaque, visible, Nothing());
+
+ if (mClonedLayerTreeProperties) {
+ // Effective transforms are needed by ComputeDifferences().
+ mRoot->ComputeEffectiveTransforms(gfx::Matrix4x4());
+ didEffectiveTransforms = true;
+
+ // We need to compute layer tree differences even if we're not going to
+ // immediately use the resulting damage area, since ComputeDifferences
+ // is also responsible for invalidates intermediate surfaces in
+ // ContainerLayers.
+ nsIntRegion changed = mClonedLayerTreeProperties->ComputeDifferences(mRoot, nullptr, &mGeometryChanged);
+
+ if (mTarget) {
+ // Since we're composing to an external target, we're not going to use
+ // the damage region from layers changes - we want to composite
+ // everything in the target bounds. Instead we accumulate the layers
+ // damage region for the next window composite.
+ mInvalidRegion.Or(mInvalidRegion, changed);
+ } else {
+ invalid = Move(changed);
+ }
+ }
+
+ if (mTarget) {
+ invalid.Or(invalid, mTargetBounds);
+ } else {
+ // If we didn't have a previous layer tree, invalidate the entire render
+ // area.
+ if (!mClonedLayerTreeProperties) {
+ invalid.Or(invalid, mRenderBounds);
+ }
+
+ // Add any additional invalid rects from the window manager or previous
+ // damage computed during ComposeToTarget().
+ invalid.Or(invalid, mInvalidRegion);
+ mInvalidRegion.SetEmpty();
+ }
+
+ if (invalid.IsEmpty() && !mWindowOverlayChanged) {
+ // Composition requested, but nothing has changed. Don't do any work.
+ mClonedLayerTreeProperties = LayerProperties::CloneFrom(GetRoot());
+ return;
+ }
+
+ // We don't want our debug overlay to cause more frames to happen
+ // so we will invalidate after we've decided if something changed.
+ InvalidateDebugOverlay(invalid, mRenderBounds);
+
+ if (!didEffectiveTransforms) {
+ // The results of our drawing always go directly into a pixel buffer,
+ // so we don't need to pass any global transform here.
+ mRoot->ComputeEffectiveTransforms(gfx::Matrix4x4());
+ }
+
+ Render(invalid, opaque);
+#if defined(MOZ_WIDGET_ANDROID)
+ RenderToPresentationSurface();
+#endif
+ mGeometryChanged = false;
+ mWindowOverlayChanged = false;
+
+ // Update cached layer tree information.
+ mClonedLayerTreeProperties = LayerProperties::CloneFrom(GetRoot());
+}
+
+already_AddRefed<DrawTarget>
+LayerManagerComposite::CreateOptimalMaskDrawTarget(const IntSize &aSize)
+{
+ NS_RUNTIMEABORT("Should only be called on the drawing side");
+ return nullptr;
+}
+
+already_AddRefed<PaintedLayer>
+LayerManagerComposite::CreatePaintedLayer()
+{
+ MOZ_ASSERT(gIsGtest, "Unless you're testing the compositor using GTest,"
+ "this should only be called on the drawing side");
+ RefPtr<PaintedLayer> layer = new PaintedLayerComposite(this);
+ return layer.forget();
+}
+
+already_AddRefed<ContainerLayer>
+LayerManagerComposite::CreateContainerLayer()
+{
+ MOZ_ASSERT(gIsGtest, "Unless you're testing the compositor using GTest,"
+ "this should only be called on the drawing side");
+ RefPtr<ContainerLayer> layer = new ContainerLayerComposite(this);
+ return layer.forget();
+}
+
+already_AddRefed<ImageLayer>
+LayerManagerComposite::CreateImageLayer()
+{
+ NS_RUNTIMEABORT("Should only be called on the drawing side");
+ return nullptr;
+}
+
+already_AddRefed<ColorLayer>
+LayerManagerComposite::CreateColorLayer()
+{
+ MOZ_ASSERT(gIsGtest, "Unless you're testing the compositor using GTest,"
+ "this should only be called on the drawing side");
+ RefPtr<ColorLayer> layer = new ColorLayerComposite(this);
+ return layer.forget();
+}
+
+already_AddRefed<CanvasLayer>
+LayerManagerComposite::CreateCanvasLayer()
+{
+ NS_RUNTIMEABORT("Should only be called on the drawing side");
+ return nullptr;
+}
+
+LayerComposite*
+LayerManagerComposite::RootLayer() const
+{
+ if (mDestroyed) {
+ NS_WARNING("Call on destroyed layer manager");
+ return nullptr;
+ }
+
+ return ToLayerComposite(mRoot);
+}
+
+#ifdef MOZ_PROFILING
+// Only build the QR feature when profiling to avoid bloating
+// our data section.
+// This table was generated using qrencode and is a binary
+// encoding of the qrcodes 0-255.
+#include "qrcode_table.h"
+#endif
+
+void
+LayerManagerComposite::InvalidateDebugOverlay(nsIntRegion& aInvalidRegion, const IntRect& aBounds)
+{
+ bool drawFps = gfxPrefs::LayersDrawFPS();
+ bool drawFrameCounter = gfxPrefs::DrawFrameCounter();
+ bool drawFrameColorBars = gfxPrefs::CompositorDrawColorBars();
+
+ if (drawFps || drawFrameCounter) {
+ aInvalidRegion.Or(aInvalidRegion, nsIntRect(0, 0, 256, 256));
+ }
+ if (drawFrameColorBars) {
+ aInvalidRegion.Or(aInvalidRegion, nsIntRect(0, 0, 10, aBounds.height));
+ }
+
+#ifdef USE_SKIA
+ bool drawPaintTimes = gfxPrefs::AlwaysPaint();
+ if (drawPaintTimes) {
+ aInvalidRegion.Or(aInvalidRegion, nsIntRect(PaintCounter::GetPaintRect()));
+ }
+#endif
+}
+
+#ifdef USE_SKIA
+void
+LayerManagerComposite::DrawPaintTimes(Compositor* aCompositor)
+{
+ if (!mPaintCounter) {
+ mPaintCounter = new PaintCounter();
+ }
+
+ TimeDuration compositeTime = TimeStamp::Now() - mRenderStartTime;
+ mPaintCounter->Draw(aCompositor, mLastPaintTime, compositeTime);
+}
+#endif
+
+static uint16_t sFrameCount = 0;
+void
+LayerManagerComposite::RenderDebugOverlay(const IntRect& aBounds)
+{
+ bool drawFps = gfxPrefs::LayersDrawFPS();
+ bool drawFrameCounter = gfxPrefs::DrawFrameCounter();
+ bool drawFrameColorBars = gfxPrefs::CompositorDrawColorBars();
+
+ TimeStamp now = TimeStamp::Now();
+
+ if (drawFps) {
+ if (!mFPS) {
+ mFPS = MakeUnique<FPSState>();
+ }
+
+ float alpha = 1;
+#ifdef ANDROID
+ // Draw a translation delay warning overlay
+ int width;
+ int border;
+ if (!mWarnTime.IsNull() && (now - mWarnTime).ToMilliseconds() < kVisualWarningDuration) {
+ EffectChain effects;
+
+ // Black blorder
+ border = 4;
+ width = 6;
+ effects.mPrimaryEffect = new EffectSolidColor(gfx::Color(0, 0, 0, 1));
+ mCompositor->DrawQuad(gfx::Rect(border, border, aBounds.width - 2 * border, width),
+ aBounds, effects, alpha, gfx::Matrix4x4());
+ mCompositor->DrawQuad(gfx::Rect(border, aBounds.height - border - width, aBounds.width - 2 * border, width),
+ aBounds, effects, alpha, gfx::Matrix4x4());
+ mCompositor->DrawQuad(gfx::Rect(border, border + width, width, aBounds.height - 2 * border - width * 2),
+ aBounds, effects, alpha, gfx::Matrix4x4());
+ mCompositor->DrawQuad(gfx::Rect(aBounds.width - border - width, border + width, width, aBounds.height - 2 * border - 2 * width),
+ aBounds, effects, alpha, gfx::Matrix4x4());
+
+ // Content
+ border = 5;
+ width = 4;
+ effects.mPrimaryEffect = new EffectSolidColor(gfx::Color(1, 1.f - mWarningLevel, 0, 1));
+ mCompositor->DrawQuad(gfx::Rect(border, border, aBounds.width - 2 * border, width),
+ aBounds, effects, alpha, gfx::Matrix4x4());
+ mCompositor->DrawQuad(gfx::Rect(border, aBounds.height - border - width, aBounds.width - 2 * border, width),
+ aBounds, effects, alpha, gfx::Matrix4x4());
+ mCompositor->DrawQuad(gfx::Rect(border, border + width, width, aBounds.height - 2 * border - width * 2),
+ aBounds, effects, alpha, gfx::Matrix4x4());
+ mCompositor->DrawQuad(gfx::Rect(aBounds.width - border - width, border + width, width, aBounds.height - 2 * border - 2 * width),
+ aBounds, effects, alpha, gfx::Matrix4x4());
+ SetDebugOverlayWantsNextFrame(true);
+ }
+#endif
+
+ float fillRatio = mCompositor->GetFillRatio();
+ mFPS->DrawFPS(now, drawFrameColorBars ? 10 : 1, 2, unsigned(fillRatio), mCompositor);
+
+ if (mUnusedApzTransformWarning) {
+ // If we have an unused APZ transform on this composite, draw a 20x20 red box
+ // in the top-right corner
+ EffectChain effects;
+ effects.mPrimaryEffect = new EffectSolidColor(gfx::Color(1, 0, 0, 1));
+ mCompositor->DrawQuad(gfx::Rect(aBounds.width - 20, 0, 20, 20),
+ aBounds, effects, alpha, gfx::Matrix4x4());
+
+ mUnusedApzTransformWarning = false;
+ SetDebugOverlayWantsNextFrame(true);
+ }
+ if (mDisabledApzWarning) {
+ // If we have a disabled APZ on this composite, draw a 20x20 yellow box
+ // in the top-right corner, to the left of the unused-apz-transform
+ // warning box
+ EffectChain effects;
+ effects.mPrimaryEffect = new EffectSolidColor(gfx::Color(1, 1, 0, 1));
+ mCompositor->DrawQuad(gfx::Rect(aBounds.width - 40, 0, 20, 20),
+ aBounds, effects, alpha, gfx::Matrix4x4());
+
+ mDisabledApzWarning = false;
+ SetDebugOverlayWantsNextFrame(true);
+ }
+
+
+ // Each frame is invalidate by the previous frame for simplicity
+ } else {
+ mFPS = nullptr;
+ }
+
+ if (drawFrameColorBars) {
+ gfx::IntRect sideRect(0, 0, 10, aBounds.height);
+
+ EffectChain effects;
+ effects.mPrimaryEffect = new EffectSolidColor(gfxUtils::GetColorForFrameNumber(sFrameCount));
+ mCompositor->DrawQuad(Rect(sideRect),
+ sideRect,
+ effects,
+ 1.0,
+ gfx::Matrix4x4());
+
+ }
+
+#ifdef MOZ_PROFILING
+ if (drawFrameCounter) {
+ profiler_set_frame_number(sFrameCount);
+ const char* qr = sQRCodeTable[sFrameCount%256];
+
+ int size = 21;
+ int padding = 2;
+ float opacity = 1.0;
+ const uint16_t bitWidth = 5;
+ gfx::IntRect clip(0,0, bitWidth*640, bitWidth*640);
+
+ // Draw the white squares at once
+ gfx::Color bitColor(1.0, 1.0, 1.0, 1.0);
+ EffectChain effects;
+ effects.mPrimaryEffect = new EffectSolidColor(bitColor);
+ int totalSize = (size + padding * 2) * bitWidth;
+ mCompositor->DrawQuad(gfx::Rect(0, 0, totalSize, totalSize),
+ clip,
+ effects,
+ opacity,
+ gfx::Matrix4x4());
+
+ // Draw a black square for every bit set in qr[index]
+ effects.mPrimaryEffect = new EffectSolidColor(gfx::Color(0, 0, 0, 1.0));
+ for (int y = 0; y < size; y++) {
+ for (int x = 0; x < size; x++) {
+ // Select the right bit from the binary encoding
+ int currBit = 128 >> ((x + y * 21) % 8);
+ int i = (x + y * 21) / 8;
+ if (qr[i] & currBit) {
+ mCompositor->DrawQuad(gfx::Rect(bitWidth * (x + padding),
+ bitWidth * (y + padding),
+ bitWidth, bitWidth),
+ clip,
+ effects,
+ opacity,
+ gfx::Matrix4x4());
+ }
+ }
+ }
+
+ }
+#endif
+
+ if (drawFrameColorBars || drawFrameCounter) {
+ // We intentionally overflow at 2^16.
+ sFrameCount++;
+ }
+
+#ifdef USE_SKIA
+ bool drawPaintTimes = gfxPrefs::AlwaysPaint();
+ if (drawPaintTimes) {
+ DrawPaintTimes(mCompositor);
+ }
+#endif
+}
+
+RefPtr<CompositingRenderTarget>
+LayerManagerComposite::PushGroupForLayerEffects()
+{
+ // This is currently true, so just making sure that any new use of this
+ // method is flagged for investigation
+ MOZ_ASSERT(gfxPrefs::LayersEffectInvert() ||
+ gfxPrefs::LayersEffectGrayscale() ||
+ gfxPrefs::LayersEffectContrast() != 0.0);
+
+ RefPtr<CompositingRenderTarget> previousTarget = mCompositor->GetCurrentRenderTarget();
+ // make our render target the same size as the destination target
+ // so that we don't have to change size if the drawing area changes.
+ IntRect rect(previousTarget->GetOrigin(), previousTarget->GetSize());
+ // XXX: I'm not sure if this is true or not...
+ MOZ_ASSERT(rect.x == 0 && rect.y == 0);
+ if (!mTwoPassTmpTarget ||
+ mTwoPassTmpTarget->GetSize() != previousTarget->GetSize() ||
+ mTwoPassTmpTarget->GetOrigin() != previousTarget->GetOrigin()) {
+ mTwoPassTmpTarget = mCompositor->CreateRenderTarget(rect, INIT_MODE_NONE);
+ }
+ MOZ_ASSERT(mTwoPassTmpTarget);
+ mCompositor->SetRenderTarget(mTwoPassTmpTarget);
+ return previousTarget;
+}
+void
+LayerManagerComposite::PopGroupForLayerEffects(RefPtr<CompositingRenderTarget> aPreviousTarget,
+ IntRect aClipRect,
+ bool aGrayscaleEffect,
+ bool aInvertEffect,
+ float aContrastEffect)
+{
+ MOZ_ASSERT(mTwoPassTmpTarget);
+
+ // This is currently true, so just making sure that any new use of this
+ // method is flagged for investigation
+ MOZ_ASSERT(aInvertEffect || aGrayscaleEffect || aContrastEffect != 0.0);
+
+ mCompositor->SetRenderTarget(aPreviousTarget);
+
+ EffectChain effectChain(RootLayer());
+ Matrix5x4 effectMatrix;
+ if (aGrayscaleEffect) {
+ // R' = G' = B' = luminance
+ // R' = 0.2126*R + 0.7152*G + 0.0722*B
+ // G' = 0.2126*R + 0.7152*G + 0.0722*B
+ // B' = 0.2126*R + 0.7152*G + 0.0722*B
+ Matrix5x4 grayscaleMatrix(0.2126f, 0.2126f, 0.2126f, 0,
+ 0.7152f, 0.7152f, 0.7152f, 0,
+ 0.0722f, 0.0722f, 0.0722f, 0,
+ 0, 0, 0, 1,
+ 0, 0, 0, 0);
+ effectMatrix = grayscaleMatrix;
+ }
+
+ if (aInvertEffect) {
+ // R' = 1 - R
+ // G' = 1 - G
+ // B' = 1 - B
+ Matrix5x4 colorInvertMatrix(-1, 0, 0, 0,
+ 0, -1, 0, 0,
+ 0, 0, -1, 0,
+ 0, 0, 0, 1,
+ 1, 1, 1, 0);
+ effectMatrix = effectMatrix * colorInvertMatrix;
+ }
+
+ if (aContrastEffect != 0.0) {
+ // Multiplying with:
+ // R' = (1 + c) * (R - 0.5) + 0.5
+ // G' = (1 + c) * (G - 0.5) + 0.5
+ // B' = (1 + c) * (B - 0.5) + 0.5
+ float cP1 = aContrastEffect + 1;
+ float hc = 0.5*aContrastEffect;
+ Matrix5x4 contrastMatrix( cP1, 0, 0, 0,
+ 0, cP1, 0, 0,
+ 0, 0, cP1, 0,
+ 0, 0, 0, 1,
+ -hc, -hc, -hc, 0);
+ effectMatrix = effectMatrix * contrastMatrix;
+ }
+
+ effectChain.mPrimaryEffect = new EffectRenderTarget(mTwoPassTmpTarget);
+ effectChain.mSecondaryEffects[EffectTypes::COLOR_MATRIX] = new EffectColorMatrix(effectMatrix);
+
+ mCompositor->DrawQuad(Rect(Point(0, 0), Size(mTwoPassTmpTarget->GetSize())), aClipRect, effectChain, 1.,
+ Matrix4x4());
+}
+
+// Used to clear the 'mLayerComposited' flag at the beginning of each Render().
+static void
+ClearLayerFlags(Layer* aLayer) {
+ ForEachNode<ForwardIterator>(
+ aLayer,
+ [] (Layer* layer)
+ {
+ if (layer->AsLayerComposite()) {
+ layer->AsLayerComposite()->SetLayerComposited(false);
+ }
+ });
+}
+
+void
+LayerManagerComposite::Render(const nsIntRegion& aInvalidRegion, const nsIntRegion& aOpaqueRegion)
+{
+ PROFILER_LABEL("LayerManagerComposite", "Render",
+ js::ProfileEntry::Category::GRAPHICS);
+
+ if (mDestroyed || !mCompositor || mCompositor->IsDestroyed()) {
+ NS_WARNING("Call on destroyed layer manager");
+ return;
+ }
+
+ ClearLayerFlags(mRoot);
+
+ // At this time, it doesn't really matter if these preferences change
+ // during the execution of the function; we should be safe in all
+ // permutations. However, may as well just get the values onces and
+ // then use them, just in case the consistency becomes important in
+ // the future.
+ bool invertVal = gfxPrefs::LayersEffectInvert();
+ bool grayscaleVal = gfxPrefs::LayersEffectGrayscale();
+ float contrastVal = gfxPrefs::LayersEffectContrast();
+ bool haveLayerEffects = (invertVal || grayscaleVal || contrastVal != 0.0);
+
+ // Set LayerScope begin/end frame
+ LayerScopeAutoFrame frame(PR_Now());
+
+ // Dump to console
+ if (gfxPrefs::LayersDump()) {
+ this->Dump(/* aSorted= */true);
+ } else if (profiler_feature_active("layersdump")) {
+ std::stringstream ss;
+ Dump(ss);
+ profiler_log(ss.str().c_str());
+ }
+
+ // Dump to LayerScope Viewer
+ if (LayerScope::CheckSendable()) {
+ // Create a LayersPacket, dump Layers into it and transfer the
+ // packet('s ownership) to LayerScope.
+ auto packet = MakeUnique<layerscope::Packet>();
+ layerscope::LayersPacket* layersPacket = packet->mutable_layers();
+ this->Dump(layersPacket);
+ LayerScope::SendLayerDump(Move(packet));
+ }
+
+ /** Our more efficient but less powerful alter ego, if one is available. */
+ RefPtr<Composer2D> composer2D;
+ composer2D = mCompositor->GetWidget()->GetComposer2D();
+
+ // We can't use composert2D if we have layer effects
+ if (!mTarget && !haveLayerEffects &&
+ gfxPrefs::Composer2DCompositionEnabled() &&
+ composer2D && composer2D->HasHwc() && composer2D->TryRenderWithHwc(mRoot,
+ mCompositor->GetWidget()->RealWidget(),
+ mGeometryChanged,
+ mCompositor->HasImageHostOverlays()))
+ {
+ LayerScope::SetHWComposed();
+ if (mFPS) {
+ double fps = mFPS->mCompositionFps.AddFrameAndGetFps(TimeStamp::Now());
+ if (gfxPrefs::LayersDrawFPS()) {
+ printf_stderr("HWComposer: FPS is %g\n", fps);
+ }
+ }
+ mCompositor->EndFrameForExternalComposition(Matrix());
+ mLastFrameMissedHWC = false;
+ return;
+ } else if (!mTarget && !haveLayerEffects) {
+ mLastFrameMissedHWC = !!composer2D;
+ }
+
+ mozilla::widget::WidgetRenderingContext widgetContext;
+#if defined(XP_MACOSX)
+ widgetContext.mLayerManager = this;
+#elif defined(MOZ_WIDGET_ANDROID)
+ widgetContext.mCompositor = GetCompositor();
+#endif
+
+ {
+ PROFILER_LABEL("LayerManagerComposite", "PreRender",
+ js::ProfileEntry::Category::GRAPHICS);
+
+ if (!mCompositor->GetWidget()->PreRender(&widgetContext)) {
+ return;
+ }
+ }
+
+ ParentLayerIntRect clipRect;
+ IntRect bounds(mRenderBounds.x, mRenderBounds.y, mRenderBounds.width, mRenderBounds.height);
+ IntRect actualBounds;
+
+ CompositorBench(mCompositor, bounds);
+
+ MOZ_ASSERT(mRoot->GetOpacity() == 1);
+#if defined(MOZ_WIDGET_ANDROID)
+ LayerMetricsWrapper wrapper = GetRootContentLayer();
+ if (wrapper) {
+ mCompositor->SetClearColor(wrapper.Metadata().GetBackgroundColor());
+ } else {
+ mCompositor->SetClearColorToDefault();
+ }
+#endif
+ if (mRoot->GetClipRect()) {
+ clipRect = *mRoot->GetClipRect();
+ IntRect rect(clipRect.x, clipRect.y, clipRect.width, clipRect.height);
+ mCompositor->BeginFrame(aInvalidRegion, &rect, bounds, aOpaqueRegion, nullptr, &actualBounds);
+ } else {
+ gfx::IntRect rect;
+ mCompositor->BeginFrame(aInvalidRegion, nullptr, bounds, aOpaqueRegion, &rect, &actualBounds);
+ clipRect = ParentLayerIntRect(rect.x, rect.y, rect.width, rect.height);
+ }
+
+ if (actualBounds.IsEmpty()) {
+ mCompositor->GetWidget()->PostRender(&widgetContext);
+ return;
+ }
+
+ // Allow widget to render a custom background.
+ mCompositor->GetWidget()->DrawWindowUnderlay(
+ &widgetContext, LayoutDeviceIntRect::FromUnknownRect(actualBounds));
+
+ RefPtr<CompositingRenderTarget> previousTarget;
+ if (haveLayerEffects) {
+ previousTarget = PushGroupForLayerEffects();
+ } else {
+ mTwoPassTmpTarget = nullptr;
+ }
+
+ // Render our layers.
+ RootLayer()->Prepare(ViewAs<RenderTargetPixel>(clipRect, PixelCastJustification::RenderTargetIsParentLayerForRoot));
+ RootLayer()->RenderLayer(clipRect.ToUnknownRect());
+
+ if (!mRegionToClear.IsEmpty()) {
+ for (auto iter = mRegionToClear.RectIter(); !iter.Done(); iter.Next()) {
+ const IntRect& r = iter.Get();
+ mCompositor->ClearRect(Rect(r.x, r.y, r.width, r.height));
+ }
+ }
+
+ if (mTwoPassTmpTarget) {
+ MOZ_ASSERT(haveLayerEffects);
+ PopGroupForLayerEffects(previousTarget, clipRect.ToUnknownRect(),
+ grayscaleVal, invertVal, contrastVal);
+ }
+
+ // Allow widget to render a custom foreground.
+ mCompositor->GetWidget()->DrawWindowOverlay(
+ &widgetContext, LayoutDeviceIntRect::FromUnknownRect(actualBounds));
+
+ // Debugging
+ RenderDebugOverlay(actualBounds);
+
+ {
+ PROFILER_LABEL("LayerManagerComposite", "EndFrame",
+ js::ProfileEntry::Category::GRAPHICS);
+
+ mCompositor->EndFrame();
+
+ // Call after EndFrame()
+ mCompositor->SetDispAcquireFence(mRoot);
+ }
+
+ if (composer2D) {
+ composer2D->Render(mCompositor->GetWidget()->RealWidget());
+ }
+
+ mCompositor->GetWidget()->PostRender(&widgetContext);
+
+ RecordFrame();
+}
+
+#if defined(MOZ_WIDGET_ANDROID)
+class ScopedCompositorProjMatrix {
+public:
+ ScopedCompositorProjMatrix(CompositorOGL* aCompositor, const Matrix4x4& aProjMatrix):
+ mCompositor(aCompositor),
+ mOriginalProjMatrix(mCompositor->GetProjMatrix())
+ {
+ mCompositor->SetProjMatrix(aProjMatrix);
+ }
+
+ ~ScopedCompositorProjMatrix()
+ {
+ mCompositor->SetProjMatrix(mOriginalProjMatrix);
+ }
+private:
+ CompositorOGL* const mCompositor;
+ const Matrix4x4 mOriginalProjMatrix;
+};
+
+class ScopedCompostitorSurfaceSize {
+public:
+ ScopedCompostitorSurfaceSize(CompositorOGL* aCompositor, const gfx::IntSize& aSize) :
+ mCompositor(aCompositor),
+ mOriginalSize(mCompositor->GetDestinationSurfaceSize())
+ {
+ mCompositor->SetDestinationSurfaceSize(aSize);
+ }
+ ~ScopedCompostitorSurfaceSize()
+ {
+ mCompositor->SetDestinationSurfaceSize(mOriginalSize);
+ }
+private:
+ CompositorOGL* const mCompositor;
+ const gfx::IntSize mOriginalSize;
+};
+
+class ScopedCompositorRenderOffset {
+public:
+ ScopedCompositorRenderOffset(CompositorOGL* aCompositor, const ScreenPoint& aOffset) :
+ mCompositor(aCompositor),
+ mOriginalOffset(mCompositor->GetScreenRenderOffset())
+ {
+ mCompositor->SetScreenRenderOffset(aOffset);
+ }
+ ~ScopedCompositorRenderOffset()
+ {
+ mCompositor->SetScreenRenderOffset(mOriginalOffset);
+ }
+private:
+ CompositorOGL* const mCompositor;
+ const ScreenPoint mOriginalOffset;
+};
+
+class ScopedContextSurfaceOverride {
+public:
+ ScopedContextSurfaceOverride(GLContextEGL* aContext, void* aSurface) :
+ mContext(aContext)
+ {
+ MOZ_ASSERT(aSurface);
+ mContext->SetEGLSurfaceOverride(aSurface);
+ mContext->MakeCurrent(true);
+ }
+ ~ScopedContextSurfaceOverride()
+ {
+ mContext->SetEGLSurfaceOverride(EGL_NO_SURFACE);
+ mContext->MakeCurrent(true);
+ }
+private:
+ GLContextEGL* const mContext;
+};
+
+void
+LayerManagerComposite::RenderToPresentationSurface()
+{
+#ifdef MOZ_WIDGET_ANDROID
+ nsIWidget* const widget = mCompositor->GetWidget()->RealWidget();
+ auto window = static_cast<ANativeWindow*>(
+ widget->GetNativeData(NS_PRESENTATION_WINDOW));
+
+ if (!window) {
+ return;
+ }
+
+ EGLSurface surface = widget->GetNativeData(NS_PRESENTATION_SURFACE);
+
+ if (!surface) {
+ //create surface;
+ surface = GLContextProviderEGL::CreateEGLSurface(window);
+ if (!surface) {
+ return;
+ }
+
+ widget->SetNativeData(NS_PRESENTATION_SURFACE,
+ reinterpret_cast<uintptr_t>(surface));
+ }
+
+ CompositorOGL* compositor = mCompositor->AsCompositorOGL();
+ GLContext* gl = compositor->gl();
+ GLContextEGL* egl = GLContextEGL::Cast(gl);
+
+ if (!egl) {
+ return;
+ }
+
+ const IntSize windowSize(ANativeWindow_getWidth(window),
+ ANativeWindow_getHeight(window));
+
+#endif
+
+ if ((windowSize.width <= 0) || (windowSize.height <= 0)) {
+ return;
+ }
+
+ ScreenRotation rotation = compositor->GetScreenRotation();
+
+ const int actualWidth = windowSize.width;
+ const int actualHeight = windowSize.height;
+
+ const gfx::IntSize originalSize = compositor->GetDestinationSurfaceSize();
+ const nsIntRect originalRect = nsIntRect(0, 0, originalSize.width, originalSize.height);
+
+ int pageWidth = originalSize.width;
+ int pageHeight = originalSize.height;
+ if (rotation == ROTATION_90 || rotation == ROTATION_270) {
+ pageWidth = originalSize.height;
+ pageHeight = originalSize.width;
+ }
+
+ float scale = 1.0;
+
+ if ((pageWidth > actualWidth) || (pageHeight > actualHeight)) {
+ const float scaleWidth = (float)actualWidth / (float)pageWidth;
+ const float scaleHeight = (float)actualHeight / (float)pageHeight;
+ scale = scaleWidth <= scaleHeight ? scaleWidth : scaleHeight;
+ }
+
+ const gfx::IntSize actualSize(actualWidth, actualHeight);
+ ScopedCompostitorSurfaceSize overrideSurfaceSize(compositor, actualSize);
+
+ const ScreenPoint offset((actualWidth - (int)(scale * pageWidth)) / 2, 0);
+ ScopedContextSurfaceOverride overrideSurface(egl, surface);
+
+ Matrix viewMatrix = ComputeTransformForRotation(originalRect,
+ rotation);
+ viewMatrix.Invert(); // unrotate
+ viewMatrix.PostScale(scale, scale);
+ viewMatrix.PostTranslate(offset.x, offset.y);
+ Matrix4x4 matrix = Matrix4x4::From2D(viewMatrix);
+
+ mRoot->ComputeEffectiveTransforms(matrix);
+ nsIntRegion opaque;
+ LayerIntRegion visible;
+ PostProcessLayers(mRoot, opaque, visible, Nothing());
+
+ nsIntRegion invalid;
+ IntRect bounds = IntRect::Truncate(0, 0, scale * pageWidth, actualHeight);
+ IntRect rect, actualBounds;
+ MOZ_ASSERT(mRoot->GetOpacity() == 1);
+ mCompositor->BeginFrame(invalid, nullptr, bounds, nsIntRegion(), &rect, &actualBounds);
+
+ // The Java side of Fennec sets a scissor rect that accounts for
+ // chrome such as the URL bar. Override that so that the entire frame buffer
+ // is cleared.
+ ScopedScissorRect scissorRect(egl, 0, 0, actualWidth, actualHeight);
+ egl->fClearColor(0.0, 0.0, 0.0, 0.0);
+ egl->fClear(LOCAL_GL_COLOR_BUFFER_BIT);
+
+ const IntRect clipRect = IntRect::Truncate(0, 0, actualWidth, actualHeight);
+
+ RootLayer()->Prepare(RenderTargetIntRect::FromUnknownRect(clipRect));
+ RootLayer()->RenderLayer(clipRect);
+
+ mCompositor->EndFrame();
+}
+#endif
+
+already_AddRefed<PaintedLayerComposite>
+LayerManagerComposite::CreatePaintedLayerComposite()
+{
+ if (mDestroyed) {
+ NS_WARNING("Call on destroyed layer manager");
+ return nullptr;
+ }
+ return RefPtr<PaintedLayerComposite>(new PaintedLayerComposite(this)).forget();
+}
+
+already_AddRefed<ContainerLayerComposite>
+LayerManagerComposite::CreateContainerLayerComposite()
+{
+ if (mDestroyed) {
+ NS_WARNING("Call on destroyed layer manager");
+ return nullptr;
+ }
+ return RefPtr<ContainerLayerComposite>(new ContainerLayerComposite(this)).forget();
+}
+
+already_AddRefed<ImageLayerComposite>
+LayerManagerComposite::CreateImageLayerComposite()
+{
+ if (mDestroyed) {
+ NS_WARNING("Call on destroyed layer manager");
+ return nullptr;
+ }
+ return RefPtr<ImageLayerComposite>(new ImageLayerComposite(this)).forget();
+}
+
+already_AddRefed<ColorLayerComposite>
+LayerManagerComposite::CreateColorLayerComposite()
+{
+ if (LayerManagerComposite::mDestroyed) {
+ NS_WARNING("Call on destroyed layer manager");
+ return nullptr;
+ }
+ return RefPtr<ColorLayerComposite>(new ColorLayerComposite(this)).forget();
+}
+
+already_AddRefed<CanvasLayerComposite>
+LayerManagerComposite::CreateCanvasLayerComposite()
+{
+ if (LayerManagerComposite::mDestroyed) {
+ NS_WARNING("Call on destroyed layer manager");
+ return nullptr;
+ }
+ return RefPtr<CanvasLayerComposite>(new CanvasLayerComposite(this)).forget();
+}
+
+already_AddRefed<RefLayerComposite>
+LayerManagerComposite::CreateRefLayerComposite()
+{
+ if (LayerManagerComposite::mDestroyed) {
+ NS_WARNING("Call on destroyed layer manager");
+ return nullptr;
+ }
+ return RefPtr<RefLayerComposite>(new RefLayerComposite(this)).forget();
+}
+
+LayerManagerComposite::AutoAddMaskEffect::AutoAddMaskEffect(Layer* aMaskLayer,
+ EffectChain& aEffects)
+ : mCompositable(nullptr), mFailed(false)
+{
+ if (!aMaskLayer) {
+ return;
+ }
+
+ mCompositable = ToLayerComposite(aMaskLayer)->GetCompositableHost();
+ if (!mCompositable) {
+ NS_WARNING("Mask layer with no compositable host");
+ mFailed = true;
+ return;
+ }
+
+ if (!mCompositable->AddMaskEffect(aEffects, aMaskLayer->GetEffectiveTransform())) {
+ mCompositable = nullptr;
+ mFailed = true;
+ }
+}
+
+LayerManagerComposite::AutoAddMaskEffect::~AutoAddMaskEffect()
+{
+ if (!mCompositable) {
+ return;
+ }
+
+ mCompositable->RemoveMaskEffect();
+}
+
+void
+LayerManagerComposite::ChangeCompositor(Compositor* aNewCompositor)
+{
+ mCompositor = aNewCompositor;
+ mTextRenderer = new TextRenderer(aNewCompositor);
+ mTwoPassTmpTarget = nullptr;
+}
+
+LayerComposite::LayerComposite(LayerManagerComposite *aManager)
+ : mCompositeManager(aManager)
+ , mCompositor(aManager->GetCompositor())
+ , mShadowOpacity(1.0)
+ , mShadowTransformSetByAnimation(false)
+ , mShadowOpacitySetByAnimation(false)
+ , mDestroyed(false)
+ , mLayerComposited(false)
+{ }
+
+LayerComposite::~LayerComposite()
+{
+}
+
+void
+LayerComposite::Destroy()
+{
+ if (!mDestroyed) {
+ mDestroyed = true;
+ CleanupResources();
+ }
+}
+
+void
+LayerComposite::AddBlendModeEffect(EffectChain& aEffectChain)
+{
+ gfx::CompositionOp blendMode = GetLayer()->GetEffectiveMixBlendMode();
+ if (blendMode == gfx::CompositionOp::OP_OVER) {
+ return;
+ }
+
+ aEffectChain.mSecondaryEffects[EffectTypes::BLEND_MODE] = new EffectBlendMode(blendMode);
+ return;
+}
+
+bool
+LayerManagerComposite::CanUseCanvasLayerForSize(const IntSize &aSize)
+{
+ return mCompositor->CanUseCanvasLayerForSize(gfx::IntSize(aSize.width,
+ aSize.height));
+}
+
+void
+LayerManagerComposite::NotifyShadowTreeTransaction()
+{
+ if (mFPS) {
+ mFPS->NotifyShadowTreeTransaction();
+ }
+}
+
+void
+LayerComposite::SetLayerManager(LayerManagerComposite* aManager)
+{
+ mCompositeManager = aManager;
+ mCompositor = aManager->GetCompositor();
+}
+
+bool
+LayerManagerComposite::AsyncPanZoomEnabled() const
+{
+ if (CompositorBridgeParent* bridge = mCompositor->GetCompositorBridgeParent()) {
+ return bridge->AsyncPanZoomEnabled();
+ }
+ return false;
+}
+
+nsIntRegion
+LayerComposite::GetFullyRenderedRegion() {
+ if (TiledContentHost* tiled = GetCompositableHost() ? GetCompositableHost()->AsTiledContentHost()
+ : nullptr) {
+ nsIntRegion shadowVisibleRegion = GetShadowVisibleRegion().ToUnknownRegion();
+ // Discard the region which hasn't been drawn yet when doing
+ // progressive drawing. Note that if the shadow visible region
+ // shrunk the tiled valig region may not have discarded this yet.
+ shadowVisibleRegion.And(shadowVisibleRegion, tiled->GetValidRegion());
+ return shadowVisibleRegion;
+ } else {
+ return GetShadowVisibleRegion().ToUnknownRegion();
+ }
+}
+
+Matrix4x4
+LayerComposite::GetShadowTransform() {
+ Matrix4x4 transform = mShadowTransform;
+ Layer* layer = GetLayer();
+
+ transform.PostScale(layer->GetPostXScale(), layer->GetPostYScale(), 1.0f);
+ if (const ContainerLayer* c = layer->AsContainerLayer()) {
+ transform.PreScale(c->GetPreXScale(), c->GetPreYScale(), 1.0f);
+ }
+
+ return transform;
+}
+
+bool
+LayerComposite::HasStaleCompositor() const
+{
+ return mCompositeManager->GetCompositor() != mCompositor;
+}
+
+static bool
+LayerHasCheckerboardingAPZC(Layer* aLayer, Color* aOutColor)
+{
+ bool answer = false;
+ for (LayerMetricsWrapper i(aLayer, LayerMetricsWrapper::StartAt::BOTTOM); i; i = i.GetParent()) {
+ if (!i.Metrics().IsScrollable()) {
+ continue;
+ }
+ if (i.GetApzc() && i.GetApzc()->IsCurrentlyCheckerboarding()) {
+ if (aOutColor) {
+ *aOutColor = i.Metadata().GetBackgroundColor();
+ }
+ answer = true;
+ break;
+ }
+ break;
+ }
+ return answer;
+}
+
+bool
+LayerComposite::NeedToDrawCheckerboarding(gfx::Color* aOutCheckerboardingColor)
+{
+ return GetLayer()->Manager()->AsyncPanZoomEnabled() &&
+ (GetLayer()->GetContentFlags() & Layer::CONTENT_OPAQUE) &&
+ GetLayer()->IsOpaqueForVisibility() &&
+ LayerHasCheckerboardingAPZC(GetLayer(), aOutCheckerboardingColor);
+}
+
+#ifndef MOZ_HAVE_PLATFORM_SPECIFIC_LAYER_BUFFERS
+
+/*static*/ bool
+LayerManagerComposite::SupportsDirectTexturing()
+{
+ return false;
+}
+
+/*static*/ void
+LayerManagerComposite::PlatformSyncBeforeReplyUpdate()
+{
+}
+
+#endif // !defined(MOZ_HAVE_PLATFORM_SPECIFIC_LAYER_BUFFERS)
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/composite/LayerManagerComposite.h b/gfx/layers/composite/LayerManagerComposite.h
new file mode 100644
index 000000000..8fe7a6b34
--- /dev/null
+++ b/gfx/layers/composite/LayerManagerComposite.h
@@ -0,0 +1,687 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_LayerManagerComposite_H
+#define GFX_LayerManagerComposite_H
+
+#include <stdint.h> // for int32_t, uint32_t
+#include "GLDefs.h" // for GLenum
+#include "Layers.h"
+#include "Units.h" // for ParentLayerIntRect
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/RefPtr.h" // for RefPtr, already_AddRefed
+#include "mozilla/gfx/2D.h"
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "mozilla/gfx/Rect.h" // for Rect
+#include "mozilla/gfx/Types.h" // for SurfaceFormat
+#include "mozilla/layers/CompositorTypes.h"
+#include "mozilla/layers/Effects.h" // for EffectChain
+#include "mozilla/layers/LayersMessages.h"
+#include "mozilla/layers/LayersTypes.h" // for LayersBackend, etc
+#include "mozilla/Maybe.h" // for Maybe
+#include "mozilla/RefPtr.h"
+#include "mozilla/UniquePtr.h"
+#include "nsAString.h"
+#include "mozilla/RefPtr.h" // for nsRefPtr
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsDebug.h" // for NS_ASSERTION
+#include "nsISupportsImpl.h" // for Layer::AddRef, etc
+#include "nsRect.h" // for mozilla::gfx::IntRect
+#include "nsRegion.h" // for nsIntRegion
+#include "nscore.h" // for nsAString, etc
+#include "LayerTreeInvalidation.h"
+
+class gfxContext;
+
+#ifdef XP_WIN
+#include <windows.h>
+#endif
+
+namespace mozilla {
+namespace gfx {
+class DrawTarget;
+} // namespace gfx
+
+namespace layers {
+
+class CanvasLayerComposite;
+class ColorLayerComposite;
+class CompositableHost;
+class Compositor;
+class ContainerLayerComposite;
+struct EffectChain;
+class ImageLayer;
+class ImageLayerComposite;
+class LayerComposite;
+class RefLayerComposite;
+class PaintedLayerComposite;
+class TextRenderer;
+class CompositingRenderTarget;
+struct FPSState;
+class PaintCounter;
+
+static const int kVisualWarningDuration = 150; // ms
+
+class LayerManagerComposite final : public LayerManager
+{
+ typedef mozilla::gfx::DrawTarget DrawTarget;
+ typedef mozilla::gfx::IntSize IntSize;
+ typedef mozilla::gfx::SurfaceFormat SurfaceFormat;
+
+public:
+ explicit LayerManagerComposite(Compositor* aCompositor);
+ ~LayerManagerComposite();
+
+ virtual 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.
+ */
+ virtual LayerManagerComposite* AsLayerManagerComposite() override
+ {
+ return this;
+ }
+
+ void UpdateRenderBounds(const gfx::IntRect& aRect);
+
+ virtual bool BeginTransaction() override;
+ virtual bool BeginTransactionWithTarget(gfxContext* aTarget) override
+ {
+ MOZ_CRASH("GFX: Use BeginTransactionWithDrawTarget");
+ return false;
+ }
+ void BeginTransactionWithDrawTarget(gfx::DrawTarget* aTarget,
+ const gfx::IntRect& aRect);
+
+ virtual bool EndEmptyTransaction(EndTransactionFlags aFlags = END_DEFAULT) override
+ {
+ MOZ_CRASH("GFX: Use EndTransaction(aTimeStamp)");
+ return false;
+ }
+ virtual void EndTransaction(DrawPaintedLayerCallback aCallback,
+ void* aCallbackData,
+ EndTransactionFlags aFlags = END_DEFAULT) override
+ {
+ MOZ_CRASH("GFX: Use EndTransaction(aTimeStamp)");
+ }
+ void EndTransaction(const TimeStamp& aTimeStamp,
+ EndTransactionFlags aFlags = END_DEFAULT);
+
+ virtual void SetRoot(Layer* aLayer) override { mRoot = aLayer; }
+
+ // XXX[nrc]: never called, we should move this logic to ClientLayerManager
+ // (bug 946926).
+ virtual bool CanUseCanvasLayerForSize(const gfx::IntSize &aSize) override;
+
+ virtual int32_t GetMaxTextureSize() const override
+ {
+ MOZ_CRASH("GFX: Call on compositor, not LayerManagerComposite");
+ }
+
+ virtual void ClearCachedResources(Layer* aSubtree = nullptr) override;
+
+ virtual already_AddRefed<PaintedLayer> CreatePaintedLayer() override;
+ virtual already_AddRefed<ContainerLayer> CreateContainerLayer() override;
+ virtual already_AddRefed<ImageLayer> CreateImageLayer() override;
+ virtual already_AddRefed<ColorLayer> CreateColorLayer() override;
+ virtual already_AddRefed<CanvasLayer> CreateCanvasLayer() override;
+ already_AddRefed<PaintedLayerComposite> CreatePaintedLayerComposite();
+ already_AddRefed<ContainerLayerComposite> CreateContainerLayerComposite();
+ already_AddRefed<ImageLayerComposite> CreateImageLayerComposite();
+ already_AddRefed<ColorLayerComposite> CreateColorLayerComposite();
+ already_AddRefed<CanvasLayerComposite> CreateCanvasLayerComposite();
+ already_AddRefed<RefLayerComposite> CreateRefLayerComposite();
+
+ virtual LayersBackend GetBackendType() override
+ {
+ MOZ_CRASH("GFX: Shouldn't be called for composited layer manager");
+ }
+ virtual void GetBackendName(nsAString& name) override
+ {
+ MOZ_CRASH("GFX: Shouldn't be called for composited layer manager");
+ }
+
+ virtual bool AreComponentAlphaLayersEnabled() override;
+
+ virtual already_AddRefed<DrawTarget>
+ CreateOptimalMaskDrawTarget(const IntSize &aSize) override;
+
+ virtual const char* Name() const override { return ""; }
+
+ /**
+ * 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.
+ */
+ void PostProcessLayers(Layer* aLayer,
+ nsIntRegion& aOpaqueRegion,
+ LayerIntRegion& aVisibleRegion,
+ const Maybe<ParentLayerIntRect>& aClipFromAncestors);
+
+ /**
+ * 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)
+ {
+ mInvalidRegion.Or(mInvalidRegion, aRegion);
+ }
+
+ void ClearApproximatelyVisibleRegions(uint64_t aLayersId,
+ const Maybe<uint32_t>& aPresShellId)
+ {
+ for (auto iter = mVisibleRegions.Iter(); !iter.Done(); iter.Next()) {
+ if (iter.Key().mLayersId == aLayersId &&
+ (!aPresShellId || iter.Key().mPresShellId == *aPresShellId)) {
+ iter.Remove();
+ }
+ }
+ }
+
+ void UpdateApproximatelyVisibleRegion(const ScrollableLayerGuid& aGuid,
+ const CSSIntRegion& aRegion)
+ {
+ CSSIntRegion* regionForScrollFrame = mVisibleRegions.LookupOrAdd(aGuid);
+ MOZ_ASSERT(regionForScrollFrame);
+
+ *regionForScrollFrame = aRegion;
+ }
+
+ CSSIntRegion* GetApproximatelyVisibleRegion(const ScrollableLayerGuid& aGuid)
+ {
+ return mVisibleRegions.Get(aGuid);
+ }
+
+ Compositor* GetCompositor() const
+ {
+ return mCompositor;
+ }
+
+ // Called by CompositorBridgeParent when a new compositor has been created due
+ // to a device reset. The layer manager must clear any cached resources
+ // attached to the old compositor, and make a best effort at ignoring
+ // layer or texture updates against the old compositor.
+ void ChangeCompositor(Compositor* aNewCompositor);
+
+ /**
+ * LayerManagerComposite provides sophisticated debug overlays
+ * that can request a next frame.
+ */
+ bool DebugOverlayWantsNextFrame() { return mDebugOverlayWantsNextFrame; }
+ void SetDebugOverlayWantsNextFrame(bool aVal)
+ { mDebugOverlayWantsNextFrame = aVal; }
+
+ void NotifyShadowTreeTransaction();
+
+ TextRenderer* GetTextRenderer() { return mTextRenderer; }
+
+ /**
+ * 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 UnusedApzTransformWarning() {
+ mUnusedApzTransformWarning = true;
+ }
+ void DisabledApzWarning() {
+ mDisabledApzWarning = true;
+ }
+
+ bool LastFrameMissedHWC() { return mLastFrameMissedHWC; }
+
+ bool AsyncPanZoomEnabled() const override;
+
+ void AppendImageCompositeNotification(const ImageCompositeNotification& 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 (!mCompositor->GetTargetContext()) {
+ mImageCompositeNotifications.AppendElement(aNotification);
+ }
+ }
+ void ExtractImageCompositeNotifications(nsTArray<ImageCompositeNotification>* aNotifications)
+ {
+ aNotifications->AppendElements(Move(mImageCompositeNotifications));
+ }
+
+ // Indicate that we need to composite even if nothing in our layers has
+ // changed, so that the widget can draw something different in its window
+ // overlay.
+ void SetWindowOverlayChanged() { mWindowOverlayChanged = true; }
+
+ void ForcePresent() { mCompositor->ForcePresent(); }
+
+ void SetPaintTime(const TimeDuration& aPaintTime) { mLastPaintTime = aPaintTime; }
+
+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.
+ */
+ void Render(const nsIntRegion& aInvalidRegion, const nsIntRegion& aOpaqueRegion);
+#if defined(MOZ_WIDGET_ANDROID)
+ void RenderToPresentationSurface();
+#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);
+
+
+ RefPtr<CompositingRenderTarget> PushGroupForLayerEffects();
+ void PopGroupForLayerEffects(RefPtr<CompositingRenderTarget> aPreviousTarget,
+ gfx::IntRect aClipRect,
+ bool aGrayscaleEffect,
+ bool aInvertEffect,
+ float aContrastEffect);
+
+ void ChangeCompositorInternal(Compositor* aNewCompositor);
+
+ float mWarningLevel;
+ mozilla::TimeStamp mWarnTime;
+ bool mUnusedApzTransformWarning;
+ bool mDisabledApzWarning;
+ RefPtr<Compositor> mCompositor;
+ UniquePtr<LayerProperties> mClonedLayerTreeProperties;
+
+ nsTArray<ImageCompositeNotification> mImageCompositeNotifications;
+
+ /**
+ * Context target, nullptr when drawing directly to our swap chain.
+ */
+ RefPtr<gfx::DrawTarget> mTarget;
+ gfx::IntRect mTargetBounds;
+
+ nsIntRegion mInvalidRegion;
+
+ typedef nsClassHashtable<nsGenericHashKey<ScrollableLayerGuid>,
+ CSSIntRegion> VisibleRegions;
+ VisibleRegions mVisibleRegions;
+
+ UniquePtr<FPSState> mFPS;
+
+ bool mInTransaction;
+ bool mIsCompositorReady;
+ bool mDebugOverlayWantsNextFrame;
+
+ RefPtr<CompositingRenderTarget> mTwoPassTmpTarget;
+ RefPtr<TextRenderer> mTextRenderer;
+ bool mGeometryChanged;
+
+ // Testing property. If hardware composer is supported, this will return
+ // true if the last frame was deemed 'too complicated' to be rendered.
+ bool mLastFrameMissedHWC;
+
+ bool mWindowOverlayChanged;
+ TimeDuration mLastPaintTime;
+ TimeStamp mRenderStartTime;
+
+#ifdef USE_SKIA
+ /**
+ * Render paint and composite times above the frame.
+ */
+ void DrawPaintTimes(Compositor* aCompositor);
+ RefPtr<PaintCounter> mPaintCounter;
+#endif
+};
+
+/**
+ * Composite layers are for use with OMTC on the compositor thread only. There
+ * must be corresponding Basic 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:
+ explicit LayerComposite(LayerManagerComposite* aManager);
+
+ virtual ~LayerComposite();
+
+ virtual LayerComposite* GetFirstChildComposite()
+ {
+ return nullptr;
+ }
+
+ /* Do NOT call this from the generic LayerComposite destructor. Only from the
+ * concrete class destructor
+ */
+ virtual void Destroy();
+
+ virtual Layer* GetLayer() = 0;
+
+ virtual void SetLayerManager(LayerManagerComposite* aManager);
+
+ LayerManagerComposite* GetLayerManager() const { return mCompositeManager; }
+
+ /**
+ * 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) = 0;
+
+ 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;
+
+ virtual void CleanupResources() = 0;
+
+ virtual void DestroyFrontBuffer() { }
+
+ void AddBlendModeEffect(EffectChain& aEffectChain);
+
+ virtual void GenEffectChain(EffectChain& aEffect) { }
+
+ /**
+ * The following methods are
+ *
+ * CONSTRUCTION PHASE ONLY
+ *
+ * They are analogous to the Layer interface.
+ */
+ void SetShadowVisibleRegion(const LayerIntRegion& aRegion)
+ {
+ mShadowVisibleRegion = 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;
+ }
+
+ void SetLayerComposited(bool value)
+ {
+ mLayerComposited = value;
+ }
+
+ void SetClearRect(const gfx::IntRect& aRect)
+ {
+ mClearRect = aRect;
+ }
+
+ // These getters can be used anytime.
+ float GetShadowOpacity() { return mShadowOpacity; }
+ const Maybe<ParentLayerIntRect>& GetShadowClipRect() { return mShadowClipRect; }
+ const LayerIntRegion& GetShadowVisibleRegion() { return mShadowVisibleRegion; }
+ const gfx::Matrix4x4& GetShadowBaseTransform() { return mShadowTransform; }
+ gfx::Matrix4x4 GetShadowTransform();
+ bool GetShadowTransformSetByAnimation() { return mShadowTransformSetByAnimation; }
+ bool GetShadowOpacitySetByAnimation() { return mShadowOpacitySetByAnimation; }
+ 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();
+
+ /**
+ * Return true if a checkerboarding background color needs to be drawn
+ * for this layer.
+ */
+ bool NeedToDrawCheckerboarding(gfx::Color* aOutCheckerboardingColor = nullptr);
+
+protected:
+ gfx::Matrix4x4 mShadowTransform;
+ LayerIntRegion mShadowVisibleRegion;
+ Maybe<ParentLayerIntRect> mShadowClipRect;
+ LayerManagerComposite* mCompositeManager;
+ RefPtr<Compositor> mCompositor;
+ float mShadowOpacity;
+ bool mShadowTransformSetByAnimation;
+ bool mShadowOpacitySetByAnimation;
+ bool mDestroyed;
+ bool mLayerComposited;
+ gfx::IntRect mClearRect;
+};
+
+// 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->AsLayerComposite()->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);
+ aLayer->AsLayerComposite()->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().ToUnknownRegion().GetBounds());
+ 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).
+ aLayer->AsLayerComposite()->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_LayerManagerComposite_H */
diff --git a/gfx/layers/composite/PaintCounter.cpp b/gfx/layers/composite/PaintCounter.cpp
new file mode 100644
index 000000000..56e57aab4
--- /dev/null
+++ b/gfx/layers/composite/PaintCounter.cpp
@@ -0,0 +1,79 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "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 "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);
+ mStride = mSurface->Stride();
+
+ mCanvas.reset(
+ SkCanvas::NewRasterDirect(MakeSkiaImageInfo(mRect.Size(), mFormat),
+ mSurface->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.setTextSize(32);
+ paint.setColor(SkColorSetRGB(0, 255, 0));
+ paint.setAntiAlias(true);
+
+ mCanvas->clear(SK_ColorTRANSPARENT);
+ mCanvas->drawText(buffer, strlen(buffer), 10, 30, 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 000000000..b5296939f
--- /dev/null
+++ b/gfx/layers/composite/PaintCounter.h
@@ -0,0 +1,49 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_PaintCounter_h_
+#define mozilla_layers_PaintCounter_h_
+
+#include <map> // for std::map
+#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;
+ sk_sp<SkCanvas> mCanvas;
+ IntSize mSize;
+ int mStride;
+
+ RefPtr<DataSourceSurface> mSurface;
+ RefPtr<DataTextureSource> mTextureSource;
+ RefPtr<TexturedEffect> mTexturedEffect;
+ 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 000000000..b58f5d690
--- /dev/null
+++ b/gfx/layers/composite/PaintedLayerComposite.cpp
@@ -0,0 +1,194 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "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/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/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:
+ mBuffer = static_cast<ContentHost*>(aHost);
+ 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(LayerManagerComposite* aManager)
+{
+ LayerComposite::SetLayerManager(aManager);
+ mManager = aManager;
+ if (mBuffer && mCompositor) {
+ mBuffer->SetCompositor(mCompositor);
+ }
+}
+
+LayerRenderState
+PaintedLayerComposite::GetRenderState()
+{
+ if (!mBuffer || !mBuffer->IsAttached() || mDestroyed) {
+ return LayerRenderState();
+ }
+ return mBuffer->GetRenderState();
+}
+
+void
+PaintedLayerComposite::RenderLayer(const gfx::IntRect& aClipRect)
+{
+ if (!mBuffer || !mBuffer->IsAttached()) {
+ return;
+ }
+ PROFILER_LABEL("PaintedLayerComposite", "RenderLayer",
+ js::ProfileEntry::Category::GRAPHICS);
+
+ Compositor* compositor = mCompositeManager->GetCompositor();
+
+ MOZ_ASSERT(mBuffer->GetCompositor() == 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(this, effectChain,
+ GetEffectiveOpacity(),
+ GetEffectiveTransform(),
+ GetSamplingFilter(),
+ clipRect,
+ &visibleRegion);
+ });
+
+ 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;
+}
+
+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 000000000..45a89eccf
--- /dev/null
+++ b/gfx/layers/composite/PaintedLayerComposite.h
@@ -0,0 +1,94 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#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 "nsDebug.h" // for NS_RUNTIMEABORT
+#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:
+ virtual void Disconnect() override;
+
+ virtual LayerRenderState GetRenderState() override;
+
+ CompositableHost* GetCompositableHost() override;
+
+ virtual void Destroy() override;
+
+ virtual Layer* GetLayer() override;
+
+ virtual void SetLayerManager(LayerManagerComposite* aManager) override;
+
+ virtual void RenderLayer(const gfx::IntRect& aClipRect) override;
+
+ virtual void CleanupResources() override;
+
+ virtual void GenEffectChain(EffectChain& aEffect) override;
+
+ virtual bool SetCompositableHost(CompositableHost* aHost) override;
+
+ virtual LayerComposite* AsLayerComposite() override { return this; }
+
+ virtual void InvalidateRegion(const nsIntRegion& aRegion) override
+ {
+ NS_RUNTIMEABORT("PaintedLayerComposites can't fill invalidated regions");
+ }
+
+ void SetValidRegion(const nsIntRegion& aRegion)
+ {
+ MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) ValidRegion", this));
+ mValidRegion = aRegion;
+ Mutated();
+ }
+
+ const virtual 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 000000000..be59cb246
--- /dev/null
+++ b/gfx/layers/composite/TextRenderer.cpp
@@ -0,0 +1,173 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "TextRenderer.h"
+#include "FontData.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;
+using namespace std;
+
+const Float sBackgroundOpacity = 0.6f;
+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);
+
+ DataSourceSurface::MappedSurface map = static_cast<TextRenderer*>(png_get_progressive_ptr(png_ptr))->GetSurfaceMap();
+
+ uint32_t* dst = (uint32_t*)(map.mData + map.mStride * row_num);
+
+ for (uint32_t x = 0; x < sTextureWidth; 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);
+ Color pixelColor(baseValue, baseValue, baseValue, baseValue + alphaValue);
+ dst[x] = pixelColor.ToABGR();
+ }
+}
+
+TextRenderer::~TextRenderer()
+{
+ if (mGlyphBitmaps) {
+ mGlyphBitmaps->Unmap();
+ }
+}
+
+void
+TextRenderer::RenderText(const string& aText, const IntPoint& aOrigin,
+ const Matrix4x4& aTransform, uint32_t aTextSize,
+ uint32_t aTargetPixelWidth)
+{
+ EnsureInitialized();
+
+ // For now we only have a bitmap font with a 16px cell size, so we just
+ // scale it up if the user wants larger text.
+ Float scaleFactor = Float(aTextSize) / Float(sCellHeight);
+
+ aTargetPixelWidth /= scaleFactor;
+
+ 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 += sGlyphWidths[uint32_t(aText[i])];
+ maxWidth = std::max(lineWidth, maxWidth);
+ }
+
+ // Create a surface to draw our glyphs to.
+ RefPtr<DataSourceSurface> textSurf =
+ Factory::CreateDataSourceSurface(IntSize(maxWidth, numLines * sCellHeight), sTextureFormat);
+ if (NS_WARN_IF(!textSurf)) {
+ return;
+ }
+
+ DataSourceSurface::MappedSurface map;
+ if (NS_WARN_IF(!textSurf->Map(DataSourceSurface::MapType::READ_WRITE, &map))) {
+ return;
+ }
+
+ // Initialize the surface to transparent white.
+ memset(map.mData, uint8_t(sBackgroundOpacity * 255.0f),
+ numLines * sCellHeight * map.mStride);
+
+ uint32_t currentXPos = 0;
+ uint32_t currentYPos = 0;
+
+ // Copy our glyphs onto the surface.
+ for (uint32_t i = 0; i < aText.length(); i++) {
+ if (aText[i] == '\n' || (aText[i] == ' ' && currentXPos > aTargetPixelWidth)) {
+ currentYPos += sCellHeight;
+ currentXPos = 0;
+ continue;
+ }
+
+ uint32_t glyphXOffset = aText[i] % (sTextureWidth / sCellWidth) * sCellWidth * BytesPerPixel(sTextureFormat);
+ uint32_t truncatedLine = aText[i] / (sTextureWidth / sCellWidth);
+ uint32_t glyphYOffset = truncatedLine * sCellHeight * mMap.mStride;
+
+ for (int y = 0; y < 16; y++) {
+ memcpy(map.mData + (y + currentYPos) * map.mStride + currentXPos * BytesPerPixel(sTextureFormat),
+ mMap.mData + glyphYOffset + y * mMap.mStride + glyphXOffset,
+ sGlyphWidths[uint32_t(aText[i])] * BytesPerPixel(sTextureFormat));
+ }
+
+ currentXPos += sGlyphWidths[uint32_t(aText[i])];
+ }
+
+ textSurf->Unmap();
+
+ RefPtr<DataTextureSource> src = mCompositor->CreateDataTextureSource();
+
+ if (!src->Update(textSurf)) {
+ // Upload failed.
+ return;
+ }
+
+ RefPtr<EffectRGB> effect = new EffectRGB(src, true, SamplingFilter::LINEAR);
+ EffectChain chain;
+ chain.mPrimaryEffect = effect;
+
+ Matrix4x4 transform = aTransform;
+ transform.PreScale(scaleFactor, scaleFactor, 1.0f);
+ mCompositor->DrawQuad(Rect(aOrigin.x, aOrigin.y, maxWidth, numLines * 16),
+ IntRect(-10000, -10000, 20000, 20000), chain, 1.0f, transform);
+}
+
+void
+TextRenderer::EnsureInitialized()
+{
+ if (mGlyphBitmaps) {
+ return;
+ }
+
+ mGlyphBitmaps = Factory::CreateDataSourceSurface(IntSize(sTextureWidth, sTextureHeight), sTextureFormat);
+ if (NS_WARN_IF(!mGlyphBitmaps)) {
+ return;
+ }
+
+ if (NS_WARN_IF(!mGlyphBitmaps->Map(DataSourceSurface::MapType::READ_WRITE, &mMap))) {
+ return;
+ }
+
+ 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, this, 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*)sFontPNG, sizeof(sFontPNG));
+
+ png_destroy_read_struct(&png_ptr, &info_ptr, nullptr);
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/composite/TextRenderer.h b/gfx/layers/composite/TextRenderer.h
new file mode 100644
index 000000000..7665558eb
--- /dev/null
+++ b/gfx/layers/composite/TextRenderer.h
@@ -0,0 +1,50 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_TextRenderer_H
+#define GFX_TextRenderer_H
+
+#include "mozilla/gfx/2D.h"
+#include "nsISupportsImpl.h"
+#include <string>
+
+namespace mozilla {
+namespace layers {
+
+class Compositor;
+
+class TextRenderer
+{
+ ~TextRenderer();
+
+public:
+ NS_INLINE_DECL_REFCOUNTING(TextRenderer)
+
+ explicit TextRenderer(Compositor *aCompositor)
+ : mCompositor(aCompositor), mMap({nullptr, 0})
+ {
+ }
+
+ void RenderText(const std::string& aText, const gfx::IntPoint& aOrigin,
+ const gfx::Matrix4x4& aTransform, uint32_t aTextSize,
+ uint32_t aTargetPixelWidth);
+
+ gfx::DataSourceSurface::MappedSurface& GetSurfaceMap() { return mMap; }
+
+private:
+
+ // Note that this may still fail to set mGlyphBitmaps to a valid value
+ // if the underlying CreateDataSourceSurface fails for some reason.
+ void EnsureInitialized();
+
+ RefPtr<Compositor> mCompositor;
+ RefPtr<gfx::DataSourceSurface> mGlyphBitmaps;
+ gfx::DataSourceSurface::MappedSurface mMap;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/composite/TextureHost.cpp b/gfx/layers/composite/TextureHost.cpp
new file mode 100644
index 000000000..8c5b8c7b7
--- /dev/null
+++ b/gfx/layers/composite/TextureHost.cpp
@@ -0,0 +1,1142 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "TextureHost.h"
+
+#include "CompositableHost.h" // for CompositableHost
+#include "LayerScope.h"
+#include "LayersLogging.h" // for AppendToString
+#include "mozilla/gfx/2D.h" // for DataSourceSurface, Factory
+#include "mozilla/ipc/Shmem.h" // for Shmem
+#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/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"
+#include "mozilla/layers/GPUVideoTextureHost.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 "gfxPrefs.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/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:
+ explicit TextureParent(HostIPCAllocator* aAllocator, uint64_t aSerial);
+
+ ~TextureParent();
+
+ bool Init(const SurfaceDescriptor& aSharedData,
+ const LayersBackend& aLayersBackend,
+ const TextureFlags& aFlags);
+
+ void NotifyNotUsed(uint64_t aTransactionId);
+
+ virtual bool RecvRecycleTexture(const TextureFlags& aTextureFlags) override;
+
+ TextureHost* GetTextureHost() { return mTextureHost; }
+
+ virtual void Destroy() override;
+
+ uint64_t GetSerial() const { return mSerial; }
+
+ virtual bool RecvDestroySync() override {
+ DestroyIfNeeded();
+ return true;
+ }
+
+ HostIPCAllocator* mSurfaceAllocator;
+ RefPtr<TextureHost> mTextureHost;
+ // mSerial is unique in TextureClient's process.
+ const uint64_t mSerial;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+PTextureParent*
+TextureHost::CreateIPDLActor(HostIPCAllocator* aAllocator,
+ const SurfaceDescriptor& aSharedData,
+ LayersBackend aLayersBackend,
+ TextureFlags aFlags,
+ uint64_t aSerial)
+{
+ if (aSharedData.type() == SurfaceDescriptor::TSurfaceDescriptorBuffer &&
+ aSharedData.get_SurfaceDescriptorBuffer().data().type() == MemoryOrShmem::Tuintptr_t &&
+ !aAllocator->IsSameProcess())
+ {
+ NS_ERROR("A client process is trying to peek at our address space using a MemoryTexture!");
+ return nullptr;
+ }
+ TextureParent* actor = new TextureParent(aAllocator, aSerial);
+ if (!actor->Init(aSharedData, aLayersBackend, aFlags)) {
+ 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;
+}
+
+// implemented in TextureHostOGL.cpp
+already_AddRefed<TextureHost> CreateTextureHostOGL(const SurfaceDescriptor& aDesc,
+ ISurfaceAllocator* aDeallocator,
+ TextureFlags aFlags);
+
+// implemented in TextureHostBasic.cpp
+already_AddRefed<TextureHost> CreateTextureHostBasic(const SurfaceDescriptor& aDesc,
+ ISurfaceAllocator* aDeallocator,
+ TextureFlags aFlags);
+
+// implemented in TextureD3D11.cpp
+already_AddRefed<TextureHost> CreateTextureHostD3D11(const SurfaceDescriptor& aDesc,
+ ISurfaceAllocator* aDeallocator,
+ TextureFlags aFlags);
+
+// implemented in TextureD3D9.cpp
+already_AddRefed<TextureHost> CreateTextureHostD3D9(const SurfaceDescriptor& aDesc,
+ ISurfaceAllocator* aDeallocator,
+ TextureFlags aFlags);
+
+already_AddRefed<TextureHost>
+TextureHost::Create(const SurfaceDescriptor& aDesc,
+ ISurfaceAllocator* aDeallocator,
+ LayersBackend aBackend,
+ TextureFlags aFlags)
+{
+ switch (aDesc.type()) {
+ case SurfaceDescriptor::TSurfaceDescriptorBuffer:
+ case SurfaceDescriptor::TSurfaceDescriptorDIB:
+ case SurfaceDescriptor::TSurfaceDescriptorFileMapping:
+ case SurfaceDescriptor::TSurfaceDescriptorGPUVideo:
+ return CreateBackendIndependentTextureHost(aDesc, aDeallocator, aFlags);
+
+ case SurfaceDescriptor::TEGLImageDescriptor:
+ case SurfaceDescriptor::TSurfaceTextureDescriptor:
+ case SurfaceDescriptor::TSurfaceDescriptorSharedGLTexture:
+ return CreateTextureHostOGL(aDesc, aDeallocator, aFlags);
+
+ case SurfaceDescriptor::TSurfaceDescriptorMacIOSurface:
+ if (aBackend == LayersBackend::LAYERS_OPENGL) {
+ return CreateTextureHostOGL(aDesc, aDeallocator, aFlags);
+ } else {
+ return CreateTextureHostBasic(aDesc, aDeallocator, aFlags);
+ }
+
+#ifdef MOZ_X11
+ case SurfaceDescriptor::TSurfaceDescriptorX11: {
+ const SurfaceDescriptorX11& desc = aDesc.get_SurfaceDescriptorX11();
+ return MakeAndAddRef<X11TextureHost>(aFlags, desc);
+ }
+#endif
+
+#ifdef XP_WIN
+ case SurfaceDescriptor::TSurfaceDescriptorD3D9:
+ return CreateTextureHostD3D9(aDesc, aDeallocator, aFlags);
+
+ case SurfaceDescriptor::TSurfaceDescriptorD3D10:
+ case SurfaceDescriptor::TSurfaceDescriptorDXGIYCbCr:
+ if (aBackend == LayersBackend::LAYERS_D3D9) {
+ return CreateTextureHostD3D9(aDesc, aDeallocator, aFlags);
+ } else {
+ return CreateTextureHostD3D11(aDesc, aDeallocator, aFlags);
+ }
+#endif
+ default:
+ MOZ_CRASH("GFX: Unsupported Surface type host");
+ }
+}
+
+already_AddRefed<TextureHost>
+CreateBackendIndependentTextureHost(const SurfaceDescriptor& aDesc,
+ ISurfaceAllocator* aDeallocator,
+ 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: {
+ result = new ShmemTextureHost(data.get_Shmem(),
+ bufferDesc.desc(),
+ aDeallocator,
+ aFlags);
+ break;
+ }
+ case MemoryOrShmem::Tuintptr_t: {
+ 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: {
+ result = new GPUVideoTextureHost(aFlags, aDesc.get_SurfaceDescriptorGPUVideo());
+ break;
+ }
+#ifdef XP_WIN
+ case SurfaceDescriptor::TSurfaceDescriptorDIB: {
+ 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)
+{
+}
+
+TextureHost::~TextureHost()
+{
+ // 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();
+}
+
+void TextureHost::Finalize()
+{
+ if (!(GetFlags() & TextureFlags::DEALLOCATE_CLIENT)) {
+ DeallocateSharedData();
+ DeallocateDeviceData();
+ }
+}
+
+void
+TextureHost::UnbindTextureSource()
+{
+ if (mReadLock) {
+ auto compositor = GetCompositor();
+ // 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 (compositor) {
+ compositor->UnlockAfterComposition(this);
+ } else {
+ // GetCompositor returned null which means no compositor can be using this
+ // texture. We can ReadUnlock right away.
+ ReadUnlock();
+ }
+ }
+}
+
+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.
+ if (!(GetFlags() & TextureFlags::RECYCLE)) {
+ return;
+ }
+
+ auto compositor = GetCompositor();
+ // 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 (!compositor ||
+ compositor->IsDestroyed() ||
+ compositor->AsBasicCompositor() ||
+ HasIntermediateBuffer()) {
+ static_cast<TextureParent*>(mActor)->NotifyNotUsed(mFwdTransactionId);
+ return;
+ }
+
+ compositor->NotifyNotUsedAfterComposition(this);
+}
+
+void
+TextureHost::CallNotifyNotUsed()
+{
+ if (!mActor) {
+ return;
+ }
+ static_cast<TextureParent*>(mActor)->NotifyNotUsed(mFwdTransactionId);
+}
+
+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()) {
+ AppendToString(aStream, GetSize(), " [size=", "]");
+ AppendToString(aStream, GetFormat(), " [format=", "]");
+ Unlock();
+ }
+ AppendToString(aStream, mFlags, " [flags=", "]");
+#ifdef MOZ_DUMP_PAINTING
+ if (gfxPrefs::LayersDumpTexture() || profiler_feature_active("layersdump")) {
+ 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)
+{
+ MOZ_COUNT_CTOR(TextureSource);
+}
+
+TextureSource::~TextureSource()
+{
+ MOZ_COUNT_DTOR(TextureSource);
+}
+
+const char*
+TextureSource::Name() const
+{
+ MOZ_CRASH("GFX: TextureSource without class name");
+ return "TextureSource";
+}
+
+BufferTextureHost::BufferTextureHost(const BufferDescriptor& aDesc,
+ TextureFlags aFlags)
+: TextureHost(aFlags)
+, mCompositor(nullptr)
+, mUpdateSerial(1)
+, mLocked(false)
+, mNeedsFullUpdate(false)
+{
+ mDescriptor = aDesc;
+ switch (mDescriptor.type()) {
+ case BufferDescriptor::TYCbCrDescriptor: {
+ const YCbCrDescriptor& ycbcr = mDescriptor.get_YCbCrDescriptor();
+ mSize = ycbcr.ySize();
+ 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;
+ }
+}
+
+BufferTextureHost::~BufferTextureHost()
+{}
+
+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::SetCompositor(Compositor* aCompositor)
+{
+ MOZ_ASSERT(aCompositor);
+ if (mCompositor == aCompositor) {
+ return;
+ }
+ if (aCompositor && mCompositor &&
+ aCompositor->GetBackendType() == mCompositor->GetBackendType()) {
+ RefPtr<TextureSource> it = mFirstSource;
+ while (it) {
+ it->SetCompositor(aCompositor);
+ it = it->GetNextSibling();
+ }
+ }
+ if (mFirstSource && mFirstSource->IsOwnedBy(this)) {
+ mFirstSource->SetOwner(nullptr);
+ }
+ if (mFirstSource) {
+ mFirstSource = nullptr;
+ mNeedsFullUpdate = true;
+ }
+ mCompositor = aCompositor;
+}
+
+void
+BufferTextureHost::DeallocateDeviceData()
+{
+ if (mFirstSource && mFirstSource->NumCompositableRefs() > 0) {
+ 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 (!MaybeUpload(!mNeedsFullUpdate ? &mMaybeUpdatedRegion : nullptr)) {
+ return false;
+ }
+ mLocked = !!mFirstSource;
+ return mLocked;
+}
+
+void
+BufferTextureHost::Unlock()
+{
+ MOZ_ASSERT(mLocked);
+ mLocked = false;
+}
+
+void
+TextureHost::DeserializeReadLock(const ReadLockDescriptor& aDesc,
+ ISurfaceAllocator* aAllocator)
+{
+ RefPtr<TextureReadLock> lock = TextureReadLock::Deserialize(aDesc, aAllocator);
+ if (!lock) {
+ return;
+ }
+
+ // If mReadLock is not null it means we haven't unlocked it yet and the content
+ // side should not have been able to write into this texture and send a new lock!
+ MOZ_ASSERT(!mReadLock);
+ mReadLock = lock.forget();
+}
+
+void
+TextureHost::ReadUnlock()
+{
+ if (mReadLock) {
+ mReadLock->ReadUnlock();
+ mReadLock = nullptr;
+ }
+}
+
+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 (!mCompositor) {
+ return false;
+ }
+
+ if (mFormat == gfx::SurfaceFormat::YUV) {
+ mFirstSource = mCompositor->CreateDataTextureSourceAroundYCbCr(this);
+ } else {
+ RefPtr<gfx::DataSourceSurface> surf =
+ gfx::Factory::CreateWrappingDataSourceSurface(GetBuffer(),
+ ImageDataSerializer::ComputeRGBStride(mFormat, mSize.width), mSize, mFormat);
+ if (!surf) {
+ return false;
+ }
+ mFirstSource = mCompositor->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,
+ Compositor* aCompositor)
+{
+ if (!aCompositor) {
+ return false;
+ }
+
+ switch (aDescriptor.type()) {
+ case BufferDescriptor::TYCbCrDescriptor: {
+ const YCbCrDescriptor& ycbcr = aDescriptor.get_YCbCrDescriptor();
+
+ if (!aCompositor->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,
+ mCompositor);
+
+ 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->SetCompositor(mCompositor);
+ it = it->GetNextSibling();
+ }
+ }
+}
+
+bool
+BufferTextureHost::BindTextureSource(CompositableTextureSourceRef& aTexture)
+{
+ MOZ_ASSERT(mLocked);
+ MOZ_ASSERT(mFirstSource);
+ aTexture = mFirstSource;
+ return !!aTexture;
+}
+
+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();
+}
+
+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 &&
+ mCompositor &&
+ !mCompositor->SupportsEffect(EffectTypes::YCBCR)) {
+ return gfx::SurfaceFormat::R8G8B8X8;
+ }
+ return mFormat;
+}
+
+YUVColorSpace
+BufferTextureHost::GetYUVColorSpace() const
+{
+ if (mFormat == gfx::SurfaceFormat::YUV) {
+ const YCbCrDescriptor& desc = mDescriptor.get_YCbCrDescriptor();
+ return desc.yUVColorSpace();
+ }
+ return YUVColorSpace::UNKNOWN;
+}
+
+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();
+ }
+
+ // 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 (!mCompositor) {
+ // This can happen if we send textures to a compositable that isn't yet
+ // attached to a layer.
+ return false;
+ }
+ if (!mHasIntermediateBuffer && EnsureWrappingTextureSource()) {
+ 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 (!mCompositor->SupportsEffect(EffectTypes::YCBCR)) {
+ RefPtr<gfx::DataSourceSurface> surf =
+ ImageDataSerializer::DataSourceSurfaceFromYCbCrDescriptor(buf, mDescriptor.get_YCbCrDescriptor());
+ if (NS_WARN_IF(!surf)) {
+ return false;
+ }
+ if (!mFirstSource) {
+ mFirstSource = mCompositor->CreateDataTextureSource(mFlags|TextureFlags::RGB_FROM_YCBCR);
+ mFirstSource->SetOwner(this);
+ }
+ mFirstSource->Update(surf, aRegion);
+ return true;
+ }
+
+ RefPtr<DataTextureSource> srcY;
+ RefPtr<DataTextureSource> srcU;
+ RefPtr<DataTextureSource> srcV;
+ if (!mFirstSource) {
+ // We don't support BigImages for YCbCr compositing.
+ srcY = mCompositor->CreateDataTextureSource(mFlags|TextureFlags::DISALLOW_BIGIMAGE);
+ srcU = mCompositor->CreateDataTextureSource(mFlags|TextureFlags::DISALLOW_BIGIMAGE);
+ srcV = mCompositor->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.ySize().width,
+ desc.ySize(),
+ gfx::SurfaceFormat::A8);
+ RefPtr<gfx::DataSourceSurface> tempCb =
+ gfx::Factory::CreateWrappingDataSourceSurface(ImageDataSerializer::GetCbChannel(buf, desc),
+ desc.cbCrSize().width,
+ desc.cbCrSize(),
+ gfx::SurfaceFormat::A8);
+ RefPtr<gfx::DataSourceSurface> tempCr =
+ gfx::Factory::CreateWrappingDataSourceSurface(ImageDataSerializer::GetCrChannel(buf, desc),
+ desc.cbCrSize().width,
+ desc.cbCrSize(),
+ gfx::SurfaceFormat::A8);
+ // 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 = mCompositor->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)
+: mSurfaceAllocator(aSurfaceAllocator)
+, mSerial(aSerial)
+{
+ 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 LayersBackend& aBackend,
+ const TextureFlags& aFlags)
+{
+ mTextureHost = TextureHost::Create(aSharedData,
+ mSurfaceAllocator,
+ aBackend,
+ aFlags);
+ if (mTextureHost) {
+ mTextureHost->mActor = this;
+ }
+
+ return !!mTextureHost;
+}
+
+void
+TextureParent::Destroy()
+{
+ if (!mTextureHost) {
+ return;
+ }
+
+ // ReadUnlock here to make sure the ReadLock's shmem does not outlive the
+ // protocol that created it.
+ mTextureHost->ReadUnlock();
+
+ if (mTextureHost->GetFlags() & TextureFlags::DEALLOCATE_CLIENT) {
+ mTextureHost->ForgetSharedData();
+ }
+
+ mTextureHost->mActor = nullptr;
+ mTextureHost = nullptr;
+}
+
+void
+TextureHost::ReceivedDestroy(PTextureParent* aActor)
+{
+ static_cast<TextureParent*>(aActor)->RecvDestroy();
+}
+
+bool
+TextureParent::RecvRecycleTexture(const TextureFlags& aTextureFlags)
+{
+ if (!mTextureHost) {
+ return true;
+ }
+ mTextureHost->RecycleTexture(aTextureFlags);
+ return true;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/composite/TextureHost.h b/gfx/layers/composite/TextureHost.h
new file mode 100644
index 000000000..c224d8777
--- /dev/null
+++ b/gfx/layers/composite/TextureHost.h
@@ -0,0 +1,900 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+* This Source Code Form is subject to the terms of the Mozilla Public
+* License, v. 2.0. If a copy of the MPL was not distributed with this
+* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#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 "gfxTypes.h"
+#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/2D.h" // for DataSourceSurface
+#include "mozilla/gfx/Point.h" // for IntSize, IntPoint
+#include "mozilla/gfx/Types.h" // for SurfaceFormat, etc
+#include "mozilla/layers/Compositor.h" // for Compositor
+#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/UniquePtr.h" // for UniquePtr
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsDebug.h" // for NS_RUNTIMEABORT
+#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
+#include "nsRegion.h" // for nsIntRegion
+#include "nsTraceRefcnt.h" // for MOZ_COUNT_CTOR, etc
+#include "nscore.h" // for nsACString
+#include "mozilla/layers/AtomicRefCountedWithFinalize.h"
+#include "mozilla/gfx/Rect.h"
+
+namespace mozilla {
+namespace ipc {
+class Shmem;
+} // namespace ipc
+
+namespace layers {
+
+class BufferDescriptor;
+class BufferTextureHost;
+class Compositor;
+class CompositableParentManager;
+class ReadLockDescriptor;
+class CompositorBridgeParent;
+class SurfaceDescriptor;
+class HostIPCAllocator;
+class ISurfaceAllocator;
+class TextureHostOGL;
+class TextureReadLock;
+class TextureSourceOGL;
+class TextureSourceD3D9;
+class TextureSourceD3D11;
+class TextureSourceBasic;
+class DataTextureSource;
+class PTextureParent;
+class TextureParent;
+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 TextureSourceD3D9* AsSourceD3D9() { 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 SetCompositor(Compositor* aCompositor) {}
+
+ 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);
+ }
+
+ int NumCompositableRefs() const { return mCompositableCount; }
+
+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() {}
+
+ 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)
+ {}
+
+ virtual const char* Name() const override { return "DataTextureSource"; }
+
+ virtual 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.
+ virtual 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,
+ ISurfaceAllocator* aDeallocator,
+ LayersBackend aBackend,
+ TextureFlags aFlags);
+
+ /**
+ * 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 YUVColorSpace GetYUVColorSpace() const { return YUVColorSpace::UNKNOWN; }
+
+ /**
+ * 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 another TextureHost will take over.
+ */
+ virtual void UnbindTextureSource();
+
+ /**
+ * 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 it's device textures.
+ */
+ virtual void SetCompositor(Compositor* aCompositor) {}
+
+ /**
+ * 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,
+ LayersBackend aLayersBackend,
+ TextureFlags aFlags,
+ uint64_t aSerial);
+ 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();
+
+ /**
+ * Specific to B2G's Composer2D
+ * XXX - more doc here
+ */
+ virtual LayerRenderState GetRenderState()
+ {
+ // By default we return an empty render state, this should be overridden
+ // by the TextureHost implementations that are used on B2G with Composer2D
+ return LayerRenderState();
+ }
+
+ // 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; }
+
+ void AddCompositableRef() { ++mCompositableCount; }
+
+ 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);
+
+ TextureReadLock* GetReadLock() { return mReadLock; }
+
+ virtual Compositor* GetCompositor() = 0;
+
+ virtual BufferTextureHost* AsBufferTextureHost() { return nullptr; }
+
+protected:
+ void ReadUnlock();
+
+ void RecycleTexture(TextureFlags aFlags);
+
+ virtual void UpdatedInternal(const nsIntRegion *Region) {}
+
+ /**
+ * Called when mCompositableCount becomes 0.
+ */
+ void NotifyNotUsed();
+
+ // for Compositor.
+ void CallNotifyNotUsed();
+
+ PTextureParent* mActor;
+ RefPtr<TextureReadLock> mReadLock;
+ TextureFlags mFlags;
+ int mCompositableCount;
+ uint64_t mFwdTransactionId;
+
+ friend class Compositor;
+ friend class TextureParent;
+ friend class TiledLayerBufferComposite;
+};
+
+/**
+ * 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);
+
+ ~BufferTextureHost();
+
+ virtual uint8_t* GetBuffer() = 0;
+
+ virtual size_t GetBufferSize() = 0;
+
+ virtual bool Lock() override;
+
+ virtual void Unlock() override;
+
+ virtual void PrepareTextureSource(CompositableTextureSourceRef& aTexture) override;
+
+ virtual bool BindTextureSource(CompositableTextureSourceRef& aTexture) override;
+
+ virtual void UnbindTextureSource() override;
+
+ virtual void DeallocateDeviceData() override;
+
+ virtual void SetCompositor(Compositor* aCompositor) override;
+
+ virtual Compositor* GetCompositor() override { return mCompositor; }
+
+ /**
+ * 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).
+ */
+ virtual gfx::SurfaceFormat GetFormat() const override;
+
+ virtual YUVColorSpace GetYUVColorSpace() const override;
+
+ virtual gfx::IntSize GetSize() const override { return mSize; }
+
+ virtual already_AddRefed<gfx::DataSourceSurface> GetAsSurface() override;
+
+ virtual bool HasIntermediateBuffer() const override { return mHasIntermediateBuffer; }
+
+ virtual BufferTextureHost* AsBufferTextureHost() override { return this; }
+
+ const BufferDescriptor& GetBufferDescriptor() const { return mDescriptor; }
+
+protected:
+ bool Upload(nsIntRegion *aRegion = nullptr);
+ bool MaybeUpload(nsIntRegion *aRegion = nullptr);
+ bool EnsureWrappingTextureSource();
+
+ virtual void UpdatedInternal(const nsIntRegion* aRegion = nullptr) 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;
+
+ 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:
+ virtual void DeallocateSharedData() override;
+
+ virtual void ForgetSharedData() override;
+
+ virtual uint8_t* GetBuffer() override;
+
+ virtual size_t GetBufferSize() override;
+
+ virtual const char *Name() override { return "ShmemTextureHost"; }
+
+ virtual 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:
+ virtual void DeallocateSharedData() override;
+
+ virtual void ForgetSharedData() override;
+
+ virtual uint8_t* GetBuffer() override;
+
+ virtual size_t GetBufferSize() override;
+
+ virtual 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)
+ , mHasComplexProjection(false)
+ {}
+ virtual ~CompositingRenderTarget() {}
+
+ virtual 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,
+ 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 000000000..7458c7497
--- /dev/null
+++ b/gfx/layers/composite/TiledContentHost.cpp
@@ -0,0 +1,645 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "TiledContentHost.h"
+#include "gfxPrefs.h" // for gfxPrefs
+#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
+//#include "mozilla/layers/CompositorBridgeParent.h" // for CompositorBridgeParent
+#include "mozilla/layers/Effects.h" // for TexturedEffect, Effect, etc
+#include "mozilla/layers/LayerMetricsWrapper.h" // for LayerMetricsWrapper
+#include "mozilla/layers/TextureHostOGL.h" // for TextureHostOGL
+#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/layers/TextureClient.h"
+
+namespace mozilla {
+using namespace gfx;
+namespace layers {
+
+class Layer;
+
+float
+TileHost::GetFadeInOpacity(float aOpacity)
+{
+ TimeStamp now = TimeStamp::Now();
+ if (!gfxPrefs::LayerTileFadeInEnabled() ||
+ mFadeStart.IsNull() ||
+ now < mFadeStart)
+ {
+ return aOpacity;
+ }
+
+ float duration = gfxPrefs::LayerTileFadeInDuration();
+ float elapsed = (now - mFadeStart).ToMilliseconds();
+ if (elapsed > duration) {
+ mFadeStart = TimeStamp();
+ return aOpacity;
+ }
+ return aOpacity * (elapsed / duration);
+}
+
+TiledLayerBufferComposite::TiledLayerBufferComposite()
+ : mFrameResolution()
+{}
+
+TiledLayerBufferComposite::~TiledLayerBufferComposite()
+{
+ Clear();
+}
+
+void
+TiledLayerBufferComposite::SetCompositor(Compositor* aCompositor)
+{
+ MOZ_ASSERT(aCompositor);
+ for (TileHost& tile : mRetainedTiles) {
+ if (tile.IsPlaceholderTile()) continue;
+ tile.mTextureHost->SetCompositor(aCompositor);
+ if (tile.mTextureHostOnWhite) {
+ tile.mTextureHostOnWhite->SetCompositor(aCompositor);
+ }
+ }
+}
+
+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()) {
+ TileIntPoint position = mTiles.TilePosition(i);
+ IntPoint offset = GetTileOffset(position);
+ 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)
+{
+ // If we can use hwc for this TiledContentHost, it implies that we have exactly
+ // one high precision tile. Please check TiledContentHost::GetRenderState() for
+ // all condition.
+ 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,
+ tile.mTextureHost->GetRenderState());
+}
+
+void
+TiledContentHost::Attach(Layer* aLayer,
+ Compositor* aCompositor,
+ AttachFlags aFlags /* = NO_FLAGS */)
+{
+ CompositableHost::Attach(aLayer, aCompositor, 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)
+{
+ if (aTiledDescriptor.resolution() < 1) {
+ if (!mLowPrecisionTiledBuffer.UseTiles(aTiledDescriptor, mCompositor, aAllocator)) {
+ return false;
+ }
+ } else {
+ if (!mTiledBuffer.UseTiles(aTiledDescriptor, mCompositor, aAllocator)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+void
+UseTileTexture(CompositableTextureHostRef& aTexture,
+ CompositableTextureSourceRef& aTextureSource,
+ const IntRect& aUpdateRect,
+ Compositor* aCompositor)
+{
+ MOZ_ASSERT(aTexture);
+ if (!aTexture) {
+ return;
+ }
+
+ if (aCompositor) {
+ aTexture->SetCompositor(aCompositor);
+ }
+
+ 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(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 = Move(mTiles[i].mTextureSource);
+ if (aTile.mTextureHostOnWhite) {
+ aTile.mTextureSourceOnWhite = 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 = Move(mTiles[i].mTextureSource);
+ if (aTile.mTextureHostOnWhite) {
+ aTile.mTextureSourceOnWhite = 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,
+ Compositor* aCompositor,
+ ISurfaceAllocator* aAllocator)
+{
+ if (mResolution != aTiles.resolution() ||
+ aTiles.tileSize() != mTileSize) {
+ Clear();
+ }
+ MOZ_ASSERT(aAllocator);
+ MOZ_ASSERT(aCompositor);
+ if (!aAllocator || !aCompositor) {
+ 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 InfallibleTArray<TileDescriptor>& tileDescriptors = aTiles.tiles();
+
+ TextureSourceRecycler oldRetainedTiles(Move(mRetainedTiles));
+ mRetainedTiles.SetLength(tileDescriptors.Length());
+
+ // 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());
+ tile.mTextureHost->SetCompositor(aCompositor);
+ tile.mTextureHost->DeserializeReadLock(texturedDesc.sharedLock(), aAllocator);
+
+ if (texturedDesc.textureOnWhite().type() == MaybeTexture::TPTextureParent) {
+ tile.mTextureHostOnWhite = TextureHost::AsTextureHost(
+ texturedDesc.textureOnWhite().get_PTextureParent()
+ );
+ tile.mTextureHostOnWhite->DeserializeReadLock(
+ texturedDesc.sharedLockOnWhite(), aAllocator
+ );
+ }
+
+ tile.mTilePosition = newTiles.TilePosition(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();
+
+ aCompositor->CompositeUntil(tile.mFadeStart +
+ TimeDuration::FromMilliseconds(gfxPrefs::LayerTileFadeInDuration()));
+ }
+ }
+
+ // 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(),
+ aCompositor);
+
+ if (tile.mTextureHostOnWhite) {
+ UseTileTexture(tile.mTextureHostOnWhite,
+ tile.mTextureSourceOnWhite,
+ texturedDesc.updateRect(),
+ aCompositor);
+ }
+ }
+
+ 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 = TileIntPoint();
+ mTiles.mSize = TileIntSize();
+ mValidRegion = nsIntRegion();
+ mResolution = 1.0;
+}
+
+void
+TiledContentHost::Composite(LayerComposite* aLayer,
+ EffectChain& aEffectChain,
+ float aOpacity,
+ const gfx::Matrix4x4& aTransform,
+ const gfx::SamplingFilter aSamplingFilter,
+ const gfx::IntRect& aClipRect,
+ const nsIntRegion* aVisibleRegion /* = nullptr */)
+{
+ MOZ_ASSERT(mCompositor);
+ // 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.
+ Color backgroundColor;
+ if (aOpacity == 1.0f && gfxPrefs::LowPrecisionOpacity() < 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)
+ ? gfxPrefs::LowPrecisionOpacity() : 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,
+ lowPrecisionOpacityReduction < 1.0f ? &backgroundColor : nullptr,
+ aEffectChain, lowPrecisionOpacityReduction * aOpacity,
+ aSamplingFilter, aClipRect, *renderRegion, aTransform);
+ RenderLayerBuffer(mTiledBuffer, nullptr, aEffectChain, aOpacity, aSamplingFilter,
+ aClipRect, *renderRegion, aTransform);
+}
+
+
+void
+TiledContentHost::RenderTile(TileHost& aTile,
+ 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)
+{
+ 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,
+ aTile.mTextureHost->GetRenderState());
+ 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 = Rect(textureRect.x / aTextureBounds.width,
+ textureRect.y / aTextureBounds.height,
+ textureRect.width / aTextureBounds.width,
+ textureRect.height / aTextureBounds.height);
+ mCompositor->DrawQuad(graphicsRect, aClipRect, aEffectChain, opacity, aTransform, aVisibleRect);
+ }
+ DiagnosticFlags flags = DiagnosticFlags::CONTENT | DiagnosticFlags::TILE;
+ if (aTile.mTextureHostOnWhite) {
+ flags |= DiagnosticFlags::COMPONENT_ALPHA;
+ }
+ mCompositor->DrawDiagnostics(flags,
+ aScreenRegion, aClipRect, aTransform, mFlashCounter);
+}
+
+void
+TiledContentHost::RenderLayerBuffer(TiledLayerBufferComposite& aLayerBuffer,
+ const Color* aBackgroundColor,
+ EffectChain& aEffectChain,
+ float aOpacity,
+ const gfx::SamplingFilter aSamplingFilter,
+ const gfx::IntRect& aClipRect,
+ nsIntRegion aVisibleRegion,
+ gfx::Matrix4x4 aTransform)
+{
+ if (!mCompositor) {
+ NS_WARNING("Can't render tiled content host - no compositor");
+ return;
+ }
+ 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);
+ mCompositor->DrawQuad(graphicsRect, aClipRect, effect, 1.0, aTransform);
+ }
+ }
+
+ for (size_t i = 0; i < aLayerBuffer.GetTileCount(); ++i) {
+ TileHost& tile = aLayerBuffer.GetTile(i);
+ if (tile.IsPlaceholderTile()) {
+ continue;
+ }
+
+ TileIntPoint tilePosition = aLayerBuffer.GetPlacement().TilePosition(i);
+ // A sanity check that catches a lot of mistakes.
+ MOZ_ASSERT(tilePosition.x == tile.mTilePosition.x && tilePosition.y == tile.mTilePosition.y);
+
+ IntPoint tileOffset = aLayerBuffer.GetTileOffset(tilePosition);
+ nsIntRegion tileDrawRegion = IntRect(tileOffset, aLayerBuffer.GetScaledTileSize());
+ tileDrawRegion.AndWith(compositeRegion);
+
+ if (tileDrawRegion.IsEmpty()) {
+ continue;
+ }
+
+ tileDrawRegion.ScaleRoundOut(resolution, resolution);
+ RenderTile(tile, aEffectChain, aOpacity,
+ aTransform, aSamplingFilter, aClipRect, tileDrawRegion,
+ tileOffset * resolution, aLayerBuffer.GetTileSize(),
+ gfx::Rect(visibleRect.x, visibleRect.y,
+ visibleRect.width, visibleRect.height));
+ if (tile.mTextureHostOnWhite) {
+ componentAlphaDiagnostic = DiagnosticFlags::COMPONENT_ALPHA;
+ }
+ }
+
+ gfx::Rect rect(visibleRect.x, visibleRect.y,
+ visibleRect.width, visibleRect.height);
+ GetCompositor()->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 (gfxPrefs::LayersDumpTexture() || profiler_feature_active("layersdump")) {
+ nsAutoCString pfx(aPrefix);
+ pfx += " ";
+
+ Dump(aStream, pfx.get(), false);
+ }
+}
+
+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 000000000..4b52394de
--- /dev/null
+++ b/gfx/layers/composite/TiledContentHost.h
@@ -0,0 +1,292 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#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()
+ {}
+
+ // 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;
+ mTilePosition = o.mTilePosition;
+ }
+ TileHost& operator=(const TileHost& o) {
+ if (this == &o) {
+ return *this;
+ }
+ mTextureHost = o.mTextureHost;
+ mTextureHostOnWhite = o.mTextureHostOnWhite;
+ mTextureSource = o.mTextureSource;
+ mTextureSourceOnWhite = o.mTextureSourceOnWhite;
+ mTilePosition = o.mTilePosition;
+ 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);
+ }
+
+ /**
+ * 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.
+ TileIntPoint mTilePosition;
+ TimeStamp mFadeStart;
+};
+
+class TiledLayerBufferComposite
+ : public TiledLayerBuffer<TiledLayerBufferComposite, TileHost>
+{
+ friend class TiledLayerBuffer<TiledLayerBufferComposite, TileHost>;
+
+public:
+ TiledLayerBufferComposite();
+ ~TiledLayerBufferComposite();
+
+ bool UseTiles(const SurfaceDescriptorTiles& aTileDescriptors,
+ Compositor* aCompositor,
+ 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 SetCompositor(Compositor* aCompositor);
+
+ 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:
+ ~TiledContentHost();
+
+public:
+ virtual LayerRenderState GetRenderState() override
+ {
+ // If we have exactly one high precision tile, then we can support hwc.
+ if (mTiledBuffer.GetTileCount() == 1 &&
+ mLowPrecisionTiledBuffer.GetTileCount() == 0) {
+ TextureHost* host = mTiledBuffer.GetTile(0).mTextureHost;
+ if (host) {
+ MOZ_ASSERT(!mTiledBuffer.GetTile(0).mTextureHostOnWhite, "Component alpha not supported!");
+
+ gfx::IntPoint offset = mTiledBuffer.GetTileOffset(mTiledBuffer.GetPlacement().TilePosition(0));
+
+ // Don't try to use HWC if the content doesn't start at the top-left of the tile.
+ if (offset != GetValidRegion().GetBounds().TopLeft()) {
+ return LayerRenderState();
+ }
+
+ LayerRenderState state = host->GetRenderState();
+ state.SetOffset(offset);
+ return state;
+ }
+ }
+ return LayerRenderState();
+ }
+
+ // 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,
+ nsIntRegion* aUpdatedRegionBack) override
+ {
+ NS_ERROR("N/A for tiled layers");
+ return false;
+ }
+
+ const nsIntRegion& GetValidLowPrecisionRegion() const
+ {
+ return mLowPrecisionTiledBuffer.GetValidRegion();
+ }
+
+ const nsIntRegion& GetValidRegion() const
+ {
+ return mTiledBuffer.GetValidRegion();
+ }
+
+ virtual void SetCompositor(Compositor* aCompositor) override
+ {
+ MOZ_ASSERT(aCompositor);
+ CompositableHost::SetCompositor(aCompositor);
+ mTiledBuffer.SetCompositor(aCompositor);
+ mLowPrecisionTiledBuffer.SetCompositor(aCompositor);
+ }
+
+ bool UseTiledLayerBuffer(ISurfaceAllocator* aAllocator,
+ const SurfaceDescriptorTiles& aTiledDescriptor);
+
+ virtual void Composite(LayerComposite* aLayer,
+ EffectChain& aEffectChain,
+ float aOpacity,
+ const gfx::Matrix4x4& aTransform,
+ const gfx::SamplingFilter aSamplingFilter,
+ const gfx::IntRect& aClipRect,
+ const nsIntRegion* aVisibleRegion = nullptr) override;
+
+ virtual CompositableType GetType() override { return CompositableType::CONTENT_TILED; }
+
+ virtual TiledContentHost* AsTiledContentHost() override { return this; }
+
+ virtual void Attach(Layer* aLayer,
+ Compositor* aCompositor,
+ AttachFlags aFlags = NO_FLAGS) override;
+
+ virtual void Detach(Layer* aLayer = nullptr,
+ AttachFlags aFlags = NO_FLAGS) override;
+
+ virtual void Dump(std::stringstream& aStream,
+ const char* aPrefix="",
+ bool aDumpHtml=false) override;
+
+ virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix) override;
+
+ virtual void AddAnimationInvalidation(nsIntRegion& aRegion) override;
+
+private:
+
+ void RenderLayerBuffer(TiledLayerBufferComposite& aLayerBuffer,
+ const gfx::Color* aBackgroundColor,
+ EffectChain& aEffectChain,
+ float aOpacity,
+ const gfx::SamplingFilter aSamplingFilter,
+ const gfx::IntRect& aClipRect,
+ nsIntRegion aMaskRegion,
+ gfx::Matrix4x4 aTransform);
+
+ // Renders a single given tile.
+ void RenderTile(TileHost& aTile,
+ 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);
+
+ 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 000000000..7ca42426d
--- /dev/null
+++ b/gfx/layers/composite/X11TextureHost.cpp
@@ -0,0 +1,107 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "X11TextureHost.h"
+#include "mozilla/layers/BasicCompositor.h"
+#include "mozilla/layers/X11TextureSourceBasic.h"
+#ifdef GL_PROVIDER_GLX
+#include "mozilla/layers/CompositorOGL.h"
+#include "mozilla/layers/X11TextureSourceOGL.h"
+#endif
+#include "gfxXlibSurface.h"
+#include "gfx2DGlue.h"
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::gfx;
+
+X11TextureHost::X11TextureHost(TextureFlags aFlags,
+ const SurfaceDescriptorX11& aDescriptor)
+ : TextureHost(aFlags)
+{
+ RefPtr<gfxXlibSurface> surface = aDescriptor.OpenForeign();
+ mSurface = surface.get();
+
+ if (!(aFlags & TextureFlags::DEALLOCATE_CLIENT)) {
+ mSurface->TakePixmap();
+ }
+}
+
+bool
+X11TextureHost::Lock()
+{
+ if (!mCompositor) {
+ return false;
+ }
+
+ if (!mTextureSource) {
+ switch (mCompositor->GetBackendType()) {
+ case LayersBackend::LAYERS_BASIC:
+ mTextureSource =
+ new X11TextureSourceBasic(mCompositor->AsBasicCompositor(), mSurface);
+ break;
+#ifdef GL_PROVIDER_GLX
+ case LayersBackend::LAYERS_OPENGL:
+ mTextureSource =
+ new X11TextureSourceOGL(mCompositor->AsCompositorOGL(), mSurface);
+ break;
+#endif
+ default:
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void
+X11TextureHost::SetCompositor(Compositor* aCompositor)
+{
+ mCompositor = aCompositor;
+ if (mTextureSource) {
+ mTextureSource->SetCompositor(aCompositor);
+ }
+}
+
+SurfaceFormat
+X11TextureHost::GetFormat() const
+{
+ gfxContentType type = mSurface->GetContentType();
+#ifdef GL_PROVIDER_GLX
+ if (mCompositor->GetBackendType() == LayersBackend::LAYERS_OPENGL) {
+ return X11TextureSourceOGL::ContentTypeToSurfaceFormat(type);
+ }
+#endif
+ return X11TextureSourceBasic::ContentTypeToSurfaceFormat(type);
+}
+
+IntSize
+X11TextureHost::GetSize() const
+{
+ 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();
+}
+
+}
+}
diff --git a/gfx/layers/composite/X11TextureHost.h b/gfx/layers/composite/X11TextureHost.h
new file mode 100644
index 000000000..1f1d34409
--- /dev/null
+++ b/gfx/layers/composite/X11TextureHost.h
@@ -0,0 +1,72 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#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;
+
+ virtual const char* Name() const override { return "X11TextureSource"; }
+};
+
+// TextureHost for Xlib-backed TextureSources.
+class X11TextureHost : public TextureHost
+{
+public:
+ X11TextureHost(TextureFlags aFlags,
+ const SurfaceDescriptorX11& aDescriptor);
+
+ virtual void SetCompositor(Compositor* aCompositor) override;
+
+ virtual Compositor* GetCompositor() override { return mCompositor; }
+
+ virtual bool Lock() override;
+
+ virtual gfx::SurfaceFormat GetFormat() const override;
+
+ virtual gfx::IntSize GetSize() const override;
+
+ virtual bool BindTextureSource(CompositableTextureSourceRef& aTexture) override
+ {
+ aTexture = mTextureSource;
+ return !!aTexture;
+ }
+
+ virtual already_AddRefed<gfx::DataSourceSurface> GetAsSurface() override;
+
+#ifdef MOZ_LAYERS_HAVE_LOG
+ virtual const char* Name() override { return "X11TextureHost"; }
+#endif
+
+protected:
+ virtual 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
diff --git a/gfx/layers/composite/qrcode_table.h b/gfx/layers/composite/qrcode_table.h
new file mode 100644
index 000000000..553be28c1
--- /dev/null
+++ b/gfx/layers/composite/qrcode_table.h
@@ -0,0 +1,259 @@
+const char * const sQRCodeTable[] = {
+ "\xFE\x5B\xFC\x16\x90\x6E\xB2\xBB\x74\xA5\xDB\xA8\xAE\xC1\x4D\x7\xFA\xAF\xE0\x1F\x0\xD3\x63\xB2\xC7\x11\x94\xE3\x6\x63\xA0\xF1\x74\xAA\x80\x78\xE3\xFA\x94\xD0\x43\xBA\xBA\x73\xFD\xD4\x83\xEE\x8A\x23\x5\x4D\x6F\xEF\x8E\x0",
+ "\xFE\x5B\xFC\x13\x90\x6E\xB6\xBB\x74\xA5\xDB\xA2\xAE\xC1\x5\x7\xFA\xAF\xE0\x1B\x0\xEF\xF6\x20\xA5\x11\xB4\xF2\x22\x6A\x84\x62\xF0\xAB\x80\x5A\xAB\xFA\x5D\xF0\x53\xBA\xBA\x97\x6D\xD2\x11\xAE\xA2\x23\x5\x84\x4F\xEA\xAA\x1",
+ "\xFE\x5B\xFC\x16\x90\x6E\xB2\xBB\x74\xA5\xDB\xA8\xAE\xC1\x4D\x7\xFA\xAF\xE0\x1F\x0\xD3\x63\xB7\xE4\x11\xAB\xCB\x7\x9B\xA0\xF8\xB6\xAA\x0\x48\xE3\xFB\x94\xD0\x4F\xBA\xBA\x13\xFD\xD7\x83\xEE\x8A\x23\x5\xD\x6F\xED\x8E\x0",
+ "\xFE\x5B\xFC\x13\x90\x6E\xB6\xBB\x74\xA5\xDB\xA2\xAE\xC1\x5\x7\xFA\xAF\xE0\x1B\x0\xEF\xF6\x25\x86\x11\x8B\xDA\x23\x92\x84\x6B\x32\xAB\x0\x6A\xAB\xFB\x5D\xF0\x5F\xBA\xBA\xF7\x6D\xD1\x11\xAE\xA2\x23\x5\xC4\x4F\xE8\xAA\x1",
+ "\xFE\x5B\xFC\x16\x90\x6E\xB2\xBB\x74\xA5\xDB\xA8\xAE\xC1\x4D\x7\xFA\xAF\xE0\x1F\x0\xD3\x63\xB1\xAD\x11\xAF\xC3\x7\xC8\x20\xFB\x6C\xAA\x80\x68\xEB\xFB\x14\xD0\x4F\xBA\xBA\x13\xFD\xD4\x83\xEE\x82\x23\x5\x4D\x6F\xE9\x8E\x0",
+ "\xFE\xFB\xFC\x15\x50\x6E\xA0\xBB\x75\xE5\xDB\xA0\xAE\xC1\x69\x7\xFA\xAF\xE0\xF\x0\xCE\x51\x7E\x69\xCA\xE2\xE4\xF9\x53\x4D\x4C\x7A\xE2\x80\x67\x17\xF8\xB0\x50\x5B\x28\xBA\xD3\xFD\xD0\xCA\xEE\x9C\xF9\x5\xCD\x6F\xEE\xE3\x1",
+ "\xFE\x5B\xFC\x13\x90\x6E\xB6\xBB\x74\xA5\xDB\xA2\xAE\xC1\x5\x7\xFA\xAF\xE0\x1B\x0\xEF\xF6\x24\x8E\x11\x82\xA2\x22\x79\x4\x62\xAE\xAA\x0\x4A\xA3\xFA\x5D\xF0\x53\xBA\xBA\xD7\x6D\xD3\x11\xAE\xA2\x23\x5\x44\x4F\xEA\xAA\x1",
+ "\xFE\x5B\xFC\x13\x90\x6E\xB6\xBB\x74\xA5\xDB\xA2\xAE\xC1\x5\x7\xFA\xAF\xE0\x1B\x0\xEF\xF6\x26\xEC\x11\xB0\xFA\x22\x39\x4\x61\x2A\xAB\x0\x7A\xA3\xFA\xDD\xF0\x53\xBA\xBA\x97\x6D\xD1\x11\xAE\xAA\x23\x5\xC4\x4F\xEE\xAA\x1",
+ "\xFE\x5B\xFC\x16\x90\x6E\xB2\xBB\x74\xA5\xDB\xA8\xAE\xC1\x4D\x7\xFA\xAF\xE0\x1F\x0\xD3\x63\xB6\x2A\x11\x81\x83\x7\x7A\xE0\xFE\xEC\xAA\x80\x58\xE7\xFA\x14\xD0\x43\xBA\xBA\x13\xFD\xD6\x83\xEE\x8A\x23\x5\xCD\x6F\xED\x8E\x0",
+ "\xFE\x5B\xFC\x13\x90\x6E\xB6\xBB\x74\xA5\xDB\xA2\xAE\xC1\x5\x7\xFA\xAF\xE0\x1B\x0\xEF\xF6\x24\x48\x11\xA1\x92\x23\x73\xC4\x6D\x68\xAB\x80\x7A\xAF\xFA\xDD\xF0\x53\xBA\xBA\xF7\x6D\xD0\x11\xAE\xA2\x23\x5\x4\x4F\xE8\xAA\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x27\xB\xAA\x82\xDD\xCA\xAB\x3B\x86\xED\x73\x80\x71\x1B\xFA\xA2\x10\x58\x46\xBA\x8A\xAD\xD3\xAA\xAE\xB5\xDB\x5\x3B\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x27\xAE\xAA\xA5\xB5\xC9\xFB\x3B\x82\x23\x73\x80\x61\x1B\xFB\x22\x10\x58\x46\xBA\xCA\xAD\xD3\xAA\xAE\xBD\xDB\x5\xFB\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x23\x24\xAA\xBC\xD5\xCB\xFB\xFB\xB4\x69\x73\x80\x61\x1B\xFA\xA2\x10\x58\x46\xBA\xCA\xAD\xD0\xAA\xAE\xBD\xDB\x5\x3B\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB3\x81\xAA\x89\xF4\xEC\xE2\xDF\x20\xA7\x73\x80\x63\x53\xFB\x6B\x30\x48\x46\xBA\x2E\x3D\xD4\x38\xEE\x95\xDB\x5\xB2\xAF\xEC\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x23\x6E\xAA\xA7\xC5\xCA\x1B\xBB\x94\xAF\x73\x80\x41\x1B\xFB\x22\x10\x54\x46\xBA\xAA\xAD\xD2\xAA\xAE\xAD\xDB\x5\xFB\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x23\xCB\xAA\x80\xAD\xC9\x4B\xBB\x90\x61\x73\x80\x51\x1B\xFA\xA2\x10\x54\x46\xBA\xEA\xAD\xD2\xAA\xAE\xA5\xDB\x5\x3B\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x20\x0\xAA\x94\xBD\xCA\xF3\x7B\xAC\x6D\x72\x0\x51\x1B\xFA\xA2\x10\x58\x46\xBA\xCA\xAD\xD0\xAA\xAE\xAD\xDB\x5\x3B\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x20\xA5\xAA\xB3\xD5\xC9\xA3\x7B\xA8\xA3\x72\x0\x41\x1B\xFB\x22\x10\x58\x46\xBA\x8A\xAD\xD0\xAA\xAE\xA5\xDB\x5\xFB\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x20\x4A\xAA\x8F\xAD\xCB\x13\x3B\x8C\xAB\x72\x0\x71\x1B\xFB\x22\x10\x54\x46\xBA\xAA\xAD\xD2\xAA\xAE\xBD\xDB\x5\xFB\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x20\xEF\xAA\xA8\xC5\xC8\x43\x3B\x88\x65\x72\x0\x61\x1B\xFA\xA2\x10\x54\x46\xBA\xEA\xAD\xD2\xAA\xAE\xB5\xDB\x5\x3B\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x24\x65\xAA\xB1\xA5\xCA\x43\xFB\xBE\x2F\x72\x0\x61\x1B\xFB\x22\x10\x54\x46\xBA\xEA\xAD\xD1\xAA\xAE\xB5\xDB\x5\xFB\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x24\xC0\xAA\x96\xCD\xC9\x13\xFB\xBA\xE1\x72\x0\x71\x1B\xFA\xA2\x10\x54\x46\xBA\xAA\xAD\xD1\xAA\xAE\xBD\xDB\x5\x3B\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x24\x2F\xAA\xAA\xB5\xCB\xA3\xBB\x9E\xE9\x72\x0\x41\x1B\xFA\xA2\x10\x58\x46\xBA\x8A\xAD\xD3\xAA\xAE\xA5\xDB\x5\x3B\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x24\x8A\xAA\x8D\xDD\xC8\xF3\xBB\x9A\x27\x72\x0\x51\x1B\xFB\x22\x10\x58\x46\xBA\xCA\xAD\xD3\xAA\xAE\xAD\xDB\x5\xFB\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x22\x62\xAA\xA6\xE5\xCA\xB3\x7B\xAF\xE9\x73\x0\x61\x1B\xFA\x22\x10\x58\x46\xBA\x8A\xAD\xD2\xAA\xAE\xA5\xDB\x5\xBB\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB2\xC7\xAA\x93\xC4\xED\xAA\x5F\x3B\x27\x73\x0\x63\x53\xFB\xEB\x30\x48\x46\xBA\x6E\x3D\xD6\x38\xEE\x8D\xDB\x5\x32\xAF\xEE\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x22\x28\xAA\xBD\xF5\xCB\x53\x3B\x8F\x2F\x73\x0\x41\x1B\xFB\xA2\x10\x54\x46\xBA\xEA\xAD\xD0\xAA\xAE\xB5\xDB\x5\x7B\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x22\x8D\xAA\x9A\x9D\xC8\x3\x3B\x8B\xE1\x73\x0\x51\x1B\xFA\x22\x10\x54\x46\xBA\xAA\xAD\xD0\xAA\xAE\xBD\xDB\x5\xBB\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x26\x7\xAA\x83\xFD\xCA\x3\xFB\xBD\xAB\x73\x0\x51\x1B\xFB\xA2\x10\x54\x46\xBA\xAA\xAD\xD3\xAA\xAE\xBD\xDB\x5\x7B\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x26\xA2\xAA\xA4\x95\xC9\x53\xFB\xB9\x65\x73\x0\x41\x1B\xFA\x22\x10\x54\x46\xBA\xEA\xAD\xD3\xAA\xAE\xB5\xDB\x5\xBB\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x26\x4D\xAA\x98\xED\xCB\xE3\xBB\x9D\x6D\x73\x0\x71\x1B\xFA\x22\x10\x58\x46\xBA\xCA\xAD\xD1\xAA\xAE\xAD\xDB\x5\xBB\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x26\xE8\xAA\xBF\x85\xC8\xB3\xBB\x99\xA3\x73\x0\x61\x1B\xFB\xA2\x10\x58\x46\xBA\x8A\xAD\xD1\xAA\xAE\xA5\xDB\x5\x7B\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x26\x49\xAA\x90\xB5\xCA\xA0\xFB\xAF\xB7\x72\x80\x71\x13\xFA\x22\x10\x58\x46\xBA\xCA\xAD\xD3\xAA\xAE\xA5\xDB\x5\x7B\x8F\xE9\x76\x1",
+ "\xFE\x4B\xFC\x14\x90\x6E\x90\xBB\x75\x25\xDB\xA3\xAE\xC1\x75\x7\xFA\xAF\xE0\x7\x0\xFB\xCD\x50\xC4\x49\x39\xE5\x2A\x7A\xC3\x48\xF7\x4A\x0\x6F\x2B\xFA\x2C\x30\x4B\xC8\xBA\xE9\x25\xD7\x49\x2E\xB5\x39\x5\x83\x6F\xE9\x4E\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x26\x3\xAA\x8B\xA5\xCB\x40\xBB\x8F\x71\x72\x80\x51\x13\xFB\xA2\x10\x54\x46\xBA\xAA\xAD\xD1\xAA\xAE\xB5\xDB\x5\xBB\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x26\xA6\xAA\xAC\xCD\xC8\x10\xBB\x8B\xBF\x72\x80\x41\x13\xFA\x22\x10\x54\x46\xBA\xEA\xAD\xD1\xAA\xAE\xBD\xDB\x5\x7B\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x22\x2C\xAA\xB5\xAD\xCA\x10\x7B\xBD\xF5\x72\x80\x41\x13\xFB\xA2\x10\x54\x46\xBA\xEA\xAD\xD2\xAA\xAE\xBD\xDB\x5\xBB\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x22\x89\xAA\x92\xC5\xC9\x40\x7B\xB9\x3B\x72\x80\x51\x13\xFA\x22\x10\x54\x46\xBA\xAA\xAD\xD2\xAA\xAE\xB5\xDB\x5\x7B\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB2\x66\xAA\xBC\xF4\xEF\xB9\x1F\xD\x33\x72\x80\x73\x5B\xFA\x6B\x30\x48\x46\xBA\x2E\x3D\xD4\x38\xEE\x8D\xDB\x5\x32\xAF\xEA\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x22\xC3\xAA\x89\xD5\xC8\xA0\x3B\x99\xFD\x72\x80\x71\x13\xFB\xA2\x10\x58\x46\xBA\xCA\xAD\xD0\xAA\xAE\xA5\xDB\x5\xBB\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x24\x2B\xAA\xA2\xED\xCA\xE0\xFB\xAC\x33\x73\x80\x41\x13\xFA\xA2\x10\x58\x46\xBA\x8A\xAD\xD1\xAA\xAE\xAD\xDB\x5\xFB\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x24\x8E\xAA\x85\x85\xC9\xB0\xFB\xA8\xFD\x73\x80\x51\x13\xFB\x22\x10\x58\x46\xBA\xCA\xAD\xD1\xAA\xAE\xA5\xDB\x5\x3B\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB4\x61\xAA\xAB\xB4\xEF\x49\x9F\x1C\xF5\x73\x80\x73\x5B\xFB\x6B\x30\x44\x46\xBA\x4E\x3D\xD7\x38\xEE\x9D\xDB\x5\x72\xAF\xEE\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x24\xC4\xAA\x9E\x95\xC8\x50\xBB\x88\x3B\x73\x80\x71\x13\xFA\xA2\x10\x54\x46\xBA\xAA\xAD\xD3\xAA\xAE\xB5\xDB\x5\xFB\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x20\x4E\xAA\x87\xF5\xCA\x50\x7B\xBE\x71\x73\x80\x71\x13\xFB\x22\x10\x54\x46\xBA\xAA\xAD\xD0\xAA\xAE\xB5\xDB\x5\x3B\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB0\xEB\xAA\xB2\xD4\xED\x49\x5F\x2A\xBF\x73\x80\x73\x5B\xFA\xEB\x30\x44\x46\xBA\x4E\x3D\xD4\x38\xEE\x9D\xDB\x5\xB2\xAF\xEA\x52\x0",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB0\x4\xAA\x8E\xAC\xEF\xF9\x1F\xE\xB7\x73\x80\x43\x5B\xFA\xEB\x30\x48\x46\xBA\x6E\x3D\xD6\x38\xEE\x85\xDB\x5\xB2\xAF\xEE\x52\x0",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB0\xA1\xAA\xA9\xC4\xEC\xA9\x1F\xA\x79\x73\x80\x53\x5B\xFB\x6B\x30\x48\x46\xBA\x2E\x3D\xD6\x38\xEE\x8D\xDB\x5\x72\xAF\xE8\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x23\x6A\xAA\xAF\x9D\xCB\x58\xFB\xA6\x75\x72\x0\x41\x13\xFB\x22\x10\x54\x46\xBA\xAA\xAD\xD0\xAA\xAE\xA5\xDB\x5\x3B\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x23\xCF\xAA\x88\xF5\xC8\x8\xFB\xA2\xBB\x72\x0\x51\x13\xFA\xA2\x10\x54\x46\xBA\xEA\xAD\xD0\xAA\xAE\xAD\xDB\x5\xFB\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x23\x20\xAA\xB4\x8D\xCA\xB8\xBB\x86\xB3\x72\x0\x61\x13\xFA\xA2\x10\x58\x46\xBA\xCA\xAD\xD2\xAA\xAE\xB5\xDB\x5\xFB\x8F\xE9\x76\x1",
+ "\xFE\xFB\xFC\x12\x90\x6E\x98\xBB\x74\xE5\xDB\xA2\xAE\xC1\xD\x7\xFA\xAF\xE0\x1F\x0\xDA\x6A\xC\x6A\x55\x54\xF9\xB8\x5A\x60\xE7\x28\x27\x0\x5C\xA7\xF8\x3E\x70\x47\xB9\xBA\xFB\x6D\xD5\xC7\x2E\x88\x8F\x5\x8D\x5F\xEB\x7\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x27\xF\xAA\x8A\x85\xCB\xE8\x7B\xB4\x37\x72\x0\x71\x13\xFA\xA2\x10\x58\x46\xBA\x8A\xAD\xD1\xAA\xAE\xBD\xDB\x5\xFB\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x27\xAA\xAA\xAD\xED\xC8\xB8\x7B\xB0\xF9\x72\x0\x61\x13\xFB\x22\x10\x58\x46\xBA\xCA\xAD\xD1\xAA\xAE\xB5\xDB\x5\x3B\x8F\xED\x76\x1",
+ "\xFE\x7B\xFC\x12\x90\x6E\x90\xBB\x75\xE5\xDB\xAB\xAE\xC1\x15\x7\xFA\xAF\xE0\xF\x0\xC7\x48\xC0\xAA\x55\x5F\xAD\x29\xA2\x81\x71\xA4\x27\x0\x7F\xAB\xFA\xAC\x30\x5B\xB9\xBA\x9\x25\xD1\x41\x2E\x98\x8F\x5\x81\x6F\xEF\x4E\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x27\xE0\xAA\xB6\xFD\xC9\x58\x3B\x90\x3F\x72\x0\x41\x13\xFA\xA2\x10\x54\x46\xBA\xAA\xAD\xD3\xAA\xAE\xA5\xDB\x5\xFB\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x21\x8\xAA\x9D\xC5\xCB\x18\xFB\xA5\xF1\x73\x0\x71\x13\xFB\xA2\x10\x54\x46\xBA\xEA\xAD\xD2\xAA\xAE\xAD\xDB\x5\xBB\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x21\xAD\xAA\xBA\xAD\xC8\x48\xFB\xA1\x3F\x73\x0\x61\x13\xFA\x22\x10\x54\x46\xBA\xAA\xAD\xD2\xAA\xAE\xA5\xDB\x5\x7B\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x21\x42\xAA\x86\xD5\xCA\xF8\xBB\x85\x37\x73\x0\x51\x13\xFA\x22\x10\x58\x46\xBA\x8A\xAD\xD0\xAA\xAE\xBD\xDB\x5\x7B\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x21\xE7\xAA\xA1\xBD\xC9\xA8\xBB\x81\xF9\x73\x0\x41\x13\xFB\xA2\x10\x58\x46\xBA\xCA\xAD\xD0\xAA\xAE\xB5\xDB\x5\xBB\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB5\x6D\xAA\xAA\x94\xEF\xE1\x5F\x27\xB3\x73\x0\x53\x5B\xFA\x6B\x30\x48\x46\xBA\x6E\x3D\xD7\x38\xEE\x95\xDB\x5\x32\xAF\xEE\x52\x0",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB5\xC8\xAA\x8D\xFC\xEC\xB1\x5F\x23\x7D\x73\x0\x43\x5B\xFB\xEB\x30\x48\x46\xBA\x2E\x3D\xD7\x38\xEE\x9D\xDB\x5\xF2\xAF\xE8\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x25\x27\xAA\xA3\xCD\xCA\x48\x3B\x97\x75\x73\x0\x61\x13\xFB\xA2\x10\x54\x46\xBA\xAA\xAD\xD1\xAA\xAE\xA5\xDB\x5\xBB\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x25\x82\xAA\x84\xA5\xC9\x18\x3B\x93\xBB\x73\x0\x71\x13\xFA\x22\x10\x54\x46\xBA\xEA\xAD\xD1\xAA\xAE\xAD\xDB\x5\x7B\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x21\xCE\xAA\xBE\xF5\xCA\x12\x3B\xAA\x37\x72\x80\x41\x1F\xFB\x22\x10\x54\x46\xBA\xCA\xAD\xD1\xAA\xAE\xAD\xDB\x5\xFB\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB1\x6B\xAA\x8B\xD4\xED\xB\x1F\x3E\xF9\x72\x80\x43\x57\xFA\xEB\x30\x44\x46\xBA\x2E\x3D\xD5\x38\xEE\x85\xDB\x5\x72\xAF\xEA\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x21\x84\xAA\xA5\xE5\xCB\xF2\x7B\x8A\xF1\x72\x80\x61\x1F\xFA\xA2\x10\x58\x46\xBA\xAA\xAD\xD3\xAA\xAE\xBD\xDB\x5\x3B\x8F\xEF\x76\x1",
+ "\xFE\x4B\xFC\x14\x90\x6E\x90\xBB\x75\x25\xDB\xA3\xAE\xC1\x75\x7\xFA\xAF\xE0\x7\x0\xFB\xCD\x57\x9\x49\xC\xB5\x2B\x28\x43\x6D\xB1\x4A\x0\x7F\x27\xFA\xAC\x30\x4B\xC8\xBA\x89\x25\xD7\x49\x2E\xAD\x39\x5\xC3\x6F\xEF\x4E\x0",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB5\xAB\xAA\x89\xA4\xEE\xEB\x9F\x28\x75\x72\x80\x63\x57\xFA\xEB\x30\x48\x46\xBA\x4E\x3D\xD4\x38\xEE\x95\xDB\x5\x72\xAF\xEC\x52\x0",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB5\xE\xAA\xAE\xCC\xED\xBB\x9F\x2C\xBB\x72\x80\x73\x57\xFB\x6B\x30\x48\x46\xBA\xE\x3D\xD4\x38\xEE\x9D\xDB\x5\xB2\xAF\xEA\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x25\xE1\xAA\x80\xFD\xCB\x42\xFB\x98\xB3\x72\x80\x51\x1F\xFB\x22\x10\x54\x46\xBA\x8A\xAD\xD2\xAA\xAE\xA5\xDB\x5\xFB\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x25\x44\xAA\xA7\x95\xC8\x12\xFB\x9C\x7D\x72\x80\x41\x1F\xFA\xA2\x10\x54\x46\xBA\xCA\xAD\xD2\xAA\xAE\xAD\xDB\x5\x3B\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x23\xAC\xAA\x8C\xAD\xCA\x52\x3B\xA9\xB3\x73\x80\x71\x1F\xFB\xA2\x10\x54\x46\xBA\x8A\xAD\xD3\xAA\xAE\xA5\xDB\x5\x7B\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x23\x9\xAA\xAB\xC5\xC9\x2\x3B\xAD\x7D\x73\x80\x61\x1F\xFA\x22\x10\x54\x46\xBA\xCA\xAD\xD3\xAA\xAE\xAD\xDB\x5\xBB\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x23\xE6\xAA\x97\xBD\xCB\xB2\x7B\x89\x75\x73\x80\x51\x1F\xFA\x22\x10\x58\x46\xBA\xEA\xAD\xD1\xAA\xAE\xB5\xDB\x5\xBB\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x23\x43\xAA\xB0\xD5\xC8\xE2\x7B\x8D\xBB\x73\x80\x41\x1F\xFB\xA2\x10\x58\x46\xBA\xAA\xAD\xD1\xAA\xAE\xBD\xDB\x5\x7B\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x27\xC9\xAA\xA9\xB5\xCA\xE2\xBB\xBB\xF1\x73\x80\x41\x1F\xFA\x22\x10\x58\x46\xBA\xAA\xAD\xD2\xAA\xAE\xBD\xDB\x5\xBB\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x27\x6C\xAA\x8E\xDD\xC9\xB2\xBB\xBF\x3F\x73\x80\x51\x1F\xFB\xA2\x10\x58\x46\xBA\xEA\xAD\xD2\xAA\xAE\xB5\xDB\x5\x7B\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x27\x83\xAA\xB2\xA5\xCB\x2\xFB\x9B\x37\x73\x80\x61\x1F\xFB\xA2\x10\x54\x46\xBA\xCA\xAD\xD0\xAA\xAE\xAD\xDB\x5\x7B\x8F\xEB\x76\x1",
+ "\xFE\xFB\xFC\x12\x90\x6E\x98\xBB\x74\xE5\xDB\xA2\xAE\xC1\xD\x7\xFA\xAF\xE0\x1F\x0\xDA\x6A\x8\xC9\x55\x52\xD1\xB9\xE0\x20\xFA\xAC\x26\x80\x5C\xAB\xF9\x3E\x70\x4B\xB9\xBA\xFB\x6D\xD7\xC7\x2E\x90\x8F\x5\xD\x5F\xE9\x7\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x24\xED\xAA\x81\xDD\xCB\xEA\x3B\xA3\xF5\x72\x0\x71\x1F\xFA\x22\x10\x58\x46\xBA\xAA\xAD\xD2\xAA\xAE\xAD\xDB\x5\xBB\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x24\x48\xAA\xA6\xB5\xC8\xBA\x3B\xA7\x3B\x72\x0\x61\x1F\xFB\xA2\x10\x58\x46\xBA\xEA\xAD\xD2\xAA\xAE\xA5\xDB\x5\x7B\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x24\xA7\xAA\x9A\xCD\xCA\xA\x7B\x83\x33\x72\x0\x51\x1F\xFB\xA2\x10\x54\x46\xBA\xCA\xAD\xD0\xAA\xAE\xBD\xDB\x5\x7B\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB4\x2\xAA\xAF\xEC\xED\x13\x5F\x17\xFD\x72\x0\x53\x57\xFA\x6B\x30\x44\x46\xBA\x2E\x3D\xD4\x38\xEE\x95\xDB\x5\xF2\xAF\xEA\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x20\x88\xAA\xA4\xC5\xCB\x5A\xBB\xB1\xB7\x72\x0\x41\x1F\xFB\xA2\x10\x54\x46\xBA\x8A\xAD\xD3\xAA\xAE\xB5\xDB\x5\x7B\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x20\x2D\xAA\x83\xAD\xC8\xA\xBB\xB5\x79\x72\x0\x51\x1F\xFA\x22\x10\x54\x46\xBA\xCA\xAD\xD3\xAA\xAE\xBD\xDB\x5\xBB\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB0\xC2\xAA\xAD\x9C\xEE\xF3\xDF\x1\x71\x72\x0\x73\x57\xFA\x6B\x30\x48\x46\xBA\x4E\x3D\xD5\x38\xEE\x85\xDB\x5\xF2\xAF\xEC\x52\x0",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB0\x67\xAA\x8A\xF4\xED\xA3\xDF\x5\xBF\x72\x0\x63\x57\xFB\xEB\x30\x48\x46\xBA\xE\x3D\xD5\x38\xEE\x8D\xDB\x5\x32\xAF\xEA\x52\x0",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB6\x8F\xAA\xA1\xCC\xEF\xE3\x1F\x30\x71\x73\x0\x53\x57\xFA\xEB\x30\x48\x46\xBA\x4E\x3D\xD4\x38\xEE\x85\xDB\x5\x72\xAF\xEA\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x26\x2A\xAA\x94\xED\xC8\xFA\x3B\xA4\xBF\x73\x0\x51\x1F\xFB\x22\x10\x58\x46\xBA\xAA\xAD\xD0\xAA\xAE\xAD\xDB\x5\xFB\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x26\xC5\xAA\xA8\x95\xCA\x4A\x7B\x80\xB7\x73\x0\x61\x1F\xFB\x22\x10\x54\x46\xBA\x8A\xAD\xD2\xAA\xAE\xB5\xDB\x5\xFB\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x26\x60\xAA\x8F\xFD\xC9\x1A\x7B\x84\x79\x73\x0\x71\x1F\xFA\xA2\x10\x54\x46\xBA\xCA\xAD\xD2\xAA\xAE\xBD\xDB\x5\x3B\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x22\xEA\xAA\x96\x9D\xCB\x1A\xBB\xB2\x33\x73\x0\x71\x1F\xFB\x22\x10\x54\x46\xBA\xCA\xAD\xD1\xAA\xAE\xBD\xDB\x5\xFB\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB2\x4F\xAA\xA3\xBC\xEC\x3\x9F\x26\xFD\x73\x0\x73\x57\xFA\xEB\x30\x44\x46\xBA\x2E\x3D\xD5\x38\xEE\x95\xDB\x5\x72\xAF\xEC\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x22\xA0\xAA\x8D\x8D\xCA\xFA\xFB\x92\xF5\x73\x0\x51\x1F\xFA\xA2\x10\x58\x46\xBA\xAA\xAD\xD3\xAA\xAE\xAD\xDB\x5\x3B\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x22\x5\xAA\xAA\xE5\xC9\xAA\xFB\x96\x3B\x73\x0\x41\x1F\xFB\x22\x10\x58\x46\xBA\xEA\xAD\xD3\xAA\xAE\xA5\xDB\x5\xFB\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x22\xA4\xAA\x85\xD5\xCB\xB9\xBB\xA0\x2F\x72\x80\x51\x17\xFA\xA2\x10\x58\x46\xBA\xAA\xAD\xD1\xAA\xAE\xA5\xDB\x5\xFB\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x22\x1\xAA\xA2\xBD\xC8\xE9\xBB\xA4\xE1\x72\x80\x41\x17\xFB\x22\x10\x58\x46\xBA\xEA\xAD\xD1\xAA\xAE\xAD\xDB\x5\x3B\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x22\xEE\xAA\x9E\xC5\xCA\x59\xFB\x80\xE9\x72\x80\x71\x17\xFB\x22\x10\x54\x46\xBA\xCA\xAD\xD3\xAA\xAE\xB5\xDB\x5\x3B\x8F\xE9\x76\x1",
+ "\xFE\x8B\xFC\x16\x90\x6E\xBE\xBB\x75\x55\xDB\xA7\xAE\xC1\x55\x7\xFA\xAF\xE0\x0\x0\xCE\x9\x7F\xED\x71\xD4\x9B\x13\x9B\xB2\xA0\xB5\x3B\x80\x4C\xA3\xF9\xCF\xB0\x50\xD4\xBA\xAE\x3D\xD1\x71\xEE\x8B\x1\x5\xB2\xAF\xED\x3F\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB5\x27\xAA\xA4\xCC\xEA\xB2\xDF\x2D\x23\x73\x80\x73\x53\xFB\xEB\x70\x48\x46\xBA\x6E\x3D\xD4\x38\xEE\x8D\xDB\x5\xF2\xAF\xEE\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x23\x64\xAA\x3D\xBD\xCE\x79\x3B\xB3\x79\x73\x80\x71\x1B\xFA\x22\x50\x5C\x46\xBA\xAA\xAD\xD2\xAA\xAE\xA5\xDB\x5\xBB\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x27\x4C\xAA\xF3\xED\xCE\x6A\xFB\xB0\x2F\x73\x80\x41\x1B\xFB\x22\x50\x5C\x46\xBA\xAA\xAD\xD3\xAA\xAE\xAD\xDB\x5\xBB\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB1\xF\xAA\x6A\x9C\xEA\xA1\x1F\x2E\x75\x73\x80\x43\x53\xFA\xEB\x70\x48\x46\xBA\x6E\x3D\xD5\x38\xEE\x85\xDB\x5\xF2\xAF\xE8\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x24\xED\xAA\x9F\xBD\xC9\x3B\x3B\xB5\x7B\x73\x80\x41\x1B\xFB\x22\x50\x50\x46\xBA\xAA\xAD\xD1\xAA\xAE\xB5\xDB\x5\x3B\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x22\xAE\xAA\x14\x85\xC9\xB9\xFB\xBB\x21\x73\x80\x51\x1B\xFA\xA2\x50\x54\x46\xBA\xCA\xAD\xD3\xAA\xAE\xBD\xDB\x5\x3B\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x26\x86\xAA\xDA\xD5\xC9\xAA\x3B\xB8\x77\x73\x80\x61\x1B\xFB\xA2\x50\x54\x46\xBA\xCA\xAD\xD2\xAA\xAE\xB5\xDB\x5\x3B\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x20\xC5\xAA\x51\xED\xC9\x28\xFB\xB6\x2D\x73\x80\x71\x1B\xFA\x22\x50\x50\x46\xBA\xAA\xAD\xD0\xAA\xAE\xBD\xDB\x5\x3B\x8F\xEB\x76\x1",
+ "\xFE\x4B\xFC\x14\x90\x6E\x90\xBB\x75\x25\xDB\xA3\xAE\xC1\x75\x7\xFA\xAF\xE0\x7\x0\xFB\xCD\x53\xAA\x49\x1F\xD5\x2E\x21\xC3\x5A\x63\x4B\x0\x7F\x23\xFB\xAC\x70\x4B\xC8\xBA\xE9\x25\xD4\x49\x2E\xBD\x39\x5\x43\x6F\xEF\x4E\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x23\xC1\xAA\x1A\xD5\xCD\x29\x3B\xB7\xB7\x73\x80\x61\x1B\xFB\xA2\x50\x5C\x46\xBA\xEA\xAD\xD2\xAA\xAE\xAD\xDB\x5\x7B\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB7\xE9\xAA\xC6\xCC\xE9\x73\xDF\x24\xE1\x73\x80\x43\x53\xFA\xEB\x70\x4C\x46\xBA\x4E\x3D\xD7\x38\xEE\x85\xDB\x5\x32\xAF\xEA\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x21\xAA\xAA\x5F\xBD\xCD\xB8\x3B\xBA\xBB\x73\x80\x41\x1B\xFB\x22\x50\x58\x46\xBA\x8A\xAD\xD1\xAA\xAE\xAD\xDB\x5\x7B\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x24\x2\xAA\xA3\xC5\xCB\x8B\x7B\x91\x73\x73\x80\x71\x1B\xFB\x22\x50\x5C\x46\xBA\x8A\xAD\xD3\xAA\xAE\xAD\xDB\x5\x3B\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x22\x41\xAA\x28\xFD\xCB\x9\xBB\x9F\x29\x73\x80\x61\x1B\xFA\xA2\x50\x58\x46\xBA\xEA\xAD\xD1\xAA\xAE\xA5\xDB\x5\x3B\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x26\x69\xAA\xE6\xAD\xCB\x1A\x7B\x9C\x7F\x73\x80\x51\x1B\xFB\xA2\x50\x58\x46\xBA\xEA\xAD\xD0\xAA\xAE\xAD\xDB\x5\x3B\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x20\x2A\xAA\x6D\x95\xCB\x98\xBB\x92\x25\x73\x80\x41\x1B\xFA\x22\x50\x5C\x46\xBA\x8A\xAD\xD2\xAA\xAE\xA5\xDB\x5\x3B\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x25\x6D\xAA\xAD\x95\xCF\x1B\xBB\x9D\xE5\x73\x80\x41\x1B\xFA\x22\x50\x54\x46\xBA\xAA\xAD\xD2\xAA\xAE\xBD\xDB\x5\x7B\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x23\x2E\xAA\x26\xAD\xCF\x99\x7B\x93\xBF\x73\x80\x51\x1B\xFB\xA2\x50\x50\x46\xBA\xCA\xAD\xD0\xAA\xAE\xB5\xDB\x5\x7B\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x27\x6\xAA\xE8\xFD\xCF\x8A\xBB\x90\xE9\x73\x80\x61\x1B\xFA\xA2\x50\x50\x46\xBA\xCA\xAD\xD1\xAA\xAE\xBD\xDB\x5\x7B\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x21\x45\xAA\x63\xC5\xCF\x8\x7B\x9E\xB3\x73\x80\x71\x1B\xFB\x22\x50\x54\x46\xBA\xAA\xAD\xD3\xAA\xAE\xB5\xDB\x5\x7B\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x24\xA7\xAA\x84\xAD\xC8\xDB\x7B\x95\xBD\x73\x80\x61\x1B\xFA\xA2\x50\x5C\x46\xBA\xCA\xAD\xD3\xAA\xAE\xA5\xDB\x5\xFB\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB2\xE4\xAA\x1D\xDC\xEC\x10\x9F\xB\xE7\x73\x80\x63\x53\xFB\x6B\x70\x48\x46\xBA\xE\x3D\xD5\x38\xEE\x8D\xDB\x5\xB2\xAF\xEA\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x26\xCC\xAA\xC1\xC5\xC8\x4A\x7B\x98\xB1\x73\x80\x41\x1B\xFA\x22\x50\x58\x46\xBA\xAA\xAD\xD0\xAA\xAE\xA5\xDB\x5\xFB\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x20\x8F\xAA\x4A\xFD\xC8\xC8\xBB\x96\xEB\x73\x80\x51\x1B\xFB\xA2\x50\x5C\x46\xBA\xCA\xAD\xD2\xAA\xAE\xAD\xDB\x5\xFB\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x25\xC8\xAA\x8A\xFD\xCC\x4B\xBB\x99\x2B\x73\x80\x51\x1B\xFB\xA2\x50\x54\x46\xBA\xEA\xAD\xD2\xAA\xAE\xB5\xDB\x5\xBB\x8F\xEB\x76\x1",
+ "\xFE\x8B\xFC\x16\x90\x6E\xBE\xBB\x75\x55\xDB\xA7\xAE\xC1\x55\x7\xFA\xAF\xE0\x0\x0\xCE\x9\x7E\x2D\x71\x6C\xF3\x16\x5B\x32\xB3\xE3\x3A\x80\x6C\xAF\xF9\x4F\xF0\x54\xD4\xBA\xAE\x3D\xD2\x71\xEE\x8B\x1\x5\xF2\xAF\xED\x3F\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x27\xA3\xAA\xCF\x95\xCC\xDA\xBB\x94\x27\x73\x80\x71\x1B\xFB\x22\x50\x50\x46\xBA\x8A\xAD\xD1\xAA\xAE\xB5\xDB\x5\xBB\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x21\xE0\xAA\x44\xAD\xCC\x58\x7B\x9A\x7D\x73\x80\x61\x1B\xFA\xA2\x50\x54\x46\xBA\xEA\xAD\xD3\xAA\xAE\xBD\xDB\x5\xBB\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB7\x6C\xAA\x82\xF4\xEF\x2A\x9F\x39\xB1\x72\x0\x73\x53\xFA\xEB\x70\x40\x46\xBA\x4E\x3D\xD5\x38\xEE\x8D\xDB\x5\xB2\xAF\xEC\x52\x0",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB1\x2F\xAA\x9\xCC\xEF\xA8\x5F\x37\xEB\x72\x0\x63\x53\xFB\x6B\x70\x44\x46\xBA\x2E\x3D\xD7\x38\xEE\x85\xDB\x5\xB2\xAF\xE8\x52\x0",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB5\x7\xAA\xC7\x9C\xEF\xBB\x9F\x34\xBD\x72\x0\x53\x53\xFA\x6B\x70\x44\x46\xBA\x2E\x3D\xD6\x38\xEE\x8D\xDB\x5\xB2\xAF\xEE\x52\x0",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB3\x44\xAA\x4C\xA4\xEF\x39\x5F\x3A\xE7\x72\x0\x43\x53\xFB\xEB\x70\x40\x46\xBA\x4E\x3D\xD4\x38\xEE\x85\xDB\x5\xB2\xAF\xEA\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x26\x3\xAA\x9E\xED\xCF\xF3\x7B\xA5\x27\x72\x0\x51\x1B\xFB\xA2\x50\x58\x46\xBA\xCA\xAD\xD0\xAA\xAE\xBD\xDB\x5\xBB\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x20\x40\xAA\x15\xD5\xCF\x71\xBB\xAB\x7D\x72\x0\x41\x1B\xFA\x22\x50\x5C\x46\xBA\xAA\xAD\xD2\xAA\xAE\xB5\xDB\x5\xBB\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x24\x68\xAA\xDB\x85\xCF\x62\x7B\xA8\x2B\x72\x0\x71\x1B\xFB\x22\x50\x5C\x46\xBA\xAA\xAD\xD3\xAA\xAE\xBD\xDB\x5\xBB\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x22\x2B\xAA\x50\xBD\xCF\xE0\xBB\xA6\x71\x72\x0\x61\x1B\xFA\xA2\x50\x58\x46\xBA\xCA\xAD\xD1\xAA\xAE\xB5\xDB\x5\xBB\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB7\xC9\xAA\xA5\x9C\xEC\x7A\x9F\x3D\x7F\x72\x0\x63\x53\xFB\x6B\x70\x40\x46\xBA\xE\x3D\xD5\x38\xEE\x85\xDB\x5\x72\xAF\xEA\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x21\x8A\xAA\x3C\xED\xC8\xB1\x7B\xA3\x25\x72\x0\x61\x1B\xFA\xA2\x50\x54\x46\xBA\xCA\xAD\xD3\xAA\xAE\xAD\xDB\x5\x3B\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x25\xA2\xAA\xF2\xBD\xC8\xA2\xBB\xA0\x73\x72\x0\x51\x1B\xFB\xA2\x50\x54\x46\xBA\xCA\xAD\xD2\xAA\xAE\xA5\xDB\x5\x3B\x8F\xE9\x76\x1",
+ "\xFE\x8B\xFC\x16\x90\x6E\xBE\xBB\x75\x55\xDB\xA7\xAE\xC1\x55\x7\xFA\xAF\xE0\x0\x0\xCE\x9\x7E\x47\x71\x14\xB3\x12\xB2\x32\x8A\xBB\x3B\x0\x6C\xAF\xF9\x4F\xF0\x54\xD4\xBA\x8E\x3D\xD2\x71\xEE\x9B\x1\x5\x72\xAF\xEF\x3F\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB6\xA6\xAA\xAB\xCC\xE8\xEA\x5F\x31\xE9\x72\x0\x53\x53\xFA\x6B\x70\x48\x46\xBA\x2E\x3D\xD4\x38\xEE\x95\xDB\x5\x32\xAF\xEE\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x20\xE5\xAA\x32\xBD\xCC\x21\xBB\xAF\xB3\x72\x0\x51\x1B\xFB\xA2\x50\x5C\x46\xBA\xEA\xAD\xD2\xAA\xAE\xBD\xDB\x5\x7B\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x24\xCD\xAA\xFC\xED\xCC\x32\x7B\xAC\xE5\x72\x0\x61\x1B\xFA\xA2\x50\x5C\x46\xBA\xEA\xAD\xD3\xAA\xAE\xB5\xDB\x5\x7B\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x22\x8E\xAA\x77\xD5\xCC\xB0\xBB\xA2\xBF\x72\x0\x71\x1B\xFB\x22\x50\x58\x46\xBA\x8A\xAD\xD1\xAA\xAE\xBD\xDB\x5\x7B\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB7\x26\xAA\x99\xE4\xEE\xCA\xDF\x19\x77\x72\x0\x53\x53\xFB\x6B\x70\x4C\x46\xBA\x2E\x3D\xD7\x38\xEE\x9D\xDB\x5\x72\xAF\xEE\x52\x0",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB1\x65\xAA\x12\xDC\xEE\x48\x1F\x17\x2D\x72\x0\x43\x53\xFA\xEB\x70\x48\x46\xBA\x4E\x3D\xD5\x38\xEE\x95\xDB\x5\x72\xAF\xEA\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x25\x4D\xAA\xCE\xC5\xCA\x12\xFB\x84\x7B\x72\x0\x61\x1B\xFB\xA2\x50\x58\x46\xBA\xEA\xAD\xD0\xAA\xAE\xBD\xDB\x5\x3B\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x23\xE\xAA\x45\xFD\xCA\x90\x3B\x8A\x21\x72\x0\x71\x1B\xFA\x22\x50\x5C\x46\xBA\x8A\xAD\xD2\xAA\xAE\xB5\xDB\x5\x3B\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x26\x49\xAA\x85\xFD\xCE\x13\x3B\x85\xE1\x72\x0\x71\x1B\xFA\x22\x50\x54\x46\xBA\xAA\xAD\xD2\xAA\xAE\xAD\xDB\x5\x7B\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x20\xA\xAA\xE\xC5\xCE\x91\xFB\x8B\xBB\x72\x0\x61\x1B\xFB\xA2\x50\x50\x46\xBA\xCA\xAD\xD0\xAA\xAE\xA5\xDB\x5\x7B\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB4\x22\xAA\xD2\xDC\xEA\xCB\x1F\x18\xED\x72\x0\x43\x53\xFA\xEB\x70\x40\x46\xBA\x6E\x3D\xD5\x38\xEE\x8D\xDB\x5\x32\xAF\xE8\x52\x0",
+ "\xFE\x4B\xFC\x14\x90\x6E\x90\xBB\x75\x25\xDB\xA3\xAE\xC1\x75\x7\xFA\xAF\xE0\x7\x0\xFB\xCD\x54\x49\x49\xC5\x95\x2D\x8A\xC3\x65\x39\x4A\x80\x4F\x23\xFA\xAC\x70\x47\xC8\xBA\xC9\x25\xD7\x49\x2E\xBD\x39\x5\x43\x6F\xEB\x4E\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x27\x83\xAA\xAC\xC5\xC9\xD3\xFB\x8D\xB9\x72\x0\x51\x1B\xFA\xA2\x50\x5C\x46\xBA\xCA\xAD\xD3\xAA\xAE\xB5\xDB\x5\xFB\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB1\xC0\xAA\x35\xB4\xED\x18\x1F\x13\xE3\x72\x0\x53\x53\xFB\x6B\x70\x48\x46\xBA\xE\x3D\xD5\x38\xEE\x9D\xDB\x5\xB2\xAF\xEC\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x25\xE8\xAA\xE9\xAD\xC9\x42\xFB\x80\xB5\x72\x0\x71\x1B\xFA\x22\x50\x58\x46\xBA\xAA\xAD\xD0\xAA\xAE\xB5\xDB\x5\xFB\x8F\xEB\x76\x1",
+ "\xFE\x4B\xFC\x14\x90\x6E\x90\xBB\x75\x25\xDB\xA3\xAE\xC1\x75\x7\xFA\xAF\xE0\x7\x0\xFB\xCD\x55\x83\x49\xEC\xAD\x2A\x4A\x3\x6D\x61\x4A\x80\x6F\x23\xFA\x2C\x70\x4F\xC8\xBA\xA9\x25\xD6\x49\x2E\xA5\x39\x5\xC3\x6F\xE9\x4E\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x26\xEC\xAA\xA2\x95\xCD\x43\x3B\x81\x2F\x72\x0\x61\x1B\xFB\xA2\x50\x54\x46\xBA\xEA\xAD\xD2\xAA\xAE\xA5\xDB\x5\xBB\x8F\xED\x76\x1",
+ "\xFE\x4B\xFC\x14\x90\x6E\x90\xBB\x75\x25\xDB\xA3\xAE\xC1\x75\x7\xFA\xAF\xE0\x7\x0\xFB\xCD\x56\x87\x49\xA7\x95\x2E\x4B\xC3\x6C\xFB\x4A\x80\x7F\x23\xFB\xAC\x70\x43\xC8\xBA\xE9\x25\xD4\x49\x2E\xB5\x39\x5\x83\x6F\xEF\x4E\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x24\x87\xAA\xE7\xFD\xCD\xD2\x3B\x8C\x23\x72\x0\x41\x1B\xFB\x22\x50\x50\x46\xBA\x8A\xAD\xD1\xAA\xAE\xA5\xDB\x5\xBB\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x22\xC4\xAA\x6C\xC5\xCD\x50\xFB\x82\x79\x72\x0\x51\x1B\xFA\xA2\x50\x54\x46\xBA\xEA\xAD\xD3\xAA\xAE\xAD\xDB\x5\xBB\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x23\x9\xAA\xB5\xA5\xCB\xD3\x3B\xBB\xF3\x72\x0\x51\x1B\xFB\x22\x50\x5C\x46\xBA\xCA\xAD\xD0\xAA\xAE\xB5\xDB\x5\x3B\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB5\x4A\xAA\x2C\xD4\xEF\x18\xDF\x25\xA9\x72\x0\x53\x53\xFA\xEB\x70\x48\x46\xBA\xE\x3D\xD6\x38\xEE\x9D\xDB\x5\x72\xAF\xE8\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x21\x62\xAA\xF0\xCD\xCB\x42\x3B\xB6\xFF\x72\x0\x71\x1B\xFB\xA2\x50\x58\x46\xBA\xAA\xAD\xD3\xAA\xAE\xB5\xDB\x5\x3B\x8F\xEF\x76\x1",
+ "\xFE\x4B\xFC\x14\x90\x6E\x90\xBB\x75\x25\xDB\xA3\xAE\xC1\x75\x7\xFA\xAF\xE0\x7\x0\xFB\xCD\x51\x9\x49\xF5\xCD\x28\x4A\xC3\x5B\x2B\x4A\x80\x6F\x23\xFB\xAC\x70\x4F\xC8\xBA\xA9\x25\xD5\x49\x2E\xA5\x39\x5\x3\x6F\xED\x4E\x0",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB2\x66\xAA\xA9\xBC\xEB\xA\xDF\x27\x65\x72\x0\x73\x53\xFA\x6B\x70\x44\x46\xBA\x4E\x3D\xD5\x38\xEE\x85\xDB\x5\x32\xAF\xE8\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x24\x25\xAA\x30\xCD\xCF\xC1\x3B\xB9\x3F\x72\x0\x71\x1B\xFB\xA2\x50\x50\x46\xBA\x8A\xAD\xD3\xAA\xAE\xAD\xDB\x5\x7B\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x20\xD\xAA\xFE\x9D\xCF\xD2\xFB\xBA\x69\x72\x0\x41\x1B\xFA\xA2\x50\x50\x46\xBA\x8A\xAD\xD2\xAA\xAE\xA5\xDB\x5\x7B\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB6\x4E\xAA\x67\xEC\xEB\x19\x1F\x24\x33\x72\x0\x43\x53\xFB\x6B\x70\x44\x46\xBA\x4E\x3D\xD4\x38\xEE\x8D\xDB\x5\x32\xAF\xEE\x52\x0",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB3\xAC\xAA\x80\x84\xEC\xCA\x1F\x2F\x3D\x72\x0\x53\x53\xFA\xEB\x70\x4C\x46\xBA\x2E\x3D\xD4\x38\xEE\x9D\xDB\x5\xB2\xAF\xEA\x52\x0",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB5\xEF\xAA\xB\xBC\xEC\x48\xDF\x21\x67\x72\x0\x43\x53\xFB\x6B\x70\x48\x46\xBA\x4E\x3D\xD6\x38\xEE\x95\xDB\x5\xB2\xAF\xEE\x52\x0",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB1\xC7\xAA\xC5\xEC\xEC\x5B\x1F\x22\x31\x72\x0\x73\x53\xFA\x6B\x70\x48\x46\xBA\x4E\x3D\xD7\x38\xEE\x9D\xDB\x5\xB2\xAF\xE8\x52\x0",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB7\x84\xAA\x4E\xD4\xEC\xD9\xDF\x2C\x6B\x72\x0\x63\x53\xFB\xEB\x70\x4C\x46\xBA\x2E\x3D\xD5\x38\xEE\x95\xDB\x5\xB2\xAF\xEC\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x22\xC3\xAA\x9C\x9D\xCC\x13\xFB\xB3\xAB\x72\x0\x71\x1B\xFB\xA2\x50\x54\x46\xBA\xAA\xAD\xD1\xAA\xAE\xAD\xDB\x5\xBB\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x24\x80\xAA\x17\xA5\xCC\x91\x3B\xBD\xF1\x72\x0\x61\x1B\xFA\x22\x50\x50\x46\xBA\xCA\xAD\xD3\xAA\xAE\xA5\xDB\x5\xBB\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x20\xA8\xAA\xD9\xF5\xCC\x82\xFB\xBE\xA7\x72\x0\x51\x1B\xFB\x22\x50\x50\x46\xBA\xCA\xAD\xD2\xAA\xAE\xAD\xDB\x5\xBB\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x26\xEB\xAA\x52\xCD\xCC\x0\x3B\xB0\xFD\x72\x0\x41\x1B\xFA\xA2\x50\x54\x46\xBA\xAA\xAD\xD0\xAA\xAE\xA5\xDB\x5\xBB\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x23\x43\xAA\xAE\xB5\xCA\x33\x7B\x9B\x35\x72\x0\x71\x1B\xFA\xA2\x50\x50\x46\xBA\xAA\xAD\xD2\xAA\xAE\xA5\xDB\x5\xFB\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x25\x0\xAA\x25\x8D\xCA\xB1\xBB\x95\x6F\x72\x0\x61\x1B\xFB\x22\x50\x54\x46\xBA\xCA\xAD\xD0\xAA\xAE\xAD\xDB\x5\xFB\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x21\x28\xAA\xEB\xDD\xCA\xA2\x7B\x96\x39\x72\x0\x51\x1B\xFA\x22\x50\x54\x46\xBA\xCA\xAD\xD1\xAA\xAE\xA5\xDB\x5\xFB\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x27\x6B\xAA\x60\xE5\xCA\x20\xBB\x98\x63\x72\x0\x41\x1B\xFB\xA2\x50\x50\x46\xBA\xAA\xAD\xD3\xAA\xAE\xAD\xDB\x5\xFB\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x22\x2C\xAA\xA0\xE5\xCE\xA3\xBB\x97\xA3\x72\x0\x41\x1B\xFB\xA2\x50\x58\x46\xBA\x8A\xAD\xD3\xAA\xAE\xB5\xDB\x5\xBB\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB4\x6F\xAA\x39\x94\xEA\x68\x5F\x9\xF9\x72\x0\x43\x53\xFA\x6B\x70\x4C\x46\xBA\x4E\x3D\xD5\x38\xEE\x9D\xDB\x5\xF2\xAF\xEE\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x20\x47\xAA\xE5\x8D\xCE\x32\xBB\x9A\xAF\x72\x0\x61\x1B\xFB\x22\x50\x5C\x46\xBA\xEA\xAD\xD0\xAA\xAE\xB5\xDB\x5\xBB\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x26\x4\xAA\x6E\xB5\xCE\xB0\x7B\x94\xF5\x72\x0\x71\x1B\xFA\xA2\x50\x58\x46\xBA\x8A\xAD\xD2\xAA\xAE\xBD\xDB\x5\xBB\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x23\xE6\xAA\x89\xDD\xC9\x63\x7B\x9F\xFB\x72\x0\x61\x1B\xFB\x22\x50\x50\x46\xBA\xEA\xAD\xD2\xAA\xAE\xAD\xDB\x5\x3B\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x25\xA5\xAA\x2\xE5\xC9\xE1\xBB\x91\xA1\x72\x0\x71\x1B\xFA\xA2\x50\x54\x46\xBA\x8A\xAD\xD0\xAA\xAE\xA5\xDB\x5\x3B\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x21\x8D\xAA\xCC\xB5\xC9\xF2\x7B\x92\xF7\x72\x0\x41\x1B\xFB\xA2\x50\x54\x46\xBA\x8A\xAD\xD1\xAA\xAE\xAD\xDB\x5\x3B\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x27\xCE\xAA\x47\x8D\xC9\x70\xBB\x9C\xAD\x72\x0\x51\x1B\xFA\x22\x50\x50\x46\xBA\xEA\xAD\xD3\xAA\xAE\xA5\xDB\x5\x3B\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x22\x89\xAA\x87\x8D\xCD\xF3\xBB\x93\x6D\x72\x0\x51\x1B\xFA\x22\x50\x58\x46\xBA\xCA\xAD\xD3\xAA\xAE\xBD\xDB\x5\x7B\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x24\xCA\xAA\xC\xB5\xCD\x71\x7B\x9D\x37\x72\x0\x41\x1B\xFB\xA2\x50\x5C\x46\xBA\xAA\xAD\xD1\xAA\xAE\xB5\xDB\x5\x7B\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x20\xE2\xAA\xC2\xE5\xCD\x62\xBB\x9E\x61\x72\x0\x71\x1B\xFA\xA2\x50\x5C\x46\xBA\xAA\xAD\xD0\xAA\xAE\xBD\xDB\x5\x7B\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x26\xA1\xAA\x49\xDD\xCD\xE0\x7B\x90\x3B\x72\x0\x61\x1B\xFB\x22\x50\x58\x46\xBA\xCA\xAD\xD2\xAA\xAE\xB5\xDB\x5\x7B\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x25\xE\xAA\xA2\xE5\xCB\x23\xBB\xAA\x35\x73\x0\x51\x1B\xFA\x22\x50\x50\x46\xBA\xAA\xAD\xD3\xAA\xAE\xA5\xDB\x5\x7B\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x23\x4D\xAA\x29\xDD\xCB\xA1\x7B\xA4\x6F\x73\x0\x41\x1B\xFB\xA2\x50\x54\x46\xBA\xCA\xAD\xD1\xAA\xAE\xAD\xDB\x5\x7B\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x27\x65\xAA\xE7\x8D\xCB\xB2\xBB\xA7\x39\x73\x0\x71\x1B\xFA\xA2\x50\x54\x46\xBA\xCA\xAD\xD0\xAA\xAE\xA5\xDB\x5\x7B\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x21\x26\xAA\x6C\xB5\xCB\x30\x7B\xA9\x63\x73\x0\x61\x1B\xFB\x22\x50\x50\x46\xBA\xAA\xAD\xD2\xAA\xAE\xAD\xDB\x5\x7B\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x24\x61\xAA\xAC\xB5\xCF\xB3\x7B\xA6\xA3\x73\x0\x61\x1B\xFB\x22\x50\x58\x46\xBA\x8A\xAD\xD2\xAA\xAE\xB5\xDB\x5\x3B\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB2\x22\xAA\x35\xC4\xEB\x78\x9F\x38\xF9\x73\x0\x63\x53\xFA\xEB\x70\x4C\x46\xBA\x4E\x3D\xD4\x38\xEE\x9D\xDB\x5\x72\xAF\xE8\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x26\xA\xAA\xE9\xDD\xCF\x22\x7B\xAB\xAF\x73\x0\x41\x1B\xFB\xA2\x50\x5C\x46\xBA\xEA\xAD\xD1\xAA\xAE\xB5\xDB\x5\x3B\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x20\x49\xAA\x62\xE5\xCF\xA0\xBB\xA5\xF5\x73\x0\x51\x1B\xFA\x22\x50\x58\x46\xBA\x8A\xAD\xD3\xAA\xAE\xBD\xDB\x5\x3B\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB5\xAB\xAA\x97\xC4\xEC\x3A\x9F\x3E\xFB\x73\x0\x53\x53\xFB\xEB\x70\x40\x46\xBA\x4E\x3D\xD7\x38\xEE\x8D\xDB\x5\xF2\xAF\xEE\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x23\xE8\xAA\xE\xB5\xC8\xF1\x7B\xA0\xA1\x73\x0\x51\x1B\xFA\x22\x50\x54\x46\xBA\x8A\xAD\xD1\xAA\xAE\xA5\xDB\x5\xBB\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x27\xC0\xAA\xC0\xE5\xC8\xE2\xBB\xA3\xF7\x73\x0\x61\x1B\xFB\x22\x50\x54\x46\xBA\x8A\xAD\xD0\xAA\xAE\xAD\xDB\x5\xBB\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x21\x83\xAA\x4B\xDD\xC8\x60\x7B\xAD\xAD\x73\x0\x71\x1B\xFA\xA2\x50\x50\x46\xBA\xEA\xAD\xD2\xAA\xAE\xA5\xDB\x5\xBB\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB4\xC4\xAA\x99\x94\xE8\xAA\x5F\x32\x6D\x73\x0\x63\x53\xFA\xEB\x70\x48\x46\xBA\x6E\x3D\xD6\x38\xEE\x9D\xDB\x5\xB2\xAF\xEA\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x22\x87\xAA\x0\xE5\xCC\x61\xBB\xAC\x37\x73\x0\x61\x1B\xFB\x22\x50\x5C\x46\xBA\xAA\xAD\xD0\xAA\xAE\xB5\xDB\x5\xFB\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x26\xAF\xAA\xCE\xB5\xCC\x72\x7B\xAF\x61\x73\x0\x51\x1B\xFA\x22\x50\x5C\x46\xBA\xAA\xAD\xD1\xAA\xAE\xBD\xDB\x5\xFB\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x20\xEC\xAA\x45\x8D\xCC\xF0\xBB\xA1\x3B\x73\x0\x41\x1B\xFB\xA2\x50\x58\x46\xBA\xCA\xAD\xD3\xAA\xAE\xB5\xDB\x5\xFB\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x25\x44\xAA\xB9\xF5\xCA\xC3\xFB\x8A\xF3\x73\x0\x71\x1B\xFB\xA2\x50\x5C\x46\xBA\xCA\xAD\xD1\xAA\xAE\xB5\xDB\x5\xBB\x8F\xEB\x76\x1",
+ "\xFE\xFB\xFC\x16\xD0\x6E\x9C\xBB\x74\xB5\xDB\xA8\xAE\xC1\x51\x7\xFA\xAF\xE0\x1E\x0\xE6\xFF\x99\xAD\x0\xA7\x98\x9E\xEB\x91\x21\xFC\x26\x0\x4B\xB3\xF9\x77\x10\x52\xEC\xBA\x7F\xFD\xD1\x0\x2E\xA8\x8F\x5\x11\x2F\xEA\x23\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x27\x2F\xAA\xFC\x9D\xCA\x52\xFB\x87\xFF\x73\x0\x51\x1B\xFB\x22\x50\x58\x46\xBA\xAA\xAD\xD2\xAA\xAE\xB5\xDB\x5\xBB\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x21\x6C\xAA\x77\xA5\xCA\xD0\x3B\x89\xA5\x73\x0\x41\x1B\xFA\xA2\x50\x5C\x46\xBA\xCA\xAD\xD0\xAA\xAE\xBD\xDB\x5\xBB\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB4\x2B\xAA\xA5\xEC\xEA\x1A\x1F\x16\x65\x73\x0\x53\x53\xFA\xEB\x70\x44\x46\xBA\x4E\x3D\xD4\x38\xEE\x85\xDB\x5\xB2\xAF\xEE\x52\x0",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB2\x68\xAA\x2E\xD4\xEA\x98\xDF\x18\x3F\x73\x0\x43\x53\xFB\x6B\x70\x40\x46\xBA\x2E\x3D\xD6\x38\xEE\x8D\xDB\x5\xB2\xAF\xEA\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x26\x40\xAA\xF2\xCD\xCE\xC2\x3B\x8B\x69\x73\x0\x61\x1B\xFA\x22\x50\x50\x46\xBA\x8A\xAD\xD3\xAA\xAE\xA5\xDB\x5\xFB\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x20\x3\xAA\x79\xF5\xCE\x40\xFB\x85\x33\x73\x0\x71\x1B\xFB\xA2\x50\x54\x46\xBA\xEA\xAD\xD1\xAA\xAE\xAD\xDB\x5\xFB\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB5\xE1\xAA\x8C\xD4\xED\xDA\xDF\x1E\x3D\x73\x0\x73\x53\xFA\x6B\x70\x4C\x46\xBA\x2E\x3D\xD5\x38\xEE\x9D\xDB\x5\x32\xAF\xEC\x52\x0",
+ "\xFE\x4B\xFC\x14\x90\x6E\x90\xBB\x75\x25\xDB\xA3\xAE\xC1\x75\x7\xFA\xAF\xE0\x7\x0\xFB\xCD\x55\x8A\x49\x9B\x9D\x2A\x9B\x3\x63\xE9\x4B\x80\x7F\x23\xFA\x2C\x70\x4B\xC8\xBA\x89\x25\xD7\x49\x2E\xAD\x39\x5\x43\x6F\xEF\x4E\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x27\x8A\xAA\xDB\xF5\xC9\x2\xFB\x83\x31\x73\x0\x41\x1B\xFA\xA2\x50\x58\x46\xBA\xEA\xAD\xD2\xAA\xAE\xBD\xDB\x5\x7B\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB1\xC9\xAA\x42\x84\xED\xC9\x1F\x1D\x6B\x73\x0\x43\x53\xFB\x6B\x70\x4C\x46\xBA\x2E\x3D\xD4\x38\xEE\x95\xDB\x5\x32\xAF\xEA\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x24\x8E\xAA\x90\xCD\xCD\x3\x3B\x82\xAB\x73\x0\x51\x1B\xFB\x22\x50\x54\x46\xBA\xAA\xAD\xD0\xAA\xAE\xAD\xDB\x5\x3B\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB2\xCD\xAA\x9\xBC\xE9\xC8\xDF\x1C\xF1\x73\x0\x53\x53\xFA\xEB\x70\x40\x46\xBA\x6E\x3D\xD6\x38\xEE\x85\xDB\x5\x72\xAF\xEC\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x26\xE5\xAA\xD5\xA5\xCD\x92\x3B\x8F\xA7\x73\x0\x71\x1B\xFB\xA2\x50\x50\x46\xBA\xCA\xAD\xD3\xAA\xAE\xAD\xDB\x5\x3B\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x20\xA6\xAA\x5E\x9D\xCD\x10\xFB\x81\xFD\x73\x0\x61\x1B\xFA\x22\x50\x54\x46\xBA\xAA\xAD\xD1\xAA\xAE\xA5\xDB\x5\x3B\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB1\x6B\xAA\x95\xB4\xEF\xDA\x1F\x28\x77\x73\x0\x73\x53\xFB\xEB\x70\x4C\x46\xBA\x2E\x3D\xD6\x38\xEE\x9D\xDB\x5\xF2\xAF\xE8\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x27\x28\xAA\xC\xC5\xCB\x11\xFB\xB6\x2D\x73\x0\x71\x1B\xFA\x22\x50\x58\x46\xBA\xEA\xAD\xD0\xAA\xAE\xB5\xDB\x5\xBB\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x23\x0\xAA\xC2\x95\xCB\x2\x3B\xB5\x7B\x73\x0\x41\x1B\xFB\x22\x50\x58\x46\xBA\xEA\xAD\xD1\xAA\xAE\xBD\xDB\x5\xBB\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x25\x43\xAA\x49\xAD\xCB\x80\xFB\xBB\x21\x73\x0\x51\x1B\xFA\xA2\x50\x5C\x46\xBA\x8A\xAD\xD3\xAA\xAE\xB5\xDB\x5\xBB\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x20\x4\xAA\x89\xAD\xCF\x3\xFB\xB4\xE1\x73\x0\x51\x1B\xFA\xA2\x50\x54\x46\xBA\xAA\xAD\xD3\xAA\xAE\xAD\xDB\x5\xFB\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x26\x47\xAA\x2\x95\xCF\x81\x3B\xBA\xBB\x73\x0\x41\x1B\xFB\x22\x50\x50\x46\xBA\xCA\xAD\xD1\xAA\xAE\xA5\xDB\x5\xFB\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x22\x6F\xAA\xCC\xC5\xCF\x92\xFB\xB9\xED\x73\x0\x71\x1B\xFA\x22\x50\x50\x46\xBA\xCA\xAD\xD0\xAA\xAE\xAD\xDB\x5\xFB\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x24\x2C\xAA\x47\xFD\xCF\x10\x3B\xB7\xB7\x73\x0\x61\x1B\xFB\xA2\x50\x54\x46\xBA\xAA\xAD\xD2\xAA\xAE\xA5\xDB\x5\xFB\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x21\xCE\xAA\xA0\x95\xC8\xC3\x3B\xBC\xB9\x73\x0\x71\x1B\xFA\x22\x50\x5C\x46\xBA\xCA\xAD\xD2\xAA\xAE\xB5\xDB\x5\x7B\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x27\x8D\xAA\x2B\xAD\xC8\x41\xFB\xB2\xE3\x73\x0\x61\x1B\xFB\xA2\x50\x58\x46\xBA\xAA\xAD\xD0\xAA\xAE\xBD\xDB\x5\x7B\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x23\xA5\xAA\xE5\xFD\xC8\x52\x3B\xB1\xB5\x73\x0\x51\x1B\xFA\xA2\x50\x58\x46\xBA\xAA\xAD\xD1\xAA\xAE\xB5\xDB\x5\x7B\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x25\xE6\xAA\x6E\xC5\xC8\xD0\xFB\xBF\xEF\x73\x0\x41\x1B\xFB\x22\x50\x5C\x46\xBA\xCA\xAD\xD3\xAA\xAE\xBD\xDB\x5\x7B\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x20\xA1\xAA\xAE\xC5\xCC\x53\xFB\xB0\x2F\x73\x0\x41\x1B\xFB\x22\x50\x54\x46\xBA\xEA\xAD\xD3\xAA\xAE\xA5\xDB\x5\x3B\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x26\xE2\xAA\x25\xFD\xCC\xD1\x3B\xBE\x75\x73\x0\x51\x1B\xFA\xA2\x50\x50\x46\xBA\x8A\xAD\xD1\xAA\xAE\xAD\xDB\x5\x3B\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x22\xCA\xAA\xEB\xAD\xCC\xC2\xFB\xBD\x23\x73\x0\x61\x1B\xFB\xA2\x50\x50\x46\xBA\x8A\xAD\xD0\xAA\xAE\xA5\xDB\x5\x3B\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x24\x89\xAA\x60\x95\xCC\x40\x3B\xB3\x79\x73\x0\x71\x1B\xFA\x22\x50\x54\x46\xBA\xEA\xAD\xD2\xAA\xAE\xAD\xDB\x5\x3B\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x21\x21\xAA\x9C\xED\xCA\x73\x7B\x98\xB1\x73\x0\x41\x1B\xFA\x22\x50\x50\x46\xBA\xEA\xAD\xD0\xAA\xAE\xAD\xDB\x5\x7B\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB7\x62\xAA\x5\x9C\xEE\xB8\x9F\x6\xEB\x73\x0\x43\x53\xFB\xEB\x70\x44\x46\xBA\x2E\x3D\xD6\x38\xEE\x85\xDB\x5\x32\xAF\xEE\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x23\x4A\xAA\xD9\x85\xCA\xE2\x7B\x95\xBD\x73\x0\x61\x1B\xFA\xA2\x50\x54\x46\xBA\x8A\xAD\xD3\xAA\xAE\xAD\xDB\x5\x7B\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x25\x9\xAA\x52\xBD\xCA\x60\xBB\x9B\xE7\x73\x0\x71\x1B\xFB\x22\x50\x50\x46\xBA\xEA\xAD\xD1\xAA\xAE\xA5\xDB\x5\x7B\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB0\x4E\xAA\x80\xF4\xEA\xAA\x9F\x4\x27\x73\x0\x63\x53\xFB\x6B\x70\x48\x46\xBA\x6E\x3D\xD5\x38\xEE\x9D\xDB\x5\x72\xAF\xEE\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x26\xD\xAA\x19\x85\xCE\x61\x7B\x9A\x7D\x73\x0\x61\x1B\xFA\xA2\x50\x5C\x46\xBA\xAA\xAD\xD3\xAA\xAE\xB5\xDB\x5\x3B\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x22\x25\xAA\xD7\xD5\xCE\x72\xBB\x99\x2B\x73\x0\x51\x1B\xFB\xA2\x50\x5C\x46\xBA\xAA\xAD\xD2\xAA\xAE\xBD\xDB\x5\x3B\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB4\x66\xAA\x4E\xA4\xEA\xB9\x5F\x7\x71\x73\x0\x53\x53\xFA\x6B\x70\x48\x46\xBA\x6E\x3D\xD4\x38\xEE\x95\xDB\x5\x72\xAF\xE8\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x21\x84\xAA\xBB\x85\xC9\x23\x7B\x9C\x7F\x73\x0\x51\x1B\xFB\xA2\x50\x50\x46\xBA\xAA\xAD\xD0\xAA\xAE\xA5\xDB\x5\xBB\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB7\xC7\xAA\x22\xF4\xED\xE8\x9F\x2\x25\x73\x0\x53\x53\xFA\x6B\x70\x44\x46\xBA\x6E\x3D\xD6\x38\xEE\x8D\xDB\x5\xF2\xAF\xE8\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x23\xEF\xAA\xFE\xED\xC9\xB2\x7B\x91\x73\x73\x0\x71\x1B\xFB\x22\x50\x54\x46\xBA\xCA\xAD\xD3\xAA\xAE\xA5\xDB\x5\xBB\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x25\xAC\xAA\x75\xD5\xC9\x30\xBB\x9F\x29\x73\x0\x61\x1B\xFA\xA2\x50\x50\x46\xBA\xAA\xAD\xD1\xAA\xAE\xAD\xDB\x5\xBB\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x20\xEB\xAA\xB5\xD5\xCD\xB3\xBB\x90\xE9\x73\x0\x61\x1B\xFA\xA2\x50\x58\x46\xBA\x8A\xAD\xD1\xAA\xAE\xB5\xDB\x5\xFB\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB6\xA8\xAA\x2C\xA4\xE9\x78\x5F\xE\xB3\x73\x0\x63\x53\xFB\x6B\x70\x4C\x46\xBA\x4E\x3D\xD7\x38\xEE\x9D\xDB\x5\xB2\xAF\xEC\x52\x0",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB2\x80\xAA\xE2\xF4\xE9\x6B\x9F\xD\xE5\x73\x0\x53\x53\xFA\x6B\x70\x4C\x46\xBA\x4E\x3D\xD6\x38\xEE\x95\xDB\x5\xB2\xAF\xEA\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x24\xC3\xAA\x7B\x85\xCD\xA0\x7B\x93\xBF\x73\x0\x51\x1B\xFB\xA2\x50\x58\x46\xBA\x8A\xAD\xD0\xAA\xAE\xBD\xDB\x5\xFB\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x21\x25\xAA\x94\xB5\xCB\x30\x3B\xAA\x6B\x72\x80\x41\x13\xFA\x22\x50\x50\x46\xBA\xEA\xAD\xD2\xAA\xAE\xA5\xDB\x5\xBB\x8F\xE9\x76\x1",
+};