michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef GFX_LAYERS_H michael@0: #define GFX_LAYERS_H michael@0: michael@0: #include // for uint32_t, uint64_t, uint8_t michael@0: #include // for FILE michael@0: #include // for int32_t, int64_t michael@0: #include "FrameMetrics.h" // for FrameMetrics michael@0: #include "Units.h" // for LayerMargin, LayerPoint michael@0: #include "gfxContext.h" // for GraphicsOperator michael@0: #include "gfxTypes.h" michael@0: #include "gfxColor.h" // for gfxRGBA michael@0: #include "gfxMatrix.h" // for gfxMatrix michael@0: #include "GraphicsFilter.h" // for GraphicsFilter michael@0: #include "gfxPoint.h" // for gfxPoint michael@0: #include "gfxRect.h" // for gfxRect michael@0: #include "gfx2DGlue.h" michael@0: #include "mozilla/Assertions.h" // for MOZ_ASSERT_HELPER2, etc michael@0: #include "mozilla/DebugOnly.h" // for DebugOnly michael@0: #include "mozilla/EventForwards.h" // for nsPaintEvent michael@0: #include "mozilla/RefPtr.h" // for TemporaryRef michael@0: #include "mozilla/TimeStamp.h" // for TimeStamp, TimeDuration michael@0: #include "mozilla/gfx/BaseMargin.h" // for BaseMargin michael@0: #include "mozilla/gfx/BasePoint.h" // for BasePoint michael@0: #include "mozilla/gfx/Point.h" // for IntSize michael@0: #include "mozilla/gfx/Types.h" // for SurfaceFormat michael@0: #include "mozilla/gfx/UserData.h" // for UserData, etc michael@0: #include "mozilla/layers/LayersTypes.h" michael@0: #include "mozilla/mozalloc.h" // for operator delete, etc michael@0: #include "nsAutoPtr.h" // for nsAutoPtr, nsRefPtr, etc michael@0: #include "nsCOMPtr.h" // for already_AddRefed michael@0: #include "nsCSSProperty.h" // for nsCSSProperty michael@0: #include "nsDebug.h" // for NS_ASSERTION michael@0: #include "nsISupportsImpl.h" // for Layer::Release, etc michael@0: #include "nsRect.h" // for nsIntRect michael@0: #include "nsRegion.h" // for nsIntRegion michael@0: #include "nsSize.h" // for nsIntSize michael@0: #include "nsString.h" // for nsCString michael@0: #include "nsStyleAnimation.h" // for nsStyleAnimation::Value, etc michael@0: #include "nsTArray.h" // for nsTArray michael@0: #include "nsTArrayForwardDeclare.h" // for InfallibleTArray michael@0: #include "nscore.h" // for nsACString, nsAString michael@0: #include "prlog.h" // for PRLogModuleInfo michael@0: #include "gfx2DGlue.h" michael@0: michael@0: class gfxContext; michael@0: michael@0: extern uint8_t gLayerManagerLayerBuilder; michael@0: michael@0: namespace mozilla { michael@0: michael@0: class FrameLayerBuilder; michael@0: class WebGLContext; michael@0: michael@0: namespace gl { michael@0: class GLContext; michael@0: } michael@0: michael@0: namespace gfx { michael@0: class DrawTarget; michael@0: class SurfaceStream; michael@0: } michael@0: michael@0: namespace css { michael@0: class ComputedTimingFunction; michael@0: } michael@0: michael@0: namespace layers { michael@0: michael@0: class Animation; michael@0: class AnimationData; michael@0: class AsyncPanZoomController; michael@0: class CommonLayerAttributes; michael@0: class Layer; michael@0: class ThebesLayer; michael@0: class ContainerLayer; michael@0: class ImageLayer; michael@0: class ColorLayer; michael@0: class ImageContainer; michael@0: class CanvasLayer; michael@0: class ReadbackLayer; michael@0: class ReadbackProcessor; michael@0: class RefLayer; michael@0: class LayerComposite; michael@0: class ShadowableLayer; michael@0: class ShadowLayerForwarder; michael@0: class LayerManagerComposite; michael@0: class SpecificLayerAttributes; michael@0: class SurfaceDescriptor; michael@0: class Compositor; michael@0: struct TextureFactoryIdentifier; michael@0: struct EffectMask; michael@0: michael@0: #define MOZ_LAYER_DECL_NAME(n, e) \ michael@0: virtual const char* Name() const { return n; } \ michael@0: virtual LayerType GetType() const { return e; } michael@0: michael@0: /** michael@0: * Base class for userdata objects attached to layers and layer managers. michael@0: */ michael@0: class LayerUserData { michael@0: public: michael@0: virtual ~LayerUserData() {} michael@0: }; michael@0: michael@0: /* michael@0: * Motivation: For truly smooth animation and video playback, we need to michael@0: * be able to compose frames and render them on a dedicated thread (i.e. michael@0: * off the main thread where DOM manipulation, script execution and layout michael@0: * induce difficult-to-bound latency). This requires Gecko to construct michael@0: * some kind of persistent scene structure (graph or tree) that can be michael@0: * safely transmitted across threads. We have other scenarios (e.g. mobile michael@0: * browsing) where retaining some rendered data between paints is desired michael@0: * for performance, so again we need a retained scene structure. michael@0: * michael@0: * Our retained scene structure is a layer tree. Each layer represents michael@0: * content which can be composited onto a destination surface; the root michael@0: * layer is usually composited into a window, and non-root layers are michael@0: * composited into their parent layers. Layers have attributes (e.g. michael@0: * opacity and clipping) that influence their compositing. michael@0: * michael@0: * We want to support a variety of layer implementations, including michael@0: * a simple "immediate mode" implementation that doesn't retain any michael@0: * rendered data between paints (i.e. uses cairo in just the way that michael@0: * Gecko used it before layers were introduced). But we also don't want michael@0: * to have bifurcated "layers"/"non-layers" rendering paths in Gecko. michael@0: * Therefore the layers API is carefully designed to permit maximally michael@0: * efficient implementation in an "immediate mode" style. See the michael@0: * BasicLayerManager for such an implementation. michael@0: */ michael@0: michael@0: static void LayerManagerUserDataDestroy(void *data) michael@0: { michael@0: delete static_cast(data); michael@0: } michael@0: michael@0: /** michael@0: * A LayerManager controls a tree of layers. All layers in the tree michael@0: * must use the same LayerManager. michael@0: * michael@0: * All modifications to a layer tree must happen inside a transaction. michael@0: * Only the state of the layer tree at the end of a transaction is michael@0: * rendered. Transactions cannot be nested michael@0: * michael@0: * Each transaction has two phases: michael@0: * 1) Construction: layers are created, inserted, removed and have michael@0: * properties set on them in this phase. michael@0: * BeginTransaction and BeginTransactionWithTarget start a transaction in michael@0: * the Construction phase. When the client has finished constructing the layer michael@0: * tree, it should call EndConstruction() to enter the drawing phase. michael@0: * 2) Drawing: ThebesLayers are rendered into in this phase, in tree michael@0: * order. When the client has finished drawing into the ThebesLayers, it should michael@0: * call EndTransaction to complete the transaction. michael@0: * michael@0: * All layer API calls happen on the main thread. michael@0: * michael@0: * Layers are refcounted. The layer manager holds a reference to the michael@0: * root layer, and each container layer holds a reference to its children. michael@0: */ michael@0: class LayerManager { michael@0: NS_INLINE_DECL_REFCOUNTING(LayerManager) michael@0: michael@0: protected: michael@0: typedef mozilla::gfx::DrawTarget DrawTarget; michael@0: typedef mozilla::gfx::IntSize IntSize; michael@0: typedef mozilla::gfx::SurfaceFormat SurfaceFormat; michael@0: michael@0: public: michael@0: LayerManager() michael@0: : mDestroyed(false) michael@0: , mSnapEffectiveTransforms(true) michael@0: , mId(0) michael@0: , mInTransaction(false) michael@0: { michael@0: InitLog(); michael@0: } michael@0: michael@0: /** michael@0: * Release layers and resources held by this layer manager, and mark michael@0: * it as destroyed. Should do any cleanup necessary in preparation michael@0: * for its widget going away. After this call, only user data calls michael@0: * are valid on the layer manager. michael@0: */ michael@0: virtual void Destroy() michael@0: { michael@0: mDestroyed = true; michael@0: mUserData.Destroy(); michael@0: mRoot = nullptr; michael@0: } michael@0: bool IsDestroyed() { return mDestroyed; } michael@0: michael@0: virtual ShadowLayerForwarder* AsShadowForwarder() michael@0: { return nullptr; } michael@0: michael@0: virtual LayerManagerComposite* AsLayerManagerComposite() michael@0: { return nullptr; } michael@0: michael@0: /** michael@0: * Returns true if this LayerManager is owned by an nsIWidget, michael@0: * and is used for drawing into the widget. michael@0: */ michael@0: virtual bool IsWidgetLayerManager() { return true; } michael@0: michael@0: /** michael@0: * Start a new transaction. Nested transactions are not allowed so michael@0: * there must be no transaction currently in progress. michael@0: * This transaction will update the state of the window from which michael@0: * this LayerManager was obtained. michael@0: */ michael@0: virtual void BeginTransaction() = 0; michael@0: /** michael@0: * Start a new transaction. Nested transactions are not allowed so michael@0: * there must be no transaction currently in progress. michael@0: * This transaction will render the contents of the layer tree to michael@0: * the given target context. The rendering will be complete when michael@0: * EndTransaction returns. michael@0: */ michael@0: virtual void BeginTransactionWithTarget(gfxContext* aTarget) = 0; michael@0: michael@0: enum EndTransactionFlags { michael@0: END_DEFAULT = 0, michael@0: END_NO_IMMEDIATE_REDRAW = 1 << 0, // Do not perform the drawing phase michael@0: END_NO_COMPOSITE = 1 << 1, // Do not composite after drawing thebes layer contents. michael@0: END_NO_REMOTE_COMPOSITE = 1 << 2 // Do not schedule a composition with a remote Compositor, if one exists. michael@0: }; michael@0: michael@0: FrameLayerBuilder* GetLayerBuilder() { michael@0: return reinterpret_cast(GetUserData(&gLayerManagerLayerBuilder)); michael@0: } michael@0: michael@0: /** michael@0: * Attempts to end an "empty transaction". There must have been no michael@0: * changes to the layer tree since the BeginTransaction(). michael@0: * It's possible for this to fail; ThebesLayers may need to be updated michael@0: * due to VRAM data being lost, for example. In such cases this method michael@0: * returns false, and the caller must proceed with a normal layer tree michael@0: * update and EndTransaction. michael@0: */ michael@0: virtual bool EndEmptyTransaction(EndTransactionFlags aFlags = END_DEFAULT) = 0; michael@0: michael@0: /** michael@0: * Function called to draw the contents of each ThebesLayer. michael@0: * aRegionToDraw contains the region that needs to be drawn. michael@0: * This would normally be a subregion of the visible region. michael@0: * The callee must draw all of aRegionToDraw. Drawing outside michael@0: * aRegionToDraw will be clipped out or ignored. michael@0: * The callee must draw all of aRegionToDraw. michael@0: * This region is relative to 0,0 in the ThebesLayer. michael@0: * michael@0: * aRegionToInvalidate contains a region whose contents have been michael@0: * changed by the layer manager and which must therefore be invalidated. michael@0: * For example, this could be non-empty if a retained layer internally michael@0: * switches from RGBA to RGB or back ... we might want to repaint it to michael@0: * consistently use subpixel-AA or not. michael@0: * This region is relative to 0,0 in the ThebesLayer. michael@0: * aRegionToInvalidate may contain areas that are outside michael@0: * aRegionToDraw; the callee must ensure that these areas are repainted michael@0: * in the current layer manager transaction or in a later layer michael@0: * manager transaction. michael@0: * michael@0: * aContext must not be used after the call has returned. michael@0: * We guarantee that buffered contents in the visible michael@0: * region are valid once drawing is complete. michael@0: * michael@0: * The origin of aContext is 0,0 in the ThebesLayer. michael@0: */ michael@0: typedef void (* DrawThebesLayerCallback)(ThebesLayer* aLayer, michael@0: gfxContext* aContext, michael@0: const nsIntRegion& aRegionToDraw, michael@0: DrawRegionClip aClip, michael@0: const nsIntRegion& aRegionToInvalidate, michael@0: void* aCallbackData); michael@0: michael@0: /** michael@0: * Finish the construction phase of the transaction, perform the michael@0: * drawing phase, and end the transaction. michael@0: * During the drawing phase, all ThebesLayers in the tree are michael@0: * drawn in tree order, exactly once each, except for those layers michael@0: * where it is known that the visible region is empty. michael@0: */ michael@0: virtual void EndTransaction(DrawThebesLayerCallback aCallback, michael@0: void* aCallbackData, michael@0: EndTransactionFlags aFlags = END_DEFAULT) = 0; michael@0: michael@0: /** michael@0: * Schedule a composition with the remote Compositor, if one exists michael@0: * for this LayerManager. Useful in conjunction with the END_NO_REMOTE_COMPOSITE michael@0: * flag to EndTransaction. michael@0: */ michael@0: virtual void Composite() {} michael@0: michael@0: virtual bool HasShadowManagerInternal() const { return false; } michael@0: bool HasShadowManager() const { return HasShadowManagerInternal(); } michael@0: michael@0: bool IsSnappingEffectiveTransforms() { return mSnapEffectiveTransforms; } michael@0: michael@0: /** michael@0: * Returns true if this LayerManager can properly support layers with michael@0: * SurfaceMode::SURFACE_COMPONENT_ALPHA. This can include disabling component michael@0: * alpha if required. michael@0: */ michael@0: virtual bool AreComponentAlphaLayersEnabled() { return true; } michael@0: michael@0: /** michael@0: * CONSTRUCTION PHASE ONLY michael@0: * Set the root layer. The root layer is initially null. If there is michael@0: * no root layer, EndTransaction won't draw anything. michael@0: */ michael@0: virtual void SetRoot(Layer* aLayer) = 0; michael@0: /** michael@0: * Can be called anytime michael@0: */ michael@0: Layer* GetRoot() { return mRoot; } michael@0: michael@0: /** michael@0: * Does a breadth-first search from the root layer to find the first michael@0: * scrollable layer. michael@0: * Can be called any time. michael@0: */ michael@0: Layer* GetPrimaryScrollableLayer(); michael@0: michael@0: /** michael@0: * Returns a list of all descendant layers for which michael@0: * GetFrameMetrics().IsScrollable() is true. michael@0: */ michael@0: void GetScrollableLayers(nsTArray& aArray); michael@0: michael@0: /** michael@0: * CONSTRUCTION PHASE ONLY michael@0: * Called when a managee has mutated. michael@0: * Subclasses overriding this method must first call their michael@0: * superclass's impl michael@0: */ michael@0: #ifdef DEBUG michael@0: // In debug builds, we check some properties of |aLayer|. michael@0: virtual void Mutated(Layer* aLayer); michael@0: #else michael@0: virtual void Mutated(Layer* aLayer) { } michael@0: #endif michael@0: michael@0: /** michael@0: * Hints that can be used during Thebes layer creation to influence the type michael@0: * or properties of the layer created. michael@0: * michael@0: * NONE: No hint. michael@0: * SCROLLABLE: This layer may represent scrollable content. michael@0: */ michael@0: enum ThebesLayerCreationHint { michael@0: NONE, SCROLLABLE michael@0: }; michael@0: michael@0: /** michael@0: * CONSTRUCTION PHASE ONLY michael@0: * Create a ThebesLayer for this manager's layer tree. michael@0: */ michael@0: virtual already_AddRefed CreateThebesLayer() = 0; michael@0: /** michael@0: * CONSTRUCTION PHASE ONLY michael@0: * Create a ThebesLayer for this manager's layer tree, with a creation hint michael@0: * parameter to help optimise the type of layer created. michael@0: */ michael@0: virtual already_AddRefed CreateThebesLayerWithHint(ThebesLayerCreationHint) { michael@0: return CreateThebesLayer(); michael@0: } michael@0: /** michael@0: * CONSTRUCTION PHASE ONLY michael@0: * Create a ContainerLayer for this manager's layer tree. michael@0: */ michael@0: virtual already_AddRefed CreateContainerLayer() = 0; michael@0: /** michael@0: * CONSTRUCTION PHASE ONLY michael@0: * Create an ImageLayer for this manager's layer tree. michael@0: */ michael@0: virtual already_AddRefed CreateImageLayer() = 0; michael@0: /** michael@0: * CONSTRUCTION PHASE ONLY michael@0: * Create a ColorLayer for this manager's layer tree. michael@0: */ michael@0: virtual already_AddRefed CreateColorLayer() = 0; michael@0: /** michael@0: * CONSTRUCTION PHASE ONLY michael@0: * Create a CanvasLayer for this manager's layer tree. michael@0: */ michael@0: virtual already_AddRefed CreateCanvasLayer() = 0; michael@0: /** michael@0: * CONSTRUCTION PHASE ONLY michael@0: * Create a ReadbackLayer for this manager's layer tree. michael@0: */ michael@0: virtual already_AddRefed CreateReadbackLayer() { return nullptr; } michael@0: /** michael@0: * CONSTRUCTION PHASE ONLY michael@0: * Create a RefLayer for this manager's layer tree. michael@0: */ michael@0: virtual already_AddRefed CreateRefLayer() { return nullptr; } michael@0: michael@0: michael@0: /** michael@0: * Can be called anytime, from any thread. michael@0: * michael@0: * Creates an Image container which forwards its images to the compositor within michael@0: * layer transactions on the main thread. michael@0: */ michael@0: static already_AddRefed CreateImageContainer(); michael@0: michael@0: /** michael@0: * Can be called anytime, from any thread. michael@0: * michael@0: * Tries to create an Image container which forwards its images to the compositor michael@0: * asynchronously using the ImageBridge IPDL protocol. If the protocol is not michael@0: * available, the returned ImageContainer will forward images within layer michael@0: * transactions, just like if it was created with CreateImageContainer(). michael@0: */ michael@0: static already_AddRefed CreateAsynchronousImageContainer(); michael@0: michael@0: /** michael@0: * Type of layer manager his is. This is to be used sparsely in order to michael@0: * avoid a lot of Layers backend specific code. It should be used only when michael@0: * Layers backend specific functionality is necessary. michael@0: */ michael@0: virtual LayersBackend GetBackendType() = 0; michael@0: michael@0: /** michael@0: * Type of layers backend that will be used to composite this layer tree. michael@0: * When compositing is done remotely, then this returns the layers type michael@0: * of the compositor. michael@0: */ michael@0: virtual LayersBackend GetCompositorBackendType() { return GetBackendType(); } michael@0: michael@0: /** michael@0: * Creates a DrawTarget which is optimized for inter-operating with this michael@0: * layer manager. michael@0: */ michael@0: virtual TemporaryRef michael@0: CreateOptimalDrawTarget(const IntSize &aSize, michael@0: SurfaceFormat imageFormat); michael@0: michael@0: /** michael@0: * Creates a DrawTarget for alpha masks which is optimized for inter- michael@0: * operating with this layer manager. In contrast to CreateOptimalDrawTarget, michael@0: * this surface is optimised for drawing alpha only and we assume that michael@0: * drawing the mask is fairly simple. michael@0: */ michael@0: virtual TemporaryRef michael@0: CreateOptimalMaskDrawTarget(const IntSize &aSize); michael@0: michael@0: /** michael@0: * Creates a DrawTarget for use with canvas which is optimized for michael@0: * inter-operating with this layermanager. michael@0: */ michael@0: virtual TemporaryRef michael@0: CreateDrawTarget(const mozilla::gfx::IntSize &aSize, michael@0: mozilla::gfx::SurfaceFormat aFormat); michael@0: michael@0: virtual bool CanUseCanvasLayerForSize(const gfx::IntSize &aSize) { return true; } michael@0: michael@0: /** michael@0: * returns the maximum texture size on this layer backend, or INT32_MAX michael@0: * if there is no maximum michael@0: */ michael@0: virtual int32_t GetMaxTextureSize() const = 0; michael@0: michael@0: /** michael@0: * Return the name of the layer manager's backend. michael@0: */ michael@0: virtual void GetBackendName(nsAString& aName) = 0; michael@0: michael@0: /** michael@0: * This setter can be used anytime. The user data for all keys is michael@0: * initially null. Ownership pases to the layer manager. michael@0: */ michael@0: void SetUserData(void* aKey, LayerUserData* aData) michael@0: { michael@0: mUserData.Add(static_cast(aKey), aData, LayerManagerUserDataDestroy); michael@0: } michael@0: /** michael@0: * This can be used anytime. Ownership passes to the caller! michael@0: */ michael@0: nsAutoPtr RemoveUserData(void* aKey) michael@0: { michael@0: nsAutoPtr d(static_cast(mUserData.Remove(static_cast(aKey)))); michael@0: return d; michael@0: } michael@0: /** michael@0: * This getter can be used anytime. michael@0: */ michael@0: bool HasUserData(void* aKey) michael@0: { michael@0: return mUserData.Has(static_cast(aKey)); michael@0: } michael@0: /** michael@0: * This getter can be used anytime. Ownership is retained by the layer michael@0: * manager. michael@0: */ michael@0: LayerUserData* GetUserData(void* aKey) const michael@0: { michael@0: return static_cast(mUserData.Get(static_cast(aKey))); michael@0: } michael@0: michael@0: /** michael@0: * Must be called outside of a layers transaction. michael@0: * michael@0: * For the subtree rooted at |aSubtree|, this attempts to free up michael@0: * any free-able resources like retained buffers, but may do nothing michael@0: * at all. After this call, the layer tree is left in an undefined michael@0: * state; the layers in |aSubtree|'s subtree may no longer have michael@0: * buffers with valid content and may no longer be able to draw michael@0: * their visible and valid regions. michael@0: * michael@0: * In general, a painting or forwarding transaction on |this| must michael@0: * complete on the tree before it returns to a valid state. michael@0: * michael@0: * Resource freeing begins from |aSubtree| or |mRoot| if |aSubtree| michael@0: * is null. |aSubtree|'s manager must be this. michael@0: */ michael@0: virtual void ClearCachedResources(Layer* aSubtree = nullptr) {} michael@0: michael@0: /** michael@0: * Flag the next paint as the first for a document. michael@0: */ michael@0: virtual void SetIsFirstPaint() {} michael@0: michael@0: /** michael@0: * Make sure that the previous transaction has been entirely michael@0: * completed. michael@0: * michael@0: * Note: This may sychronously wait on a remote compositor michael@0: * to complete rendering. michael@0: */ michael@0: virtual void FlushRendering() { } michael@0: michael@0: /** michael@0: * Checks if we need to invalidate the OS widget to trigger michael@0: * painting when updating this layer manager. michael@0: */ michael@0: virtual bool NeedsWidgetInvalidation() { return true; } michael@0: michael@0: virtual const char* Name() const { return "???"; } michael@0: michael@0: /** michael@0: * Dump information about this layer manager and its managed tree to michael@0: * aFile, which defaults to stderr. michael@0: */ michael@0: void Dump(FILE* aFile=nullptr, const char* aPrefix="", bool aDumpHtml=false); michael@0: /** michael@0: * Dump information about just this layer manager itself to aFile, michael@0: * which defaults to stderr. michael@0: */ michael@0: void DumpSelf(FILE* aFile=nullptr, const char* aPrefix=""); michael@0: michael@0: /** michael@0: * Log information about this layer manager and its managed tree to michael@0: * the NSPR log (if enabled for "Layers"). michael@0: */ michael@0: void Log(const char* aPrefix=""); michael@0: /** michael@0: * Log information about just this layer manager itself to the NSPR michael@0: * log (if enabled for "Layers"). michael@0: */ michael@0: void LogSelf(const char* aPrefix=""); michael@0: michael@0: /** michael@0: * Record (and return) frame-intervals and paint-times for frames which were presented michael@0: * between calling StartFrameTimeRecording and StopFrameTimeRecording. michael@0: * michael@0: * - Uses a cyclic buffer and serves concurrent consumers, so if Stop is called too late michael@0: * (elements were overwritten since Start), result is considered invalid and hence empty. michael@0: * - Buffer is capable of holding 10 seconds @ 60fps (or more if frames were less frequent). michael@0: * Can be changed (up to 1 hour) via pref: toolkit.framesRecording.bufferSize. michael@0: * - Note: the first frame-interval may be longer than expected because last frame michael@0: * might have been presented some time before calling StartFrameTimeRecording. michael@0: */ michael@0: michael@0: /** michael@0: * Returns a handle which represents current recording start position. michael@0: */ michael@0: virtual uint32_t StartFrameTimeRecording(int32_t aBufferSize); michael@0: michael@0: /** michael@0: * Clears, then populates aFrameIntervals with the recorded frame timing michael@0: * data. The array will be empty if data was overwritten since michael@0: * aStartIndex was obtained. michael@0: */ michael@0: virtual void StopFrameTimeRecording(uint32_t aStartIndex, michael@0: nsTArray& aFrameIntervals); michael@0: michael@0: void RecordFrame(); michael@0: void PostPresent(); michael@0: michael@0: void BeginTabSwitch(); michael@0: michael@0: static bool IsLogEnabled(); michael@0: static PRLogModuleInfo* GetLog() { return sLog; } michael@0: michael@0: bool IsCompositingCheap(LayersBackend aBackend) michael@0: { michael@0: // LayersBackend::LAYERS_NONE is an error state, but in that case we should try to michael@0: // avoid loading the compositor! michael@0: return LayersBackend::LAYERS_BASIC != aBackend && LayersBackend::LAYERS_NONE != aBackend; michael@0: } michael@0: michael@0: virtual bool IsCompositingCheap() { return true; } michael@0: michael@0: bool IsInTransaction() const { return mInTransaction; } michael@0: michael@0: virtual void SetRegionToClear(const nsIntRegion& aRegion) michael@0: { michael@0: mRegionToClear = aRegion; michael@0: } michael@0: michael@0: protected: michael@0: nsRefPtr mRoot; michael@0: gfx::UserData mUserData; michael@0: bool mDestroyed; michael@0: bool mSnapEffectiveTransforms; michael@0: michael@0: nsIntRegion mRegionToClear; michael@0: michael@0: // Protected destructor, to discourage deletion outside of Release(): michael@0: virtual ~LayerManager() {} michael@0: michael@0: // Print interesting information about this into aTo. Internally michael@0: // used to implement Dump*() and Log*(). michael@0: virtual nsACString& PrintInfo(nsACString& aTo, const char* aPrefix); michael@0: michael@0: static void InitLog(); michael@0: static PRLogModuleInfo* sLog; michael@0: uint64_t mId; michael@0: bool mInTransaction; michael@0: private: michael@0: struct FramesTimingRecording michael@0: { michael@0: // Stores state and data for frame intervals and paint times recording. michael@0: // see LayerManager::StartFrameTimeRecording() at Layers.cpp for more details. michael@0: FramesTimingRecording() michael@0: : mIsPaused(true) michael@0: , mNextIndex(0) michael@0: {} michael@0: bool mIsPaused; michael@0: uint32_t mNextIndex; michael@0: TimeStamp mLastFrameTime; michael@0: nsTArray mIntervals; michael@0: uint32_t mLatestStartIndex; michael@0: uint32_t mCurrentRunStartIndex; michael@0: }; michael@0: FramesTimingRecording mRecording; michael@0: michael@0: TimeStamp mTabSwitchStart; michael@0: }; michael@0: michael@0: typedef InfallibleTArray AnimationArray; michael@0: michael@0: struct AnimData { michael@0: InfallibleTArray mStartValues; michael@0: InfallibleTArray mEndValues; michael@0: InfallibleTArray > mFunctions; michael@0: }; michael@0: michael@0: /** michael@0: * A Layer represents anything that can be rendered onto a destination michael@0: * surface. michael@0: */ michael@0: class Layer { michael@0: NS_INLINE_DECL_REFCOUNTING(Layer) michael@0: michael@0: public: michael@0: // Keep these in alphabetical order michael@0: enum LayerType { michael@0: TYPE_CANVAS, michael@0: TYPE_COLOR, michael@0: TYPE_CONTAINER, michael@0: TYPE_IMAGE, michael@0: TYPE_READBACK, michael@0: TYPE_REF, michael@0: TYPE_SHADOW, michael@0: TYPE_THEBES michael@0: }; michael@0: michael@0: /** michael@0: * Returns the LayerManager this Layer belongs to. Note that the layer michael@0: * manager might be in a destroyed state, at which point it's only michael@0: * valid to set/get user data from it. michael@0: */ michael@0: LayerManager* Manager() { return mManager; } michael@0: michael@0: enum { michael@0: /** michael@0: * If this is set, the caller is promising that by the end of this michael@0: * transaction the entire visible region (as specified by michael@0: * SetVisibleRegion) will be filled with opaque content. michael@0: */ michael@0: CONTENT_OPAQUE = 0x01, michael@0: /** michael@0: * If this is set, the caller is notifying that the contents of this layer michael@0: * require per-component alpha for optimal fidelity. However, there is no michael@0: * guarantee that component alpha will be supported for this layer at michael@0: * paint time. michael@0: * This should never be set at the same time as CONTENT_OPAQUE. michael@0: */ michael@0: CONTENT_COMPONENT_ALPHA = 0x02, michael@0: michael@0: /** michael@0: * If this is set then this layer is part of a preserve-3d group, and should michael@0: * be sorted with sibling layers that are also part of the same group. michael@0: */ michael@0: CONTENT_PRESERVE_3D = 0x04, michael@0: /** michael@0: * This indicates that the transform may be changed on during an empty michael@0: * transaction where there is no possibility of redrawing the content, so the michael@0: * implementation should be ready for that. michael@0: */ michael@0: CONTENT_MAY_CHANGE_TRANSFORM = 0x08, michael@0: michael@0: /** michael@0: * Disable subpixel AA for this layer. This is used if the display isn't suited michael@0: * for subpixel AA like hidpi or rotated content. michael@0: */ michael@0: CONTENT_DISABLE_SUBPIXEL_AA = 0x10 michael@0: }; michael@0: /** michael@0: * CONSTRUCTION PHASE ONLY michael@0: * This lets layout make some promises about what will be drawn into the michael@0: * visible region of the ThebesLayer. This enables internal quality michael@0: * and performance optimizations. michael@0: */ michael@0: void SetContentFlags(uint32_t aFlags) michael@0: { michael@0: NS_ASSERTION((aFlags & (CONTENT_OPAQUE | CONTENT_COMPONENT_ALPHA)) != michael@0: (CONTENT_OPAQUE | CONTENT_COMPONENT_ALPHA), michael@0: "Can't be opaque and require component alpha"); michael@0: if (mContentFlags != aFlags) { michael@0: MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) ContentFlags", this)); michael@0: mContentFlags = aFlags; michael@0: Mutated(); michael@0: } michael@0: } michael@0: /** michael@0: * CONSTRUCTION PHASE ONLY michael@0: * Tell this layer which region will be visible. The visible region michael@0: * is a region which contains all the contents of the layer that can michael@0: * actually affect the rendering of the window. It can exclude areas michael@0: * that are covered by opaque contents of other layers, and it can michael@0: * exclude areas where this layer simply contains no content at all. michael@0: * (This can be an overapproximation to the "true" visible region.) michael@0: * michael@0: * There is no general guarantee that drawing outside the bounds of the michael@0: * visible region will be ignored. So if a layer draws outside the bounds michael@0: * of its visible region, it needs to ensure that what it draws is valid. michael@0: */ michael@0: virtual void SetVisibleRegion(const nsIntRegion& aRegion) michael@0: { michael@0: if (!mVisibleRegion.IsEqual(aRegion)) { michael@0: MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) VisibleRegion was %s is %s", this, michael@0: mVisibleRegion.ToString().get(), aRegion.ToString().get())); michael@0: mVisibleRegion = aRegion; michael@0: Mutated(); michael@0: } michael@0: } michael@0: michael@0: /* michael@0: * Compositor event handling michael@0: * ========================= michael@0: * When a touch-start event (or similar) is sent to the AsyncPanZoomController, michael@0: * it needs to decide whether the event should be sent to the main thread. michael@0: * Each layer has a list of event handling regions. When the compositor needs michael@0: * to determine how to handle a touch event, it scans the layer tree from top michael@0: * to bottom in z-order (traversing children before their parents). Points michael@0: * outside the clip region for a layer cause that layer (and its subtree) michael@0: * to be ignored. If a layer has a mask layer, and that mask layer's alpha michael@0: * value is zero at the event point, then the layer and its subtree should michael@0: * be ignored. michael@0: * For each layer, if the point is outside its hit region, we ignore the layer michael@0: * and move onto the next. If the point is inside its hit region but michael@0: * outside the dispatch-to-content region, we can initiate a gesture without michael@0: * consulting the content thread. Otherwise we must dispatch the event to michael@0: * content. michael@0: */ michael@0: /** michael@0: * CONSTRUCTION PHASE ONLY michael@0: * Set the event handling region. michael@0: */ michael@0: void SetEventRegions(const EventRegions& aRegions) michael@0: { michael@0: if (mEventRegions != aRegions) { michael@0: MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) eventregions were %s, now %s", this, michael@0: mEventRegions.ToString().get(), aRegions.ToString().get())); michael@0: mEventRegions = aRegions; michael@0: Mutated(); michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * CONSTRUCTION PHASE ONLY michael@0: * Set the opacity which will be applied to this layer as it michael@0: * is composited to the destination. michael@0: */ michael@0: void SetOpacity(float aOpacity) michael@0: { michael@0: if (mOpacity != aOpacity) { michael@0: MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) Opacity", this)); michael@0: mOpacity = aOpacity; michael@0: Mutated(); michael@0: } michael@0: } michael@0: michael@0: void SetMixBlendMode(gfx::CompositionOp aMixBlendMode) michael@0: { michael@0: if (mMixBlendMode != aMixBlendMode) { michael@0: MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) MixBlendMode", this)); michael@0: mMixBlendMode = aMixBlendMode; michael@0: Mutated(); michael@0: } michael@0: } michael@0: michael@0: void DeprecatedSetMixBlendMode(gfxContext::GraphicsOperator aMixBlendMode) michael@0: { michael@0: SetMixBlendMode(gfx::CompositionOpForOp(aMixBlendMode)); michael@0: } michael@0: michael@0: void SetForceIsolatedGroup(bool aForceIsolatedGroup) michael@0: { michael@0: if(mForceIsolatedGroup != aForceIsolatedGroup) { michael@0: MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) ForceIsolatedGroup", this)); michael@0: mForceIsolatedGroup = aForceIsolatedGroup; michael@0: Mutated(); michael@0: } michael@0: } michael@0: michael@0: bool GetForceIsolatedGroup() const michael@0: { michael@0: return mForceIsolatedGroup; michael@0: } michael@0: michael@0: /** michael@0: * CONSTRUCTION PHASE ONLY michael@0: * Set a clip rect which will be applied to this layer as it is michael@0: * composited to the destination. The coordinates are relative to michael@0: * the parent layer (i.e. the contents of this layer michael@0: * are transformed before this clip rect is applied). michael@0: * For the root layer, the coordinates are relative to the widget, michael@0: * in device pixels. michael@0: * If aRect is null no clipping will be performed. michael@0: */ michael@0: void SetClipRect(const nsIntRect* aRect) michael@0: { michael@0: if (mUseClipRect) { michael@0: if (!aRect) { michael@0: MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) ClipRect was %d,%d,%d,%d is ", this, michael@0: mClipRect.x, mClipRect.y, mClipRect.width, mClipRect.height)); michael@0: mUseClipRect = false; michael@0: Mutated(); michael@0: } else { michael@0: if (!aRect->IsEqualEdges(mClipRect)) { michael@0: MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) ClipRect was %d,%d,%d,%d is %d,%d,%d,%d", this, michael@0: mClipRect.x, mClipRect.y, mClipRect.width, mClipRect.height, michael@0: aRect->x, aRect->y, aRect->width, aRect->height)); michael@0: mClipRect = *aRect; michael@0: Mutated(); michael@0: } michael@0: } michael@0: } else { michael@0: if (aRect) { michael@0: MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) ClipRect was is %d,%d,%d,%d", this, michael@0: aRect->x, aRect->y, aRect->width, aRect->height)); michael@0: mUseClipRect = true; michael@0: mClipRect = *aRect; michael@0: Mutated(); michael@0: } michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * CONSTRUCTION PHASE ONLY michael@0: * Set a layer to mask this layer. michael@0: * michael@0: * The mask layer should be applied using its effective transform (after it michael@0: * is calculated by ComputeEffectiveTransformForMaskLayer), this should use michael@0: * this layer's parent's transform and the mask layer's transform, but not michael@0: * this layer's. That is, the mask layer is specified relative to this layer's michael@0: * position in it's parent layer's coord space. michael@0: * Currently, only 2D translations are supported for the mask layer transform. michael@0: * michael@0: * Ownership of aMaskLayer passes to this. michael@0: * Typical use would be an ImageLayer with an alpha image used for masking. michael@0: * See also ContainerState::BuildMaskLayer in FrameLayerBuilder.cpp. michael@0: */ michael@0: void SetMaskLayer(Layer* aMaskLayer) michael@0: { michael@0: #ifdef DEBUG michael@0: if (aMaskLayer) { michael@0: bool maskIs2D = aMaskLayer->GetTransform().CanDraw2D(); michael@0: NS_ASSERTION(maskIs2D, "Mask layer has invalid transform."); michael@0: } michael@0: #endif michael@0: michael@0: if (mMaskLayer != aMaskLayer) { michael@0: MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) MaskLayer", this)); michael@0: mMaskLayer = aMaskLayer; michael@0: Mutated(); michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * CONSTRUCTION PHASE ONLY michael@0: * Tell this layer what its transform should be. The transformation michael@0: * is applied when compositing the layer into its parent container. michael@0: */ michael@0: void SetBaseTransform(const gfx::Matrix4x4& aMatrix) michael@0: { michael@0: NS_ASSERTION(!aMatrix.IsSingular(), michael@0: "Shouldn't be trying to draw with a singular matrix!"); michael@0: mPendingTransform = nullptr; michael@0: if (mTransform == aMatrix) { michael@0: return; michael@0: } michael@0: MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) BaseTransform", this)); michael@0: mTransform = aMatrix; michael@0: Mutated(); michael@0: } michael@0: michael@0: /** michael@0: * Can be called at any time. michael@0: * michael@0: * Like SetBaseTransform(), but can be called before the next michael@0: * transform (i.e. outside an open transaction). Semantically, this michael@0: * method enqueues a new transform value to be set immediately after michael@0: * the next transaction is opened. michael@0: */ michael@0: void SetBaseTransformForNextTransaction(const gfx::Matrix4x4& aMatrix) michael@0: { michael@0: mPendingTransform = new gfx::Matrix4x4(aMatrix); michael@0: } michael@0: michael@0: void SetPostScale(float aXScale, float aYScale) michael@0: { michael@0: if (mPostXScale == aXScale && mPostYScale == aYScale) { michael@0: return; michael@0: } michael@0: MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) PostScale", this)); michael@0: mPostXScale = aXScale; michael@0: mPostYScale = aYScale; michael@0: Mutated(); michael@0: } michael@0: michael@0: /** michael@0: * CONSTRUCTION PHASE ONLY michael@0: * A layer is "fixed position" when it draws content from a content michael@0: * (not chrome) document, the topmost content document has a root scrollframe michael@0: * with a displayport, but the layer does not move when that displayport scrolls. michael@0: */ michael@0: void SetIsFixedPosition(bool aFixedPosition) michael@0: { michael@0: if (mIsFixedPosition != aFixedPosition) { michael@0: MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) IsFixedPosition", this)); michael@0: mIsFixedPosition = aFixedPosition; michael@0: Mutated(); michael@0: } michael@0: } michael@0: michael@0: // Call AddAnimation to add a new animation to this layer from layout code. michael@0: // Caller must fill in all the properties of the returned animation. michael@0: Animation* AddAnimation(); michael@0: // ClearAnimations clears animations on this layer. michael@0: void ClearAnimations(); michael@0: // This is only called when the layer tree is updated. Do not call this from michael@0: // layout code. To add an animation to this layer, use AddAnimation. michael@0: void SetAnimations(const AnimationArray& aAnimations); michael@0: michael@0: // These are a parallel to AddAnimation and clearAnimations, except michael@0: // they add pending animations that apply only when the next michael@0: // transaction is begun. (See also michael@0: // SetBaseTransformForNextTransaction.) michael@0: Animation* AddAnimationForNextTransaction(); michael@0: void ClearAnimationsForNextTransaction(); michael@0: michael@0: /** michael@0: * CONSTRUCTION PHASE ONLY michael@0: * If a layer is "fixed position", this determines which point on the layer michael@0: * is considered the "anchor" point, that is, the point which remains in the michael@0: * same position when compositing the layer tree with a transformation michael@0: * (such as when asynchronously scrolling and zooming). michael@0: */ michael@0: void SetFixedPositionAnchor(const LayerPoint& aAnchor) michael@0: { michael@0: if (mAnchor != aAnchor) { michael@0: MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) FixedPositionAnchor", this)); michael@0: mAnchor = aAnchor; michael@0: Mutated(); michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * CONSTRUCTION PHASE ONLY michael@0: * If a layer represents a fixed position element or elements that are on michael@0: * a document that has had fixed position element margins set on it, these michael@0: * will be mirrored here. This allows for asynchronous animation of the michael@0: * margins by reconciling the difference between this value and a value that michael@0: * is updated more frequently. michael@0: * If the left or top margins are negative, it means that the elements this michael@0: * layer represents are auto-positioned, and so fixed position margins should michael@0: * not have an effect on the corresponding axis. michael@0: */ michael@0: void SetFixedPositionMargins(const LayerMargin& aMargins) michael@0: { michael@0: if (mMargins != aMargins) { michael@0: MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) FixedPositionMargins", this)); michael@0: mMargins = aMargins; michael@0: Mutated(); michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * CONSTRUCTION PHASE ONLY michael@0: * If a layer is "sticky position", |aScrollId| holds the scroll identifier michael@0: * of the scrollable content that contains it. The difference between the two michael@0: * rectangles |aOuter| and |aInner| is treated as two intervals in each michael@0: * dimension, with the current scroll position at the origin. For each michael@0: * dimension, while that component of the scroll position lies within either michael@0: * interval, the layer should not move relative to its scrolling container. michael@0: */ michael@0: void SetStickyPositionData(FrameMetrics::ViewID aScrollId, LayerRect aOuter, michael@0: LayerRect aInner) michael@0: { michael@0: if (!mStickyPositionData || michael@0: !mStickyPositionData->mOuter.IsEqualEdges(aOuter) || michael@0: !mStickyPositionData->mInner.IsEqualEdges(aInner)) { michael@0: MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) StickyPositionData", this)); michael@0: if (!mStickyPositionData) { michael@0: mStickyPositionData = new StickyPositionData; michael@0: } michael@0: mStickyPositionData->mScrollId = aScrollId; michael@0: mStickyPositionData->mOuter = aOuter; michael@0: mStickyPositionData->mInner = aInner; michael@0: Mutated(); michael@0: } michael@0: } michael@0: michael@0: enum ScrollDirection { michael@0: NONE, michael@0: VERTICAL, michael@0: HORIZONTAL michael@0: }; michael@0: michael@0: /** michael@0: * CONSTRUCTION PHASE ONLY michael@0: * If a layer is a scrollbar layer, |aScrollId| holds the scroll identifier michael@0: * of the scrollable content that the scrollbar is for. michael@0: */ michael@0: void SetScrollbarData(FrameMetrics::ViewID aScrollId, ScrollDirection aDir) michael@0: { michael@0: if (mScrollbarTargetId != aScrollId || michael@0: mScrollbarDirection != aDir) { michael@0: MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) ScrollbarData", this)); michael@0: mScrollbarTargetId = aScrollId; michael@0: mScrollbarDirection = aDir; michael@0: Mutated(); michael@0: } michael@0: } michael@0: michael@0: // These getters can be used anytime. michael@0: float GetOpacity() { return mOpacity; } michael@0: gfx::CompositionOp GetMixBlendMode() const { return mMixBlendMode; } michael@0: const nsIntRect* GetClipRect() { return mUseClipRect ? &mClipRect : nullptr; } michael@0: uint32_t GetContentFlags() { return mContentFlags; } michael@0: const nsIntRegion& GetVisibleRegion() { return mVisibleRegion; } michael@0: const EventRegions& GetEventRegions() const { return mEventRegions; } michael@0: ContainerLayer* GetParent() { return mParent; } michael@0: Layer* GetNextSibling() { return mNextSibling; } michael@0: const Layer* GetNextSibling() const { return mNextSibling; } michael@0: Layer* GetPrevSibling() { return mPrevSibling; } michael@0: const Layer* GetPrevSibling() const { return mPrevSibling; } michael@0: virtual Layer* GetFirstChild() const { return nullptr; } michael@0: virtual Layer* GetLastChild() const { return nullptr; } michael@0: const gfx::Matrix4x4 GetTransform() const; michael@0: const gfx::Matrix4x4& GetBaseTransform() const { return mTransform; } michael@0: float GetPostXScale() const { return mPostXScale; } michael@0: float GetPostYScale() const { return mPostYScale; } michael@0: bool GetIsFixedPosition() { return mIsFixedPosition; } michael@0: bool GetIsStickyPosition() { return mStickyPositionData; } michael@0: LayerPoint GetFixedPositionAnchor() { return mAnchor; } michael@0: const LayerMargin& GetFixedPositionMargins() { return mMargins; } michael@0: FrameMetrics::ViewID GetStickyScrollContainerId() { return mStickyPositionData->mScrollId; } michael@0: const LayerRect& GetStickyScrollRangeOuter() { return mStickyPositionData->mOuter; } michael@0: const LayerRect& GetStickyScrollRangeInner() { return mStickyPositionData->mInner; } michael@0: FrameMetrics::ViewID GetScrollbarTargetContainerId() { return mScrollbarTargetId; } michael@0: ScrollDirection GetScrollbarDirection() { return mScrollbarDirection; } michael@0: Layer* GetMaskLayer() const { return mMaskLayer; } michael@0: michael@0: // Note that all lengths in animation data are either in CSS pixels or app michael@0: // units and must be converted to device pixels by the compositor. michael@0: AnimationArray& GetAnimations() { return mAnimations; } michael@0: InfallibleTArray& GetAnimationData() { return mAnimationData; } michael@0: michael@0: uint64_t GetAnimationGeneration() { return mAnimationGeneration; } michael@0: void SetAnimationGeneration(uint64_t aCount) { mAnimationGeneration = aCount; } michael@0: michael@0: /** michael@0: * Returns the local transform for this layer: either mTransform or, michael@0: * for shadow layers, GetShadowTransform() michael@0: */ michael@0: const gfx::Matrix4x4 GetLocalTransform(); michael@0: michael@0: /** michael@0: * Returns the local opacity for this layer: either mOpacity or, michael@0: * for shadow layers, GetShadowOpacity() michael@0: */ michael@0: const float GetLocalOpacity(); michael@0: michael@0: /** michael@0: * DRAWING PHASE ONLY michael@0: * michael@0: * Apply pending changes to layers before drawing them, if those michael@0: * pending changes haven't been overridden by later changes. michael@0: */ michael@0: void ApplyPendingUpdatesToSubtree(); michael@0: michael@0: /** michael@0: * DRAWING PHASE ONLY michael@0: * michael@0: * Write layer-subtype-specific attributes into aAttrs. Used to michael@0: * synchronize layer attributes to their shadows'. michael@0: */ michael@0: virtual void FillSpecificAttributes(SpecificLayerAttributes& aAttrs) { } michael@0: michael@0: // Returns true if it's OK to save the contents of aLayer in an michael@0: // opaque surface (a surface without an alpha channel). michael@0: // If we can use a surface without an alpha channel, we should, because michael@0: // it will often make painting of antialiased text faster and higher michael@0: // quality. michael@0: bool CanUseOpaqueSurface(); michael@0: michael@0: SurfaceMode GetSurfaceMode() michael@0: { michael@0: if (CanUseOpaqueSurface()) michael@0: return SurfaceMode::SURFACE_OPAQUE; michael@0: if (mContentFlags & CONTENT_COMPONENT_ALPHA) michael@0: return SurfaceMode::SURFACE_COMPONENT_ALPHA; michael@0: return SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA; michael@0: } michael@0: michael@0: /** michael@0: * This setter can be used anytime. The user data for all keys is michael@0: * initially null. Ownership pases to the layer manager. michael@0: */ michael@0: void SetUserData(void* aKey, LayerUserData* aData) michael@0: { michael@0: mUserData.Add(static_cast(aKey), aData, LayerManagerUserDataDestroy); michael@0: } michael@0: /** michael@0: * This can be used anytime. Ownership passes to the caller! michael@0: */ michael@0: nsAutoPtr RemoveUserData(void* aKey) michael@0: { michael@0: nsAutoPtr d(static_cast(mUserData.Remove(static_cast(aKey)))); michael@0: return d; michael@0: } michael@0: /** michael@0: * This getter can be used anytime. michael@0: */ michael@0: bool HasUserData(void* aKey) michael@0: { michael@0: return mUserData.Has(static_cast(aKey)); michael@0: } michael@0: /** michael@0: * This getter can be used anytime. Ownership is retained by the layer michael@0: * manager. michael@0: */ michael@0: LayerUserData* GetUserData(void* aKey) const michael@0: { michael@0: return static_cast(mUserData.Get(static_cast(aKey))); michael@0: } michael@0: michael@0: /** michael@0: * |Disconnect()| is used by layers hooked up over IPC. It may be michael@0: * called at any time, and may not be called at all. Using an michael@0: * IPC-enabled layer after Destroy() (drawing etc.) results in a michael@0: * safe no-op; no crashy or uaf etc. michael@0: * michael@0: * XXX: this interface is essentially LayerManager::Destroy, but at michael@0: * Layer granularity. It might be beneficial to unify them. michael@0: */ michael@0: virtual void Disconnect() {} michael@0: michael@0: /** michael@0: * Dynamic downcast to a Thebes layer. Returns null if this is not michael@0: * a ThebesLayer. michael@0: */ michael@0: virtual ThebesLayer* AsThebesLayer() { return nullptr; } michael@0: michael@0: /** michael@0: * Dynamic cast to a ContainerLayer. Returns null if this is not michael@0: * a ContainerLayer. michael@0: */ michael@0: virtual ContainerLayer* AsContainerLayer() { return nullptr; } michael@0: virtual const ContainerLayer* AsContainerLayer() const { return nullptr; } michael@0: michael@0: /** michael@0: * Dynamic cast to a RefLayer. Returns null if this is not a michael@0: * RefLayer. michael@0: */ michael@0: virtual RefLayer* AsRefLayer() { return nullptr; } michael@0: michael@0: /** michael@0: * Dynamic cast to a Color. Returns null if this is not a michael@0: * ColorLayer. michael@0: */ michael@0: virtual ColorLayer* AsColorLayer() { return nullptr; } michael@0: michael@0: /** michael@0: * Dynamic cast to a LayerComposite. Return null if this is not a michael@0: * LayerComposite. Can be used anytime. michael@0: */ michael@0: virtual LayerComposite* AsLayerComposite() { return nullptr; } michael@0: michael@0: /** michael@0: * Dynamic cast to a ShadowableLayer. Return null if this is not a michael@0: * ShadowableLayer. Can be used anytime. michael@0: */ michael@0: virtual ShadowableLayer* AsShadowableLayer() { return nullptr; } michael@0: michael@0: // These getters can be used anytime. They return the effective michael@0: // values that should be used when drawing this layer to screen, michael@0: // accounting for this layer possibly being a shadow. michael@0: const nsIntRect* GetEffectiveClipRect(); michael@0: const nsIntRegion& GetEffectiveVisibleRegion(); michael@0: michael@0: /** michael@0: * Returns the product of the opacities of this layer and all ancestors up michael@0: * to and excluding the nearest ancestor that has UseIntermediateSurface() set. michael@0: */ michael@0: float GetEffectiveOpacity(); michael@0: michael@0: /** michael@0: * Returns the blendmode of this layer. michael@0: */ michael@0: gfx::CompositionOp GetEffectiveMixBlendMode(); michael@0: gfxContext::GraphicsOperator DeprecatedGetEffectiveMixBlendMode(); michael@0: michael@0: /** michael@0: * This returns the effective transform computed by michael@0: * ComputeEffectiveTransforms. Typically this is a transform that transforms michael@0: * this layer all the way to some intermediate surface or destination michael@0: * surface. For non-BasicLayers this will be a transform to the nearest michael@0: * ancestor with UseIntermediateSurface() (or to the root, if there is no michael@0: * such ancestor), but for BasicLayers it's different. michael@0: */ michael@0: const gfx::Matrix4x4& GetEffectiveTransform() const { return mEffectiveTransform; } michael@0: michael@0: /** michael@0: * @param aTransformToSurface the composition of the transforms michael@0: * from the parent layer (if any) to the destination pixel grid. michael@0: * michael@0: * Computes mEffectiveTransform for this layer and all its descendants. michael@0: * mEffectiveTransform transforms this layer up to the destination michael@0: * pixel grid (whatever aTransformToSurface is relative to). michael@0: * michael@0: * We promise that when this is called on a layer, all ancestor layers michael@0: * have already had ComputeEffectiveTransforms called. michael@0: */ michael@0: virtual void ComputeEffectiveTransforms(const gfx::Matrix4x4& aTransformToSurface) = 0; michael@0: michael@0: /** michael@0: * computes the effective transform for a mask layer, if this layer has one michael@0: */ michael@0: void ComputeEffectiveTransformForMaskLayer(const gfx::Matrix4x4& aTransformToSurface); michael@0: michael@0: /** michael@0: * Calculate the scissor rect required when rendering this layer. michael@0: * Returns a rectangle relative to the intermediate surface belonging to the michael@0: * nearest ancestor that has an intermediate surface, or relative to the root michael@0: * viewport if no ancestor has an intermediate surface, corresponding to the michael@0: * clip rect for this layer intersected with aCurrentScissorRect. michael@0: * If no ancestor has an intermediate surface, the clip rect is transformed michael@0: * by aWorldTransform before being combined with aCurrentScissorRect, if michael@0: * aWorldTransform is non-null. michael@0: */ michael@0: nsIntRect CalculateScissorRect(const nsIntRect& aCurrentScissorRect, michael@0: const gfx::Matrix* aWorldTransform); michael@0: michael@0: virtual const char* Name() const =0; michael@0: virtual LayerType GetType() const =0; michael@0: michael@0: /** michael@0: * Only the implementation should call this. This is per-implementation michael@0: * private data. Normally, all layers with a given layer manager michael@0: * use the same type of ImplData. michael@0: */ michael@0: void* ImplData() { return mImplData; } michael@0: michael@0: /** michael@0: * Only the implementation should use these methods. michael@0: */ michael@0: void SetParent(ContainerLayer* aParent) { mParent = aParent; } michael@0: void SetNextSibling(Layer* aSibling) { mNextSibling = aSibling; } michael@0: void SetPrevSibling(Layer* aSibling) { mPrevSibling = aSibling; } michael@0: michael@0: /** michael@0: * Dump information about this layer manager and its managed tree to michael@0: * aFile, which defaults to stderr. michael@0: */ michael@0: void Dump(FILE* aFile=nullptr, const char* aPrefix="", bool aDumpHtml=false); michael@0: /** michael@0: * Dump information about just this layer manager itself to aFile, michael@0: * which defaults to stderr. michael@0: */ michael@0: void DumpSelf(FILE* aFile=nullptr, const char* aPrefix=""); michael@0: michael@0: /** michael@0: * Log information about this layer manager and its managed tree to michael@0: * the NSPR log (if enabled for "Layers"). michael@0: */ michael@0: void Log(const char* aPrefix=""); michael@0: /** michael@0: * Log information about just this layer manager itself to the NSPR michael@0: * log (if enabled for "Layers"). michael@0: */ michael@0: void LogSelf(const char* aPrefix=""); michael@0: michael@0: // Print interesting information about this into aTo. Internally michael@0: // used to implement Dump*() and Log*(). If subclasses have michael@0: // additional interesting properties, they should override this with michael@0: // an implementation that first calls the base implementation then michael@0: // appends additional info to aTo. michael@0: virtual nsACString& PrintInfo(nsACString& aTo, const char* aPrefix); michael@0: michael@0: static bool IsLogEnabled() { return LayerManager::IsLogEnabled(); } michael@0: michael@0: /** michael@0: * Returns the current area of the layer (in layer-space coordinates) michael@0: * marked as needed to be recomposited. michael@0: */ michael@0: const nsIntRegion& GetInvalidRegion() { return mInvalidRegion; } michael@0: const void SetInvalidRegion(const nsIntRegion& aRect) { mInvalidRegion = aRect; } michael@0: michael@0: /** michael@0: * Mark the entirety of the layer's visible region as being invalid. michael@0: */ michael@0: void SetInvalidRectToVisibleRegion() { mInvalidRegion = GetVisibleRegion(); } michael@0: michael@0: /** michael@0: * Adds to the current invalid rect. michael@0: */ michael@0: void AddInvalidRect(const nsIntRect& aRect) { mInvalidRegion.Or(mInvalidRegion, aRect); } michael@0: michael@0: /** michael@0: * Clear the invalid rect, marking the layer as being identical to what is currently michael@0: * composited. michael@0: */ michael@0: void ClearInvalidRect() { mInvalidRegion.SetEmpty(); } michael@0: michael@0: void ApplyPendingUpdatesForThisTransaction(); michael@0: michael@0: #ifdef DEBUG michael@0: void SetDebugColorIndex(uint32_t aIndex) { mDebugColorIndex = aIndex; } michael@0: uint32_t GetDebugColorIndex() { return mDebugColorIndex; } michael@0: #endif michael@0: michael@0: virtual LayerRenderState GetRenderState() { return LayerRenderState(); } michael@0: michael@0: michael@0: void Mutated() michael@0: { michael@0: mManager->Mutated(this); michael@0: } michael@0: michael@0: protected: michael@0: Layer(LayerManager* aManager, void* aImplData); michael@0: michael@0: // Protected destructor, to discourage deletion outside of Release(): michael@0: virtual ~Layer(); michael@0: michael@0: /** michael@0: * We can snap layer transforms for two reasons: michael@0: * 1) To avoid unnecessary resampling when a transform is a translation michael@0: * by a non-integer number of pixels. michael@0: * Snapping the translation to an integer number of pixels avoids michael@0: * blurring the layer and can be faster to composite. michael@0: * 2) When a layer is used to render a rectangular object, we need to michael@0: * emulate the rendering of rectangular inactive content and snap the michael@0: * edges of the rectangle to pixel boundaries. This is both to ensure michael@0: * layer rendering is consistent with inactive content rendering, and to michael@0: * avoid seams. michael@0: * This function implements type 1 snapping. If aTransform is a 2D michael@0: * translation, and this layer's layer manager has enabled snapping michael@0: * (which is the default), return aTransform with the translation snapped michael@0: * to nearest pixels. Otherwise just return aTransform. Call this when the michael@0: * layer does not correspond to a single rectangular content object. michael@0: * This function does not try to snap if aTransform has a scale, because in michael@0: * that case resampling is inevitable and there's no point in trying to michael@0: * avoid it. In fact snapping can cause problems because pixel edges in the michael@0: * layer's content can be rendered unpredictably (jiggling) as the scale michael@0: * interacts with the snapping of the translation, especially with animated michael@0: * transforms. michael@0: * @param aResidualTransform a transform to apply before the result transform michael@0: * in order to get the results to completely match aTransform. michael@0: */ michael@0: gfx::Matrix4x4 SnapTransformTranslation(const gfx::Matrix4x4& aTransform, michael@0: gfx::Matrix* aResidualTransform); michael@0: /** michael@0: * See comment for SnapTransformTranslation. michael@0: * This function implements type 2 snapping. If aTransform is a translation michael@0: * and/or scale, transform aSnapRect by aTransform, snap to pixel boundaries, michael@0: * and return the transform that maps aSnapRect to that rect. Otherwise michael@0: * just return aTransform. michael@0: * @param aSnapRect a rectangle whose edges should be snapped to pixel michael@0: * boundaries in the destination surface. michael@0: * @param aResidualTransform a transform to apply before the result transform michael@0: * in order to get the results to completely match aTransform. michael@0: */ michael@0: gfx::Matrix4x4 SnapTransform(const gfx::Matrix4x4& aTransform, michael@0: const gfxRect& aSnapRect, michael@0: gfx::Matrix* aResidualTransform); michael@0: michael@0: /** michael@0: * Returns true if this layer's effective transform is not just michael@0: * a translation by integers, or if this layer or some ancestor layer michael@0: * is marked as having a transform that may change without a full layer michael@0: * transaction. michael@0: */ michael@0: bool MayResample(); michael@0: michael@0: LayerManager* mManager; michael@0: ContainerLayer* mParent; michael@0: Layer* mNextSibling; michael@0: Layer* mPrevSibling; michael@0: void* mImplData; michael@0: nsRefPtr mMaskLayer; michael@0: gfx::UserData mUserData; michael@0: nsIntRegion mVisibleRegion; michael@0: EventRegions mEventRegions; michael@0: gfx::Matrix4x4 mTransform; michael@0: // A mutation of |mTransform| that we've queued to be applied at the michael@0: // end of the next transaction (if nothing else overrides it in the michael@0: // meantime). michael@0: nsAutoPtr mPendingTransform; michael@0: float mPostXScale; michael@0: float mPostYScale; michael@0: gfx::Matrix4x4 mEffectiveTransform; michael@0: AnimationArray mAnimations; michael@0: // See mPendingTransform above. michael@0: nsAutoPtr mPendingAnimations; michael@0: InfallibleTArray mAnimationData; michael@0: float mOpacity; michael@0: gfx::CompositionOp mMixBlendMode; michael@0: bool mForceIsolatedGroup; michael@0: nsIntRect mClipRect; michael@0: nsIntRect mTileSourceRect; michael@0: nsIntRegion mInvalidRegion; michael@0: uint32_t mContentFlags; michael@0: bool mUseClipRect; michael@0: bool mUseTileSourceRect; michael@0: bool mIsFixedPosition; michael@0: LayerPoint mAnchor; michael@0: LayerMargin mMargins; michael@0: struct StickyPositionData { michael@0: FrameMetrics::ViewID mScrollId; michael@0: LayerRect mOuter; michael@0: LayerRect mInner; michael@0: }; michael@0: nsAutoPtr mStickyPositionData; michael@0: FrameMetrics::ViewID mScrollbarTargetId; michael@0: ScrollDirection mScrollbarDirection; michael@0: DebugOnly mDebugColorIndex; michael@0: // If this layer is used for OMTA, then this counter is used to ensure we michael@0: // stay in sync with the animation manager michael@0: uint64_t mAnimationGeneration; michael@0: }; michael@0: michael@0: /** michael@0: * A Layer which we can draw into using Thebes. It is a conceptually michael@0: * infinite surface, but each ThebesLayer has an associated "valid region" michael@0: * of contents that it is currently storing, which is finite. ThebesLayer michael@0: * implementations can store content between paints. michael@0: * michael@0: * ThebesLayers are rendered into during the drawing phase of a transaction. michael@0: * michael@0: * Currently the contents of a ThebesLayer are in the device output color michael@0: * space. michael@0: */ michael@0: class ThebesLayer : public Layer { michael@0: public: michael@0: /** michael@0: * CONSTRUCTION PHASE ONLY michael@0: * Tell this layer that the content in some region has changed and michael@0: * will need to be repainted. This area is removed from the valid michael@0: * region. michael@0: */ michael@0: virtual void InvalidateRegion(const nsIntRegion& aRegion) = 0; michael@0: /** michael@0: * CONSTRUCTION PHASE ONLY michael@0: * Set whether ComputeEffectiveTransforms should compute the michael@0: * "residual translation" --- the translation that should be applied *before* michael@0: * mEffectiveTransform to get the ideal transform for this ThebesLayer. michael@0: * When this is true, ComputeEffectiveTransforms will compute the residual michael@0: * and ensure that the layer is invalidated whenever the residual changes. michael@0: * When it's false, a change in the residual will not trigger invalidation michael@0: * and GetResidualTranslation will return 0,0. michael@0: * So when the residual is to be ignored, set this to false for better michael@0: * performance. michael@0: */ michael@0: void SetAllowResidualTranslation(bool aAllow) { mAllowResidualTranslation = aAllow; } michael@0: michael@0: /** michael@0: * Can be used anytime michael@0: */ michael@0: const nsIntRegion& GetValidRegion() const { return mValidRegion; } michael@0: michael@0: virtual ThebesLayer* AsThebesLayer() { return this; } michael@0: michael@0: MOZ_LAYER_DECL_NAME("ThebesLayer", TYPE_THEBES) michael@0: michael@0: virtual void ComputeEffectiveTransforms(const gfx::Matrix4x4& aTransformToSurface) michael@0: { michael@0: gfx::Matrix4x4 idealTransform = GetLocalTransform() * aTransformToSurface; michael@0: gfx::Matrix residual; michael@0: mEffectiveTransform = SnapTransformTranslation(idealTransform, michael@0: mAllowResidualTranslation ? &residual : nullptr); michael@0: // The residual can only be a translation because SnapTransformTranslation michael@0: // only changes the transform if it's a translation michael@0: NS_ASSERTION(residual.IsTranslation(), michael@0: "Residual transform can only be a translation"); michael@0: if (!gfx::ThebesPoint(residual.GetTranslation()).WithinEpsilonOf(mResidualTranslation, 1e-3f)) { michael@0: mResidualTranslation = gfx::ThebesPoint(residual.GetTranslation()); michael@0: NS_ASSERTION(-0.5 <= mResidualTranslation.x && mResidualTranslation.x < 0.5 && michael@0: -0.5 <= mResidualTranslation.y && mResidualTranslation.y < 0.5, michael@0: "Residual translation out of range"); michael@0: mValidRegion.SetEmpty(); michael@0: } michael@0: ComputeEffectiveTransformForMaskLayer(aTransformToSurface); michael@0: } michael@0: michael@0: bool UsedForReadback() { return mUsedForReadback; } michael@0: void SetUsedForReadback(bool aUsed) { mUsedForReadback = aUsed; } michael@0: /** michael@0: * Returns the residual translation. Apply this translation when drawing michael@0: * into the ThebesLayer so that when mEffectiveTransform is applied afterwards michael@0: * by layer compositing, the results exactly match the "ideal transform" michael@0: * (the product of the transform of this layer and its ancestors). michael@0: * Returns 0,0 unless SetAllowResidualTranslation(true) has been called. michael@0: * The residual translation components are always in the range [-0.5, 0.5). michael@0: */ michael@0: gfxPoint GetResidualTranslation() const { return mResidualTranslation; } michael@0: michael@0: protected: michael@0: ThebesLayer(LayerManager* aManager, void* aImplData) michael@0: : Layer(aManager, aImplData) michael@0: , mValidRegion() michael@0: , mUsedForReadback(false) michael@0: , mAllowResidualTranslation(false) michael@0: { michael@0: mContentFlags = 0; // Clear NO_TEXT, NO_TEXT_OVER_TRANSPARENT michael@0: } michael@0: michael@0: virtual nsACString& PrintInfo(nsACString& aTo, const char* aPrefix); michael@0: michael@0: /** michael@0: * ComputeEffectiveTransforms snaps the ideal transform to get mEffectiveTransform. michael@0: * mResidualTranslation is the translation that should be applied *before* michael@0: * mEffectiveTransform to get the ideal transform. michael@0: */ michael@0: gfxPoint mResidualTranslation; michael@0: nsIntRegion mValidRegion; michael@0: /** michael@0: * Set when this ThebesLayer is participating in readback, i.e. some michael@0: * ReadbackLayer (may) be getting its background from this layer. michael@0: */ michael@0: bool mUsedForReadback; michael@0: /** michael@0: * True when michael@0: */ michael@0: bool mAllowResidualTranslation; michael@0: }; michael@0: michael@0: /** michael@0: * A Layer which other layers render into. It holds references to its michael@0: * children. michael@0: */ michael@0: class ContainerLayer : public Layer { michael@0: public: michael@0: michael@0: ~ContainerLayer(); michael@0: michael@0: /** michael@0: * CONSTRUCTION PHASE ONLY michael@0: * Insert aChild into the child list of this container. aChild must michael@0: * not be currently in any child list or the root for the layer manager. michael@0: * If aAfter is non-null, it must be a child of this container and michael@0: * we insert after that layer. If it's null we insert at the start. michael@0: */ michael@0: virtual bool InsertAfter(Layer* aChild, Layer* aAfter); michael@0: /** michael@0: * CONSTRUCTION PHASE ONLY michael@0: * Remove aChild from the child list of this container. aChild must michael@0: * be a child of this container. michael@0: */ michael@0: virtual bool RemoveChild(Layer* aChild); michael@0: /** michael@0: * CONSTRUCTION PHASE ONLY michael@0: * Reposition aChild from the child list of this container. aChild must michael@0: * be a child of this container. michael@0: * If aAfter is non-null, it must be a child of this container and we michael@0: * reposition after that layer. If it's null, we reposition at the start. michael@0: */ michael@0: virtual bool RepositionChild(Layer* aChild, Layer* aAfter); michael@0: michael@0: /** michael@0: * CONSTRUCTION PHASE ONLY michael@0: * Set the (sub)document metrics used to render the Layer subtree michael@0: * rooted at this. michael@0: */ michael@0: void SetFrameMetrics(const FrameMetrics& aFrameMetrics) michael@0: { michael@0: if (mFrameMetrics != aFrameMetrics) { michael@0: MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) FrameMetrics", this)); michael@0: mFrameMetrics = aFrameMetrics; michael@0: Mutated(); michael@0: } michael@0: } michael@0: michael@0: // These functions allow attaching an AsyncPanZoomController to this layer, michael@0: // and can be used anytime. michael@0: // A container layer has an APZC only-if GetFrameMetrics().IsScrollable() michael@0: void SetAsyncPanZoomController(AsyncPanZoomController *controller); michael@0: AsyncPanZoomController* GetAsyncPanZoomController() const; michael@0: michael@0: /** michael@0: * CONSTRUCTION PHASE ONLY michael@0: * Set the ViewID of the ContainerLayer to which overscroll should be handed michael@0: * off. A value of NULL_SCROLL_ID means that the default handoff-parent-finding michael@0: * behaviour should be used (i.e. walk up the layer tree to find the next michael@0: * scrollable ancestor layer). michael@0: */ michael@0: void SetScrollHandoffParentId(FrameMetrics::ViewID aScrollParentId) michael@0: { michael@0: if (mScrollHandoffParentId != aScrollParentId) { michael@0: MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) ScrollHandoffParentId", this)); michael@0: mScrollHandoffParentId = aScrollParentId; michael@0: Mutated(); michael@0: } michael@0: } michael@0: michael@0: void SetPreScale(float aXScale, float aYScale) michael@0: { michael@0: if (mPreXScale == aXScale && mPreYScale == aYScale) { michael@0: return; michael@0: } michael@0: michael@0: MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) PreScale", this)); michael@0: mPreXScale = aXScale; michael@0: mPreYScale = aYScale; michael@0: Mutated(); michael@0: } michael@0: michael@0: void SetInheritedScale(float aXScale, float aYScale) michael@0: { michael@0: if (mInheritedXScale == aXScale && mInheritedYScale == aYScale) { michael@0: return; michael@0: } michael@0: michael@0: MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) InheritedScale", this)); michael@0: mInheritedXScale = aXScale; michael@0: mInheritedYScale = aYScale; michael@0: Mutated(); michael@0: } michael@0: michael@0: virtual void FillSpecificAttributes(SpecificLayerAttributes& aAttrs); michael@0: michael@0: void SortChildrenBy3DZOrder(nsTArray& aArray); michael@0: michael@0: // These getters can be used anytime. michael@0: michael@0: virtual ContainerLayer* AsContainerLayer() { return this; } michael@0: virtual const ContainerLayer* AsContainerLayer() const { return this; } michael@0: michael@0: virtual Layer* GetFirstChild() const { return mFirstChild; } michael@0: virtual Layer* GetLastChild() const { return mLastChild; } michael@0: const FrameMetrics& GetFrameMetrics() const { return mFrameMetrics; } michael@0: FrameMetrics::ViewID GetScrollHandoffParentId() const { return mScrollHandoffParentId; } michael@0: float GetPreXScale() const { return mPreXScale; } michael@0: float GetPreYScale() const { return mPreYScale; } michael@0: float GetInheritedXScale() const { return mInheritedXScale; } michael@0: float GetInheritedYScale() const { return mInheritedYScale; } michael@0: michael@0: MOZ_LAYER_DECL_NAME("ContainerLayer", TYPE_CONTAINER) michael@0: michael@0: /** michael@0: * ContainerLayer backends need to override ComputeEffectiveTransforms michael@0: * since the decision about whether to use a temporary surface for the michael@0: * container is backend-specific. ComputeEffectiveTransforms must also set michael@0: * mUseIntermediateSurface. michael@0: */ michael@0: virtual void ComputeEffectiveTransforms(const gfx::Matrix4x4& aTransformToSurface) = 0; michael@0: michael@0: /** michael@0: * Call this only after ComputeEffectiveTransforms has been invoked michael@0: * on this layer. michael@0: * Returns true if this will use an intermediate surface. This is largely michael@0: * backend-dependent, but it affects the operation of GetEffectiveOpacity(). michael@0: */ michael@0: bool UseIntermediateSurface() { return mUseIntermediateSurface; } michael@0: michael@0: /** michael@0: * Returns the rectangle covered by the intermediate surface, michael@0: * in this layer's coordinate system michael@0: */ michael@0: nsIntRect GetIntermediateSurfaceRect() michael@0: { michael@0: NS_ASSERTION(mUseIntermediateSurface, "Must have intermediate surface"); michael@0: return mVisibleRegion.GetBounds(); michael@0: } michael@0: michael@0: /** michael@0: * Returns true if this container has more than one non-empty child michael@0: */ michael@0: bool HasMultipleChildren(); michael@0: michael@0: /** michael@0: * Returns true if this container supports children with component alpha. michael@0: * Should only be called while painting a child of this layer. michael@0: */ michael@0: bool SupportsComponentAlphaChildren() { return mSupportsComponentAlphaChildren; } michael@0: michael@0: /** michael@0: * Returns true if aLayer or any layer in its parent chain has the opaque michael@0: * content flag set. michael@0: */ michael@0: static bool HasOpaqueAncestorLayer(Layer* aLayer); michael@0: michael@0: protected: michael@0: friend class ReadbackProcessor; michael@0: michael@0: void DidInsertChild(Layer* aLayer); michael@0: void DidRemoveChild(Layer* aLayer); michael@0: michael@0: ContainerLayer(LayerManager* aManager, void* aImplData); michael@0: michael@0: /** michael@0: * A default implementation of ComputeEffectiveTransforms for use by OpenGL michael@0: * and D3D. michael@0: */ michael@0: void DefaultComputeEffectiveTransforms(const gfx::Matrix4x4& aTransformToSurface); michael@0: michael@0: /** michael@0: * Loops over the children calling ComputeEffectiveTransforms on them. michael@0: */ michael@0: void ComputeEffectiveTransformsForChildren(const gfx::Matrix4x4& aTransformToSurface); michael@0: michael@0: virtual nsACString& PrintInfo(nsACString& aTo, const char* aPrefix); michael@0: michael@0: Layer* mFirstChild; michael@0: Layer* mLastChild; michael@0: FrameMetrics mFrameMetrics; michael@0: nsRefPtr mAPZC; michael@0: FrameMetrics::ViewID mScrollHandoffParentId; michael@0: float mPreXScale; michael@0: float mPreYScale; michael@0: // The resolution scale inherited from the parent layer. This will already michael@0: // be part of mTransform. michael@0: float mInheritedXScale; michael@0: float mInheritedYScale; michael@0: bool mUseIntermediateSurface; michael@0: bool mSupportsComponentAlphaChildren; michael@0: bool mMayHaveReadbackChild; michael@0: }; michael@0: michael@0: /** michael@0: * A Layer which just renders a solid color in its visible region. It actually michael@0: * can fill any area that contains the visible region, so if you need to michael@0: * restrict the area filled, set a clip region on this layer. michael@0: */ michael@0: class ColorLayer : public Layer { michael@0: public: michael@0: virtual ColorLayer* AsColorLayer() { return this; } michael@0: michael@0: /** michael@0: * CONSTRUCTION PHASE ONLY michael@0: * Set the color of the layer. michael@0: */ michael@0: virtual void SetColor(const gfxRGBA& aColor) michael@0: { michael@0: if (mColor != aColor) { michael@0: MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) Color", this)); michael@0: mColor = aColor; michael@0: Mutated(); michael@0: } michael@0: } michael@0: michael@0: void SetBounds(const nsIntRect& aBounds) michael@0: { michael@0: if (!mBounds.IsEqualEdges(aBounds)) { michael@0: mBounds = aBounds; michael@0: Mutated(); michael@0: } michael@0: } michael@0: michael@0: const nsIntRect& GetBounds() michael@0: { michael@0: return mBounds; michael@0: } michael@0: michael@0: // This getter can be used anytime. michael@0: virtual const gfxRGBA& GetColor() { return mColor; } michael@0: michael@0: MOZ_LAYER_DECL_NAME("ColorLayer", TYPE_COLOR) michael@0: michael@0: virtual void ComputeEffectiveTransforms(const gfx::Matrix4x4& aTransformToSurface) michael@0: { michael@0: gfx::Matrix4x4 idealTransform = GetLocalTransform() * aTransformToSurface; michael@0: mEffectiveTransform = SnapTransformTranslation(idealTransform, nullptr); michael@0: ComputeEffectiveTransformForMaskLayer(aTransformToSurface); michael@0: } michael@0: michael@0: protected: michael@0: ColorLayer(LayerManager* aManager, void* aImplData) michael@0: : Layer(aManager, aImplData), michael@0: mColor(0.0, 0.0, 0.0, 0.0) michael@0: {} michael@0: michael@0: virtual nsACString& PrintInfo(nsACString& aTo, const char* aPrefix); michael@0: michael@0: nsIntRect mBounds; michael@0: gfxRGBA mColor; michael@0: }; michael@0: michael@0: /** michael@0: * A Layer for HTML Canvas elements. It's backed by either a michael@0: * gfxASurface or a GLContext (for WebGL layers), and has some control michael@0: * for intelligent updating from the source if necessary (for example, michael@0: * if hardware compositing is not available, for reading from the GL michael@0: * buffer into an image surface that we can layer composite.) michael@0: * michael@0: * After Initialize is called, the underlying canvas Surface/GLContext michael@0: * must not be modified during a layer transaction. michael@0: */ michael@0: class CanvasLayer : public Layer { michael@0: public: michael@0: struct Data { michael@0: Data() michael@0: : mDrawTarget(nullptr) michael@0: , mGLContext(nullptr) michael@0: , mStream(nullptr) michael@0: , mTexID(0) michael@0: , mSize(0,0) michael@0: , mIsGLAlphaPremult(false) michael@0: { } michael@0: michael@0: // One of these two must be specified for Canvas2D, but never both michael@0: mozilla::gfx::DrawTarget *mDrawTarget; // a DrawTarget for the canvas contents michael@0: mozilla::gl::GLContext* mGLContext; // or this, for GL. michael@0: michael@0: // Canvas/SkiaGL uses this michael@0: mozilla::gfx::SurfaceStream* mStream; michael@0: michael@0: // ID of the texture backing the canvas layer (defaults to 0) michael@0: uint32_t mTexID; michael@0: michael@0: // The size of the canvas content michael@0: nsIntSize mSize; michael@0: michael@0: // Whether mGLContext contains data that is alpha-premultiplied. michael@0: bool mIsGLAlphaPremult; michael@0: }; michael@0: michael@0: /** michael@0: * CONSTRUCTION PHASE ONLY michael@0: * Initialize this CanvasLayer with the given data. The data must michael@0: * have either mSurface or mGLContext initialized (but not both), as michael@0: * well as mSize. michael@0: * michael@0: * This must only be called once. michael@0: */ michael@0: virtual void Initialize(const Data& aData) = 0; michael@0: michael@0: /** michael@0: * Check the data is owned by this layer is still valid for rendering michael@0: */ michael@0: virtual bool IsDataValid(const Data& aData) { return true; } michael@0: michael@0: /** michael@0: * Notify this CanvasLayer that the canvas surface contents have michael@0: * changed (or will change) before the next transaction. michael@0: */ michael@0: void Updated() { mDirty = true; SetInvalidRectToVisibleRegion(); } michael@0: michael@0: /** michael@0: * Notify this CanvasLayer that the canvas surface contents have michael@0: * been painted since the last change. michael@0: */ michael@0: void Painted() { mDirty = false; } michael@0: michael@0: /** michael@0: * Returns true if the canvas surface contents have changed since the michael@0: * last paint. michael@0: */ michael@0: bool IsDirty() michael@0: { michael@0: // We can only tell if we are dirty if we're part of the michael@0: // widget's retained layer tree. michael@0: if (!mManager || !mManager->IsWidgetLayerManager()) { michael@0: return true; michael@0: } michael@0: return mDirty; michael@0: } michael@0: michael@0: /** michael@0: * Register a callback to be called at the start of each transaction. michael@0: */ michael@0: typedef void PreTransactionCallback(void* closureData); michael@0: void SetPreTransactionCallback(PreTransactionCallback* callback, void* closureData) michael@0: { michael@0: mPreTransCallback = callback; michael@0: mPreTransCallbackData = closureData; michael@0: } michael@0: michael@0: protected: michael@0: void FirePreTransactionCallback() michael@0: { michael@0: if (mPreTransCallback) { michael@0: mPreTransCallback(mPreTransCallbackData); michael@0: } michael@0: } michael@0: michael@0: public: michael@0: /** michael@0: * Register a callback to be called at the end of each transaction. michael@0: */ michael@0: typedef void (* DidTransactionCallback)(void* aClosureData); michael@0: void SetDidTransactionCallback(DidTransactionCallback aCallback, void* aClosureData) michael@0: { michael@0: mPostTransCallback = aCallback; michael@0: mPostTransCallbackData = aClosureData; michael@0: } michael@0: michael@0: /** michael@0: * CONSTRUCTION PHASE ONLY michael@0: * Set the filter used to resample this image (if necessary). michael@0: */ michael@0: void SetFilter(GraphicsFilter aFilter) michael@0: { michael@0: if (mFilter != aFilter) { michael@0: MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) Filter", this)); michael@0: mFilter = aFilter; michael@0: Mutated(); michael@0: } michael@0: } michael@0: GraphicsFilter GetFilter() const { return mFilter; } michael@0: michael@0: MOZ_LAYER_DECL_NAME("CanvasLayer", TYPE_CANVAS) michael@0: michael@0: virtual void ComputeEffectiveTransforms(const gfx::Matrix4x4& aTransformToSurface) michael@0: { michael@0: // Snap our local transform first, and snap the inherited transform as well. michael@0: // This makes our snapping equivalent to what would happen if our content michael@0: // was drawn into a ThebesLayer (gfxContext would snap using the local michael@0: // transform, then we'd snap again when compositing the ThebesLayer). michael@0: mEffectiveTransform = michael@0: SnapTransform(GetLocalTransform(), gfxRect(0, 0, mBounds.width, mBounds.height), michael@0: nullptr)* michael@0: SnapTransformTranslation(aTransformToSurface, nullptr); michael@0: ComputeEffectiveTransformForMaskLayer(aTransformToSurface); michael@0: } michael@0: michael@0: protected: michael@0: CanvasLayer(LayerManager* aManager, void* aImplData) michael@0: : Layer(aManager, aImplData) michael@0: , mPreTransCallback(nullptr) michael@0: , mPreTransCallbackData(nullptr) michael@0: , mPostTransCallback(nullptr) michael@0: , mPostTransCallbackData(nullptr) michael@0: , mFilter(GraphicsFilter::FILTER_GOOD) michael@0: , mDirty(false) michael@0: {} michael@0: michael@0: virtual nsACString& PrintInfo(nsACString& aTo, const char* aPrefix); michael@0: michael@0: void FireDidTransactionCallback() michael@0: { michael@0: if (mPostTransCallback) { michael@0: mPostTransCallback(mPostTransCallbackData); michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * 0, 0, canvaswidth, canvasheight michael@0: */ michael@0: nsIntRect mBounds; michael@0: PreTransactionCallback* mPreTransCallback; michael@0: void* mPreTransCallbackData; michael@0: DidTransactionCallback mPostTransCallback; michael@0: void* mPostTransCallbackData; michael@0: GraphicsFilter mFilter; michael@0: michael@0: private: michael@0: /** michael@0: * Set to true in Updated(), cleared during a transaction. michael@0: */ michael@0: bool mDirty; michael@0: }; michael@0: michael@0: /** michael@0: * ContainerLayer that refers to a "foreign" layer tree, through an michael@0: * ID. Usage of RefLayer looks like michael@0: * michael@0: * Construction phase: michael@0: * allocate ID for layer subtree michael@0: * create RefLayer, SetReferentId(ID) michael@0: * michael@0: * Composition: michael@0: * look up subtree for GetReferentId() michael@0: * ConnectReferentLayer(subtree) michael@0: * compose michael@0: * ClearReferentLayer() michael@0: * michael@0: * Clients will usually want to Connect/Clear() on each transaction to michael@0: * avoid difficulties managing memory across multiple layer subtrees. michael@0: */ michael@0: class RefLayer : public ContainerLayer { michael@0: friend class LayerManager; michael@0: michael@0: private: michael@0: virtual bool InsertAfter(Layer* aChild, Layer* aAfter) MOZ_OVERRIDE michael@0: { MOZ_CRASH(); return false; } michael@0: michael@0: virtual bool RemoveChild(Layer* aChild) michael@0: { MOZ_CRASH(); return false; } michael@0: michael@0: virtual bool RepositionChild(Layer* aChild, Layer* aAfter) michael@0: { MOZ_CRASH(); return false; } michael@0: michael@0: using ContainerLayer::SetFrameMetrics; michael@0: michael@0: public: michael@0: /** michael@0: * CONSTRUCTION PHASE ONLY michael@0: * Set the ID of the layer's referent. michael@0: */ michael@0: void SetReferentId(uint64_t aId) michael@0: { michael@0: MOZ_ASSERT(aId != 0); michael@0: if (mId != aId) { michael@0: MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) ReferentId", this)); michael@0: mId = aId; michael@0: Mutated(); michael@0: } michael@0: } michael@0: /** michael@0: * CONSTRUCTION PHASE ONLY michael@0: * Connect this ref layer to its referent, temporarily. michael@0: * ClearReferentLayer() must be called after composition. michael@0: */ michael@0: void ConnectReferentLayer(Layer* aLayer) michael@0: { michael@0: MOZ_ASSERT(!mFirstChild && !mLastChild); michael@0: MOZ_ASSERT(!aLayer->GetParent()); michael@0: MOZ_ASSERT(aLayer->Manager() == Manager()); michael@0: michael@0: mFirstChild = mLastChild = aLayer; michael@0: aLayer->SetParent(this); michael@0: } michael@0: michael@0: /** michael@0: * DRAWING PHASE ONLY michael@0: * |aLayer| is the same as the argument to ConnectReferentLayer(). michael@0: */ michael@0: void DetachReferentLayer(Layer* aLayer) michael@0: { michael@0: MOZ_ASSERT(aLayer == mFirstChild && mFirstChild == mLastChild); michael@0: MOZ_ASSERT(aLayer->GetParent() == this); michael@0: michael@0: mFirstChild = mLastChild = nullptr; michael@0: aLayer->SetParent(nullptr); michael@0: } michael@0: michael@0: // These getters can be used anytime. michael@0: virtual RefLayer* AsRefLayer() { return this; } michael@0: michael@0: virtual int64_t GetReferentId() { return mId; } michael@0: michael@0: /** michael@0: * DRAWING PHASE ONLY michael@0: */ michael@0: virtual void FillSpecificAttributes(SpecificLayerAttributes& aAttrs); michael@0: michael@0: MOZ_LAYER_DECL_NAME("RefLayer", TYPE_REF) michael@0: michael@0: protected: michael@0: RefLayer(LayerManager* aManager, void* aImplData) michael@0: : ContainerLayer(aManager, aImplData) , mId(0) michael@0: {} michael@0: michael@0: virtual nsACString& PrintInfo(nsACString& aTo, const char* aPrefix); michael@0: michael@0: Layer* mTempReferent; michael@0: // 0 is a special value that means "no ID". michael@0: uint64_t mId; michael@0: }; michael@0: michael@0: void SetAntialiasingFlags(Layer* aLayer, gfxContext* aTarget); michael@0: void SetAntialiasingFlags(Layer* aLayer, gfx::DrawTarget* aTarget); michael@0: michael@0: #ifdef MOZ_DUMP_PAINTING michael@0: void WriteSnapshotToDumpFile(Layer* aLayer, gfx::DataSourceSurface* aSurf); michael@0: void WriteSnapshotToDumpFile(LayerManager* aManager, gfx::DataSourceSurface* aSurf); michael@0: void WriteSnapshotToDumpFile(Compositor* aCompositor, gfx::DrawTarget* aTarget); michael@0: #endif michael@0: michael@0: } michael@0: } michael@0: michael@0: #endif /* GFX_LAYERS_H */