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