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