Bug 1632310 [wpt PR 23186] - Add test for computed versus resolved style., a=testonly
[gecko.git] / gfx / layers / client / TiledContentClient.cpp
blob9f7f5b9dd7ebb41dd0e5f932da4f5fad7ddbb8fa
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
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 "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
37 # include "cairo.h"
38 # include <sstream>
39 using mozilla::layers::Layer;
40 static void DrawDebugOverlay(mozilla::gfx::DrawTarget* dt, int x, int y,
41 int width, int height) {
42 gfxContext c(dt);
44 // Draw border
45 c.NewPath();
46 c.SetDeviceColor(Color(0.f, 0.f, 0.f));
47 c.Rectangle(gfxRect(0, 0, width, height));
48 c.Stroke();
50 // Build tile description
51 std::stringstream ss;
52 ss << x << ", " << y;
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;
63 c.NewPath();
64 c.SetDeviceColor(Color(0.f, 0.f, 0.f));
65 c.Rectangle(gfxRect(gfxPoint(2, 2), gfxSize(textWidth, 30)));
66 c.Fill();
68 c.NewPath();
69 c.SetDeviceColor(Color(1.0, 0.0, 0.0));
70 c.Rectangle(gfxRect(gfxPoint(2, 2), gfxSize(textWidth, 30)));
71 c.Stroke();
73 c.NewPath();
74 cairo_move_to(cr, 4, 28);
75 cairo_show_text(cr, ss.str().c_str());
78 #endif
80 namespace mozilla {
82 using namespace gfx;
84 namespace layers {
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) {
117 MOZ_ASSERT(aLayer);
119 CompositorBridgeChild* compositor = nullptr;
120 if (aLayer.Manager() && aLayer.Manager()->AsClientLayerManager()) {
121 compositor =
122 aLayer.Manager()->AsClientLayerManager()->GetCompositorBridgeChild();
125 if (!compositor) {
126 return false;
129 const FrameMetrics& contentMetrics = aLayer.Metrics();
130 FrameMetrics compositorMetrics;
132 if (!compositor->LookupCompositorFrameMetrics(contentMetrics.GetScrollId(),
133 compositorMetrics)) {
134 return false;
137 aViewTransform = ComputeViewTransform(contentMetrics, compositorMetrics);
139 // Reset the checkerboard risk flag when switching to low precision
140 // rendering.
141 if (aLowPrecision && !mLastProgressiveUpdateWasLowPrecision) {
142 // Skip low precision rendering until we're at risk of checkerboarding.
143 if (!mProgressiveUpdateWasInDanger) {
144 TILING_LOG(
145 "TILING: Aborting low-precision rendering because not at risk of "
146 "checkerboarding\n");
147 return true;
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());
162 return true;
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) {
181 return false;
184 // When not a low precision pass and the page is in danger of checker boarding
185 // abort update.
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;
201 return true;
205 // Abort drawing stale low-precision content if there's a more recent
206 // display-port in the pipeline.
207 if (aLowPrecision && !aHasPendingNewThebesContent) {
208 TILING_LOG(
209 "TILING: Aborting low-precision because of new pending content\n");
210 return true;
213 return false;
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.
231 CSSRect showing =
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
240 // positives.
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());
253 return true;
255 return false;
258 bool ClientTiledLayerBuffer::HasFormatChanged() const {
259 SurfaceMode mode;
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;
274 #else
275 if (!mPaintedLayer.GetParent() ||
276 !mPaintedLayer.GetParent()->SupportsComponentAlphaChildren()) {
277 mode = SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA;
278 } else {
279 content = gfxContentType::COLOR;
281 #endif
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;
290 #else
291 if (mPaintedLayer.MayResample()) {
292 mode = SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA;
293 content = gfxContentType::COLOR_ALPHA;
295 #endif
298 if (aMode) {
299 *aMode = mode;
301 return content;
304 class TileExpiry final : public nsExpirationTracker<TileClient, 3> {
305 public:
306 TileExpiry() : nsExpirationTracker<TileClient, 3>(1000, "TileExpiry") {}
308 static void AddTile(TileClient* aTile) {
309 if (!sTileExpiry) {
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; }
323 private:
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) {
336 if (mBuffer) {
337 TileExpiry::RemoveTile(aContainer);
339 mBuffer = aNewValue;
340 if (mBuffer) {
341 TileExpiry::AddTile(aContainer);
345 void TileClient::PrivateProtector::Set(TileClient* const aContainer,
346 TextureClient* aNewValue) {
347 Set(aContainer, RefPtr<TextureClient>(aNewValue));
350 // Placeholder
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;
369 #endif
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;
385 #endif
386 mAllocator = o.mAllocator;
387 mInvalidFront = o.mInvalidFront;
388 mInvalidBack = o.mInvalidBack;
389 return *this;
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();
401 aStream << ")";
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) {
421 return;
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
429 // discard it.
430 DiscardFrontBuffer();
431 return;
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.
444 return;
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,
456 readMode);
457 if (!frontBuffer.Succeeded()) {
458 return;
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() {
475 if (mFrontBuffer) {
476 MOZ_ASSERT(mFrontBuffer->GetReadLock());
478 MOZ_ASSERT(mAllocator);
479 if (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
506 // pool.
507 aAllocator->ReportClientLost();
508 } else {
509 aAllocator->ReturnTextureClientDeferred(aTexture);
511 if (aTexture->IsLocked()) {
512 aTexture->Unlock();
517 void TileClient::DiscardBackBuffer() {
518 if (mBackBuffer) {
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();
538 if (!texture) {
539 gfxCriticalError() << "[Tiling:Client] Failed to allocate a TextureClient";
540 return nullptr;
543 if (!aCompositable.AddTextureClient(texture)) {
544 gfxCriticalError() << "[Tiling:Client] Failed to connect a TextureClient";
545 return nullptr;
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()))) {
557 return;
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);
577 if (!mAllocator) {
578 gfxCriticalError() << "[TileClient] Missing TextureClientAllocator.";
579 return Nothing();
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()) {
606 DiscardBackBuffer();
608 Flip();
609 } else {
610 if (!mBackBuffer || mBackBuffer->IsReadLocked()) {
611 mBackBuffer.Set(this, CreateBackBufferTexture(mBackBuffer, aCompositable,
612 mAllocator));
613 if (!mBackBuffer) {
614 DiscardBackBuffer();
615 DiscardFrontBuffer();
616 return Nothing();
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) {
626 DiscardBackBuffer();
627 DiscardFrontBuffer();
628 return Nothing();
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)";
642 DiscardBackBuffer();
643 DiscardFrontBuffer();
644 return Nothing();
647 if (mBackBufferOnWhite && !mBackBufferOnWhite->Lock(lockMode)) {
648 gfxCriticalError() << "[Tiling:Client] Failed to lock a tile (W)";
649 DiscardBackBuffer();
650 DiscardFrontBuffer();
651 return Nothing();
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)) {
662 gfxCriticalError()
663 << "[Tiling:Client] Failed to acquire draw targets for tile";
664 DiscardBackBuffer();
665 DiscardFrontBuffer();
666 return Nothing();
669 // Construct a dual target if necessary
670 RefPtr<DrawTarget> target;
671 if (backBufferOnWhite) {
672 backBuffer = Factory::CreateDualDrawTarget(backBuffer, backBufferOnWhite);
673 backBufferOnWhite = nullptr;
674 target = backBuffer;
675 } else {
676 target = backBuffer;
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());
684 target = capture;
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
695 IntRect updatedRect;
696 ValidateFromFront(aDirtyRegion, aVisibleRegion, target, aFlags, &updatedRect,
697 &clients);
699 return Some(AcquiredBackBuffer{
700 target,
701 capture,
702 backBuffer,
703 std::move(updatedRect),
704 std::move(clients),
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())
726 : Nothing(),
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) {
752 aStream << 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