summaryrefslogtreecommitdiffstats
path: root/gfx/layers/client/TiledContentClient.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/layers/client/TiledContentClient.cpp')
-rw-r--r--gfx/layers/client/TiledContentClient.cpp734
1 files changed, 734 insertions, 0 deletions
diff --git a/gfx/layers/client/TiledContentClient.cpp b/gfx/layers/client/TiledContentClient.cpp
new file mode 100644
index 0000000000..a5a95ef36b
--- /dev/null
+++ b/gfx/layers/client/TiledContentClient.cpp
@@ -0,0 +1,734 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/layers/TiledContentClient.h"
+#include <math.h> // for ceil, ceilf, floor
+#include <algorithm>
+#include "ClientTiledPaintedLayer.h" // for ClientTiledPaintedLayer
+#include "GeckoProfiler.h" // for AUTO_PROFILER_LABEL
+#include "ClientLayerManager.h" // for ClientLayerManager
+#include "gfxContext.h" // for gfxContext, etc
+#include "gfxPlatform.h" // for gfxPlatform
+#include "gfxRect.h" // for gfxRect
+#include "mozilla/MathAlgorithms.h" // for Abs
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "mozilla/gfx/Rect.h" // for Rect
+#include "mozilla/gfx/Tools.h" // for BytesPerPixel
+#include "mozilla/layers/APZUtils.h" // for AboutToCheckerboard
+#include "mozilla/layers/CompositableForwarder.h"
+#include "mozilla/layers/CompositorBridgeChild.h" // for CompositorBridgeChild
+#include "mozilla/layers/LayerMetricsWrapper.h"
+#include "mozilla/layers/ShadowLayers.h" // for ShadowLayerForwarder
+#include "mozilla/layers/PaintThread.h" // for PaintThread
+#include "TextureClientPool.h"
+#include "nsISupportsImpl.h" // for gfxContext::AddRef, etc
+#include "nsExpirationTracker.h" // for nsExpirationTracker
+#include "nsMathUtils.h" // for NS_lroundf
+#include "TiledLayerBuffer.h"
+#include "UnitTransforms.h" // for TransformTo
+#include "mozilla/StaticPrefs_layers.h"
+#include "mozilla/UniquePtr.h"
+
+#ifdef GFX_TILEDLAYER_DEBUG_OVERLAY
+# include "cairo.h"
+# include <sstream>
+using mozilla::layers::Layer;
+static void DrawDebugOverlay(mozilla::gfx::DrawTarget* dt, int x, int y,
+ int width, int height) {
+ gfxContext c(dt);
+
+ // Draw border
+ c.NewPath();
+ c.SetDeviceColor(Color(0.f, 0.f, 0.f));
+ c.Rectangle(gfxRect(0, 0, width, height));
+ c.Stroke();
+
+ // Build tile description
+ std::stringstream ss;
+ ss << x << ", " << y;
+
+ // Draw text using cairo toy text API
+ // XXX: this drawing will silently fail if |dt| doesn't have a Cairo backend
+ cairo_t* cr = gfxFont::RefCairo(dt);
+ cairo_set_font_size(cr, 25);
+ cairo_text_extents_t extents;
+ cairo_text_extents(cr, ss.str().c_str(), &extents);
+
+ int textWidth = extents.width + 6;
+
+ c.NewPath();
+ c.SetDeviceColor(Color(0.f, 0.f, 0.f));
+ c.Rectangle(gfxRect(gfxPoint(2, 2), gfxSize(textWidth, 30)));
+ c.Fill();
+
+ c.NewPath();
+ c.SetDeviceColor(Color(1.0, 0.0, 0.0));
+ c.Rectangle(gfxRect(gfxPoint(2, 2), gfxSize(textWidth, 30)));
+ c.Stroke();
+
+ c.NewPath();
+ cairo_move_to(cr, 4, 28);
+ cairo_show_text(cr, ss.str().c_str());
+}
+
+#endif
+
+namespace mozilla {
+
+using namespace gfx;
+
+namespace layers {
+
+SharedFrameMetricsHelper::SharedFrameMetricsHelper()
+ : mLastProgressiveUpdateWasLowPrecision(false),
+ mProgressiveUpdateWasInDanger(false) {
+ MOZ_COUNT_CTOR(SharedFrameMetricsHelper);
+}
+
+SharedFrameMetricsHelper::~SharedFrameMetricsHelper() {
+ MOZ_COUNT_DTOR(SharedFrameMetricsHelper);
+}
+
+static inline bool FuzzyEquals(float a, float b) {
+ return (fabsf(a - b) < 1e-6);
+}
+
+static AsyncTransform ComputeViewTransform(
+ const FrameMetrics& aContentMetrics,
+ const FrameMetrics& aCompositorMetrics) {
+ // This is basically the same code as
+ // AsyncPanZoomController::GetCurrentAsyncTransform but with aContentMetrics
+ // used in place of mLastContentPaintMetrics, because they should be
+ // equivalent, modulo race conditions while transactions are inflight.
+
+ ParentLayerPoint translation = (aCompositorMetrics.GetVisualScrollOffset() -
+ aContentMetrics.GetLayoutScrollOffset()) *
+ aCompositorMetrics.GetZoom();
+ return AsyncTransform(aCompositorMetrics.GetAsyncZoom(), -translation);
+}
+
+bool SharedFrameMetricsHelper::UpdateFromCompositorFrameMetrics(
+ const LayerMetricsWrapper& aLayer, bool aHasPendingNewThebesContent,
+ bool aLowPrecision, AsyncTransform& aViewTransform) {
+ MOZ_ASSERT(aLayer);
+
+ CompositorBridgeChild* compositor = nullptr;
+ if (aLayer.Manager() && aLayer.Manager()->AsClientLayerManager()) {
+ compositor =
+ aLayer.Manager()->AsClientLayerManager()->GetCompositorBridgeChild();
+ }
+
+ if (!compositor) {
+ return false;
+ }
+
+ const FrameMetrics& contentMetrics = aLayer.Metrics();
+ FrameMetrics compositorMetrics;
+
+ if (!compositor->LookupCompositorFrameMetrics(contentMetrics.GetScrollId(),
+ compositorMetrics)) {
+ return false;
+ }
+
+ aViewTransform = ComputeViewTransform(contentMetrics, compositorMetrics);
+
+ // Reset the checkerboard risk flag when switching to low precision
+ // rendering.
+ if (aLowPrecision && !mLastProgressiveUpdateWasLowPrecision) {
+ // Skip low precision rendering until we're at risk of checkerboarding.
+ if (!mProgressiveUpdateWasInDanger) {
+ TILING_LOG(
+ "TILING: Aborting low-precision rendering because not at risk of "
+ "checkerboarding\n");
+ return true;
+ }
+ mProgressiveUpdateWasInDanger = false;
+ }
+ mLastProgressiveUpdateWasLowPrecision = aLowPrecision;
+
+ // Always abort updates if the resolution has changed. There's no use
+ // in drawing at the incorrect resolution.
+ if (!FuzzyEquals(compositorMetrics.GetZoom().xScale,
+ contentMetrics.GetZoom().xScale) ||
+ !FuzzyEquals(compositorMetrics.GetZoom().yScale,
+ contentMetrics.GetZoom().yScale)) {
+ TILING_LOG("TILING: Aborting because resolution changed from %s to %s\n",
+ ToString(contentMetrics.GetZoom()).c_str(),
+ ToString(compositorMetrics.GetZoom()).c_str());
+ return true;
+ }
+
+ // Never abort drawing if we can't be sure we've sent a more recent
+ // display-port. If we abort updating when we shouldn't, we can end up
+ // with blank regions on the screen and we open up the risk of entering
+ // an endless updating cycle.
+ // XXX Suspicious comparisons between layout and visual scroll offsets.
+ // This may not do the right thing when we're zoomed in.
+ // However, note that the code as written will err on the side of returning
+ // false (whenever we're zoomed in and there's a persistent nonzero offset
+ // between the layout and visual viewports), which is the safer option.
+ if (fabsf(contentMetrics.GetLayoutScrollOffset().x -
+ compositorMetrics.GetVisualScrollOffset().x) <= 2 &&
+ fabsf(contentMetrics.GetLayoutScrollOffset().y -
+ compositorMetrics.GetVisualScrollOffset().y) <= 2 &&
+ fabsf(contentMetrics.GetDisplayPort().X() -
+ compositorMetrics.GetDisplayPort().X()) <= 2 &&
+ fabsf(contentMetrics.GetDisplayPort().Y() -
+ compositorMetrics.GetDisplayPort().Y()) <= 2 &&
+ fabsf(contentMetrics.GetDisplayPort().Width() -
+ compositorMetrics.GetDisplayPort().Width()) <= 2 &&
+ fabsf(contentMetrics.GetDisplayPort().Height() -
+ compositorMetrics.GetDisplayPort().Height()) <= 2) {
+ return false;
+ }
+
+ // When not a low precision pass and the page is in danger of checker boarding
+ // abort update.
+ if (!aLowPrecision && !mProgressiveUpdateWasInDanger) {
+ const nsTArray<ScrollPositionUpdate>& scrollUpdates =
+ aLayer.Metadata().GetScrollUpdates();
+ bool scrollUpdatePending = !scrollUpdates.IsEmpty() &&
+ compositorMetrics.GetScrollGeneration() <
+ scrollUpdates.LastElement().GetGeneration();
+ // If scrollUpdatePending is true, then that means the content-side
+ // metrics has a new scroll offset that is going to be forced into the
+ // compositor but it hasn't gotten there yet.
+ // Even though right now comparing the metrics might indicate we're
+ // about to checkerboard (and that's true), the checkerboarding will
+ // disappear as soon as the new scroll offset update is processed
+ // on the compositor side. To avoid leaving things in a low-precision
+ // paint, we need to detect and handle this case (bug 1026756).
+ if (!scrollUpdatePending &&
+ apz::AboutToCheckerboard(contentMetrics, compositorMetrics)) {
+ mProgressiveUpdateWasInDanger = true;
+ return true;
+ }
+ }
+
+ // Abort drawing stale low-precision content if there's a more recent
+ // display-port in the pipeline.
+ if (aLowPrecision && !aHasPendingNewThebesContent) {
+ TILING_LOG(
+ "TILING: Aborting low-precision because of new pending content\n");
+ return true;
+ }
+
+ return false;
+}
+
+bool ClientTiledLayerBuffer::HasFormatChanged() const {
+ SurfaceMode mode;
+ gfxContentType content = GetContentType(&mode);
+ return content != mLastPaintContentType || mode != mLastPaintSurfaceMode;
+}
+
+gfxContentType ClientTiledLayerBuffer::GetContentType(
+ SurfaceMode* aMode) const {
+ gfxContentType content = mPaintedLayer.CanUseOpaqueSurface()
+ ? gfxContentType::COLOR
+ : gfxContentType::COLOR_ALPHA;
+ SurfaceMode mode = mPaintedLayer.GetSurfaceMode();
+
+ if (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
+#if defined(MOZ_GFX_OPTIMIZE_MOBILE)
+ mode = SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA;
+#else
+ if (!mPaintedLayer.GetParent() ||
+ !mPaintedLayer.GetParent()->SupportsComponentAlphaChildren()) {
+ mode = SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA;
+ } else {
+ content = gfxContentType::COLOR;
+ }
+#endif
+ } else if (mode == SurfaceMode::SURFACE_OPAQUE) {
+#if defined(MOZ_GFX_OPTIMIZE_MOBILE)
+ if (IsLowPrecision()) {
+ // If we're in low-res mode, drawing can sample from outside the visible
+ // region. Make sure that we only sample transparency if that happens.
+ mode = SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA;
+ content = gfxContentType::COLOR_ALPHA;
+ }
+#else
+ if (mPaintedLayer.MayResample()) {
+ mode = SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA;
+ content = gfxContentType::COLOR_ALPHA;
+ }
+#endif
+ }
+
+ if (aMode) {
+ *aMode = mode;
+ }
+ return content;
+}
+
+class TileExpiry final : public nsExpirationTracker<TileClient, 3> {
+ public:
+ TileExpiry() : nsExpirationTracker<TileClient, 3>(1000, "TileExpiry") {}
+
+ static void AddTile(TileClient* aTile) {
+ if (!sTileExpiry) {
+ sTileExpiry = MakeUnique<TileExpiry>();
+ }
+
+ sTileExpiry->AddObject(aTile);
+ }
+
+ static void RemoveTile(TileClient* aTile) {
+ MOZ_ASSERT(sTileExpiry);
+ sTileExpiry->RemoveObject(aTile);
+ }
+
+ static void Shutdown() { sTileExpiry = nullptr; }
+
+ private:
+ virtual void NotifyExpired(TileClient* aTile) override {
+ aTile->DiscardBackBuffer();
+ }
+
+ static UniquePtr<TileExpiry> sTileExpiry;
+};
+UniquePtr<TileExpiry> TileExpiry::sTileExpiry;
+
+void ShutdownTileCache() { TileExpiry::Shutdown(); }
+
+void TileClient::PrivateProtector::Set(TileClient* const aContainer,
+ RefPtr<TextureClient> aNewValue) {
+ if (mBuffer) {
+ TileExpiry::RemoveTile(aContainer);
+ }
+ mBuffer = aNewValue;
+ if (mBuffer) {
+ TileExpiry::AddTile(aContainer);
+ }
+}
+
+void TileClient::PrivateProtector::Set(TileClient* const aContainer,
+ TextureClient* aNewValue) {
+ Set(aContainer, RefPtr<TextureClient>(aNewValue));
+}
+
+// Placeholder
+TileClient::TileClient() : mWasPlaceholder(false) {}
+
+TileClient::~TileClient() {
+ if (mExpirationState.IsTracked()) {
+ MOZ_ASSERT(mBackBuffer);
+ TileExpiry::RemoveTile(this);
+ }
+}
+
+TileClient::TileClient(const TileClient& o) {
+ mBackBuffer.Set(this, o.mBackBuffer);
+ mBackBufferOnWhite = o.mBackBufferOnWhite;
+ mFrontBuffer = o.mFrontBuffer;
+ mFrontBufferOnWhite = o.mFrontBufferOnWhite;
+ mWasPlaceholder = o.mWasPlaceholder;
+ mUpdateRect = o.mUpdateRect;
+#ifdef GFX_TILEDLAYER_DEBUG_OVERLAY
+ mLastUpdate = o.mLastUpdate;
+#endif
+ mAllocator = o.mAllocator;
+ mInvalidFront = o.mInvalidFront;
+ mInvalidBack = o.mInvalidBack;
+}
+
+TileClient& TileClient::operator=(const TileClient& o) {
+ if (this == &o) return *this;
+ mBackBuffer.Set(this, o.mBackBuffer);
+ mBackBufferOnWhite = o.mBackBufferOnWhite;
+ mFrontBuffer = o.mFrontBuffer;
+ mFrontBufferOnWhite = o.mFrontBufferOnWhite;
+ mWasPlaceholder = o.mWasPlaceholder;
+ mUpdateRect = o.mUpdateRect;
+#ifdef GFX_TILEDLAYER_DEBUG_OVERLAY
+ mLastUpdate = o.mLastUpdate;
+#endif
+ mAllocator = o.mAllocator;
+ mInvalidFront = o.mInvalidFront;
+ mInvalidBack = o.mInvalidBack;
+ return *this;
+}
+
+void TileClient::Dump(std::stringstream& aStream) {
+ aStream << "TileClient(bb=" << (TextureClient*)mBackBuffer
+ << " fb=" << mFrontBuffer.get();
+ if (mBackBufferOnWhite) {
+ aStream << " bbow=" << mBackBufferOnWhite.get();
+ }
+ if (mFrontBufferOnWhite) {
+ aStream << " fbow=" << mFrontBufferOnWhite.get();
+ }
+ aStream << ")";
+}
+
+void TileClient::Flip() {
+ RefPtr<TextureClient> frontBuffer = mFrontBuffer;
+ RefPtr<TextureClient> frontBufferOnWhite = mFrontBufferOnWhite;
+ mFrontBuffer = mBackBuffer;
+ mFrontBufferOnWhite = mBackBufferOnWhite;
+ mBackBuffer.Set(this, frontBuffer);
+ mBackBufferOnWhite = frontBufferOnWhite;
+ nsIntRegion invalidFront = mInvalidFront;
+ mInvalidFront = mInvalidBack;
+ mInvalidBack = invalidFront;
+}
+
+void TileClient::ValidateFromFront(
+ const nsIntRegion& aDirtyRegion, const nsIntRegion& aVisibleRegion,
+ gfx::DrawTarget* aBackBuffer, TilePaintFlags aFlags, IntRect* aCopiedRect,
+ AutoTArray<RefPtr<TextureClient>, 4>* aClients) {
+ if (!mBackBuffer || !mFrontBuffer) {
+ return;
+ }
+
+ gfx::IntSize tileSize = mFrontBuffer->GetSize();
+ const IntRect tileRect = IntRect(0, 0, tileSize.width, tileSize.height);
+
+ if (aDirtyRegion.Contains(tileRect)) {
+ // The dirty region means that we no longer need the front buffer, so
+ // discard it.
+ DiscardFrontBuffer();
+ return;
+ }
+
+ // Region that needs copying.
+ nsIntRegion regionToCopy = mInvalidBack;
+
+ regionToCopy.Sub(regionToCopy, aDirtyRegion);
+ regionToCopy.And(regionToCopy, aVisibleRegion);
+
+ *aCopiedRect = regionToCopy.GetBounds();
+
+ if (regionToCopy.IsEmpty()) {
+ // Just redraw it all.
+ return;
+ }
+
+ // Copy the bounding rect of regionToCopy. As tiles are quite small, it
+ // is unlikely that we'd save much by copying each individual rect of the
+ // region, but we can reevaluate this if it becomes an issue.
+ const IntRect rectToCopy = regionToCopy.GetBounds();
+ OpenMode readMode = !!(aFlags & TilePaintFlags::Async)
+ ? OpenMode::OPEN_READ_ASYNC
+ : OpenMode::OPEN_READ;
+
+ DualTextureClientAutoLock frontBuffer(mFrontBuffer, mFrontBufferOnWhite,
+ readMode);
+ if (!frontBuffer.Succeeded()) {
+ return;
+ }
+
+ RefPtr<gfx::SourceSurface> frontSurface = frontBuffer->Snapshot();
+ aBackBuffer->CopySurface(frontSurface, rectToCopy, rectToCopy.TopLeft());
+
+ if (aFlags & TilePaintFlags::Async) {
+ aClients->AppendElement(mFrontBuffer);
+ if (mFrontBufferOnWhite) {
+ aClients->AppendElement(mFrontBufferOnWhite);
+ }
+ }
+
+ mInvalidBack.Sub(mInvalidBack, aVisibleRegion);
+}
+
+void TileClient::DiscardFrontBuffer() {
+ if (mFrontBuffer) {
+ MOZ_ASSERT(mFrontBuffer->GetReadLock());
+
+ MOZ_ASSERT(mAllocator);
+ if (mAllocator) {
+ mAllocator->ReturnTextureClientDeferred(mFrontBuffer);
+ if (mFrontBufferOnWhite) {
+ mAllocator->ReturnTextureClientDeferred(mFrontBufferOnWhite);
+ }
+ }
+
+ if (mFrontBuffer->IsLocked()) {
+ mFrontBuffer->Unlock();
+ }
+ if (mFrontBufferOnWhite && mFrontBufferOnWhite->IsLocked()) {
+ mFrontBufferOnWhite->Unlock();
+ }
+ mFrontBuffer = nullptr;
+ mFrontBufferOnWhite = nullptr;
+ }
+}
+
+static void DiscardTexture(TextureClient* aTexture,
+ TextureClientAllocator* aAllocator) {
+ MOZ_ASSERT(aAllocator);
+ if (aTexture && aAllocator) {
+ MOZ_ASSERT(aTexture->GetReadLock());
+ if (!aTexture->HasSynchronization() && aTexture->IsReadLocked()) {
+ // Our current back-buffer is still locked by the compositor. This can
+ // occur when the client is producing faster than the compositor can
+ // consume. In this case we just want to drop it and not return it to the
+ // pool.
+ aAllocator->ReportClientLost();
+ } else {
+ aAllocator->ReturnTextureClientDeferred(aTexture);
+ }
+ if (aTexture->IsLocked()) {
+ aTexture->Unlock();
+ }
+ }
+}
+
+void TileClient::DiscardBackBuffer() {
+ if (mBackBuffer) {
+ DiscardTexture(mBackBuffer, mAllocator);
+ mBackBuffer.Set(this, nullptr);
+ DiscardTexture(mBackBufferOnWhite, mAllocator);
+ mBackBufferOnWhite = nullptr;
+ }
+}
+
+static already_AddRefed<TextureClient> CreateBackBufferTexture(
+ TextureClient* aCurrentTexture, CompositableClient& aCompositable,
+ TextureClientAllocator* aAllocator) {
+ if (aCurrentTexture) {
+ // Our current back-buffer is still locked by the compositor. This can occur
+ // when the client is producing faster than the compositor can consume. In
+ // this case we just want to drop it and not return it to the pool.
+ aAllocator->ReportClientLost();
+ }
+
+ RefPtr<TextureClient> texture = aAllocator->GetTextureClient();
+
+ if (!texture) {
+ gfxCriticalError() << "[Tiling:Client] Failed to allocate a TextureClient";
+ return nullptr;
+ }
+
+ if (!aCompositable.AddTextureClient(texture)) {
+ gfxCriticalError() << "[Tiling:Client] Failed to connect a TextureClient";
+ return nullptr;
+ }
+
+ return texture.forget();
+}
+
+void TileClient::GetSyncTextureSerials(SurfaceMode aMode,
+ nsTArray<uint64_t>& aSerials) {
+ if (mFrontBuffer && mFrontBuffer->HasIntermediateBuffer() &&
+ !mFrontBuffer->IsReadLocked() &&
+ (aMode != SurfaceMode::SURFACE_COMPONENT_ALPHA ||
+ (mFrontBufferOnWhite && !mFrontBufferOnWhite->IsReadLocked()))) {
+ return;
+ }
+
+ if (mBackBuffer && !mBackBuffer->HasIntermediateBuffer() &&
+ mBackBuffer->IsReadLocked()) {
+ aSerials.AppendElement(mBackBuffer->GetSerial());
+ }
+
+ if (aMode == SurfaceMode::SURFACE_COMPONENT_ALPHA && mBackBufferOnWhite &&
+ !mBackBufferOnWhite->HasIntermediateBuffer() &&
+ mBackBufferOnWhite->IsReadLocked()) {
+ aSerials.AppendElement(mBackBufferOnWhite->GetSerial());
+ }
+}
+
+Maybe<AcquiredBackBuffer> TileClient::AcquireBackBuffer(
+ CompositableClient& aCompositable, const nsIntRegion& aDirtyRegion,
+ const nsIntRegion& aVisibleRegion, gfxContentType aContent,
+ SurfaceMode aMode, TilePaintFlags aFlags) {
+ AUTO_PROFILER_LABEL("TileClient::AcquireBackBuffer", GRAPHICS_TileAllocation);
+ if (!mAllocator) {
+ gfxCriticalError() << "[TileClient] Missing TextureClientAllocator.";
+ return Nothing();
+ }
+ if (aMode != SurfaceMode::SURFACE_COMPONENT_ALPHA) {
+ // It can happen that a component-alpha layer stops being on component alpha
+ // on the next frame, just drop the buffers on white if that happens.
+ if (mBackBufferOnWhite) {
+ mAllocator->ReportClientLost();
+ mBackBufferOnWhite = nullptr;
+ }
+ if (mFrontBufferOnWhite) {
+ mAllocator->ReportClientLost();
+ mFrontBufferOnWhite = nullptr;
+ }
+ }
+
+ // Try to re-use the front-buffer if possible
+ if (mFrontBuffer && mFrontBuffer->HasIntermediateBuffer() &&
+ !mFrontBuffer->IsReadLocked() &&
+ (aMode != SurfaceMode::SURFACE_COMPONENT_ALPHA ||
+ (mFrontBufferOnWhite && !mFrontBufferOnWhite->IsReadLocked()))) {
+ // If we had a backbuffer we no longer need it since we can re-use the
+ // front buffer here. It can be worth it to hold on to the back buffer
+ // so we don't need to pay the cost of initializing a new back buffer
+ // later (copying pixels and texture upload). But this could increase
+ // our memory usage and lead to OOM more frequently from spikes in usage,
+ // so we have this behavior behind a pref.
+ if (!StaticPrefs::layers_tiles_retain_back_buffer()) {
+ DiscardBackBuffer();
+ }
+ Flip();
+ } else {
+ if (!mBackBuffer || mBackBuffer->IsReadLocked()) {
+ mBackBuffer.Set(this, CreateBackBufferTexture(mBackBuffer, aCompositable,
+ mAllocator));
+ if (!mBackBuffer) {
+ DiscardBackBuffer();
+ DiscardFrontBuffer();
+ return Nothing();
+ }
+ mInvalidBack = IntRect(IntPoint(), mBackBuffer->GetSize());
+ }
+
+ if (aMode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
+ if (!mBackBufferOnWhite || mBackBufferOnWhite->IsReadLocked()) {
+ mBackBufferOnWhite = CreateBackBufferTexture(mBackBufferOnWhite,
+ aCompositable, mAllocator);
+ if (!mBackBufferOnWhite) {
+ DiscardBackBuffer();
+ DiscardFrontBuffer();
+ return Nothing();
+ }
+ mInvalidBack = IntRect(IntPoint(), mBackBufferOnWhite->GetSize());
+ }
+ }
+ }
+
+ // Lock the texture clients
+ OpenMode lockMode = aFlags & TilePaintFlags::Async
+ ? OpenMode::OPEN_READ_WRITE_ASYNC
+ : OpenMode::OPEN_READ_WRITE;
+
+ if (!mBackBuffer->Lock(lockMode)) {
+ gfxCriticalError() << "[Tiling:Client] Failed to lock a tile (B)";
+ DiscardBackBuffer();
+ DiscardFrontBuffer();
+ return Nothing();
+ }
+
+ if (mBackBufferOnWhite && !mBackBufferOnWhite->Lock(lockMode)) {
+ gfxCriticalError() << "[Tiling:Client] Failed to lock a tile (W)";
+ DiscardBackBuffer();
+ DiscardFrontBuffer();
+ return Nothing();
+ }
+
+ // Borrow their draw targets
+ RefPtr<gfx::DrawTarget> backBuffer = mBackBuffer->BorrowDrawTarget();
+ RefPtr<gfx::DrawTarget> backBufferOnWhite = nullptr;
+ if (mBackBufferOnWhite) {
+ backBufferOnWhite = mBackBufferOnWhite->BorrowDrawTarget();
+ }
+
+ if (!backBuffer || (mBackBufferOnWhite && !backBufferOnWhite)) {
+ gfxCriticalError()
+ << "[Tiling:Client] Failed to acquire draw targets for tile";
+ DiscardBackBuffer();
+ DiscardFrontBuffer();
+ return Nothing();
+ }
+
+ // Construct a dual target if necessary
+ RefPtr<DrawTarget> target;
+ if (backBufferOnWhite) {
+ backBuffer = Factory::CreateDualDrawTarget(backBuffer, backBufferOnWhite);
+ backBufferOnWhite = nullptr;
+ target = backBuffer;
+ } else {
+ target = backBuffer;
+ }
+
+ // Construct a capture draw target if necessary
+ RefPtr<DrawTargetCapture> capture;
+ if (aFlags & TilePaintFlags::Async) {
+ capture = Factory::CreateCaptureDrawTargetForTarget(
+ target, StaticPrefs::layers_omtp_capture_limit_AtStartup());
+ target = capture;
+ }
+
+ // Gather texture clients
+ AutoTArray<RefPtr<TextureClient>, 4> clients;
+ clients.AppendElement(RefPtr<TextureClient>(mBackBuffer));
+ if (mBackBufferOnWhite) {
+ clients.AppendElement(mBackBufferOnWhite);
+ }
+
+ // Copy from the front buerr to the back if necessary
+ IntRect updatedRect;
+ ValidateFromFront(aDirtyRegion, aVisibleRegion, target, aFlags, &updatedRect,
+ &clients);
+
+ return Some(AcquiredBackBuffer{
+ target,
+ capture,
+ backBuffer,
+ std::move(updatedRect),
+ std::move(clients),
+ });
+}
+
+TileDescriptor TileClient::GetTileDescriptor() {
+ if (IsPlaceholderTile()) {
+ mWasPlaceholder = true;
+ return PlaceholderTileDescriptor();
+ }
+ bool wasPlaceholder = mWasPlaceholder;
+ mWasPlaceholder = false;
+
+ bool readLocked = mFrontBuffer->OnForwardedToHost();
+ bool readLockedOnWhite = false;
+
+ if (mFrontBufferOnWhite) {
+ readLockedOnWhite = mFrontBufferOnWhite->OnForwardedToHost();
+ }
+
+ return TexturedTileDescriptor(
+ nullptr, mFrontBuffer->GetIPDLActor(), Nothing(),
+ mFrontBufferOnWhite ? Some(mFrontBufferOnWhite->GetIPDLActor())
+ : Nothing(),
+ mUpdateRect, readLocked, readLockedOnWhite, wasPlaceholder);
+}
+
+void ClientTiledLayerBuffer::UnlockTile(TileClient& aTile) {
+ // We locked the back buffer, and flipped so we now need to unlock the front
+ if (aTile.mFrontBuffer && aTile.mFrontBuffer->IsLocked()) {
+ aTile.mFrontBuffer->Unlock();
+ aTile.mFrontBuffer->SyncWithObject(
+ mCompositableClient.GetForwarder()->GetSyncObject());
+ }
+ if (aTile.mFrontBufferOnWhite && aTile.mFrontBufferOnWhite->IsLocked()) {
+ aTile.mFrontBufferOnWhite->Unlock();
+ aTile.mFrontBufferOnWhite->SyncWithObject(
+ mCompositableClient.GetForwarder()->GetSyncObject());
+ }
+ if (aTile.mBackBuffer && aTile.mBackBuffer->IsLocked()) {
+ aTile.mBackBuffer->Unlock();
+ }
+ if (aTile.mBackBufferOnWhite && aTile.mBackBufferOnWhite->IsLocked()) {
+ aTile.mBackBufferOnWhite->Unlock();
+ }
+}
+
+void TiledContentClient::PrintInfo(std::stringstream& aStream,
+ const char* aPrefix) {
+ aStream << aPrefix;
+ aStream << nsPrintfCString("%sTiledContentClient (0x%p)", mName, this).get();
+}
+
+void TiledContentClient::Dump(std::stringstream& aStream, const char* aPrefix,
+ bool aDumpHtml, TextureDumpMode aCompress) {
+ GetTiledBuffer()->Dump(aStream, aPrefix, aDumpHtml, aCompress);
+}
+
+void BasicTiledLayerPaintData::ResetPaintData() {
+ mLowPrecisionPaintCount = 0;
+ mPaintFinished = false;
+ mHasTransformAnimation = false;
+ mCompositionBounds.SetEmpty();
+ mCriticalDisplayPort = Nothing();
+}
+
+} // namespace layers
+} // namespace mozilla