1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #include "mozilla/layers/MultiTiledContentClient.h"
8
9 #include "ClientTiledPaintedLayer.h"
10 #include "mozilla/StaticPrefs_layers.h"
11 #include "mozilla/layers/LayerMetricsWrapper.h"
12
13 namespace mozilla {
14
15 using namespace gfx;
16
17 namespace layers {
18
MultiTiledContentClient(ClientTiledPaintedLayer & aPaintedLayer,ClientLayerManager * aManager)19 MultiTiledContentClient::MultiTiledContentClient(
20 ClientTiledPaintedLayer& aPaintedLayer, ClientLayerManager* aManager)
21 : TiledContentClient(aManager, "Multi"),
22 mTiledBuffer(aPaintedLayer, *this, aManager, &mSharedFrameMetricsHelper),
23 mLowPrecisionTiledBuffer(aPaintedLayer, *this, aManager,
24 &mSharedFrameMetricsHelper) {
25 MOZ_COUNT_CTOR(MultiTiledContentClient);
26 mLowPrecisionTiledBuffer.SetResolution(
27 StaticPrefs::layers_low_precision_resolution());
28 mHasLowPrecision = StaticPrefs::layers_low_precision_buffer();
29 }
30
ClearCachedResources()31 void MultiTiledContentClient::ClearCachedResources() {
32 CompositableClient::ClearCachedResources();
33 mTiledBuffer.DiscardBuffers();
34 mLowPrecisionTiledBuffer.DiscardBuffers();
35 }
36
UpdatedBuffer(TiledBufferType aType)37 void MultiTiledContentClient::UpdatedBuffer(TiledBufferType aType) {
38 ClientMultiTiledLayerBuffer* buffer = aType == LOW_PRECISION_TILED_BUFFER
39 ? &mLowPrecisionTiledBuffer
40 : &mTiledBuffer;
41
42 MOZ_ASSERT(aType != LOW_PRECISION_TILED_BUFFER || mHasLowPrecision);
43
44 mForwarder->UseTiledLayerBuffer(this, buffer->GetSurfaceDescriptorTiles());
45 }
46
ClientMultiTiledLayerBuffer(ClientTiledPaintedLayer & aPaintedLayer,CompositableClient & aCompositableClient,ClientLayerManager * aManager,SharedFrameMetricsHelper * aHelper)47 ClientMultiTiledLayerBuffer::ClientMultiTiledLayerBuffer(
48 ClientTiledPaintedLayer& aPaintedLayer,
49 CompositableClient& aCompositableClient, ClientLayerManager* aManager,
50 SharedFrameMetricsHelper* aHelper)
51 : ClientTiledLayerBuffer(aPaintedLayer, aCompositableClient),
52 mManager(aManager),
53 mCallback(nullptr),
54 mCallbackData(nullptr),
55 mSharedFrameMetricsHelper(aHelper) {}
56
DiscardBuffers()57 void ClientMultiTiledLayerBuffer::DiscardBuffers() {
58 for (TileClient& tile : mRetainedTiles) {
59 tile.DiscardBuffers();
60 }
61 }
62
63 SurfaceDescriptorTiles
GetSurfaceDescriptorTiles()64 ClientMultiTiledLayerBuffer::GetSurfaceDescriptorTiles() {
65 nsTArray<TileDescriptor> tiles;
66
67 for (TileClient& tile : mRetainedTiles) {
68 TileDescriptor tileDesc = tile.GetTileDescriptor();
69 tiles.AppendElement(tileDesc);
70 // Reset the update rect
71 tile.mUpdateRect = IntRect();
72 }
73 return SurfaceDescriptorTiles(
74 mValidRegion, tiles, mTileOrigin, mTileSize, mTiles.mFirst.x,
75 mTiles.mFirst.y, mTiles.mSize.width, mTiles.mSize.height, mResolution,
76 mFrameResolution.xScale, mFrameResolution.yScale,
77 mWasLastPaintProgressive);
78 }
79
PaintThebes(const nsIntRegion & aNewValidRegion,const nsIntRegion & aPaintRegion,const nsIntRegion & aDirtyRegion,LayerManager::DrawPaintedLayerCallback aCallback,void * aCallbackData,TilePaintFlags aFlags)80 void ClientMultiTiledLayerBuffer::PaintThebes(
81 const nsIntRegion& aNewValidRegion, const nsIntRegion& aPaintRegion,
82 const nsIntRegion& aDirtyRegion,
83 LayerManager::DrawPaintedLayerCallback aCallback, void* aCallbackData,
84 TilePaintFlags aFlags) {
85 TILING_LOG("TILING %p: PaintThebes painting region %s\n", &mPaintedLayer,
86 Stringify(aPaintRegion).c_str());
87 TILING_LOG("TILING %p: PaintThebes new valid region %s\n", &mPaintedLayer,
88 Stringify(aNewValidRegion).c_str());
89
90 mCallback = aCallback;
91 mCallbackData = aCallbackData;
92 mWasLastPaintProgressive = !!(aFlags & TilePaintFlags::Progressive);
93
94 #ifdef GFX_TILEDLAYER_PREF_WARNINGS
95 long start = PR_IntervalNow();
96 #endif
97
98 #ifdef GFX_TILEDLAYER_PREF_WARNINGS
99 if (PR_IntervalNow() - start > 30) {
100 const IntRect bounds = aPaintRegion.GetBounds();
101 printf_stderr("Time to draw %i: %i, %i, %i, %i\n", PR_IntervalNow() - start,
102 bounds.x, bounds.y, bounds.width, bounds.height);
103 if (aPaintRegion.IsComplex()) {
104 printf_stderr("Complex region\n");
105 for (auto iter = aPaintRegion.RectIter(); !iter.Done(); iter.Next()) {
106 const IntRect& rect = iter.Get();
107 printf_stderr(" rect %i, %i, %i, %i\n", rect.x, rect.y, rect.width,
108 rect.height);
109 }
110 }
111 }
112 start = PR_IntervalNow();
113 #endif
114
115 AUTO_PROFILER_LABEL("ClientMultiTiledLayerBuffer::PaintThebes", GRAPHICS);
116
117 mNewValidRegion = aNewValidRegion;
118 Update(aNewValidRegion, aPaintRegion, aDirtyRegion, aFlags);
119
120 #ifdef GFX_TILEDLAYER_PREF_WARNINGS
121 if (PR_IntervalNow() - start > 10) {
122 const IntRect bounds = aPaintRegion.GetBounds();
123 printf_stderr("Time to tile %i: %i, %i, %i, %i\n", PR_IntervalNow() - start,
124 bounds.x, bounds.y, bounds.width, bounds.height);
125 }
126 #endif
127
128 mLastPaintContentType = GetContentType(&mLastPaintSurfaceMode);
129 mCallback = nullptr;
130 mCallbackData = nullptr;
131 }
132
MaybeSyncTextures(const nsIntRegion & aPaintRegion,const TilesPlacement & aNewTiles,const IntSize & aScaledTileSize)133 void ClientMultiTiledLayerBuffer::MaybeSyncTextures(
134 const nsIntRegion& aPaintRegion, const TilesPlacement& aNewTiles,
135 const IntSize& aScaledTileSize) {
136 if (mManager->AsShadowForwarder()->SupportsTextureDirectMapping()) {
137 AutoTArray<uint64_t, 10> syncTextureSerials;
138 SurfaceMode mode;
139 Unused << GetContentType(&mode);
140
141 // Pre-pass through the tiles (mirroring the filter logic below) to gather
142 // texture IDs that we need to ensure are unused by the GPU before we
143 // continue.
144 if (!aPaintRegion.IsEmpty()) {
145 MOZ_ASSERT(mPaintTasks.IsEmpty());
146 for (size_t i = 0; i < mRetainedTiles.Length(); ++i) {
147 const TileCoordIntPoint tileCoord = aNewTiles.TileCoord(i);
148
149 IntPoint tileOffset = GetTileOffset(tileCoord);
150 nsIntRegion tileDrawRegion = IntRect(tileOffset, aScaledTileSize);
151 tileDrawRegion.AndWith(aPaintRegion);
152
153 if (tileDrawRegion.IsEmpty()) {
154 continue;
155 }
156
157 TileClient& tile = mRetainedTiles[i];
158 tile.GetSyncTextureSerials(mode, syncTextureSerials);
159 }
160 }
161
162 if (syncTextureSerials.Length() > 0) {
163 mManager->AsShadowForwarder()->SyncTextures(syncTextureSerials);
164 }
165 }
166 }
167
Update(const nsIntRegion & newValidRegion,const nsIntRegion & aPaintRegion,const nsIntRegion & aDirtyRegion,TilePaintFlags aFlags)168 void ClientMultiTiledLayerBuffer::Update(const nsIntRegion& newValidRegion,
169 const nsIntRegion& aPaintRegion,
170 const nsIntRegion& aDirtyRegion,
171 TilePaintFlags aFlags) {
172 const IntSize scaledTileSize = GetScaledTileSize();
173 const gfx::IntRect newBounds = newValidRegion.GetBounds();
174
175 const TilesPlacement oldTiles = mTiles;
176 const TilesPlacement newTiles(
177 floor_div(newBounds.X(), scaledTileSize.width),
178 floor_div(newBounds.Y(), scaledTileSize.height),
179 floor_div(
180 GetTileStart(newBounds.X(), scaledTileSize.width) + newBounds.Width(),
181 scaledTileSize.width) +
182 1,
183 floor_div(GetTileStart(newBounds.Y(), scaledTileSize.height) +
184 newBounds.Height(),
185 scaledTileSize.height) +
186 1);
187
188 const size_t oldTileCount = mRetainedTiles.Length();
189 const size_t newTileCount = newTiles.mSize.width * newTiles.mSize.height;
190
191 nsTArray<TileClient> oldRetainedTiles;
192 mRetainedTiles.SwapElements(oldRetainedTiles);
193 mRetainedTiles.SetLength(newTileCount);
194
195 for (size_t oldIndex = 0; oldIndex < oldTileCount; oldIndex++) {
196 const TileCoordIntPoint tileCoord = oldTiles.TileCoord(oldIndex);
197 const size_t newIndex = newTiles.TileIndex(tileCoord);
198 // First, get the already existing tiles to the right place in the new
199 // array. Leave placeholders (default constructor) where there was no tile.
200 if (newTiles.HasTile(tileCoord)) {
201 mRetainedTiles[newIndex] = oldRetainedTiles[oldIndex];
202 } else {
203 // release tiles that we are not going to reuse before allocating new ones
204 // to avoid allocating unnecessarily.
205 oldRetainedTiles[oldIndex].DiscardBuffers();
206 }
207 }
208
209 oldRetainedTiles.Clear();
210
211 nsIntRegion paintRegion = aPaintRegion;
212 nsIntRegion dirtyRegion = aDirtyRegion;
213
214 MaybeSyncTextures(paintRegion, newTiles, scaledTileSize);
215
216 if (!paintRegion.IsEmpty()) {
217 MOZ_ASSERT(mPaintTasks.IsEmpty());
218
219 for (size_t i = 0; i < newTileCount; ++i) {
220 const TileCoordIntPoint tileCoord = newTiles.TileCoord(i);
221
222 IntPoint tileOffset = GetTileOffset(tileCoord);
223 nsIntRegion tileDrawRegion = IntRect(tileOffset, scaledTileSize);
224 tileDrawRegion.AndWith(paintRegion);
225
226 if (tileDrawRegion.IsEmpty()) {
227 continue;
228 }
229
230 TileClient& tile = mRetainedTiles[i];
231 if (!ValidateTile(tile, GetTileOffset(tileCoord), tileDrawRegion,
232 aFlags)) {
233 gfxCriticalError() << "ValidateTile failed";
234 }
235
236 // Validating the tile may have required more to be painted.
237 paintRegion.OrWith(tileDrawRegion);
238 dirtyRegion.OrWith(tileDrawRegion);
239 }
240
241 if (!mPaintTiles.IsEmpty()) {
242 // Create a tiled draw target
243 gfx::TileSet tileset;
244 for (size_t i = 0; i < mPaintTiles.Length(); ++i) {
245 mPaintTiles[i].mTileOrigin -= mTilingOrigin;
246 }
247 tileset.mTiles = mPaintTiles.Elements();
248 tileset.mTileCount = mPaintTiles.Length();
249 RefPtr<DrawTarget> drawTarget =
250 gfx::Factory::CreateTiledDrawTarget(tileset);
251 if (!drawTarget || !drawTarget->IsValid()) {
252 gfxDevCrash(LogReason::InvalidContext) << "Invalid tiled draw target";
253 return;
254 }
255 drawTarget->SetTransform(Matrix());
256
257 // Draw into the tiled draw target
258 RefPtr<gfxContext> ctx = gfxContext::CreateOrNull(drawTarget);
259 MOZ_ASSERT(ctx); // already checked the draw target above
260 ctx->SetMatrix(ctx->CurrentMatrix()
261 .PreScale(mResolution, mResolution)
262 .PreTranslate(-mTilingOrigin));
263
264 mCallback(&mPaintedLayer, ctx, paintRegion, dirtyRegion,
265 DrawRegionClip::DRAW, nsIntRegion(), mCallbackData);
266 ctx = nullptr;
267
268 // Edge padding allows us to avoid resampling artifacts
269 if (StaticPrefs::layers_tiles_edge_padding_AtStartup() &&
270 mResolution == 1) {
271 drawTarget->PadEdges(newValidRegion.MovedBy(-mTilingOrigin));
272 }
273
274 // Reset
275 mPaintTiles.Clear();
276 mTilingOrigin = IntPoint(std::numeric_limits<int32_t>::max(),
277 std::numeric_limits<int32_t>::max());
278 }
279
280 // Dispatch to the paint thread
281 if (aFlags & TilePaintFlags::Async) {
282 bool queuedTask = false;
283
284 while (!mPaintTasks.IsEmpty()) {
285 UniquePtr<PaintTask> task = mPaintTasks.PopLastElement();
286 if (!task->mCapture->IsEmpty()) {
287 PaintThread::Get()->QueuePaintTask(std::move(task));
288 queuedTask = true;
289 }
290 }
291
292 if (queuedTask) {
293 mManager->SetQueuedAsyncPaints();
294 }
295
296 mPaintTasks.Clear();
297 }
298
299 for (uint32_t i = 0; i < mRetainedTiles.Length(); ++i) {
300 TileClient& tile = mRetainedTiles[i];
301 UnlockTile(tile);
302 }
303 }
304
305 mTiles = newTiles;
306 mValidRegion = newValidRegion;
307 }
308
ValidateTile(TileClient & aTile,const nsIntPoint & aTileOrigin,nsIntRegion & aDirtyRegion,TilePaintFlags aFlags)309 bool ClientMultiTiledLayerBuffer::ValidateTile(TileClient& aTile,
310 const nsIntPoint& aTileOrigin,
311 nsIntRegion& aDirtyRegion,
312 TilePaintFlags aFlags) {
313 #ifdef GFX_TILEDLAYER_PREF_WARNINGS
314 if (aDirtyRegion.IsComplex()) {
315 printf_stderr("Complex region\n");
316 }
317 #endif
318
319 SurfaceMode mode;
320 gfxContentType content = GetContentType(&mode);
321
322 if (!aTile.mAllocator) {
323 aTile.SetTextureAllocator(
324 mManager->GetCompositorBridgeChild()->GetTexturePool(
325 mManager->AsShadowForwarder(),
326 gfxPlatform::GetPlatform()->Optimal2DFormatForContent(content),
327 TextureFlags::DISALLOW_BIGIMAGE | TextureFlags::IMMEDIATE_UPLOAD |
328 TextureFlags::NON_BLOCKING_READ_LOCK));
329 MOZ_ASSERT(aTile.mAllocator);
330 }
331
332 nsIntRegion tileDirtyRegion = aDirtyRegion.MovedBy(-aTileOrigin);
333 tileDirtyRegion.ScaleRoundOut(mResolution, mResolution);
334
335 nsIntRegion tileVisibleRegion = mNewValidRegion.MovedBy(-aTileOrigin);
336 tileVisibleRegion.ScaleRoundOut(mResolution, mResolution);
337
338 std::vector<RefPtr<TextureClient>> asyncPaintClients;
339
340 Maybe<AcquiredBackBuffer> backBuffer =
341 aTile.AcquireBackBuffer(mCompositableClient, tileDirtyRegion,
342 tileVisibleRegion, content, mode, aFlags);
343
344 if (!backBuffer) {
345 return false;
346 }
347
348 // Mark the area we need to paint in the back buffer as invalid in the
349 // front buffer as they will become out of sync.
350 aTile.mInvalidFront.OrWith(tileDirtyRegion);
351
352 // Add the backbuffer's invalid region intersected with the visible region to
353 // the dirty region we will be painting. This will be empty if we are able to
354 // copy from the front into the back.
355 nsIntRegion tileInvalidRegion = aTile.mInvalidBack;
356 tileInvalidRegion.AndWith(tileVisibleRegion);
357
358 nsIntRegion invalidRegion = tileInvalidRegion;
359 invalidRegion.MoveBy(aTileOrigin);
360 invalidRegion.ScaleInverseRoundOut(mResolution, mResolution);
361
362 tileDirtyRegion.OrWith(tileInvalidRegion);
363 aDirtyRegion.OrWith(invalidRegion);
364
365 // Mark the region we will be painting and the region we copied from the front
366 // buffer as needing to be uploaded to the compositor
367 aTile.mUpdateRect =
368 tileDirtyRegion.GetBounds().Union(backBuffer->mUpdatedRect);
369
370 // We need to clear the dirty region of the tile before painting
371 // if we are painting non-opaque content
372 if (mode != SurfaceMode::SURFACE_OPAQUE) {
373 for (auto iter = tileDirtyRegion.RectIter(); !iter.Done(); iter.Next()) {
374 const gfx::Rect drawRect(iter.Get().X(), iter.Get().Y(),
375 iter.Get().Width(), iter.Get().Height());
376 backBuffer->mTarget->ClearRect(drawRect);
377 }
378 }
379
380 gfx::Tile paintTile;
381 paintTile.mTileOrigin = gfx::IntPoint(aTileOrigin.x, aTileOrigin.y);
382 paintTile.mDrawTarget = backBuffer->mTarget;
383 mPaintTiles.AppendElement(paintTile);
384
385 if (aFlags & TilePaintFlags::Async) {
386 UniquePtr<PaintTask> task(new PaintTask());
387 task->mCapture = backBuffer->mCapture;
388 task->mTarget = backBuffer->mBackBuffer;
389 task->mClients = std::move(backBuffer->mTextureClients);
390 mPaintTasks.AppendElement(std::move(task));
391 } else {
392 MOZ_RELEASE_ASSERT(backBuffer->mTarget == backBuffer->mBackBuffer);
393 MOZ_RELEASE_ASSERT(backBuffer->mCapture == nullptr);
394 }
395
396 mTilingOrigin.x = std::min(mTilingOrigin.x, paintTile.mTileOrigin.x);
397 mTilingOrigin.y = std::min(mTilingOrigin.y, paintTile.mTileOrigin.y);
398
399 // The new buffer is now validated, remove the dirty region from it.
400 aTile.mInvalidBack.SubOut(tileDirtyRegion);
401
402 aTile.Flip();
403
404 return true;
405 }
406
407 /**
408 * This function takes the transform stored in aTransformToCompBounds
409 * (which was generated in GetTransformToAncestorsParentLayer), and
410 * modifies it with the ViewTransform from the compositor side so that
411 * it reflects what the compositor is actually rendering. This operation
412 * basically adds in the layer's async transform.
413 * This function then returns the scroll ancestor's composition bounds,
414 * transformed into the painted layer's LayerPixel coordinates, accounting
415 * for the compositor state.
416 */
GetCompositorSideCompositionBounds(const LayerMetricsWrapper & aScrollAncestor,const LayerToParentLayerMatrix4x4 & aTransformToCompBounds,const AsyncTransform & aAPZTransform,const LayerRect & aClip)417 static Maybe<LayerRect> GetCompositorSideCompositionBounds(
418 const LayerMetricsWrapper& aScrollAncestor,
419 const LayerToParentLayerMatrix4x4& aTransformToCompBounds,
420 const AsyncTransform& aAPZTransform, const LayerRect& aClip) {
421 LayerToParentLayerMatrix4x4 transform =
422 aTransformToCompBounds * AsyncTransformComponentMatrix(aAPZTransform);
423
424 return UntransformBy(transform.Inverse(),
425 aScrollAncestor.Metrics().GetCompositionBounds(), aClip);
426 }
427
ComputeProgressiveUpdateRegion(const nsIntRegion & aInvalidRegion,const nsIntRegion & aOldValidRegion,nsIntRegion & aRegionToPaint,BasicTiledLayerPaintData * aPaintData,bool aIsRepeated)428 bool ClientMultiTiledLayerBuffer::ComputeProgressiveUpdateRegion(
429 const nsIntRegion& aInvalidRegion, const nsIntRegion& aOldValidRegion,
430 nsIntRegion& aRegionToPaint, BasicTiledLayerPaintData* aPaintData,
431 bool aIsRepeated) {
432 aRegionToPaint = aInvalidRegion;
433
434 // If the composition bounds rect is empty, we can't make any sensible
435 // decision about how to update coherently. In this case, just update
436 // everything in one transaction.
437 if (aPaintData->mCompositionBounds.IsEmpty()) {
438 aPaintData->mPaintFinished = true;
439 return false;
440 }
441
442 // If this is a low precision buffer, we force progressive updates. The
443 // assumption is that the contents is less important, so visual coherency
444 // is lower priority than speed.
445 bool drawingLowPrecision = IsLowPrecision();
446
447 // Find out if we have any non-stale content to update.
448 nsIntRegion staleRegion;
449 staleRegion.And(aInvalidRegion, aOldValidRegion);
450
451 TILING_LOG("TILING %p: Progressive update stale region %s\n", &mPaintedLayer,
452 Stringify(staleRegion).c_str());
453
454 LayerMetricsWrapper scrollAncestor;
455 mPaintedLayer.GetAncestorLayers(&scrollAncestor, nullptr, nullptr);
456
457 // Find out the current view transform to determine which tiles to draw
458 // first, and see if we should just abort this paint. Aborting is usually
459 // caused by there being an incoming, more relevant paint.
460 AsyncTransform viewTransform;
461 MOZ_ASSERT(mSharedFrameMetricsHelper);
462
463 bool abortPaint = mSharedFrameMetricsHelper->UpdateFromCompositorFrameMetrics(
464 scrollAncestor, !staleRegion.Contains(aInvalidRegion),
465 drawingLowPrecision, viewTransform);
466
467 TILING_LOG(
468 "TILING %p: Progressive update view transform %s zoom %f abort %d\n",
469 &mPaintedLayer, ToString(viewTransform.mTranslation).c_str(),
470 viewTransform.mScale.scale, abortPaint);
471
472 if (abortPaint) {
473 // We ignore if front-end wants to abort if this is the first,
474 // non-low-precision paint, as in that situation, we're about to override
475 // front-end's page/viewport metrics.
476 if (!aPaintData->mFirstPaint || drawingLowPrecision) {
477 AUTO_PROFILER_LABEL(
478 "ClientMultiTiledLayerBuffer::ComputeProgressiveUpdateRegion",
479 GRAPHICS);
480
481 aRegionToPaint.SetEmpty();
482 return aIsRepeated;
483 }
484 }
485
486 Maybe<LayerRect> transformedCompositionBounds =
487 GetCompositorSideCompositionBounds(
488 scrollAncestor, aPaintData->mTransformToCompBounds, viewTransform,
489 LayerRect(mPaintedLayer.GetVisibleRegion().GetBounds()));
490
491 if (!transformedCompositionBounds) {
492 aPaintData->mPaintFinished = true;
493 return false;
494 }
495
496 TILING_LOG("TILING %p: Progressive update transformed compositor bounds %s\n",
497 &mPaintedLayer, Stringify(*transformedCompositionBounds).c_str());
498
499 // Compute a "coherent update rect" that we should paint all at once in a
500 // single transaction. This is to avoid rendering glitches on animated
501 // page content, and when layers change size/shape.
502 // On Fennec uploads are more expensive because we're not using gralloc, so
503 // we use a coherent update rect that is intersected with the screen at the
504 // time of issuing the draw command. This will paint faster but also
505 // potentially make the progressive paint more visible to the user while
506 // scrolling.
507 IntRect coherentUpdateRect(RoundedOut(
508 #ifdef MOZ_WIDGET_ANDROID
509 transformedCompositionBounds->Intersect(
510 aPaintData->mCompositionBounds)
511 #else
512 *transformedCompositionBounds
513 #endif
514 )
515 .ToUnknownRect());
516
517 TILING_LOG("TILING %p: Progressive update final coherency rect %s\n",
518 &mPaintedLayer, Stringify(coherentUpdateRect).c_str());
519
520 aRegionToPaint.And(aInvalidRegion, coherentUpdateRect);
521 aRegionToPaint.Or(aRegionToPaint, staleRegion);
522 bool drawingStale = !aRegionToPaint.IsEmpty();
523 if (!drawingStale) {
524 aRegionToPaint = aInvalidRegion;
525 }
526
527 // Prioritise tiles that are currently visible on the screen.
528 bool paintingVisible = false;
529 if (aRegionToPaint.Intersects(coherentUpdateRect)) {
530 aRegionToPaint.And(aRegionToPaint, coherentUpdateRect);
531 paintingVisible = true;
532 }
533
534 TILING_LOG("TILING %p: Progressive update final paint region %s\n",
535 &mPaintedLayer, Stringify(aRegionToPaint).c_str());
536
537 // Paint area that's visible and overlaps previously valid content to avoid
538 // visible glitches in animated elements, such as gifs.
539 bool paintInSingleTransaction =
540 paintingVisible && (drawingStale || aPaintData->mFirstPaint);
541
542 TILING_LOG(
543 "TILING %p: paintingVisible %d drawingStale %d firstPaint %d "
544 "singleTransaction %d\n",
545 &mPaintedLayer, paintingVisible, drawingStale, aPaintData->mFirstPaint,
546 paintInSingleTransaction);
547
548 // The following code decides what order to draw tiles in, based on the
549 // current scroll direction of the primary scrollable layer.
550 NS_ASSERTION(!aRegionToPaint.IsEmpty(), "Unexpectedly empty paint region!");
551 IntRect paintBounds = aRegionToPaint.GetBounds();
552
553 int startX, incX, startY, incY;
554 gfx::IntSize scaledTileSize = GetScaledTileSize();
555 if (aPaintData->mScrollOffset.x >= aPaintData->mLastScrollOffset.x) {
556 startX = RoundDownToTileEdge(paintBounds.X(), scaledTileSize.width);
557 incX = scaledTileSize.width;
558 } else {
559 startX = RoundDownToTileEdge(paintBounds.XMost() - 1, scaledTileSize.width);
560 incX = -scaledTileSize.width;
561 }
562
563 if (aPaintData->mScrollOffset.y >= aPaintData->mLastScrollOffset.y) {
564 startY = RoundDownToTileEdge(paintBounds.Y(), scaledTileSize.height);
565 incY = scaledTileSize.height;
566 } else {
567 startY =
568 RoundDownToTileEdge(paintBounds.YMost() - 1, scaledTileSize.height);
569 incY = -scaledTileSize.height;
570 }
571
572 // Find a tile to draw.
573 IntRect tileBounds(startX, startY, scaledTileSize.width,
574 scaledTileSize.height);
575 int32_t scrollDiffX =
576 aPaintData->mScrollOffset.x - aPaintData->mLastScrollOffset.x;
577 int32_t scrollDiffY =
578 aPaintData->mScrollOffset.y - aPaintData->mLastScrollOffset.y;
579 // This loop will always terminate, as there is at least one tile area
580 // along the first/last row/column intersecting with regionToPaint, or its
581 // bounds would have been smaller.
582 while (true) {
583 aRegionToPaint.And(aInvalidRegion, tileBounds);
584 if (!aRegionToPaint.IsEmpty()) {
585 if (mResolution != 1) {
586 // Paint the entire tile for low-res. This is aimed to fixing low-res
587 // resampling and to avoid doing costly region accurate painting for a
588 // small area.
589 aRegionToPaint = tileBounds;
590 }
591 break;
592 }
593 if (Abs(scrollDiffY) >= Abs(scrollDiffX)) {
594 tileBounds.MoveByX(incX);
595 } else {
596 tileBounds.MoveByY(incY);
597 }
598 }
599
600 if (!aRegionToPaint.Contains(aInvalidRegion)) {
601 // The region needed to paint is larger then our progressive chunk size
602 // therefore update what we want to paint and ask for a new paint
603 // transaction.
604
605 // If we need to draw more than one tile to maintain coherency, make
606 // sure it happens in the same transaction by requesting this work be
607 // repeated immediately.
608 // If this is unnecessary, the remaining work will be done tile-by-tile in
609 // subsequent transactions. The caller code is responsible for scheduling
610 // the subsequent transactions as long as we don't set the mPaintFinished
611 // flag to true.
612 return (!drawingLowPrecision && paintInSingleTransaction);
613 }
614
615 // We're not repeating painting and we've not requested a repeat transaction,
616 // so the paint is finished. If there's still a separate low precision
617 // paint to do, it will get marked as unfinished later.
618 aPaintData->mPaintFinished = true;
619 return false;
620 }
621
ProgressiveUpdate(const nsIntRegion & aValidRegion,const nsIntRegion & aInvalidRegion,const nsIntRegion & aOldValidRegion,nsIntRegion & aOutDrawnRegion,BasicTiledLayerPaintData * aPaintData,LayerManager::DrawPaintedLayerCallback aCallback,void * aCallbackData)622 bool ClientMultiTiledLayerBuffer::ProgressiveUpdate(
623 const nsIntRegion& aValidRegion, const nsIntRegion& aInvalidRegion,
624 const nsIntRegion& aOldValidRegion, nsIntRegion& aOutDrawnRegion,
625 BasicTiledLayerPaintData* aPaintData,
626 LayerManager::DrawPaintedLayerCallback aCallback, void* aCallbackData) {
627 TILING_LOG("TILING %p: Progressive update valid region %s\n", &mPaintedLayer,
628 Stringify(aValidRegion).c_str());
629 TILING_LOG("TILING %p: Progressive update invalid region %s\n",
630 &mPaintedLayer, Stringify(aInvalidRegion).c_str());
631 TILING_LOG("TILING %p: Progressive update old valid region %s\n",
632 &mPaintedLayer, Stringify(aOldValidRegion).c_str());
633
634 bool repeat = false;
635 bool isBufferChanged = false;
636 nsIntRegion remainingInvalidRegion = aInvalidRegion;
637 nsIntRegion updatedValidRegion = aValidRegion;
638 do {
639 // Compute the region that should be updated. Repeat as many times as
640 // is required.
641 nsIntRegion regionToPaint;
642 repeat =
643 ComputeProgressiveUpdateRegion(remainingInvalidRegion, aOldValidRegion,
644 regionToPaint, aPaintData, repeat);
645
646 TILING_LOG(
647 "TILING %p: Progressive update computed paint region %s repeat %d\n",
648 &mPaintedLayer, Stringify(regionToPaint).c_str(), repeat);
649
650 // There's no further work to be done.
651 if (regionToPaint.IsEmpty()) {
652 break;
653 }
654
655 isBufferChanged = true;
656
657 // Keep track of what we're about to refresh.
658 aOutDrawnRegion.OrWith(regionToPaint);
659 updatedValidRegion.OrWith(regionToPaint);
660
661 // aValidRegion may have been altered by InvalidateRegion, but we still
662 // want to display stale content until it gets progressively updated.
663 // Create a region that includes stale content.
664 nsIntRegion validOrStale;
665 validOrStale.Or(updatedValidRegion, aOldValidRegion);
666
667 // Paint the computed region and subtract it from the invalid region.
668 PaintThebes(validOrStale, regionToPaint, remainingInvalidRegion, aCallback,
669 aCallbackData, TilePaintFlags::Progressive);
670 remainingInvalidRegion.SubOut(regionToPaint);
671 } while (repeat);
672
673 TILING_LOG(
674 "TILING %p: Progressive update final valid region %s buffer changed %d\n",
675 &mPaintedLayer, Stringify(updatedValidRegion).c_str(), isBufferChanged);
676 TILING_LOG("TILING %p: Progressive update final invalid region %s\n",
677 &mPaintedLayer, Stringify(remainingInvalidRegion).c_str());
678
679 // Return false if nothing has been drawn, or give what has been drawn
680 // to the shadow layer to upload.
681 return isBufferChanged;
682 }
683
684 } // namespace layers
685 } // namespace mozilla
686