/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef GFX_LayerManagerComposite_H #define GFX_LayerManagerComposite_H #include // for uint64_t, int32_t #include // for deque #include // for operator new #include // for remove_reference<>::type #include // for move, forward #include "CompositableHost.h" // for ImageCompositeNotificationInfo #include "Units.h" // for LayerIntRegion, ParentLayerIntRect, RenderTargetIntRect #include "mozilla/AlreadyAddRefed.h" // for already_AddRefed #include "mozilla/Assertions.h" // for MOZ_CRASH, AssertionConditionType, MOZ_ASSERT, MOZ_AS... #include "mozilla/Maybe.h" // for Maybe #include "mozilla/RefPtr.h" // for RefPtr #include "mozilla/TimeStamp.h" // for TimeStamp, BaseTimeDuration #include "mozilla/UniquePtr.h" // for UniquePtr #include "mozilla/gfx/2D.h" // for DrawTarget #include "mozilla/gfx/Matrix.h" // for Matrix4x4 #include "mozilla/gfx/Point.h" // for IntSize #include "mozilla/gfx/Polygon.h" // for Polygon #include "mozilla/gfx/Rect.h" // for IntRect #include "mozilla/gfx/Types.h" // for DeviceColor (ptr only), SurfaceFormat #include "mozilla/layers/CompositionRecorder.h" // for CompositionRecorder, CollectedFrames (ptr only) #include "mozilla/layers/Compositor.h" // for Compositor #include "mozilla/layers/CompositorTypes.h" // for DiagnosticTypes, TextureFactoryIdentifier #include "mozilla/layers/LayerManager.h" // for LayerManager::END_DEFAULT, LayerManager::EndTransacti... #include "mozilla/layers/LayersTypes.h" // for CompositionOpportunityId, LayersBackend, LayersBacken... #include "mozilla/layers/ScreenshotGrabber.h" // for ScreenshotGrabber #include "nsDebug.h" // for NS_WARNING #include "nsIThread.h" // for TimeDuration #include "nsRegion.h" // for nsIntRegion #include "nsRegionFwd.h" // for IntRegion #include "nsStringFwd.h" // for nsCString, nsAString #include "nsTArray.h" // for nsTArray class gfxContext; #ifdef XP_WIN # include #endif namespace mozilla { namespace layers { class CanvasLayer; class CanvasLayerComposite; class ColorLayer; class ColorLayerComposite; class ContainerLayer; class ContainerLayerComposite; class Diagnostics; struct EffectChain; class ImageLayer; class ImageLayerComposite; class LayerComposite; class NativeLayer; class NativeLayerRoot; class RefLayerComposite; class PaintTiming; class PaintedLayer; class PaintedLayerComposite; class RefLayer; class SurfacePoolHandle; class TextRenderer; class TextureSourceProvider; class CompositingRenderTarget; struct FPSState; class PaintCounter; class LayerMLGPU; class LayerManagerMLGPU; class UiCompositorControllerParent; class Layer; struct LayerProperties; static const int kVisualWarningDuration = 150; // ms // An implementation of LayerManager that acts as a pair with ClientLayerManager // and is mirrored across IPDL. This gets managed/updated by // LayerTransactionParent. class HostLayerManager : public LayerManager { public: HostLayerManager(); virtual ~HostLayerManager(); bool BeginTransactionWithTarget(gfxContext* aTarget, const nsCString& aURL) override { MOZ_CRASH("GFX: Use BeginTransactionWithDrawTarget"); } bool EndEmptyTransaction(EndTransactionFlags aFlags = END_DEFAULT) override { MOZ_CRASH("GFX: Use EndTransaction(aTimeStamp)"); return false; } void EndTransaction(DrawPaintedLayerCallback aCallback, void* aCallbackData, EndTransactionFlags aFlags = END_DEFAULT) override { MOZ_CRASH("GFX: Use EndTransaction(aTimeStamp)"); } int32_t GetMaxTextureSize() const override { MOZ_CRASH("GFX: Call on compositor, not LayerManagerComposite"); } void GetBackendName(nsAString& name) override { MOZ_CRASH("GFX: Shouldn't be called for composited layer manager"); } virtual void ForcePresent() = 0; virtual void AddInvalidRegion(const nsIntRegion& aRegion) = 0; virtual void NotifyShadowTreeTransaction() {} virtual void BeginTransactionWithDrawTarget(gfx::DrawTarget* aTarget, const gfx::IntRect& aRect) = 0; virtual Compositor* GetCompositor() const = 0; virtual TextureSourceProvider* GetTextureSourceProvider() const = 0; virtual void EndTransaction(const TimeStamp& aTimeStamp, EndTransactionFlags aFlags = END_DEFAULT) = 0; virtual void UpdateRenderBounds(const gfx::IntRect& aRect) {} virtual void SetDiagnosticTypes(DiagnosticTypes aDiagnostics) {} virtual void InvalidateAll() = 0; HostLayerManager* AsHostLayerManager() override { return this; } virtual LayerManagerMLGPU* AsLayerManagerMLGPU() { return nullptr; } void ExtractImageCompositeNotifications( nsTArray* aNotifications) { aNotifications->AppendElements(std::move(mImageCompositeNotifications)); } void AppendImageCompositeNotification( const ImageCompositeNotificationInfo& aNotification) { // Only send composite notifications when we're drawing to the screen, // because that's what they mean. // Also when we're not drawing to the screen, DidComposite will not be // called to extract and send these notifications, so they might linger // and contain stale ImageContainerParent pointers. if (IsCompositingToScreen()) { mImageCompositeNotifications.AppendElement(aNotification); } } /** * LayerManagerComposite provides sophisticated debug overlays * that can request a next frame. */ bool DebugOverlayWantsNextFrame() { return mDebugOverlayWantsNextFrame; } void SetDebugOverlayWantsNextFrame(bool aVal) { mDebugOverlayWantsNextFrame = aVal; } /** * Add an on frame warning. * @param severity ranges from 0 to 1. It's used to compute the warning color. */ void VisualFrameWarning(float severity) { mozilla::TimeStamp now = TimeStamp::Now(); if (mWarnTime.IsNull() || severity > mWarningLevel || mWarnTime + TimeDuration::FromMilliseconds(kVisualWarningDuration) < now) { mWarnTime = now; mWarningLevel = severity; } } void SetPaintTime(const TimeDuration& aPaintTime) { mLastPaintTime = aPaintTime; } virtual bool AlwaysScheduleComposite() const { return false; } virtual bool IsCompositingToScreen() const { return false; } void RecordPaintTimes(const PaintTiming& aTiming); void RecordUpdateTime(float aValue); CompositionOpportunityId GetCompositionOpportunityId() const { return mCompositionOpportunityId; } TimeStamp GetCompositionTime() const { return mCompositionTime; } void SetCompositionTime(TimeStamp aTimeStamp) { mCompositionTime = aTimeStamp; if (!mCompositionTime.IsNull() && !mCompositeUntilTime.IsNull() && mCompositionTime >= mCompositeUntilTime) { mCompositeUntilTime = TimeStamp(); } } void CompositeUntil(TimeStamp aTimeStamp) { if (mCompositeUntilTime.IsNull() || mCompositeUntilTime < aTimeStamp) { mCompositeUntilTime = aTimeStamp; } } TimeStamp GetCompositeUntilTime() const { return mCompositeUntilTime; } // We maintaining a global mapping from ID to CompositorBridgeParent for // async compositables. uint64_t GetCompositorBridgeID() const { return mCompositorBridgeID; } void SetCompositorBridgeID(uint64_t aID) { MOZ_ASSERT(mCompositorBridgeID == 0, "The compositor ID must be set only once."); mCompositorBridgeID = aID; } void SetCompositionRecorder(UniquePtr aRecorder) { mCompositionRecorder = std::move(aRecorder); } /** * Write the frames collected by the |CompositionRecorder| to disk. * * If there is not currently a |CompositionRecorder|, this is a no-op. */ void WriteCollectedFrames(); Maybe GetCollectedFrames(); protected: bool mDebugOverlayWantsNextFrame; nsTArray mImageCompositeNotifications; // Testing property. If hardware composer is supported, this will return // true if the last frame was deemed 'too complicated' to be rendered. float mWarningLevel; mozilla::TimeStamp mWarnTime; UniquePtr mDiagnostics; uint64_t mCompositorBridgeID; TimeDuration mLastPaintTime; TimeStamp mRenderStartTime; UniquePtr mCompositionRecorder = nullptr; // Render time for the current composition. TimeStamp mCompositionTime; // CompositionOpportunityId of the current composition. CompositionOpportunityId mCompositionOpportunityId; // When nonnull, during rendering, some compositable indicated that it will // change its rendering at this time. In order not to miss it, we composite // on every vsync until this time occurs (this is the latest such time). TimeStamp mCompositeUntilTime; #if defined(MOZ_WIDGET_ANDROID) public: // Used by UiCompositorControllerParent to set itself as the target for the // contents of the frame buffer after a composite. // Implemented in LayerManagerComposite virtual void RequestScreenPixels(UiCompositorControllerParent* aController) {} #endif // defined(MOZ_WIDGET_ANDROID) }; // A layer manager implementation that uses the Compositor API // to render layers. class LayerManagerComposite final : public HostLayerManager { typedef mozilla::gfx::DrawTarget DrawTarget; typedef mozilla::gfx::IntSize IntSize; typedef mozilla::gfx::SurfaceFormat SurfaceFormat; public: explicit LayerManagerComposite(Compositor* aCompositor); virtual ~LayerManagerComposite(); void Destroy() override; /** * Sets the clipping region for this layer manager. This is important on * windows because using OGL we no longer have GDI's native clipping. Therefor * widget must tell us what part of the screen is being invalidated, * and we should clip to this. * * \param aClippingRegion Region to clip to. Setting an empty region * will disable clipping. */ void SetClippingRegion(const nsIntRegion& aClippingRegion) { mClippingRegion = aClippingRegion; } /** * LayerManager implementation. */ LayerManagerComposite* AsLayerManagerComposite() override { return this; } void UpdateRenderBounds(const gfx::IntRect& aRect) override; bool BeginTransaction(const nsCString& aURL) override; void BeginTransactionWithDrawTarget(gfx::DrawTarget* aTarget, const gfx::IntRect& aRect) override; void EndTransaction(const TimeStamp& aTimeStamp, EndTransactionFlags aFlags = END_DEFAULT) override; virtual void EndTransaction( DrawPaintedLayerCallback aCallback, void* aCallbackData, EndTransactionFlags aFlags = END_DEFAULT) override { MOZ_CRASH("GFX: Use EndTransaction(aTimeStamp)"); } void SetRoot(Layer* aLayer) override; // XXX[nrc]: never called, we should move this logic to ClientLayerManager // (bug 946926). bool CanUseCanvasLayerForSize(const gfx::IntSize& aSize) override; void ClearCachedResources(Layer* aSubtree = nullptr) override; already_AddRefed CreatePaintedLayer() override; already_AddRefed CreateContainerLayer() override; already_AddRefed CreateImageLayer() override; already_AddRefed CreateColorLayer() override; already_AddRefed CreateCanvasLayer() override; already_AddRefed CreateRefLayer() override; bool AreComponentAlphaLayersEnabled() override; already_AddRefed CreateOptimalMaskDrawTarget( const IntSize& aSize) override; const char* Name() const override { return ""; } bool IsCompositingToScreen() const override; bool AlwaysScheduleComposite() const override; /** * Post-processes layers before composition. This performs the following: * * - Applies occlusion culling. This restricts the shadow visible region of * layers that are covered with opaque content. |aOpaqueRegion| is the * region already known to be covered with opaque content, in the * post-transform coordinate space of aLayer. * * - Recomputes visible regions to account for async transforms. * Each layer accumulates into |aVisibleRegion| its post-transform * (including async transforms) visible region. * * - aRenderTargetClip is the exact clip required for aLayer, in the * coordinates of the nearest render target (the same as * GetEffectiveTransform). * * - aClipFromAncestors is the approximate combined clip from all * ancestors, in the coordinate space of our parent, but maybe be an * overestimate in the presence of complex transforms. */ void PostProcessLayers(nsIntRegion& aOpaqueRegion); void PostProcessLayers(Layer* aLayer, nsIntRegion& aOpaqueRegion, LayerIntRegion& aVisibleRegion, const Maybe& aRenderTargetClip, const Maybe& aClipFromAncestors, bool aCanContributeOpaque); /** * RAII helper class to add a mask effect with the compositable from * aMaskLayer to the EffectChain aEffect and notify the compositable when we * are done. */ class AutoAddMaskEffect { public: AutoAddMaskEffect(Layer* aMaskLayer, EffectChain& aEffect); ~AutoAddMaskEffect(); bool Failed() const { return mFailed; } private: CompositableHost* mCompositable; bool mFailed; }; /** * returns true if PlatformAllocBuffer will return a buffer that supports * direct texturing */ static bool SupportsDirectTexturing(); static void PlatformSyncBeforeReplyUpdate(); void AddInvalidRegion(const nsIntRegion& aRegion) override { mInvalidRegion.Or(mInvalidRegion, aRegion); } Compositor* GetCompositor() const override { return mCompositor; } TextureSourceProvider* GetTextureSourceProvider() const override { return mCompositor; } void NotifyShadowTreeTransaction() override; TextRenderer* GetTextRenderer() { return mTextRenderer; } void UnusedApzTransformWarning() { mUnusedApzTransformWarning = true; } void DisabledApzWarning() { mDisabledApzWarning = true; } bool AsyncPanZoomEnabled() const override; public: TextureFactoryIdentifier GetTextureFactoryIdentifier() override { return mCompositor->GetTextureFactoryIdentifier(); } LayersBackend GetBackendType() override { return mCompositor ? mCompositor->GetBackendType() : LayersBackend::LAYERS_NONE; } void SetDiagnosticTypes(DiagnosticTypes aDiagnostics) override { mCompositor->SetDiagnosticTypes(aDiagnostics); } void InvalidateAll() override { AddInvalidRegion(nsIntRegion(mRenderBounds)); } void ForcePresent() override { mCompositor->ForcePresent(); } private: /** Region we're clipping our current drawing to. */ nsIntRegion mClippingRegion; gfx::IntRect mRenderBounds; /** Current root layer. */ LayerComposite* RootLayer() const; /** * Update the invalid region and render it. */ void UpdateAndRender(); /** * Render the current layer tree to the active target. * Returns true if the current invalid region can be cleared, false if * rendering was canceled. */ bool Render(const nsIntRegion& aInvalidRegion, const nsIntRegion& aOpaqueRegion); #if defined(MOZ_WIDGET_ANDROID) void RenderToPresentationSurface(); // Shifts the content down so the toolbar does not cover it. // Returns the Y shift of the content in screen pixels ScreenCoord GetContentShiftForToolbar(); // Renders the static snapshot after the content has been rendered. void RenderToolbar(); // Used by robocop tests to get a snapshot of the frame buffer. void HandlePixelsTarget(); #endif /** * We need to know our invalid region before we're ready to render. */ void InvalidateDebugOverlay(nsIntRegion& aInvalidRegion, const gfx::IntRect& aBounds); /** * Render debug overlays such as the FPS/FrameCounter above the frame. */ void RenderDebugOverlay(const gfx::IntRect& aBounds); void DrawBorder(const gfx::IntRect& aOuter, int32_t aBorderWidth, const gfx::DeviceColor& aColor, const gfx::Matrix4x4& aTransform); void DrawTranslationWarningOverlay(const gfx::IntRect& aBounds); void UpdateDebugOverlayNativeLayers(); RefPtr PushGroupForLayerEffects(); void PopGroupForLayerEffects(RefPtr aPreviousTarget, gfx::IntRect aClipRect, bool aGrayscaleEffect, bool aInvertEffect, float aContrastEffect); /** * Create or recycle native layers to cover aRegion or aRect. * This method takes existing layers from the front of aLayersToRecycle (or * creates new layers if no layers are left to recycle) and appends them to * the end of mNativeLayers. The "take from front, add to back" approach keeps * the layer to rect assignment stable between frames. * Updates the rect and opaqueness on the layers. For layers that moved or * resized, *aWindowInvalidRegion is updated to include the area impacted by * the move. * Any layers left in aLayersToRecycle are not needed and can be disposed of. */ void PlaceNativeLayers(const gfx::IntRegion& aRegion, bool aOpaque, std::deque>* aLayersToRecycle, gfx::IntRegion* aWindowInvalidRegion); void PlaceNativeLayer(const gfx::IntRect& aRect, bool aOpaque, std::deque>* aLayersToRecycle, gfx::IntRegion* aWindowInvalidRegion); bool mUnusedApzTransformWarning; bool mDisabledApzWarning; RefPtr mCompositor; UniquePtr mClonedLayerTreeProperties; /** * Context target, nullptr when drawing directly to our swap chain. */ RefPtr mTarget; gfx::IntRect mTargetBounds; nsIntRegion mInvalidRegion; bool mInTransaction; bool mIsCompositorReady; RefPtr mTwoPassTmpTarget; ScreenshotGrabber mProfilerScreenshotGrabber; RefPtr mTextRenderer; RefPtr mNativeLayerRoot; RefPtr mSurfacePoolHandle; std::deque> mNativeLayers; RefPtr mGPUStatsLayer; RefPtr mUnusedTransformWarningLayer; RefPtr mDisabledApzWarningLayer; #ifdef USE_SKIA /** * Render paint and composite times above the frame. */ void DrawPaintTimes(Compositor* aCompositor); RefPtr mPaintCounter; #endif #if defined(MOZ_WIDGET_ANDROID) public: virtual void RequestScreenPixels( UiCompositorControllerParent* aController) override { mScreenPixelsTarget = aController; } private: UiCompositorControllerParent* mScreenPixelsTarget; #endif // defined(MOZ_WIDGET_ANDROID) }; /** * Compositor layers are for use with OMTC on the compositor thread only. There * must be corresponding Client layers on the content thread. For composite * layers, the layer manager only maintains the layer tree. */ class HostLayer { public: explicit HostLayer(HostLayerManager* aManager) : mCompositorManager(aManager), mShadowOpacity(1.0), mShadowTransformSetByAnimation(false), mShadowOpacitySetByAnimation(false) {} virtual void SetLayerManager(HostLayerManager* aManager) { mCompositorManager = aManager; } HostLayerManager* GetLayerManager() const { return mCompositorManager; } virtual ~HostLayer() = default; virtual LayerComposite* GetFirstChildComposite() { return nullptr; } virtual Layer* GetLayer() = 0; virtual LayerMLGPU* AsLayerMLGPU() { return nullptr; } virtual bool SetCompositableHost(CompositableHost*) { // We must handle this gracefully, see bug 967824 NS_WARNING( "called SetCompositableHost for a layer type not accepting a " "compositable"); return false; } virtual CompositableHost* GetCompositableHost() = 0; /** * The following methods are * * CONSTRUCTION PHASE ONLY * * They are analogous to the Layer interface. */ void SetShadowVisibleRegion(const LayerIntRegion& aRegion) { mShadowVisibleRegion = aRegion; } void SetShadowVisibleRegion(LayerIntRegion&& aRegion) { mShadowVisibleRegion = std::move(aRegion); } void SetShadowOpacity(float aOpacity) { mShadowOpacity = aOpacity; } void SetShadowOpacitySetByAnimation(bool aSetByAnimation) { mShadowOpacitySetByAnimation = aSetByAnimation; } void SetShadowClipRect(const Maybe& aRect) { mShadowClipRect = aRect; } void SetShadowBaseTransform(const gfx::Matrix4x4& aMatrix) { mShadowTransform = aMatrix; } void SetShadowTransformSetByAnimation(bool aSetByAnimation) { mShadowTransformSetByAnimation = aSetByAnimation; } // These getters can be used anytime. float GetShadowOpacity() { return mShadowOpacity; } const Maybe& GetShadowClipRect() { return mShadowClipRect; } virtual const LayerIntRegion& GetShadowVisibleRegion() { return mShadowVisibleRegion; } const gfx::Matrix4x4& GetShadowBaseTransform() { return mShadowTransform; } gfx::Matrix4x4 GetShadowTransform(); bool GetShadowTransformSetByAnimation() { return mShadowTransformSetByAnimation; } bool GetShadowOpacitySetByAnimation() { return mShadowOpacitySetByAnimation; } void RecomputeShadowVisibleRegionFromChildren(); protected: HostLayerManager* mCompositorManager; gfx::Matrix4x4 mShadowTransform; LayerIntRegion mShadowVisibleRegion; Maybe mShadowClipRect; float mShadowOpacity; bool mShadowTransformSetByAnimation; bool mShadowOpacitySetByAnimation; }; /** * Composite layers are for use with OMTC on the compositor thread only. There * must be corresponding Client layers on the content thread. For composite * layers, the layer manager only maintains the layer tree, all rendering is * done by a Compositor (see Compositor.h). As such, composite layers are * platform-independent and can be used on any platform for which there is a * Compositor implementation. * * The composite layer tree reflects exactly the basic layer tree. To * composite to screen, the layer manager walks the layer tree calling render * methods which in turn call into their CompositableHosts' Composite methods. * These call Compositor::DrawQuad to do the rendering. * * Mostly, layers are updated during the layers transaction. This is done from * CompositableClient to CompositableHost without interacting with the layer. * * A reference to the Compositor is stored in LayerManagerComposite. */ class LayerComposite : public HostLayer { public: explicit LayerComposite(LayerManagerComposite* aManager); virtual ~LayerComposite(); void SetLayerManager(HostLayerManager* aManager) override; LayerComposite* GetFirstChildComposite() override { return nullptr; } /* Do NOT call this from the generic LayerComposite destructor. Only from the * concrete class destructor */ virtual void Destroy(); virtual void Cleanup() {} /** * Perform a first pass over the layer tree to render all of the intermediate * surfaces that we can. This allows us to avoid framebuffer switches in the * middle of our render which is inefficient especially on mobile GPUs. This * must be called before RenderLayer. */ virtual void Prepare(const RenderTargetIntRect& aClipRect) {} // TODO: This should also take RenderTargetIntRect like Prepare. virtual void RenderLayer(const gfx::IntRect& aClipRect, const Maybe& aGeometry) = 0; bool SetCompositableHost(CompositableHost*) override { // We must handle this gracefully, see bug 967824 NS_WARNING( "called SetCompositableHost for a layer type not accepting a " "compositable"); return false; } virtual void CleanupResources() = 0; virtual void DestroyFrontBuffer() {} void AddBlendModeEffect(EffectChain& aEffectChain); virtual void GenEffectChain(EffectChain& aEffect) {} void SetLayerComposited(bool value) { mLayerComposited = value; } void SetClearRect(const gfx::IntRect& aRect) { mClearRect = aRect; } bool HasLayerBeenComposited() { return mLayerComposited; } gfx::IntRect GetClearRect() { return mClearRect; } // Returns false if the layer is attached to an older compositor. bool HasStaleCompositor() const; /** * Return the part of the visible region that has been fully rendered. * While progressive drawing is in progress this region will be * a subset of the shadow visible region. */ virtual nsIntRegion GetFullyRenderedRegion(); protected: LayerManagerComposite* mCompositeManager; RefPtr mCompositor; bool mDestroyed; bool mLayerComposited; gfx::IntRect mClearRect; }; class WindowLMC : public profiler_screenshots::Window { public: explicit WindowLMC(Compositor* aCompositor) : mCompositor(aCompositor) {} already_AddRefed GetWindowContents( const gfx::IntSize& aWindowSize) override; already_AddRefed CreateDownscaleTarget( const gfx::IntSize& aSize) override; already_AddRefed CreateAsyncReadbackBuffer(const gfx::IntSize& aSize) override; protected: Compositor* mCompositor; }; } // namespace layers } // namespace mozilla #endif /* GFX_LayerManagerComposite_H */