From 1eea1ecca9d220b11ca238586bd91ca251252785 Mon Sep 17 00:00:00 2001 From: Ryan Hunt Date: Mon, 16 Oct 2017 19:45:11 -0400 Subject: [PATCH] Kill RotatedContentBuffer (bug 1409871 part 12, r=nical) This commit is the guts of the refactoring. This commit removes rotated content buffer, and moves the necessary functionality to content client. All content clients now do not inherit from rotated content buffer, but contain a refptr to a rotated buffer. For the basic content client, this is a DrawTargetRotatedBuffer. For the remote content client, this is a RemoteRotatedBuffer. The double buffered content client contains an additional RemoteRotatedBuffer for the front buffer. Each derived class of content client only needs to implement a method to create its own rotated buffer. The rest is handled by the base content client. The remote content clients still override some additional methods to sync buffers and do IPC. MozReview-Commit-ID: B9vcSi8UYhm --HG-- extra : rebase_source : f4b1dcb3cd06f7d0da7ed85e5d3f4bf5ec445e7f --- gfx/layers/CompositorTypes.h | 11 +- gfx/layers/RotatedBuffer.cpp | 497 +-------------- gfx/layers/RotatedBuffer.h | 330 ++-------- gfx/layers/basic/BasicPaintedLayer.cpp | 8 +- gfx/layers/basic/BasicPaintedLayer.h | 4 +- gfx/layers/client/ClientPaintedLayer.cpp | 14 +- gfx/layers/client/ClientPaintedLayer.h | 4 +- gfx/layers/client/ContentClient.cpp | 752 ++++++++++++++++------- gfx/layers/client/ContentClient.h | 306 +++++---- gfx/layers/composite/ContentHost.h | 5 +- 10 files changed, 812 insertions(+), 1119 deletions(-) diff --git a/gfx/layers/CompositorTypes.h b/gfx/layers/CompositorTypes.h index 5ee176b7a9ba..66b86eb2b9b8 100644 --- a/gfx/layers/CompositorTypes.h +++ b/gfx/layers/CompositorTypes.h @@ -248,14 +248,15 @@ enum class OpenMode : uint8_t { OPEN_NONE = 0, OPEN_READ = 0x1, OPEN_WRITE = 0x2, - OPEN_READ_WRITE = OPEN_READ|OPEN_WRITE, - OPEN_READ_ONLY = OPEN_READ, - OPEN_WRITE_ONLY = OPEN_WRITE, - // This is only used in conjunction with OMTP to indicate that the DrawTarget // that is being borrowed will be painted asynchronously, and so will outlive // the write lock. - OPEN_ASYNC_WRITE = 0x04 + OPEN_ASYNC_WRITE = 0x04, + + OPEN_READ_WRITE = OPEN_READ|OPEN_WRITE, + OPEN_READ_ASYNC_WRITE = OPEN_READ|OPEN_WRITE|OPEN_ASYNC_WRITE, + OPEN_READ_ONLY = OPEN_READ, + OPEN_WRITE_ONLY = OPEN_WRITE, }; MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(OpenMode) diff --git a/gfx/layers/RotatedBuffer.cpp b/gfx/layers/RotatedBuffer.cpp index 6449f63742cd..de849ec51e52 100644 --- a/gfx/layers/RotatedBuffer.cpp +++ b/gfx/layers/RotatedBuffer.cpp @@ -289,19 +289,6 @@ WrapRotationAxis(int32_t* aRotationPoint, int32_t aSize) } } -static IntRect -ComputeBufferRect(const IntRect& aRequestedRect) -{ - IntRect rect(aRequestedRect); - // Set a minimum width to guarantee a minimum size of buffers we - // allocate (and work around problems on some platforms with smaller - // dimensions). 64 used to be the magic number needed to work around - // a rendering glitch on b2g (see bug 788411). Now that we don't support - // this device anymore we should be fine with 8 pixels as the minimum. - rect.SetWidth(std::max(aRequestedRect.Width(), 8)); - return rect; -} - bool RotatedBuffer::AdjustTo(const gfx::IntRect& aDestBufferRect, const gfx::IntRect& aDrawBounds, @@ -469,6 +456,18 @@ RotatedBuffer::BorrowDrawTargetForQuadrantUpdate(const IntRect& aBounds, return mLoanedDrawTarget; } +gfx::SurfaceFormat +RemoteRotatedBuffer::GetFormat() const +{ + return mClient->GetFormat(); +} + +bool +RemoteRotatedBuffer::IsLocked() +{ + return mClient->IsLocked(); +} + bool RemoteRotatedBuffer::Lock(OpenMode aMode) { @@ -518,6 +517,15 @@ RemoteRotatedBuffer::Unlock() } } +void +RemoteRotatedBuffer::SyncWithObject(SyncObjectClient* aSyncObject) +{ + mClient->SyncWithObject(aSyncObject); + if (mClientOnWhite) { + mClientOnWhite->SyncWithObject(aSyncObject); + } +} + void RemoteRotatedBuffer::Clear() { @@ -549,6 +557,12 @@ RemoteRotatedBuffer::GetDTBufferOnWhite() const return mTargetOnWhite; } +gfx::SurfaceFormat +DrawTargetRotatedBuffer::GetFormat() const +{ + return mTarget->GetFormat(); +} + already_AddRefed DrawTargetRotatedBuffer::GetSourceSurface(ContextSource aSource) const { @@ -572,6 +586,12 @@ DrawTargetRotatedBuffer::GetDTBufferOnWhite() const return mTargetOnWhite; } +gfx::SurfaceFormat +SourceRotatedBuffer::GetFormat() const +{ + return mSource->GetFormat(); +} + already_AddRefed SourceRotatedBuffer::GetSourceSurface(ContextSource aSource) const { @@ -587,457 +607,6 @@ SourceRotatedBuffer::GetSourceSurface(ContextSource aSource) const return surf.forget(); } -RotatedContentBuffer::BufferDecision -RotatedContentBuffer::CalculateBufferForPaint(PaintedLayer* aLayer, - uint32_t aFlags) -{ - ContentType layerContentType = - aLayer->CanUseOpaqueSurface() ? gfxContentType::COLOR : - gfxContentType::COLOR_ALPHA; - - SurfaceMode mode; - ContentType contentType; - IntRect destBufferRect; - nsIntRegion neededRegion; - nsIntRegion validRegion = aLayer->GetValidRegion(); - - bool canReuseBuffer = HaveBuffer(); - bool canKeepBufferContents = true; - - while (true) { - mode = aLayer->GetSurfaceMode(); - neededRegion = aLayer->GetVisibleRegion().ToUnknownRegion(); - canReuseBuffer &= BufferSizeOkFor(neededRegion.GetBounds().Size()); - contentType = layerContentType; - - if (canReuseBuffer) { - if (mBufferRect.Contains(neededRegion.GetBounds())) { - // We don't need to adjust mBufferRect. - destBufferRect = mBufferRect; - } else if (neededRegion.GetBounds().Size() <= mBufferRect.Size()) { - // The buffer's big enough but doesn't contain everything that's - // going to be visible. We'll move it. - destBufferRect = IntRect(neededRegion.GetBounds().TopLeft(), mBufferRect.Size()); - } else { - destBufferRect = neededRegion.GetBounds(); - } - } else { - // We won't be reusing the buffer. Compute a new rect. - destBufferRect = ComputeBufferRect(neededRegion.GetBounds()); - } - - if (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) { -#if defined(MOZ_GFX_OPTIMIZE_MOBILE) - mode = SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA; -#else - if (!aLayer->GetParent() || - !aLayer->GetParent()->SupportsComponentAlphaChildren() || - !aLayer->AsShadowableLayer() || - !aLayer->AsShadowableLayer()->HasShadow()) { - mode = SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA; - } else { - contentType = gfxContentType::COLOR; - } -#endif - } - - if ((aFlags & PAINT_WILL_RESAMPLE) && - (!neededRegion.GetBounds().IsEqualInterior(destBufferRect) || - neededRegion.GetNumRects() > 1)) - { - // The area we add to neededRegion might not be painted opaquely. - if (mode == SurfaceMode::SURFACE_OPAQUE) { - contentType = gfxContentType::COLOR_ALPHA; - mode = SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA; - } - - // We need to validate the entire buffer, to make sure that only valid - // pixels are sampled. - neededRegion = destBufferRect; - } - - // If we have an existing buffer, but the content type has changed or we - // have transitioned into/out of component alpha, then we need to recreate it. - if (canReuseBuffer && - (contentType != BufferContentType() || - (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) != HaveBufferOnWhite())) - { - // Restart the decision process; we won't re-enter since we guard on - // being able to re-use the buffer. - canReuseBuffer = false; - canKeepBufferContents = false; - validRegion.SetEmpty(); - continue; - } - - break; - } - - NS_ASSERTION(destBufferRect.Contains(neededRegion.GetBounds()), - "Destination rect doesn't contain what we need to paint"); - - BufferDecision dest; - dest.mNeededRegion = Move(neededRegion); - dest.mValidRegion = Move(validRegion); - dest.mBufferRect = destBufferRect; - dest.mBufferMode = mode; - dest.mBufferContentType = contentType; - dest.mCanReuseBuffer = canReuseBuffer; - dest.mCanKeepBufferContents = canKeepBufferContents; - return dest; -} - -gfxContentType -RotatedContentBuffer::BufferContentType() -{ - if (mBufferProvider || (mDTBuffer && mDTBuffer->IsValid())) { - SurfaceFormat format = SurfaceFormat::B8G8R8A8; - - if (mBufferProvider) { - format = mBufferProvider->GetFormat(); - } else if (mDTBuffer && mDTBuffer->IsValid()) { - format = mDTBuffer->GetFormat(); - } - - return ContentForFormat(format); - } - return gfxContentType::SENTINEL; -} - -bool -RotatedContentBuffer::BufferSizeOkFor(const IntSize& aSize) -{ - return (aSize == mBufferRect.Size() || - (SizedToVisibleBounds != mBufferSizePolicy && - aSize < mBufferRect.Size())); -} - -bool -RotatedContentBuffer::EnsureBuffer() -{ - NS_ASSERTION(!mLoanedDrawTarget, "Loaned draw target must be returned"); - if (!mDTBuffer || !mDTBuffer->IsValid()) { - if (mBufferProvider) { - mDTBuffer = mBufferProvider->BorrowDrawTarget(); - } - } - - NS_WARNING_ASSERTION(mDTBuffer && mDTBuffer->IsValid(), "no buffer"); - return !!mDTBuffer; -} - -bool -RotatedContentBuffer::EnsureBufferOnWhite() -{ - NS_ASSERTION(!mLoanedDrawTarget, "Loaned draw target must be returned"); - if (!mDTBufferOnWhite) { - if (mBufferProviderOnWhite) { - mDTBufferOnWhite = - mBufferProviderOnWhite->BorrowDrawTarget(); - } - } - - NS_WARNING_ASSERTION(mDTBufferOnWhite, "no buffer"); - return !!mDTBufferOnWhite; -} - -bool -RotatedContentBuffer::HaveBuffer() const -{ - return mBufferProvider || (mDTBuffer && mDTBuffer->IsValid()); -} - -bool -RotatedContentBuffer::HaveBufferOnWhite() const -{ - return mBufferProviderOnWhite || (mDTBufferOnWhite && mDTBufferOnWhite->IsValid()); -} - -void -RotatedContentBuffer::FlushBuffers() -{ - if (mDTBuffer) { - mDTBuffer->Flush(); - } - if (mDTBufferOnWhite) { - mDTBufferOnWhite->Flush(); - } -} - -RotatedContentBuffer::PaintState -RotatedContentBuffer::BeginPaint(PaintedLayer* aLayer, - uint32_t aFlags) -{ - PaintState result; - - BufferDecision dest = CalculateBufferForPaint(aLayer, aFlags); - result.mContentType = dest.mBufferContentType; - - if (!dest.mCanKeepBufferContents) { - // We're effectively clearing the valid region, so we need to draw - // the entire needed region now. - MOZ_ASSERT(!dest.mCanReuseBuffer); - MOZ_ASSERT(dest.mValidRegion.IsEmpty()); - - result.mRegionToInvalidate = aLayer->GetValidRegion(); - Clear(); - -#if defined(MOZ_DUMP_PAINTING) - if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) { - if (result.mContentType != BufferContentType()) { - printf_stderr("Invalidating entire rotated buffer (layer %p): content type changed\n", aLayer); - } else if ((dest.mBufferMode == SurfaceMode::SURFACE_COMPONENT_ALPHA) != HaveBufferOnWhite()) { - printf_stderr("Invalidating entire rotated buffer (layer %p): component alpha changed\n", aLayer); - } - } -#endif - } - - result.mRegionToDraw.Sub(dest.mNeededRegion, - dest.mValidRegion); - - if (result.mRegionToDraw.IsEmpty()) - return result; - - if (HaveBuffer()) { - if (LockBuffers()) { - // Do not modify result.mRegionToDraw or result.mContentType after this call. - // Do not modify mBufferRect, mBufferRotation, or mDidSelfCopy, - // or call CreateBuffer before this call. - FinalizeFrame(result.mRegionToDraw); - } else { - // Abandon everything and redraw it all. Ideally we'd reallocate and copy - // the old to the new and then call FinalizeFrame on the new buffer so that - // we only need to draw the latest bits, but we need a big refactor to support - // that ordering. - result.mRegionToDraw = dest.mNeededRegion; - dest.mCanReuseBuffer = false; - Clear(); - } - } - - // We need to disable rotation if we're going to be resampled when - // drawing, because we might sample across the rotation boundary. - // Also disable buffer rotation when using webrender. - bool canHaveRotation = gfxPlatform::BufferRotationEnabled() && - !(aFlags & (PAINT_WILL_RESAMPLE | PAINT_NO_ROTATION)) && - !(aLayer->Manager()->AsWebRenderLayerManager()); - bool canDrawRotated = aFlags & PAINT_CAN_DRAW_ROTATED; - - IntRect drawBounds = result.mRegionToDraw.GetBounds(); - RefPtr destDTBuffer; - RefPtr destDTBufferOnWhite; - uint32_t bufferFlags = 0; - if (dest.mBufferMode == SurfaceMode::SURFACE_COMPONENT_ALPHA) { - bufferFlags |= BUFFER_COMPONENT_ALPHA; - } - if (dest.mCanReuseBuffer) { - if (!EnsureBuffer() || - (HaveBufferOnWhite() && !EnsureBufferOnWhite())) { - return result; - } - - if (!AdjustTo(dest.mBufferRect, - drawBounds, - canHaveRotation, - canDrawRotated)) { - dest.mBufferRect = ComputeBufferRect(dest.mNeededRegion.GetBounds()); - CreateBuffer(result.mContentType, dest.mBufferRect, bufferFlags, - &destDTBuffer, &destDTBufferOnWhite); - - if (!destDTBuffer || - (!destDTBufferOnWhite && (bufferFlags & BUFFER_COMPONENT_ALPHA))) { - if (Factory::ReasonableSurfaceSize(IntSize(dest.mBufferRect.Width(), dest.mBufferRect.Height()))) { - gfxCriticalNote << "Failed 1 buffer db=" << hexa(destDTBuffer.get()) - << " dw=" << hexa(destDTBufferOnWhite.get()) - << " for " << dest.mBufferRect.x << ", " - << dest.mBufferRect.y << ", " - << dest.mBufferRect.Width() << ", " - << dest.mBufferRect.Height(); - } - return result; - } - } - } else { - // The buffer's not big enough, so allocate a new one - CreateBuffer(result.mContentType, dest.mBufferRect, bufferFlags, - &destDTBuffer, &destDTBufferOnWhite); - if (!destDTBuffer || - (!destDTBufferOnWhite && (bufferFlags & BUFFER_COMPONENT_ALPHA))) { - if (Factory::ReasonableSurfaceSize(IntSize(dest.mBufferRect.Width(), dest.mBufferRect.Height()))) { - gfxCriticalNote << "Failed 2 buffer db=" << hexa(destDTBuffer.get()) - << " dw=" << hexa(destDTBufferOnWhite.get()) - << " for " << dest.mBufferRect.x << ", " - << dest.mBufferRect.y << ", " - << dest.mBufferRect.Width() << ", " - << dest.mBufferRect.Height(); - } - return result; - } - } - - NS_ASSERTION(!(aFlags & PAINT_WILL_RESAMPLE) || dest.mBufferRect == dest.mNeededRegion.GetBounds(), - "If we're resampling, we need to validate the entire buffer"); - - // If needed, copy the old buffer over to the new one - if (destDTBuffer) { - if ((HaveBuffer() && EnsureBuffer()) && - (dest.mBufferMode != SurfaceMode::SURFACE_COMPONENT_ALPHA || (HaveBufferOnWhite() && EnsureBufferOnWhite()))) { - DrawTargetRotatedBuffer oldBuffer = DrawTargetRotatedBuffer(mDTBuffer, mDTBufferOnWhite, - mBufferRect, mBufferRotation); - - mDTBuffer = destDTBuffer.forget(); - mDTBufferOnWhite = destDTBufferOnWhite.forget(); - mBufferRect = dest.mBufferRect; - mBufferRotation = IntPoint(0,0); - - UpdateDestinationFrom(oldBuffer, nsIntRegion(mBufferRect)); - } else { - mDTBuffer = destDTBuffer.forget(); - mDTBufferOnWhite = destDTBufferOnWhite.forget(); - mBufferRect = dest.mBufferRect; - mBufferRotation = IntPoint(0,0); - } - } - NS_ASSERTION(canHaveRotation || mBufferRotation == IntPoint(0,0), - "Rotation disabled, but we have nonzero rotation?"); - - nsIntRegion invalidate; - invalidate.Sub(aLayer->GetValidRegion(), dest.mBufferRect); - result.mRegionToInvalidate.Or(result.mRegionToInvalidate, invalidate); - - result.mClip = DrawRegionClip::DRAW; - result.mMode = dest.mBufferMode; - - return result; -} - -RefPtr -RotatedContentBuffer::BorrowDrawTargetForRecording(PaintState& aPaintState, - DrawIterator* aIter, - bool aSetTransform) -{ - if (aPaintState.mMode == SurfaceMode::SURFACE_NONE || - !EnsureBuffer() || - (HaveBufferOnWhite() && !EnsureBufferOnWhite())) { - return nullptr; - } - - Matrix transform; - DrawTarget* result = BorrowDrawTargetForQuadrantUpdate(aPaintState.mRegionToDraw.GetBounds(), - BUFFER_BOTH, aIter, - aSetTransform, - &transform); - if (!result) { - return nullptr; - } - - nsIntRegion regionToDraw = - ExpandDrawRegion(aPaintState, aIter, result->GetBackendType()); - - RefPtr state = - new CapturedPaintState(regionToDraw, - result, - mDTBufferOnWhite, - transform, - aPaintState.mMode, - aPaintState.mContentType); - return state; -} - -/*static */ bool -RotatedContentBuffer::PrepareDrawTargetForPainting(CapturedPaintState* aState) -{ - MOZ_ASSERT(aState); - RefPtr target = aState->mTarget; - RefPtr whiteTarget = aState->mTargetOnWhite; - - if (aState->mSurfaceMode == SurfaceMode::SURFACE_COMPONENT_ALPHA) { - if (!target || !target->IsValid() || - !whiteTarget || !whiteTarget->IsValid()) { - // This can happen in release builds if allocating one of the two buffers - // failed. This in turn can happen if unreasonably large textures are - // requested. - return false; - } - for (auto iter = aState->mRegionToDraw.RectIter(); !iter.Done(); iter.Next()) { - const IntRect& rect = iter.Get(); - target->FillRect(Rect(rect.x, rect.y, rect.Width(), rect.Height()), - ColorPattern(Color(0.0, 0.0, 0.0, 1.0))); - whiteTarget->FillRect(Rect(rect.x, rect.y, rect.Width(), rect.Height()), - ColorPattern(Color(1.0, 1.0, 1.0, 1.0))); - } - } else if (aState->mContentType == gfxContentType::COLOR_ALPHA && - target->IsValid()) { - // HaveBuffer() => we have an existing buffer that we must clear - for (auto iter = aState->mRegionToDraw.RectIter(); !iter.Done(); iter.Next()) { - const IntRect& rect = iter.Get(); - target->ClearRect(Rect(rect.x, rect.y, rect.Width(), rect.Height())); - } - } - - return true; -} - -nsIntRegion -RotatedContentBuffer::ExpandDrawRegion(PaintState& aPaintState, - DrawIterator* aIter, - BackendType aBackendType) -{ - nsIntRegion* drawPtr = &aPaintState.mRegionToDraw; - if (aIter) { - // The iterators draw region currently only contains the bounds of the region, - // this makes it the precise region. - aIter->mDrawRegion.And(aIter->mDrawRegion, aPaintState.mRegionToDraw); - drawPtr = &aIter->mDrawRegion; - } - if (aBackendType == BackendType::DIRECT2D || - aBackendType == BackendType::DIRECT2D1_1) { - // Simplify the draw region to avoid hitting expensive drawing paths - // for complex regions. - drawPtr->SimplifyOutwardByArea(100 * 100); - } - return *drawPtr; -} - -DrawTarget* -RotatedContentBuffer::BorrowDrawTargetForPainting(PaintState& aPaintState, - DrawIterator* aIter /* = nullptr */) -{ - RefPtr capturedState = - BorrowDrawTargetForRecording(aPaintState, aIter, true); - - if (!capturedState) { - return nullptr; - } - - if (!RotatedContentBuffer::PrepareDrawTargetForPainting(capturedState)) { - return nullptr; - } - - return capturedState->mTarget; -} - -already_AddRefed -RotatedContentBuffer::GetSourceSurface(ContextSource aSource) const -{ - if (!mDTBuffer || !mDTBuffer->IsValid()) { - gfxCriticalNote << "Invalid buffer in RotatedContentBuffer::GetSourceSurface " << gfx::hexa(mDTBuffer); - return nullptr; - } - - if (aSource == BUFFER_BLACK) { - return mDTBuffer->Snapshot(); - } else { - if (!mDTBufferOnWhite || !mDTBufferOnWhite->IsValid()) { - gfxCriticalNote << "Invalid buffer on white in RotatedContentBuffer::GetSourceSurface " << gfx::hexa(mDTBufferOnWhite); - return nullptr; - } - MOZ_ASSERT(aSource == BUFFER_WHITE); - return mDTBufferOnWhite->Snapshot(); - } -} - } // namespace layers } // namespace mozilla diff --git a/gfx/layers/RotatedBuffer.h b/gfx/layers/RotatedBuffer.h index b05bf84f0c7a..bd4e9703d0a6 100644 --- a/gfx/layers/RotatedBuffer.h +++ b/gfx/layers/RotatedBuffer.h @@ -33,9 +33,10 @@ class PaintedLayer; // See comments on BorrowDrawTargetForQuadrantUpdate. class BorrowDrawTarget { -protected: +public: void ReturnDrawTarget(gfx::DrawTarget*& aReturned); +protected: // The draw target loaned by BorrowDrawTargetForQuadrantUpdate. It should not // be used, we just keep a reference to ensure it is kept alive and so we can // correctly restore state when it is returned. @@ -68,6 +69,8 @@ class RotatedBuffer : public BorrowDrawTarget public: typedef gfxContentType ContentType; + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RotatedBuffer) + RotatedBuffer(const gfx::IntRect& aBufferRect, const gfx::IntPoint& aBufferRotation) : mBufferRect(aBufferRect) @@ -139,16 +142,54 @@ public: const gfx::IntRect& BufferRect() const { return mBufferRect; } const gfx::IntPoint& BufferRotation() const { return mBufferRotation; } + void SetBufferRect(const gfx::IntRect& aBufferRect) { + mBufferRect = aBufferRect; + } + void SetBufferRotation(const gfx::IntPoint& aBufferRotation) { + mBufferRotation = aBufferRotation; + } + + bool DidSelfCopy() const { return mDidSelfCopy; } + void ClearDidSelfCopy() { mDidSelfCopy = false; } + + virtual gfx::SurfaceFormat GetFormat() const = 0; + virtual bool HaveBuffer() const = 0; virtual bool HaveBufferOnWhite() const = 0; + virtual bool IsLocked() = 0; + virtual bool Lock(OpenMode aMode) = 0; + virtual void Unlock() = 0; + virtual already_AddRefed GetSourceSurface(ContextSource aSource) const = 0; -protected: + /** + * Get a draw target at the specified resolution for updating |aBounds|, + * which must be contained within a single quadrant. + * + * The result should only be held temporarily by the caller (it will be kept + * alive by this). Once used it should be returned using ReturnDrawTarget. + * BorrowDrawTargetForQuadrantUpdate may not be called more than once without + * first calling ReturnDrawTarget. + * + * ReturnDrawTarget will by default restore the transform on the draw target. + * But it is the callers responsibility to restore the clip. + * The caller should flush the draw target, if necessary. + * If aSetTransform is false, the required transform will be set in aOutTransform. + */ + gfx::DrawTarget* + BorrowDrawTargetForQuadrantUpdate(const gfx::IntRect& aBounds, + ContextSource aSource, + DrawIterator* aIter, + bool aSetTransform = true, + gfx::Matrix* aOutTransform = nullptr); virtual gfx::DrawTarget* GetDTBuffer() const = 0; virtual gfx::DrawTarget* GetDTBufferOnWhite() const = 0; +protected: + virtual ~RotatedBuffer() {} + enum XSide { LEFT, RIGHT }; @@ -171,27 +212,6 @@ protected: gfx::SourceSurface* aMask, const gfx::Matrix* aMaskTransform) const; - /** - * Get a draw target at the specified resolution for updating |aBounds|, - * which must be contained within a single quadrant. - * - * The result should only be held temporarily by the caller (it will be kept - * alive by this). Once used it should be returned using ReturnDrawTarget. - * BorrowDrawTargetForQuadrantUpdate may not be called more than once without - * first calling ReturnDrawTarget. - * - * ReturnDrawTarget will by default restore the transform on the draw target. - * But it is the callers responsibility to restore the clip. - * The caller should flush the draw target, if necessary. - * If aSetTransform is false, the required transform will be set in aOutTransform. - */ - gfx::DrawTarget* - BorrowDrawTargetForQuadrantUpdate(const gfx::IntRect& aBounds, - ContextSource aSource, - DrawIterator* aIter, - bool aSetTransform = true, - gfx::Matrix* aOutTransform = nullptr); - /** The area of the PaintedLayer that is covered by the buffer as a whole */ gfx::IntRect mBufferRect; /** @@ -221,14 +241,18 @@ public: , mClientOnWhite(aClientOnWhite) { } - bool Lock(OpenMode aMode); - void Unlock(); + virtual bool IsLocked() override; + virtual bool Lock(OpenMode aMode) override; + virtual void Unlock() override; + void SyncWithObject(SyncObjectClient* aSyncObject); void Clear(); TextureClient* GetClient() const { return mClient; } TextureClient* GetClientOnWhite() const { return mClientOnWhite; } + virtual gfx::SurfaceFormat GetFormat() const override; + virtual bool HaveBuffer() const override { return !!mClient; } virtual bool HaveBufferOnWhite() const override { return !!mClientOnWhite; } @@ -257,6 +281,12 @@ public: , mTargetOnWhite(aTargetOnWhite) { } + virtual bool IsLocked() override { return false; } + virtual bool Lock(OpenMode aMode) override { return true; } + virtual void Unlock() override {} + + virtual gfx::SurfaceFormat GetFormat() const override; + virtual bool HaveBuffer() const override { return !!mTarget; } virtual bool HaveBufferOnWhite() const override { return !!mTargetOnWhite; } @@ -282,8 +312,14 @@ public: , mSourceOnWhite(aSourceOnWhite) { } + virtual bool IsLocked() override { return false; } + virtual bool Lock(OpenMode aMode) override { return false; } + virtual void Unlock() override {} + virtual already_AddRefed GetSourceSurface(ContextSource aSource) const; + virtual gfx::SurfaceFormat GetFormat() const override; + virtual bool HaveBuffer() const { return !!mSource; } virtual bool HaveBufferOnWhite() const { return !!mSourceOnWhite; } @@ -296,250 +332,6 @@ private: RefPtr mSourceOnWhite; }; -/** - * This class encapsulates the buffer used to retain PaintedLayer contents, - * i.e., the contents of the layer's GetVisibleRegion(). - */ -class RotatedContentBuffer : public RotatedBuffer -{ -public: - typedef gfxContentType ContentType; - - /** - * Controls the size of the backing buffer of this. - * - SizedToVisibleBounds: the backing buffer is exactly the same - * size as the bounds of PaintedLayer's visible region - * - ContainsVisibleBounds: the backing buffer is large enough to - * fit visible bounds. May be larger. - */ - enum BufferSizePolicy { - SizedToVisibleBounds, - ContainsVisibleBounds - }; - - explicit RotatedContentBuffer(BufferSizePolicy aBufferSizePolicy) - : mBufferProvider(nullptr) - , mBufferProviderOnWhite(nullptr) - , mBufferSizePolicy(aBufferSizePolicy) - { - MOZ_COUNT_CTOR(RotatedContentBuffer); - } - virtual ~RotatedContentBuffer() - { - MOZ_COUNT_DTOR(RotatedContentBuffer); - } - - /** - * Wipe out all retained contents. Call this when the entire - * buffer becomes invalid. - */ - void Clear() - { - UnlockBuffers(); - mDTBuffer = nullptr; - mDTBufferOnWhite = nullptr; - mBufferProvider = nullptr; - mBufferProviderOnWhite = nullptr; - mBufferRect.SetEmpty(); - } - - /** - * This is returned by BeginPaint. The caller should draw into mTarget. - * mRegionToDraw must be drawn. mRegionToInvalidate has been invalidated - * by RotatedContentBuffer and must be redrawn on the screen. - * mRegionToInvalidate is set when the buffer has changed from - * opaque to transparent or vice versa, since the details of rendering can - * depend on the buffer type. - */ - struct PaintState { - PaintState() - : mRegionToDraw() - , mRegionToInvalidate() - , mMode(SurfaceMode::SURFACE_NONE) - , mClip(DrawRegionClip::NONE) - , mContentType(gfxContentType::SENTINEL) - {} - - nsIntRegion mRegionToDraw; - nsIntRegion mRegionToInvalidate; - SurfaceMode mMode; - DrawRegionClip mClip; - ContentType mContentType; - }; - - enum { - PAINT_WILL_RESAMPLE = 0x01, - PAINT_NO_ROTATION = 0x02, - PAINT_CAN_DRAW_ROTATED = 0x04 - }; - /** - * Start a drawing operation. This returns a PaintState describing what - * needs to be drawn to bring the buffer up to date in the visible region. - * This queries aLayer to get the currently valid and visible regions. - * The returned mTarget may be null if mRegionToDraw is empty. - * Otherwise it must not be null. - * mRegionToInvalidate will contain mRegionToDraw. - * @param aFlags when PAINT_WILL_RESAMPLE is passed, this indicates that - * buffer will be resampled when rendering (i.e the effective transform - * combined with the scale for the resolution is not just an integer - * translation). This will disable buffer rotation (since we don't want - * to resample across the rotation boundary) and will ensure that we - * make the entire buffer contents valid (since we don't want to sample - * invalid pixels outside the visible region, if the visible region doesn't - * fill the buffer bounds). - * PAINT_CAN_DRAW_ROTATED can be passed if the caller supports drawing - * rotated content that crosses the physical buffer boundary. The caller - * will need to call BorrowDrawTargetForPainting multiple times to achieve - * this. - */ - PaintState BeginPaint(PaintedLayer* aLayer, - uint32_t aFlags); - - /** - * Fetch a DrawTarget for rendering. The DrawTarget remains owned by - * this. See notes on BorrowDrawTargetForQuadrantUpdate. - * May return null. If the return value is non-null, it must be - * 'un-borrowed' using ReturnDrawTarget. - * - * If PAINT_CAN_DRAW_ROTATED was specified for BeginPaint, then the caller - * must call this function repeatedly (with an iterator) until it returns - * nullptr. The caller should draw the mDrawRegion of the iterator instead - * of mRegionToDraw in the PaintState. - * - * @param aPaintState Paint state data returned by a call to BeginPaint - * @param aIter Paint state iterator. Only required if PAINT_CAN_DRAW_ROTATED - * was specified to BeginPaint. - */ - gfx::DrawTarget* BorrowDrawTargetForPainting(PaintState& aPaintState, - DrawIterator* aIter = nullptr); - - /** - * Borrow a draw target for recording. The required transform for correct painting - * is not applied to the returned DrawTarget by default, BUT it is - * required to be whenever drawing does happen. - */ - RefPtr BorrowDrawTargetForRecording(PaintState& aPaintState, - DrawIterator* aIter, - bool aSetTransform = false); - - nsIntRegion ExpandDrawRegion(PaintState& aPaintState, - DrawIterator* aIter, - gfx::BackendType aBackendType); - - static bool PrepareDrawTargetForPainting(CapturedPaintState*); - enum { - BUFFER_COMPONENT_ALPHA = 0x02 // Dual buffers should be created for drawing with - // component alpha. - }; - /** - * Return a new surface of |aSize| and |aType|. - * - * If the created buffer supports azure content, then the result(s) will - * be returned in aBlackDT/aWhiteDT, otherwise aBlackSurface/aWhiteSurface - * will be used. - */ - virtual void - CreateBuffer(ContentType aType, const gfx::IntRect& aRect, uint32_t aFlags, - RefPtr* aBlackDT, RefPtr* aWhiteDT) = 0; - - virtual already_AddRefed GetSourceSurface(ContextSource aSource) const; - -protected: - // new texture client versions - void SetBufferProvider(TextureClient* aClient) - { - // Only this buffer provider can give us a buffer. If we - // already have one, something has gone wrong. - MOZ_ASSERT(!aClient || !mDTBuffer || !mDTBuffer->IsValid()); - - mBufferProvider = aClient; - if (!mBufferProvider) { - mDTBuffer = nullptr; - } - } - - void SetBufferProviderOnWhite(TextureClient* aClient) - { - // Only this buffer provider can give us a buffer. If we - // already have one, something has gone wrong. - MOZ_ASSERT(!aClient || !mDTBufferOnWhite || !mDTBufferOnWhite->IsValid()); - - mBufferProviderOnWhite = aClient; - if (!mBufferProviderOnWhite) { - mDTBufferOnWhite = nullptr; - } - } - -protected: - struct BufferDecision { - nsIntRegion mNeededRegion; - nsIntRegion mValidRegion; - gfx::IntRect mBufferRect; - SurfaceMode mBufferMode; - ContentType mBufferContentType; - bool mCanReuseBuffer; - bool mCanKeepBufferContents; - }; - - BufferDecision CalculateBufferForPaint(PaintedLayer* aLayer, - uint32_t aFlags); - - /** - * Return the buffer's content type. Requires a valid buffer or - * buffer provider. - */ - gfxContentType BufferContentType(); - bool BufferSizeOkFor(const gfx::IntSize& aSize); - /** - * If the buffer hasn't been mapped, map it. - */ - bool EnsureBuffer(); - bool EnsureBufferOnWhite(); - - // Flush our buffers if they are mapped. - void FlushBuffers(); - - /** - * Get the underlying buffer, if any. This is useful because we can pass - * in the buffer as the default "reference surface" if there is one. - * Don't use it for anything else! - */ - virtual gfx::DrawTarget* GetDTBuffer() const { return mDTBuffer; } - virtual gfx::DrawTarget* GetDTBufferOnWhite() const { return mDTBufferOnWhite; } - - /** - * True if we have a buffer where we can get it (but not necessarily - * mapped currently). - */ - virtual bool HaveBuffer() const; - virtual bool HaveBufferOnWhite() const; - - /** - * Any actions that should be performed at the last moment before we begin - * rendering the next frame. I.e., after we calculate what we will draw, - * but before we rotate the buffer and possibly create new buffers. - * aRegionToDraw is the region which is guaranteed to be overwritten when - * drawing the next frame. - */ - virtual void FinalizeFrame(const nsIntRegion& aRegionToDraw) {} - - virtual bool LockBuffers() { return true; } - virtual void UnlockBuffers() {} - - RefPtr mDTBuffer; - RefPtr mDTBufferOnWhite; - - /** - * These members are only set transiently. They're used to map mDTBuffer - * when we're using surfaces that require explicit map/unmap. Only one - * may be used at a time. - */ - TextureClient* mBufferProvider; - TextureClient* mBufferProviderOnWhite; - - BufferSizePolicy mBufferSizePolicy; -}; - } // namespace layers } // namespace mozilla diff --git a/gfx/layers/basic/BasicPaintedLayer.cpp b/gfx/layers/basic/BasicPaintedLayer.cpp index 0778bee9b18c..d8eaf6802428 100644 --- a/gfx/layers/basic/BasicPaintedLayer.cpp +++ b/gfx/layers/basic/BasicPaintedLayer.cpp @@ -150,16 +150,16 @@ BasicPaintedLayer::Validate(LayerManager::DrawPaintedLayerCallback aCallback, uint32_t flags = 0; #ifndef MOZ_WIDGET_ANDROID if (BasicManager()->CompositorMightResample()) { - flags |= RotatedContentBuffer::PAINT_WILL_RESAMPLE; + flags |= ContentClient::PAINT_WILL_RESAMPLE; } - if (!(flags & RotatedContentBuffer::PAINT_WILL_RESAMPLE)) { + if (!(flags & ContentClient::PAINT_WILL_RESAMPLE)) { if (MayResample()) { - flags |= RotatedContentBuffer::PAINT_WILL_RESAMPLE; + flags |= ContentClient::PAINT_WILL_RESAMPLE; } } #endif if (mDrawAtomically) { - flags |= RotatedContentBuffer::PAINT_NO_ROTATION; + flags |= ContentClient::PAINT_NO_ROTATION; } PaintState state = mContentClient->BeginPaintBuffer(this, flags); diff --git a/gfx/layers/basic/BasicPaintedLayer.h b/gfx/layers/basic/BasicPaintedLayer.h index 0459e79a88ee..47ed37d11eb9 100644 --- a/gfx/layers/basic/BasicPaintedLayer.h +++ b/gfx/layers/basic/BasicPaintedLayer.h @@ -27,8 +27,8 @@ class ReadbackProcessor; class BasicPaintedLayer : public PaintedLayer, public BasicImplData { public: - typedef RotatedContentBuffer::PaintState PaintState; - typedef RotatedContentBuffer::ContentType ContentType; + typedef ContentClient::PaintState PaintState; + typedef ContentClient::ContentType ContentType; explicit BasicPaintedLayer(BasicLayerManager* aLayerManager, gfx::BackendType aBackend) : PaintedLayer(aLayerManager, static_cast(this)), diff --git a/gfx/layers/client/ClientPaintedLayer.cpp b/gfx/layers/client/ClientPaintedLayer.cpp index 4faa88267917..ac73415212e3 100644 --- a/gfx/layers/client/ClientPaintedLayer.cpp +++ b/gfx/layers/client/ClientPaintedLayer.cpp @@ -116,14 +116,14 @@ ClientPaintedLayer::UpdatePaintRegion(PaintState& aState) uint32_t ClientPaintedLayer::GetPaintFlags() { - uint32_t flags = RotatedContentBuffer::PAINT_CAN_DRAW_ROTATED; + uint32_t flags = ContentClient::PAINT_CAN_DRAW_ROTATED; #ifndef MOZ_IGNORE_PAINT_WILL_RESAMPLE if (ClientManager()->CompositorMightResample()) { - flags |= RotatedContentBuffer::PAINT_WILL_RESAMPLE; + flags |= ContentClient::PAINT_WILL_RESAMPLE; } - if (!(flags & RotatedContentBuffer::PAINT_WILL_RESAMPLE)) { + if (!(flags & ContentClient::PAINT_WILL_RESAMPLE)) { if (MayResample()) { - flags |= RotatedContentBuffer::PAINT_WILL_RESAMPLE; + flags |= ContentClient::PAINT_WILL_RESAMPLE; } } #endif @@ -148,7 +148,7 @@ ClientPaintedLayer::PaintThebes(nsTArray* aReadbackUp } bool didUpdate = false; - RotatedContentBuffer::DrawIterator iter; + RotatedBuffer::DrawIterator iter; while (DrawTarget* target = mContentClient->BorrowDrawTargetForPainting(state, &iter)) { if (!target || !target->IsValid()) { if (target) { @@ -218,7 +218,7 @@ ClientPaintedLayer::PaintOffMainThread() } bool didUpdate = false; - RotatedContentBuffer::DrawIterator iter; + RotatedBuffer::DrawIterator iter; // Debug Protip: Change to BorrowDrawTargetForPainting if using sync OMTP. while (RefPtr captureState = @@ -255,7 +255,7 @@ ClientPaintedLayer::PaintOffMainThread() captureState->mCapture = captureDT.forget(); PaintThread::Get()->PaintContents(captureState, - RotatedContentBuffer::PrepareDrawTargetForPainting); + ContentClient::PrepareDrawTargetForPainting); mContentClient->ReturnDrawTargetToBuffer(target); diff --git a/gfx/layers/client/ClientPaintedLayer.h b/gfx/layers/client/ClientPaintedLayer.h index 6777ec3405a5..8d89be13c884 100644 --- a/gfx/layers/client/ClientPaintedLayer.h +++ b/gfx/layers/client/ClientPaintedLayer.h @@ -32,8 +32,8 @@ class SpecificLayerAttributes; class ClientPaintedLayer : public PaintedLayer, public ClientLayer { public: - typedef RotatedContentBuffer::PaintState PaintState; - typedef RotatedContentBuffer::ContentType ContentType; + typedef ContentClient::PaintState PaintState; + typedef ContentClient::ContentType ContentType; explicit ClientPaintedLayer(ClientLayerManager* aLayerManager, LayerManager::PaintedLayerCreationHint aCreationHint = LayerManager::NONE) : diff --git a/gfx/layers/client/ContentClient.cpp b/gfx/layers/client/ContentClient.cpp index f24fee00afe2..2e822dc63cd4 100644 --- a/gfx/layers/client/ContentClient.cpp +++ b/gfx/layers/client/ContentClient.cpp @@ -13,7 +13,6 @@ #include "gfxUtils.h" // for gfxUtils #include "ipc/ShadowLayers.h" // for ShadowLayerForwarder #include "mozilla/ArrayUtils.h" // for ArrayLength -#include "mozilla/Maybe.h" #include "mozilla/gfx/2D.h" // for DrawTarget, Factory #include "mozilla/gfx/BasePoint.h" // for BasePoint #include "mozilla/gfx/BaseSize.h" // for BaseSize @@ -35,6 +34,7 @@ #endif #include "ReadbackLayer.h" +#include #include using namespace std; @@ -45,17 +45,30 @@ using namespace gfx; namespace layers { -static TextureFlags TextureFlagsForRotatedContentBufferFlags(uint32_t aBufferFlags) +static TextureFlags TextureFlagsForContentClientFlags(uint32_t aBufferFlags) { TextureFlags result = TextureFlags::NO_FLAGS; - if (aBufferFlags & RotatedContentBuffer::BUFFER_COMPONENT_ALPHA) { + if (aBufferFlags & ContentClient::BUFFER_COMPONENT_ALPHA) { result |= TextureFlags::COMPONENT_ALPHA; } return result; } +static IntRect +ComputeBufferRect(const IntRect& aRequestedRect) +{ + IntRect rect(aRequestedRect); + // Set a minimum width to guarantee a minimum size of buffers we + // allocate (and work around problems on some platforms with smaller + // dimensions). 64 used to be the magic number needed to work around + // a rendering glitch on b2g (see bug 788411). Now that we don't support + // this device anymore we should be fine with 8 pixels as the minimum. + rect.SetWidth(std::max(aRequestedRect.Width(), 8)); + return rect; +} + /* static */ already_AddRefed ContentClient::CreateContentClient(CompositableForwarder* aForwarder) { @@ -91,6 +104,377 @@ ContentClient::CreateContentClient(CompositableForwarder* aForwarder) return MakeAndAddRef(aForwarder); } +void +ContentClient::Clear() +{ + mBuffer = nullptr; +} + +ContentClient::PaintState +ContentClient::BeginPaintBuffer(PaintedLayer* aLayer, + uint32_t aFlags) +{ + PaintState result; + + BufferDecision dest = CalculateBufferForPaint(aLayer, aFlags); + result.mContentType = dest.mBufferContentType; + + if (!dest.mCanKeepBufferContents) { + // We're effectively clearing the valid region, so we need to draw + // the entire needed region now. + MOZ_ASSERT(!dest.mCanReuseBuffer); + MOZ_ASSERT(dest.mValidRegion.IsEmpty()); + + result.mRegionToInvalidate = aLayer->GetValidRegion(); + Clear(); + +#if defined(MOZ_DUMP_PAINTING) + if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) { + if (result.mContentType != BufferContentType()) { + printf_stderr("Invalidating entire rotated buffer (layer %p): content type changed\n", aLayer); + } else if ((dest.mBufferMode == SurfaceMode::SURFACE_COMPONENT_ALPHA) != mBuffer->HaveBufferOnWhite()) { + printf_stderr("Invalidating entire rotated buffer (layer %p): component alpha changed\n", aLayer); + } + } +#endif + } + + result.mRegionToDraw.Sub(dest.mNeededRegion, + dest.mValidRegion); + + if (result.mRegionToDraw.IsEmpty()) + return result; + + if (mBuffer) { + if (mBuffer->Lock(LockMode())) { + // Do not modify result.mRegionToDraw or result.mContentType after this call. + // Do not modify mBufferRect, mBufferRotation, or mDidSelfCopy, + // or call CreateBuffer before this call. + FinalizeFrame(result.mRegionToDraw); + } else { + // Abandon everything and redraw it all. Ideally we'd reallocate and copy + // the old to the new and then call FinalizeFrame on the new buffer so that + // we only need to draw the latest bits, but we need a big refactor to support + // that ordering. + result.mRegionToDraw = dest.mNeededRegion; + dest.mCanReuseBuffer = false; + Clear(); + } + } + + // We need to disable rotation if we're going to be resampled when + // drawing, because we might sample across the rotation boundary. + // Also disable buffer rotation when using webrender. + bool canHaveRotation = gfxPlatform::BufferRotationEnabled() && + !(aFlags & (PAINT_WILL_RESAMPLE | PAINT_NO_ROTATION)) && + !(aLayer->Manager()->AsWebRenderLayerManager()); + bool canDrawRotated = aFlags & PAINT_CAN_DRAW_ROTATED; + + IntRect drawBounds = result.mRegionToDraw.GetBounds(); + RefPtr newBuffer; + uint32_t bufferFlags = 0; + if (dest.mBufferMode == SurfaceMode::SURFACE_COMPONENT_ALPHA) { + bufferFlags |= BUFFER_COMPONENT_ALPHA; + } + if (dest.mCanReuseBuffer && mBuffer) { + if (!mBuffer->AdjustTo(dest.mBufferRect, + drawBounds, + canHaveRotation, + canDrawRotated)) { + dest.mBufferRect = ComputeBufferRect(dest.mNeededRegion.GetBounds()); + newBuffer = CreateBuffer(result.mContentType, dest.mBufferRect, bufferFlags); + + if (!newBuffer) { + if (Factory::ReasonableSurfaceSize(IntSize(dest.mBufferRect.Width(), dest.mBufferRect.Height()))) { + gfxCriticalNote << "Failed 1 buffer for " + << dest.mBufferRect.x << ", " + << dest.mBufferRect.y << ", " + << dest.mBufferRect.Width() << ", " + << dest.mBufferRect.Height(); + } + return result; + } + } + } else { + // The buffer's not big enough, so allocate a new one + newBuffer = CreateBuffer(result.mContentType, dest.mBufferRect, bufferFlags); + if (!newBuffer) { + if (Factory::ReasonableSurfaceSize(IntSize(dest.mBufferRect.Width(), dest.mBufferRect.Height()))) { + gfxCriticalNote << "Failed 2 buffer for " + << dest.mBufferRect.x << ", " + << dest.mBufferRect.y << ", " + << dest.mBufferRect.Width() << ", " + << dest.mBufferRect.Height(); + } + return result; + } + } + + NS_ASSERTION(!(aFlags & PAINT_WILL_RESAMPLE) || dest.mBufferRect == dest.mNeededRegion.GetBounds(), + "If we're resampling, we need to validate the entire buffer"); + + // If needed, copy the old buffer over to the new one + if (newBuffer) { + if (mBuffer) { + newBuffer->UpdateDestinationFrom(*mBuffer, nsIntRegion(newBuffer->BufferRect())); + } + + // Ensure our reference to the front buffer is released + // as well as the old back buffer. + Clear(); + + mBuffer = newBuffer; + } + + NS_ASSERTION(canHaveRotation || mBuffer->BufferRotation() == IntPoint(0,0), + "Rotation disabled, but we have nonzero rotation?"); + + nsIntRegion invalidate; + invalidate.Sub(aLayer->GetValidRegion(), dest.mBufferRect); + result.mRegionToInvalidate.Or(result.mRegionToInvalidate, invalidate); + + result.mClip = DrawRegionClip::DRAW; + result.mMode = dest.mBufferMode; + + return result; +} + +DrawTarget* +ContentClient::BorrowDrawTargetForPainting(ContentClient::PaintState& aPaintState, + RotatedBuffer::DrawIterator* aIter /* = nullptr */) +{ + RefPtr capturedState = + ContentClient::BorrowDrawTargetForRecording(aPaintState, aIter, true); + + if (!capturedState) { + return nullptr; + } + + if (!ContentClient::PrepareDrawTargetForPainting(capturedState)) { + return nullptr; + } + + return capturedState->mTarget; +} + +RefPtr +ContentClient::BorrowDrawTargetForRecording(ContentClient::PaintState& aPaintState, + RotatedBuffer::DrawIterator* aIter, + bool aSetTransform) +{ + if (aPaintState.mMode == SurfaceMode::SURFACE_NONE || !mBuffer) { + return nullptr; + } + + Matrix transform; + DrawTarget* result = mBuffer->BorrowDrawTargetForQuadrantUpdate( + aPaintState.mRegionToDraw.GetBounds(), + RotatedBuffer::BUFFER_BOTH, aIter, + aSetTransform, + &transform); + if (!result) { + return nullptr; + } + + nsIntRegion regionToDraw = + ExpandDrawRegion(aPaintState, aIter, result->GetBackendType()); + + RefPtr state = + new CapturedPaintState(regionToDraw, + result, + mBuffer->GetDTBufferOnWhite(), + transform, + aPaintState.mMode, + aPaintState.mContentType); + return state; +} + +void +ContentClient::ReturnDrawTargetToBuffer(gfx::DrawTarget*& aReturned) +{ + mBuffer->ReturnDrawTarget(aReturned); +} + +nsIntRegion +ContentClient::ExpandDrawRegion(ContentClient::PaintState& aPaintState, + RotatedBuffer::DrawIterator* aIter, + BackendType aBackendType) +{ + nsIntRegion* drawPtr = &aPaintState.mRegionToDraw; + if (aIter) { + // The iterators draw region currently only contains the bounds of the region, + // this makes it the precise region. + aIter->mDrawRegion.And(aIter->mDrawRegion, aPaintState.mRegionToDraw); + drawPtr = &aIter->mDrawRegion; + } + if (aBackendType == BackendType::DIRECT2D || + aBackendType == BackendType::DIRECT2D1_1) { + // Simplify the draw region to avoid hitting expensive drawing paths + // for complex regions. + drawPtr->SimplifyOutwardByArea(100 * 100); + } + return *drawPtr; +} + +/*static */ bool +ContentClient::PrepareDrawTargetForPainting(CapturedPaintState* aState) +{ + MOZ_ASSERT(aState); + RefPtr target = aState->mTarget; + RefPtr whiteTarget = aState->mTargetOnWhite; + + if (aState->mSurfaceMode == SurfaceMode::SURFACE_COMPONENT_ALPHA) { + if (!target || !target->IsValid() || + !whiteTarget || !whiteTarget->IsValid()) { + // This can happen in release builds if allocating one of the two buffers + // failed. This in turn can happen if unreasonably large textures are + // requested. + return false; + } + for (auto iter = aState->mRegionToDraw.RectIter(); !iter.Done(); iter.Next()) { + const IntRect& rect = iter.Get(); + target->FillRect(Rect(rect.x, rect.y, rect.Width(), rect.Height()), + ColorPattern(Color(0.0, 0.0, 0.0, 1.0))); + whiteTarget->FillRect(Rect(rect.x, rect.y, rect.Width(), rect.Height()), + ColorPattern(Color(1.0, 1.0, 1.0, 1.0))); + } + } else if (aState->mContentType == gfxContentType::COLOR_ALPHA && + target->IsValid()) { + // HaveBuffer() => we have an existing buffer that we must clear + for (auto iter = aState->mRegionToDraw.RectIter(); !iter.Done(); iter.Next()) { + const IntRect& rect = iter.Get(); + target->ClearRect(Rect(rect.x, rect.y, rect.Width(), rect.Height())); + } + } + + return true; +} + +ContentClient::BufferDecision +ContentClient::CalculateBufferForPaint(PaintedLayer* aLayer, + uint32_t aFlags) +{ + gfxContentType layerContentType = + aLayer->CanUseOpaqueSurface() ? gfxContentType::COLOR : + gfxContentType::COLOR_ALPHA; + + SurfaceMode mode; + gfxContentType contentType; + IntRect destBufferRect; + nsIntRegion neededRegion; + nsIntRegion validRegion = aLayer->GetValidRegion(); + + bool canReuseBuffer = !!mBuffer; + bool canKeepBufferContents = true; + + while (true) { + mode = aLayer->GetSurfaceMode(); + neededRegion = aLayer->GetVisibleRegion().ToUnknownRegion(); + canReuseBuffer = canReuseBuffer && BufferSizeOkFor(neededRegion.GetBounds().Size()); + contentType = layerContentType; + + if (canReuseBuffer) { + if (mBuffer->BufferRect().Contains(neededRegion.GetBounds())) { + // We don't need to adjust mBufferRect. + destBufferRect = mBuffer->BufferRect(); + } else if (neededRegion.GetBounds().Size() <= mBuffer->BufferRect().Size()) { + // The buffer's big enough but doesn't contain everything that's + // going to be visible. We'll move it. + destBufferRect = IntRect(neededRegion.GetBounds().TopLeft(), mBuffer->BufferRect().Size()); + } else { + destBufferRect = neededRegion.GetBounds(); + } + } else { + // We won't be reusing the buffer. Compute a new rect. + destBufferRect = ComputeBufferRect(neededRegion.GetBounds()); + } + + if (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) { +#if defined(MOZ_GFX_OPTIMIZE_MOBILE) + mode = SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA; +#else + if (!aLayer->GetParent() || + !aLayer->GetParent()->SupportsComponentAlphaChildren() || + !aLayer->AsShadowableLayer() || + !aLayer->AsShadowableLayer()->HasShadow()) { + mode = SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA; + } else { + contentType = gfxContentType::COLOR; + } +#endif + } + + if ((aFlags & PAINT_WILL_RESAMPLE) && + (!neededRegion.GetBounds().IsEqualInterior(destBufferRect) || + neededRegion.GetNumRects() > 1)) + { + // The area we add to neededRegion might not be painted opaquely. + if (mode == SurfaceMode::SURFACE_OPAQUE) { + contentType = gfxContentType::COLOR_ALPHA; + mode = SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA; + } + + // We need to validate the entire buffer, to make sure that only valid + // pixels are sampled. + neededRegion = destBufferRect; + } + + // If we have an existing buffer, but the content type has changed or we + // have transitioned into/out of component alpha, then we need to recreate it. + if (canReuseBuffer && + (contentType != BufferContentType() || + (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) != mBuffer->HaveBufferOnWhite())) + { + // Restart the decision process; we won't re-enter since we guard on + // being able to re-use the buffer. + canReuseBuffer = false; + canKeepBufferContents = false; + validRegion.SetEmpty(); + continue; + } + + break; + } + + NS_ASSERTION(destBufferRect.Contains(neededRegion.GetBounds()), + "Destination rect doesn't contain what we need to paint"); + + BufferDecision dest; + dest.mNeededRegion = Move(neededRegion); + dest.mValidRegion = Move(validRegion); + dest.mBufferRect = destBufferRect; + dest.mBufferMode = mode; + dest.mBufferContentType = contentType; + dest.mCanReuseBuffer = canReuseBuffer; + dest.mCanKeepBufferContents = canKeepBufferContents; + return dest; +} + +gfxContentType +ContentClient::BufferContentType() +{ + if (mBuffer) { + return ContentForFormat(mBuffer->GetFormat()); + } + return gfxContentType::SENTINEL; +} + +bool +ContentClient::BufferSizeOkFor(const IntSize& aSize) +{ + MOZ_ASSERT(mBuffer); + return (aSize == mBuffer->BufferRect().Size() || + (SizedToVisibleBounds != mBufferSizePolicy && + aSize < mBuffer->BufferRect().Size())); +} + +OpenMode +ContentClient::LockMode() const +{ + return mInAsyncPaint ? OpenMode::OPEN_READ_ASYNC_WRITE + : OpenMode::OPEN_READ_WRITE; +} + void ContentClient::BeginAsyncPaint() { @@ -113,8 +497,7 @@ ContentClient::PrintInfo(std::stringstream& aStream, const char* aPrefix) // We pass a null pointer for the ContentClient Forwarder argument, which means // this client will not have a ContentHost on the other side. ContentClientBasic::ContentClientBasic(gfx::BackendType aBackend) - : ContentClient(nullptr) - , RotatedContentBuffer(ContainsVisibleBounds) + : ContentClient(nullptr, ContainsVisibleBounds) , mBackend(aBackend) {} @@ -126,20 +509,18 @@ ContentClientBasic::DrawTo(PaintedLayer* aLayer, gfx::SourceSurface* aMask, const gfx::Matrix* aMaskTransform) { - if (!EnsureBuffer()) { + if (!mBuffer) { return; } - RotatedContentBuffer::DrawTo(aLayer, aTarget, aOpacity, aOp, - aMask, aMaskTransform); + mBuffer->DrawTo(aLayer, aTarget, aOpacity, aOp, + aMask, aMaskTransform); } -void -ContentClientBasic::CreateBuffer(ContentType aType, +RefPtr +ContentClientBasic::CreateBuffer(gfxContentType aType, const IntRect& aRect, - uint32_t aFlags, - RefPtr* aBlackDT, - RefPtr* aWhiteDT) + uint32_t aFlags) { MOZ_ASSERT(!(aFlags & BUFFER_COMPONENT_ALPHA)); if (aFlags & BUFFER_COMPONENT_ALPHA) { @@ -147,61 +528,49 @@ ContentClientBasic::CreateBuffer(ContentType aType, } IntSize size(aRect.Width(), aRect.Height()); + RefPtr drawTarget; + #ifdef XP_WIN if (mBackend == BackendType::CAIRO && (aType == gfxContentType::COLOR || aType == gfxContentType::COLOR_ALPHA)) { RefPtr surf = new gfxWindowsSurface(size, aType == gfxContentType::COLOR ? gfxImageFormat::X8R8G8B8_UINT32 : gfxImageFormat::A8R8G8B8_UINT32); - *aBlackDT = gfxPlatform::GetPlatform()->CreateDrawTargetForSurface(surf, size); - - if (*aBlackDT) { - return; - } + drawTarget = gfxPlatform::GetPlatform()->CreateDrawTargetForSurface(surf, size); } #endif - *aBlackDT = gfxPlatform::GetPlatform()->CreateDrawTargetForBackend( - mBackend, size, - gfxPlatform::GetPlatform()->Optimal2DFormatForContent(aType)); + if (!drawTarget) { + drawTarget = gfxPlatform::GetPlatform()->CreateDrawTargetForBackend( + mBackend, size, + gfxPlatform::GetPlatform()->Optimal2DFormatForContent(aType)); + } + + return new DrawTargetRotatedBuffer(drawTarget, nullptr, aRect, IntPoint(0,0)); } RefPtr -ContentClientBasic::BorrowDrawTargetForRecording(PaintState& aPaintState, - RotatedContentBuffer::DrawIterator* aIter) +ContentClientBasic::BorrowDrawTargetForRecording(ContentClient::PaintState& aPaintState, + RotatedBuffer::DrawIterator* aIter, + bool aSetTransform) { // BasicLayers does not yet support OMTP. return nullptr; } -void -ContentClientRemoteBuffer::DestroyBuffers() -{ - if (!mTextureClient) { - return; - } - - mOldTextures.AppendElement(mTextureClient); - mTextureClient = nullptr; - if (mTextureClientOnWhite) { - mOldTextures.AppendElement(mTextureClientOnWhite); - mTextureClientOnWhite = nullptr; - } - - DestroyFrontBuffer(); -} - RefPtr -ContentClientRemoteBuffer::BorrowDrawTargetForRecording(PaintState& aPaintState, - RotatedContentBuffer::DrawIterator* aIter) +ContentClientRemoteBuffer::BorrowDrawTargetForRecording(ContentClient::PaintState& aPaintState, + RotatedBuffer::DrawIterator* aIter, + bool aSetTransform) { - RefPtr cps = RotatedContentBuffer::BorrowDrawTargetForRecording(aPaintState, aIter); + RefPtr cps = ContentClient::BorrowDrawTargetForRecording(aPaintState, aIter, aSetTransform); if (!cps) { return nullptr; } - cps->mTextureClient = mTextureClient; - cps->mTextureClientOnWhite = mTextureClientOnWhite; + RemoteRotatedBuffer* remoteBuffer = GetRemoteBuffer(); + cps->mTextureClient = remoteBuffer->GetClient(); + cps->mTextureClientOnWhite = remoteBuffer->GetClientOnWhite(); return cps.forget(); } @@ -265,15 +634,6 @@ void ContentClientRemoteBuffer::BeginPaint() { EnsureBackBufferIfFrontBuffer(); - - // XXX: So we might not have a TextureClient yet.. because it will - // only be created by CreateBuffer.. which will deliver a locked surface!. - if (mTextureClient) { - SetBufferProvider(mTextureClient); - } - if (mTextureClientOnWhite) { - SetBufferProviderOnWhite(mTextureClientOnWhite); - } } void @@ -286,41 +646,31 @@ ContentClientRemoteBuffer::BeginAsyncPaint() void ContentClientRemoteBuffer::EndPaint(nsTArray* aReadbackUpdates) { - MOZ_ASSERT(!mTextureClientOnWhite || !aReadbackUpdates || aReadbackUpdates->Length() == 0); + MOZ_ASSERT(!mBuffer || !mBuffer->HaveBufferOnWhite() || + !aReadbackUpdates || aReadbackUpdates->Length() == 0); - // XXX: We might still not have a texture client if PaintThebes - // decided we didn't need one yet because the region to draw was empty. - SetBufferProvider(nullptr); - SetBufferProviderOnWhite(nullptr); - for (unsigned i = 0; i< mOldTextures.Length(); ++i) { - if (mOldTextures[i]->IsLocked()) { - mOldTextures[i]->Unlock(); - } - } - mOldTextures.Clear(); + RemoteRotatedBuffer* remoteBuffer = GetRemoteBuffer(); - if (mTextureClient && mTextureClient->IsLocked()) { + if (remoteBuffer && remoteBuffer->IsLocked()) { if (aReadbackUpdates && aReadbackUpdates->Length() > 0) { - RefPtr readbackSink = new RemoteBufferReadbackProcessor(aReadbackUpdates, mBufferRect, mBufferRotation); + RefPtr readbackSink = new RemoteBufferReadbackProcessor(aReadbackUpdates, + remoteBuffer->BufferRect(), + remoteBuffer->BufferRotation()); - mTextureClient->SetReadbackSink(readbackSink); + remoteBuffer->GetClient()->SetReadbackSink(readbackSink); } - mTextureClient->Unlock(); - mTextureClient->SyncWithObject(mForwarder->GetSyncObject()); - } - if (mTextureClientOnWhite && mTextureClientOnWhite->IsLocked()) { - mTextureClientOnWhite->Unlock(); - mTextureClientOnWhite->SyncWithObject(mForwarder->GetSyncObject()); + remoteBuffer->Unlock(); + remoteBuffer->SyncWithObject(mForwarder->GetSyncObject()); } ContentClient::EndPaint(aReadbackUpdates); } -void -ContentClientRemoteBuffer::BuildTextureClients(SurfaceFormat aFormat, - const IntRect& aRect, - uint32_t aFlags) +RefPtr +ContentClientRemoteBuffer::CreateBuffer(gfxContentType aType, + const IntRect& aRect, + uint32_t aFlags) { // If we hit this assertion, then it might be due to an empty transaction // followed by a real transaction. Our buffers should be created (but not @@ -331,86 +681,68 @@ ContentClientRemoteBuffer::BuildTextureClients(SurfaceFormat aFormat, MOZ_ASSERT(!mIsNewBuffer, "Bad! Did we create a buffer twice without painting?"); - mIsNewBuffer = true; - - DestroyBuffers(); - - mSurfaceFormat = aFormat; - mSize = IntSize(aRect.Width(), aRect.Height()); - mTextureFlags = TextureFlagsForRotatedContentBufferFlags(aFlags); + gfx::SurfaceFormat format = gfxPlatform::GetPlatform()->Optimal2DFormatForContent(aType); + TextureFlags textureFlags = TextureFlagsForContentClientFlags(aFlags); if (aFlags & BUFFER_COMPONENT_ALPHA) { - mTextureFlags |= TextureFlags::COMPONENT_ALPHA; + textureFlags |= TextureFlags::COMPONENT_ALPHA; } - CreateBackBuffer(mBufferRect); + RefPtr buffer = CreateBufferInternal(aRect, format, textureFlags); + + if (!buffer) { + return nullptr; + } + + DebugOnly locked = buffer->Lock(LockMode()); + MOZ_ASSERT(locked, "Could not lock the RemoteRotatedBuffer"); + + mIsNewBuffer = true; + mTextureFlags = textureFlags; + + return buffer; } -void -ContentClientRemoteBuffer::CreateBackBuffer(const IntRect& aBufferRect) +RefPtr +ContentClientRemoteBuffer::CreateBufferInternal(const gfx::IntRect& aRect, + gfx::SurfaceFormat aFormat, + TextureFlags aFlags) { - // gfx::BackendType::NONE means fallback to the content backend TextureAllocationFlags textureAllocFlags - = (mTextureFlags & TextureFlags::COMPONENT_ALPHA) ? + = (aFlags & TextureFlags::COMPONENT_ALPHA) ? TextureAllocationFlags::ALLOC_CLEAR_BUFFER_BLACK : TextureAllocationFlags::ALLOC_CLEAR_BUFFER; - mTextureClient = CreateTextureClientForDrawing( - mSurfaceFormat, mSize, BackendSelector::Content, - mTextureFlags | ExtraTextureFlags(), + RefPtr textureClient = CreateTextureClientForDrawing( + aFormat, aRect.Size(), BackendSelector::Content, + aFlags | ExtraTextureFlags(), textureAllocFlags ); - if (!mTextureClient || !AddTextureClient(mTextureClient)) { - AbortTextureClientCreation(); - return; - } - mTextureClient->EnableBlockingReadLock(); - if (mTextureFlags & TextureFlags::COMPONENT_ALPHA) { - mTextureClientOnWhite = mTextureClient->CreateSimilar( + if (!textureClient || !AddTextureClient(textureClient)) { + return nullptr; + } + textureClient->EnableBlockingReadLock(); + + RefPtr textureClientOnWhite; + if (aFlags & TextureFlags::COMPONENT_ALPHA) { + textureClientOnWhite = textureClient->CreateSimilar( mForwarder->GetCompositorBackendType(), - mTextureFlags | ExtraTextureFlags(), + aFlags | ExtraTextureFlags(), TextureAllocationFlags::ALLOC_CLEAR_BUFFER_WHITE ); - if (!mTextureClientOnWhite || !AddTextureClient(mTextureClientOnWhite)) { - AbortTextureClientCreation(); - return; + if (!textureClientOnWhite || !AddTextureClient(textureClientOnWhite)) { + return nullptr; } // We don't enable the readlock for the white buffer since we always // use them together and waiting on the lock for the black // should be sufficient. } -} -void -ContentClientRemoteBuffer::CreateBuffer(ContentType aType, - const IntRect& aRect, - uint32_t aFlags, - RefPtr* aBlackDT, - RefPtr* aWhiteDT) -{ - BuildTextureClients(gfxPlatform::GetPlatform()->Optimal2DFormatForContent(aType), aRect, aFlags); - if (!mTextureClient) { - return; - } - - OpenMode mode = OpenMode::OPEN_READ_WRITE; - if (mInAsyncPaint) { - mode |= OpenMode::OPEN_ASYNC_WRITE; - } - - // We just created the textures and we are about to get their draw targets - // so we have to lock them here. - DebugOnly locked = mTextureClient->Lock(mode); - MOZ_ASSERT(locked, "Could not lock the TextureClient"); - - *aBlackDT = mTextureClient->BorrowDrawTarget(); - if (aFlags & BUFFER_COMPONENT_ALPHA) { - locked = mTextureClientOnWhite->Lock(mode); - MOZ_ASSERT(locked, "Could not lock the second TextureClient for component alpha"); - - *aWhiteDT = mTextureClientOnWhite->BorrowDrawTarget(); - } + return new RemoteRotatedBuffer(textureClient, + textureClientOnWhite, + aRect, + IntPoint(0,0)); } nsIntRegion @@ -418,7 +750,7 @@ ContentClientRemoteBuffer::GetUpdatedRegion(const nsIntRegion& aRegionToDraw, const nsIntRegion& aVisibleRegion) { nsIntRegion updatedRegion; - if (mIsNewBuffer || mDidSelfCopy) { + if (mIsNewBuffer || mBuffer->DidSelfCopy()) { // A buffer reallocation clears both buffers. The front buffer has all the // content by now, but the back buffer is still clear. Here, in effect, we // are saying to copy all of the pixels of the front buffer to the back. @@ -432,9 +764,9 @@ ContentClientRemoteBuffer::GetUpdatedRegion(const nsIntRegion& aRegionToDraw, updatedRegion = aRegionToDraw; } - NS_ASSERTION(BufferRect().Contains(aRegionToDraw.GetBounds()), + MOZ_ASSERT(mBuffer, "should have a back buffer by now"); + NS_ASSERTION(mBuffer->BufferRect().Contains(aRegionToDraw.GetBounds()), "Update outside of buffer rect!"); - MOZ_ASSERT(mTextureClient, "should have a back buffer by now"); return updatedRegion; } @@ -446,72 +778,46 @@ ContentClientRemoteBuffer::Updated(const nsIntRegion& aRegionToDraw, nsIntRegion updatedRegion = GetUpdatedRegion(aRegionToDraw, aVisibleRegion); - MOZ_ASSERT(mTextureClient); - if (mTextureClientOnWhite) { - mForwarder->UseComponentAlphaTextures(this, mTextureClient, - mTextureClientOnWhite); + RemoteRotatedBuffer* remoteBuffer = GetRemoteBuffer(); + + MOZ_ASSERT(remoteBuffer && remoteBuffer->GetClient()); + if (remoteBuffer->HaveBufferOnWhite()) { + mForwarder->UseComponentAlphaTextures(this, + remoteBuffer->GetClient(), + remoteBuffer->GetClientOnWhite()); } else { AutoTArray textures; CompositableForwarder::TimedTextureClient* t = textures.AppendElement(); - t->mTextureClient = mTextureClient; - IntSize size = mTextureClient->GetSize(); + t->mTextureClient = remoteBuffer->GetClient(); + IntSize size = remoteBuffer->GetClient()->GetSize(); t->mPictureRect = nsIntRect(0, 0, size.width, size.height); GetForwarder()->UseTextures(this, textures); } + // This forces a synchronous transaction, so we can swap buffers now // and know that we'll have sole ownership of the old front buffer // by the time we paint next. mForwarder->UpdateTextureRegion(this, - ThebesBufferData(BufferRect(), - BufferRotation()), + ThebesBufferData(remoteBuffer->BufferRect(), + remoteBuffer->BufferRotation()), updatedRegion); SwapBuffers(updatedRegion); } -bool -ContentClientRemoteBuffer::LockBuffers() -{ - OpenMode mode = OpenMode::OPEN_READ_WRITE; - if (mInAsyncPaint) { - mode |= OpenMode::OPEN_ASYNC_WRITE; - } - if (mTextureClient) { - bool locked = mTextureClient->Lock(mode); - if (!locked) { - return false; - } - } - if (mTextureClientOnWhite) { - bool locked = mTextureClientOnWhite->Lock(mode); - if (!locked) { - UnlockBuffers(); - return false; - } - } - return true; -} - -void -ContentClientRemoteBuffer::UnlockBuffers() -{ - if (mTextureClient && mTextureClient->IsLocked()) { - mTextureClient->Unlock(); - } - if (mTextureClientOnWhite && mTextureClientOnWhite->IsLocked()) { - mTextureClientOnWhite->Unlock(); - } -} - void ContentClientRemoteBuffer::Dump(std::stringstream& aStream, const char* aPrefix, bool aDumpHtml, TextureDumpMode aCompress) { + RemoteRotatedBuffer* remoteBuffer = GetRemoteBuffer(); + // TODO We should combine the OnWhite/OnBlack here an just output a single image. if (!aDumpHtml) { aStream << "\n" << aPrefix << "Surface: "; } - CompositableClient::DumpTextureClient(aStream, mTextureClient, aCompress); + CompositableClient::DumpTextureClient(aStream, + remoteBuffer ? remoteBuffer->GetClient() : nullptr, + aCompress); } void @@ -523,27 +829,9 @@ ContentClientDoubleBuffered::Dump(std::stringstream& aStream, if (!aDumpHtml) { aStream << "\n" << aPrefix << "Surface: "; } - if (mFrontBuffer) { - CompositableClient::DumpTextureClient(aStream, mFrontBuffer->GetClient(), aCompress); - } -} - -void -ContentClientDoubleBuffered::DestroyFrontBuffer() -{ - if (mFrontBuffer) { - RefPtr client = mFrontBuffer->GetClient(); - RefPtr clientOnWhite = mFrontBuffer->GetClientOnWhite(); - - if (client) { - mOldTextures.AppendElement(client); - } - if (clientOnWhite) { - mOldTextures.AppendElement(clientOnWhite); - } - - mFrontBuffer = Nothing(); - } + CompositableClient::DumpTextureClient(aStream, + mFrontBuffer ? mFrontBuffer->GetClient() : nullptr, + aCompress); } void @@ -551,25 +839,13 @@ ContentClientDoubleBuffered::SwapBuffers(const nsIntRegion& aFrontUpdatedRegion) { mFrontUpdatedRegion = aFrontUpdatedRegion; - RefPtr newBack; - RefPtr newBackOnWhite; - IntRect newBackBufferRect; - nsIntPoint newBackBufferRotation; + RefPtr frontBuffer = mFrontBuffer; + RefPtr backBuffer = GetRemoteBuffer(); - if (mFrontBuffer) { - newBack = mFrontBuffer->GetClient(); - newBackOnWhite = mFrontBuffer->GetClientOnWhite(); - newBackBufferRect = mFrontBuffer->BufferRect(); - newBackBufferRotation = mFrontBuffer->BufferRotation(); - } + std::swap(frontBuffer, backBuffer); - mFrontBuffer = Some(RemoteRotatedBuffer(mTextureClient, mTextureClientOnWhite, - mBufferRect, mBufferRotation)); - - mTextureClient = newBack; - mTextureClientOnWhite = newBackOnWhite; - mBufferRect = newBackBufferRect; - mBufferRotation = newBackBufferRotation; + mFrontBuffer = frontBuffer; + mBuffer = backBuffer; mFrontAndBackBufferDiffer = true; } @@ -585,22 +861,25 @@ ContentClientDoubleBuffered::BeginPaint() return; } - if (!mFrontBuffer) { + if (!mFrontBuffer || !mBuffer) { mFrontAndBackBufferDiffer = false; return; } - if (mDidSelfCopy) { + if (mFrontBuffer->DidSelfCopy()) { // We can't easily draw our front buffer into us, since we're going to be // copying stuff around anyway it's easiest if we just move our situation // to non-rotated while we're at it. If this situation occurs we'll have // hit a self-copy path in PaintThebes before as well anyway. - mBufferRect.MoveTo(mFrontBuffer->BufferRect().TopLeft()); - mBufferRotation = nsIntPoint(); + gfx::IntRect backBufferRect = mBuffer->BufferRect(); + backBufferRect.MoveTo(mFrontBuffer->BufferRect().TopLeft()); + + mBuffer->SetBufferRect(backBufferRect); + mBuffer->SetBufferRotation(IntPoint(0,0)); return; } - mBufferRect = mFrontBuffer->BufferRect(); - mBufferRotation = mFrontBuffer->BufferRotation(); + mBuffer->SetBufferRect(mFrontBuffer->BufferRect()); + mBuffer->SetBufferRotation(mFrontBuffer->BufferRotation()); } void @@ -618,7 +897,7 @@ void ContentClientDoubleBuffered::FinalizeFrame(const nsIntRegion& aRegionToDraw) { if (!mFrontAndBackBufferDiffer) { - MOZ_ASSERT(!mDidSelfCopy, "If we have to copy the world, then our buffers are different, right?"); + MOZ_ASSERT(!mFrontBuffer->DidSelfCopy(), "If we have to copy the world, then our buffers are different, right?"); return; } MOZ_ASSERT(mFrontBuffer); @@ -636,9 +915,9 @@ ContentClientDoubleBuffered::FinalizeFrame(const nsIntRegion& aRegionToDraw) mFrontAndBackBufferDiffer = false; nsIntRegion updateRegion = mFrontUpdatedRegion; - if (mDidSelfCopy) { - mDidSelfCopy = false; - updateRegion = mBufferRect; + if (mFrontBuffer->DidSelfCopy()) { + mFrontBuffer->ClearDidSelfCopy(); + updateRegion = mBuffer->BufferRect(); } // No point in sync'ing what we are going to draw over anyway. And if there is @@ -648,13 +927,12 @@ ContentClientDoubleBuffered::FinalizeFrame(const nsIntRegion& aRegionToDraw) return; } - if (!EnsureBuffer() || - (HaveBufferOnWhite() && !EnsureBufferOnWhite())) { + if (!mBuffer) { return; } if (mFrontBuffer->Lock(OpenMode::OPEN_READ_ONLY)) { - UpdateDestinationFrom(*mFrontBuffer, updateRegion); + mBuffer->UpdateDestinationFrom(*mFrontBuffer, updateRegion); mFrontBuffer->Unlock(); } } @@ -662,11 +940,11 @@ ContentClientDoubleBuffered::FinalizeFrame(const nsIntRegion& aRegionToDraw) void ContentClientDoubleBuffered::EnsureBackBufferIfFrontBuffer() { - if (!mTextureClient && mFrontBuffer) { - CreateBackBuffer(mFrontBuffer->BufferRect()); - - mBufferRect = mFrontBuffer->BufferRect(); - mBufferRotation = mFrontBuffer->BufferRotation(); + if (!mBuffer && mFrontBuffer) { + mBuffer = CreateBufferInternal(mFrontBuffer->BufferRect(), + mFrontBuffer->GetFormat(), + mTextureFlags); + MOZ_ASSERT(mBuffer); } } diff --git a/gfx/layers/client/ContentClient.h b/gfx/layers/client/ContentClient.h index 1db6fd02ff8b..8a981db8b952 100644 --- a/gfx/layers/client/ContentClient.h +++ b/gfx/layers/client/ContentClient.h @@ -76,6 +76,8 @@ class CapturedPaintState; class ContentClient : public CompositableClient { public: + typedef gfxContentType ContentType; + /** * Creates, configures, and returns a new content client. If necessary, a * message will be sent to the compositor to create a corresponding content @@ -83,67 +85,185 @@ public: */ static already_AddRefed CreateContentClient(CompositableForwarder* aFwd); - explicit ContentClient(CompositableForwarder* aForwarder) - : CompositableClient(aForwarder), - mInAsyncPaint(false) + /** + * Controls the size of the backing buffer of this. + * - SizedToVisibleBounds: the backing buffer is exactly the same + * size as the bounds of PaintedLayer's visible region + * - ContainsVisibleBounds: the backing buffer is large enough to + * fit visible bounds. May be larger. + */ + enum BufferSizePolicy { + SizedToVisibleBounds, + ContainsVisibleBounds + }; + + explicit ContentClient(CompositableForwarder* aForwarder, + BufferSizePolicy aBufferSizePolicy) + : CompositableClient(aForwarder) + , mBufferSizePolicy(aBufferSizePolicy) + , mInAsyncPaint(false) {} virtual ~ContentClient() {} virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix); - virtual void Clear() = 0; - virtual RotatedContentBuffer::PaintState BeginPaintBuffer(PaintedLayer* aLayer, - uint32_t aFlags) = 0; - virtual gfx::DrawTarget* BorrowDrawTargetForPainting(RotatedContentBuffer::PaintState& aPaintState, - RotatedContentBuffer::DrawIterator* aIter = nullptr) = 0; - virtual void ReturnDrawTargetToBuffer(gfx::DrawTarget*& aReturned) = 0; + virtual void Clear(); + + /** + * This is returned by BeginPaint. The caller should draw into mTarget. + * mRegionToDraw must be drawn. mRegionToInvalidate has been invalidated + * by RotatedContentBuffer and must be redrawn on the screen. + * mRegionToInvalidate is set when the buffer has changed from + * opaque to transparent or vice versa, since the details of rendering can + * depend on the buffer type. + */ + struct PaintState { + PaintState() + : mRegionToDraw() + , mRegionToInvalidate() + , mMode(SurfaceMode::SURFACE_NONE) + , mClip(DrawRegionClip::NONE) + , mContentType(gfxContentType::SENTINEL) + {} + + nsIntRegion mRegionToDraw; + nsIntRegion mRegionToInvalidate; + SurfaceMode mMode; + DrawRegionClip mClip; + gfxContentType mContentType; + }; + + enum { + PAINT_WILL_RESAMPLE = 0x01, + PAINT_NO_ROTATION = 0x02, + PAINT_CAN_DRAW_ROTATED = 0x04 + }; + + /** + * Start a drawing operation. This returns a PaintState describing what + * needs to be drawn to bring the buffer up to date in the visible region. + * This queries aLayer to get the currently valid and visible regions. + * The returned mTarget may be null if mRegionToDraw is empty. + * Otherwise it must not be null. + * mRegionToInvalidate will contain mRegionToDraw. + * @param aFlags when PAINT_WILL_RESAMPLE is passed, this indicates that + * buffer will be resampled when rendering (i.e the effective transform + * combined with the scale for the resolution is not just an integer + * translation). This will disable buffer rotation (since we don't want + * to resample across the rotation boundary) and will ensure that we + * make the entire buffer contents valid (since we don't want to sample + * invalid pixels outside the visible region, if the visible region doesn't + * fill the buffer bounds). + * PAINT_CAN_DRAW_ROTATED can be passed if the caller supports drawing + * rotated content that crosses the physical buffer boundary. The caller + * will need to call BorrowDrawTargetForPainting multiple times to achieve + * this. + */ + PaintState BeginPaintBuffer(PaintedLayer* aLayer, uint32_t aFlags); + + /** + * Fetch a DrawTarget for rendering. The DrawTarget remains owned by + * this. See notes on BorrowDrawTargetForQuadrantUpdate. + * May return null. If the return value is non-null, it must be + * 'un-borrowed' using ReturnDrawTarget. + * + * If PAINT_CAN_DRAW_ROTATED was specified for BeginPaint, then the caller + * must call this function repeatedly (with an iterator) until it returns + * nullptr. The caller should draw the mDrawRegion of the iterator instead + * of mRegionToDraw in the PaintState. + * + * @param aPaintState Paint state data returned by a call to BeginPaint + * @param aIter Paint state iterator. Only required if PAINT_CAN_DRAW_ROTATED + * was specified to BeginPaint. + */ + virtual gfx::DrawTarget* BorrowDrawTargetForPainting( + PaintState& aPaintState, + RotatedBuffer::DrawIterator* aIter = nullptr); + + /** + * Borrow a draw target for recording. The required transform for correct painting + * is not applied to the returned DrawTarget by default, BUT it is + * required to be whenever drawing does happen. + */ virtual RefPtr BorrowDrawTargetForRecording( - RotatedContentBuffer::PaintState& aPaintState, - RotatedContentBuffer::DrawIterator* aIter) = 0; + PaintState& aPaintState, + RotatedBuffer::DrawIterator* aIter, + bool aSetTransform = false); + + virtual void ReturnDrawTargetToBuffer(gfx::DrawTarget*& aReturned); // Called as part of the layers transation reply. Conveys data about our // buffer(s) from the compositor. If appropriate we should swap references // to our buffers. virtual void SwapBuffers(const nsIntRegion& aFrontUpdatedRegion) {} - // call before and after painting into this content client + // Call before and after painting into this content client virtual void BeginPaint() {} virtual void BeginAsyncPaint(); virtual void EndPaint(nsTArray* aReadbackUpdates = nullptr); + nsIntRegion ExpandDrawRegion(PaintState& aPaintState, + RotatedBuffer::DrawIterator* aIter, + gfx::BackendType aBackendType); + + static bool PrepareDrawTargetForPainting(CapturedPaintState*); + + enum { + BUFFER_COMPONENT_ALPHA = 0x02 // Dual buffers should be created for drawing with + // component alpha. + }; + protected: + struct BufferDecision { + nsIntRegion mNeededRegion; + nsIntRegion mValidRegion; + gfx::IntRect mBufferRect; + SurfaceMode mBufferMode; + gfxContentType mBufferContentType; + bool mCanReuseBuffer; + bool mCanKeepBufferContents; + }; + + BufferDecision CalculateBufferForPaint(PaintedLayer* aLayer, + uint32_t aFlags); + + /** + * Return the buffer's content type. Requires a valid buffer or + * buffer provider. + */ + gfxContentType BufferContentType(); + bool BufferSizeOkFor(const gfx::IntSize& aSize); + + OpenMode LockMode() const; + + /** + * Any actions that should be performed at the last moment before we begin + * rendering the next frame. I.e., after we calculate what we will draw, + * but before we rotate the buffer and possibly create new buffers. + * aRegionToDraw is the region which is guaranteed to be overwritten when + * drawing the next frame. + */ + virtual void FinalizeFrame(const nsIntRegion& aRegionToDraw) {} + + virtual RefPtr CreateBuffer(gfxContentType aType, + const gfx::IntRect& aRect, + uint32_t aFlags) = 0; + + RefPtr mBuffer; + BufferSizePolicy mBufferSizePolicy; bool mInAsyncPaint; }; -// thin wrapper around RotatedContentBuffer, for on-mtc +// Thin wrapper around DrawTargetRotatedBuffer, for on-mtc class ContentClientBasic final : public ContentClient - , protected RotatedContentBuffer { public: explicit ContentClientBasic(gfx::BackendType aBackend); - typedef RotatedContentBuffer::PaintState PaintState; - typedef RotatedContentBuffer::ContentType ContentType; - - virtual void Clear() override { RotatedContentBuffer::Clear(); } - virtual PaintState BeginPaintBuffer(PaintedLayer* aLayer, - uint32_t aFlags) override - { - return RotatedContentBuffer::BeginPaint(aLayer, aFlags); - } - virtual gfx::DrawTarget* BorrowDrawTargetForPainting(PaintState& aPaintState, - RotatedContentBuffer::DrawIterator* aIter = nullptr) override - { - return RotatedContentBuffer::BorrowDrawTargetForPainting(aPaintState, aIter); - } virtual RefPtr BorrowDrawTargetForRecording(PaintState& aPaintState, - RotatedContentBuffer::DrawIterator* aIter) override; - - virtual void ReturnDrawTargetToBuffer(gfx::DrawTarget*& aReturned) override - { - BorrowDrawTarget::ReturnDrawTarget(aReturned); - } + RotatedBuffer::DrawIterator* aIter, + bool aSetTransform) override; void DrawTo(PaintedLayer* aLayer, gfx::DrawTarget* aTarget, @@ -152,14 +272,16 @@ public: gfx::SourceSurface* aMask, const gfx::Matrix* aMaskTransform); - virtual void CreateBuffer(ContentType aType, const gfx::IntRect& aRect, uint32_t aFlags, - RefPtr* aBlackDT, RefPtr* aWhiteDT) override; - virtual TextureInfo GetTextureInfo() const override { MOZ_CRASH("GFX: Should not be called on non-remote ContentClient"); } +protected: + virtual RefPtr CreateBuffer(gfxContentType aType, + const gfx::IntRect& aRect, + uint32_t aFlags) override; + private: gfx::BackendType mBackend; }; @@ -181,50 +303,21 @@ private: */ // Version using new texture clients class ContentClientRemoteBuffer : public ContentClient - , protected RotatedContentBuffer { - using RotatedContentBuffer::BufferRect; - using RotatedContentBuffer::BufferRotation; public: explicit ContentClientRemoteBuffer(CompositableForwarder* aForwarder) - : ContentClient(aForwarder) - , RotatedContentBuffer(ContainsVisibleBounds) + : ContentClient(aForwarder, ContainsVisibleBounds) , mIsNewBuffer(false) - , mSurfaceFormat(gfx::SurfaceFormat::B8G8R8A8) {} - typedef RotatedContentBuffer::PaintState PaintState; - typedef RotatedContentBuffer::ContentType ContentType; - - virtual void Clear() override - { - RotatedContentBuffer::Clear(); - mTextureClient = nullptr; - mTextureClientOnWhite = nullptr; - } - virtual void Dump(std::stringstream& aStream, const char* aPrefix="", bool aDumpHtml=false, TextureDumpMode aCompress=TextureDumpMode::Compress) override; - virtual PaintState BeginPaintBuffer(PaintedLayer* aLayer, - uint32_t aFlags) override - { - return RotatedContentBuffer::BeginPaint(aLayer, aFlags); - } - virtual gfx::DrawTarget* BorrowDrawTargetForPainting(PaintState& aPaintState, - RotatedContentBuffer::DrawIterator* aIter = nullptr) override - { - return RotatedContentBuffer::BorrowDrawTargetForPainting(aPaintState, aIter); - } virtual RefPtr BorrowDrawTargetForRecording(PaintState& aPaintState, - RotatedContentBuffer::DrawIterator* aIter) override; - - virtual void ReturnDrawTargetToBuffer(gfx::DrawTarget*& aReturned) override - { - BorrowDrawTarget::ReturnDrawTarget(aReturned); - } + RotatedBuffer::DrawIterator* aIter, + bool aSetTransform) override; /** * Begin/End Paint map a gfxASurface from the texture client @@ -241,64 +334,33 @@ public: virtual void Updated(const nsIntRegion& aRegionToDraw, const nsIntRegion& aVisibleRegion); - // Expose these protected methods from the superclass. - virtual const gfx::IntRect& BufferRect() const - { - return RotatedContentBuffer::BufferRect(); - } - virtual const nsIntPoint& BufferRotation() const - { - return RotatedContentBuffer::BufferRotation(); - } - - virtual void CreateBuffer(ContentType aType, const gfx::IntRect& aRect, uint32_t aFlags, - RefPtr* aBlackDT, RefPtr* aWhiteDT) override; - virtual TextureFlags ExtraTextureFlags() const { return TextureFlags::IMMEDIATE_UPLOAD; } protected: - void DestroyBuffers(); - virtual nsIntRegion GetUpdatedRegion(const nsIntRegion& aRegionToDraw, const nsIntRegion& aVisibleRegion); - void BuildTextureClients(gfx::SurfaceFormat aFormat, - const gfx::IntRect& aRect, - uint32_t aFlags); - - void CreateBackBuffer(const gfx::IntRect& aBufferRect); - // Ensure we have a valid back buffer if we have a valid front buffer (i.e. // if a backbuffer has been created.) virtual void EnsureBackBufferIfFrontBuffer() {} - // Create the front buffer for the ContentClient/Host pair if necessary - // and notify the compositor that we have created the buffer(s). - virtual void DestroyFrontBuffer() {} + virtual RefPtr CreateBuffer(gfxContentType aType, + const gfx::IntRect& aRect, + uint32_t aFlags) override; - virtual void AbortTextureClientCreation() + RefPtr CreateBufferInternal(const gfx::IntRect& aRect, + gfx::SurfaceFormat aFormat, + TextureFlags aFlags); + + RemoteRotatedBuffer* GetRemoteBuffer() const { - mTextureClient = nullptr; - mTextureClientOnWhite = nullptr; - mIsNewBuffer = false; + return static_cast(mBuffer.get()); } - virtual bool LockBuffers() override; - virtual void UnlockBuffers() override; - - RefPtr mTextureClient; - RefPtr mTextureClientOnWhite; - // keep a record of texture clients we have created and need to keep around - // (for RotatedBuffer to access), then unlock and remove them when we are done - // painting. - nsTArray > mOldTextures; - bool mIsNewBuffer; - gfx::IntSize mSize; - gfx::SurfaceFormat mSurfaceFormat; }; /** @@ -322,10 +384,15 @@ public: virtual ~ContentClientDoubleBuffered() {} + virtual void Dump(std::stringstream& aStream, + const char* aPrefix="", + bool aDumpHtml=false, + TextureDumpMode aCompress=TextureDumpMode::Compress) override; + virtual void Clear() override { - ContentClientRemoteBuffer::Clear(); - mFrontBuffer = Nothing(); + ContentClient::Clear(); + mFrontBuffer = nullptr; } virtual void SwapBuffers(const nsIntRegion& aFrontUpdatedRegion) override; @@ -342,23 +409,8 @@ public: return TextureInfo(CompositableType::CONTENT_DOUBLE, mTextureFlags); } - virtual void Dump(std::stringstream& aStream, - const char* aPrefix="", - bool aDumpHtml=false, - TextureDumpMode aCompress=TextureDumpMode::Compress) override; -protected: - virtual void DestroyFrontBuffer() override; - private: - - virtual void AbortTextureClientCreation() override - { - mTextureClient = nullptr; - mTextureClientOnWhite = nullptr; - mFrontBuffer = Nothing(); - } - - Maybe mFrontBuffer; + RefPtr mFrontBuffer; nsIntRegion mFrontUpdatedRegion; bool mFrontAndBackBufferDiffer; }; diff --git a/gfx/layers/composite/ContentHost.h b/gfx/layers/composite/ContentHost.h index b7d3ce141c7e..303053434a4b 100644 --- a/gfx/layers/composite/ContentHost.h +++ b/gfx/layers/composite/ContentHost.h @@ -20,6 +20,7 @@ #include "mozilla/gfx/Rect.h" // for Rect #include "mozilla/gfx/Types.h" // for SamplingFilter #include "mozilla/layers/CompositorTypes.h" // for TextureInfo, etc +#include "mozilla/layers/ContentClient.h" // for ContentClient #include "mozilla/layers/ISurfaceAllocator.h" // for ISurfaceAllocator #include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor #include "mozilla/layers/LayersTypes.h" // for etc @@ -91,8 +92,8 @@ protected: class ContentHostBase : public ContentHost { public: - typedef RotatedContentBuffer::ContentType ContentType; - typedef RotatedContentBuffer::PaintState PaintState; + typedef ContentClient::ContentType ContentType; + typedef ContentClient::PaintState PaintState; explicit ContentHostBase(const TextureInfo& aTextureInfo); virtual ~ContentHostBase();