Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/gfx/layers/client/TiledContentClient.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/TiledContentClient.h"
8
#include <math.h>                       // for ceil, ceilf, floor
9
#include <algorithm>
10
#include "ClientTiledPaintedLayer.h"     // for ClientTiledPaintedLayer
11
#include "GeckoProfiler.h"              // for AUTO_PROFILER_LABEL
12
#include "ClientLayerManager.h"         // for ClientLayerManager
13
#include "gfxContext.h"                 // for gfxContext, etc
14
#include "gfxPlatform.h"                // for gfxPlatform
15
#include "gfxPrefs.h"                   // for gfxPrefs
16
#include "gfxRect.h"                    // for gfxRect
17
#include "mozilla/MathAlgorithms.h"     // for Abs
18
#include "mozilla/gfx/Point.h"          // for IntSize
19
#include "mozilla/gfx/Rect.h"           // for Rect
20
#include "mozilla/gfx/Tools.h"          // for BytesPerPixel
21
#include "mozilla/layers/CompositableForwarder.h"
22
#include "mozilla/layers/CompositorBridgeChild.h" // for CompositorBridgeChild
23
#include "mozilla/layers/LayerMetricsWrapper.h"
24
#include "mozilla/layers/ShadowLayers.h"  // for ShadowLayerForwarder
25
#include "mozilla/layers/PaintThread.h"  // for PaintThread
26
#include "TextureClientPool.h"
27
#include "nsDebug.h"                    // for NS_ASSERTION
28
#include "nsISupportsImpl.h"            // for gfxContext::AddRef, etc
29
#include "nsExpirationTracker.h"        // for nsExpirationTracker
30
#include "nsMathUtils.h"               // for NS_lroundf
31
#include "LayersLogging.h"
32
#include "UnitTransforms.h"             // for TransformTo
33
#include "mozilla/UniquePtr.h"
34
35
// This is the minimum area that we deem reasonable to copy from the front buffer to the
36
// back buffer on tile updates. If the valid region is smaller than this, we just
37
// redraw it and save on the copy (and requisite surface-locking involved).
38
#define MINIMUM_TILE_COPY_AREA (1.f/16.f)
39
40
#ifdef GFX_TILEDLAYER_DEBUG_OVERLAY
41
#include "cairo.h"
42
#include <sstream>
43
using mozilla::layers::Layer;
44
static void DrawDebugOverlay(mozilla::gfx::DrawTarget* dt, int x, int y, int width, int height)
45
{
46
  gfxContext c(dt);
47
48
  // Draw border
49
  c.NewPath();
50
  c.SetDeviceColor(Color(0.f, 0.f, 0.f));
51
  c.Rectangle(gfxRect(0, 0, width, height));
52
  c.Stroke();
53
54
  // Build tile description
55
  std::stringstream ss;
56
  ss << x << ", " << y;
57
58
  // Draw text using cairo toy text API
59
  // XXX: this drawing will silently fail if |dt| doesn't have a Cairo backend
60
  cairo_t* cr = gfxFont::RefCairo(dt);
61
  cairo_set_font_size(cr, 25);
62
  cairo_text_extents_t extents;
63
  cairo_text_extents(cr, ss.str().c_str(), &extents);
64
65
  int textWidth = extents.width + 6;
66
67
  c.NewPath();
68
  c.SetDeviceColor(Color(0.f, 0.f, 0.f));
69
  c.Rectangle(gfxRect(gfxPoint(2,2),gfxSize(textWidth, 30)));
70
  c.Fill();
71
72
  c.NewPath();
73
  c.SetDeviceColor(Color(1.0, 0.0, 0.0));
74
  c.Rectangle(gfxRect(gfxPoint(2,2),gfxSize(textWidth, 30)));
75
  c.Stroke();
76
77
  c.NewPath();
78
  cairo_move_to(cr, 4, 28);
79
  cairo_show_text(cr, ss.str().c_str());
80
81
}
82
83
#endif
84
85
namespace mozilla {
86
87
using namespace gfx;
88
89
namespace layers {
90
91
SharedFrameMetricsHelper::SharedFrameMetricsHelper()
92
  : mLastProgressiveUpdateWasLowPrecision(false)
93
  , mProgressiveUpdateWasInDanger(false)
94
0
{
95
0
  MOZ_COUNT_CTOR(SharedFrameMetricsHelper);
96
0
}
97
98
SharedFrameMetricsHelper::~SharedFrameMetricsHelper()
99
0
{
100
0
  MOZ_COUNT_DTOR(SharedFrameMetricsHelper);
101
0
}
102
103
static inline bool
104
0
FuzzyEquals(float a, float b) {
105
0
  return (fabsf(a - b) < 1e-6);
106
0
}
107
108
static AsyncTransform
109
ComputeViewTransform(const FrameMetrics& aContentMetrics, const FrameMetrics& aCompositorMetrics)
110
0
{
111
0
  // This is basically the same code as AsyncPanZoomController::GetCurrentAsyncTransform
112
0
  // but with aContentMetrics used in place of mLastContentPaintMetrics, because they
113
0
  // should be equivalent, modulo race conditions while transactions are inflight.
114
0
115
0
  ParentLayerPoint translation = (aCompositorMetrics.GetScrollOffset() - aContentMetrics.GetScrollOffset())
116
0
                               * aCompositorMetrics.GetZoom();
117
0
  return AsyncTransform(aCompositorMetrics.GetAsyncZoom(), -translation);
118
0
}
119
120
bool
121
SharedFrameMetricsHelper::UpdateFromCompositorFrameMetrics(
122
    const LayerMetricsWrapper& aLayer,
123
    bool aHasPendingNewThebesContent,
124
    bool aLowPrecision,
125
    AsyncTransform& aViewTransform)
126
0
{
127
0
  MOZ_ASSERT(aLayer);
128
0
129
0
  CompositorBridgeChild* compositor = nullptr;
130
0
  if (aLayer.Manager() &&
131
0
      aLayer.Manager()->AsClientLayerManager()) {
132
0
    compositor = aLayer.Manager()->AsClientLayerManager()->GetCompositorBridgeChild();
133
0
  }
134
0
135
0
  if (!compositor) {
136
0
    return false;
137
0
  }
138
0
139
0
  const FrameMetrics& contentMetrics = aLayer.Metrics();
140
0
  FrameMetrics compositorMetrics;
141
0
142
0
  if (!compositor->LookupCompositorFrameMetrics(contentMetrics.GetScrollId(),
143
0
                                                compositorMetrics)) {
144
0
    return false;
145
0
  }
146
0
147
0
  aViewTransform = ComputeViewTransform(contentMetrics, compositorMetrics);
148
0
149
0
  // Reset the checkerboard risk flag when switching to low precision
150
0
  // rendering.
151
0
  if (aLowPrecision && !mLastProgressiveUpdateWasLowPrecision) {
152
0
    // Skip low precision rendering until we're at risk of checkerboarding.
153
0
    if (!mProgressiveUpdateWasInDanger) {
154
0
      TILING_LOG("TILING: Aborting low-precision rendering because not at risk of checkerboarding\n");
155
0
      return true;
156
0
    }
157
0
    mProgressiveUpdateWasInDanger = false;
158
0
  }
159
0
  mLastProgressiveUpdateWasLowPrecision = aLowPrecision;
160
0
161
0
  // Always abort updates if the resolution has changed. There's no use
162
0
  // in drawing at the incorrect resolution.
163
0
  if (!FuzzyEquals(compositorMetrics.GetZoom().xScale, contentMetrics.GetZoom().xScale) ||
164
0
      !FuzzyEquals(compositorMetrics.GetZoom().yScale, contentMetrics.GetZoom().yScale)) {
165
0
    TILING_LOG("TILING: Aborting because resolution changed from %s to %s\n",
166
0
        ToString(contentMetrics.GetZoom()).c_str(), ToString(compositorMetrics.GetZoom()).c_str());
167
0
    return true;
168
0
  }
169
0
170
0
  // Never abort drawing if we can't be sure we've sent a more recent
171
0
  // display-port. If we abort updating when we shouldn't, we can end up
172
0
  // with blank regions on the screen and we open up the risk of entering
173
0
  // an endless updating cycle.
174
0
  if (fabsf(contentMetrics.GetScrollOffset().x - compositorMetrics.GetScrollOffset().x) <= 2 &&
175
0
      fabsf(contentMetrics.GetScrollOffset().y - compositorMetrics.GetScrollOffset().y) <= 2 &&
176
0
      fabsf(contentMetrics.GetDisplayPort().X() - compositorMetrics.GetDisplayPort().X()) <= 2 &&
177
0
      fabsf(contentMetrics.GetDisplayPort().Y() - compositorMetrics.GetDisplayPort().Y()) <= 2 &&
178
0
      fabsf(contentMetrics.GetDisplayPort().Width() - compositorMetrics.GetDisplayPort().Width()) <= 2 &&
179
0
      fabsf(contentMetrics.GetDisplayPort().Height() - compositorMetrics.GetDisplayPort().Height()) <= 2) {
180
0
    return false;
181
0
  }
182
0
183
0
  // When not a low precision pass and the page is in danger of checker boarding
184
0
  // abort update.
185
0
  if (!aLowPrecision && !mProgressiveUpdateWasInDanger) {
186
0
    bool scrollUpdatePending = contentMetrics.GetScrollOffsetUpdated() &&
187
0
        contentMetrics.GetScrollGeneration() != compositorMetrics.GetScrollGeneration();
188
0
    // If scrollUpdatePending is true, then that means the content-side
189
0
    // metrics has a new scroll offset that is going to be forced into the
190
0
    // compositor but it hasn't gotten there yet.
191
0
    // Even though right now comparing the metrics might indicate we're
192
0
    // about to checkerboard (and that's true), the checkerboarding will
193
0
    // disappear as soon as the new scroll offset update is processed
194
0
    // on the compositor side. To avoid leaving things in a low-precision
195
0
    // paint, we need to detect and handle this case (bug 1026756).
196
0
    if (!scrollUpdatePending && AboutToCheckerboard(contentMetrics, compositorMetrics)) {
197
0
      mProgressiveUpdateWasInDanger = true;
198
0
      return true;
199
0
    }
200
0
  }
201
0
202
0
  // Abort drawing stale low-precision content if there's a more recent
203
0
  // display-port in the pipeline.
204
0
  if (aLowPrecision && !aHasPendingNewThebesContent) {
205
0
    TILING_LOG("TILING: Aborting low-precision because of new pending content\n");
206
0
    return true;
207
0
  }
208
0
209
0
  return false;
210
0
}
211
212
bool
213
SharedFrameMetricsHelper::AboutToCheckerboard(const FrameMetrics& aContentMetrics,
214
                                              const FrameMetrics& aCompositorMetrics)
215
0
{
216
0
  // The size of the painted area is originally computed in layer pixels in layout, but then
217
0
  // converted to app units and then back to CSS pixels before being put in the FrameMetrics.
218
0
  // This process can introduce some rounding error, so we inflate the rect by one app unit
219
0
  // to account for that.
220
0
  CSSRect painted = (aContentMetrics.GetCriticalDisplayPort().IsEmpty()
221
0
                      ? aContentMetrics.GetDisplayPort()
222
0
                      : aContentMetrics.GetCriticalDisplayPort())
223
0
                    + aContentMetrics.GetScrollOffset();
224
0
  painted.Inflate(CSSMargin::FromAppUnits(nsMargin(1, 1, 1, 1)));
225
0
226
0
  // Inflate the rect by the danger zone. See the description of the danger zone prefs
227
0
  // in AsyncPanZoomController.cpp for an explanation of this.
228
0
  CSSRect showing = CSSRect(aCompositorMetrics.GetScrollOffset(),
229
0
                            aCompositorMetrics.CalculateBoundedCompositedSizeInCssPixels());
230
0
  showing.Inflate(LayerSize(gfxPrefs::APZDangerZoneX(), gfxPrefs::APZDangerZoneY())
231
0
                  / aCompositorMetrics.LayersPixelsPerCSSPixel());
232
0
233
0
  // Clamp both rects to the scrollable rect, because having either of those
234
0
  // exceed the scrollable rect doesn't make sense, and could lead to false
235
0
  // positives.
236
0
  painted = painted.Intersect(aContentMetrics.GetScrollableRect());
237
0
  showing = showing.Intersect(aContentMetrics.GetScrollableRect());
238
0
239
0
  if (!painted.Contains(showing)) {
240
0
    TILING_LOG("TILING: About to checkerboard; content %s\n", Stringify(aContentMetrics).c_str());
241
0
    TILING_LOG("TILING: About to checkerboard; painted %s\n", Stringify(painted).c_str());
242
0
    TILING_LOG("TILING: About to checkerboard; compositor %s\n", Stringify(aCompositorMetrics).c_str());
243
0
    TILING_LOG("TILING: About to checkerboard; showing %s\n", Stringify(showing).c_str());
244
0
    return true;
245
0
  }
246
0
  return false;
247
0
}
248
249
bool
250
ClientTiledLayerBuffer::HasFormatChanged() const
251
0
{
252
0
  SurfaceMode mode;
253
0
  gfxContentType content = GetContentType(&mode);
254
0
  return content != mLastPaintContentType ||
255
0
         mode != mLastPaintSurfaceMode;
256
0
}
257
258
259
gfxContentType
260
ClientTiledLayerBuffer::GetContentType(SurfaceMode* aMode) const
261
0
{
262
0
  gfxContentType content =
263
0
    mPaintedLayer.CanUseOpaqueSurface() ? gfxContentType::COLOR :
264
0
                                          gfxContentType::COLOR_ALPHA;
265
0
  SurfaceMode mode = mPaintedLayer.GetSurfaceMode();
266
0
267
0
  if (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
268
#if defined(MOZ_GFX_OPTIMIZE_MOBILE)
269
    mode = SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA;
270
#else
271
0
    if (!mPaintedLayer.GetParent() ||
272
0
        !mPaintedLayer.GetParent()->SupportsComponentAlphaChildren()) {
273
0
      mode = SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA;
274
0
    } else {
275
0
      content = gfxContentType::COLOR;
276
0
    }
277
0
#endif
278
0
  } else if (mode == SurfaceMode::SURFACE_OPAQUE) {
279
#if defined(MOZ_GFX_OPTIMIZE_MOBILE)
280
    if (IsLowPrecision()) {
281
      // If we're in low-res mode, drawing can sample from outside the visible
282
      // region. Make sure that we only sample transparency if that happens.
283
      mode = SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA;
284
      content = gfxContentType::COLOR_ALPHA;
285
    }
286
#else
287
0
    if (mPaintedLayer.MayResample()) {
288
0
      mode = SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA;
289
0
      content = gfxContentType::COLOR_ALPHA;
290
0
    }
291
0
#endif
292
0
  }
293
0
294
0
  if (aMode) {
295
0
    *aMode = mode;
296
0
  }
297
0
  return content;
298
0
}
299
300
class TileExpiry final : public nsExpirationTracker<TileClient, 3>
301
{
302
  public:
303
0
    TileExpiry() : nsExpirationTracker<TileClient, 3>(1000, "TileExpiry") {}
304
305
    static void AddTile(TileClient* aTile)
306
0
    {
307
0
      if (!sTileExpiry) {
308
0
        sTileExpiry = MakeUnique<TileExpiry>();
309
0
      }
310
0
311
0
      sTileExpiry->AddObject(aTile);
312
0
    }
313
314
    static void RemoveTile(TileClient* aTile)
315
0
    {
316
0
      MOZ_ASSERT(sTileExpiry);
317
0
      sTileExpiry->RemoveObject(aTile);
318
0
    }
319
320
0
    static void Shutdown() {
321
0
      sTileExpiry = nullptr;
322
0
    }
323
  private:
324
    virtual void NotifyExpired(TileClient* aTile) override
325
0
    {
326
0
      aTile->DiscardBackBuffer();
327
0
    }
328
329
    static UniquePtr<TileExpiry> sTileExpiry;
330
};
331
UniquePtr<TileExpiry> TileExpiry::sTileExpiry;
332
333
void ShutdownTileCache()
334
0
{
335
0
  TileExpiry::Shutdown();
336
0
}
337
338
void
339
TileClient::PrivateProtector::Set(TileClient * const aContainer, RefPtr<TextureClient> aNewValue)
340
0
{
341
0
  if (mBuffer) {
342
0
    TileExpiry::RemoveTile(aContainer);
343
0
  }
344
0
  mBuffer = aNewValue;
345
0
  if (mBuffer) {
346
0
    TileExpiry::AddTile(aContainer);
347
0
  }
348
0
}
349
350
void
351
TileClient::PrivateProtector::Set(TileClient * const aContainer, TextureClient* aNewValue)
352
0
{
353
0
  Set(aContainer, RefPtr<TextureClient>(aNewValue));
354
0
}
355
356
// Placeholder
357
TileClient::TileClient()
358
  : mWasPlaceholder(false)
359
0
{
360
0
}
361
362
TileClient::~TileClient()
363
0
{
364
0
  if (mExpirationState.IsTracked()) {
365
0
    MOZ_ASSERT(mBackBuffer);
366
0
    TileExpiry::RemoveTile(this);
367
0
  }
368
0
}
369
370
TileClient::TileClient(const TileClient& o)
371
0
{
372
0
  mBackBuffer.Set(this, o.mBackBuffer);
373
0
  mBackBufferOnWhite = o.mBackBufferOnWhite;
374
0
  mFrontBuffer = o.mFrontBuffer;
375
0
  mFrontBufferOnWhite = o.mFrontBufferOnWhite;
376
0
  mWasPlaceholder = o.mWasPlaceholder;
377
0
  mUpdateRect = o.mUpdateRect;
378
#ifdef GFX_TILEDLAYER_DEBUG_OVERLAY
379
  mLastUpdate = o.mLastUpdate;
380
#endif
381
  mAllocator = o.mAllocator;
382
0
  mInvalidFront = o.mInvalidFront;
383
0
  mInvalidBack = o.mInvalidBack;
384
0
}
385
386
TileClient&
387
TileClient::operator=(const TileClient& o)
388
0
{
389
0
  if (this == &o) return *this;
390
0
  mBackBuffer.Set(this, o.mBackBuffer);
391
0
  mBackBufferOnWhite = o.mBackBufferOnWhite;
392
0
  mFrontBuffer = o.mFrontBuffer;
393
0
  mFrontBufferOnWhite = o.mFrontBufferOnWhite;
394
0
  mWasPlaceholder = o.mWasPlaceholder;
395
0
  mUpdateRect = o.mUpdateRect;
396
#ifdef GFX_TILEDLAYER_DEBUG_OVERLAY
397
  mLastUpdate = o.mLastUpdate;
398
#endif
399
  mAllocator = o.mAllocator;
400
0
  mInvalidFront = o.mInvalidFront;
401
0
  mInvalidBack = o.mInvalidBack;
402
0
  return *this;
403
0
}
404
405
void
406
TileClient::Dump(std::stringstream& aStream)
407
0
{
408
0
  aStream << "TileClient(bb=" << (TextureClient*)mBackBuffer << " fb=" << mFrontBuffer.get();
409
0
  if (mBackBufferOnWhite) {
410
0
    aStream << " bbow=" << mBackBufferOnWhite.get();
411
0
  }
412
0
  if (mFrontBufferOnWhite) {
413
0
    aStream << " fbow=" << mFrontBufferOnWhite.get();
414
0
  }
415
0
  aStream << ")";
416
0
}
417
418
void
419
TileClient::Flip()
420
0
{
421
0
  RefPtr<TextureClient> frontBuffer = mFrontBuffer;
422
0
  RefPtr<TextureClient> frontBufferOnWhite = mFrontBufferOnWhite;
423
0
  mFrontBuffer = mBackBuffer;
424
0
  mFrontBufferOnWhite = mBackBufferOnWhite;
425
0
  mBackBuffer.Set(this, frontBuffer);
426
0
  mBackBufferOnWhite = frontBufferOnWhite;
427
0
  nsIntRegion invalidFront = mInvalidFront;
428
0
  mInvalidFront = mInvalidBack;
429
0
  mInvalidBack = invalidFront;
430
0
}
431
432
void
433
TileClient::ValidateFromFront(const nsIntRegion& aDirtyRegion,
434
                              const nsIntRegion& aVisibleRegion,
435
                              gfx::DrawTarget* aBackBuffer,
436
                              TilePaintFlags aFlags,
437
                              IntRect* aCopiedRect,
438
                              AutoTArray<RefPtr<TextureClient>, 4>* aClients)
439
0
{
440
0
  if (!mBackBuffer || !mFrontBuffer) {
441
0
    return;
442
0
  }
443
0
444
0
  gfx::IntSize tileSize = mFrontBuffer->GetSize();
445
0
  const IntRect tileRect = IntRect(0, 0, tileSize.width, tileSize.height);
446
0
447
0
  if (aDirtyRegion.Contains(tileRect)) {
448
0
    // The dirty region means that we no longer need the front buffer, so
449
0
    // discard it.
450
0
    DiscardFrontBuffer();
451
0
    return;
452
0
  }
453
0
454
0
  // Region that needs copying.
455
0
  nsIntRegion regionToCopy = mInvalidBack;
456
0
457
0
  regionToCopy.Sub(regionToCopy, aDirtyRegion);
458
0
  regionToCopy.And(regionToCopy, aVisibleRegion);
459
0
460
0
  *aCopiedRect = regionToCopy.GetBounds();
461
0
462
0
  if (regionToCopy.IsEmpty()) {
463
0
    // Just redraw it all.
464
0
    return;
465
0
  }
466
0
467
0
  // Copy the bounding rect of regionToCopy. As tiles are quite small, it
468
0
  // is unlikely that we'd save much by copying each individual rect of the
469
0
  // region, but we can reevaluate this if it becomes an issue.
470
0
  const IntRect rectToCopy = regionToCopy.GetBounds();
471
0
  OpenMode readMode = !!(aFlags & TilePaintFlags::Async) ? OpenMode::OPEN_READ_ASYNC : OpenMode::OPEN_READ;
472
0
473
0
  DualTextureClientAutoLock frontBuffer(mFrontBuffer, mFrontBufferOnWhite, readMode);
474
0
  if (!frontBuffer.Succeeded()) {
475
0
    return;
476
0
  }
477
0
478
0
  RefPtr<gfx::SourceSurface> frontSurface = frontBuffer->Snapshot();
479
0
  aBackBuffer->CopySurface(frontSurface, rectToCopy, rectToCopy.TopLeft());
480
0
481
0
  if (aFlags & TilePaintFlags::Async) {
482
0
    aClients->AppendElement(mFrontBuffer);
483
0
    if (mFrontBufferOnWhite) {
484
0
      aClients->AppendElement(mFrontBufferOnWhite);
485
0
    }
486
0
  }
487
0
488
0
  mInvalidBack.Sub(mInvalidBack, aVisibleRegion);
489
0
}
490
491
void
492
TileClient::DiscardFrontBuffer()
493
0
{
494
0
  if (mFrontBuffer) {
495
0
    MOZ_ASSERT(mFrontBuffer->GetReadLock());
496
0
497
0
    MOZ_ASSERT(mAllocator);
498
0
    if (mAllocator) {
499
0
      mAllocator->ReturnTextureClientDeferred(mFrontBuffer);
500
0
      if (mFrontBufferOnWhite) {
501
0
        mAllocator->ReturnTextureClientDeferred(mFrontBufferOnWhite);
502
0
      }
503
0
    }
504
0
505
0
    if (mFrontBuffer->IsLocked()) {
506
0
      mFrontBuffer->Unlock();
507
0
    }
508
0
    if (mFrontBufferOnWhite && mFrontBufferOnWhite->IsLocked()) {
509
0
      mFrontBufferOnWhite->Unlock();
510
0
    }
511
0
    mFrontBuffer = nullptr;
512
0
    mFrontBufferOnWhite = nullptr;
513
0
  }
514
0
}
515
516
static void
517
DiscardTexture(TextureClient* aTexture, TextureClientAllocator* aAllocator)
518
0
{
519
0
  MOZ_ASSERT(aAllocator);
520
0
  if (aTexture && aAllocator) {
521
0
    MOZ_ASSERT(aTexture->GetReadLock());
522
0
    if (!aTexture->HasSynchronization() && aTexture->IsReadLocked()) {
523
0
      // Our current back-buffer is still locked by the compositor. This can occur
524
0
      // when the client is producing faster than the compositor can consume. In
525
0
      // this case we just want to drop it and not return it to the pool.
526
0
     aAllocator->ReportClientLost();
527
0
    } else {
528
0
      aAllocator->ReturnTextureClientDeferred(aTexture);
529
0
    }
530
0
    if (aTexture->IsLocked()) {
531
0
      aTexture->Unlock();
532
0
    }
533
0
  }
534
0
}
535
536
void
537
TileClient::DiscardBackBuffer()
538
0
{
539
0
  if (mBackBuffer) {
540
0
    DiscardTexture(mBackBuffer, mAllocator);
541
0
    mBackBuffer.Set(this, nullptr);
542
0
    DiscardTexture(mBackBufferOnWhite, mAllocator);
543
0
    mBackBufferOnWhite = nullptr;
544
0
  }
545
0
}
546
547
static already_AddRefed<TextureClient>
548
CreateBackBufferTexture(TextureClient* aCurrentTexture,
549
                        CompositableClient& aCompositable,
550
                        TextureClientAllocator* aAllocator)
551
0
{
552
0
  if (aCurrentTexture) {
553
0
    // Our current back-buffer is still locked by the compositor. This can occur
554
0
    // when the client is producing faster than the compositor can consume. In
555
0
    // this case we just want to drop it and not return it to the pool.
556
0
    aAllocator->ReportClientLost();
557
0
  }
558
0
559
0
  RefPtr<TextureClient> texture = aAllocator->GetTextureClient();
560
0
561
0
  if (!texture) {
562
0
    gfxCriticalError() << "[Tiling:Client] Failed to allocate a TextureClient";
563
0
    return nullptr;
564
0
  }
565
0
566
0
  if (!aCompositable.AddTextureClient(texture)) {
567
0
    gfxCriticalError() << "[Tiling:Client] Failed to connect a TextureClient";
568
0
    return nullptr;
569
0
  }
570
0
571
0
  return texture.forget();
572
0
}
573
574
void
575
TileClient::GetSyncTextureSerials(SurfaceMode aMode, nsTArray<uint64_t>& aSerials)
576
0
{
577
0
  if (mFrontBuffer &&
578
0
      mFrontBuffer->HasIntermediateBuffer() &&
579
0
      !mFrontBuffer->IsReadLocked() &&
580
0
      (aMode != SurfaceMode::SURFACE_COMPONENT_ALPHA || (
581
0
        mFrontBufferOnWhite && !mFrontBufferOnWhite->IsReadLocked())))
582
0
  {
583
0
    return;
584
0
  }
585
0
586
0
  if (mBackBuffer &&
587
0
      !mBackBuffer->HasIntermediateBuffer() &&
588
0
      mBackBuffer->IsReadLocked()) {
589
0
    aSerials.AppendElement(mBackBuffer->GetSerial());
590
0
  }
591
0
592
0
  if (aMode == SurfaceMode::SURFACE_COMPONENT_ALPHA &&
593
0
      mBackBufferOnWhite &&
594
0
      !mBackBufferOnWhite->HasIntermediateBuffer() &&
595
0
      mBackBufferOnWhite->IsReadLocked()) {
596
0
    aSerials.AppendElement(mBackBufferOnWhite->GetSerial());
597
0
  }
598
0
}
599
600
Maybe<AcquiredBackBuffer>
601
TileClient::AcquireBackBuffer(CompositableClient& aCompositable,
602
                              const nsIntRegion& aDirtyRegion,
603
                              const nsIntRegion& aVisibleRegion,
604
                              gfxContentType aContent,
605
                              SurfaceMode aMode,
606
                              TilePaintFlags aFlags)
607
0
{
608
0
  if (!mAllocator) {
609
0
    gfxCriticalError() << "[TileClient] Missing TextureClientAllocator.";
610
0
    return Nothing();
611
0
  }
612
0
  if (aMode != SurfaceMode::SURFACE_COMPONENT_ALPHA) {
613
0
    // It can happen that a component-alpha layer stops being on component alpha
614
0
    // on the next frame, just drop the buffers on white if that happens.
615
0
    if (mBackBufferOnWhite) {
616
0
      mAllocator->ReportClientLost();
617
0
      mBackBufferOnWhite = nullptr;
618
0
    }
619
0
    if (mFrontBufferOnWhite) {
620
0
      mAllocator->ReportClientLost();
621
0
      mFrontBufferOnWhite = nullptr;
622
0
    }
623
0
  }
624
0
625
0
  // Try to re-use the front-buffer if possible
626
0
  if (mFrontBuffer &&
627
0
      mFrontBuffer->HasIntermediateBuffer() &&
628
0
      !mFrontBuffer->IsReadLocked() &&
629
0
      (aMode != SurfaceMode::SURFACE_COMPONENT_ALPHA || (
630
0
        mFrontBufferOnWhite && !mFrontBufferOnWhite->IsReadLocked()))) {
631
0
    // If we had a backbuffer we no longer need it since we can re-use the
632
0
    // front buffer here. It can be worth it to hold on to the back buffer
633
0
    // so we don't need to pay the cost of initializing a new back buffer
634
0
    // later (copying pixels and texture upload). But this could increase
635
0
    // our memory usage and lead to OOM more frequently from spikes in usage,
636
0
    // so we have this behavior behind a pref.
637
0
    if (!gfxPrefs::LayersTileRetainBackBuffer()) {
638
0
      DiscardBackBuffer();
639
0
    }
640
0
    Flip();
641
0
  } else {
642
0
    if (!mBackBuffer || mBackBuffer->IsReadLocked()) {
643
0
      mBackBuffer.Set(this,
644
0
        CreateBackBufferTexture(mBackBuffer, aCompositable, mAllocator)
645
0
      );
646
0
      if (!mBackBuffer) {
647
0
        DiscardBackBuffer();
648
0
        DiscardFrontBuffer();
649
0
        return Nothing();
650
0
      }
651
0
      mInvalidBack = IntRect(IntPoint(), mBackBuffer->GetSize());
652
0
    }
653
0
654
0
    if (aMode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
655
0
      if (!mBackBufferOnWhite || mBackBufferOnWhite->IsReadLocked()) {
656
0
        mBackBufferOnWhite = CreateBackBufferTexture(
657
0
          mBackBufferOnWhite, aCompositable, mAllocator
658
0
        );
659
0
        if (!mBackBufferOnWhite) {
660
0
          DiscardBackBuffer();
661
0
          DiscardFrontBuffer();
662
0
          return Nothing();
663
0
        }
664
0
        mInvalidBack = IntRect(IntPoint(), mBackBufferOnWhite->GetSize());
665
0
      }
666
0
    }
667
0
  }
668
0
669
0
  // Lock the texture clients
670
0
  OpenMode lockMode = aFlags & TilePaintFlags::Async ? OpenMode::OPEN_READ_WRITE_ASYNC
671
0
                                                     : OpenMode::OPEN_READ_WRITE;
672
0
673
0
  if (!mBackBuffer->Lock(lockMode)) {
674
0
    gfxCriticalError() << "[Tiling:Client] Failed to lock a tile (B)";
675
0
    DiscardBackBuffer();
676
0
    DiscardFrontBuffer();
677
0
    return Nothing();
678
0
  }
679
0
680
0
  if (mBackBufferOnWhite && !mBackBufferOnWhite->Lock(lockMode)) {
681
0
    gfxCriticalError() << "[Tiling:Client] Failed to lock a tile (W)";
682
0
    DiscardBackBuffer();
683
0
    DiscardFrontBuffer();
684
0
    return Nothing();
685
0
  }
686
0
687
0
  // Borrow their draw targets
688
0
  RefPtr<gfx::DrawTarget> backBuffer = mBackBuffer->BorrowDrawTarget();
689
0
  RefPtr<gfx::DrawTarget> backBufferOnWhite = nullptr;
690
0
  if (mBackBufferOnWhite) {
691
0
    backBufferOnWhite = mBackBufferOnWhite->BorrowDrawTarget();
692
0
  }
693
0
694
0
  if (!backBuffer || (mBackBufferOnWhite && !backBufferOnWhite)) {
695
0
    gfxCriticalError() << "[Tiling:Client] Failed to acquire draw targets for tile";
696
0
    DiscardBackBuffer();
697
0
    DiscardFrontBuffer();
698
0
    return Nothing();
699
0
  }
700
0
701
0
  // Construct a dual target if necessary
702
0
  RefPtr<DrawTarget> target;
703
0
  if (backBufferOnWhite) {
704
0
    backBuffer = Factory::CreateDualDrawTarget(backBuffer, backBufferOnWhite);
705
0
    backBufferOnWhite = nullptr;
706
0
    target = backBuffer;
707
0
  } else {
708
0
    target = backBuffer;
709
0
  }
710
0
711
0
  // Construct a capture draw target if necessary
712
0
  RefPtr<DrawTargetCapture> capture;
713
0
  if (aFlags & TilePaintFlags::Async) {
714
0
    capture = Factory::CreateCaptureDrawTargetForTarget(target, gfxPrefs::LayersOMTPCaptureLimit());
715
0
    target = capture;
716
0
  }
717
0
718
0
  // Gather texture clients
719
0
  AutoTArray<RefPtr<TextureClient>, 4> clients;
720
0
  clients.AppendElement(RefPtr<TextureClient>(mBackBuffer));
721
0
  if (mBackBufferOnWhite) {
722
0
    clients.AppendElement(mBackBufferOnWhite);
723
0
  }
724
0
725
0
  // Copy from the front buerr to the back if necessary
726
0
  IntRect updatedRect;
727
0
  ValidateFromFront(aDirtyRegion,
728
0
                    aVisibleRegion,
729
0
                    target,
730
0
                    aFlags,
731
0
                    &updatedRect,
732
0
                    &clients);
733
0
734
0
  return Some(AcquiredBackBuffer {
735
0
    target,
736
0
    capture,
737
0
    backBuffer,
738
0
    std::move(updatedRect),
739
0
    std::move(clients),
740
0
  });
741
0
}
742
743
TileDescriptor
744
TileClient::GetTileDescriptor()
745
0
{
746
0
  if (IsPlaceholderTile()) {
747
0
    mWasPlaceholder = true;
748
0
    return PlaceholderTileDescriptor();
749
0
  }
750
0
  bool wasPlaceholder = mWasPlaceholder;
751
0
  mWasPlaceholder = false;
752
0
753
0
  bool readLocked = mFrontBuffer->OnForwardedToHost();
754
0
  bool readLockedOnWhite = false;
755
0
756
0
  if (mFrontBufferOnWhite) {
757
0
    readLockedOnWhite = mFrontBufferOnWhite->OnForwardedToHost();
758
0
  }
759
0
760
0
  return TexturedTileDescriptor(nullptr, mFrontBuffer->GetIPDLActor(),
761
0
                                mFrontBufferOnWhite ? MaybeTexture(mFrontBufferOnWhite->GetIPDLActor()) : MaybeTexture(null_t()),
762
0
                                mUpdateRect,
763
0
                                readLocked, readLockedOnWhite,
764
0
                                wasPlaceholder);
765
0
}
766
767
void
768
ClientTiledLayerBuffer::UnlockTile(TileClient& aTile)
769
0
{
770
0
  // We locked the back buffer, and flipped so we now need to unlock the front
771
0
  if (aTile.mFrontBuffer && aTile.mFrontBuffer->IsLocked()) {
772
0
    aTile.mFrontBuffer->Unlock();
773
0
    aTile.mFrontBuffer->SyncWithObject(mCompositableClient.GetForwarder()->GetSyncObject());
774
0
  }
775
0
  if (aTile.mFrontBufferOnWhite && aTile.mFrontBufferOnWhite->IsLocked()) {
776
0
    aTile.mFrontBufferOnWhite->Unlock();
777
0
    aTile.mFrontBufferOnWhite->SyncWithObject(mCompositableClient.GetForwarder()->GetSyncObject());
778
0
  }
779
0
  if (aTile.mBackBuffer && aTile.mBackBuffer->IsLocked()) {
780
0
    aTile.mBackBuffer->Unlock();
781
0
  }
782
0
  if (aTile.mBackBufferOnWhite && aTile.mBackBufferOnWhite->IsLocked()) {
783
0
    aTile.mBackBufferOnWhite->Unlock();
784
0
  }
785
0
}
786
787
void
788
TiledContentClient::PrintInfo(std::stringstream& aStream, const char* aPrefix)
789
0
{
790
0
  aStream << aPrefix;
791
0
  aStream << nsPrintfCString("%sTiledContentClient (0x%p)", mName, this).get();
792
0
}
793
794
void
795
TiledContentClient::Dump(std::stringstream& aStream,
796
                         const char* aPrefix,
797
                         bool aDumpHtml,
798
                         TextureDumpMode aCompress)
799
0
{
800
0
  GetTiledBuffer()->Dump(aStream, aPrefix, aDumpHtml, aCompress);
801
0
}
802
803
void
804
BasicTiledLayerPaintData::ResetPaintData()
805
0
{
806
0
  mLowPrecisionPaintCount = 0;
807
0
  mPaintFinished = false;
808
0
  mHasTransformAnimation = false;
809
0
  mCompositionBounds.SetEmpty();
810
0
  mCriticalDisplayPort = Nothing();
811
0
}
812
813
} // namespace layers
814
} // namespace mozilla