Coverage Report

Created: 2018-09-25 14:53

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