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
36 // buffer to the back buffer on tile updates. If the valid region is smaller
37 // than this, we just redraw it and save on the copy (and requisite
38 // surface-locking involved).
39 #define MINIMUM_TILE_COPY_AREA (1.f / 16.f)
40 
41 #ifdef GFX_TILEDLAYER_DEBUG_OVERLAY
42 #include "cairo.h"
43 #include <sstream>
44 using mozilla::layers::Layer;
DrawDebugOverlay(mozilla::gfx::DrawTarget * dt,int x,int y,int width,int height)45 static void DrawDebugOverlay(mozilla::gfx::DrawTarget* dt, int x, int y,
46                              int width, int height) {
47   gfxContext c(dt);
48 
49   // Draw border
50   c.NewPath();
51   c.SetDeviceColor(Color(0.f, 0.f, 0.f));
52   c.Rectangle(gfxRect(0, 0, width, height));
53   c.Stroke();
54 
55   // Build tile description
56   std::stringstream ss;
57   ss << x << ", " << y;
58 
59   // Draw text using cairo toy text API
60   // XXX: this drawing will silently fail if |dt| doesn't have a Cairo backend
61   cairo_t* cr = gfxFont::RefCairo(dt);
62   cairo_set_font_size(cr, 25);
63   cairo_text_extents_t extents;
64   cairo_text_extents(cr, ss.str().c_str(), &extents);
65 
66   int textWidth = extents.width + 6;
67 
68   c.NewPath();
69   c.SetDeviceColor(Color(0.f, 0.f, 0.f));
70   c.Rectangle(gfxRect(gfxPoint(2, 2), gfxSize(textWidth, 30)));
71   c.Fill();
72 
73   c.NewPath();
74   c.SetDeviceColor(Color(1.0, 0.0, 0.0));
75   c.Rectangle(gfxRect(gfxPoint(2, 2), gfxSize(textWidth, 30)));
76   c.Stroke();
77 
78   c.NewPath();
79   cairo_move_to(cr, 4, 28);
80   cairo_show_text(cr, ss.str().c_str());
81 }
82 
83 #endif
84 
85 namespace mozilla {
86 
87 using namespace gfx;
88 
89 namespace layers {
90 
MultiTiledContentClient(ClientTiledPaintedLayer & aPaintedLayer,ClientLayerManager * aManager)91 MultiTiledContentClient::MultiTiledContentClient(
92     ClientTiledPaintedLayer& aPaintedLayer, ClientLayerManager* aManager)
93     : TiledContentClient(aManager, "Multi"),
94       mTiledBuffer(aPaintedLayer, *this, aManager, &mSharedFrameMetricsHelper),
95       mLowPrecisionTiledBuffer(aPaintedLayer, *this, aManager,
96                                &mSharedFrameMetricsHelper) {
97   MOZ_COUNT_CTOR(MultiTiledContentClient);
98   mLowPrecisionTiledBuffer.SetResolution(gfxPrefs::LowPrecisionResolution());
99   mHasLowPrecision = gfxPrefs::UseLowPrecisionBuffer();
100 }
101 
ClearCachedResources()102 void MultiTiledContentClient::ClearCachedResources() {
103   CompositableClient::ClearCachedResources();
104   mTiledBuffer.DiscardBuffers();
105   mLowPrecisionTiledBuffer.DiscardBuffers();
106 }
107 
UpdatedBuffer(TiledBufferType aType)108 void MultiTiledContentClient::UpdatedBuffer(TiledBufferType aType) {
109   ClientMultiTiledLayerBuffer* buffer = aType == LOW_PRECISION_TILED_BUFFER
110                                             ? &mLowPrecisionTiledBuffer
111                                             : &mTiledBuffer;
112 
113   MOZ_ASSERT(aType != LOW_PRECISION_TILED_BUFFER || mHasLowPrecision);
114 
115   mForwarder->UseTiledLayerBuffer(this, buffer->GetSurfaceDescriptorTiles());
116   buffer->ClearPaintedRegion();
117 }
118 
SharedFrameMetricsHelper()119 SharedFrameMetricsHelper::SharedFrameMetricsHelper()
120     : mLastProgressiveUpdateWasLowPrecision(false),
121       mProgressiveUpdateWasInDanger(false) {
122   MOZ_COUNT_CTOR(SharedFrameMetricsHelper);
123 }
124 
~SharedFrameMetricsHelper()125 SharedFrameMetricsHelper::~SharedFrameMetricsHelper() {
126   MOZ_COUNT_DTOR(SharedFrameMetricsHelper);
127 }
128 
FuzzyEquals(float a,float b)129 static inline bool FuzzyEquals(float a, float b) {
130   return (fabsf(a - b) < 1e-6);
131 }
132 
ComputeViewTransform(const FrameMetrics & aContentMetrics,const FrameMetrics & aCompositorMetrics)133 static AsyncTransform ComputeViewTransform(
134     const FrameMetrics& aContentMetrics,
135     const FrameMetrics& aCompositorMetrics) {
136   // This is basically the same code as
137   // AsyncPanZoomController::GetCurrentAsyncTransform but with aContentMetrics
138   // used in place of mLastContentPaintMetrics, because they should be
139   // equivalent, modulo race conditions while transactions are inflight.
140 
141   ParentLayerPoint translation = (aCompositorMetrics.GetScrollOffset() -
142                                   aContentMetrics.GetScrollOffset()) *
143                                  aCompositorMetrics.GetZoom();
144   return AsyncTransform(aCompositorMetrics.GetAsyncZoom(), -translation);
145 }
146 
UpdateFromCompositorFrameMetrics(const LayerMetricsWrapper & aLayer,bool aHasPendingNewThebesContent,bool aLowPrecision,AsyncTransform & aViewTransform)147 bool SharedFrameMetricsHelper::UpdateFromCompositorFrameMetrics(
148     const LayerMetricsWrapper& aLayer, bool aHasPendingNewThebesContent,
149     bool aLowPrecision, AsyncTransform& aViewTransform) {
150   MOZ_ASSERT(aLayer);
151 
152   CompositorBridgeChild* compositor = nullptr;
153   if (aLayer.Manager() && aLayer.Manager()->AsClientLayerManager()) {
154     compositor =
155         aLayer.Manager()->AsClientLayerManager()->GetCompositorBridgeChild();
156   }
157 
158   if (!compositor) {
159     return false;
160   }
161 
162   const FrameMetrics& contentMetrics = aLayer.Metrics();
163   FrameMetrics compositorMetrics;
164 
165   if (!compositor->LookupCompositorFrameMetrics(contentMetrics.GetScrollId(),
166                                                 compositorMetrics)) {
167     return false;
168   }
169 
170   aViewTransform = ComputeViewTransform(contentMetrics, compositorMetrics);
171 
172   // Reset the checkerboard risk flag when switching to low precision
173   // rendering.
174   if (aLowPrecision && !mLastProgressiveUpdateWasLowPrecision) {
175     // Skip low precision rendering until we're at risk of checkerboarding.
176     if (!mProgressiveUpdateWasInDanger) {
177       TILING_LOG(
178           "TILING: Aborting low-precision rendering because not at risk of "
179           "checkerboarding\n");
180       return true;
181     }
182     mProgressiveUpdateWasInDanger = false;
183   }
184   mLastProgressiveUpdateWasLowPrecision = aLowPrecision;
185 
186   // Always abort updates if the resolution has changed. There's no use
187   // in drawing at the incorrect resolution.
188   if (!FuzzyEquals(compositorMetrics.GetZoom().xScale,
189                    contentMetrics.GetZoom().xScale) ||
190       !FuzzyEquals(compositorMetrics.GetZoom().yScale,
191                    contentMetrics.GetZoom().yScale)) {
192     TILING_LOG("TILING: Aborting because resolution changed from %s to %s\n",
193                ToString(contentMetrics.GetZoom()).c_str(),
194                ToString(compositorMetrics.GetZoom()).c_str());
195     return true;
196   }
197 
198   // Never abort drawing if we can't be sure we've sent a more recent
199   // display-port. If we abort updating when we shouldn't, we can end up
200   // with blank regions on the screen and we open up the risk of entering
201   // an endless updating cycle.
202   if (fabsf(contentMetrics.GetScrollOffset().x -
203             compositorMetrics.GetScrollOffset().x) <= 2 &&
204       fabsf(contentMetrics.GetScrollOffset().y -
205             compositorMetrics.GetScrollOffset().y) <= 2 &&
206       fabsf(contentMetrics.GetDisplayPort().X() -
207             compositorMetrics.GetDisplayPort().X()) <= 2 &&
208       fabsf(contentMetrics.GetDisplayPort().Y() -
209             compositorMetrics.GetDisplayPort().Y()) <= 2 &&
210       fabsf(contentMetrics.GetDisplayPort().Width() -
211             compositorMetrics.GetDisplayPort().Width()) <= 2 &&
212       fabsf(contentMetrics.GetDisplayPort().Height() -
213             compositorMetrics.GetDisplayPort().Height()) <= 2) {
214     return false;
215   }
216 
217   // When not a low precision pass and the page is in danger of checker boarding
218   // abort update.
219   if (!aLowPrecision && !mProgressiveUpdateWasInDanger) {
220     bool scrollUpdatePending = contentMetrics.GetScrollOffsetUpdated() &&
221                                contentMetrics.GetScrollGeneration() !=
222                                    compositorMetrics.GetScrollGeneration();
223     // If scrollUpdatePending is true, then that means the content-side
224     // metrics has a new scroll offset that is going to be forced into the
225     // compositor but it hasn't gotten there yet.
226     // Even though right now comparing the metrics might indicate we're
227     // about to checkerboard (and that's true), the checkerboarding will
228     // disappear as soon as the new scroll offset update is processed
229     // on the compositor side. To avoid leaving things in a low-precision
230     // paint, we need to detect and handle this case (bug 1026756).
231     if (!scrollUpdatePending &&
232         AboutToCheckerboard(contentMetrics, compositorMetrics)) {
233       mProgressiveUpdateWasInDanger = true;
234       return true;
235     }
236   }
237 
238   // Abort drawing stale low-precision content if there's a more recent
239   // display-port in the pipeline.
240   if (aLowPrecision && !aHasPendingNewThebesContent) {
241     TILING_LOG(
242         "TILING: Aborting low-precision because of new pending content\n");
243     return true;
244   }
245 
246   return false;
247 }
248 
AboutToCheckerboard(const FrameMetrics & aContentMetrics,const FrameMetrics & aCompositorMetrics)249 bool SharedFrameMetricsHelper::AboutToCheckerboard(
250     const FrameMetrics& aContentMetrics,
251     const FrameMetrics& aCompositorMetrics) {
252   // The size of the painted area is originally computed in layer pixels in
253   // layout, but then converted to app units and then back to CSS pixels before
254   // being put in the FrameMetrics. This process can introduce some rounding
255   // error, so we inflate the rect by one app unit to account for that.
256   CSSRect painted = (aContentMetrics.GetCriticalDisplayPort().IsEmpty()
257                          ? aContentMetrics.GetDisplayPort()
258                          : aContentMetrics.GetCriticalDisplayPort()) +
259                     aContentMetrics.GetScrollOffset();
260   painted.Inflate(CSSMargin::FromAppUnits(nsMargin(1, 1, 1, 1)));
261 
262   // Inflate the rect by the danger zone. See the description of the danger zone
263   // prefs in AsyncPanZoomController.cpp for an explanation of this.
264   CSSRect showing =
265       CSSRect(aCompositorMetrics.GetScrollOffset(),
266               aCompositorMetrics.CalculateBoundedCompositedSizeInCssPixels());
267   showing.Inflate(
268       LayerSize(gfxPrefs::APZDangerZoneX(), gfxPrefs::APZDangerZoneY()) /
269       aCompositorMetrics.LayersPixelsPerCSSPixel());
270 
271   // Clamp both rects to the scrollable rect, because having either of those
272   // exceed the scrollable rect doesn't make sense, and could lead to false
273   // positives.
274   painted = painted.Intersect(aContentMetrics.GetScrollableRect());
275   showing = showing.Intersect(aContentMetrics.GetScrollableRect());
276 
277   if (!painted.Contains(showing)) {
278     TILING_LOG("TILING: About to checkerboard; content %s\n",
279                Stringify(aContentMetrics).c_str());
280     TILING_LOG("TILING: About to checkerboard; painted %s\n",
281                Stringify(painted).c_str());
282     TILING_LOG("TILING: About to checkerboard; compositor %s\n",
283                Stringify(aCompositorMetrics).c_str());
284     TILING_LOG("TILING: About to checkerboard; showing %s\n",
285                Stringify(showing).c_str());
286     return true;
287   }
288   return false;
289 }
290 
ClientMultiTiledLayerBuffer(ClientTiledPaintedLayer & aPaintedLayer,CompositableClient & aCompositableClient,ClientLayerManager * aManager,SharedFrameMetricsHelper * aHelper)291 ClientMultiTiledLayerBuffer::ClientMultiTiledLayerBuffer(
292     ClientTiledPaintedLayer& aPaintedLayer,
293     CompositableClient& aCompositableClient, ClientLayerManager* aManager,
294     SharedFrameMetricsHelper* aHelper)
295     : ClientTiledLayerBuffer(aPaintedLayer, aCompositableClient),
296       mManager(aManager),
297       mCallback(nullptr),
298       mCallbackData(nullptr),
299       mSharedFrameMetricsHelper(aHelper) {}
300 
HasFormatChanged() const301 bool ClientTiledLayerBuffer::HasFormatChanged() const {
302   SurfaceMode mode;
303   gfxContentType content = GetContentType(&mode);
304   return content != mLastPaintContentType || mode != mLastPaintSurfaceMode;
305 }
306 
GetContentType(SurfaceMode * aMode) const307 gfxContentType ClientTiledLayerBuffer::GetContentType(
308     SurfaceMode* aMode) const {
309   gfxContentType content = mPaintedLayer.CanUseOpaqueSurface()
310                                ? gfxContentType::COLOR
311                                : gfxContentType::COLOR_ALPHA;
312   SurfaceMode mode = mPaintedLayer.GetSurfaceMode();
313 
314   if (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
315 #if defined(MOZ_GFX_OPTIMIZE_MOBILE)
316     mode = SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA;
317 #else
318     if (!mPaintedLayer.GetParent() ||
319         !mPaintedLayer.GetParent()->SupportsComponentAlphaChildren()) {
320       mode = SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA;
321     } else {
322       content = gfxContentType::COLOR;
323     }
324 #endif
325   } else if (mode == SurfaceMode::SURFACE_OPAQUE) {
326 #if defined(MOZ_GFX_OPTIMIZE_MOBILE)
327     if (IsLowPrecision()) {
328       // If we're in low-res mode, drawing can sample from outside the visible
329       // region. Make sure that we only sample transparency if that happens.
330       mode = SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA;
331       content = gfxContentType::COLOR_ALPHA;
332     }
333 #else
334     if (mPaintedLayer.MayResample()) {
335       mode = SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA;
336       content = gfxContentType::COLOR_ALPHA;
337     }
338 #endif
339   }
340 
341   if (aMode) {
342     *aMode = mode;
343   }
344   return content;
345 }
346 
347 class TileExpiry final : public nsExpirationTracker<TileClient, 3> {
348  public:
TileExpiry()349   TileExpiry() : nsExpirationTracker<TileClient, 3>(1000, "TileExpiry") {}
350 
AddTile(TileClient * aTile)351   static void AddTile(TileClient* aTile) {
352     if (!sTileExpiry) {
353       sTileExpiry = MakeUnique<TileExpiry>();
354     }
355 
356     sTileExpiry->AddObject(aTile);
357   }
358 
RemoveTile(TileClient * aTile)359   static void RemoveTile(TileClient* aTile) {
360     MOZ_ASSERT(sTileExpiry);
361     sTileExpiry->RemoveObject(aTile);
362   }
363 
Shutdown()364   static void Shutdown() { sTileExpiry = nullptr; }
365 
366  private:
NotifyExpired(TileClient * aTile)367   virtual void NotifyExpired(TileClient* aTile) override {
368     aTile->DiscardBackBuffer();
369   }
370 
371   static UniquePtr<TileExpiry> sTileExpiry;
372 };
373 UniquePtr<TileExpiry> TileExpiry::sTileExpiry;
374 
ShutdownTileCache()375 void ShutdownTileCache() { TileExpiry::Shutdown(); }
376 
Set(TileClient * const aContainer,RefPtr<TextureClient> aNewValue)377 void TileClient::PrivateProtector::Set(TileClient* const aContainer,
378                                        RefPtr<TextureClient> aNewValue) {
379   if (mBuffer) {
380     TileExpiry::RemoveTile(aContainer);
381   }
382   mBuffer = aNewValue;
383   if (mBuffer) {
384     TileExpiry::AddTile(aContainer);
385   }
386 }
387 
Set(TileClient * const aContainer,TextureClient * aNewValue)388 void TileClient::PrivateProtector::Set(TileClient* const aContainer,
389                                        TextureClient* aNewValue) {
390   Set(aContainer, RefPtr<TextureClient>(aNewValue));
391 }
392 
393 // Placeholder
TileClient()394 TileClient::TileClient() : mWasPlaceholder(false) {}
395 
~TileClient()396 TileClient::~TileClient() {
397   if (mExpirationState.IsTracked()) {
398     MOZ_ASSERT(mBackBuffer);
399     TileExpiry::RemoveTile(this);
400   }
401 }
402 
TileClient(const TileClient & o)403 TileClient::TileClient(const TileClient& o) {
404   mBackBuffer.Set(this, o.mBackBuffer);
405   mBackBufferOnWhite = o.mBackBufferOnWhite;
406   mFrontBuffer = o.mFrontBuffer;
407   mFrontBufferOnWhite = o.mFrontBufferOnWhite;
408   mWasPlaceholder = o.mWasPlaceholder;
409   mUpdateRect = o.mUpdateRect;
410 #ifdef GFX_TILEDLAYER_DEBUG_OVERLAY
411   mLastUpdate = o.mLastUpdate;
412 #endif
413   mAllocator = o.mAllocator;
414   mInvalidFront = o.mInvalidFront;
415   mInvalidBack = o.mInvalidBack;
416 }
417 
operator =(const TileClient & o)418 TileClient& TileClient::operator=(const TileClient& o) {
419   if (this == &o) return *this;
420   mBackBuffer.Set(this, o.mBackBuffer);
421   mBackBufferOnWhite = o.mBackBufferOnWhite;
422   mFrontBuffer = o.mFrontBuffer;
423   mFrontBufferOnWhite = o.mFrontBufferOnWhite;
424   mWasPlaceholder = o.mWasPlaceholder;
425   mUpdateRect = o.mUpdateRect;
426 #ifdef GFX_TILEDLAYER_DEBUG_OVERLAY
427   mLastUpdate = o.mLastUpdate;
428 #endif
429   mAllocator = o.mAllocator;
430   mInvalidFront = o.mInvalidFront;
431   mInvalidBack = o.mInvalidBack;
432   return *this;
433 }
434 
Dump(std::stringstream & aStream)435 void TileClient::Dump(std::stringstream& aStream) {
436   aStream << "TileClient(bb=" << (TextureClient*)mBackBuffer
437           << " fb=" << mFrontBuffer.get();
438   if (mBackBufferOnWhite) {
439     aStream << " bbow=" << mBackBufferOnWhite.get();
440   }
441   if (mFrontBufferOnWhite) {
442     aStream << " fbow=" << mFrontBufferOnWhite.get();
443   }
444   aStream << ")";
445 }
446 
Flip()447 void TileClient::Flip() {
448   RefPtr<TextureClient> frontBuffer = mFrontBuffer;
449   RefPtr<TextureClient> frontBufferOnWhite = mFrontBufferOnWhite;
450   mFrontBuffer = mBackBuffer;
451   mFrontBufferOnWhite = mBackBufferOnWhite;
452   mBackBuffer.Set(this, frontBuffer);
453   mBackBufferOnWhite = frontBufferOnWhite;
454   nsIntRegion invalidFront = mInvalidFront;
455   mInvalidFront = mInvalidBack;
456   mInvalidBack = invalidFront;
457 }
458 
CopyFrontToBack(TextureClient * aFront,TextureClient * aBack,const gfx::IntRect & aRectToCopy,TilePaintFlags aFlags,std::vector<CapturedTiledPaintState::Copy> * aCopies,std::vector<RefPtr<TextureClient>> * aClients)459 static bool CopyFrontToBack(TextureClient* aFront, TextureClient* aBack,
460                             const gfx::IntRect& aRectToCopy,
461                             TilePaintFlags aFlags,
462                             std::vector<CapturedTiledPaintState::Copy>* aCopies,
463                             std::vector<RefPtr<TextureClient>>* aClients) {
464   bool asyncPaint = !!(aFlags & TilePaintFlags::Async);
465   OpenMode asyncFlags = asyncPaint ? OpenMode::OPEN_ASYNC : OpenMode::OPEN_NONE;
466 
467   TextureClientAutoLock frontLock(aFront, OpenMode::OPEN_READ | asyncFlags);
468   if (!frontLock.Succeeded()) {
469     return false;
470   }
471 
472   if (!aBack->Lock(OpenMode::OPEN_READ_WRITE | asyncFlags)) {
473     gfxCriticalError()
474         << "[Tiling:Client] Failed to lock the tile's back buffer";
475     return false;
476   }
477 
478   RefPtr<gfx::DrawTarget> backBuffer = aBack->BorrowDrawTarget();
479   if (!backBuffer) {
480     gfxWarning()
481         << "[Tiling:Client] Failed to aquire the back buffer's draw target";
482     return false;
483   }
484 
485   RefPtr<gfx::DrawTarget> frontBuffer = aFront->BorrowDrawTarget();
486   if (!frontBuffer) {
487     gfxWarning()
488         << "[Tiling:Client] Failed to aquire the front buffer's draw target";
489     return false;
490   }
491 
492   auto copy = CapturedTiledPaintState::Copy{frontBuffer, backBuffer,
493                                             aRectToCopy, aRectToCopy.TopLeft()};
494 
495   if (asyncPaint && aCopies) {
496     aClients->push_back(aFront);
497     aCopies->push_back(copy);
498   } else {
499     copy.CopyBuffer();
500   }
501   return true;
502 }
503 
ValidateBackBufferFromFront(const nsIntRegion & aDirtyRegion,nsIntRegion & aAddPaintedRegion,TilePaintFlags aFlags,std::vector<CapturedTiledPaintState::Copy> * aCopies,std::vector<RefPtr<TextureClient>> * aClients)504 void TileClient::ValidateBackBufferFromFront(
505     const nsIntRegion& aDirtyRegion, nsIntRegion& aAddPaintedRegion,
506     TilePaintFlags aFlags, std::vector<CapturedTiledPaintState::Copy>* aCopies,
507     std::vector<RefPtr<TextureClient>>* aClients) {
508   if (mBackBuffer && mFrontBuffer) {
509     gfx::IntSize tileSize = mFrontBuffer->GetSize();
510     const IntRect tileRect = IntRect(0, 0, tileSize.width, tileSize.height);
511 
512     if (aDirtyRegion.Contains(tileRect)) {
513       // The dirty region means that we no longer need the front buffer, so
514       // discard it.
515       DiscardFrontBuffer();
516     } else {
517       // Region that needs copying.
518       nsIntRegion regionToCopy = mInvalidBack;
519 
520       regionToCopy.Sub(regionToCopy, aDirtyRegion);
521 
522       aAddPaintedRegion = regionToCopy;
523 
524       if (regionToCopy.IsEmpty()) {
525         // Just redraw it all.
526         return;
527       }
528 
529       // Copy the bounding rect of regionToCopy. As tiles are quite small, it
530       // is unlikely that we'd save much by copying each individual rect of the
531       // region, but we can reevaluate this if it becomes an issue.
532       const IntRect rectToCopy = regionToCopy.GetBounds();
533       gfx::IntRect gfxRectToCopy(rectToCopy.X(), rectToCopy.Y(),
534                                  rectToCopy.Width(), rectToCopy.Height());
535       if (CopyFrontToBack(mFrontBuffer, mBackBuffer, gfxRectToCopy, aFlags,
536                           aCopies, aClients)) {
537         if (mBackBufferOnWhite) {
538           MOZ_ASSERT(mFrontBufferOnWhite);
539           if (CopyFrontToBack(mFrontBufferOnWhite, mBackBufferOnWhite,
540                               gfxRectToCopy, aFlags, aCopies, aClients)) {
541             mInvalidBack.SetEmpty();
542           }
543         } else {
544           mInvalidBack.SetEmpty();
545         }
546       }
547     }
548   }
549 }
550 
DiscardFrontBuffer()551 void TileClient::DiscardFrontBuffer() {
552   if (mFrontBuffer) {
553     MOZ_ASSERT(mFrontBuffer->GetReadLock());
554 
555     MOZ_ASSERT(mAllocator);
556     if (mAllocator) {
557       mAllocator->ReturnTextureClientDeferred(mFrontBuffer);
558       if (mFrontBufferOnWhite) {
559         mAllocator->ReturnTextureClientDeferred(mFrontBufferOnWhite);
560       }
561     }
562 
563     if (mFrontBuffer->IsLocked()) {
564       mFrontBuffer->Unlock();
565     }
566     if (mFrontBufferOnWhite && mFrontBufferOnWhite->IsLocked()) {
567       mFrontBufferOnWhite->Unlock();
568     }
569     mFrontBuffer = nullptr;
570     mFrontBufferOnWhite = nullptr;
571   }
572 }
573 
DiscardTexture(TextureClient * aTexture,TextureClientAllocator * aAllocator)574 static void DiscardTexture(TextureClient* aTexture,
575                            TextureClientAllocator* aAllocator) {
576   MOZ_ASSERT(aAllocator);
577   if (aTexture && aAllocator) {
578     MOZ_ASSERT(aTexture->GetReadLock());
579     if (!aTexture->HasSynchronization() && aTexture->IsReadLocked()) {
580       // Our current back-buffer is still locked by the compositor. This can
581       // occur when the client is producing faster than the compositor can
582       // consume. In this case we just want to drop it and not return it to the
583       // pool.
584       aAllocator->ReportClientLost();
585     } else {
586       aAllocator->ReturnTextureClientDeferred(aTexture);
587     }
588     if (aTexture->IsLocked()) {
589       aTexture->Unlock();
590     }
591   }
592 }
593 
DiscardBackBuffer()594 void TileClient::DiscardBackBuffer() {
595   if (mBackBuffer) {
596     DiscardTexture(mBackBuffer, mAllocator);
597     mBackBuffer.Set(this, nullptr);
598     DiscardTexture(mBackBufferOnWhite, mAllocator);
599     mBackBufferOnWhite = nullptr;
600   }
601 }
602 
CreateBackBufferTexture(TextureClient * aCurrentTexture,CompositableClient & aCompositable,TextureClientAllocator * aAllocator)603 static already_AddRefed<TextureClient> CreateBackBufferTexture(
604     TextureClient* aCurrentTexture, CompositableClient& aCompositable,
605     TextureClientAllocator* aAllocator) {
606   if (aCurrentTexture) {
607     // Our current back-buffer is still locked by the compositor. This can occur
608     // when the client is producing faster than the compositor can consume. In
609     // this case we just want to drop it and not return it to the pool.
610     aAllocator->ReportClientLost();
611   }
612 
613   RefPtr<TextureClient> texture = aAllocator->GetTextureClient();
614 
615   if (!texture) {
616     gfxCriticalError() << "[Tiling:Client] Failed to allocate a TextureClient";
617     return nullptr;
618   }
619 
620   if (!aCompositable.AddTextureClient(texture)) {
621     gfxCriticalError() << "[Tiling:Client] Failed to connect a TextureClient";
622     return nullptr;
623   }
624 
625   return texture.forget();
626 }
627 
GetBackBuffer(CompositableClient & aCompositable,const nsIntRegion & aDirtyRegion,gfxContentType aContent,SurfaceMode aMode,nsIntRegion & aAddPaintedRegion,TilePaintFlags aFlags,RefPtr<TextureClient> * aBackBufferOnWhite,std::vector<CapturedTiledPaintState::Copy> * aCopies,std::vector<RefPtr<TextureClient>> * aClients)628 TextureClient* TileClient::GetBackBuffer(
629     CompositableClient& aCompositable, const nsIntRegion& aDirtyRegion,
630     gfxContentType aContent, SurfaceMode aMode, nsIntRegion& aAddPaintedRegion,
631     TilePaintFlags aFlags, RefPtr<TextureClient>* aBackBufferOnWhite,
632     std::vector<CapturedTiledPaintState::Copy>* aCopies,
633     std::vector<RefPtr<TextureClient>>* aClients) {
634   if (!mAllocator) {
635     gfxCriticalError() << "[TileClient] Missing TextureClientAllocator.";
636     return nullptr;
637   }
638   if (aMode != SurfaceMode::SURFACE_COMPONENT_ALPHA) {
639     // It can happen that a component-alpha layer stops being on component alpha
640     // on the next frame, just drop the buffers on white if that happens.
641     if (mBackBufferOnWhite) {
642       mAllocator->ReportClientLost();
643       mBackBufferOnWhite = nullptr;
644     }
645     if (mFrontBufferOnWhite) {
646       mAllocator->ReportClientLost();
647       mFrontBufferOnWhite = nullptr;
648     }
649   }
650 
651   // Try to re-use the front-buffer if possible
652   if (mFrontBuffer && mFrontBuffer->HasIntermediateBuffer() &&
653       !mFrontBuffer->IsReadLocked() &&
654       (aMode != SurfaceMode::SURFACE_COMPONENT_ALPHA ||
655        (mFrontBufferOnWhite && !mFrontBufferOnWhite->IsReadLocked()))) {
656     // If we had a backbuffer we no longer care about it since we'll
657     // re-use the front buffer.
658     DiscardBackBuffer();
659     Flip();
660   } else {
661     if (!mBackBuffer || mBackBuffer->IsReadLocked()) {
662       mBackBuffer.Set(this, CreateBackBufferTexture(mBackBuffer, aCompositable,
663                                                     mAllocator));
664       if (!mBackBuffer) {
665         DiscardBackBuffer();
666         DiscardFrontBuffer();
667         return nullptr;
668       }
669       mInvalidBack = IntRect(IntPoint(), mBackBuffer->GetSize());
670     }
671 
672     if (aMode == SurfaceMode::SURFACE_COMPONENT_ALPHA &&
673         (!mBackBufferOnWhite || mBackBufferOnWhite->IsReadLocked())) {
674       mBackBufferOnWhite = CreateBackBufferTexture(mBackBufferOnWhite,
675                                                    aCompositable, mAllocator);
676       if (!mBackBufferOnWhite) {
677         DiscardBackBuffer();
678         DiscardFrontBuffer();
679         return nullptr;
680       }
681       mInvalidBack = IntRect(IntPoint(), mBackBufferOnWhite->GetSize());
682     }
683 
684     ValidateBackBufferFromFront(aDirtyRegion, aAddPaintedRegion, aFlags,
685                                 aCopies, aClients);
686   }
687 
688   OpenMode lockMode = aFlags & TilePaintFlags::Async
689                           ? OpenMode::OPEN_READ_WRITE_ASYNC
690                           : OpenMode::OPEN_READ_WRITE;
691 
692   if (!mBackBuffer->IsLocked()) {
693     if (!mBackBuffer->Lock(lockMode)) {
694       gfxCriticalError() << "[Tiling:Client] Failed to lock a tile (B)";
695       DiscardBackBuffer();
696       DiscardFrontBuffer();
697       return nullptr;
698     }
699   }
700 
701   if (mBackBufferOnWhite && !mBackBufferOnWhite->IsLocked()) {
702     if (!mBackBufferOnWhite->Lock(lockMode)) {
703       gfxCriticalError() << "[Tiling:Client] Failed to lock a tile (W)";
704       DiscardBackBuffer();
705       DiscardFrontBuffer();
706       return nullptr;
707     }
708   }
709 
710   *aBackBufferOnWhite = mBackBufferOnWhite;
711   return mBackBuffer;
712 }
713 
GetTileDescriptor()714 TileDescriptor TileClient::GetTileDescriptor() {
715   if (IsPlaceholderTile()) {
716     mWasPlaceholder = true;
717     return PlaceholderTileDescriptor();
718   }
719   bool wasPlaceholder = mWasPlaceholder;
720   mWasPlaceholder = false;
721 
722   bool readLocked = mFrontBuffer->OnForwardedToHost();
723   bool readLockedOnWhite = false;
724 
725   if (mFrontBufferOnWhite) {
726     readLockedOnWhite = mFrontBufferOnWhite->OnForwardedToHost();
727   }
728 
729   return TexturedTileDescriptor(
730       nullptr, mFrontBuffer->GetIPDLActor(),
731       mFrontBufferOnWhite ? MaybeTexture(mFrontBufferOnWhite->GetIPDLActor())
732                           : MaybeTexture(null_t()),
733       mUpdateRect, readLocked, readLockedOnWhite, wasPlaceholder);
734 }
735 
DiscardBuffers()736 void ClientMultiTiledLayerBuffer::DiscardBuffers() {
737   for (TileClient& tile : mRetainedTiles) {
738     tile.DiscardBuffers();
739   }
740 }
741 
742 SurfaceDescriptorTiles
GetSurfaceDescriptorTiles()743 ClientMultiTiledLayerBuffer::GetSurfaceDescriptorTiles() {
744   InfallibleTArray<TileDescriptor> tiles;
745 
746   for (TileClient& tile : mRetainedTiles) {
747     TileDescriptor tileDesc = tile.GetTileDescriptor();
748     tiles.AppendElement(tileDesc);
749     // Reset the update rect
750     tile.mUpdateRect = IntRect();
751   }
752   return SurfaceDescriptorTiles(
753       mValidRegion, tiles, mTileOrigin, mTileSize, mTiles.mFirst.x,
754       mTiles.mFirst.y, mTiles.mSize.width, mTiles.mSize.height, mResolution,
755       mFrameResolution.xScale, mFrameResolution.yScale,
756       mWasLastPaintProgressive);
757 }
758 
PaintThebes(const nsIntRegion & aNewValidRegion,const nsIntRegion & aPaintRegion,const nsIntRegion & aDirtyRegion,LayerManager::DrawPaintedLayerCallback aCallback,void * aCallbackData,TilePaintFlags aFlags)759 void ClientMultiTiledLayerBuffer::PaintThebes(
760     const nsIntRegion& aNewValidRegion, const nsIntRegion& aPaintRegion,
761     const nsIntRegion& aDirtyRegion,
762     LayerManager::DrawPaintedLayerCallback aCallback, void* aCallbackData,
763     TilePaintFlags aFlags) {
764   TILING_LOG("TILING %p: PaintThebes painting region %s\n", &mPaintedLayer,
765              Stringify(aPaintRegion).c_str());
766   TILING_LOG("TILING %p: PaintThebes new valid region %s\n", &mPaintedLayer,
767              Stringify(aNewValidRegion).c_str());
768 
769   mCallback = aCallback;
770   mCallbackData = aCallbackData;
771   mWasLastPaintProgressive = !!(aFlags & TilePaintFlags::Progressive);
772 
773 #ifdef GFX_TILEDLAYER_PREF_WARNINGS
774   long start = PR_IntervalNow();
775 #endif
776 
777 #ifdef GFX_TILEDLAYER_PREF_WARNINGS
778   if (PR_IntervalNow() - start > 30) {
779     const IntRect bounds = aPaintRegion.GetBounds();
780     printf_stderr("Time to draw %i: %i, %i, %i, %i\n", PR_IntervalNow() - start,
781                   bounds.x, bounds.y, bounds.width, bounds.height);
782     if (aPaintRegion.IsComplex()) {
783       printf_stderr("Complex region\n");
784       for (auto iter = aPaintRegion.RectIter(); !iter.Done(); iter.Next()) {
785         const IntRect& rect = iter.Get();
786         printf_stderr(" rect %i, %i, %i, %i\n", rect.x, rect.y, rect.width,
787                       rect.height);
788       }
789     }
790   }
791   start = PR_IntervalNow();
792 #endif
793 
794   AUTO_PROFILER_LABEL("ClientMultiTiledLayerBuffer::PaintThebes", GRAPHICS);
795 
796   mNewValidRegion = aNewValidRegion;
797   Update(aNewValidRegion, aPaintRegion, aDirtyRegion, aFlags);
798 
799 #ifdef GFX_TILEDLAYER_PREF_WARNINGS
800   if (PR_IntervalNow() - start > 10) {
801     const IntRect bounds = aPaintRegion.GetBounds();
802     printf_stderr("Time to tile %i: %i, %i, %i, %i\n", PR_IntervalNow() - start,
803                   bounds.x, bounds.y, bounds.width, bounds.height);
804   }
805 #endif
806 
807   mLastPaintContentType = GetContentType(&mLastPaintSurfaceMode);
808   mCallback = nullptr;
809   mCallbackData = nullptr;
810 }
811 
PadDrawTargetOutFromRegion(RefPtr<DrawTarget> drawTarget,nsIntRegion & region)812 void PadDrawTargetOutFromRegion(RefPtr<DrawTarget> drawTarget,
813                                 nsIntRegion& region) {
814   struct LockedBits {
815     uint8_t* data;
816     IntSize size;
817     int32_t stride;
818     SurfaceFormat format;
819     static int clamp(int x, int min, int max) {
820       if (x < min) x = min;
821       if (x > max) x = max;
822       return x;
823     }
824 
825     static void ensure_memcpy(uint8_t* dst, uint8_t* src, size_t n,
826                               uint8_t* bitmap, int stride, int height) {
827       if (src + n > bitmap + stride * height) {
828         MOZ_CRASH("GFX: long src memcpy");
829       }
830       if (src < bitmap) {
831         MOZ_CRASH("GFX: short src memcpy");
832       }
833       if (dst + n > bitmap + stride * height) {
834         MOZ_CRASH("GFX: long dst mempcy");
835       }
836       if (dst < bitmap) {
837         MOZ_CRASH("GFX: short dst mempcy");
838       }
839     }
840 
841     static void visitor(void* closure, VisitSide side, int x1, int y1, int x2,
842                         int y2) {
843       LockedBits* lb = static_cast<LockedBits*>(closure);
844       uint8_t* bitmap = lb->data;
845       const int bpp = gfx::BytesPerPixel(lb->format);
846       const int stride = lb->stride;
847       const int width = lb->size.width;
848       const int height = lb->size.height;
849 
850       if (side == VisitSide::TOP) {
851         if (y1 > 0) {
852           x1 = clamp(x1, 0, width - 1);
853           x2 = clamp(x2, 0, width - 1);
854           ensure_memcpy(&bitmap[x1 * bpp + (y1 - 1) * stride],
855                         &bitmap[x1 * bpp + y1 * stride], (x2 - x1) * bpp,
856                         bitmap, stride, height);
857           memcpy(&bitmap[x1 * bpp + (y1 - 1) * stride],
858                  &bitmap[x1 * bpp + y1 * stride], (x2 - x1) * bpp);
859         }
860       } else if (side == VisitSide::BOTTOM) {
861         if (y1 < height) {
862           x1 = clamp(x1, 0, width - 1);
863           x2 = clamp(x2, 0, width - 1);
864           ensure_memcpy(&bitmap[x1 * bpp + y1 * stride],
865                         &bitmap[x1 * bpp + (y1 - 1) * stride], (x2 - x1) * bpp,
866                         bitmap, stride, height);
867           memcpy(&bitmap[x1 * bpp + y1 * stride],
868                  &bitmap[x1 * bpp + (y1 - 1) * stride], (x2 - x1) * bpp);
869         }
870       } else if (side == VisitSide::LEFT) {
871         if (x1 > 0) {
872           while (y1 != y2) {
873             memcpy(&bitmap[(x1 - 1) * bpp + y1 * stride],
874                    &bitmap[x1 * bpp + y1 * stride], bpp);
875             y1++;
876           }
877         }
878       } else if (side == VisitSide::RIGHT) {
879         if (x1 < width) {
880           while (y1 != y2) {
881             memcpy(&bitmap[x1 * bpp + y1 * stride],
882                    &bitmap[(x1 - 1) * bpp + y1 * stride], bpp);
883             y1++;
884           }
885         }
886       }
887     }
888   } lb;
889 
890   if (drawTarget->LockBits(&lb.data, &lb.size, &lb.stride, &lb.format)) {
891     // we can only pad software targets so if we can't lock the bits don't pad
892     region.VisitEdges(lb.visitor, &lb);
893     drawTarget->ReleaseBits(lb.data);
894   }
895 }
896 
UnlockTile(TileClient & aTile)897 void ClientTiledLayerBuffer::UnlockTile(TileClient& aTile) {
898   // We locked the back buffer, and flipped so we now need to unlock the front
899   if (aTile.mFrontBuffer && aTile.mFrontBuffer->IsLocked()) {
900     aTile.mFrontBuffer->Unlock();
901     aTile.mFrontBuffer->SyncWithObject(
902         mCompositableClient.GetForwarder()->GetSyncObject());
903   }
904   if (aTile.mFrontBufferOnWhite && aTile.mFrontBufferOnWhite->IsLocked()) {
905     aTile.mFrontBufferOnWhite->Unlock();
906     aTile.mFrontBufferOnWhite->SyncWithObject(
907         mCompositableClient.GetForwarder()->GetSyncObject());
908   }
909   if (aTile.mBackBuffer && aTile.mBackBuffer->IsLocked()) {
910     aTile.mBackBuffer->Unlock();
911   }
912   if (aTile.mBackBufferOnWhite && aTile.mBackBufferOnWhite->IsLocked()) {
913     aTile.mBackBufferOnWhite->Unlock();
914   }
915 }
916 
Update(const nsIntRegion & newValidRegion,const nsIntRegion & aPaintRegion,const nsIntRegion & aDirtyRegion,TilePaintFlags aFlags)917 void ClientMultiTiledLayerBuffer::Update(const nsIntRegion& newValidRegion,
918                                          const nsIntRegion& aPaintRegion,
919                                          const nsIntRegion& aDirtyRegion,
920                                          TilePaintFlags aFlags) {
921   const IntSize scaledTileSize = GetScaledTileSize();
922   const gfx::IntRect newBounds = newValidRegion.GetBounds();
923 
924   const TilesPlacement oldTiles = mTiles;
925   const TilesPlacement newTiles(
926       floor_div(newBounds.X(), scaledTileSize.width),
927       floor_div(newBounds.Y(), scaledTileSize.height),
928       floor_div(
929           GetTileStart(newBounds.X(), scaledTileSize.width) + newBounds.Width(),
930           scaledTileSize.width) +
931           1,
932       floor_div(GetTileStart(newBounds.Y(), scaledTileSize.height) +
933                     newBounds.Height(),
934                 scaledTileSize.height) +
935           1);
936 
937   const size_t oldTileCount = mRetainedTiles.Length();
938   const size_t newTileCount = newTiles.mSize.width * newTiles.mSize.height;
939 
940   nsTArray<TileClient> oldRetainedTiles;
941   mRetainedTiles.SwapElements(oldRetainedTiles);
942   mRetainedTiles.SetLength(newTileCount);
943 
944   for (size_t oldIndex = 0; oldIndex < oldTileCount; oldIndex++) {
945     const TileIntPoint tilePosition = oldTiles.TilePosition(oldIndex);
946     const size_t newIndex = newTiles.TileIndex(tilePosition);
947     // First, get the already existing tiles to the right place in the new
948     // array. Leave placeholders (default constructor) where there was no tile.
949     if (newTiles.HasTile(tilePosition)) {
950       mRetainedTiles[newIndex] = oldRetainedTiles[oldIndex];
951     } else {
952       // release tiles that we are not going to reuse before allocating new ones
953       // to avoid allocating unnecessarily.
954       oldRetainedTiles[oldIndex].DiscardBuffers();
955     }
956   }
957 
958   oldRetainedTiles.Clear();
959 
960   nsIntRegion paintRegion = aPaintRegion;
961   nsIntRegion dirtyRegion = aDirtyRegion;
962   if (!paintRegion.IsEmpty()) {
963     MOZ_ASSERT(mPaintStates.size() == 0);
964     for (size_t i = 0; i < newTileCount; ++i) {
965       const TileIntPoint tilePosition = newTiles.TilePosition(i);
966 
967       IntPoint tileOffset = GetTileOffset(tilePosition);
968       nsIntRegion tileDrawRegion = IntRect(tileOffset, scaledTileSize);
969       tileDrawRegion.AndWith(paintRegion);
970 
971       if (tileDrawRegion.IsEmpty()) {
972         continue;
973       }
974 
975       TileClient& tile = mRetainedTiles[i];
976       if (!ValidateTile(tile, GetTileOffset(tilePosition), tileDrawRegion,
977                         aFlags)) {
978         gfxCriticalError() << "ValidateTile failed";
979       }
980 
981       // Validating the tile may have required more to be painted.
982       paintRegion.OrWith(tileDrawRegion);
983       dirtyRegion.OrWith(tileDrawRegion);
984     }
985 
986     if (!mPaintTiles.empty()) {
987       // Create a tiled draw target
988       gfx::TileSet tileset;
989       for (size_t i = 0; i < mPaintTiles.size(); ++i) {
990         mPaintTiles[i].mTileOrigin -= mTilingOrigin;
991       }
992       tileset.mTiles = &mPaintTiles[0];
993       tileset.mTileCount = mPaintTiles.size();
994       RefPtr<DrawTarget> drawTarget =
995           gfx::Factory::CreateTiledDrawTarget(tileset);
996       if (!drawTarget || !drawTarget->IsValid()) {
997         gfxDevCrash(LogReason::InvalidContext) << "Invalid tiled draw target";
998         return;
999       }
1000       drawTarget->SetTransform(Matrix());
1001 
1002       // Draw into the tiled draw target
1003       RefPtr<gfxContext> ctx = gfxContext::CreateOrNull(drawTarget);
1004       MOZ_ASSERT(ctx);  // already checked the draw target above
1005       ctx->SetMatrix(ctx->CurrentMatrix()
1006                          .PreScale(mResolution, mResolution)
1007                          .PreTranslate(-mTilingOrigin));
1008 
1009       mCallback(&mPaintedLayer, ctx, paintRegion, dirtyRegion,
1010                 DrawRegionClip::DRAW, nsIntRegion(), mCallbackData);
1011       ctx = nullptr;
1012 
1013       if (aFlags & TilePaintFlags::Async) {
1014         for (const auto& state : mPaintStates) {
1015           PaintThread::Get()->PaintTiledContents(state);
1016         }
1017         mManager->SetQueuedAsyncPaints();
1018         MOZ_ASSERT(mPaintStates.size() > 0);
1019         mPaintStates.clear();
1020       } else {
1021         MOZ_ASSERT(mPaintStates.size() == 0);
1022       }
1023 
1024       // Reset
1025       mPaintTiles.clear();
1026       mTilingOrigin = IntPoint(std::numeric_limits<int32_t>::max(),
1027                                std::numeric_limits<int32_t>::max());
1028     }
1029 
1030     bool edgePaddingEnabled = gfxPrefs::TileEdgePaddingEnabled();
1031 
1032     for (uint32_t i = 0; i < mRetainedTiles.Length(); ++i) {
1033       TileClient& tile = mRetainedTiles[i];
1034 
1035       // Only worry about padding when not doing low-res because it simplifies
1036       // the math and the artifacts won't be noticable
1037       // Edge padding prevents sampling artifacts when compositing.
1038       if (edgePaddingEnabled && mResolution == 1 && tile.mFrontBuffer &&
1039           tile.mFrontBuffer->IsLocked()) {
1040         const TileIntPoint tilePosition = newTiles.TilePosition(i);
1041         IntPoint tileOffset = GetTileOffset(tilePosition);
1042         // Strictly speakig we want the unscaled rect here, but it doesn't
1043         // matter because we only run this code when the resolution is equal
1044         // to 1.
1045         IntRect tileRect = IntRect(tileOffset.x, tileOffset.y,
1046                                    GetTileSize().width, GetTileSize().height);
1047 
1048         nsIntRegion tileDrawRegion = IntRect(tileOffset, scaledTileSize);
1049         tileDrawRegion.AndWith(paintRegion);
1050 
1051         nsIntRegion tileValidRegion = mValidRegion;
1052         tileValidRegion.OrWith(tileDrawRegion);
1053 
1054         // We only need to pad out if the tile has area that's not valid
1055         if (!tileValidRegion.Contains(tileRect)) {
1056           tileValidRegion = tileValidRegion.Intersect(tileRect);
1057           // translate the region into tile space and pad
1058           tileValidRegion.MoveBy(-IntPoint(tileOffset.x, tileOffset.y));
1059           RefPtr<DrawTarget> drawTarget = tile.mFrontBuffer->BorrowDrawTarget();
1060           PadDrawTargetOutFromRegion(drawTarget, tileValidRegion);
1061         }
1062       }
1063       UnlockTile(tile);
1064     }
1065   }
1066 
1067   mTiles = newTiles;
1068   mValidRegion = newValidRegion;
1069   mPaintedRegion.OrWith(paintRegion);
1070 }
1071 
ValidateTile(TileClient & aTile,const nsIntPoint & aTileOrigin,nsIntRegion & aDirtyRegion,TilePaintFlags aFlags)1072 bool ClientMultiTiledLayerBuffer::ValidateTile(TileClient& aTile,
1073                                                const nsIntPoint& aTileOrigin,
1074                                                nsIntRegion& aDirtyRegion,
1075                                                TilePaintFlags aFlags) {
1076   AUTO_PROFILER_LABEL("ClientMultiTiledLayerBuffer::ValidateTile", GRAPHICS);
1077 
1078 #ifdef GFX_TILEDLAYER_PREF_WARNINGS
1079   if (aDirtyRegion.IsComplex()) {
1080     printf_stderr("Complex region\n");
1081   }
1082 #endif
1083 
1084   SurfaceMode mode;
1085   gfxContentType content = GetContentType(&mode);
1086 
1087   if (!aTile.mAllocator) {
1088     aTile.SetTextureAllocator(
1089         mManager->GetCompositorBridgeChild()->GetTexturePool(
1090             mManager->AsShadowForwarder(),
1091             gfxPlatform::GetPlatform()->Optimal2DFormatForContent(content),
1092             TextureFlags::DISALLOW_BIGIMAGE | TextureFlags::IMMEDIATE_UPLOAD |
1093                 TextureFlags::NON_BLOCKING_READ_LOCK));
1094     MOZ_ASSERT(aTile.mAllocator);
1095   }
1096 
1097   nsIntRegion offsetScaledDirtyRegion = aDirtyRegion.MovedBy(-aTileOrigin);
1098   offsetScaledDirtyRegion.ScaleRoundOut(mResolution, mResolution);
1099 
1100   std::vector<CapturedTiledPaintState::Copy> asyncPaintCopies;
1101   std::vector<RefPtr<TextureClient>> asyncPaintClients;
1102 
1103   nsIntRegion extraPainted;
1104   RefPtr<TextureClient> backBufferOnWhite;
1105   RefPtr<TextureClient> backBuffer = aTile.GetBackBuffer(
1106       mCompositableClient, offsetScaledDirtyRegion, content, mode, extraPainted,
1107       aFlags, &backBufferOnWhite, &asyncPaintCopies, &asyncPaintClients);
1108 
1109   // Mark the area we need to paint in the back buffer as invalid in the
1110   // front buffer as they will become out of sync.
1111   aTile.mInvalidFront.OrWith(offsetScaledDirtyRegion);
1112 
1113   // Add backbuffer's invalid region to the dirty region to be painted.
1114   // This will be empty if we were able to copy from the front in to the back.
1115   nsIntRegion invalidBack = aTile.mInvalidBack;
1116   invalidBack.MoveBy(aTileOrigin);
1117   invalidBack.ScaleInverseRoundOut(mResolution, mResolution);
1118   invalidBack.AndWith(mNewValidRegion);
1119   aDirtyRegion.OrWith(invalidBack);
1120   offsetScaledDirtyRegion.OrWith(aTile.mInvalidBack);
1121 
1122   aTile.mUpdateRect =
1123       offsetScaledDirtyRegion.GetBounds().Union(extraPainted.GetBounds());
1124 
1125   extraPainted.MoveBy(aTileOrigin);
1126   extraPainted.And(extraPainted, mNewValidRegion);
1127   mPaintedRegion.Or(mPaintedRegion, extraPainted);
1128 
1129   if (!backBuffer) {
1130     return false;
1131   }
1132 
1133   RefPtr<DrawTarget> dt = backBuffer->BorrowDrawTarget();
1134   RefPtr<DrawTarget> dtOnWhite;
1135   if (backBufferOnWhite) {
1136     dtOnWhite = backBufferOnWhite->BorrowDrawTarget();
1137   }
1138 
1139   if (!dt || (backBufferOnWhite && !dtOnWhite)) {
1140     aTile.DiscardBuffers();
1141     return false;
1142   }
1143 
1144   RefPtr<DrawTarget> drawTarget;
1145   if (dtOnWhite) {
1146     drawTarget = Factory::CreateDualDrawTarget(dt, dtOnWhite);
1147   } else {
1148     drawTarget = dt;
1149   }
1150 
1151   auto clear =
1152       CapturedTiledPaintState::Clear{dt, dtOnWhite, offsetScaledDirtyRegion};
1153 
1154   gfx::Tile paintTile;
1155   paintTile.mTileOrigin = gfx::IntPoint(aTileOrigin.x, aTileOrigin.y);
1156 
1157   if (aFlags & TilePaintFlags::Async) {
1158     RefPtr<CapturedTiledPaintState> asyncPaint = new CapturedTiledPaintState();
1159 
1160     RefPtr<DrawTargetCapture> captureDT = Factory::CreateCaptureDrawTarget(
1161         drawTarget->GetBackendType(), drawTarget->GetSize(),
1162         drawTarget->GetFormat());
1163     paintTile.mDrawTarget = captureDT;
1164     asyncPaint->mTarget = drawTarget;
1165     asyncPaint->mCapture = captureDT;
1166 
1167     asyncPaint->mCopies = std::move(asyncPaintCopies);
1168     asyncPaint->mClears.push_back(clear);
1169 
1170     asyncPaint->mClients = std::move(asyncPaintClients);
1171     asyncPaint->mClients.push_back(backBuffer);
1172     if (backBufferOnWhite) {
1173       asyncPaint->mClients.push_back(backBufferOnWhite);
1174     }
1175 
1176     mPaintStates.push_back(asyncPaint);
1177   } else {
1178     paintTile.mDrawTarget = drawTarget;
1179     clear.ClearBuffer();
1180   }
1181 
1182   mPaintTiles.push_back(paintTile);
1183 
1184   mTilingOrigin.x = std::min(mTilingOrigin.x, paintTile.mTileOrigin.x);
1185   mTilingOrigin.y = std::min(mTilingOrigin.y, paintTile.mTileOrigin.y);
1186 
1187   // The new buffer is now validated, remove the dirty region from it.
1188   aTile.mInvalidBack.SubOut(offsetScaledDirtyRegion);
1189 
1190   aTile.Flip();
1191 
1192   return true;
1193 }
1194 
1195 /**
1196  * This function takes the transform stored in aTransformToCompBounds
1197  * (which was generated in GetTransformToAncestorsParentLayer), and
1198  * modifies it with the ViewTransform from the compositor side so that
1199  * it reflects what the compositor is actually rendering. This operation
1200  * basically adds in the layer's async transform.
1201  * This function then returns the scroll ancestor's composition bounds,
1202  * transformed into the painted layer's LayerPixel coordinates, accounting
1203  * for the compositor state.
1204  */
GetCompositorSideCompositionBounds(const LayerMetricsWrapper & aScrollAncestor,const LayerToParentLayerMatrix4x4 & aTransformToCompBounds,const AsyncTransform & aAPZTransform,const LayerRect & aClip)1205 static Maybe<LayerRect> GetCompositorSideCompositionBounds(
1206     const LayerMetricsWrapper& aScrollAncestor,
1207     const LayerToParentLayerMatrix4x4& aTransformToCompBounds,
1208     const AsyncTransform& aAPZTransform, const LayerRect& aClip) {
1209   LayerToParentLayerMatrix4x4 transform =
1210       aTransformToCompBounds * AsyncTransformComponentMatrix(aAPZTransform);
1211 
1212   return UntransformBy(transform.Inverse(),
1213                        aScrollAncestor.Metrics().GetCompositionBounds(), aClip);
1214 }
1215 
ComputeProgressiveUpdateRegion(const nsIntRegion & aInvalidRegion,const nsIntRegion & aOldValidRegion,nsIntRegion & aRegionToPaint,BasicTiledLayerPaintData * aPaintData,bool aIsRepeated)1216 bool ClientMultiTiledLayerBuffer::ComputeProgressiveUpdateRegion(
1217     const nsIntRegion& aInvalidRegion, const nsIntRegion& aOldValidRegion,
1218     nsIntRegion& aRegionToPaint, BasicTiledLayerPaintData* aPaintData,
1219     bool aIsRepeated) {
1220   aRegionToPaint = aInvalidRegion;
1221 
1222   // If the composition bounds rect is empty, we can't make any sensible
1223   // decision about how to update coherently. In this case, just update
1224   // everything in one transaction.
1225   if (aPaintData->mCompositionBounds.IsEmpty()) {
1226     aPaintData->mPaintFinished = true;
1227     return false;
1228   }
1229 
1230   // If this is a low precision buffer, we force progressive updates. The
1231   // assumption is that the contents is less important, so visual coherency
1232   // is lower priority than speed.
1233   bool drawingLowPrecision = IsLowPrecision();
1234 
1235   // Find out if we have any non-stale content to update.
1236   nsIntRegion staleRegion;
1237   staleRegion.And(aInvalidRegion, aOldValidRegion);
1238 
1239   TILING_LOG("TILING %p: Progressive update stale region %s\n", &mPaintedLayer,
1240              Stringify(staleRegion).c_str());
1241 
1242   LayerMetricsWrapper scrollAncestor;
1243   mPaintedLayer.GetAncestorLayers(&scrollAncestor, nullptr, nullptr);
1244 
1245   // Find out the current view transform to determine which tiles to draw
1246   // first, and see if we should just abort this paint. Aborting is usually
1247   // caused by there being an incoming, more relevant paint.
1248   AsyncTransform viewTransform;
1249   MOZ_ASSERT(mSharedFrameMetricsHelper);
1250 
1251   bool abortPaint = mSharedFrameMetricsHelper->UpdateFromCompositorFrameMetrics(
1252       scrollAncestor, !staleRegion.Contains(aInvalidRegion),
1253       drawingLowPrecision, viewTransform);
1254 
1255   TILING_LOG(
1256       "TILING %p: Progressive update view transform %s zoom %f abort %d\n",
1257       &mPaintedLayer, ToString(viewTransform.mTranslation).c_str(),
1258       viewTransform.mScale.scale, abortPaint);
1259 
1260   if (abortPaint) {
1261     // We ignore if front-end wants to abort if this is the first,
1262     // non-low-precision paint, as in that situation, we're about to override
1263     // front-end's page/viewport metrics.
1264     if (!aPaintData->mFirstPaint || drawingLowPrecision) {
1265       AUTO_PROFILER_LABEL(
1266           "ClientMultiTiledLayerBuffer::ComputeProgressiveUpdateRegion",
1267           GRAPHICS);
1268 
1269       aRegionToPaint.SetEmpty();
1270       return aIsRepeated;
1271     }
1272   }
1273 
1274   Maybe<LayerRect> transformedCompositionBounds =
1275       GetCompositorSideCompositionBounds(
1276           scrollAncestor, aPaintData->mTransformToCompBounds, viewTransform,
1277           LayerRect(mPaintedLayer.GetVisibleRegion().GetBounds()));
1278 
1279   if (!transformedCompositionBounds) {
1280     aPaintData->mPaintFinished = true;
1281     return false;
1282   }
1283 
1284   TILING_LOG("TILING %p: Progressive update transformed compositor bounds %s\n",
1285              &mPaintedLayer, Stringify(*transformedCompositionBounds).c_str());
1286 
1287   // Compute a "coherent update rect" that we should paint all at once in a
1288   // single transaction. This is to avoid rendering glitches on animated
1289   // page content, and when layers change size/shape.
1290   // On Fennec uploads are more expensive because we're not using gralloc, so
1291   // we use a coherent update rect that is intersected with the screen at the
1292   // time of issuing the draw command. This will paint faster but also
1293   // potentially make the progressive paint more visible to the user while
1294   // scrolling.
1295   IntRect coherentUpdateRect(RoundedOut(
1296 #ifdef MOZ_WIDGET_ANDROID
1297                                  transformedCompositionBounds->Intersect(
1298                                      aPaintData->mCompositionBounds)
1299 #else
1300                                  *transformedCompositionBounds
1301 #endif
1302                                      )
1303                                  .ToUnknownRect());
1304 
1305   TILING_LOG("TILING %p: Progressive update final coherency rect %s\n",
1306              &mPaintedLayer, Stringify(coherentUpdateRect).c_str());
1307 
1308   aRegionToPaint.And(aInvalidRegion, coherentUpdateRect);
1309   aRegionToPaint.Or(aRegionToPaint, staleRegion);
1310   bool drawingStale = !aRegionToPaint.IsEmpty();
1311   if (!drawingStale) {
1312     aRegionToPaint = aInvalidRegion;
1313   }
1314 
1315   // Prioritise tiles that are currently visible on the screen.
1316   bool paintingVisible = false;
1317   if (aRegionToPaint.Intersects(coherentUpdateRect)) {
1318     aRegionToPaint.And(aRegionToPaint, coherentUpdateRect);
1319     paintingVisible = true;
1320   }
1321 
1322   TILING_LOG("TILING %p: Progressive update final paint region %s\n",
1323              &mPaintedLayer, Stringify(aRegionToPaint).c_str());
1324 
1325   // Paint area that's visible and overlaps previously valid content to avoid
1326   // visible glitches in animated elements, such as gifs.
1327   bool paintInSingleTransaction =
1328       paintingVisible && (drawingStale || aPaintData->mFirstPaint);
1329 
1330   TILING_LOG(
1331       "TILING %p: paintingVisible %d drawingStale %d firstPaint %d "
1332       "singleTransaction %d\n",
1333       &mPaintedLayer, paintingVisible, drawingStale, aPaintData->mFirstPaint,
1334       paintInSingleTransaction);
1335 
1336   // The following code decides what order to draw tiles in, based on the
1337   // current scroll direction of the primary scrollable layer.
1338   NS_ASSERTION(!aRegionToPaint.IsEmpty(), "Unexpectedly empty paint region!");
1339   IntRect paintBounds = aRegionToPaint.GetBounds();
1340 
1341   int startX, incX, startY, incY;
1342   gfx::IntSize scaledTileSize = GetScaledTileSize();
1343   if (aPaintData->mScrollOffset.x >= aPaintData->mLastScrollOffset.x) {
1344     startX = RoundDownToTileEdge(paintBounds.X(), scaledTileSize.width);
1345     incX = scaledTileSize.width;
1346   } else {
1347     startX = RoundDownToTileEdge(paintBounds.XMost() - 1, scaledTileSize.width);
1348     incX = -scaledTileSize.width;
1349   }
1350 
1351   if (aPaintData->mScrollOffset.y >= aPaintData->mLastScrollOffset.y) {
1352     startY = RoundDownToTileEdge(paintBounds.Y(), scaledTileSize.height);
1353     incY = scaledTileSize.height;
1354   } else {
1355     startY =
1356         RoundDownToTileEdge(paintBounds.YMost() - 1, scaledTileSize.height);
1357     incY = -scaledTileSize.height;
1358   }
1359 
1360   // Find a tile to draw.
1361   IntRect tileBounds(startX, startY, scaledTileSize.width,
1362                      scaledTileSize.height);
1363   int32_t scrollDiffX =
1364       aPaintData->mScrollOffset.x - aPaintData->mLastScrollOffset.x;
1365   int32_t scrollDiffY =
1366       aPaintData->mScrollOffset.y - aPaintData->mLastScrollOffset.y;
1367   // This loop will always terminate, as there is at least one tile area
1368   // along the first/last row/column intersecting with regionToPaint, or its
1369   // bounds would have been smaller.
1370   while (true) {
1371     aRegionToPaint.And(aInvalidRegion, tileBounds);
1372     if (!aRegionToPaint.IsEmpty()) {
1373       if (mResolution != 1) {
1374         // Paint the entire tile for low-res. This is aimed to fixing low-res
1375         // resampling and to avoid doing costly region accurate painting for a
1376         // small area.
1377         aRegionToPaint = tileBounds;
1378       }
1379       break;
1380     }
1381     if (Abs(scrollDiffY) >= Abs(scrollDiffX)) {
1382       tileBounds.MoveByX(incX);
1383     } else {
1384       tileBounds.MoveByY(incY);
1385     }
1386   }
1387 
1388   if (!aRegionToPaint.Contains(aInvalidRegion)) {
1389     // The region needed to paint is larger then our progressive chunk size
1390     // therefore update what we want to paint and ask for a new paint
1391     // transaction.
1392 
1393     // If we need to draw more than one tile to maintain coherency, make
1394     // sure it happens in the same transaction by requesting this work be
1395     // repeated immediately.
1396     // If this is unnecessary, the remaining work will be done tile-by-tile in
1397     // subsequent transactions. The caller code is responsible for scheduling
1398     // the subsequent transactions as long as we don't set the mPaintFinished
1399     // flag to true.
1400     return (!drawingLowPrecision && paintInSingleTransaction);
1401   }
1402 
1403   // We're not repeating painting and we've not requested a repeat transaction,
1404   // so the paint is finished. If there's still a separate low precision
1405   // paint to do, it will get marked as unfinished later.
1406   aPaintData->mPaintFinished = true;
1407   return false;
1408 }
1409 
ProgressiveUpdate(const nsIntRegion & aValidRegion,const nsIntRegion & aInvalidRegion,const nsIntRegion & aOldValidRegion,nsIntRegion & aOutDrawnRegion,BasicTiledLayerPaintData * aPaintData,LayerManager::DrawPaintedLayerCallback aCallback,void * aCallbackData)1410 bool ClientMultiTiledLayerBuffer::ProgressiveUpdate(
1411     const nsIntRegion& aValidRegion, const nsIntRegion& aInvalidRegion,
1412     const nsIntRegion& aOldValidRegion, nsIntRegion& aOutDrawnRegion,
1413     BasicTiledLayerPaintData* aPaintData,
1414     LayerManager::DrawPaintedLayerCallback aCallback, void* aCallbackData) {
1415   TILING_LOG("TILING %p: Progressive update valid region %s\n", &mPaintedLayer,
1416              Stringify(aValidRegion).c_str());
1417   TILING_LOG("TILING %p: Progressive update invalid region %s\n",
1418              &mPaintedLayer, Stringify(aInvalidRegion).c_str());
1419   TILING_LOG("TILING %p: Progressive update old valid region %s\n",
1420              &mPaintedLayer, Stringify(aOldValidRegion).c_str());
1421 
1422   bool repeat = false;
1423   bool isBufferChanged = false;
1424   nsIntRegion remainingInvalidRegion = aInvalidRegion;
1425   nsIntRegion updatedValidRegion = aValidRegion;
1426   do {
1427     // Compute the region that should be updated. Repeat as many times as
1428     // is required.
1429     nsIntRegion regionToPaint;
1430     repeat =
1431         ComputeProgressiveUpdateRegion(remainingInvalidRegion, aOldValidRegion,
1432                                        regionToPaint, aPaintData, repeat);
1433 
1434     TILING_LOG(
1435         "TILING %p: Progressive update computed paint region %s repeat %d\n",
1436         &mPaintedLayer, Stringify(regionToPaint).c_str(), repeat);
1437 
1438     // There's no further work to be done.
1439     if (regionToPaint.IsEmpty()) {
1440       break;
1441     }
1442 
1443     isBufferChanged = true;
1444 
1445     // Keep track of what we're about to refresh.
1446     aOutDrawnRegion.OrWith(regionToPaint);
1447     updatedValidRegion.OrWith(regionToPaint);
1448 
1449     // aValidRegion may have been altered by InvalidateRegion, but we still
1450     // want to display stale content until it gets progressively updated.
1451     // Create a region that includes stale content.
1452     nsIntRegion validOrStale;
1453     validOrStale.Or(updatedValidRegion, aOldValidRegion);
1454 
1455     // Paint the computed region and subtract it from the invalid region.
1456     PaintThebes(validOrStale, regionToPaint, remainingInvalidRegion, aCallback,
1457                 aCallbackData, TilePaintFlags::Progressive);
1458     remainingInvalidRegion.SubOut(regionToPaint);
1459   } while (repeat);
1460 
1461   TILING_LOG(
1462       "TILING %p: Progressive update final valid region %s buffer changed %d\n",
1463       &mPaintedLayer, Stringify(updatedValidRegion).c_str(), isBufferChanged);
1464   TILING_LOG("TILING %p: Progressive update final invalid region %s\n",
1465              &mPaintedLayer, Stringify(remainingInvalidRegion).c_str());
1466 
1467   // Return false if nothing has been drawn, or give what has been drawn
1468   // to the shadow layer to upload.
1469   return isBufferChanged;
1470 }
1471 
PrintInfo(std::stringstream & aStream,const char * aPrefix)1472 void TiledContentClient::PrintInfo(std::stringstream& aStream,
1473                                    const char* aPrefix) {
1474   aStream << aPrefix;
1475   aStream << nsPrintfCString("%sTiledContentClient (0x%p)", mName, this).get();
1476 }
1477 
Dump(std::stringstream & aStream,const char * aPrefix,bool aDumpHtml,TextureDumpMode aCompress)1478 void TiledContentClient::Dump(std::stringstream& aStream, const char* aPrefix,
1479                               bool aDumpHtml, TextureDumpMode aCompress) {
1480   GetTiledBuffer()->Dump(aStream, aPrefix, aDumpHtml, aCompress);
1481 }
1482 
ResetPaintData()1483 void BasicTiledLayerPaintData::ResetPaintData() {
1484   mLowPrecisionPaintCount = 0;
1485   mPaintFinished = false;
1486   mHasTransformAnimation = false;
1487   mCompositionBounds.SetEmpty();
1488   mCriticalDisplayPort = Nothing();
1489 }
1490 
1491 }  // namespace layers
1492 }  // namespace mozilla
1493