1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/gfx/layers/client/TiledContentClient.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1119 @@ 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/TiledContentClient.h" 1.10 +#include <math.h> // for ceil, ceilf, floor 1.11 +#include "ClientTiledThebesLayer.h" // for ClientTiledThebesLayer 1.12 +#include "GeckoProfiler.h" // for PROFILER_LABEL 1.13 +#include "ClientLayerManager.h" // for ClientLayerManager 1.14 +#include "CompositorChild.h" // for CompositorChild 1.15 +#include "gfxContext.h" // for gfxContext, etc 1.16 +#include "gfxPlatform.h" // for gfxPlatform 1.17 +#include "gfxPrefs.h" // for gfxPrefs 1.18 +#include "gfxRect.h" // for gfxRect 1.19 +#include "mozilla/MathAlgorithms.h" // for Abs 1.20 +#include "mozilla/gfx/Point.h" // for IntSize 1.21 +#include "mozilla/gfx/Rect.h" // for Rect 1.22 +#include "mozilla/layers/CompositableForwarder.h" 1.23 +#include "mozilla/layers/ShadowLayers.h" // for ShadowLayerForwarder 1.24 +#include "TextureClientPool.h" 1.25 +#include "nsDebug.h" // for NS_ASSERTION 1.26 +#include "nsISupportsImpl.h" // for gfxContext::AddRef, etc 1.27 +#include "nsSize.h" // for nsIntSize 1.28 +#include "gfxReusableSharedImageSurfaceWrapper.h" 1.29 +#include "nsMathUtils.h" // for NS_roundf 1.30 +#include "gfx2DGlue.h" 1.31 + 1.32 +// This is the minimum area that we deem reasonable to copy from the front buffer to the 1.33 +// back buffer on tile updates. If the valid region is smaller than this, we just 1.34 +// redraw it and save on the copy (and requisite surface-locking involved). 1.35 +#define MINIMUM_TILE_COPY_AREA (1.f/16.f) 1.36 + 1.37 +#ifdef GFX_TILEDLAYER_DEBUG_OVERLAY 1.38 +#include "cairo.h" 1.39 +#include <sstream> 1.40 +using mozilla::layers::Layer; 1.41 +static void DrawDebugOverlay(mozilla::gfx::DrawTarget* dt, int x, int y, int width, int height) 1.42 +{ 1.43 + gfxContext c(dt); 1.44 + 1.45 + // Draw border 1.46 + c.NewPath(); 1.47 + c.SetDeviceColor(gfxRGBA(0.0, 0.0, 0.0, 1.0)); 1.48 + c.Rectangle(gfxRect(0, 0, width, height)); 1.49 + c.Stroke(); 1.50 + 1.51 + // Build tile description 1.52 + std::stringstream ss; 1.53 + ss << x << ", " << y; 1.54 + 1.55 + // Draw text using cairo toy text API 1.56 + cairo_t* cr = c.GetCairo(); 1.57 + cairo_set_font_size(cr, 25); 1.58 + cairo_text_extents_t extents; 1.59 + cairo_text_extents(cr, ss.str().c_str(), &extents); 1.60 + 1.61 + int textWidth = extents.width + 6; 1.62 + 1.63 + c.NewPath(); 1.64 + c.SetDeviceColor(gfxRGBA(0.0, 0.0, 0.0, 1.0)); 1.65 + c.Rectangle(gfxRect(gfxPoint(2,2),gfxSize(textWidth, 30))); 1.66 + c.Fill(); 1.67 + 1.68 + c.NewPath(); 1.69 + c.SetDeviceColor(gfxRGBA(1.0, 0.0, 0.0, 1.0)); 1.70 + c.Rectangle(gfxRect(gfxPoint(2,2),gfxSize(textWidth, 30))); 1.71 + c.Stroke(); 1.72 + 1.73 + c.NewPath(); 1.74 + cairo_move_to(cr, 4, 28); 1.75 + cairo_show_text(cr, ss.str().c_str()); 1.76 + 1.77 +} 1.78 + 1.79 +#endif 1.80 + 1.81 +namespace mozilla { 1.82 + 1.83 +using namespace gfx; 1.84 + 1.85 +namespace layers { 1.86 + 1.87 + 1.88 +TiledContentClient::TiledContentClient(ClientTiledThebesLayer* aThebesLayer, 1.89 + ClientLayerManager* aManager) 1.90 + : CompositableClient(aManager->AsShadowForwarder()) 1.91 +{ 1.92 + MOZ_COUNT_CTOR(TiledContentClient); 1.93 + 1.94 + mTiledBuffer = ClientTiledLayerBuffer(aThebesLayer, this, aManager, 1.95 + &mSharedFrameMetricsHelper); 1.96 + mLowPrecisionTiledBuffer = ClientTiledLayerBuffer(aThebesLayer, this, aManager, 1.97 + &mSharedFrameMetricsHelper); 1.98 + 1.99 + mLowPrecisionTiledBuffer.SetResolution(gfxPrefs::LowPrecisionResolution()/1000.f); 1.100 +} 1.101 + 1.102 +void 1.103 +TiledContentClient::ClearCachedResources() 1.104 +{ 1.105 + mTiledBuffer.DiscardBackBuffers(); 1.106 + mLowPrecisionTiledBuffer.DiscardBackBuffers(); 1.107 +} 1.108 + 1.109 +void 1.110 +TiledContentClient::UseTiledLayerBuffer(TiledBufferType aType) 1.111 +{ 1.112 + ClientTiledLayerBuffer* buffer = aType == LOW_PRECISION_TILED_BUFFER 1.113 + ? &mLowPrecisionTiledBuffer 1.114 + : &mTiledBuffer; 1.115 + 1.116 + // Take a ReadLock on behalf of the TiledContentHost. This 1.117 + // reference will be adopted when the descriptor is opened in 1.118 + // TiledLayerBufferComposite. 1.119 + buffer->ReadLock(); 1.120 + 1.121 + mForwarder->UseTiledLayerBuffer(this, buffer->GetSurfaceDescriptorTiles()); 1.122 + buffer->ClearPaintedRegion(); 1.123 +} 1.124 + 1.125 +SharedFrameMetricsHelper::SharedFrameMetricsHelper() 1.126 + : mLastProgressiveUpdateWasLowPrecision(false) 1.127 + , mProgressiveUpdateWasInDanger(false) 1.128 +{ 1.129 + MOZ_COUNT_CTOR(SharedFrameMetricsHelper); 1.130 +} 1.131 + 1.132 +SharedFrameMetricsHelper::~SharedFrameMetricsHelper() 1.133 +{ 1.134 + MOZ_COUNT_DTOR(SharedFrameMetricsHelper); 1.135 +} 1.136 + 1.137 +static inline bool 1.138 +FuzzyEquals(float a, float b) { 1.139 + return (fabsf(a - b) < 1e-6); 1.140 +} 1.141 + 1.142 +bool 1.143 +SharedFrameMetricsHelper::UpdateFromCompositorFrameMetrics( 1.144 + ContainerLayer* aLayer, 1.145 + bool aHasPendingNewThebesContent, 1.146 + bool aLowPrecision, 1.147 + ParentLayerRect& aCompositionBounds, 1.148 + CSSToParentLayerScale& aZoom) 1.149 +{ 1.150 + MOZ_ASSERT(aLayer); 1.151 + 1.152 + CompositorChild* compositor = CompositorChild::Get(); 1.153 + 1.154 + if (!compositor) { 1.155 + FindFallbackContentFrameMetrics(aLayer, aCompositionBounds, aZoom); 1.156 + return false; 1.157 + } 1.158 + 1.159 + const FrameMetrics& contentMetrics = aLayer->GetFrameMetrics(); 1.160 + FrameMetrics compositorMetrics; 1.161 + 1.162 + if (!compositor->LookupCompositorFrameMetrics(contentMetrics.GetScrollId(), 1.163 + compositorMetrics)) { 1.164 + FindFallbackContentFrameMetrics(aLayer, aCompositionBounds, aZoom); 1.165 + return false; 1.166 + } 1.167 + 1.168 + aCompositionBounds = ParentLayerRect(compositorMetrics.mCompositionBounds); 1.169 + aZoom = compositorMetrics.GetZoomToParent(); 1.170 + 1.171 + // Reset the checkerboard risk flag when switching to low precision 1.172 + // rendering. 1.173 + if (aLowPrecision && !mLastProgressiveUpdateWasLowPrecision) { 1.174 + // Skip low precision rendering until we're at risk of checkerboarding. 1.175 + if (!mProgressiveUpdateWasInDanger) { 1.176 + return true; 1.177 + } 1.178 + mProgressiveUpdateWasInDanger = false; 1.179 + } 1.180 + mLastProgressiveUpdateWasLowPrecision = aLowPrecision; 1.181 + 1.182 + // Always abort updates if the resolution has changed. There's no use 1.183 + // in drawing at the incorrect resolution. 1.184 + if (!FuzzyEquals(compositorMetrics.GetZoom().scale, contentMetrics.GetZoom().scale)) { 1.185 + return true; 1.186 + } 1.187 + 1.188 + // Never abort drawing if we can't be sure we've sent a more recent 1.189 + // display-port. If we abort updating when we shouldn't, we can end up 1.190 + // with blank regions on the screen and we open up the risk of entering 1.191 + // an endless updating cycle. 1.192 + if (fabsf(contentMetrics.GetScrollOffset().x - compositorMetrics.GetScrollOffset().x) <= 2 && 1.193 + fabsf(contentMetrics.GetScrollOffset().y - compositorMetrics.GetScrollOffset().y) <= 2 && 1.194 + fabsf(contentMetrics.mDisplayPort.x - compositorMetrics.mDisplayPort.x) <= 2 && 1.195 + fabsf(contentMetrics.mDisplayPort.y - compositorMetrics.mDisplayPort.y) <= 2 && 1.196 + fabsf(contentMetrics.mDisplayPort.width - compositorMetrics.mDisplayPort.width) <= 2 && 1.197 + fabsf(contentMetrics.mDisplayPort.height - compositorMetrics.mDisplayPort.height)) { 1.198 + return false; 1.199 + } 1.200 + 1.201 + // When not a low precision pass and the page is in danger of checker boarding 1.202 + // abort update. 1.203 + if (!aLowPrecision && !mProgressiveUpdateWasInDanger) { 1.204 + if (AboutToCheckerboard(contentMetrics, compositorMetrics)) { 1.205 + mProgressiveUpdateWasInDanger = true; 1.206 + return true; 1.207 + } 1.208 + } 1.209 + 1.210 + // Abort drawing stale low-precision content if there's a more recent 1.211 + // display-port in the pipeline. 1.212 + if (aLowPrecision && !aHasPendingNewThebesContent) { 1.213 + return true; 1.214 + } 1.215 + 1.216 + return false; 1.217 +} 1.218 + 1.219 +void 1.220 +SharedFrameMetricsHelper::FindFallbackContentFrameMetrics(ContainerLayer* aLayer, 1.221 + ParentLayerRect& aCompositionBounds, 1.222 + CSSToParentLayerScale& aZoom) { 1.223 + if (!aLayer) { 1.224 + return; 1.225 + } 1.226 + 1.227 + ContainerLayer* layer = aLayer; 1.228 + const FrameMetrics* contentMetrics = &(layer->GetFrameMetrics()); 1.229 + 1.230 + // Walk up the layer tree until a valid composition bounds is found 1.231 + while (layer && contentMetrics->mCompositionBounds.IsEmpty()) { 1.232 + layer = layer->GetParent(); 1.233 + contentMetrics = layer ? &(layer->GetFrameMetrics()) : contentMetrics; 1.234 + } 1.235 + 1.236 + MOZ_ASSERT(!contentMetrics->mCompositionBounds.IsEmpty()); 1.237 + 1.238 + aCompositionBounds = ParentLayerRect(contentMetrics->mCompositionBounds); 1.239 + aZoom = contentMetrics->GetZoomToParent(); // TODO(botond): double-check this 1.240 + return; 1.241 +} 1.242 + 1.243 +bool 1.244 +SharedFrameMetricsHelper::AboutToCheckerboard(const FrameMetrics& aContentMetrics, 1.245 + const FrameMetrics& aCompositorMetrics) 1.246 +{ 1.247 + return !aContentMetrics.mDisplayPort.Contains(aCompositorMetrics.CalculateCompositedRectInCssPixels() - aCompositorMetrics.GetScrollOffset()); 1.248 +} 1.249 + 1.250 +ClientTiledLayerBuffer::ClientTiledLayerBuffer(ClientTiledThebesLayer* aThebesLayer, 1.251 + CompositableClient* aCompositableClient, 1.252 + ClientLayerManager* aManager, 1.253 + SharedFrameMetricsHelper* aHelper) 1.254 + : mThebesLayer(aThebesLayer) 1.255 + , mCompositableClient(aCompositableClient) 1.256 + , mManager(aManager) 1.257 + , mLastPaintOpaque(false) 1.258 + , mSharedFrameMetricsHelper(aHelper) 1.259 +{ 1.260 +} 1.261 + 1.262 +bool 1.263 +ClientTiledLayerBuffer::HasFormatChanged() const 1.264 +{ 1.265 + return mThebesLayer->CanUseOpaqueSurface() != mLastPaintOpaque; 1.266 +} 1.267 + 1.268 + 1.269 +gfxContentType 1.270 +ClientTiledLayerBuffer::GetContentType() const 1.271 +{ 1.272 + if (mThebesLayer->CanUseOpaqueSurface()) { 1.273 + return gfxContentType::COLOR; 1.274 + } else { 1.275 + return gfxContentType::COLOR_ALPHA; 1.276 + } 1.277 +} 1.278 + 1.279 +gfxMemorySharedReadLock::gfxMemorySharedReadLock() 1.280 + : mReadCount(1) 1.281 +{ 1.282 + MOZ_COUNT_CTOR(gfxMemorySharedReadLock); 1.283 +} 1.284 + 1.285 +gfxMemorySharedReadLock::~gfxMemorySharedReadLock() 1.286 +{ 1.287 + MOZ_COUNT_DTOR(gfxMemorySharedReadLock); 1.288 +} 1.289 + 1.290 +int32_t 1.291 +gfxMemorySharedReadLock::ReadLock() 1.292 +{ 1.293 + NS_ASSERT_OWNINGTHREAD(gfxMemorySharedReadLock); 1.294 + 1.295 + return PR_ATOMIC_INCREMENT(&mReadCount); 1.296 +} 1.297 + 1.298 +int32_t 1.299 +gfxMemorySharedReadLock::ReadUnlock() 1.300 +{ 1.301 + int32_t readCount = PR_ATOMIC_DECREMENT(&mReadCount); 1.302 + NS_ASSERTION(readCount >= 0, "ReadUnlock called without ReadLock."); 1.303 + 1.304 + return readCount; 1.305 +} 1.306 + 1.307 +int32_t 1.308 +gfxMemorySharedReadLock::GetReadCount() 1.309 +{ 1.310 + NS_ASSERT_OWNINGTHREAD(gfxMemorySharedReadLock); 1.311 + return mReadCount; 1.312 +} 1.313 + 1.314 +gfxShmSharedReadLock::gfxShmSharedReadLock(ISurfaceAllocator* aAllocator) 1.315 + : mAllocator(aAllocator) 1.316 + , mAllocSuccess(false) 1.317 +{ 1.318 + MOZ_COUNT_CTOR(gfxShmSharedReadLock); 1.319 + MOZ_ASSERT(mAllocator); 1.320 + if (mAllocator) { 1.321 +#define MOZ_ALIGN_WORD(x) (((x) + 3) & ~3) 1.322 + if (mAllocator->AllocShmemSection(MOZ_ALIGN_WORD(sizeof(ShmReadLockInfo)), &mShmemSection)) { 1.323 + ShmReadLockInfo* info = GetShmReadLockInfoPtr(); 1.324 + info->readCount = 1; 1.325 + mAllocSuccess = true; 1.326 + } 1.327 + } 1.328 +} 1.329 + 1.330 +gfxShmSharedReadLock::~gfxShmSharedReadLock() 1.331 +{ 1.332 + MOZ_COUNT_DTOR(gfxShmSharedReadLock); 1.333 +} 1.334 + 1.335 +int32_t 1.336 +gfxShmSharedReadLock::ReadLock() { 1.337 + NS_ASSERT_OWNINGTHREAD(gfxShmSharedReadLock); 1.338 + if (!mAllocSuccess) { 1.339 + return 0; 1.340 + } 1.341 + ShmReadLockInfo* info = GetShmReadLockInfoPtr(); 1.342 + return PR_ATOMIC_INCREMENT(&info->readCount); 1.343 +} 1.344 + 1.345 +int32_t 1.346 +gfxShmSharedReadLock::ReadUnlock() { 1.347 + if (!mAllocSuccess) { 1.348 + return 0; 1.349 + } 1.350 + ShmReadLockInfo* info = GetShmReadLockInfoPtr(); 1.351 + int32_t readCount = PR_ATOMIC_DECREMENT(&info->readCount); 1.352 + NS_ASSERTION(readCount >= 0, "ReadUnlock called without a ReadLock."); 1.353 + if (readCount <= 0) { 1.354 + mAllocator->FreeShmemSection(mShmemSection); 1.355 + } 1.356 + return readCount; 1.357 +} 1.358 + 1.359 +int32_t 1.360 +gfxShmSharedReadLock::GetReadCount() { 1.361 + NS_ASSERT_OWNINGTHREAD(gfxShmSharedReadLock); 1.362 + if (!mAllocSuccess) { 1.363 + return 0; 1.364 + } 1.365 + ShmReadLockInfo* info = GetShmReadLockInfoPtr(); 1.366 + return info->readCount; 1.367 +} 1.368 + 1.369 +// Placeholder 1.370 +TileClient::TileClient() 1.371 + : mBackBuffer(nullptr) 1.372 + , mFrontBuffer(nullptr) 1.373 + , mBackLock(nullptr) 1.374 + , mFrontLock(nullptr) 1.375 +{ 1.376 +} 1.377 + 1.378 +TileClient::TileClient(const TileClient& o) 1.379 +{ 1.380 + mBackBuffer = o.mBackBuffer; 1.381 + mFrontBuffer = o.mFrontBuffer; 1.382 + mBackLock = o.mBackLock; 1.383 + mFrontLock = o.mFrontLock; 1.384 +#ifdef GFX_TILEDLAYER_DEBUG_OVERLAY 1.385 + mLastUpdate = o.mLastUpdate; 1.386 +#endif 1.387 + mManager = o.mManager; 1.388 + mInvalidFront = o.mInvalidFront; 1.389 + mInvalidBack = o.mInvalidBack; 1.390 +} 1.391 + 1.392 +TileClient& 1.393 +TileClient::operator=(const TileClient& o) 1.394 +{ 1.395 + if (this == &o) return *this; 1.396 + mBackBuffer = o.mBackBuffer; 1.397 + mFrontBuffer = o.mFrontBuffer; 1.398 + mBackLock = o.mBackLock; 1.399 + mFrontLock = o.mFrontLock; 1.400 +#ifdef GFX_TILEDLAYER_DEBUG_OVERLAY 1.401 + mLastUpdate = o.mLastUpdate; 1.402 +#endif 1.403 + mManager = o.mManager; 1.404 + mInvalidFront = o.mInvalidFront; 1.405 + mInvalidBack = o.mInvalidBack; 1.406 + return *this; 1.407 +} 1.408 + 1.409 + 1.410 +void 1.411 +TileClient::Flip() 1.412 +{ 1.413 + RefPtr<TextureClient> frontBuffer = mFrontBuffer; 1.414 + mFrontBuffer = mBackBuffer; 1.415 + mBackBuffer = frontBuffer; 1.416 + RefPtr<gfxSharedReadLock> frontLock = mFrontLock; 1.417 + mFrontLock = mBackLock; 1.418 + mBackLock = frontLock; 1.419 + nsIntRegion invalidFront = mInvalidFront; 1.420 + mInvalidFront = mInvalidBack; 1.421 + mInvalidBack = invalidFront; 1.422 +} 1.423 + 1.424 +void 1.425 +TileClient::ValidateBackBufferFromFront(const nsIntRegion& aDirtyRegion, 1.426 + bool aCanRerasterizeValidRegion) 1.427 +{ 1.428 + if (mBackBuffer && mFrontBuffer) { 1.429 + gfx::IntSize tileSize = mFrontBuffer->GetSize(); 1.430 + const nsIntRect tileRect = nsIntRect(0, 0, tileSize.width, tileSize.height); 1.431 + 1.432 + if (aDirtyRegion.Contains(tileRect)) { 1.433 + // The dirty region means that we no longer need the front buffer, so 1.434 + // discard it. 1.435 + DiscardFrontBuffer(); 1.436 + } else { 1.437 + // Region that needs copying. 1.438 + nsIntRegion regionToCopy = mInvalidBack; 1.439 + 1.440 + regionToCopy.Sub(regionToCopy, aDirtyRegion); 1.441 + 1.442 + if (regionToCopy.IsEmpty() || 1.443 + (aCanRerasterizeValidRegion && 1.444 + regionToCopy.Area() < tileSize.width * tileSize.height * MINIMUM_TILE_COPY_AREA)) { 1.445 + // Just redraw it all. 1.446 + return; 1.447 + } 1.448 + 1.449 + if (!mFrontBuffer->Lock(OPEN_READ)) { 1.450 + NS_WARNING("Failed to lock the tile's front buffer"); 1.451 + return; 1.452 + } 1.453 + TextureClientAutoUnlock autoFront(mFrontBuffer); 1.454 + 1.455 + if (!mBackBuffer->Lock(OPEN_WRITE)) { 1.456 + NS_WARNING("Failed to lock the tile's back buffer"); 1.457 + return; 1.458 + } 1.459 + TextureClientAutoUnlock autoBack(mBackBuffer); 1.460 + 1.461 + // Copy the bounding rect of regionToCopy. As tiles are quite small, it 1.462 + // is unlikely that we'd save much by copying each individual rect of the 1.463 + // region, but we can reevaluate this if it becomes an issue. 1.464 + const nsIntRect rectToCopy = regionToCopy.GetBounds(); 1.465 + gfx::IntRect gfxRectToCopy(rectToCopy.x, rectToCopy.y, rectToCopy.width, rectToCopy.height); 1.466 + gfx::IntPoint gfxRectToCopyTopLeft = gfxRectToCopy.TopLeft(); 1.467 + mFrontBuffer->CopyToTextureClient(mBackBuffer, &gfxRectToCopy, &gfxRectToCopyTopLeft); 1.468 + 1.469 + mInvalidBack.SetEmpty(); 1.470 + } 1.471 + } 1.472 +} 1.473 + 1.474 +void 1.475 +TileClient::DiscardFrontBuffer() 1.476 +{ 1.477 + if (mFrontBuffer) { 1.478 + MOZ_ASSERT(mFrontLock); 1.479 + mManager->GetTexturePool(mFrontBuffer->GetFormat())->ReturnTextureClientDeferred(mFrontBuffer); 1.480 + mFrontLock->ReadUnlock(); 1.481 + mFrontBuffer = nullptr; 1.482 + mFrontLock = nullptr; 1.483 + } 1.484 +} 1.485 + 1.486 +void 1.487 +TileClient::DiscardBackBuffer() 1.488 +{ 1.489 + if (mBackBuffer) { 1.490 + MOZ_ASSERT(mBackLock); 1.491 + if (!mBackBuffer->ImplementsLocking() && mBackLock->GetReadCount() > 1) { 1.492 + // Our current back-buffer is still locked by the compositor. This can occur 1.493 + // when the client is producing faster than the compositor can consume. In 1.494 + // this case we just want to drop it and not return it to the pool. 1.495 + mManager->GetTexturePool(mBackBuffer->GetFormat())->ReportClientLost(); 1.496 + } else { 1.497 + mManager->GetTexturePool(mBackBuffer->GetFormat())->ReturnTextureClient(mBackBuffer); 1.498 + } 1.499 + mBackLock->ReadUnlock(); 1.500 + mBackBuffer = nullptr; 1.501 + mBackLock = nullptr; 1.502 + } 1.503 +} 1.504 + 1.505 +TextureClient* 1.506 +TileClient::GetBackBuffer(const nsIntRegion& aDirtyRegion, TextureClientPool *aPool, bool *aCreatedTextureClient, bool aCanRerasterizeValidRegion) 1.507 +{ 1.508 + // Try to re-use the front-buffer if possible 1.509 + if (mFrontBuffer && 1.510 + mFrontBuffer->HasInternalBuffer() && 1.511 + mFrontLock->GetReadCount() == 1) { 1.512 + // If we had a backbuffer we no longer care about it since we'll 1.513 + // re-use the front buffer. 1.514 + DiscardBackBuffer(); 1.515 + Flip(); 1.516 + return mBackBuffer; 1.517 + } 1.518 + 1.519 + if (!mBackBuffer || 1.520 + mBackLock->GetReadCount() > 1) { 1.521 + if (mBackBuffer) { 1.522 + // Our current back-buffer is still locked by the compositor. This can occur 1.523 + // when the client is producing faster than the compositor can consume. In 1.524 + // this case we just want to drop it and not return it to the pool. 1.525 + aPool->ReportClientLost(); 1.526 + } 1.527 + mBackBuffer = aPool->GetTextureClient(); 1.528 + // Create a lock for our newly created back-buffer. 1.529 + if (gfxPlatform::GetPlatform()->PreferMemoryOverShmem()) { 1.530 + // If our compositor is in the same process, we can save some cycles by not 1.531 + // using shared memory. 1.532 + mBackLock = new gfxMemorySharedReadLock(); 1.533 + } else { 1.534 + mBackLock = new gfxShmSharedReadLock(mManager->AsShadowForwarder()); 1.535 + } 1.536 + 1.537 + MOZ_ASSERT(mBackLock->IsValid()); 1.538 + 1.539 + *aCreatedTextureClient = true; 1.540 + mInvalidBack = nsIntRect(0, 0, mBackBuffer->GetSize().width, mBackBuffer->GetSize().height); 1.541 + } 1.542 + 1.543 + ValidateBackBufferFromFront(aDirtyRegion, aCanRerasterizeValidRegion); 1.544 + 1.545 + return mBackBuffer; 1.546 +} 1.547 + 1.548 +TileDescriptor 1.549 +TileClient::GetTileDescriptor() 1.550 +{ 1.551 + if (IsPlaceholderTile()) { 1.552 + return PlaceholderTileDescriptor(); 1.553 + } 1.554 + MOZ_ASSERT(mFrontLock); 1.555 + if (mFrontLock->GetType() == gfxSharedReadLock::TYPE_MEMORY) { 1.556 + // AddRef here and Release when receiving on the host side to make sure the 1.557 + // reference count doesn't go to zero before the host receives the message. 1.558 + // see TiledLayerBufferComposite::TiledLayerBufferComposite 1.559 + mFrontLock->AddRef(); 1.560 + } 1.561 + 1.562 + if (mFrontLock->GetType() == gfxSharedReadLock::TYPE_MEMORY) { 1.563 + return TexturedTileDescriptor(nullptr, mFrontBuffer->GetIPDLActor(), 1.564 + TileLock(uintptr_t(mFrontLock.get()))); 1.565 + } else { 1.566 + gfxShmSharedReadLock *lock = static_cast<gfxShmSharedReadLock*>(mFrontLock.get()); 1.567 + return TexturedTileDescriptor(nullptr, mFrontBuffer->GetIPDLActor(), 1.568 + TileLock(lock->GetShmemSection())); 1.569 + } 1.570 +} 1.571 + 1.572 +void 1.573 +ClientTiledLayerBuffer::ReadUnlock() { 1.574 + for (size_t i = 0; i < mRetainedTiles.Length(); i++) { 1.575 + if (mRetainedTiles[i].IsPlaceholderTile()) continue; 1.576 + mRetainedTiles[i].ReadUnlock(); 1.577 + } 1.578 +} 1.579 + 1.580 +void 1.581 +ClientTiledLayerBuffer::ReadLock() { 1.582 + for (size_t i = 0; i < mRetainedTiles.Length(); i++) { 1.583 + if (mRetainedTiles[i].IsPlaceholderTile()) continue; 1.584 + mRetainedTiles[i].ReadLock(); 1.585 + } 1.586 +} 1.587 + 1.588 +void 1.589 +ClientTiledLayerBuffer::Release() 1.590 +{ 1.591 + for (size_t i = 0; i < mRetainedTiles.Length(); i++) { 1.592 + if (mRetainedTiles[i].IsPlaceholderTile()) continue; 1.593 + mRetainedTiles[i].Release(); 1.594 + } 1.595 +} 1.596 + 1.597 +void 1.598 +ClientTiledLayerBuffer::DiscardBackBuffers() 1.599 +{ 1.600 + for (size_t i = 0; i < mRetainedTiles.Length(); i++) { 1.601 + if (mRetainedTiles[i].IsPlaceholderTile()) continue; 1.602 + mRetainedTiles[i].DiscardBackBuffer(); 1.603 + } 1.604 +} 1.605 + 1.606 +SurfaceDescriptorTiles 1.607 +ClientTiledLayerBuffer::GetSurfaceDescriptorTiles() 1.608 +{ 1.609 + InfallibleTArray<TileDescriptor> tiles; 1.610 + 1.611 + for (size_t i = 0; i < mRetainedTiles.Length(); i++) { 1.612 + TileDescriptor tileDesc; 1.613 + if (mRetainedTiles.SafeElementAt(i, GetPlaceholderTile()) == GetPlaceholderTile()) { 1.614 + tileDesc = PlaceholderTileDescriptor(); 1.615 + } else { 1.616 + tileDesc = mRetainedTiles[i].GetTileDescriptor(); 1.617 + } 1.618 + tiles.AppendElement(tileDesc); 1.619 + } 1.620 + return SurfaceDescriptorTiles(mValidRegion, mPaintedRegion, 1.621 + tiles, mRetainedWidth, mRetainedHeight, 1.622 + mResolution, mFrameResolution.scale); 1.623 +} 1.624 + 1.625 +void 1.626 +ClientTiledLayerBuffer::PaintThebes(const nsIntRegion& aNewValidRegion, 1.627 + const nsIntRegion& aPaintRegion, 1.628 + LayerManager::DrawThebesLayerCallback aCallback, 1.629 + void* aCallbackData) 1.630 +{ 1.631 + mCallback = aCallback; 1.632 + mCallbackData = aCallbackData; 1.633 + 1.634 +#ifdef GFX_TILEDLAYER_PREF_WARNINGS 1.635 + long start = PR_IntervalNow(); 1.636 +#endif 1.637 + 1.638 + // If this region is empty XMost() - 1 will give us a negative value. 1.639 + NS_ASSERTION(!aPaintRegion.GetBounds().IsEmpty(), "Empty paint region\n"); 1.640 + 1.641 + bool useSinglePaintBuffer = UseSinglePaintBuffer(); 1.642 + // XXX The single-tile case doesn't work at the moment, see bug 850396 1.643 + /* 1.644 + if (useSinglePaintBuffer) { 1.645 + // Check if the paint only spans a single tile. If that's 1.646 + // the case there's no point in using a single paint buffer. 1.647 + nsIntRect paintBounds = aPaintRegion.GetBounds(); 1.648 + useSinglePaintBuffer = GetTileStart(paintBounds.x) != 1.649 + GetTileStart(paintBounds.XMost() - 1) || 1.650 + GetTileStart(paintBounds.y) != 1.651 + GetTileStart(paintBounds.YMost() - 1); 1.652 + } 1.653 + */ 1.654 + 1.655 + if (useSinglePaintBuffer) { 1.656 + nsRefPtr<gfxContext> ctxt; 1.657 + 1.658 + const nsIntRect bounds = aPaintRegion.GetBounds(); 1.659 + { 1.660 + PROFILER_LABEL("ClientTiledLayerBuffer", "PaintThebesSingleBufferAlloc"); 1.661 + gfxImageFormat format = 1.662 + gfxPlatform::GetPlatform()->OptimalFormatForContent( 1.663 + GetContentType()); 1.664 + 1.665 + mSinglePaintDrawTarget = 1.666 + gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget( 1.667 + gfx::IntSize(ceilf(bounds.width * mResolution), 1.668 + ceilf(bounds.height * mResolution)), 1.669 + gfx::ImageFormatToSurfaceFormat(format)); 1.670 + 1.671 + if (!mSinglePaintDrawTarget) { 1.672 + return; 1.673 + } 1.674 + 1.675 + ctxt = new gfxContext(mSinglePaintDrawTarget); 1.676 + 1.677 + mSinglePaintBufferOffset = nsIntPoint(bounds.x, bounds.y); 1.678 + } 1.679 + ctxt->NewPath(); 1.680 + ctxt->Scale(mResolution, mResolution); 1.681 + ctxt->Translate(gfxPoint(-bounds.x, -bounds.y)); 1.682 +#ifdef GFX_TILEDLAYER_PREF_WARNINGS 1.683 + if (PR_IntervalNow() - start > 3) { 1.684 + printf_stderr("Slow alloc %i\n", PR_IntervalNow() - start); 1.685 + } 1.686 + start = PR_IntervalNow(); 1.687 +#endif 1.688 + PROFILER_LABEL("ClientTiledLayerBuffer", "PaintThebesSingleBufferDraw"); 1.689 + 1.690 + mCallback(mThebesLayer, ctxt, aPaintRegion, DrawRegionClip::CLIP_NONE, nsIntRegion(), mCallbackData); 1.691 + } 1.692 + 1.693 +#ifdef GFX_TILEDLAYER_PREF_WARNINGS 1.694 + if (PR_IntervalNow() - start > 30) { 1.695 + const nsIntRect bounds = aPaintRegion.GetBounds(); 1.696 + printf_stderr("Time to draw %i: %i, %i, %i, %i\n", PR_IntervalNow() - start, bounds.x, bounds.y, bounds.width, bounds.height); 1.697 + if (aPaintRegion.IsComplex()) { 1.698 + printf_stderr("Complex region\n"); 1.699 + nsIntRegionRectIterator it(aPaintRegion); 1.700 + for (const nsIntRect* rect = it.Next(); rect != nullptr; rect = it.Next()) { 1.701 + printf_stderr(" rect %i, %i, %i, %i\n", rect->x, rect->y, rect->width, rect->height); 1.702 + } 1.703 + } 1.704 + } 1.705 + start = PR_IntervalNow(); 1.706 +#endif 1.707 + 1.708 + PROFILER_LABEL("ClientTiledLayerBuffer", "PaintThebesUpdate"); 1.709 + Update(aNewValidRegion, aPaintRegion); 1.710 + 1.711 +#ifdef GFX_TILEDLAYER_PREF_WARNINGS 1.712 + if (PR_IntervalNow() - start > 10) { 1.713 + const nsIntRect bounds = aPaintRegion.GetBounds(); 1.714 + printf_stderr("Time to tile %i: %i, %i, %i, %i\n", PR_IntervalNow() - start, bounds.x, bounds.y, bounds.width, bounds.height); 1.715 + } 1.716 +#endif 1.717 + 1.718 + mLastPaintOpaque = mThebesLayer->CanUseOpaqueSurface(); 1.719 + mCallback = nullptr; 1.720 + mCallbackData = nullptr; 1.721 + mSinglePaintDrawTarget = nullptr; 1.722 +} 1.723 + 1.724 +TileClient 1.725 +ClientTiledLayerBuffer::ValidateTile(TileClient aTile, 1.726 + const nsIntPoint& aTileOrigin, 1.727 + const nsIntRegion& aDirtyRegion) 1.728 +{ 1.729 + PROFILER_LABEL("ClientTiledLayerBuffer", "ValidateTile"); 1.730 + 1.731 +#ifdef GFX_TILEDLAYER_PREF_WARNINGS 1.732 + if (aDirtyRegion.IsComplex()) { 1.733 + printf_stderr("Complex region\n"); 1.734 + } 1.735 +#endif 1.736 + 1.737 + if (aTile.IsPlaceholderTile()) { 1.738 + aTile.SetLayerManager(mManager); 1.739 + } 1.740 + 1.741 + // Discard our front and backbuffers if our contents changed. In this case 1.742 + // the calling code will already have taken care of invalidating the entire 1.743 + // layer. 1.744 + if (HasFormatChanged()) { 1.745 + aTile.DiscardBackBuffer(); 1.746 + aTile.DiscardFrontBuffer(); 1.747 + } 1.748 + 1.749 + bool createdTextureClient = false; 1.750 + nsIntRegion offsetScaledDirtyRegion = aDirtyRegion.MovedBy(-aTileOrigin); 1.751 + offsetScaledDirtyRegion.ScaleRoundOut(mResolution, mResolution); 1.752 + 1.753 + bool usingSinglePaintBuffer = !!mSinglePaintDrawTarget; 1.754 + RefPtr<TextureClient> backBuffer = 1.755 + aTile.GetBackBuffer(offsetScaledDirtyRegion, 1.756 + mManager->GetTexturePool(gfxPlatform::GetPlatform()->Optimal2DFormatForContent(GetContentType())), 1.757 + &createdTextureClient, !usingSinglePaintBuffer); 1.758 + 1.759 + if (!backBuffer->Lock(OPEN_READ_WRITE)) { 1.760 + NS_WARNING("Failed to lock tile TextureClient for updating."); 1.761 + aTile.DiscardFrontBuffer(); 1.762 + return aTile; 1.763 + } 1.764 + 1.765 + // We must not keep a reference to the DrawTarget after it has been unlocked, 1.766 + // make sure these are null'd before unlocking as destruction of the context 1.767 + // may cause the target to be flushed. 1.768 + RefPtr<DrawTarget> drawTarget = backBuffer->GetAsDrawTarget(); 1.769 + drawTarget->SetTransform(Matrix()); 1.770 + 1.771 + RefPtr<gfxContext> ctxt = new gfxContext(drawTarget); 1.772 + 1.773 + if (usingSinglePaintBuffer) { 1.774 + // XXX Perhaps we should just copy the bounding rectangle here? 1.775 + RefPtr<gfx::SourceSurface> source = mSinglePaintDrawTarget->Snapshot(); 1.776 + nsIntRegionRectIterator it(aDirtyRegion); 1.777 + for (const nsIntRect* dirtyRect = it.Next(); dirtyRect != nullptr; dirtyRect = it.Next()) { 1.778 +#ifdef GFX_TILEDLAYER_PREF_WARNINGS 1.779 + printf_stderr(" break into subdirtyRect %i, %i, %i, %i\n", 1.780 + dirtyRect->x, dirtyRect->y, dirtyRect->width, dirtyRect->height); 1.781 +#endif 1.782 + gfx::Rect drawRect(dirtyRect->x - aTileOrigin.x, 1.783 + dirtyRect->y - aTileOrigin.y, 1.784 + dirtyRect->width, 1.785 + dirtyRect->height); 1.786 + drawRect.Scale(mResolution); 1.787 + 1.788 + gfx::IntRect copyRect(NS_roundf((dirtyRect->x - mSinglePaintBufferOffset.x) * mResolution), 1.789 + NS_roundf((dirtyRect->y - mSinglePaintBufferOffset.y) * mResolution), 1.790 + drawRect.width, 1.791 + drawRect.height); 1.792 + gfx::IntPoint copyTarget(NS_roundf(drawRect.x), NS_roundf(drawRect.y)); 1.793 + drawTarget->CopySurface(source, copyRect, copyTarget); 1.794 + 1.795 + // Mark the newly updated area as invalid in the front buffer 1.796 + aTile.mInvalidFront.Or(aTile.mInvalidFront, nsIntRect(copyTarget.x, copyTarget.y, copyRect.width, copyRect.height)); 1.797 + } 1.798 + 1.799 + // The new buffer is now validated, remove the dirty region from it. 1.800 + aTile.mInvalidBack.Sub(nsIntRect(0, 0, GetTileSize().width, GetTileSize().height), 1.801 + offsetScaledDirtyRegion); 1.802 + } else { 1.803 + // Area of the full tile... 1.804 + nsIntRegion tileRegion = 1.805 + nsIntRect(aTileOrigin.x, aTileOrigin.y, 1.806 + GetScaledTileSize().width, GetScaledTileSize().height); 1.807 + 1.808 + // Intersect this area with the portion that's dirty. 1.809 + tileRegion = tileRegion.Intersect(aDirtyRegion); 1.810 + 1.811 + // Add the resolution scale to store the dirty region. 1.812 + nsIntPoint unscaledTileOrigin = nsIntPoint(aTileOrigin.x * mResolution, 1.813 + aTileOrigin.y * mResolution); 1.814 + nsIntRegion unscaledTileRegion(tileRegion); 1.815 + unscaledTileRegion.ScaleRoundOut(mResolution, mResolution); 1.816 + 1.817 + // Move invalid areas into scaled layer space. 1.818 + aTile.mInvalidFront.MoveBy(unscaledTileOrigin); 1.819 + aTile.mInvalidBack.MoveBy(unscaledTileOrigin); 1.820 + 1.821 + // Add the area that's going to be redrawn to the invalid area of the 1.822 + // front region. 1.823 + aTile.mInvalidFront.Or(aTile.mInvalidFront, unscaledTileRegion); 1.824 + 1.825 + // Add invalid areas of the backbuffer to the area to redraw. 1.826 + tileRegion.Or(tileRegion, aTile.mInvalidBack); 1.827 + 1.828 + // Move invalid areas back into tile space. 1.829 + aTile.mInvalidFront.MoveBy(-unscaledTileOrigin); 1.830 + 1.831 + // This will be validated now. 1.832 + aTile.mInvalidBack.SetEmpty(); 1.833 + 1.834 + nsIntRect bounds = tileRegion.GetBounds(); 1.835 + bounds.MoveBy(-aTileOrigin); 1.836 + 1.837 + if (GetContentType() != gfxContentType::COLOR) { 1.838 + drawTarget->ClearRect(Rect(bounds.x, bounds.y, bounds.width, bounds.height)); 1.839 + } 1.840 + 1.841 + ctxt->NewPath(); 1.842 + ctxt->Clip(gfxRect(bounds.x, bounds.y, bounds.width, bounds.height)); 1.843 + ctxt->Translate(gfxPoint(-unscaledTileOrigin.x, -unscaledTileOrigin.y)); 1.844 + ctxt->Scale(mResolution, mResolution); 1.845 + mCallback(mThebesLayer, ctxt, 1.846 + tileRegion.GetBounds(), 1.847 + DrawRegionClip::CLIP_NONE, 1.848 + nsIntRegion(), mCallbackData); 1.849 + 1.850 + } 1.851 + 1.852 +#ifdef GFX_TILEDLAYER_DEBUG_OVERLAY 1.853 + DrawDebugOverlay(drawTarget, aTileOrigin.x * mResolution, 1.854 + aTileOrigin.y * mResolution, GetTileLength(), GetTileLength()); 1.855 +#endif 1.856 + 1.857 + ctxt = nullptr; 1.858 + drawTarget = nullptr; 1.859 + 1.860 + backBuffer->Unlock(); 1.861 + 1.862 + aTile.Flip(); 1.863 + 1.864 + if (createdTextureClient) { 1.865 + if (!mCompositableClient->AddTextureClient(backBuffer)) { 1.866 + NS_WARNING("Failed to add tile TextureClient."); 1.867 + aTile.DiscardFrontBuffer(); 1.868 + aTile.DiscardBackBuffer(); 1.869 + return aTile; 1.870 + } 1.871 + } 1.872 + 1.873 + // Note, we don't call UpdatedTexture. The Updated function is called manually 1.874 + // by the TiledContentHost before composition. 1.875 + 1.876 + if (backBuffer->HasInternalBuffer()) { 1.877 + // If our new buffer has an internal buffer, we don't want to keep another 1.878 + // TextureClient around unnecessarily, so discard the back-buffer. 1.879 + aTile.DiscardBackBuffer(); 1.880 + } 1.881 + 1.882 + return aTile; 1.883 +} 1.884 + 1.885 +static LayoutDeviceRect 1.886 +TransformCompositionBounds(const ParentLayerRect& aCompositionBounds, 1.887 + const CSSToParentLayerScale& aZoom, 1.888 + const ParentLayerPoint& aScrollOffset, 1.889 + const CSSToParentLayerScale& aResolution, 1.890 + const gfx3DMatrix& aTransformParentLayerToLayoutDevice) 1.891 +{ 1.892 + // Transform the current composition bounds into ParentLayer coordinates 1.893 + // by compensating for the difference in resolution and subtracting the 1.894 + // old composition bounds origin. 1.895 + ParentLayerRect offsetViewportRect = (aCompositionBounds / aZoom) * aResolution; 1.896 + offsetViewportRect.MoveBy(-aScrollOffset); 1.897 + 1.898 + gfxRect transformedViewport = 1.899 + aTransformParentLayerToLayoutDevice.TransformBounds( 1.900 + gfxRect(offsetViewportRect.x, offsetViewportRect.y, 1.901 + offsetViewportRect.width, offsetViewportRect.height)); 1.902 + 1.903 + return LayoutDeviceRect(transformedViewport.x, 1.904 + transformedViewport.y, 1.905 + transformedViewport.width, 1.906 + transformedViewport.height); 1.907 +} 1.908 + 1.909 +bool 1.910 +ClientTiledLayerBuffer::ComputeProgressiveUpdateRegion(const nsIntRegion& aInvalidRegion, 1.911 + const nsIntRegion& aOldValidRegion, 1.912 + nsIntRegion& aRegionToPaint, 1.913 + BasicTiledLayerPaintData* aPaintData, 1.914 + bool aIsRepeated) 1.915 +{ 1.916 + aRegionToPaint = aInvalidRegion; 1.917 + 1.918 + // If the composition bounds rect is empty, we can't make any sensible 1.919 + // decision about how to update coherently. In this case, just update 1.920 + // everything in one transaction. 1.921 + if (aPaintData->mCompositionBounds.IsEmpty()) { 1.922 + aPaintData->mPaintFinished = true; 1.923 + return false; 1.924 + } 1.925 + 1.926 + // If this is a low precision buffer, we force progressive updates. The 1.927 + // assumption is that the contents is less important, so visual coherency 1.928 + // is lower priority than speed. 1.929 + bool drawingLowPrecision = IsLowPrecision(); 1.930 + 1.931 + // Find out if we have any non-stale content to update. 1.932 + nsIntRegion staleRegion; 1.933 + staleRegion.And(aInvalidRegion, aOldValidRegion); 1.934 + 1.935 + // Find out the current view transform to determine which tiles to draw 1.936 + // first, and see if we should just abort this paint. Aborting is usually 1.937 + // caused by there being an incoming, more relevant paint. 1.938 + ParentLayerRect compositionBounds; 1.939 + CSSToParentLayerScale zoom; 1.940 +#if defined(MOZ_WIDGET_ANDROID) 1.941 + bool abortPaint = mManager->ProgressiveUpdateCallback(!staleRegion.Contains(aInvalidRegion), 1.942 + compositionBounds, zoom, 1.943 + !drawingLowPrecision); 1.944 +#else 1.945 + MOZ_ASSERT(mSharedFrameMetricsHelper); 1.946 + 1.947 + ContainerLayer* parent = mThebesLayer->AsLayer()->GetParent(); 1.948 + 1.949 + bool abortPaint = 1.950 + mSharedFrameMetricsHelper->UpdateFromCompositorFrameMetrics( 1.951 + parent, 1.952 + !staleRegion.Contains(aInvalidRegion), 1.953 + drawingLowPrecision, 1.954 + compositionBounds, 1.955 + zoom); 1.956 +#endif 1.957 + 1.958 + if (abortPaint) { 1.959 + // We ignore if front-end wants to abort if this is the first, 1.960 + // non-low-precision paint, as in that situation, we're about to override 1.961 + // front-end's page/viewport metrics. 1.962 + if (!aPaintData->mFirstPaint || drawingLowPrecision) { 1.963 + PROFILER_LABEL("ContentClient", "Abort painting"); 1.964 + aRegionToPaint.SetEmpty(); 1.965 + return aIsRepeated; 1.966 + } 1.967 + } 1.968 + 1.969 + // Transform the composition bounds, which is in the ParentLayer coordinates 1.970 + // of the nearest ContainerLayer with a valid displayport to LayoutDevice 1.971 + // coordinates relative to this layer. 1.972 + LayoutDeviceRect transformedCompositionBounds = 1.973 + TransformCompositionBounds(compositionBounds, zoom, aPaintData->mScrollOffset, 1.974 + aPaintData->mResolution, aPaintData->mTransformParentLayerToLayoutDevice); 1.975 + 1.976 + // Paint tiles that have stale content or that intersected with the screen 1.977 + // at the time of issuing the draw command in a single transaction first. 1.978 + // This is to avoid rendering glitches on animated page content, and when 1.979 + // layers change size/shape. 1.980 + LayoutDeviceRect typedCoherentUpdateRect = 1.981 + transformedCompositionBounds.Intersect(aPaintData->mCompositionBounds); 1.982 + 1.983 + // Offset by the viewport origin, as the composition bounds are stored in 1.984 + // Layer space and not LayoutDevice space. 1.985 + typedCoherentUpdateRect.MoveBy(aPaintData->mViewport.TopLeft()); 1.986 + 1.987 + // Convert to untyped to intersect with the invalid region. 1.988 + nsIntRect roundedCoherentUpdateRect = 1.989 + LayoutDeviceIntRect::ToUntyped(RoundedOut(typedCoherentUpdateRect)); 1.990 + 1.991 + aRegionToPaint.And(aInvalidRegion, roundedCoherentUpdateRect); 1.992 + aRegionToPaint.Or(aRegionToPaint, staleRegion); 1.993 + bool drawingStale = !aRegionToPaint.IsEmpty(); 1.994 + if (!drawingStale) { 1.995 + aRegionToPaint = aInvalidRegion; 1.996 + } 1.997 + 1.998 + // Prioritise tiles that are currently visible on the screen. 1.999 + bool paintVisible = false; 1.1000 + if (aRegionToPaint.Intersects(roundedCoherentUpdateRect)) { 1.1001 + aRegionToPaint.And(aRegionToPaint, roundedCoherentUpdateRect); 1.1002 + paintVisible = true; 1.1003 + } 1.1004 + 1.1005 + // Paint area that's visible and overlaps previously valid content to avoid 1.1006 + // visible glitches in animated elements, such as gifs. 1.1007 + bool paintInSingleTransaction = paintVisible && (drawingStale || aPaintData->mFirstPaint); 1.1008 + 1.1009 + // The following code decides what order to draw tiles in, based on the 1.1010 + // current scroll direction of the primary scrollable layer. 1.1011 + NS_ASSERTION(!aRegionToPaint.IsEmpty(), "Unexpectedly empty paint region!"); 1.1012 + nsIntRect paintBounds = aRegionToPaint.GetBounds(); 1.1013 + 1.1014 + int startX, incX, startY, incY; 1.1015 + gfx::IntSize scaledTileSize = GetScaledTileSize(); 1.1016 + if (aPaintData->mScrollOffset.x >= aPaintData->mLastScrollOffset.x) { 1.1017 + startX = RoundDownToTileEdge(paintBounds.x, scaledTileSize.width); 1.1018 + incX = scaledTileSize.width; 1.1019 + } else { 1.1020 + startX = RoundDownToTileEdge(paintBounds.XMost() - 1, scaledTileSize.width); 1.1021 + incX = -scaledTileSize.width; 1.1022 + } 1.1023 + 1.1024 + if (aPaintData->mScrollOffset.y >= aPaintData->mLastScrollOffset.y) { 1.1025 + startY = RoundDownToTileEdge(paintBounds.y, scaledTileSize.height); 1.1026 + incY = scaledTileSize.height; 1.1027 + } else { 1.1028 + startY = RoundDownToTileEdge(paintBounds.YMost() - 1, scaledTileSize.height); 1.1029 + incY = -scaledTileSize.height; 1.1030 + } 1.1031 + 1.1032 + // Find a tile to draw. 1.1033 + nsIntRect tileBounds(startX, startY, scaledTileSize.width, scaledTileSize.height); 1.1034 + int32_t scrollDiffX = aPaintData->mScrollOffset.x - aPaintData->mLastScrollOffset.x; 1.1035 + int32_t scrollDiffY = aPaintData->mScrollOffset.y - aPaintData->mLastScrollOffset.y; 1.1036 + // This loop will always terminate, as there is at least one tile area 1.1037 + // along the first/last row/column intersecting with regionToPaint, or its 1.1038 + // bounds would have been smaller. 1.1039 + while (true) { 1.1040 + aRegionToPaint.And(aInvalidRegion, tileBounds); 1.1041 + if (!aRegionToPaint.IsEmpty()) { 1.1042 + break; 1.1043 + } 1.1044 + if (Abs(scrollDiffY) >= Abs(scrollDiffX)) { 1.1045 + tileBounds.x += incX; 1.1046 + } else { 1.1047 + tileBounds.y += incY; 1.1048 + } 1.1049 + } 1.1050 + 1.1051 + if (!aRegionToPaint.Contains(aInvalidRegion)) { 1.1052 + // The region needed to paint is larger then our progressive chunk size 1.1053 + // therefore update what we want to paint and ask for a new paint transaction. 1.1054 + 1.1055 + // If we need to draw more than one tile to maintain coherency, make 1.1056 + // sure it happens in the same transaction by requesting this work be 1.1057 + // repeated immediately. 1.1058 + // If this is unnecessary, the remaining work will be done tile-by-tile in 1.1059 + // subsequent transactions. 1.1060 + if (!drawingLowPrecision && paintInSingleTransaction) { 1.1061 + return true; 1.1062 + } 1.1063 + 1.1064 + mManager->SetRepeatTransaction(); 1.1065 + return false; 1.1066 + } 1.1067 + 1.1068 + // We're not repeating painting and we've not requested a repeat transaction, 1.1069 + // so the paint is finished. If there's still a separate low precision 1.1070 + // paint to do, it will get marked as unfinished later. 1.1071 + aPaintData->mPaintFinished = true; 1.1072 + return false; 1.1073 +} 1.1074 + 1.1075 +bool 1.1076 +ClientTiledLayerBuffer::ProgressiveUpdate(nsIntRegion& aValidRegion, 1.1077 + nsIntRegion& aInvalidRegion, 1.1078 + const nsIntRegion& aOldValidRegion, 1.1079 + BasicTiledLayerPaintData* aPaintData, 1.1080 + LayerManager::DrawThebesLayerCallback aCallback, 1.1081 + void* aCallbackData) 1.1082 +{ 1.1083 + bool repeat = false; 1.1084 + bool isBufferChanged = false; 1.1085 + do { 1.1086 + // Compute the region that should be updated. Repeat as many times as 1.1087 + // is required. 1.1088 + nsIntRegion regionToPaint; 1.1089 + repeat = ComputeProgressiveUpdateRegion(aInvalidRegion, 1.1090 + aOldValidRegion, 1.1091 + regionToPaint, 1.1092 + aPaintData, 1.1093 + repeat); 1.1094 + 1.1095 + // There's no further work to be done. 1.1096 + if (regionToPaint.IsEmpty()) { 1.1097 + break; 1.1098 + } 1.1099 + 1.1100 + isBufferChanged = true; 1.1101 + 1.1102 + // Keep track of what we're about to refresh. 1.1103 + aValidRegion.Or(aValidRegion, regionToPaint); 1.1104 + 1.1105 + // aValidRegion may have been altered by InvalidateRegion, but we still 1.1106 + // want to display stale content until it gets progressively updated. 1.1107 + // Create a region that includes stale content. 1.1108 + nsIntRegion validOrStale; 1.1109 + validOrStale.Or(aValidRegion, aOldValidRegion); 1.1110 + 1.1111 + // Paint the computed region and subtract it from the invalid region. 1.1112 + PaintThebes(validOrStale, regionToPaint, aCallback, aCallbackData); 1.1113 + aInvalidRegion.Sub(aInvalidRegion, regionToPaint); 1.1114 + } while (repeat); 1.1115 + 1.1116 + // Return false if nothing has been drawn, or give what has been drawn 1.1117 + // to the shadow layer to upload. 1.1118 + return isBufferChanged; 1.1119 +} 1.1120 + 1.1121 +} 1.1122 +}