michael@0: /* -*- Mode: C++; tab-width: 20; 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 MOZILLA_GFX_TILEDCONTENTCLIENT_H michael@0: #define MOZILLA_GFX_TILEDCONTENTCLIENT_H michael@0: michael@0: #include // for size_t michael@0: #include // for uint16_t michael@0: #include // for swap michael@0: #include "Layers.h" // for LayerManager, etc michael@0: #include "TiledLayerBuffer.h" // for TiledLayerBuffer michael@0: #include "Units.h" // for CSSPoint michael@0: #include "gfx3DMatrix.h" // for gfx3DMatrix michael@0: #include "gfxTypes.h" michael@0: #include "mozilla/Attributes.h" // for MOZ_OVERRIDE michael@0: #include "mozilla/RefPtr.h" // for RefPtr michael@0: #include "mozilla/ipc/Shmem.h" // for Shmem michael@0: #include "mozilla/ipc/SharedMemory.h" // for SharedMemory michael@0: #include "mozilla/layers/CompositableClient.h" // for CompositableClient michael@0: #include "mozilla/layers/CompositorTypes.h" // for TextureInfo, etc michael@0: #include "mozilla/layers/LayersMessages.h" // for TileDescriptor michael@0: #include "mozilla/layers/TextureClient.h" michael@0: #include "mozilla/layers/TextureClientPool.h" michael@0: #include "ClientLayerManager.h" michael@0: #include "mozilla/mozalloc.h" // for operator delete michael@0: #include "nsAutoPtr.h" // for nsRefPtr michael@0: #include "nsISupportsImpl.h" // for MOZ_COUNT_DTOR michael@0: #include "nsPoint.h" // for nsIntPoint michael@0: #include "nsRect.h" // for nsIntRect michael@0: #include "nsRegion.h" // for nsIntRegion michael@0: #include "nsTArray.h" // for nsTArray, nsTArray_Impl, etc michael@0: #include "mozilla/layers/ISurfaceAllocator.h" michael@0: #include "gfxReusableSurfaceWrapper.h" michael@0: #include "pratom.h" // For PR_ATOMIC_INCREMENT/DECREMENT michael@0: #include "gfxPrefs.h" michael@0: michael@0: namespace mozilla { michael@0: namespace layers { michael@0: michael@0: class BasicTileDescriptor; michael@0: class ClientTiledThebesLayer; michael@0: class ClientLayerManager; michael@0: michael@0: michael@0: // A class to help implement copy-on-write semantics for shared tiles. michael@0: class gfxSharedReadLock { michael@0: protected: michael@0: virtual ~gfxSharedReadLock() {} michael@0: michael@0: public: michael@0: NS_INLINE_DECL_THREADSAFE_REFCOUNTING(gfxSharedReadLock) michael@0: michael@0: virtual int32_t ReadLock() = 0; michael@0: virtual int32_t ReadUnlock() = 0; michael@0: virtual int32_t GetReadCount() = 0; michael@0: virtual bool IsValid() const = 0; michael@0: michael@0: enum gfxSharedReadLockType { michael@0: TYPE_MEMORY, michael@0: TYPE_SHMEM michael@0: }; michael@0: virtual gfxSharedReadLockType GetType() = 0; michael@0: michael@0: protected: michael@0: NS_DECL_OWNINGTHREAD michael@0: }; michael@0: michael@0: class gfxMemorySharedReadLock : public gfxSharedReadLock { michael@0: public: michael@0: gfxMemorySharedReadLock(); michael@0: michael@0: ~gfxMemorySharedReadLock(); michael@0: michael@0: virtual int32_t ReadLock() MOZ_OVERRIDE; michael@0: michael@0: virtual int32_t ReadUnlock() MOZ_OVERRIDE; michael@0: michael@0: virtual int32_t GetReadCount() MOZ_OVERRIDE; michael@0: michael@0: virtual gfxSharedReadLockType GetType() MOZ_OVERRIDE { return TYPE_MEMORY; } michael@0: michael@0: virtual bool IsValid() const MOZ_OVERRIDE { return true; }; michael@0: michael@0: private: michael@0: int32_t mReadCount; michael@0: }; michael@0: michael@0: class gfxShmSharedReadLock : public gfxSharedReadLock { michael@0: private: michael@0: struct ShmReadLockInfo { michael@0: int32_t readCount; michael@0: }; michael@0: michael@0: public: michael@0: gfxShmSharedReadLock(ISurfaceAllocator* aAllocator); michael@0: michael@0: ~gfxShmSharedReadLock(); michael@0: michael@0: virtual int32_t ReadLock() MOZ_OVERRIDE; michael@0: michael@0: virtual int32_t ReadUnlock() MOZ_OVERRIDE; michael@0: michael@0: virtual int32_t GetReadCount() MOZ_OVERRIDE; michael@0: michael@0: virtual bool IsValid() const MOZ_OVERRIDE { return mAllocSuccess; }; michael@0: michael@0: virtual gfxSharedReadLockType GetType() MOZ_OVERRIDE { return TYPE_SHMEM; } michael@0: michael@0: mozilla::layers::ShmemSection& GetShmemSection() { return mShmemSection; } michael@0: michael@0: static already_AddRefed michael@0: Open(mozilla::layers::ISurfaceAllocator* aAllocator, const mozilla::layers::ShmemSection& aShmemSection) michael@0: { michael@0: nsRefPtr readLock = new gfxShmSharedReadLock(aAllocator, aShmemSection); michael@0: return readLock.forget(); michael@0: } michael@0: michael@0: private: michael@0: gfxShmSharedReadLock(ISurfaceAllocator* aAllocator, const mozilla::layers::ShmemSection& aShmemSection) michael@0: : mAllocator(aAllocator) michael@0: , mShmemSection(aShmemSection) michael@0: , mAllocSuccess(true) michael@0: { michael@0: MOZ_COUNT_CTOR(gfxShmSharedReadLock); michael@0: } michael@0: michael@0: ShmReadLockInfo* GetShmReadLockInfoPtr() michael@0: { michael@0: return reinterpret_cast michael@0: (mShmemSection.shmem().get() + mShmemSection.offset()); michael@0: } michael@0: michael@0: RefPtr mAllocator; michael@0: mozilla::layers::ShmemSection mShmemSection; michael@0: bool mAllocSuccess; michael@0: }; michael@0: michael@0: /** michael@0: * Represent a single tile in tiled buffer. The buffer keeps tiles, michael@0: * each tile keeps a reference to a texture client and a read-lock. This michael@0: * read-lock is used to help implement a copy-on-write mechanism. The tile michael@0: * should be locked before being sent to the compositor. The compositor should michael@0: * unlock the read-lock as soon as it has finished with the buffer in the michael@0: * TextureHost to prevent more textures being created than is necessary. michael@0: * Ideal place to store per tile debug information. michael@0: */ michael@0: struct TileClient michael@0: { michael@0: // Placeholder michael@0: TileClient(); michael@0: michael@0: TileClient(const TileClient& o); michael@0: michael@0: TileClient& operator=(const TileClient& o); michael@0: michael@0: bool operator== (const TileClient& o) const michael@0: { michael@0: return mFrontBuffer == o.mFrontBuffer; michael@0: } michael@0: michael@0: bool operator!= (const TileClient& o) const michael@0: { michael@0: return mFrontBuffer != o.mFrontBuffer; michael@0: } michael@0: michael@0: void SetLayerManager(ClientLayerManager *aManager) michael@0: { michael@0: mManager = aManager; michael@0: } michael@0: michael@0: bool IsPlaceholderTile() michael@0: { michael@0: return mBackBuffer == nullptr && mFrontBuffer == nullptr; michael@0: } michael@0: michael@0: void ReadUnlock() michael@0: { michael@0: MOZ_ASSERT(mFrontLock, "ReadLock with no gfxSharedReadLock"); michael@0: if (mFrontLock) { michael@0: mFrontLock->ReadUnlock(); michael@0: } michael@0: } michael@0: michael@0: void ReadLock() michael@0: { michael@0: MOZ_ASSERT(mFrontLock, "ReadLock with no gfxSharedReadLock"); michael@0: if (mFrontLock) { michael@0: mFrontLock->ReadLock(); michael@0: } michael@0: } michael@0: michael@0: void Release() michael@0: { michael@0: DiscardFrontBuffer(); michael@0: DiscardBackBuffer(); michael@0: } michael@0: michael@0: TileDescriptor GetTileDescriptor(); michael@0: michael@0: /** michael@0: * Swaps the front and back buffers. michael@0: */ michael@0: void Flip(); michael@0: michael@0: /** michael@0: * Returns an unlocked TextureClient that can be used for writing new michael@0: * data to the tile. This may flip the front-buffer to the back-buffer if michael@0: * the front-buffer is still locked by the host, or does not have an michael@0: * internal buffer (and so will always be locked). michael@0: */ michael@0: TextureClient* GetBackBuffer(const nsIntRegion& aDirtyRegion, michael@0: TextureClientPool *aPool, michael@0: bool *aCreatedTextureClient, michael@0: bool aCanRerasterizeValidRegion); michael@0: michael@0: void DiscardFrontBuffer(); michael@0: michael@0: void DiscardBackBuffer(); michael@0: michael@0: RefPtr mBackBuffer; michael@0: RefPtr mFrontBuffer; michael@0: RefPtr mBackLock; michael@0: RefPtr mFrontLock; michael@0: RefPtr mManager; michael@0: #ifdef GFX_TILEDLAYER_DEBUG_OVERLAY michael@0: TimeStamp mLastUpdate; michael@0: #endif michael@0: nsIntRegion mInvalidFront; michael@0: nsIntRegion mInvalidBack; michael@0: michael@0: private: michael@0: void ValidateBackBufferFromFront(const nsIntRegion &aDirtyRegion, michael@0: bool aCanRerasterizeValidRegion); michael@0: }; michael@0: michael@0: /** michael@0: * This struct stores all the data necessary to perform a paint so that it michael@0: * doesn't need to be recalculated on every repeated transaction. michael@0: */ michael@0: struct BasicTiledLayerPaintData { michael@0: /* michael@0: * The scroll offset of the content from the nearest ancestor layer that michael@0: * represents scrollable content with a display port set. michael@0: */ michael@0: ParentLayerPoint mScrollOffset; michael@0: michael@0: /* michael@0: * The scroll offset of the content from the nearest ancestor layer that michael@0: * represents scrollable content with a display port set, for the last michael@0: * layer update transaction. michael@0: */ michael@0: ParentLayerPoint mLastScrollOffset; michael@0: michael@0: /* michael@0: * The transform matrix to go from Screen units to ParentLayer units. michael@0: */ michael@0: gfx3DMatrix mTransformParentLayerToLayoutDevice; michael@0: michael@0: /* michael@0: * The critical displayport of the content from the nearest ancestor layer michael@0: * that represents scrollable content with a display port set. Empty if a michael@0: * critical displayport is not set. michael@0: * michael@0: * This is in LayoutDevice coordinates, but is stored as an nsIntRect for michael@0: * convenience when intersecting with the layer's mValidRegion. michael@0: */ michael@0: nsIntRect mCriticalDisplayPort; michael@0: michael@0: /* michael@0: * The viewport of the content from the nearest ancestor layer that michael@0: * represents scrollable content with a display port set. michael@0: */ michael@0: LayoutDeviceRect mViewport; michael@0: michael@0: /* michael@0: * The render resolution of the document that the content this layer michael@0: * represents is in. michael@0: */ michael@0: CSSToParentLayerScale mResolution; michael@0: michael@0: /* michael@0: * The composition bounds of the layer, in LayoutDevice coordinates. This is michael@0: * used to make sure that tiled updates to regions that are visible to the michael@0: * user are grouped coherently. michael@0: */ michael@0: LayoutDeviceRect mCompositionBounds; michael@0: michael@0: /* michael@0: * Low precision updates are always executed a tile at a time in repeated michael@0: * transactions. This counter is set to 1 on the first transaction of a low michael@0: * precision update, and incremented for each subsequent transaction. michael@0: */ michael@0: uint16_t mLowPrecisionPaintCount; michael@0: michael@0: /* michael@0: * Whether this is the first time this layer is painting michael@0: */ michael@0: bool mFirstPaint : 1; michael@0: michael@0: /* michael@0: * Whether there is further work to complete this paint. This is used to michael@0: * determine whether or not to repeat the transaction when painting michael@0: * progressively. michael@0: */ michael@0: bool mPaintFinished : 1; michael@0: }; michael@0: michael@0: class SharedFrameMetricsHelper michael@0: { michael@0: public: michael@0: SharedFrameMetricsHelper(); michael@0: ~SharedFrameMetricsHelper(); michael@0: michael@0: /** michael@0: * This is called by the BasicTileLayer to determine if it is still interested michael@0: * in the update of this display-port to continue. We can return true here michael@0: * to abort the current update and continue with any subsequent ones. This michael@0: * is useful for slow-to-render pages when the display-port starts lagging michael@0: * behind enough that continuing to draw it is wasted effort. michael@0: */ michael@0: bool UpdateFromCompositorFrameMetrics(ContainerLayer* aLayer, michael@0: bool aHasPendingNewThebesContent, michael@0: bool aLowPrecision, michael@0: ParentLayerRect& aCompositionBounds, michael@0: CSSToParentLayerScale& aZoom); michael@0: michael@0: /** michael@0: * When a shared FrameMetrics can not be found for a given layer, michael@0: * this function is used to find the first non-empty composition bounds michael@0: * by traversing up the layer tree. michael@0: */ michael@0: void FindFallbackContentFrameMetrics(ContainerLayer* aLayer, michael@0: ParentLayerRect& aCompositionBounds, michael@0: CSSToParentLayerScale& aZoom); michael@0: /** michael@0: * Determines if the compositor's upcoming composition bounds has fallen michael@0: * outside of the contents display port. If it has then the compositor michael@0: * will start to checker board. Checker boarding is when the compositor michael@0: * tries to composite a tile and it is not available. Historically michael@0: * a tile with a checker board pattern was used. Now a blank tile is used. michael@0: */ michael@0: bool AboutToCheckerboard(const FrameMetrics& aContentMetrics, michael@0: const FrameMetrics& aCompositorMetrics); michael@0: private: michael@0: bool mLastProgressiveUpdateWasLowPrecision; michael@0: bool mProgressiveUpdateWasInDanger; michael@0: }; michael@0: michael@0: /** michael@0: * Provide an instance of TiledLayerBuffer backed by drawable TextureClients. michael@0: * This buffer provides an implementation of ValidateTile using a michael@0: * thebes callback and can support painting using a single paint buffer. michael@0: * Whether a single paint buffer is used is controlled by michael@0: * gfxPrefs::PerTileDrawing(). michael@0: */ michael@0: class ClientTiledLayerBuffer michael@0: : public TiledLayerBuffer michael@0: { michael@0: friend class TiledLayerBuffer; michael@0: michael@0: public: michael@0: ClientTiledLayerBuffer(ClientTiledThebesLayer* aThebesLayer, michael@0: CompositableClient* aCompositableClient, michael@0: ClientLayerManager* aManager, michael@0: SharedFrameMetricsHelper* aHelper); michael@0: ClientTiledLayerBuffer() michael@0: : mThebesLayer(nullptr) michael@0: , mCompositableClient(nullptr) michael@0: , mManager(nullptr) michael@0: , mLastPaintOpaque(false) michael@0: , mSharedFrameMetricsHelper(nullptr) michael@0: {} michael@0: michael@0: void PaintThebes(const nsIntRegion& aNewValidRegion, michael@0: const nsIntRegion& aPaintRegion, michael@0: LayerManager::DrawThebesLayerCallback aCallback, michael@0: void* aCallbackData); michael@0: michael@0: void ReadUnlock(); michael@0: michael@0: void ReadLock(); michael@0: michael@0: void Release(); michael@0: michael@0: void DiscardBackBuffers(); michael@0: michael@0: const CSSToParentLayerScale& GetFrameResolution() { return mFrameResolution; } michael@0: michael@0: void SetFrameResolution(const CSSToParentLayerScale& aResolution) { mFrameResolution = aResolution; } michael@0: michael@0: bool HasFormatChanged() const; michael@0: michael@0: /** michael@0: * Performs a progressive update of a given tiled buffer. michael@0: * See ComputeProgressiveUpdateRegion below for parameter documentation. michael@0: */ michael@0: bool ProgressiveUpdate(nsIntRegion& aValidRegion, michael@0: nsIntRegion& aInvalidRegion, michael@0: const nsIntRegion& aOldValidRegion, michael@0: BasicTiledLayerPaintData* aPaintData, michael@0: LayerManager::DrawThebesLayerCallback aCallback, michael@0: void* aCallbackData); michael@0: michael@0: SurfaceDescriptorTiles GetSurfaceDescriptorTiles(); michael@0: michael@0: protected: michael@0: TileClient ValidateTile(TileClient aTile, michael@0: const nsIntPoint& aTileRect, michael@0: const nsIntRegion& dirtyRect); michael@0: michael@0: // If this returns true, we perform the paint operation into a single large michael@0: // buffer and copy it out to the tiles instead of calling PaintThebes() on michael@0: // each tile individually. Somewhat surprisingly, this turns out to be faster michael@0: // on Android. michael@0: bool UseSinglePaintBuffer() { return !gfxPrefs::PerTileDrawing(); } michael@0: michael@0: void ReleaseTile(TileClient aTile) { aTile.Release(); } michael@0: michael@0: void SwapTiles(TileClient& aTileA, TileClient& aTileB) { std::swap(aTileA, aTileB); } michael@0: michael@0: TileClient GetPlaceholderTile() const { return TileClient(); } michael@0: michael@0: private: michael@0: gfxContentType GetContentType() const; michael@0: ClientTiledThebesLayer* mThebesLayer; michael@0: CompositableClient* mCompositableClient; michael@0: ClientLayerManager* mManager; michael@0: LayerManager::DrawThebesLayerCallback mCallback; michael@0: void* mCallbackData; michael@0: CSSToParentLayerScale mFrameResolution; michael@0: bool mLastPaintOpaque; michael@0: michael@0: // The DrawTarget we use when UseSinglePaintBuffer() above is true. michael@0: RefPtr mSinglePaintDrawTarget; michael@0: nsIntPoint mSinglePaintBufferOffset; michael@0: SharedFrameMetricsHelper* mSharedFrameMetricsHelper; michael@0: michael@0: /** michael@0: * Calculates the region to update in a single progressive update transaction. michael@0: * This employs some heuristics to update the most 'sensible' region to michael@0: * update at this point in time, and how large an update should be performed michael@0: * at once to maintain visual coherency. michael@0: * michael@0: * aInvalidRegion is the current invalid region. michael@0: * aOldValidRegion is the valid region of mTiledBuffer at the beginning of the michael@0: * current transaction. michael@0: * aRegionToPaint will be filled with the region to update. This may be empty, michael@0: * which indicates that there is no more work to do. michael@0: * aIsRepeated should be true if this function has already been called during michael@0: * this transaction. michael@0: * michael@0: * Returns true if it should be called again, false otherwise. In the case michael@0: * that aRegionToPaint is empty, this will return aIsRepeated for convenience. michael@0: */ michael@0: bool ComputeProgressiveUpdateRegion(const nsIntRegion& aInvalidRegion, michael@0: const nsIntRegion& aOldValidRegion, michael@0: nsIntRegion& aRegionToPaint, michael@0: BasicTiledLayerPaintData* aPaintData, michael@0: bool aIsRepeated); michael@0: }; michael@0: michael@0: class TiledContentClient : public CompositableClient michael@0: { michael@0: // XXX: for now the layer which owns us interacts directly with our buffers. michael@0: // We should have a content client for each tiled buffer which manages its michael@0: // own valid region, resolution, etc. Then we could have a much cleaner michael@0: // interface and tidy up BasicTiledThebesLayer::PaintThebes (bug 862547). michael@0: friend class ClientTiledThebesLayer; michael@0: michael@0: public: michael@0: TiledContentClient(ClientTiledThebesLayer* aThebesLayer, michael@0: ClientLayerManager* aManager); michael@0: michael@0: ~TiledContentClient() michael@0: { michael@0: MOZ_COUNT_DTOR(TiledContentClient); michael@0: michael@0: mTiledBuffer.Release(); michael@0: mLowPrecisionTiledBuffer.Release(); michael@0: } michael@0: michael@0: virtual TextureInfo GetTextureInfo() const MOZ_OVERRIDE michael@0: { michael@0: return TextureInfo(BUFFER_TILED); michael@0: } michael@0: michael@0: virtual void ClearCachedResources() MOZ_OVERRIDE; michael@0: michael@0: enum TiledBufferType { michael@0: TILED_BUFFER, michael@0: LOW_PRECISION_TILED_BUFFER michael@0: }; michael@0: void UseTiledLayerBuffer(TiledBufferType aType); michael@0: michael@0: private: michael@0: SharedFrameMetricsHelper mSharedFrameMetricsHelper; michael@0: ClientTiledLayerBuffer mTiledBuffer; michael@0: ClientTiledLayerBuffer mLowPrecisionTiledBuffer; michael@0: }; michael@0: michael@0: } michael@0: } michael@0: michael@0: #endif