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/. */
7 #include "mozilla/layers/TiledContentClient.h"
8 #include <math.h> // for ceil, ceilf, floor
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 "gfxRect.h" // for gfxRect
16 #include "mozilla/MathAlgorithms.h" // for Abs
17 #include "mozilla/StaticPrefs_apz.h"
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 "nsISupportsImpl.h" // for gfxContext::AddRef, etc
28 #include "nsExpirationTracker.h" // for nsExpirationTracker
29 #include "nsMathUtils.h" // for NS_lroundf
30 #include "LayersLogging.h"
31 #include "UnitTransforms.h" // for TransformTo
32 #include "mozilla/StaticPrefs_apz.h"
33 #include "mozilla/StaticPrefs_layers.h"
34 #include "mozilla/UniquePtr.h"
36 #ifdef GFX_TILEDLAYER_DEBUG_OVERLAY
39 using mozilla::layers::Layer
;
40 static void DrawDebugOverlay(mozilla::gfx::DrawTarget
* dt
, int x
, int y
,
41 int width
, int height
) {
46 c
.SetDeviceColor(Color(0.f
, 0.f
, 0.f
));
47 c
.Rectangle(gfxRect(0, 0, width
, height
));
50 // Build tile description
54 // Draw text using cairo toy text API
55 // XXX: this drawing will silently fail if |dt| doesn't have a Cairo backend
56 cairo_t
* cr
= gfxFont::RefCairo(dt
);
57 cairo_set_font_size(cr
, 25);
58 cairo_text_extents_t extents
;
59 cairo_text_extents(cr
, ss
.str().c_str(), &extents
);
61 int textWidth
= extents
.width
+ 6;
64 c
.SetDeviceColor(Color(0.f
, 0.f
, 0.f
));
65 c
.Rectangle(gfxRect(gfxPoint(2, 2), gfxSize(textWidth
, 30)));
69 c
.SetDeviceColor(Color(1.0, 0.0, 0.0));
70 c
.Rectangle(gfxRect(gfxPoint(2, 2), gfxSize(textWidth
, 30)));
74 cairo_move_to(cr
, 4, 28);
75 cairo_show_text(cr
, ss
.str().c_str());
86 SharedFrameMetricsHelper::SharedFrameMetricsHelper()
87 : mLastProgressiveUpdateWasLowPrecision(false),
88 mProgressiveUpdateWasInDanger(false) {
89 MOZ_COUNT_CTOR(SharedFrameMetricsHelper
);
92 SharedFrameMetricsHelper::~SharedFrameMetricsHelper() {
93 MOZ_COUNT_DTOR(SharedFrameMetricsHelper
);
96 static inline bool FuzzyEquals(float a
, float b
) {
97 return (fabsf(a
- b
) < 1e-6);
100 static AsyncTransform
ComputeViewTransform(
101 const FrameMetrics
& aContentMetrics
,
102 const FrameMetrics
& aCompositorMetrics
) {
103 // This is basically the same code as
104 // AsyncPanZoomController::GetCurrentAsyncTransform but with aContentMetrics
105 // used in place of mLastContentPaintMetrics, because they should be
106 // equivalent, modulo race conditions while transactions are inflight.
108 ParentLayerPoint translation
= (aCompositorMetrics
.GetScrollOffset() -
109 aContentMetrics
.GetScrollOffset()) *
110 aCompositorMetrics
.GetZoom();
111 return AsyncTransform(aCompositorMetrics
.GetAsyncZoom(), -translation
);
114 bool SharedFrameMetricsHelper::UpdateFromCompositorFrameMetrics(
115 const LayerMetricsWrapper
& aLayer
, bool aHasPendingNewThebesContent
,
116 bool aLowPrecision
, AsyncTransform
& aViewTransform
) {
119 CompositorBridgeChild
* compositor
= nullptr;
120 if (aLayer
.Manager() && aLayer
.Manager()->AsClientLayerManager()) {
122 aLayer
.Manager()->AsClientLayerManager()->GetCompositorBridgeChild();
129 const FrameMetrics
& contentMetrics
= aLayer
.Metrics();
130 FrameMetrics compositorMetrics
;
132 if (!compositor
->LookupCompositorFrameMetrics(contentMetrics
.GetScrollId(),
133 compositorMetrics
)) {
137 aViewTransform
= ComputeViewTransform(contentMetrics
, compositorMetrics
);
139 // Reset the checkerboard risk flag when switching to low precision
141 if (aLowPrecision
&& !mLastProgressiveUpdateWasLowPrecision
) {
142 // Skip low precision rendering until we're at risk of checkerboarding.
143 if (!mProgressiveUpdateWasInDanger
) {
145 "TILING: Aborting low-precision rendering because not at risk of "
146 "checkerboarding\n");
149 mProgressiveUpdateWasInDanger
= false;
151 mLastProgressiveUpdateWasLowPrecision
= aLowPrecision
;
153 // Always abort updates if the resolution has changed. There's no use
154 // in drawing at the incorrect resolution.
155 if (!FuzzyEquals(compositorMetrics
.GetZoom().xScale
,
156 contentMetrics
.GetZoom().xScale
) ||
157 !FuzzyEquals(compositorMetrics
.GetZoom().yScale
,
158 contentMetrics
.GetZoom().yScale
)) {
159 TILING_LOG("TILING: Aborting because resolution changed from %s to %s\n",
160 ToString(contentMetrics
.GetZoom()).c_str(),
161 ToString(compositorMetrics
.GetZoom()).c_str());
165 // Never abort drawing if we can't be sure we've sent a more recent
166 // display-port. If we abort updating when we shouldn't, we can end up
167 // with blank regions on the screen and we open up the risk of entering
168 // an endless updating cycle.
169 if (fabsf(contentMetrics
.GetScrollOffset().x
-
170 compositorMetrics
.GetScrollOffset().x
) <= 2 &&
171 fabsf(contentMetrics
.GetScrollOffset().y
-
172 compositorMetrics
.GetScrollOffset().y
) <= 2 &&
173 fabsf(contentMetrics
.GetDisplayPort().X() -
174 compositorMetrics
.GetDisplayPort().X()) <= 2 &&
175 fabsf(contentMetrics
.GetDisplayPort().Y() -
176 compositorMetrics
.GetDisplayPort().Y()) <= 2 &&
177 fabsf(contentMetrics
.GetDisplayPort().Width() -
178 compositorMetrics
.GetDisplayPort().Width()) <= 2 &&
179 fabsf(contentMetrics
.GetDisplayPort().Height() -
180 compositorMetrics
.GetDisplayPort().Height()) <= 2) {
184 // When not a low precision pass and the page is in danger of checker boarding
186 if (!aLowPrecision
&& !mProgressiveUpdateWasInDanger
) {
187 bool scrollUpdatePending
= contentMetrics
.GetScrollOffsetUpdated() &&
188 contentMetrics
.GetScrollGeneration() !=
189 compositorMetrics
.GetScrollGeneration();
190 // If scrollUpdatePending is true, then that means the content-side
191 // metrics has a new scroll offset that is going to be forced into the
192 // compositor but it hasn't gotten there yet.
193 // Even though right now comparing the metrics might indicate we're
194 // about to checkerboard (and that's true), the checkerboarding will
195 // disappear as soon as the new scroll offset update is processed
196 // on the compositor side. To avoid leaving things in a low-precision
197 // paint, we need to detect and handle this case (bug 1026756).
198 if (!scrollUpdatePending
&&
199 AboutToCheckerboard(contentMetrics
, compositorMetrics
)) {
200 mProgressiveUpdateWasInDanger
= true;
205 // Abort drawing stale low-precision content if there's a more recent
206 // display-port in the pipeline.
207 if (aLowPrecision
&& !aHasPendingNewThebesContent
) {
209 "TILING: Aborting low-precision because of new pending content\n");
216 bool SharedFrameMetricsHelper::AboutToCheckerboard(
217 const FrameMetrics
& aContentMetrics
,
218 const FrameMetrics
& aCompositorMetrics
) {
219 // The size of the painted area is originally computed in layer pixels in
220 // layout, but then converted to app units and then back to CSS pixels before
221 // being put in the FrameMetrics. This process can introduce some rounding
222 // error, so we inflate the rect by one app unit to account for that.
223 CSSRect painted
= (aContentMetrics
.GetCriticalDisplayPort().IsEmpty()
224 ? aContentMetrics
.GetDisplayPort()
225 : aContentMetrics
.GetCriticalDisplayPort()) +
226 aContentMetrics
.GetScrollOffset();
227 painted
.Inflate(CSSMargin::FromAppUnits(nsMargin(1, 1, 1, 1)));
229 // Inflate the rect by the danger zone. See the description of the danger zone
230 // prefs in AsyncPanZoomController.cpp for an explanation of this.
232 CSSRect(aCompositorMetrics
.GetScrollOffset(),
233 aCompositorMetrics
.CalculateBoundedCompositedSizeInCssPixels());
234 showing
.Inflate(LayerSize(StaticPrefs::apz_danger_zone_x(),
235 StaticPrefs::apz_danger_zone_y()) /
236 aCompositorMetrics
.LayersPixelsPerCSSPixel());
238 // Clamp both rects to the scrollable rect, because having either of those
239 // exceed the scrollable rect doesn't make sense, and could lead to false
241 painted
= painted
.Intersect(aContentMetrics
.GetScrollableRect());
242 showing
= showing
.Intersect(aContentMetrics
.GetScrollableRect());
244 if (!painted
.Contains(showing
)) {
245 TILING_LOG("TILING: About to checkerboard; content %s\n",
246 Stringify(aContentMetrics
).c_str());
247 TILING_LOG("TILING: About to checkerboard; painted %s\n",
248 Stringify(painted
).c_str());
249 TILING_LOG("TILING: About to checkerboard; compositor %s\n",
250 Stringify(aCompositorMetrics
).c_str());
251 TILING_LOG("TILING: About to checkerboard; showing %s\n",
252 Stringify(showing
).c_str());
258 bool ClientTiledLayerBuffer::HasFormatChanged() const {
260 gfxContentType content
= GetContentType(&mode
);
261 return content
!= mLastPaintContentType
|| mode
!= mLastPaintSurfaceMode
;
264 gfxContentType
ClientTiledLayerBuffer::GetContentType(
265 SurfaceMode
* aMode
) const {
266 gfxContentType content
= mPaintedLayer
.CanUseOpaqueSurface()
267 ? gfxContentType::COLOR
268 : gfxContentType::COLOR_ALPHA
;
269 SurfaceMode mode
= mPaintedLayer
.GetSurfaceMode();
271 if (mode
== SurfaceMode::SURFACE_COMPONENT_ALPHA
) {
272 #if defined(MOZ_GFX_OPTIMIZE_MOBILE)
273 mode
= SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA
;
275 if (!mPaintedLayer
.GetParent() ||
276 !mPaintedLayer
.GetParent()->SupportsComponentAlphaChildren()) {
277 mode
= SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA
;
279 content
= gfxContentType::COLOR
;
282 } else if (mode
== SurfaceMode::SURFACE_OPAQUE
) {
283 #if defined(MOZ_GFX_OPTIMIZE_MOBILE)
284 if (IsLowPrecision()) {
285 // If we're in low-res mode, drawing can sample from outside the visible
286 // region. Make sure that we only sample transparency if that happens.
287 mode
= SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA
;
288 content
= gfxContentType::COLOR_ALPHA
;
291 if (mPaintedLayer
.MayResample()) {
292 mode
= SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA
;
293 content
= gfxContentType::COLOR_ALPHA
;
304 class TileExpiry final
: public nsExpirationTracker
<TileClient
, 3> {
306 TileExpiry() : nsExpirationTracker
<TileClient
, 3>(1000, "TileExpiry") {}
308 static void AddTile(TileClient
* aTile
) {
310 sTileExpiry
= MakeUnique
<TileExpiry
>();
313 sTileExpiry
->AddObject(aTile
);
316 static void RemoveTile(TileClient
* aTile
) {
317 MOZ_ASSERT(sTileExpiry
);
318 sTileExpiry
->RemoveObject(aTile
);
321 static void Shutdown() { sTileExpiry
= nullptr; }
324 virtual void NotifyExpired(TileClient
* aTile
) override
{
325 aTile
->DiscardBackBuffer();
328 static UniquePtr
<TileExpiry
> sTileExpiry
;
330 UniquePtr
<TileExpiry
> TileExpiry::sTileExpiry
;
332 void ShutdownTileCache() { TileExpiry::Shutdown(); }
334 void TileClient::PrivateProtector::Set(TileClient
* const aContainer
,
335 RefPtr
<TextureClient
> aNewValue
) {
337 TileExpiry::RemoveTile(aContainer
);
341 TileExpiry::AddTile(aContainer
);
345 void TileClient::PrivateProtector::Set(TileClient
* const aContainer
,
346 TextureClient
* aNewValue
) {
347 Set(aContainer
, RefPtr
<TextureClient
>(aNewValue
));
351 TileClient::TileClient() : mWasPlaceholder(false) {}
353 TileClient::~TileClient() {
354 if (mExpirationState
.IsTracked()) {
355 MOZ_ASSERT(mBackBuffer
);
356 TileExpiry::RemoveTile(this);
360 TileClient::TileClient(const TileClient
& o
) {
361 mBackBuffer
.Set(this, o
.mBackBuffer
);
362 mBackBufferOnWhite
= o
.mBackBufferOnWhite
;
363 mFrontBuffer
= o
.mFrontBuffer
;
364 mFrontBufferOnWhite
= o
.mFrontBufferOnWhite
;
365 mWasPlaceholder
= o
.mWasPlaceholder
;
366 mUpdateRect
= o
.mUpdateRect
;
367 #ifdef GFX_TILEDLAYER_DEBUG_OVERLAY
368 mLastUpdate
= o
.mLastUpdate
;
370 mAllocator
= o
.mAllocator
;
371 mInvalidFront
= o
.mInvalidFront
;
372 mInvalidBack
= o
.mInvalidBack
;
375 TileClient
& TileClient::operator=(const TileClient
& o
) {
376 if (this == &o
) return *this;
377 mBackBuffer
.Set(this, o
.mBackBuffer
);
378 mBackBufferOnWhite
= o
.mBackBufferOnWhite
;
379 mFrontBuffer
= o
.mFrontBuffer
;
380 mFrontBufferOnWhite
= o
.mFrontBufferOnWhite
;
381 mWasPlaceholder
= o
.mWasPlaceholder
;
382 mUpdateRect
= o
.mUpdateRect
;
383 #ifdef GFX_TILEDLAYER_DEBUG_OVERLAY
384 mLastUpdate
= o
.mLastUpdate
;
386 mAllocator
= o
.mAllocator
;
387 mInvalidFront
= o
.mInvalidFront
;
388 mInvalidBack
= o
.mInvalidBack
;
392 void TileClient::Dump(std::stringstream
& aStream
) {
393 aStream
<< "TileClient(bb=" << (TextureClient
*)mBackBuffer
394 << " fb=" << mFrontBuffer
.get();
395 if (mBackBufferOnWhite
) {
396 aStream
<< " bbow=" << mBackBufferOnWhite
.get();
398 if (mFrontBufferOnWhite
) {
399 aStream
<< " fbow=" << mFrontBufferOnWhite
.get();
404 void TileClient::Flip() {
405 RefPtr
<TextureClient
> frontBuffer
= mFrontBuffer
;
406 RefPtr
<TextureClient
> frontBufferOnWhite
= mFrontBufferOnWhite
;
407 mFrontBuffer
= mBackBuffer
;
408 mFrontBufferOnWhite
= mBackBufferOnWhite
;
409 mBackBuffer
.Set(this, frontBuffer
);
410 mBackBufferOnWhite
= frontBufferOnWhite
;
411 nsIntRegion invalidFront
= mInvalidFront
;
412 mInvalidFront
= mInvalidBack
;
413 mInvalidBack
= invalidFront
;
416 void TileClient::ValidateFromFront(
417 const nsIntRegion
& aDirtyRegion
, const nsIntRegion
& aVisibleRegion
,
418 gfx::DrawTarget
* aBackBuffer
, TilePaintFlags aFlags
, IntRect
* aCopiedRect
,
419 AutoTArray
<RefPtr
<TextureClient
>, 4>* aClients
) {
420 if (!mBackBuffer
|| !mFrontBuffer
) {
424 gfx::IntSize tileSize
= mFrontBuffer
->GetSize();
425 const IntRect tileRect
= IntRect(0, 0, tileSize
.width
, tileSize
.height
);
427 if (aDirtyRegion
.Contains(tileRect
)) {
428 // The dirty region means that we no longer need the front buffer, so
430 DiscardFrontBuffer();
434 // Region that needs copying.
435 nsIntRegion regionToCopy
= mInvalidBack
;
437 regionToCopy
.Sub(regionToCopy
, aDirtyRegion
);
438 regionToCopy
.And(regionToCopy
, aVisibleRegion
);
440 *aCopiedRect
= regionToCopy
.GetBounds();
442 if (regionToCopy
.IsEmpty()) {
443 // Just redraw it all.
447 // Copy the bounding rect of regionToCopy. As tiles are quite small, it
448 // is unlikely that we'd save much by copying each individual rect of the
449 // region, but we can reevaluate this if it becomes an issue.
450 const IntRect rectToCopy
= regionToCopy
.GetBounds();
451 OpenMode readMode
= !!(aFlags
& TilePaintFlags::Async
)
452 ? OpenMode::OPEN_READ_ASYNC
453 : OpenMode::OPEN_READ
;
455 DualTextureClientAutoLock
frontBuffer(mFrontBuffer
, mFrontBufferOnWhite
,
457 if (!frontBuffer
.Succeeded()) {
461 RefPtr
<gfx::SourceSurface
> frontSurface
= frontBuffer
->Snapshot();
462 aBackBuffer
->CopySurface(frontSurface
, rectToCopy
, rectToCopy
.TopLeft());
464 if (aFlags
& TilePaintFlags::Async
) {
465 aClients
->AppendElement(mFrontBuffer
);
466 if (mFrontBufferOnWhite
) {
467 aClients
->AppendElement(mFrontBufferOnWhite
);
471 mInvalidBack
.Sub(mInvalidBack
, aVisibleRegion
);
474 void TileClient::DiscardFrontBuffer() {
476 MOZ_ASSERT(mFrontBuffer
->GetReadLock());
478 MOZ_ASSERT(mAllocator
);
480 mAllocator
->ReturnTextureClientDeferred(mFrontBuffer
);
481 if (mFrontBufferOnWhite
) {
482 mAllocator
->ReturnTextureClientDeferred(mFrontBufferOnWhite
);
486 if (mFrontBuffer
->IsLocked()) {
487 mFrontBuffer
->Unlock();
489 if (mFrontBufferOnWhite
&& mFrontBufferOnWhite
->IsLocked()) {
490 mFrontBufferOnWhite
->Unlock();
492 mFrontBuffer
= nullptr;
493 mFrontBufferOnWhite
= nullptr;
497 static void DiscardTexture(TextureClient
* aTexture
,
498 TextureClientAllocator
* aAllocator
) {
499 MOZ_ASSERT(aAllocator
);
500 if (aTexture
&& aAllocator
) {
501 MOZ_ASSERT(aTexture
->GetReadLock());
502 if (!aTexture
->HasSynchronization() && aTexture
->IsReadLocked()) {
503 // Our current back-buffer is still locked by the compositor. This can
504 // occur when the client is producing faster than the compositor can
505 // consume. In this case we just want to drop it and not return it to the
507 aAllocator
->ReportClientLost();
509 aAllocator
->ReturnTextureClientDeferred(aTexture
);
511 if (aTexture
->IsLocked()) {
517 void TileClient::DiscardBackBuffer() {
519 DiscardTexture(mBackBuffer
, mAllocator
);
520 mBackBuffer
.Set(this, nullptr);
521 DiscardTexture(mBackBufferOnWhite
, mAllocator
);
522 mBackBufferOnWhite
= nullptr;
526 static already_AddRefed
<TextureClient
> CreateBackBufferTexture(
527 TextureClient
* aCurrentTexture
, CompositableClient
& aCompositable
,
528 TextureClientAllocator
* aAllocator
) {
529 if (aCurrentTexture
) {
530 // Our current back-buffer is still locked by the compositor. This can occur
531 // when the client is producing faster than the compositor can consume. In
532 // this case we just want to drop it and not return it to the pool.
533 aAllocator
->ReportClientLost();
536 RefPtr
<TextureClient
> texture
= aAllocator
->GetTextureClient();
539 gfxCriticalError() << "[Tiling:Client] Failed to allocate a TextureClient";
543 if (!aCompositable
.AddTextureClient(texture
)) {
544 gfxCriticalError() << "[Tiling:Client] Failed to connect a TextureClient";
548 return texture
.forget();
551 void TileClient::GetSyncTextureSerials(SurfaceMode aMode
,
552 nsTArray
<uint64_t>& aSerials
) {
553 if (mFrontBuffer
&& mFrontBuffer
->HasIntermediateBuffer() &&
554 !mFrontBuffer
->IsReadLocked() &&
555 (aMode
!= SurfaceMode::SURFACE_COMPONENT_ALPHA
||
556 (mFrontBufferOnWhite
&& !mFrontBufferOnWhite
->IsReadLocked()))) {
560 if (mBackBuffer
&& !mBackBuffer
->HasIntermediateBuffer() &&
561 mBackBuffer
->IsReadLocked()) {
562 aSerials
.AppendElement(mBackBuffer
->GetSerial());
565 if (aMode
== SurfaceMode::SURFACE_COMPONENT_ALPHA
&& mBackBufferOnWhite
&&
566 !mBackBufferOnWhite
->HasIntermediateBuffer() &&
567 mBackBufferOnWhite
->IsReadLocked()) {
568 aSerials
.AppendElement(mBackBufferOnWhite
->GetSerial());
572 Maybe
<AcquiredBackBuffer
> TileClient::AcquireBackBuffer(
573 CompositableClient
& aCompositable
, const nsIntRegion
& aDirtyRegion
,
574 const nsIntRegion
& aVisibleRegion
, gfxContentType aContent
,
575 SurfaceMode aMode
, TilePaintFlags aFlags
) {
576 AUTO_PROFILER_LABEL("TileClient::AcquireBackBuffer", GRAPHICS_TileAllocation
);
578 gfxCriticalError() << "[TileClient] Missing TextureClientAllocator.";
581 if (aMode
!= SurfaceMode::SURFACE_COMPONENT_ALPHA
) {
582 // It can happen that a component-alpha layer stops being on component alpha
583 // on the next frame, just drop the buffers on white if that happens.
584 if (mBackBufferOnWhite
) {
585 mAllocator
->ReportClientLost();
586 mBackBufferOnWhite
= nullptr;
588 if (mFrontBufferOnWhite
) {
589 mAllocator
->ReportClientLost();
590 mFrontBufferOnWhite
= nullptr;
594 // Try to re-use the front-buffer if possible
595 if (mFrontBuffer
&& mFrontBuffer
->HasIntermediateBuffer() &&
596 !mFrontBuffer
->IsReadLocked() &&
597 (aMode
!= SurfaceMode::SURFACE_COMPONENT_ALPHA
||
598 (mFrontBufferOnWhite
&& !mFrontBufferOnWhite
->IsReadLocked()))) {
599 // If we had a backbuffer we no longer need it since we can re-use the
600 // front buffer here. It can be worth it to hold on to the back buffer
601 // so we don't need to pay the cost of initializing a new back buffer
602 // later (copying pixels and texture upload). But this could increase
603 // our memory usage and lead to OOM more frequently from spikes in usage,
604 // so we have this behavior behind a pref.
605 if (!StaticPrefs::layers_tiles_retain_back_buffer()) {
610 if (!mBackBuffer
|| mBackBuffer
->IsReadLocked()) {
611 mBackBuffer
.Set(this, CreateBackBufferTexture(mBackBuffer
, aCompositable
,
615 DiscardFrontBuffer();
618 mInvalidBack
= IntRect(IntPoint(), mBackBuffer
->GetSize());
621 if (aMode
== SurfaceMode::SURFACE_COMPONENT_ALPHA
) {
622 if (!mBackBufferOnWhite
|| mBackBufferOnWhite
->IsReadLocked()) {
623 mBackBufferOnWhite
= CreateBackBufferTexture(mBackBufferOnWhite
,
624 aCompositable
, mAllocator
);
625 if (!mBackBufferOnWhite
) {
627 DiscardFrontBuffer();
630 mInvalidBack
= IntRect(IntPoint(), mBackBufferOnWhite
->GetSize());
635 // Lock the texture clients
636 OpenMode lockMode
= aFlags
& TilePaintFlags::Async
637 ? OpenMode::OPEN_READ_WRITE_ASYNC
638 : OpenMode::OPEN_READ_WRITE
;
640 if (!mBackBuffer
->Lock(lockMode
)) {
641 gfxCriticalError() << "[Tiling:Client] Failed to lock a tile (B)";
643 DiscardFrontBuffer();
647 if (mBackBufferOnWhite
&& !mBackBufferOnWhite
->Lock(lockMode
)) {
648 gfxCriticalError() << "[Tiling:Client] Failed to lock a tile (W)";
650 DiscardFrontBuffer();
654 // Borrow their draw targets
655 RefPtr
<gfx::DrawTarget
> backBuffer
= mBackBuffer
->BorrowDrawTarget();
656 RefPtr
<gfx::DrawTarget
> backBufferOnWhite
= nullptr;
657 if (mBackBufferOnWhite
) {
658 backBufferOnWhite
= mBackBufferOnWhite
->BorrowDrawTarget();
661 if (!backBuffer
|| (mBackBufferOnWhite
&& !backBufferOnWhite
)) {
663 << "[Tiling:Client] Failed to acquire draw targets for tile";
665 DiscardFrontBuffer();
669 // Construct a dual target if necessary
670 RefPtr
<DrawTarget
> target
;
671 if (backBufferOnWhite
) {
672 backBuffer
= Factory::CreateDualDrawTarget(backBuffer
, backBufferOnWhite
);
673 backBufferOnWhite
= nullptr;
679 // Construct a capture draw target if necessary
680 RefPtr
<DrawTargetCapture
> capture
;
681 if (aFlags
& TilePaintFlags::Async
) {
682 capture
= Factory::CreateCaptureDrawTargetForTarget(
683 target
, StaticPrefs::layers_omtp_capture_limit_AtStartup());
687 // Gather texture clients
688 AutoTArray
<RefPtr
<TextureClient
>, 4> clients
;
689 clients
.AppendElement(RefPtr
<TextureClient
>(mBackBuffer
));
690 if (mBackBufferOnWhite
) {
691 clients
.AppendElement(mBackBufferOnWhite
);
694 // Copy from the front buerr to the back if necessary
696 ValidateFromFront(aDirtyRegion
, aVisibleRegion
, target
, aFlags
, &updatedRect
,
699 return Some(AcquiredBackBuffer
{
703 std::move(updatedRect
),
708 TileDescriptor
TileClient::GetTileDescriptor() {
709 if (IsPlaceholderTile()) {
710 mWasPlaceholder
= true;
711 return PlaceholderTileDescriptor();
713 bool wasPlaceholder
= mWasPlaceholder
;
714 mWasPlaceholder
= false;
716 bool readLocked
= mFrontBuffer
->OnForwardedToHost();
717 bool readLockedOnWhite
= false;
719 if (mFrontBufferOnWhite
) {
720 readLockedOnWhite
= mFrontBufferOnWhite
->OnForwardedToHost();
723 return TexturedTileDescriptor(
724 nullptr, mFrontBuffer
->GetIPDLActor(), Nothing(),
725 mFrontBufferOnWhite
? Some(mFrontBufferOnWhite
->GetIPDLActor())
727 mUpdateRect
, readLocked
, readLockedOnWhite
, wasPlaceholder
);
730 void ClientTiledLayerBuffer::UnlockTile(TileClient
& aTile
) {
731 // We locked the back buffer, and flipped so we now need to unlock the front
732 if (aTile
.mFrontBuffer
&& aTile
.mFrontBuffer
->IsLocked()) {
733 aTile
.mFrontBuffer
->Unlock();
734 aTile
.mFrontBuffer
->SyncWithObject(
735 mCompositableClient
.GetForwarder()->GetSyncObject());
737 if (aTile
.mFrontBufferOnWhite
&& aTile
.mFrontBufferOnWhite
->IsLocked()) {
738 aTile
.mFrontBufferOnWhite
->Unlock();
739 aTile
.mFrontBufferOnWhite
->SyncWithObject(
740 mCompositableClient
.GetForwarder()->GetSyncObject());
742 if (aTile
.mBackBuffer
&& aTile
.mBackBuffer
->IsLocked()) {
743 aTile
.mBackBuffer
->Unlock();
745 if (aTile
.mBackBufferOnWhite
&& aTile
.mBackBufferOnWhite
->IsLocked()) {
746 aTile
.mBackBufferOnWhite
->Unlock();
750 void TiledContentClient::PrintInfo(std::stringstream
& aStream
,
751 const char* aPrefix
) {
753 aStream
<< nsPrintfCString("%sTiledContentClient (0x%p)", mName
, this).get();
756 void TiledContentClient::Dump(std::stringstream
& aStream
, const char* aPrefix
,
757 bool aDumpHtml
, TextureDumpMode aCompress
) {
758 GetTiledBuffer()->Dump(aStream
, aPrefix
, aDumpHtml
, aCompress
);
761 void BasicTiledLayerPaintData::ResetPaintData() {
762 mLowPrecisionPaintCount
= 0;
763 mPaintFinished
= false;
764 mHasTransformAnimation
= false;
765 mCompositionBounds
.SetEmpty();
766 mCriticalDisplayPort
= Nothing();
769 } // namespace layers
770 } // namespace mozilla