diff -r 000000000000 -r 6474c204b198 gfx/layers/client/ContentClient.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gfx/layers/client/ContentClient.cpp Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,890 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/layers/ContentClient.h" +#include "BasicLayers.h" // for BasicLayerManager +#include "gfxColor.h" // for gfxRGBA +#include "gfxContext.h" // for gfxContext, etc +#include "gfxPlatform.h" // for gfxPlatform +#include "gfxPrefs.h" // for gfxPrefs +#include "gfxPoint.h" // for gfxIntSize, gfxPoint +#include "gfxTeeSurface.h" // for gfxTeeSurface +#include "gfxUtils.h" // for gfxUtils +#include "ipc/ShadowLayers.h" // for ShadowLayerForwarder +#include "mozilla/ArrayUtils.h" // for ArrayLength +#include "mozilla/gfx/2D.h" // for DrawTarget, Factory +#include "mozilla/gfx/BasePoint.h" // for BasePoint +#include "mozilla/gfx/BaseSize.h" // for BaseSize +#include "mozilla/gfx/Rect.h" // for Rect +#include "mozilla/gfx/Types.h" +#include "mozilla/layers/LayerManagerComposite.h" +#include "mozilla/layers/LayersMessages.h" // for ThebesBufferData +#include "mozilla/layers/LayersTypes.h" +#include "nsAutoPtr.h" // for nsRefPtr +#include "nsDebug.h" // for NS_ASSERTION, NS_WARNING, etc +#include "nsISupportsImpl.h" // for gfxContext::Release, etc +#include "nsIWidget.h" // for nsIWidget +#include "prenv.h" // for PR_GetEnv +#ifdef XP_WIN +#include "gfxWindowsPlatform.h" +#endif +#include "gfx2DGlue.h" + +namespace mozilla { + +using namespace gfx; + +namespace layers { + +static TextureFlags TextureFlagsForRotatedContentBufferFlags(uint32_t aBufferFlags) +{ + TextureFlags result = 0; + + if (aBufferFlags & RotatedContentBuffer::BUFFER_COMPONENT_ALPHA) { + result |= TEXTURE_COMPONENT_ALPHA; + } + + if (aBufferFlags & RotatedContentBuffer::ALLOW_REPEAT) { + result |= TEXTURE_ALLOW_REPEAT; + } + + return result; +} + + +/* static */ TemporaryRef +ContentClient::CreateContentClient(CompositableForwarder* aForwarder) +{ + LayersBackend backend = aForwarder->GetCompositorBackendType(); + if (backend != LayersBackend::LAYERS_OPENGL && + backend != LayersBackend::LAYERS_D3D9 && + backend != LayersBackend::LAYERS_D3D11 && + backend != LayersBackend::LAYERS_BASIC) { + return nullptr; + } + + bool useDoubleBuffering = false; + +#ifdef XP_WIN + if (backend == LayersBackend::LAYERS_D3D11) { + useDoubleBuffering = !!gfxWindowsPlatform::GetPlatform()->GetD2DDevice(); + } else +#endif + { + useDoubleBuffering = (LayerManagerComposite::SupportsDirectTexturing() && + backend != LayersBackend::LAYERS_D3D9) || + backend == LayersBackend::LAYERS_BASIC; + } + + if (useDoubleBuffering || PR_GetEnv("MOZ_FORCE_DOUBLE_BUFFERING")) { + return new ContentClientDoubleBuffered(aForwarder); + } +#ifdef XP_MACOSX + if (backend == LayersBackend::LAYERS_OPENGL) { + return new ContentClientIncremental(aForwarder); + } +#endif + return new ContentClientSingleBuffered(aForwarder); +} + +void +ContentClient::EndPaint() +{ + // It is very important that this is called after any overridden EndPaint behaviour, + // because destroying textures is a three stage process: + // 1. We are done with the buffer and move it to ContentClient::mOldTextures, + // that happens in DestroyBuffers which is may be called indirectly from + // PaintThebes. + // 2. The content client calls RemoveTextureClient on the texture clients in + // mOldTextures and forgets them. They then become invalid. The compositable + // client keeps a record of IDs. This happens in EndPaint. + // 3. An IPC message is sent to destroy the corresponding texture host. That + // happens from OnTransaction. + // It is important that these steps happen in order. + OnTransaction(); +} + +// 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() + : ContentClient(nullptr) + , RotatedContentBuffer(ContainsVisibleBounds) +{} + +void +ContentClientBasic::CreateBuffer(ContentType aType, + const nsIntRect& aRect, + uint32_t aFlags, + RefPtr* aBlackDT, + RefPtr* aWhiteDT) +{ + MOZ_ASSERT(!(aFlags & BUFFER_COMPONENT_ALPHA)); + gfxImageFormat format = + gfxPlatform::GetPlatform()->OptimalFormatForContent(aType); + + *aBlackDT = gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget( + IntSize(aRect.width, aRect.height), + ImageFormatToSurfaceFormat(format)); +} + +void +ContentClientRemoteBuffer::DestroyBuffers() +{ + if (!mTextureClient) { + return; + } + + mOldTextures.AppendElement(mTextureClient); + mTextureClient = nullptr; + if (mTextureClientOnWhite) { + mOldTextures.AppendElement(mTextureClientOnWhite); + mTextureClientOnWhite = nullptr; + } + + DestroyFrontBuffer(); +} + +void +ContentClientRemoteBuffer::BeginPaint() +{ + // 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 +ContentClientRemoteBuffer::EndPaint() +{ + // 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(); + + if (mTextureClient && mTextureClient->IsLocked()) { + mTextureClient->Unlock(); + } + if (mTextureClientOnWhite && mTextureClientOnWhite->IsLocked()) { + mTextureClientOnWhite->Unlock(); + } + ContentClientRemote::EndPaint(); +} + +bool +ContentClientRemoteBuffer::CreateAndAllocateTextureClient(RefPtr& aClient, + TextureFlags aFlags) +{ + // gfx::BackendType::NONE means fallback to the content backend + aClient = CreateTextureClientForDrawing(mSurfaceFormat, + mTextureInfo.mTextureFlags | aFlags, + gfx::BackendType::NONE, + mSize); + if (!aClient) { + return false; + } + + if (!aClient->AllocateForSurface(mSize, ALLOC_CLEAR_BUFFER)) { + aClient = CreateTextureClientForDrawing(mSurfaceFormat, + mTextureInfo.mTextureFlags | TEXTURE_ALLOC_FALLBACK | aFlags, + gfx::BackendType::NONE, + mSize); + if (!aClient) { + return false; + } + if (!aClient->AllocateForSurface(mSize, ALLOC_CLEAR_BUFFER)) { + NS_WARNING("Could not allocate texture client"); + aClient = nullptr; + return false; + } + } + + NS_WARN_IF_FALSE(aClient->IsValid(), "Created an invalid texture client"); + return true; +} + +void +ContentClientRemoteBuffer::BuildTextureClients(SurfaceFormat aFormat, + const nsIntRect& 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 + // painted in the empty transaction) and then painted (but not created) in the + // real transaction. That is kind of fragile, and this assert will catch + // circumstances where we screw that up, e.g., by unnecessarily recreating our + // buffers. + NS_ABORT_IF_FALSE(!mIsNewBuffer, + "Bad! Did we create a buffer twice without painting?"); + + mIsNewBuffer = true; + + DestroyBuffers(); + + mSurfaceFormat = aFormat; + mSize = gfx::IntSize(aRect.width, aRect.height); + mTextureInfo.mTextureFlags = TextureFlagsForRotatedContentBufferFlags(aFlags); + + if (!CreateAndAllocateTextureClient(mTextureClient, TEXTURE_ON_BLACK) || + !AddTextureClient(mTextureClient)) { + AbortTextureClientCreation(); + return; + } + + if (aFlags & BUFFER_COMPONENT_ALPHA) { + if (!CreateAndAllocateTextureClient(mTextureClientOnWhite, TEXTURE_ON_WHITE) || + !AddTextureClient(mTextureClientOnWhite)) { + AbortTextureClientCreation(); + return; + } + mTextureInfo.mTextureFlags |= TEXTURE_COMPONENT_ALPHA; + } + + CreateFrontBuffer(aRect); +} + +void +ContentClientRemoteBuffer::CreateBuffer(ContentType aType, + const nsIntRect& aRect, + uint32_t aFlags, + RefPtr* aBlackDT, + RefPtr* aWhiteDT) +{ + BuildTextureClients(gfxPlatform::GetPlatform()->Optimal2DFormatForContent(aType), aRect, aFlags); + if (!mTextureClient) { + return; + } + + // 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(OPEN_READ_WRITE); + MOZ_ASSERT(locked, "Could not lock the TextureClient"); + + *aBlackDT = mTextureClient->GetAsDrawTarget(); + if (aFlags & BUFFER_COMPONENT_ALPHA) { + locked = mTextureClientOnWhite->Lock(OPEN_READ_WRITE); + MOZ_ASSERT(locked, "Could not lock the second TextureClient for component alpha"); + + *aWhiteDT = mTextureClientOnWhite->GetAsDrawTarget(); + } +} + +nsIntRegion +ContentClientRemoteBuffer::GetUpdatedRegion(const nsIntRegion& aRegionToDraw, + const nsIntRegion& aVisibleRegion, + bool aDidSelfCopy) +{ + nsIntRegion updatedRegion; + if (mIsNewBuffer || aDidSelfCopy) { + // 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. + // Also when we self-copied in the buffer, the buffer space + // changes and some changed buffer content isn't reflected in the + // draw or invalidate region (on purpose!). When this happens, we + // need to read back the entire buffer too. + updatedRegion = aVisibleRegion; + mIsNewBuffer = false; + } else { + updatedRegion = aRegionToDraw; + } + + NS_ASSERTION(BufferRect().Contains(aRegionToDraw.GetBounds()), + "Update outside of buffer rect!"); + NS_ABORT_IF_FALSE(mTextureClient, "should have a back buffer by now"); + + return updatedRegion; +} + +void +ContentClientRemoteBuffer::Updated(const nsIntRegion& aRegionToDraw, + const nsIntRegion& aVisibleRegion, + bool aDidSelfCopy) +{ + nsIntRegion updatedRegion = GetUpdatedRegion(aRegionToDraw, + aVisibleRegion, + aDidSelfCopy); + + MOZ_ASSERT(mTextureClient); + if (mTextureClientOnWhite) { + mForwarder->UseComponentAlphaTextures(this, mTextureClient, + mTextureClientOnWhite); + } else { + mForwarder->UseTexture(this, mTextureClient); + } + mForwarder->UpdateTextureRegion(this, + ThebesBufferData(BufferRect(), + BufferRotation()), + updatedRegion); +} + +void +ContentClientRemoteBuffer::SwapBuffers(const nsIntRegion& aFrontUpdatedRegion) +{ + MOZ_ASSERT(mTextureClient); + mFrontAndBackBufferDiffer = true; +} + +void +ContentClientDoubleBuffered::CreateFrontBuffer(const nsIntRect& aBufferRect) +{ + if (!CreateAndAllocateTextureClient(mFrontClient, TEXTURE_ON_BLACK) || + !AddTextureClient(mFrontClient)) { + AbortTextureClientCreation(); + return; + } + if (mTextureInfo.mTextureFlags & TEXTURE_COMPONENT_ALPHA) { + if (!CreateAndAllocateTextureClient(mFrontClientOnWhite, TEXTURE_ON_WHITE) || + !AddTextureClient(mFrontClientOnWhite)) { + AbortTextureClientCreation(); + return; + } + } + + mFrontBufferRect = aBufferRect; + mFrontBufferRotation = nsIntPoint(); +} + +void +ContentClientDoubleBuffered::DestroyFrontBuffer() +{ + MOZ_ASSERT(mFrontClient); + + mOldTextures.AppendElement(mFrontClient); + mFrontClient = nullptr; + if (mFrontClientOnWhite) { + mOldTextures.AppendElement(mFrontClientOnWhite); + mFrontClientOnWhite = nullptr; + } +} + +void +ContentClientDoubleBuffered::SwapBuffers(const nsIntRegion& aFrontUpdatedRegion) +{ + mFrontUpdatedRegion = aFrontUpdatedRegion; + + RefPtr oldBack = mTextureClient; + mTextureClient = mFrontClient; + mFrontClient = oldBack; + + oldBack = mTextureClientOnWhite; + mTextureClientOnWhite = mFrontClientOnWhite; + mFrontClientOnWhite = oldBack; + + nsIntRect oldBufferRect = mBufferRect; + mBufferRect = mFrontBufferRect; + mFrontBufferRect = oldBufferRect; + + nsIntPoint oldBufferRotation = mBufferRotation; + mBufferRotation = mFrontBufferRotation; + mFrontBufferRotation = oldBufferRotation; + + MOZ_ASSERT(mFrontClient); + + ContentClientRemoteBuffer::SwapBuffers(aFrontUpdatedRegion); +} + +void +ContentClientDoubleBuffered::BeginPaint() +{ + ContentClientRemoteBuffer::BeginPaint(); + + mIsNewBuffer = false; + + if (!mFrontAndBackBufferDiffer) { + return; + } + + if (mDidSelfCopy) { + // 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(mFrontBufferRect.TopLeft()); + mBufferRotation = nsIntPoint(); + return; + } + mBufferRect = mFrontBufferRect; + mBufferRotation = mFrontBufferRotation; +} + +// Sync front/back buffers content +// After executing, the new back buffer has the same (interesting) pixels as +// the new front buffer, and mValidRegion et al. are correct wrt the new +// back buffer (i.e. as they were for the old back buffer) +void +ContentClientDoubleBuffered::FinalizeFrame(const nsIntRegion& aRegionToDraw) +{ + if (mTextureClient) { + DebugOnly locked = mTextureClient->Lock(OPEN_READ_WRITE); + MOZ_ASSERT(locked); + } + if (mTextureClientOnWhite) { + DebugOnly locked = mTextureClientOnWhite->Lock(OPEN_READ_WRITE); + MOZ_ASSERT(locked); + } + + if (!mFrontAndBackBufferDiffer) { + MOZ_ASSERT(!mDidSelfCopy, "If we have to copy the world, then our buffers are different, right?"); + return; + } + MOZ_ASSERT(mFrontClient); + + MOZ_LAYERS_LOG(("BasicShadowableThebes(%p): reading back ", + this, + mFrontUpdatedRegion.GetBounds().x, + mFrontUpdatedRegion.GetBounds().y, + mFrontUpdatedRegion.GetBounds().width, + mFrontUpdatedRegion.GetBounds().height)); + + mFrontAndBackBufferDiffer = false; + + nsIntRegion updateRegion = mFrontUpdatedRegion; + if (mDidSelfCopy) { + mDidSelfCopy = false; + updateRegion = mBufferRect; + } + + // No point in sync'ing what we are going to draw over anyway. And if there is + // nothing to sync at all, there is nothing to do and we can go home early. + updateRegion.Sub(updateRegion, aRegionToDraw); + if (updateRegion.IsEmpty()) { + return; + } + + // We need to ensure that we lock these two buffers in the same + // order as the compositor to prevent deadlocks. + if (!mFrontClient->Lock(OPEN_READ_ONLY)) { + return; + } + if (mFrontClientOnWhite && + !mFrontClientOnWhite->Lock(OPEN_READ_ONLY)) { + mFrontClient->Unlock(); + return; + } + { + // Restrict the DrawTargets and frontBuffer to a scope to make + // sure there is no more external references to the DrawTargets + // when we Unlock the TextureClients. + RefPtr dt = mFrontClient->GetAsDrawTarget(); + RefPtr dtOnWhite = mFrontClientOnWhite + ? mFrontClientOnWhite->GetAsDrawTarget() + : nullptr; + RotatedBuffer frontBuffer(dt, + dtOnWhite, + mFrontBufferRect, + mFrontBufferRotation); + UpdateDestinationFrom(frontBuffer, updateRegion); + } + + mFrontClient->Unlock(); + if (mFrontClientOnWhite) { + mFrontClientOnWhite->Unlock(); + } +} + +void +ContentClientDoubleBuffered::UpdateDestinationFrom(const RotatedBuffer& aSource, + const nsIntRegion& aUpdateRegion) +{ + DrawIterator iter; + while (DrawTarget* destDT = + BorrowDrawTargetForQuadrantUpdate(aUpdateRegion.GetBounds(), BUFFER_BLACK, &iter)) { + bool isClippingCheap = IsClippingCheap(destDT, iter.mDrawRegion); + if (isClippingCheap) { + gfxUtils::ClipToRegion(destDT, iter.mDrawRegion); + } + + aSource.DrawBufferWithRotation(destDT, BUFFER_BLACK, 1.0, CompositionOp::OP_SOURCE); + if (isClippingCheap) { + destDT->PopClip(); + } + // Flush the destination before the sources become inaccessible (Unlock). + destDT->Flush(); + ReturnDrawTargetToBuffer(destDT); + } + + if (aSource.HaveBufferOnWhite()) { + MOZ_ASSERT(HaveBufferOnWhite()); + DrawIterator whiteIter; + while (DrawTarget* destDT = + BorrowDrawTargetForQuadrantUpdate(aUpdateRegion.GetBounds(), BUFFER_WHITE, &whiteIter)) { + bool isClippingCheap = IsClippingCheap(destDT, whiteIter.mDrawRegion); + if (isClippingCheap) { + gfxUtils::ClipToRegion(destDT, whiteIter.mDrawRegion); + } + + aSource.DrawBufferWithRotation(destDT, BUFFER_WHITE, 1.0, CompositionOp::OP_SOURCE); + if (isClippingCheap) { + destDT->PopClip(); + } + // Flush the destination before the sources become inaccessible (Unlock). + destDT->Flush(); + ReturnDrawTargetToBuffer(destDT); + } + } +} + +void +ContentClientSingleBuffered::FinalizeFrame(const nsIntRegion& aRegionToDraw) +{ + if (mTextureClient) { + DebugOnly locked = mTextureClient->Lock(OPEN_READ_WRITE); + MOZ_ASSERT(locked); + } + if (mTextureClientOnWhite) { + DebugOnly locked = mTextureClientOnWhite->Lock(OPEN_READ_WRITE); + MOZ_ASSERT(locked); + } +} + +static void +WrapRotationAxis(int32_t* aRotationPoint, int32_t aSize) +{ + if (*aRotationPoint < 0) { + *aRotationPoint += aSize; + } else if (*aRotationPoint >= aSize) { + *aRotationPoint -= aSize; + } +} + +static void +FillSurface(DrawTarget* aDT, const nsIntRegion& aRegion, + const nsIntPoint& aOffset, const gfxRGBA& aColor) +{ + nsIntRegionRectIterator iter(aRegion); + const nsIntRect* r; + while ((r = iter.Next()) != nullptr) { + aDT->FillRect(Rect(r->x - aOffset.x, r->y - aOffset.y, + r->width, r->height), + ColorPattern(ToColor(aColor))); + } +} + +RotatedContentBuffer::PaintState +ContentClientIncremental::BeginPaintBuffer(ThebesLayer* aLayer, + uint32_t aFlags) +{ + mTextureInfo.mDeprecatedTextureHostFlags = 0; + PaintState result; + // We need to disable rotation if we're going to be resampled when + // drawing, because we might sample across the rotation boundary. + bool canHaveRotation = !(aFlags & RotatedContentBuffer::PAINT_WILL_RESAMPLE); + + nsIntRegion validRegion = aLayer->GetValidRegion(); + + bool canUseOpaqueSurface = aLayer->CanUseOpaqueSurface(); + ContentType contentType = + canUseOpaqueSurface ? gfxContentType::COLOR : + gfxContentType::COLOR_ALPHA; + + SurfaceMode mode; + nsIntRegion neededRegion; + bool canReuseBuffer; + nsIntRect destBufferRect; + + while (true) { + mode = aLayer->GetSurfaceMode(); + neededRegion = aLayer->GetVisibleRegion(); + // If we're going to resample, we need a buffer that's in clamp mode. + canReuseBuffer = neededRegion.GetBounds().Size() <= mBufferRect.Size() && + mHasBuffer && + (!(aFlags & RotatedContentBuffer::PAINT_WILL_RESAMPLE) || + !(mTextureInfo.mTextureFlags & TEXTURE_ALLOW_REPEAT)); + + if (canReuseBuffer) { + if (mBufferRect.Contains(neededRegion.GetBounds())) { + // We don't need to adjust mBufferRect. + destBufferRect = mBufferRect; + } else { + // The buffer's big enough but doesn't contain everything that's + // going to be visible. We'll move it. + destBufferRect = nsIntRect(neededRegion.GetBounds().TopLeft(), mBufferRect.Size()); + } + } else { + destBufferRect = neededRegion.GetBounds(); + } + + if (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) { + if (!gfxPrefs::ComponentAlphaEnabled() || + !aLayer->GetParent() || + !aLayer->GetParent()->SupportsComponentAlphaChildren()) { + mode = SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA; + } else { + contentType = gfxContentType::COLOR; + } + } + + if ((aFlags & RotatedContentBuffer::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; + } + // For component alpha layers, we leave contentType as gfxContentType::COLOR. + + // We need to validate the entire buffer, to make sure that only valid + // pixels are sampled + neededRegion = destBufferRect; + } + + if (mHasBuffer && + (mContentType != contentType || + (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) != mHasBufferOnWhite)) { + // We're effectively clearing the valid region, so we need to draw + // the entire needed region now. + result.mRegionToInvalidate = aLayer->GetValidRegion(); + validRegion.SetEmpty(); + mHasBuffer = false; + mHasBufferOnWhite = false; + mBufferRect.SetRect(0, 0, 0, 0); + mBufferRotation.MoveTo(0, 0); + // Restart decision process with the cleared buffer. We can only go + // around the loop one more iteration, since mTexImage is null now. + continue; + } + + break; + } + + result.mRegionToDraw.Sub(neededRegion, validRegion); + if (result.mRegionToDraw.IsEmpty()) + return result; + + if (destBufferRect.width > mForwarder->GetMaxTextureSize() || + destBufferRect.height > mForwarder->GetMaxTextureSize()) { + return result; + } + + // BlitTextureImage depends on the FBO texture target being + // TEXTURE_2D. This isn't the case on some older X1600-era Radeons. + if (!mForwarder->SupportsTextureBlitting() || + !mForwarder->SupportsPartialUploads()) { + result.mRegionToDraw = neededRegion; + validRegion.SetEmpty(); + mHasBuffer = false; + mHasBufferOnWhite = false; + mBufferRect.SetRect(0, 0, 0, 0); + mBufferRotation.MoveTo(0, 0); + canReuseBuffer = false; + } + + nsIntRect drawBounds = result.mRegionToDraw.GetBounds(); + bool createdBuffer = false; + + uint32_t bufferFlags = canHaveRotation ? TEXTURE_ALLOW_REPEAT : 0; + if (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) { + bufferFlags |= TEXTURE_COMPONENT_ALPHA; + } + if (canReuseBuffer) { + nsIntRect keepArea; + if (keepArea.IntersectRect(destBufferRect, mBufferRect)) { + // Set mBufferRotation so that the pixels currently in mBuffer + // will still be rendered in the right place when mBufferRect + // changes to destBufferRect. + nsIntPoint newRotation = mBufferRotation + + (destBufferRect.TopLeft() - mBufferRect.TopLeft()); + WrapRotationAxis(&newRotation.x, mBufferRect.width); + WrapRotationAxis(&newRotation.y, mBufferRect.height); + NS_ASSERTION(nsIntRect(nsIntPoint(0,0), mBufferRect.Size()).Contains(newRotation), + "newRotation out of bounds"); + int32_t xBoundary = destBufferRect.XMost() - newRotation.x; + int32_t yBoundary = destBufferRect.YMost() - newRotation.y; + if ((drawBounds.x < xBoundary && xBoundary < drawBounds.XMost()) || + (drawBounds.y < yBoundary && yBoundary < drawBounds.YMost()) || + (newRotation != nsIntPoint(0,0) && !canHaveRotation)) { + // The stuff we need to redraw will wrap around an edge of the + // buffer, so we will need to do a self-copy + // If mBufferRotation == nsIntPoint(0,0) we could do a real + // self-copy but we're not going to do that in GL yet. + // We can't do a real self-copy because the buffer is rotated. + // So allocate a new buffer for the destination. + destBufferRect = neededRegion.GetBounds(); + createdBuffer = true; + } else { + mBufferRect = destBufferRect; + mBufferRotation = newRotation; + } + } else { + // No pixels are going to be kept. The whole visible region + // will be redrawn, so we don't need to copy anything, so we don't + // set destBuffer. + mBufferRect = destBufferRect; + mBufferRotation = nsIntPoint(0,0); + } + } else { + // The buffer's not big enough, so allocate a new one + createdBuffer = true; + } + NS_ASSERTION(!(aFlags & RotatedContentBuffer::PAINT_WILL_RESAMPLE) || + destBufferRect == neededRegion.GetBounds(), + "If we're resampling, we need to validate the entire buffer"); + + if (!createdBuffer && !mHasBuffer) { + return result; + } + + if (createdBuffer) { + if (mHasBuffer && + (mode != SurfaceMode::SURFACE_COMPONENT_ALPHA || mHasBufferOnWhite)) { + mTextureInfo.mDeprecatedTextureHostFlags = TEXTURE_HOST_COPY_PREVIOUS; + } + + mHasBuffer = true; + if (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) { + mHasBufferOnWhite = true; + } + mBufferRect = destBufferRect; + mBufferRotation = nsIntPoint(0,0); + NotifyBufferCreated(contentType, bufferFlags); + } + + NS_ASSERTION(canHaveRotation || mBufferRotation == nsIntPoint(0,0), + "Rotation disabled, but we have nonzero rotation?"); + + nsIntRegion invalidate; + invalidate.Sub(aLayer->GetValidRegion(), destBufferRect); + result.mRegionToInvalidate.Or(result.mRegionToInvalidate, invalidate); + + // If we do partial updates, we have to clip drawing to the regionToDraw. + // If we don't clip, background images will be fillrect'd to the region correctly, + // while text or lines will paint outside of the regionToDraw. This becomes apparent + // with concave regions. Right now the scrollbars invalidate a narrow strip of the bar + // although they never cover it. This leads to two draw rects, the narow strip and the actually + // newly exposed area. It would be wise to fix this glitch in any way to have simpler + // clip and draw regions. + result.mClip = DrawRegionClip::DRAW; + result.mMode = mode; + + return result; +} + +DrawTarget* +ContentClientIncremental::BorrowDrawTargetForPainting(const PaintState& aPaintState, + RotatedContentBuffer::DrawIterator* aIter) +{ + if (aPaintState.mMode == SurfaceMode::SURFACE_NONE) { + return nullptr; + } + + if (aIter) { + if (aIter->mCount++ > 0) { + return nullptr; + } + aIter->mDrawRegion = aPaintState.mRegionToDraw; + } + + DrawTarget* result = nullptr; + + nsIntRect drawBounds = aPaintState.mRegionToDraw.GetBounds(); + MOZ_ASSERT(!mLoanedDrawTarget); + + // BeginUpdate is allowed to modify the given region, + // if it wants more to be repainted than we request. + if (aPaintState.mMode == SurfaceMode::SURFACE_COMPONENT_ALPHA) { + nsIntRegion drawRegionCopy = aPaintState.mRegionToDraw; + RefPtr onBlack = GetUpdateSurface(BUFFER_BLACK, drawRegionCopy); + RefPtr onWhite = GetUpdateSurface(BUFFER_WHITE, aPaintState.mRegionToDraw); + if (onBlack && onWhite) { + NS_ASSERTION(aPaintState.mRegionToDraw == drawRegionCopy, + "BeginUpdate should always modify the draw region in the same way!"); + FillSurface(onBlack, aPaintState.mRegionToDraw, nsIntPoint(drawBounds.x, drawBounds.y), gfxRGBA(0.0, 0.0, 0.0, 1.0)); + FillSurface(onWhite, aPaintState.mRegionToDraw, nsIntPoint(drawBounds.x, drawBounds.y), gfxRGBA(1.0, 1.0, 1.0, 1.0)); + mLoanedDrawTarget = Factory::CreateDualDrawTarget(onBlack, onWhite); + } else { + mLoanedDrawTarget = nullptr; + } + } else { + mLoanedDrawTarget = GetUpdateSurface(BUFFER_BLACK, aPaintState.mRegionToDraw); + } + if (!mLoanedDrawTarget) { + NS_WARNING("unable to get context for update"); + return nullptr; + } + + result = mLoanedDrawTarget; + mLoanedTransform = mLoanedDrawTarget->GetTransform(); + mLoanedTransform.Translate(-drawBounds.x, -drawBounds.y); + result->SetTransform(mLoanedTransform); + mLoanedTransform.Translate(drawBounds.x, drawBounds.y); + + if (mContentType == gfxContentType::COLOR_ALPHA) { + gfxUtils::ClipToRegion(result, aPaintState.mRegionToDraw); + nsIntRect bounds = aPaintState.mRegionToDraw.GetBounds(); + result->ClearRect(Rect(bounds.x, bounds.y, bounds.width, bounds.height)); + } + + return result; +} + +void +ContentClientIncremental::Updated(const nsIntRegion& aRegionToDraw, + const nsIntRegion& aVisibleRegion, + bool aDidSelfCopy) +{ + if (IsSurfaceDescriptorValid(mUpdateDescriptor)) { + mForwarder->UpdateTextureIncremental(this, + TextureFront, + mUpdateDescriptor, + aRegionToDraw, + mBufferRect, + mBufferRotation); + mUpdateDescriptor = SurfaceDescriptor(); + } + if (IsSurfaceDescriptorValid(mUpdateDescriptorOnWhite)) { + mForwarder->UpdateTextureIncremental(this, + TextureOnWhiteFront, + mUpdateDescriptorOnWhite, + aRegionToDraw, + mBufferRect, + mBufferRotation); + mUpdateDescriptorOnWhite = SurfaceDescriptor(); + } + +} + +TemporaryRef +ContentClientIncremental::GetUpdateSurface(BufferType aType, + const nsIntRegion& aUpdateRegion) +{ + nsIntRect rgnSize = aUpdateRegion.GetBounds(); + if (!mBufferRect.Contains(rgnSize)) { + NS_ERROR("update outside of image"); + return nullptr; + } + SurfaceDescriptor desc; + if (!mForwarder->AllocSurfaceDescriptor(rgnSize.Size().ToIntSize(), + mContentType, + &desc)) { + NS_WARNING("creating SurfaceDescriptor failed!"); + Clear(); + return nullptr; + } + + if (aType == BUFFER_BLACK) { + MOZ_ASSERT(!IsSurfaceDescriptorValid(mUpdateDescriptor)); + mUpdateDescriptor = desc; + } else { + MOZ_ASSERT(!IsSurfaceDescriptorValid(mUpdateDescriptorOnWhite)); + MOZ_ASSERT(aType == BUFFER_WHITE); + mUpdateDescriptorOnWhite = desc; + } + + return GetDrawTargetForDescriptor(desc, gfx::BackendType::COREGRAPHICS); +} + +} +}