diff options
Diffstat (limited to 'gfx/layers/client/TiledContentClient.cpp')
-rw-r--r-- | gfx/layers/client/TiledContentClient.cpp | 734 |
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 |