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