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