fune/gfx/layers/client/TiledContentClient.cpp
Margareta Eliza Balazs b1e7992b82 Backed out 8 changesets (bug 1265824) for bustage in /builds/worker/workspace/build/src/gfx/layers/opengl/CompositorOGL.cpp on a CLOSED TREE
Backed out changeset 1099d6f15f9f (bug 1265824)
Backed out changeset b5ba15b1a70f (bug 1265824)
Backed out changeset 51795de4adaf (bug 1265824)
Backed out changeset be68741ff4ce (bug 1265824)
Backed out changeset 4731dc56702d (bug 1265824)
Backed out changeset 984133e9614b (bug 1265824)
Backed out changeset efce316a4425 (bug 1265824)
Backed out changeset 367abce30668 (bug 1265824)
2018-07-19 09:33:28 +03:00

837 lines
28 KiB
C++

/* -*- 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 "gfxPrefs.h" // for gfxPrefs
#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/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 "nsDebug.h" // for NS_ASSERTION
#include "nsISupportsImpl.h" // for gfxContext::AddRef, etc
#include "nsExpirationTracker.h" // for nsExpirationTracker
#include "nsMathUtils.h" // for NS_lroundf
#include "LayersLogging.h"
#include "UnitTransforms.h" // for TransformTo
#include "mozilla/UniquePtr.h"
// This is the minimum area that we deem reasonable to copy from the front buffer to the
// back buffer on tile updates. If the valid region is smaller than this, we just
// redraw it and save on the copy (and requisite surface-locking involved).
#define MINIMUM_TILE_COPY_AREA (1.f/16.f)
#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.GetScrollOffset() - aContentMetrics.GetScrollOffset())
* 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.
if (fabsf(contentMetrics.GetScrollOffset().x - compositorMetrics.GetScrollOffset().x) <= 2 &&
fabsf(contentMetrics.GetScrollOffset().y - compositorMetrics.GetScrollOffset().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) {
bool scrollUpdatePending = contentMetrics.GetScrollOffsetUpdated() &&
contentMetrics.GetScrollGeneration() != compositorMetrics.GetScrollGeneration();
// 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 && 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
SharedFrameMetricsHelper::AboutToCheckerboard(const FrameMetrics& aContentMetrics,
const FrameMetrics& aCompositorMetrics)
{
// The size of the painted area is originally computed in layer pixels in layout, but then
// converted to app units and then back to CSS pixels before being put in the FrameMetrics.
// This process can introduce some rounding error, so we inflate the rect by one app unit
// to account for that.
CSSRect painted = (aContentMetrics.GetCriticalDisplayPort().IsEmpty()
? aContentMetrics.GetDisplayPort()
: aContentMetrics.GetCriticalDisplayPort())
+ aContentMetrics.GetScrollOffset();
painted.Inflate(CSSMargin::FromAppUnits(nsMargin(1, 1, 1, 1)));
// Inflate the rect by the danger zone. See the description of the danger zone prefs
// in AsyncPanZoomController.cpp for an explanation of this.
CSSRect showing = CSSRect(aCompositorMetrics.GetScrollOffset(),
aCompositorMetrics.CalculateBoundedCompositedSizeInCssPixels());
showing.Inflate(LayerSize(gfxPrefs::APZDangerZoneX(), gfxPrefs::APZDangerZoneY())
/ aCompositorMetrics.LayersPixelsPerCSSPixel());
// Clamp both rects to the scrollable rect, because having either of those
// exceed the scrollable rect doesn't make sense, and could lead to false
// positives.
painted = painted.Intersect(aContentMetrics.GetScrollableRect());
showing = showing.Intersect(aContentMetrics.GetScrollableRect());
if (!painted.Contains(showing)) {
TILING_LOG("TILING: About to checkerboard; content %s\n", Stringify(aContentMetrics).c_str());
TILING_LOG("TILING: About to checkerboard; painted %s\n", Stringify(painted).c_str());
TILING_LOG("TILING: About to checkerboard; compositor %s\n", Stringify(aCompositorMetrics).c_str());
TILING_LOG("TILING: About to checkerboard; showing %s\n", Stringify(showing).c_str());
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;
}
static bool
CopyFrontToBack(TextureClient* aFront,
TextureClient* aBack,
const gfx::IntRect& aRectToCopy,
TilePaintFlags aFlags,
std::vector<CapturedTiledPaintState::Copy>* aCopies,
std::vector<RefPtr<TextureClient>>* aClients)
{
bool asyncPaint = !!(aFlags & TilePaintFlags::Async);
OpenMode asyncFlags = asyncPaint ? OpenMode::OPEN_ASYNC : OpenMode::OPEN_NONE;
TextureClientAutoLock frontLock(aFront, OpenMode::OPEN_READ | asyncFlags);
if (!frontLock.Succeeded()) {
return false;
}
if (!aBack->Lock(OpenMode::OPEN_READ_WRITE | asyncFlags)) {
gfxCriticalError() << "[Tiling:Client] Failed to lock the tile's back buffer";
return false;
}
RefPtr<gfx::DrawTarget> backBuffer = aBack->BorrowDrawTarget();
if (!backBuffer) {
gfxWarning() << "[Tiling:Client] Failed to aquire the back buffer's draw target";
return false;
}
RefPtr<gfx::DrawTarget> frontBuffer = aFront->BorrowDrawTarget();
if (!frontBuffer) {
gfxWarning() << "[Tiling:Client] Failed to aquire the front buffer's draw target";
return false;
}
auto copy = CapturedTiledPaintState::Copy{
frontBuffer, backBuffer, aRectToCopy, aRectToCopy.TopLeft()
};
if (asyncPaint && aCopies) {
aClients->push_back(aFront);
aCopies->push_back(copy);
} else {
copy.CopyBuffer();
}
return true;
}
void
TileClient::ValidateBackBufferFromFront(const nsIntRegion& aDirtyRegion,
const nsIntRegion& aVisibleRegion,
nsIntRegion& aAddPaintedRegion,
TilePaintFlags aFlags,
std::vector<CapturedTiledPaintState::Copy>* aCopies,
std::vector<RefPtr<TextureClient>>* 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);
aAddPaintedRegion = regionToCopy;
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();
if (!CopyFrontToBack(mFrontBuffer, mBackBuffer, rectToCopy, aFlags, aCopies, aClients)) {
return;
}
if (mBackBufferOnWhite) {
MOZ_ASSERT(mFrontBufferOnWhite);
if (!CopyFrontToBack(mFrontBufferOnWhite, mBackBufferOnWhite, rectToCopy, aFlags, aCopies, aClients)) {
return;
}
}
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;
}
}
bool
TileClient::CopyFromBuffer(RefPtr<TextureClient> aBuffer,
RefPtr<TextureClient> aBufferOnWhite,
nsIntPoint aBufferOrigin,
nsIntPoint aTileOrigin,
const nsIntRegion& aRegion,
TilePaintFlags aFlags,
std::vector<CapturedTiledPaintState::Copy>* aCopies)
{
if (aRegion.IsEmpty()) {
return true;
}
bool asyncPaint = !!(aFlags & TilePaintFlags::Async);
auto CopyBuffer = [&aRegion, asyncPaint, &aCopies] (auto aSrc, auto aSrcOrigin, auto aDest, auto aDestOrigin) {
MOZ_ASSERT(aDest->IsLocked());
OpenMode asyncFlags = asyncPaint ? OpenMode::OPEN_ASYNC
: OpenMode::OPEN_NONE;
TextureClientAutoLock lock(aSrc, OpenMode::OPEN_READ | asyncFlags);
if (!lock.Succeeded()) {
return false;
}
RefPtr<gfx::DrawTarget> srcTarget = aSrc->BorrowDrawTarget();
RefPtr<gfx::DrawTarget> destTarget = aDest->BorrowDrawTarget();
if (!srcTarget || !destTarget) {
return false;
}
for (auto iter = aRegion.RectIter(); !iter.Done(); iter.Next()) {
const gfx::IntRect src = iter.Get() - aSrcOrigin;
const gfx::IntPoint dest = iter.Get().TopLeft() - aDestOrigin;
auto copy = CapturedTiledPaintState::Copy{
srcTarget, destTarget, src, dest
};
if (asyncPaint) {
aCopies->push_back(copy);
} else {
copy.CopyBuffer();
}
}
return true;
};
return CopyBuffer(aBuffer, aBufferOrigin, mBackBuffer, aTileOrigin) &&
(!aBufferOnWhite ||
!mBackBufferOnWhite ||
CopyBuffer(aBufferOnWhite, aBufferOrigin, mBackBufferOnWhite, aTileOrigin));
}
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();
}
TextureClient*
TileClient::GetBackBuffer(CompositableClient& aCompositable,
const nsIntRegion& aDirtyRegion,
const nsIntRegion& aVisibleRegion,
gfxContentType aContent,
SurfaceMode aMode,
nsIntRegion& aAddPaintedRegion,
TilePaintFlags aFlags,
RefPtr<TextureClient>* aBackBufferOnWhite,
std::vector<CapturedTiledPaintState::Copy>* aCopies,
std::vector<RefPtr<TextureClient>>* aClients)
{
if (!mAllocator) {
gfxCriticalError() << "[TileClient] Missing TextureClientAllocator.";
return nullptr;
}
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 (!gfxPrefs::LayersTileRetainBackBuffer()) {
DiscardBackBuffer();
}
Flip();
} else {
if (!mBackBuffer || mBackBuffer->IsReadLocked()) {
mBackBuffer.Set(this,
CreateBackBufferTexture(mBackBuffer, aCompositable, mAllocator)
);
if (!mBackBuffer) {
DiscardBackBuffer();
DiscardFrontBuffer();
return nullptr;
}
mInvalidBack = IntRect(IntPoint(), mBackBuffer->GetSize());
}
if (aMode == SurfaceMode::SURFACE_COMPONENT_ALPHA
&& (!mBackBufferOnWhite || mBackBufferOnWhite->IsReadLocked())) {
mBackBufferOnWhite = CreateBackBufferTexture(
mBackBufferOnWhite, aCompositable, mAllocator
);
if (!mBackBufferOnWhite) {
DiscardBackBuffer();
DiscardFrontBuffer();
return nullptr;
}
mInvalidBack = IntRect(IntPoint(), mBackBufferOnWhite->GetSize());
}
ValidateBackBufferFromFront(aDirtyRegion, aVisibleRegion, aAddPaintedRegion, aFlags, aCopies, aClients);
}
OpenMode lockMode = aFlags & TilePaintFlags::Async ? OpenMode::OPEN_READ_WRITE_ASYNC
: OpenMode::OPEN_READ_WRITE;
if (!mBackBuffer->IsLocked()) {
if (!mBackBuffer->Lock(lockMode)) {
gfxCriticalError() << "[Tiling:Client] Failed to lock a tile (B)";
DiscardBackBuffer();
DiscardFrontBuffer();
return nullptr;
}
}
if (mBackBufferOnWhite && !mBackBufferOnWhite->IsLocked()) {
if (!mBackBufferOnWhite->Lock(lockMode)) {
gfxCriticalError() << "[Tiling:Client] Failed to lock a tile (W)";
DiscardBackBuffer();
DiscardFrontBuffer();
return nullptr;
}
}
*aBackBufferOnWhite = mBackBufferOnWhite;
return mBackBuffer;
}
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(),
mFrontBufferOnWhite ? MaybeTexture(mFrontBufferOnWhite->GetIPDLActor()) : MaybeTexture(null_t()),
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