1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/gfx/layers/client/ContentClient.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,890 @@ 1.4 +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- 1.5 + * This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "mozilla/layers/ContentClient.h" 1.10 +#include "BasicLayers.h" // for BasicLayerManager 1.11 +#include "gfxColor.h" // for gfxRGBA 1.12 +#include "gfxContext.h" // for gfxContext, etc 1.13 +#include "gfxPlatform.h" // for gfxPlatform 1.14 +#include "gfxPrefs.h" // for gfxPrefs 1.15 +#include "gfxPoint.h" // for gfxIntSize, gfxPoint 1.16 +#include "gfxTeeSurface.h" // for gfxTeeSurface 1.17 +#include "gfxUtils.h" // for gfxUtils 1.18 +#include "ipc/ShadowLayers.h" // for ShadowLayerForwarder 1.19 +#include "mozilla/ArrayUtils.h" // for ArrayLength 1.20 +#include "mozilla/gfx/2D.h" // for DrawTarget, Factory 1.21 +#include "mozilla/gfx/BasePoint.h" // for BasePoint 1.22 +#include "mozilla/gfx/BaseSize.h" // for BaseSize 1.23 +#include "mozilla/gfx/Rect.h" // for Rect 1.24 +#include "mozilla/gfx/Types.h" 1.25 +#include "mozilla/layers/LayerManagerComposite.h" 1.26 +#include "mozilla/layers/LayersMessages.h" // for ThebesBufferData 1.27 +#include "mozilla/layers/LayersTypes.h" 1.28 +#include "nsAutoPtr.h" // for nsRefPtr 1.29 +#include "nsDebug.h" // for NS_ASSERTION, NS_WARNING, etc 1.30 +#include "nsISupportsImpl.h" // for gfxContext::Release, etc 1.31 +#include "nsIWidget.h" // for nsIWidget 1.32 +#include "prenv.h" // for PR_GetEnv 1.33 +#ifdef XP_WIN 1.34 +#include "gfxWindowsPlatform.h" 1.35 +#endif 1.36 +#include "gfx2DGlue.h" 1.37 + 1.38 +namespace mozilla { 1.39 + 1.40 +using namespace gfx; 1.41 + 1.42 +namespace layers { 1.43 + 1.44 +static TextureFlags TextureFlagsForRotatedContentBufferFlags(uint32_t aBufferFlags) 1.45 +{ 1.46 + TextureFlags result = 0; 1.47 + 1.48 + if (aBufferFlags & RotatedContentBuffer::BUFFER_COMPONENT_ALPHA) { 1.49 + result |= TEXTURE_COMPONENT_ALPHA; 1.50 + } 1.51 + 1.52 + if (aBufferFlags & RotatedContentBuffer::ALLOW_REPEAT) { 1.53 + result |= TEXTURE_ALLOW_REPEAT; 1.54 + } 1.55 + 1.56 + return result; 1.57 +} 1.58 + 1.59 + 1.60 +/* static */ TemporaryRef<ContentClient> 1.61 +ContentClient::CreateContentClient(CompositableForwarder* aForwarder) 1.62 +{ 1.63 + LayersBackend backend = aForwarder->GetCompositorBackendType(); 1.64 + if (backend != LayersBackend::LAYERS_OPENGL && 1.65 + backend != LayersBackend::LAYERS_D3D9 && 1.66 + backend != LayersBackend::LAYERS_D3D11 && 1.67 + backend != LayersBackend::LAYERS_BASIC) { 1.68 + return nullptr; 1.69 + } 1.70 + 1.71 + bool useDoubleBuffering = false; 1.72 + 1.73 +#ifdef XP_WIN 1.74 + if (backend == LayersBackend::LAYERS_D3D11) { 1.75 + useDoubleBuffering = !!gfxWindowsPlatform::GetPlatform()->GetD2DDevice(); 1.76 + } else 1.77 +#endif 1.78 + { 1.79 + useDoubleBuffering = (LayerManagerComposite::SupportsDirectTexturing() && 1.80 + backend != LayersBackend::LAYERS_D3D9) || 1.81 + backend == LayersBackend::LAYERS_BASIC; 1.82 + } 1.83 + 1.84 + if (useDoubleBuffering || PR_GetEnv("MOZ_FORCE_DOUBLE_BUFFERING")) { 1.85 + return new ContentClientDoubleBuffered(aForwarder); 1.86 + } 1.87 +#ifdef XP_MACOSX 1.88 + if (backend == LayersBackend::LAYERS_OPENGL) { 1.89 + return new ContentClientIncremental(aForwarder); 1.90 + } 1.91 +#endif 1.92 + return new ContentClientSingleBuffered(aForwarder); 1.93 +} 1.94 + 1.95 +void 1.96 +ContentClient::EndPaint() 1.97 +{ 1.98 + // It is very important that this is called after any overridden EndPaint behaviour, 1.99 + // because destroying textures is a three stage process: 1.100 + // 1. We are done with the buffer and move it to ContentClient::mOldTextures, 1.101 + // that happens in DestroyBuffers which is may be called indirectly from 1.102 + // PaintThebes. 1.103 + // 2. The content client calls RemoveTextureClient on the texture clients in 1.104 + // mOldTextures and forgets them. They then become invalid. The compositable 1.105 + // client keeps a record of IDs. This happens in EndPaint. 1.106 + // 3. An IPC message is sent to destroy the corresponding texture host. That 1.107 + // happens from OnTransaction. 1.108 + // It is important that these steps happen in order. 1.109 + OnTransaction(); 1.110 +} 1.111 + 1.112 +// We pass a null pointer for the ContentClient Forwarder argument, which means 1.113 +// this client will not have a ContentHost on the other side. 1.114 +ContentClientBasic::ContentClientBasic() 1.115 + : ContentClient(nullptr) 1.116 + , RotatedContentBuffer(ContainsVisibleBounds) 1.117 +{} 1.118 + 1.119 +void 1.120 +ContentClientBasic::CreateBuffer(ContentType aType, 1.121 + const nsIntRect& aRect, 1.122 + uint32_t aFlags, 1.123 + RefPtr<gfx::DrawTarget>* aBlackDT, 1.124 + RefPtr<gfx::DrawTarget>* aWhiteDT) 1.125 +{ 1.126 + MOZ_ASSERT(!(aFlags & BUFFER_COMPONENT_ALPHA)); 1.127 + gfxImageFormat format = 1.128 + gfxPlatform::GetPlatform()->OptimalFormatForContent(aType); 1.129 + 1.130 + *aBlackDT = gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget( 1.131 + IntSize(aRect.width, aRect.height), 1.132 + ImageFormatToSurfaceFormat(format)); 1.133 +} 1.134 + 1.135 +void 1.136 +ContentClientRemoteBuffer::DestroyBuffers() 1.137 +{ 1.138 + if (!mTextureClient) { 1.139 + return; 1.140 + } 1.141 + 1.142 + mOldTextures.AppendElement(mTextureClient); 1.143 + mTextureClient = nullptr; 1.144 + if (mTextureClientOnWhite) { 1.145 + mOldTextures.AppendElement(mTextureClientOnWhite); 1.146 + mTextureClientOnWhite = nullptr; 1.147 + } 1.148 + 1.149 + DestroyFrontBuffer(); 1.150 +} 1.151 + 1.152 +void 1.153 +ContentClientRemoteBuffer::BeginPaint() 1.154 +{ 1.155 + // XXX: So we might not have a TextureClient yet.. because it will 1.156 + // only be created by CreateBuffer.. which will deliver a locked surface!. 1.157 + if (mTextureClient) { 1.158 + SetBufferProvider(mTextureClient); 1.159 + } 1.160 + if (mTextureClientOnWhite) { 1.161 + SetBufferProviderOnWhite(mTextureClientOnWhite); 1.162 + } 1.163 +} 1.164 + 1.165 +void 1.166 +ContentClientRemoteBuffer::EndPaint() 1.167 +{ 1.168 + // XXX: We might still not have a texture client if PaintThebes 1.169 + // decided we didn't need one yet because the region to draw was empty. 1.170 + SetBufferProvider(nullptr); 1.171 + SetBufferProviderOnWhite(nullptr); 1.172 + for (unsigned i = 0; i< mOldTextures.Length(); ++i) { 1.173 + if (mOldTextures[i]->IsLocked()) { 1.174 + mOldTextures[i]->Unlock(); 1.175 + } 1.176 + } 1.177 + mOldTextures.Clear(); 1.178 + 1.179 + if (mTextureClient && mTextureClient->IsLocked()) { 1.180 + mTextureClient->Unlock(); 1.181 + } 1.182 + if (mTextureClientOnWhite && mTextureClientOnWhite->IsLocked()) { 1.183 + mTextureClientOnWhite->Unlock(); 1.184 + } 1.185 + ContentClientRemote::EndPaint(); 1.186 +} 1.187 + 1.188 +bool 1.189 +ContentClientRemoteBuffer::CreateAndAllocateTextureClient(RefPtr<TextureClient>& aClient, 1.190 + TextureFlags aFlags) 1.191 +{ 1.192 + // gfx::BackendType::NONE means fallback to the content backend 1.193 + aClient = CreateTextureClientForDrawing(mSurfaceFormat, 1.194 + mTextureInfo.mTextureFlags | aFlags, 1.195 + gfx::BackendType::NONE, 1.196 + mSize); 1.197 + if (!aClient) { 1.198 + return false; 1.199 + } 1.200 + 1.201 + if (!aClient->AllocateForSurface(mSize, ALLOC_CLEAR_BUFFER)) { 1.202 + aClient = CreateTextureClientForDrawing(mSurfaceFormat, 1.203 + mTextureInfo.mTextureFlags | TEXTURE_ALLOC_FALLBACK | aFlags, 1.204 + gfx::BackendType::NONE, 1.205 + mSize); 1.206 + if (!aClient) { 1.207 + return false; 1.208 + } 1.209 + if (!aClient->AllocateForSurface(mSize, ALLOC_CLEAR_BUFFER)) { 1.210 + NS_WARNING("Could not allocate texture client"); 1.211 + aClient = nullptr; 1.212 + return false; 1.213 + } 1.214 + } 1.215 + 1.216 + NS_WARN_IF_FALSE(aClient->IsValid(), "Created an invalid texture client"); 1.217 + return true; 1.218 +} 1.219 + 1.220 +void 1.221 +ContentClientRemoteBuffer::BuildTextureClients(SurfaceFormat aFormat, 1.222 + const nsIntRect& aRect, 1.223 + uint32_t aFlags) 1.224 +{ 1.225 + // If we hit this assertion, then it might be due to an empty transaction 1.226 + // followed by a real transaction. Our buffers should be created (but not 1.227 + // painted in the empty transaction) and then painted (but not created) in the 1.228 + // real transaction. That is kind of fragile, and this assert will catch 1.229 + // circumstances where we screw that up, e.g., by unnecessarily recreating our 1.230 + // buffers. 1.231 + NS_ABORT_IF_FALSE(!mIsNewBuffer, 1.232 + "Bad! Did we create a buffer twice without painting?"); 1.233 + 1.234 + mIsNewBuffer = true; 1.235 + 1.236 + DestroyBuffers(); 1.237 + 1.238 + mSurfaceFormat = aFormat; 1.239 + mSize = gfx::IntSize(aRect.width, aRect.height); 1.240 + mTextureInfo.mTextureFlags = TextureFlagsForRotatedContentBufferFlags(aFlags); 1.241 + 1.242 + if (!CreateAndAllocateTextureClient(mTextureClient, TEXTURE_ON_BLACK) || 1.243 + !AddTextureClient(mTextureClient)) { 1.244 + AbortTextureClientCreation(); 1.245 + return; 1.246 + } 1.247 + 1.248 + if (aFlags & BUFFER_COMPONENT_ALPHA) { 1.249 + if (!CreateAndAllocateTextureClient(mTextureClientOnWhite, TEXTURE_ON_WHITE) || 1.250 + !AddTextureClient(mTextureClientOnWhite)) { 1.251 + AbortTextureClientCreation(); 1.252 + return; 1.253 + } 1.254 + mTextureInfo.mTextureFlags |= TEXTURE_COMPONENT_ALPHA; 1.255 + } 1.256 + 1.257 + CreateFrontBuffer(aRect); 1.258 +} 1.259 + 1.260 +void 1.261 +ContentClientRemoteBuffer::CreateBuffer(ContentType aType, 1.262 + const nsIntRect& aRect, 1.263 + uint32_t aFlags, 1.264 + RefPtr<gfx::DrawTarget>* aBlackDT, 1.265 + RefPtr<gfx::DrawTarget>* aWhiteDT) 1.266 +{ 1.267 + BuildTextureClients(gfxPlatform::GetPlatform()->Optimal2DFormatForContent(aType), aRect, aFlags); 1.268 + if (!mTextureClient) { 1.269 + return; 1.270 + } 1.271 + 1.272 + // We just created the textures and we are about to get their draw targets 1.273 + // so we have to lock them here. 1.274 + DebugOnly<bool> locked = mTextureClient->Lock(OPEN_READ_WRITE); 1.275 + MOZ_ASSERT(locked, "Could not lock the TextureClient"); 1.276 + 1.277 + *aBlackDT = mTextureClient->GetAsDrawTarget(); 1.278 + if (aFlags & BUFFER_COMPONENT_ALPHA) { 1.279 + locked = mTextureClientOnWhite->Lock(OPEN_READ_WRITE); 1.280 + MOZ_ASSERT(locked, "Could not lock the second TextureClient for component alpha"); 1.281 + 1.282 + *aWhiteDT = mTextureClientOnWhite->GetAsDrawTarget(); 1.283 + } 1.284 +} 1.285 + 1.286 +nsIntRegion 1.287 +ContentClientRemoteBuffer::GetUpdatedRegion(const nsIntRegion& aRegionToDraw, 1.288 + const nsIntRegion& aVisibleRegion, 1.289 + bool aDidSelfCopy) 1.290 +{ 1.291 + nsIntRegion updatedRegion; 1.292 + if (mIsNewBuffer || aDidSelfCopy) { 1.293 + // A buffer reallocation clears both buffers. The front buffer has all the 1.294 + // content by now, but the back buffer is still clear. Here, in effect, we 1.295 + // are saying to copy all of the pixels of the front buffer to the back. 1.296 + // Also when we self-copied in the buffer, the buffer space 1.297 + // changes and some changed buffer content isn't reflected in the 1.298 + // draw or invalidate region (on purpose!). When this happens, we 1.299 + // need to read back the entire buffer too. 1.300 + updatedRegion = aVisibleRegion; 1.301 + mIsNewBuffer = false; 1.302 + } else { 1.303 + updatedRegion = aRegionToDraw; 1.304 + } 1.305 + 1.306 + NS_ASSERTION(BufferRect().Contains(aRegionToDraw.GetBounds()), 1.307 + "Update outside of buffer rect!"); 1.308 + NS_ABORT_IF_FALSE(mTextureClient, "should have a back buffer by now"); 1.309 + 1.310 + return updatedRegion; 1.311 +} 1.312 + 1.313 +void 1.314 +ContentClientRemoteBuffer::Updated(const nsIntRegion& aRegionToDraw, 1.315 + const nsIntRegion& aVisibleRegion, 1.316 + bool aDidSelfCopy) 1.317 +{ 1.318 + nsIntRegion updatedRegion = GetUpdatedRegion(aRegionToDraw, 1.319 + aVisibleRegion, 1.320 + aDidSelfCopy); 1.321 + 1.322 + MOZ_ASSERT(mTextureClient); 1.323 + if (mTextureClientOnWhite) { 1.324 + mForwarder->UseComponentAlphaTextures(this, mTextureClient, 1.325 + mTextureClientOnWhite); 1.326 + } else { 1.327 + mForwarder->UseTexture(this, mTextureClient); 1.328 + } 1.329 + mForwarder->UpdateTextureRegion(this, 1.330 + ThebesBufferData(BufferRect(), 1.331 + BufferRotation()), 1.332 + updatedRegion); 1.333 +} 1.334 + 1.335 +void 1.336 +ContentClientRemoteBuffer::SwapBuffers(const nsIntRegion& aFrontUpdatedRegion) 1.337 +{ 1.338 + MOZ_ASSERT(mTextureClient); 1.339 + mFrontAndBackBufferDiffer = true; 1.340 +} 1.341 + 1.342 +void 1.343 +ContentClientDoubleBuffered::CreateFrontBuffer(const nsIntRect& aBufferRect) 1.344 +{ 1.345 + if (!CreateAndAllocateTextureClient(mFrontClient, TEXTURE_ON_BLACK) || 1.346 + !AddTextureClient(mFrontClient)) { 1.347 + AbortTextureClientCreation(); 1.348 + return; 1.349 + } 1.350 + if (mTextureInfo.mTextureFlags & TEXTURE_COMPONENT_ALPHA) { 1.351 + if (!CreateAndAllocateTextureClient(mFrontClientOnWhite, TEXTURE_ON_WHITE) || 1.352 + !AddTextureClient(mFrontClientOnWhite)) { 1.353 + AbortTextureClientCreation(); 1.354 + return; 1.355 + } 1.356 + } 1.357 + 1.358 + mFrontBufferRect = aBufferRect; 1.359 + mFrontBufferRotation = nsIntPoint(); 1.360 +} 1.361 + 1.362 +void 1.363 +ContentClientDoubleBuffered::DestroyFrontBuffer() 1.364 +{ 1.365 + MOZ_ASSERT(mFrontClient); 1.366 + 1.367 + mOldTextures.AppendElement(mFrontClient); 1.368 + mFrontClient = nullptr; 1.369 + if (mFrontClientOnWhite) { 1.370 + mOldTextures.AppendElement(mFrontClientOnWhite); 1.371 + mFrontClientOnWhite = nullptr; 1.372 + } 1.373 +} 1.374 + 1.375 +void 1.376 +ContentClientDoubleBuffered::SwapBuffers(const nsIntRegion& aFrontUpdatedRegion) 1.377 +{ 1.378 + mFrontUpdatedRegion = aFrontUpdatedRegion; 1.379 + 1.380 + RefPtr<TextureClient> oldBack = mTextureClient; 1.381 + mTextureClient = mFrontClient; 1.382 + mFrontClient = oldBack; 1.383 + 1.384 + oldBack = mTextureClientOnWhite; 1.385 + mTextureClientOnWhite = mFrontClientOnWhite; 1.386 + mFrontClientOnWhite = oldBack; 1.387 + 1.388 + nsIntRect oldBufferRect = mBufferRect; 1.389 + mBufferRect = mFrontBufferRect; 1.390 + mFrontBufferRect = oldBufferRect; 1.391 + 1.392 + nsIntPoint oldBufferRotation = mBufferRotation; 1.393 + mBufferRotation = mFrontBufferRotation; 1.394 + mFrontBufferRotation = oldBufferRotation; 1.395 + 1.396 + MOZ_ASSERT(mFrontClient); 1.397 + 1.398 + ContentClientRemoteBuffer::SwapBuffers(aFrontUpdatedRegion); 1.399 +} 1.400 + 1.401 +void 1.402 +ContentClientDoubleBuffered::BeginPaint() 1.403 +{ 1.404 + ContentClientRemoteBuffer::BeginPaint(); 1.405 + 1.406 + mIsNewBuffer = false; 1.407 + 1.408 + if (!mFrontAndBackBufferDiffer) { 1.409 + return; 1.410 + } 1.411 + 1.412 + if (mDidSelfCopy) { 1.413 + // We can't easily draw our front buffer into us, since we're going to be 1.414 + // copying stuff around anyway it's easiest if we just move our situation 1.415 + // to non-rotated while we're at it. If this situation occurs we'll have 1.416 + // hit a self-copy path in PaintThebes before as well anyway. 1.417 + mBufferRect.MoveTo(mFrontBufferRect.TopLeft()); 1.418 + mBufferRotation = nsIntPoint(); 1.419 + return; 1.420 + } 1.421 + mBufferRect = mFrontBufferRect; 1.422 + mBufferRotation = mFrontBufferRotation; 1.423 +} 1.424 + 1.425 +// Sync front/back buffers content 1.426 +// After executing, the new back buffer has the same (interesting) pixels as 1.427 +// the new front buffer, and mValidRegion et al. are correct wrt the new 1.428 +// back buffer (i.e. as they were for the old back buffer) 1.429 +void 1.430 +ContentClientDoubleBuffered::FinalizeFrame(const nsIntRegion& aRegionToDraw) 1.431 +{ 1.432 + if (mTextureClient) { 1.433 + DebugOnly<bool> locked = mTextureClient->Lock(OPEN_READ_WRITE); 1.434 + MOZ_ASSERT(locked); 1.435 + } 1.436 + if (mTextureClientOnWhite) { 1.437 + DebugOnly<bool> locked = mTextureClientOnWhite->Lock(OPEN_READ_WRITE); 1.438 + MOZ_ASSERT(locked); 1.439 + } 1.440 + 1.441 + if (!mFrontAndBackBufferDiffer) { 1.442 + MOZ_ASSERT(!mDidSelfCopy, "If we have to copy the world, then our buffers are different, right?"); 1.443 + return; 1.444 + } 1.445 + MOZ_ASSERT(mFrontClient); 1.446 + 1.447 + MOZ_LAYERS_LOG(("BasicShadowableThebes(%p): reading back <x=%d,y=%d,w=%d,h=%d>", 1.448 + this, 1.449 + mFrontUpdatedRegion.GetBounds().x, 1.450 + mFrontUpdatedRegion.GetBounds().y, 1.451 + mFrontUpdatedRegion.GetBounds().width, 1.452 + mFrontUpdatedRegion.GetBounds().height)); 1.453 + 1.454 + mFrontAndBackBufferDiffer = false; 1.455 + 1.456 + nsIntRegion updateRegion = mFrontUpdatedRegion; 1.457 + if (mDidSelfCopy) { 1.458 + mDidSelfCopy = false; 1.459 + updateRegion = mBufferRect; 1.460 + } 1.461 + 1.462 + // No point in sync'ing what we are going to draw over anyway. And if there is 1.463 + // nothing to sync at all, there is nothing to do and we can go home early. 1.464 + updateRegion.Sub(updateRegion, aRegionToDraw); 1.465 + if (updateRegion.IsEmpty()) { 1.466 + return; 1.467 + } 1.468 + 1.469 + // We need to ensure that we lock these two buffers in the same 1.470 + // order as the compositor to prevent deadlocks. 1.471 + if (!mFrontClient->Lock(OPEN_READ_ONLY)) { 1.472 + return; 1.473 + } 1.474 + if (mFrontClientOnWhite && 1.475 + !mFrontClientOnWhite->Lock(OPEN_READ_ONLY)) { 1.476 + mFrontClient->Unlock(); 1.477 + return; 1.478 + } 1.479 + { 1.480 + // Restrict the DrawTargets and frontBuffer to a scope to make 1.481 + // sure there is no more external references to the DrawTargets 1.482 + // when we Unlock the TextureClients. 1.483 + RefPtr<DrawTarget> dt = mFrontClient->GetAsDrawTarget(); 1.484 + RefPtr<DrawTarget> dtOnWhite = mFrontClientOnWhite 1.485 + ? mFrontClientOnWhite->GetAsDrawTarget() 1.486 + : nullptr; 1.487 + RotatedBuffer frontBuffer(dt, 1.488 + dtOnWhite, 1.489 + mFrontBufferRect, 1.490 + mFrontBufferRotation); 1.491 + UpdateDestinationFrom(frontBuffer, updateRegion); 1.492 + } 1.493 + 1.494 + mFrontClient->Unlock(); 1.495 + if (mFrontClientOnWhite) { 1.496 + mFrontClientOnWhite->Unlock(); 1.497 + } 1.498 +} 1.499 + 1.500 +void 1.501 +ContentClientDoubleBuffered::UpdateDestinationFrom(const RotatedBuffer& aSource, 1.502 + const nsIntRegion& aUpdateRegion) 1.503 +{ 1.504 + DrawIterator iter; 1.505 + while (DrawTarget* destDT = 1.506 + BorrowDrawTargetForQuadrantUpdate(aUpdateRegion.GetBounds(), BUFFER_BLACK, &iter)) { 1.507 + bool isClippingCheap = IsClippingCheap(destDT, iter.mDrawRegion); 1.508 + if (isClippingCheap) { 1.509 + gfxUtils::ClipToRegion(destDT, iter.mDrawRegion); 1.510 + } 1.511 + 1.512 + aSource.DrawBufferWithRotation(destDT, BUFFER_BLACK, 1.0, CompositionOp::OP_SOURCE); 1.513 + if (isClippingCheap) { 1.514 + destDT->PopClip(); 1.515 + } 1.516 + // Flush the destination before the sources become inaccessible (Unlock). 1.517 + destDT->Flush(); 1.518 + ReturnDrawTargetToBuffer(destDT); 1.519 + } 1.520 + 1.521 + if (aSource.HaveBufferOnWhite()) { 1.522 + MOZ_ASSERT(HaveBufferOnWhite()); 1.523 + DrawIterator whiteIter; 1.524 + while (DrawTarget* destDT = 1.525 + BorrowDrawTargetForQuadrantUpdate(aUpdateRegion.GetBounds(), BUFFER_WHITE, &whiteIter)) { 1.526 + bool isClippingCheap = IsClippingCheap(destDT, whiteIter.mDrawRegion); 1.527 + if (isClippingCheap) { 1.528 + gfxUtils::ClipToRegion(destDT, whiteIter.mDrawRegion); 1.529 + } 1.530 + 1.531 + aSource.DrawBufferWithRotation(destDT, BUFFER_WHITE, 1.0, CompositionOp::OP_SOURCE); 1.532 + if (isClippingCheap) { 1.533 + destDT->PopClip(); 1.534 + } 1.535 + // Flush the destination before the sources become inaccessible (Unlock). 1.536 + destDT->Flush(); 1.537 + ReturnDrawTargetToBuffer(destDT); 1.538 + } 1.539 + } 1.540 +} 1.541 + 1.542 +void 1.543 +ContentClientSingleBuffered::FinalizeFrame(const nsIntRegion& aRegionToDraw) 1.544 +{ 1.545 + if (mTextureClient) { 1.546 + DebugOnly<bool> locked = mTextureClient->Lock(OPEN_READ_WRITE); 1.547 + MOZ_ASSERT(locked); 1.548 + } 1.549 + if (mTextureClientOnWhite) { 1.550 + DebugOnly<bool> locked = mTextureClientOnWhite->Lock(OPEN_READ_WRITE); 1.551 + MOZ_ASSERT(locked); 1.552 + } 1.553 +} 1.554 + 1.555 +static void 1.556 +WrapRotationAxis(int32_t* aRotationPoint, int32_t aSize) 1.557 +{ 1.558 + if (*aRotationPoint < 0) { 1.559 + *aRotationPoint += aSize; 1.560 + } else if (*aRotationPoint >= aSize) { 1.561 + *aRotationPoint -= aSize; 1.562 + } 1.563 +} 1.564 + 1.565 +static void 1.566 +FillSurface(DrawTarget* aDT, const nsIntRegion& aRegion, 1.567 + const nsIntPoint& aOffset, const gfxRGBA& aColor) 1.568 +{ 1.569 + nsIntRegionRectIterator iter(aRegion); 1.570 + const nsIntRect* r; 1.571 + while ((r = iter.Next()) != nullptr) { 1.572 + aDT->FillRect(Rect(r->x - aOffset.x, r->y - aOffset.y, 1.573 + r->width, r->height), 1.574 + ColorPattern(ToColor(aColor))); 1.575 + } 1.576 +} 1.577 + 1.578 +RotatedContentBuffer::PaintState 1.579 +ContentClientIncremental::BeginPaintBuffer(ThebesLayer* aLayer, 1.580 + uint32_t aFlags) 1.581 +{ 1.582 + mTextureInfo.mDeprecatedTextureHostFlags = 0; 1.583 + PaintState result; 1.584 + // We need to disable rotation if we're going to be resampled when 1.585 + // drawing, because we might sample across the rotation boundary. 1.586 + bool canHaveRotation = !(aFlags & RotatedContentBuffer::PAINT_WILL_RESAMPLE); 1.587 + 1.588 + nsIntRegion validRegion = aLayer->GetValidRegion(); 1.589 + 1.590 + bool canUseOpaqueSurface = aLayer->CanUseOpaqueSurface(); 1.591 + ContentType contentType = 1.592 + canUseOpaqueSurface ? gfxContentType::COLOR : 1.593 + gfxContentType::COLOR_ALPHA; 1.594 + 1.595 + SurfaceMode mode; 1.596 + nsIntRegion neededRegion; 1.597 + bool canReuseBuffer; 1.598 + nsIntRect destBufferRect; 1.599 + 1.600 + while (true) { 1.601 + mode = aLayer->GetSurfaceMode(); 1.602 + neededRegion = aLayer->GetVisibleRegion(); 1.603 + // If we're going to resample, we need a buffer that's in clamp mode. 1.604 + canReuseBuffer = neededRegion.GetBounds().Size() <= mBufferRect.Size() && 1.605 + mHasBuffer && 1.606 + (!(aFlags & RotatedContentBuffer::PAINT_WILL_RESAMPLE) || 1.607 + !(mTextureInfo.mTextureFlags & TEXTURE_ALLOW_REPEAT)); 1.608 + 1.609 + if (canReuseBuffer) { 1.610 + if (mBufferRect.Contains(neededRegion.GetBounds())) { 1.611 + // We don't need to adjust mBufferRect. 1.612 + destBufferRect = mBufferRect; 1.613 + } else { 1.614 + // The buffer's big enough but doesn't contain everything that's 1.615 + // going to be visible. We'll move it. 1.616 + destBufferRect = nsIntRect(neededRegion.GetBounds().TopLeft(), mBufferRect.Size()); 1.617 + } 1.618 + } else { 1.619 + destBufferRect = neededRegion.GetBounds(); 1.620 + } 1.621 + 1.622 + if (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) { 1.623 + if (!gfxPrefs::ComponentAlphaEnabled() || 1.624 + !aLayer->GetParent() || 1.625 + !aLayer->GetParent()->SupportsComponentAlphaChildren()) { 1.626 + mode = SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA; 1.627 + } else { 1.628 + contentType = gfxContentType::COLOR; 1.629 + } 1.630 + } 1.631 + 1.632 + if ((aFlags & RotatedContentBuffer::PAINT_WILL_RESAMPLE) && 1.633 + (!neededRegion.GetBounds().IsEqualInterior(destBufferRect) || 1.634 + neededRegion.GetNumRects() > 1)) { 1.635 + // The area we add to neededRegion might not be painted opaquely 1.636 + if (mode == SurfaceMode::SURFACE_OPAQUE) { 1.637 + contentType = gfxContentType::COLOR_ALPHA; 1.638 + mode = SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA; 1.639 + } 1.640 + // For component alpha layers, we leave contentType as gfxContentType::COLOR. 1.641 + 1.642 + // We need to validate the entire buffer, to make sure that only valid 1.643 + // pixels are sampled 1.644 + neededRegion = destBufferRect; 1.645 + } 1.646 + 1.647 + if (mHasBuffer && 1.648 + (mContentType != contentType || 1.649 + (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) != mHasBufferOnWhite)) { 1.650 + // We're effectively clearing the valid region, so we need to draw 1.651 + // the entire needed region now. 1.652 + result.mRegionToInvalidate = aLayer->GetValidRegion(); 1.653 + validRegion.SetEmpty(); 1.654 + mHasBuffer = false; 1.655 + mHasBufferOnWhite = false; 1.656 + mBufferRect.SetRect(0, 0, 0, 0); 1.657 + mBufferRotation.MoveTo(0, 0); 1.658 + // Restart decision process with the cleared buffer. We can only go 1.659 + // around the loop one more iteration, since mTexImage is null now. 1.660 + continue; 1.661 + } 1.662 + 1.663 + break; 1.664 + } 1.665 + 1.666 + result.mRegionToDraw.Sub(neededRegion, validRegion); 1.667 + if (result.mRegionToDraw.IsEmpty()) 1.668 + return result; 1.669 + 1.670 + if (destBufferRect.width > mForwarder->GetMaxTextureSize() || 1.671 + destBufferRect.height > mForwarder->GetMaxTextureSize()) { 1.672 + return result; 1.673 + } 1.674 + 1.675 + // BlitTextureImage depends on the FBO texture target being 1.676 + // TEXTURE_2D. This isn't the case on some older X1600-era Radeons. 1.677 + if (!mForwarder->SupportsTextureBlitting() || 1.678 + !mForwarder->SupportsPartialUploads()) { 1.679 + result.mRegionToDraw = neededRegion; 1.680 + validRegion.SetEmpty(); 1.681 + mHasBuffer = false; 1.682 + mHasBufferOnWhite = false; 1.683 + mBufferRect.SetRect(0, 0, 0, 0); 1.684 + mBufferRotation.MoveTo(0, 0); 1.685 + canReuseBuffer = false; 1.686 + } 1.687 + 1.688 + nsIntRect drawBounds = result.mRegionToDraw.GetBounds(); 1.689 + bool createdBuffer = false; 1.690 + 1.691 + uint32_t bufferFlags = canHaveRotation ? TEXTURE_ALLOW_REPEAT : 0; 1.692 + if (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) { 1.693 + bufferFlags |= TEXTURE_COMPONENT_ALPHA; 1.694 + } 1.695 + if (canReuseBuffer) { 1.696 + nsIntRect keepArea; 1.697 + if (keepArea.IntersectRect(destBufferRect, mBufferRect)) { 1.698 + // Set mBufferRotation so that the pixels currently in mBuffer 1.699 + // will still be rendered in the right place when mBufferRect 1.700 + // changes to destBufferRect. 1.701 + nsIntPoint newRotation = mBufferRotation + 1.702 + (destBufferRect.TopLeft() - mBufferRect.TopLeft()); 1.703 + WrapRotationAxis(&newRotation.x, mBufferRect.width); 1.704 + WrapRotationAxis(&newRotation.y, mBufferRect.height); 1.705 + NS_ASSERTION(nsIntRect(nsIntPoint(0,0), mBufferRect.Size()).Contains(newRotation), 1.706 + "newRotation out of bounds"); 1.707 + int32_t xBoundary = destBufferRect.XMost() - newRotation.x; 1.708 + int32_t yBoundary = destBufferRect.YMost() - newRotation.y; 1.709 + if ((drawBounds.x < xBoundary && xBoundary < drawBounds.XMost()) || 1.710 + (drawBounds.y < yBoundary && yBoundary < drawBounds.YMost()) || 1.711 + (newRotation != nsIntPoint(0,0) && !canHaveRotation)) { 1.712 + // The stuff we need to redraw will wrap around an edge of the 1.713 + // buffer, so we will need to do a self-copy 1.714 + // If mBufferRotation == nsIntPoint(0,0) we could do a real 1.715 + // self-copy but we're not going to do that in GL yet. 1.716 + // We can't do a real self-copy because the buffer is rotated. 1.717 + // So allocate a new buffer for the destination. 1.718 + destBufferRect = neededRegion.GetBounds(); 1.719 + createdBuffer = true; 1.720 + } else { 1.721 + mBufferRect = destBufferRect; 1.722 + mBufferRotation = newRotation; 1.723 + } 1.724 + } else { 1.725 + // No pixels are going to be kept. The whole visible region 1.726 + // will be redrawn, so we don't need to copy anything, so we don't 1.727 + // set destBuffer. 1.728 + mBufferRect = destBufferRect; 1.729 + mBufferRotation = nsIntPoint(0,0); 1.730 + } 1.731 + } else { 1.732 + // The buffer's not big enough, so allocate a new one 1.733 + createdBuffer = true; 1.734 + } 1.735 + NS_ASSERTION(!(aFlags & RotatedContentBuffer::PAINT_WILL_RESAMPLE) || 1.736 + destBufferRect == neededRegion.GetBounds(), 1.737 + "If we're resampling, we need to validate the entire buffer"); 1.738 + 1.739 + if (!createdBuffer && !mHasBuffer) { 1.740 + return result; 1.741 + } 1.742 + 1.743 + if (createdBuffer) { 1.744 + if (mHasBuffer && 1.745 + (mode != SurfaceMode::SURFACE_COMPONENT_ALPHA || mHasBufferOnWhite)) { 1.746 + mTextureInfo.mDeprecatedTextureHostFlags = TEXTURE_HOST_COPY_PREVIOUS; 1.747 + } 1.748 + 1.749 + mHasBuffer = true; 1.750 + if (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) { 1.751 + mHasBufferOnWhite = true; 1.752 + } 1.753 + mBufferRect = destBufferRect; 1.754 + mBufferRotation = nsIntPoint(0,0); 1.755 + NotifyBufferCreated(contentType, bufferFlags); 1.756 + } 1.757 + 1.758 + NS_ASSERTION(canHaveRotation || mBufferRotation == nsIntPoint(0,0), 1.759 + "Rotation disabled, but we have nonzero rotation?"); 1.760 + 1.761 + nsIntRegion invalidate; 1.762 + invalidate.Sub(aLayer->GetValidRegion(), destBufferRect); 1.763 + result.mRegionToInvalidate.Or(result.mRegionToInvalidate, invalidate); 1.764 + 1.765 + // If we do partial updates, we have to clip drawing to the regionToDraw. 1.766 + // If we don't clip, background images will be fillrect'd to the region correctly, 1.767 + // while text or lines will paint outside of the regionToDraw. This becomes apparent 1.768 + // with concave regions. Right now the scrollbars invalidate a narrow strip of the bar 1.769 + // although they never cover it. This leads to two draw rects, the narow strip and the actually 1.770 + // newly exposed area. It would be wise to fix this glitch in any way to have simpler 1.771 + // clip and draw regions. 1.772 + result.mClip = DrawRegionClip::DRAW; 1.773 + result.mMode = mode; 1.774 + 1.775 + return result; 1.776 +} 1.777 + 1.778 +DrawTarget* 1.779 +ContentClientIncremental::BorrowDrawTargetForPainting(const PaintState& aPaintState, 1.780 + RotatedContentBuffer::DrawIterator* aIter) 1.781 +{ 1.782 + if (aPaintState.mMode == SurfaceMode::SURFACE_NONE) { 1.783 + return nullptr; 1.784 + } 1.785 + 1.786 + if (aIter) { 1.787 + if (aIter->mCount++ > 0) { 1.788 + return nullptr; 1.789 + } 1.790 + aIter->mDrawRegion = aPaintState.mRegionToDraw; 1.791 + } 1.792 + 1.793 + DrawTarget* result = nullptr; 1.794 + 1.795 + nsIntRect drawBounds = aPaintState.mRegionToDraw.GetBounds(); 1.796 + MOZ_ASSERT(!mLoanedDrawTarget); 1.797 + 1.798 + // BeginUpdate is allowed to modify the given region, 1.799 + // if it wants more to be repainted than we request. 1.800 + if (aPaintState.mMode == SurfaceMode::SURFACE_COMPONENT_ALPHA) { 1.801 + nsIntRegion drawRegionCopy = aPaintState.mRegionToDraw; 1.802 + RefPtr<DrawTarget> onBlack = GetUpdateSurface(BUFFER_BLACK, drawRegionCopy); 1.803 + RefPtr<DrawTarget> onWhite = GetUpdateSurface(BUFFER_WHITE, aPaintState.mRegionToDraw); 1.804 + if (onBlack && onWhite) { 1.805 + NS_ASSERTION(aPaintState.mRegionToDraw == drawRegionCopy, 1.806 + "BeginUpdate should always modify the draw region in the same way!"); 1.807 + FillSurface(onBlack, aPaintState.mRegionToDraw, nsIntPoint(drawBounds.x, drawBounds.y), gfxRGBA(0.0, 0.0, 0.0, 1.0)); 1.808 + FillSurface(onWhite, aPaintState.mRegionToDraw, nsIntPoint(drawBounds.x, drawBounds.y), gfxRGBA(1.0, 1.0, 1.0, 1.0)); 1.809 + mLoanedDrawTarget = Factory::CreateDualDrawTarget(onBlack, onWhite); 1.810 + } else { 1.811 + mLoanedDrawTarget = nullptr; 1.812 + } 1.813 + } else { 1.814 + mLoanedDrawTarget = GetUpdateSurface(BUFFER_BLACK, aPaintState.mRegionToDraw); 1.815 + } 1.816 + if (!mLoanedDrawTarget) { 1.817 + NS_WARNING("unable to get context for update"); 1.818 + return nullptr; 1.819 + } 1.820 + 1.821 + result = mLoanedDrawTarget; 1.822 + mLoanedTransform = mLoanedDrawTarget->GetTransform(); 1.823 + mLoanedTransform.Translate(-drawBounds.x, -drawBounds.y); 1.824 + result->SetTransform(mLoanedTransform); 1.825 + mLoanedTransform.Translate(drawBounds.x, drawBounds.y); 1.826 + 1.827 + if (mContentType == gfxContentType::COLOR_ALPHA) { 1.828 + gfxUtils::ClipToRegion(result, aPaintState.mRegionToDraw); 1.829 + nsIntRect bounds = aPaintState.mRegionToDraw.GetBounds(); 1.830 + result->ClearRect(Rect(bounds.x, bounds.y, bounds.width, bounds.height)); 1.831 + } 1.832 + 1.833 + return result; 1.834 +} 1.835 + 1.836 +void 1.837 +ContentClientIncremental::Updated(const nsIntRegion& aRegionToDraw, 1.838 + const nsIntRegion& aVisibleRegion, 1.839 + bool aDidSelfCopy) 1.840 +{ 1.841 + if (IsSurfaceDescriptorValid(mUpdateDescriptor)) { 1.842 + mForwarder->UpdateTextureIncremental(this, 1.843 + TextureFront, 1.844 + mUpdateDescriptor, 1.845 + aRegionToDraw, 1.846 + mBufferRect, 1.847 + mBufferRotation); 1.848 + mUpdateDescriptor = SurfaceDescriptor(); 1.849 + } 1.850 + if (IsSurfaceDescriptorValid(mUpdateDescriptorOnWhite)) { 1.851 + mForwarder->UpdateTextureIncremental(this, 1.852 + TextureOnWhiteFront, 1.853 + mUpdateDescriptorOnWhite, 1.854 + aRegionToDraw, 1.855 + mBufferRect, 1.856 + mBufferRotation); 1.857 + mUpdateDescriptorOnWhite = SurfaceDescriptor(); 1.858 + } 1.859 + 1.860 +} 1.861 + 1.862 +TemporaryRef<DrawTarget> 1.863 +ContentClientIncremental::GetUpdateSurface(BufferType aType, 1.864 + const nsIntRegion& aUpdateRegion) 1.865 +{ 1.866 + nsIntRect rgnSize = aUpdateRegion.GetBounds(); 1.867 + if (!mBufferRect.Contains(rgnSize)) { 1.868 + NS_ERROR("update outside of image"); 1.869 + return nullptr; 1.870 + } 1.871 + SurfaceDescriptor desc; 1.872 + if (!mForwarder->AllocSurfaceDescriptor(rgnSize.Size().ToIntSize(), 1.873 + mContentType, 1.874 + &desc)) { 1.875 + NS_WARNING("creating SurfaceDescriptor failed!"); 1.876 + Clear(); 1.877 + return nullptr; 1.878 + } 1.879 + 1.880 + if (aType == BUFFER_BLACK) { 1.881 + MOZ_ASSERT(!IsSurfaceDescriptorValid(mUpdateDescriptor)); 1.882 + mUpdateDescriptor = desc; 1.883 + } else { 1.884 + MOZ_ASSERT(!IsSurfaceDescriptorValid(mUpdateDescriptorOnWhite)); 1.885 + MOZ_ASSERT(aType == BUFFER_WHITE); 1.886 + mUpdateDescriptorOnWhite = desc; 1.887 + } 1.888 + 1.889 + return GetDrawTargetForDescriptor(desc, gfx::BackendType::COREGRAPHICS); 1.890 +} 1.891 + 1.892 +} 1.893 +}