summaryrefslogtreecommitdiffstats
path: root/gfx/layers/composite/LayerManagerComposite.h
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/layers/composite/LayerManagerComposite.h')
-rw-r--r--gfx/layers/composite/LayerManagerComposite.h687
1 files changed, 687 insertions, 0 deletions
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 */