gecko-dev/gfx/layers/client/MultiTiledContentClient.cpp

684 строки
26 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/MultiTiledContentClient.h"
#include "ClientTiledPaintedLayer.h"
#include "mozilla/StaticPrefs.h"
#include "mozilla/layers/LayerMetricsWrapper.h"
namespace mozilla {
using namespace gfx;
namespace layers {
MultiTiledContentClient::MultiTiledContentClient(
ClientTiledPaintedLayer& aPaintedLayer, ClientLayerManager* aManager)
: TiledContentClient(aManager, "Multi"),
mTiledBuffer(aPaintedLayer, *this, aManager, &mSharedFrameMetricsHelper),
mLowPrecisionTiledBuffer(aPaintedLayer, *this, aManager,
&mSharedFrameMetricsHelper) {
MOZ_COUNT_CTOR(MultiTiledContentClient);
mLowPrecisionTiledBuffer.SetResolution(StaticPrefs::LowPrecisionResolution());
mHasLowPrecision = StaticPrefs::UseLowPrecisionBuffer();
}
void MultiTiledContentClient::ClearCachedResources() {
CompositableClient::ClearCachedResources();
mTiledBuffer.DiscardBuffers();
mLowPrecisionTiledBuffer.DiscardBuffers();
}
void MultiTiledContentClient::UpdatedBuffer(TiledBufferType aType) {
ClientMultiTiledLayerBuffer* buffer = aType == LOW_PRECISION_TILED_BUFFER
? &mLowPrecisionTiledBuffer
: &mTiledBuffer;
MOZ_ASSERT(aType != LOW_PRECISION_TILED_BUFFER || mHasLowPrecision);
mForwarder->UseTiledLayerBuffer(this, buffer->GetSurfaceDescriptorTiles());
}
ClientMultiTiledLayerBuffer::ClientMultiTiledLayerBuffer(
ClientTiledPaintedLayer& aPaintedLayer,
CompositableClient& aCompositableClient, ClientLayerManager* aManager,
SharedFrameMetricsHelper* aHelper)
: ClientTiledLayerBuffer(aPaintedLayer, aCompositableClient),
mManager(aManager),
mCallback(nullptr),
mCallbackData(nullptr),
mSharedFrameMetricsHelper(aHelper) {}
void ClientMultiTiledLayerBuffer::DiscardBuffers() {
for (TileClient& tile : mRetainedTiles) {
tile.DiscardBuffers();
}
}
SurfaceDescriptorTiles
ClientMultiTiledLayerBuffer::GetSurfaceDescriptorTiles() {
InfallibleTArray<TileDescriptor> tiles;
for (TileClient& tile : mRetainedTiles) {
TileDescriptor tileDesc = tile.GetTileDescriptor();
tiles.AppendElement(tileDesc);
// Reset the update rect
tile.mUpdateRect = IntRect();
}
return SurfaceDescriptorTiles(
mValidRegion, tiles, mTileOrigin, mTileSize, mTiles.mFirst.x,
mTiles.mFirst.y, mTiles.mSize.width, mTiles.mSize.height, mResolution,
mFrameResolution.xScale, mFrameResolution.yScale,
mWasLastPaintProgressive);
}
void ClientMultiTiledLayerBuffer::PaintThebes(
const nsIntRegion& aNewValidRegion, const nsIntRegion& aPaintRegion,
const nsIntRegion& aDirtyRegion,
LayerManager::DrawPaintedLayerCallback aCallback, void* aCallbackData,
TilePaintFlags aFlags) {
TILING_LOG("TILING %p: PaintThebes painting region %s\n", &mPaintedLayer,
Stringify(aPaintRegion).c_str());
TILING_LOG("TILING %p: PaintThebes new valid region %s\n", &mPaintedLayer,
Stringify(aNewValidRegion).c_str());
mCallback = aCallback;
mCallbackData = aCallbackData;
mWasLastPaintProgressive = !!(aFlags & TilePaintFlags::Progressive);
#ifdef GFX_TILEDLAYER_PREF_WARNINGS
long start = PR_IntervalNow();
#endif
#ifdef GFX_TILEDLAYER_PREF_WARNINGS
if (PR_IntervalNow() - start > 30) {
const IntRect bounds = aPaintRegion.GetBounds();
printf_stderr("Time to draw %i: %i, %i, %i, %i\n", PR_IntervalNow() - start,
bounds.x, bounds.y, bounds.width, bounds.height);
if (aPaintRegion.IsComplex()) {
printf_stderr("Complex region\n");
for (auto iter = aPaintRegion.RectIter(); !iter.Done(); iter.Next()) {
const IntRect& rect = iter.Get();
printf_stderr(" rect %i, %i, %i, %i\n", rect.x, rect.y, rect.width,
rect.height);
}
}
}
start = PR_IntervalNow();
#endif
AUTO_PROFILER_LABEL("ClientMultiTiledLayerBuffer::PaintThebes", GRAPHICS);
mNewValidRegion = aNewValidRegion;
Update(aNewValidRegion, aPaintRegion, aDirtyRegion, aFlags);
#ifdef GFX_TILEDLAYER_PREF_WARNINGS
if (PR_IntervalNow() - start > 10) {
const IntRect bounds = aPaintRegion.GetBounds();
printf_stderr("Time to tile %i: %i, %i, %i, %i\n", PR_IntervalNow() - start,
bounds.x, bounds.y, bounds.width, bounds.height);
}
#endif
mLastPaintContentType = GetContentType(&mLastPaintSurfaceMode);
mCallback = nullptr;
mCallbackData = nullptr;
}
void ClientMultiTiledLayerBuffer::MaybeSyncTextures(
const nsIntRegion& aPaintRegion, const TilesPlacement& aNewTiles,
const IntSize& aScaledTileSize) {
if (mManager->AsShadowForwarder()->SupportsTextureDirectMapping()) {
AutoTArray<uint64_t, 10> syncTextureSerials;
SurfaceMode mode;
Unused << GetContentType(&mode);
// Pre-pass through the tiles (mirroring the filter logic below) to gather
// texture IDs that we need to ensure are unused by the GPU before we
// continue.
if (!aPaintRegion.IsEmpty()) {
MOZ_ASSERT(mPaintTasks.IsEmpty());
for (size_t i = 0; i < mRetainedTiles.Length(); ++i) {
const TileCoordIntPoint tileCoord = aNewTiles.TileCoord(i);
IntPoint tileOffset = GetTileOffset(tileCoord);
nsIntRegion tileDrawRegion = IntRect(tileOffset, aScaledTileSize);
tileDrawRegion.AndWith(aPaintRegion);
if (tileDrawRegion.IsEmpty()) {
continue;
}
TileClient& tile = mRetainedTiles[i];
tile.GetSyncTextureSerials(mode, syncTextureSerials);
}
}
if (syncTextureSerials.Length() > 0) {
mManager->AsShadowForwarder()->SyncTextures(syncTextureSerials);
}
}
}
void ClientMultiTiledLayerBuffer::Update(const nsIntRegion& newValidRegion,
const nsIntRegion& aPaintRegion,
const nsIntRegion& aDirtyRegion,
TilePaintFlags aFlags) {
const IntSize scaledTileSize = GetScaledTileSize();
const gfx::IntRect newBounds = newValidRegion.GetBounds();
const TilesPlacement oldTiles = mTiles;
const TilesPlacement newTiles(
floor_div(newBounds.X(), scaledTileSize.width),
floor_div(newBounds.Y(), scaledTileSize.height),
floor_div(
GetTileStart(newBounds.X(), scaledTileSize.width) + newBounds.Width(),
scaledTileSize.width) +
1,
floor_div(GetTileStart(newBounds.Y(), scaledTileSize.height) +
newBounds.Height(),
scaledTileSize.height) +
1);
const size_t oldTileCount = mRetainedTiles.Length();
const size_t newTileCount = newTiles.mSize.width * newTiles.mSize.height;
nsTArray<TileClient> oldRetainedTiles;
mRetainedTiles.SwapElements(oldRetainedTiles);
mRetainedTiles.SetLength(newTileCount);
for (size_t oldIndex = 0; oldIndex < oldTileCount; oldIndex++) {
const TileCoordIntPoint tileCoord = oldTiles.TileCoord(oldIndex);
const size_t newIndex = newTiles.TileIndex(tileCoord);
// First, get the already existing tiles to the right place in the new
// array. Leave placeholders (default constructor) where there was no tile.
if (newTiles.HasTile(tileCoord)) {
mRetainedTiles[newIndex] = oldRetainedTiles[oldIndex];
} else {
// release tiles that we are not going to reuse before allocating new ones
// to avoid allocating unnecessarily.
oldRetainedTiles[oldIndex].DiscardBuffers();
}
}
oldRetainedTiles.Clear();
nsIntRegion paintRegion = aPaintRegion;
nsIntRegion dirtyRegion = aDirtyRegion;
MaybeSyncTextures(paintRegion, newTiles, scaledTileSize);
if (!paintRegion.IsEmpty()) {
MOZ_ASSERT(mPaintTasks.IsEmpty());
for (size_t i = 0; i < newTileCount; ++i) {
const TileCoordIntPoint tileCoord = newTiles.TileCoord(i);
IntPoint tileOffset = GetTileOffset(tileCoord);
nsIntRegion tileDrawRegion = IntRect(tileOffset, scaledTileSize);
tileDrawRegion.AndWith(paintRegion);
if (tileDrawRegion.IsEmpty()) {
continue;
}
TileClient& tile = mRetainedTiles[i];
if (!ValidateTile(tile, GetTileOffset(tileCoord), tileDrawRegion,
aFlags)) {
gfxCriticalError() << "ValidateTile failed";
}
// Validating the tile may have required more to be painted.
paintRegion.OrWith(tileDrawRegion);
dirtyRegion.OrWith(tileDrawRegion);
}
if (!mPaintTiles.IsEmpty()) {
// Create a tiled draw target
gfx::TileSet tileset;
for (size_t i = 0; i < mPaintTiles.Length(); ++i) {
mPaintTiles[i].mTileOrigin -= mTilingOrigin;
}
tileset.mTiles = mPaintTiles.Elements();
tileset.mTileCount = mPaintTiles.Length();
RefPtr<DrawTarget> drawTarget =
gfx::Factory::CreateTiledDrawTarget(tileset);
if (!drawTarget || !drawTarget->IsValid()) {
gfxDevCrash(LogReason::InvalidContext) << "Invalid tiled draw target";
return;
}
drawTarget->SetTransform(Matrix());
// Draw into the tiled draw target
RefPtr<gfxContext> ctx = gfxContext::CreateOrNull(drawTarget);
MOZ_ASSERT(ctx); // already checked the draw target above
ctx->SetMatrix(ctx->CurrentMatrix()
.PreScale(mResolution, mResolution)
.PreTranslate(-mTilingOrigin));
mCallback(&mPaintedLayer, ctx, paintRegion, dirtyRegion,
DrawRegionClip::DRAW, nsIntRegion(), mCallbackData);
ctx = nullptr;
// Edge padding allows us to avoid resampling artifacts
if (StaticPrefs::TileEdgePaddingEnabled() && mResolution == 1) {
drawTarget->PadEdges(newValidRegion.MovedBy(-mTilingOrigin));
}
// Reset
mPaintTiles.Clear();
mTilingOrigin = IntPoint(std::numeric_limits<int32_t>::max(),
std::numeric_limits<int32_t>::max());
}
// Dispatch to the paint thread
if (aFlags & TilePaintFlags::Async) {
bool queuedTask = false;
while (!mPaintTasks.IsEmpty()) {
UniquePtr<PaintTask> task = mPaintTasks.PopLastElement();
if (!task->mCapture->IsEmpty()) {
PaintThread::Get()->QueuePaintTask(std::move(task));
queuedTask = true;
}
}
if (queuedTask) {
mManager->SetQueuedAsyncPaints();
}
mPaintTasks.Clear();
}
for (uint32_t i = 0; i < mRetainedTiles.Length(); ++i) {
TileClient& tile = mRetainedTiles[i];
UnlockTile(tile);
}
}
mTiles = newTiles;
mValidRegion = newValidRegion;
}
bool ClientMultiTiledLayerBuffer::ValidateTile(TileClient& aTile,
const nsIntPoint& aTileOrigin,
nsIntRegion& aDirtyRegion,
TilePaintFlags aFlags) {
#ifdef GFX_TILEDLAYER_PREF_WARNINGS
if (aDirtyRegion.IsComplex()) {
printf_stderr("Complex region\n");
}
#endif
SurfaceMode mode;
gfxContentType content = GetContentType(&mode);
if (!aTile.mAllocator) {
aTile.SetTextureAllocator(
mManager->GetCompositorBridgeChild()->GetTexturePool(
mManager->AsShadowForwarder(),
gfxPlatform::GetPlatform()->Optimal2DFormatForContent(content),
TextureFlags::DISALLOW_BIGIMAGE | TextureFlags::IMMEDIATE_UPLOAD |
TextureFlags::NON_BLOCKING_READ_LOCK));
MOZ_ASSERT(aTile.mAllocator);
}
nsIntRegion tileDirtyRegion = aDirtyRegion.MovedBy(-aTileOrigin);
tileDirtyRegion.ScaleRoundOut(mResolution, mResolution);
nsIntRegion tileVisibleRegion = mNewValidRegion.MovedBy(-aTileOrigin);
tileVisibleRegion.ScaleRoundOut(mResolution, mResolution);
std::vector<RefPtr<TextureClient>> asyncPaintClients;
Maybe<AcquiredBackBuffer> backBuffer =
aTile.AcquireBackBuffer(mCompositableClient, tileDirtyRegion,
tileVisibleRegion, content, mode, aFlags);
if (!backBuffer) {
return false;
}
// Mark the area we need to paint in the back buffer as invalid in the
// front buffer as they will become out of sync.
aTile.mInvalidFront.OrWith(tileDirtyRegion);
// Add the backbuffer's invalid region intersected with the visible region to
// the dirty region we will be painting. This will be empty if we are able to
// copy from the front into the back.
nsIntRegion tileInvalidRegion = aTile.mInvalidBack;
tileInvalidRegion.AndWith(tileVisibleRegion);
nsIntRegion invalidRegion = tileInvalidRegion;
invalidRegion.MoveBy(aTileOrigin);
invalidRegion.ScaleInverseRoundOut(mResolution, mResolution);
tileDirtyRegion.OrWith(tileInvalidRegion);
aDirtyRegion.OrWith(invalidRegion);
// Mark the region we will be painting and the region we copied from the front
// buffer as needing to be uploaded to the compositor
aTile.mUpdateRect =
tileDirtyRegion.GetBounds().Union(backBuffer->mUpdatedRect);
// We need to clear the dirty region of the tile before painting
// if we are painting non-opaque content
if (mode != SurfaceMode::SURFACE_OPAQUE) {
for (auto iter = tileDirtyRegion.RectIter(); !iter.Done(); iter.Next()) {
const gfx::Rect drawRect(iter.Get().X(), iter.Get().Y(),
iter.Get().Width(), iter.Get().Height());
backBuffer->mTarget->ClearRect(drawRect);
}
}
gfx::Tile paintTile;
paintTile.mTileOrigin = gfx::IntPoint(aTileOrigin.x, aTileOrigin.y);
paintTile.mDrawTarget = backBuffer->mTarget;
mPaintTiles.AppendElement(paintTile);
if (aFlags & TilePaintFlags::Async) {
UniquePtr<PaintTask> task(new PaintTask());
task->mCapture = backBuffer->mCapture;
task->mTarget = backBuffer->mBackBuffer;
task->mClients = std::move(backBuffer->mTextureClients);
mPaintTasks.AppendElement(std::move(task));
} else {
MOZ_RELEASE_ASSERT(backBuffer->mTarget == backBuffer->mBackBuffer);
MOZ_RELEASE_ASSERT(backBuffer->mCapture == nullptr);
}
mTilingOrigin.x = std::min(mTilingOrigin.x, paintTile.mTileOrigin.x);
mTilingOrigin.y = std::min(mTilingOrigin.y, paintTile.mTileOrigin.y);
// The new buffer is now validated, remove the dirty region from it.
aTile.mInvalidBack.SubOut(tileDirtyRegion);
aTile.Flip();
return true;
}
/**
* This function takes the transform stored in aTransformToCompBounds
* (which was generated in GetTransformToAncestorsParentLayer), and
* modifies it with the ViewTransform from the compositor side so that
* it reflects what the compositor is actually rendering. This operation
* basically adds in the layer's async transform.
* This function then returns the scroll ancestor's composition bounds,
* transformed into the painted layer's LayerPixel coordinates, accounting
* for the compositor state.
*/
static Maybe<LayerRect> GetCompositorSideCompositionBounds(
const LayerMetricsWrapper& aScrollAncestor,
const LayerToParentLayerMatrix4x4& aTransformToCompBounds,
const AsyncTransform& aAPZTransform, const LayerRect& aClip) {
LayerToParentLayerMatrix4x4 transform =
aTransformToCompBounds * AsyncTransformComponentMatrix(aAPZTransform);
return UntransformBy(transform.Inverse(),
aScrollAncestor.Metrics().GetCompositionBounds(), aClip);
}
bool ClientMultiTiledLayerBuffer::ComputeProgressiveUpdateRegion(
const nsIntRegion& aInvalidRegion, const nsIntRegion& aOldValidRegion,
nsIntRegion& aRegionToPaint, BasicTiledLayerPaintData* aPaintData,
bool aIsRepeated) {
aRegionToPaint = aInvalidRegion;
// If the composition bounds rect is empty, we can't make any sensible
// decision about how to update coherently. In this case, just update
// everything in one transaction.
if (aPaintData->mCompositionBounds.IsEmpty()) {
aPaintData->mPaintFinished = true;
return false;
}
// If this is a low precision buffer, we force progressive updates. The
// assumption is that the contents is less important, so visual coherency
// is lower priority than speed.
bool drawingLowPrecision = IsLowPrecision();
// Find out if we have any non-stale content to update.
nsIntRegion staleRegion;
staleRegion.And(aInvalidRegion, aOldValidRegion);
TILING_LOG("TILING %p: Progressive update stale region %s\n", &mPaintedLayer,
Stringify(staleRegion).c_str());
LayerMetricsWrapper scrollAncestor;
mPaintedLayer.GetAncestorLayers(&scrollAncestor, nullptr, nullptr);
// Find out the current view transform to determine which tiles to draw
// first, and see if we should just abort this paint. Aborting is usually
// caused by there being an incoming, more relevant paint.
AsyncTransform viewTransform;
MOZ_ASSERT(mSharedFrameMetricsHelper);
bool abortPaint = mSharedFrameMetricsHelper->UpdateFromCompositorFrameMetrics(
scrollAncestor, !staleRegion.Contains(aInvalidRegion),
drawingLowPrecision, viewTransform);
TILING_LOG(
"TILING %p: Progressive update view transform %s zoom %f abort %d\n",
&mPaintedLayer, ToString(viewTransform.mTranslation).c_str(),
viewTransform.mScale.scale, abortPaint);
if (abortPaint) {
// We ignore if front-end wants to abort if this is the first,
// non-low-precision paint, as in that situation, we're about to override
// front-end's page/viewport metrics.
if (!aPaintData->mFirstPaint || drawingLowPrecision) {
AUTO_PROFILER_LABEL(
"ClientMultiTiledLayerBuffer::ComputeProgressiveUpdateRegion",
GRAPHICS);
aRegionToPaint.SetEmpty();
return aIsRepeated;
}
}
Maybe<LayerRect> transformedCompositionBounds =
GetCompositorSideCompositionBounds(
scrollAncestor, aPaintData->mTransformToCompBounds, viewTransform,
LayerRect(mPaintedLayer.GetVisibleRegion().GetBounds()));
if (!transformedCompositionBounds) {
aPaintData->mPaintFinished = true;
return false;
}
TILING_LOG("TILING %p: Progressive update transformed compositor bounds %s\n",
&mPaintedLayer, Stringify(*transformedCompositionBounds).c_str());
// Compute a "coherent update rect" that we should paint all at once in a
// single transaction. This is to avoid rendering glitches on animated
// page content, and when layers change size/shape.
// On Fennec uploads are more expensive because we're not using gralloc, so
// we use a coherent update rect that is intersected with the screen at the
// time of issuing the draw command. This will paint faster but also
// potentially make the progressive paint more visible to the user while
// scrolling.
IntRect coherentUpdateRect(RoundedOut(
#ifdef MOZ_WIDGET_ANDROID
transformedCompositionBounds->Intersect(
aPaintData->mCompositionBounds)
#else
*transformedCompositionBounds
#endif
)
.ToUnknownRect());
TILING_LOG("TILING %p: Progressive update final coherency rect %s\n",
&mPaintedLayer, Stringify(coherentUpdateRect).c_str());
aRegionToPaint.And(aInvalidRegion, coherentUpdateRect);
aRegionToPaint.Or(aRegionToPaint, staleRegion);
bool drawingStale = !aRegionToPaint.IsEmpty();
if (!drawingStale) {
aRegionToPaint = aInvalidRegion;
}
// Prioritise tiles that are currently visible on the screen.
bool paintingVisible = false;
if (aRegionToPaint.Intersects(coherentUpdateRect)) {
aRegionToPaint.And(aRegionToPaint, coherentUpdateRect);
paintingVisible = true;
}
TILING_LOG("TILING %p: Progressive update final paint region %s\n",
&mPaintedLayer, Stringify(aRegionToPaint).c_str());
// Paint area that's visible and overlaps previously valid content to avoid
// visible glitches in animated elements, such as gifs.
bool paintInSingleTransaction =
paintingVisible && (drawingStale || aPaintData->mFirstPaint);
TILING_LOG(
"TILING %p: paintingVisible %d drawingStale %d firstPaint %d "
"singleTransaction %d\n",
&mPaintedLayer, paintingVisible, drawingStale, aPaintData->mFirstPaint,
paintInSingleTransaction);
// The following code decides what order to draw tiles in, based on the
// current scroll direction of the primary scrollable layer.
NS_ASSERTION(!aRegionToPaint.IsEmpty(), "Unexpectedly empty paint region!");
IntRect paintBounds = aRegionToPaint.GetBounds();
int startX, incX, startY, incY;
gfx::IntSize scaledTileSize = GetScaledTileSize();
if (aPaintData->mScrollOffset.x >= aPaintData->mLastScrollOffset.x) {
startX = RoundDownToTileEdge(paintBounds.X(), scaledTileSize.width);
incX = scaledTileSize.width;
} else {
startX = RoundDownToTileEdge(paintBounds.XMost() - 1, scaledTileSize.width);
incX = -scaledTileSize.width;
}
if (aPaintData->mScrollOffset.y >= aPaintData->mLastScrollOffset.y) {
startY = RoundDownToTileEdge(paintBounds.Y(), scaledTileSize.height);
incY = scaledTileSize.height;
} else {
startY =
RoundDownToTileEdge(paintBounds.YMost() - 1, scaledTileSize.height);
incY = -scaledTileSize.height;
}
// Find a tile to draw.
IntRect tileBounds(startX, startY, scaledTileSize.width,
scaledTileSize.height);
int32_t scrollDiffX =
aPaintData->mScrollOffset.x - aPaintData->mLastScrollOffset.x;
int32_t scrollDiffY =
aPaintData->mScrollOffset.y - aPaintData->mLastScrollOffset.y;
// This loop will always terminate, as there is at least one tile area
// along the first/last row/column intersecting with regionToPaint, or its
// bounds would have been smaller.
while (true) {
aRegionToPaint.And(aInvalidRegion, tileBounds);
if (!aRegionToPaint.IsEmpty()) {
if (mResolution != 1) {
// Paint the entire tile for low-res. This is aimed to fixing low-res
// resampling and to avoid doing costly region accurate painting for a
// small area.
aRegionToPaint = tileBounds;
}
break;
}
if (Abs(scrollDiffY) >= Abs(scrollDiffX)) {
tileBounds.MoveByX(incX);
} else {
tileBounds.MoveByY(incY);
}
}
if (!aRegionToPaint.Contains(aInvalidRegion)) {
// The region needed to paint is larger then our progressive chunk size
// therefore update what we want to paint and ask for a new paint
// transaction.
// If we need to draw more than one tile to maintain coherency, make
// sure it happens in the same transaction by requesting this work be
// repeated immediately.
// If this is unnecessary, the remaining work will be done tile-by-tile in
// subsequent transactions. The caller code is responsible for scheduling
// the subsequent transactions as long as we don't set the mPaintFinished
// flag to true.
return (!drawingLowPrecision && paintInSingleTransaction);
}
// We're not repeating painting and we've not requested a repeat transaction,
// so the paint is finished. If there's still a separate low precision
// paint to do, it will get marked as unfinished later.
aPaintData->mPaintFinished = true;
return false;
}
bool ClientMultiTiledLayerBuffer::ProgressiveUpdate(
const nsIntRegion& aValidRegion, const nsIntRegion& aInvalidRegion,
const nsIntRegion& aOldValidRegion, nsIntRegion& aOutDrawnRegion,
BasicTiledLayerPaintData* aPaintData,
LayerManager::DrawPaintedLayerCallback aCallback, void* aCallbackData) {
TILING_LOG("TILING %p: Progressive update valid region %s\n", &mPaintedLayer,
Stringify(aValidRegion).c_str());
TILING_LOG("TILING %p: Progressive update invalid region %s\n",
&mPaintedLayer, Stringify(aInvalidRegion).c_str());
TILING_LOG("TILING %p: Progressive update old valid region %s\n",
&mPaintedLayer, Stringify(aOldValidRegion).c_str());
bool repeat = false;
bool isBufferChanged = false;
nsIntRegion remainingInvalidRegion = aInvalidRegion;
nsIntRegion updatedValidRegion = aValidRegion;
do {
// Compute the region that should be updated. Repeat as many times as
// is required.
nsIntRegion regionToPaint;
repeat =
ComputeProgressiveUpdateRegion(remainingInvalidRegion, aOldValidRegion,
regionToPaint, aPaintData, repeat);
TILING_LOG(
"TILING %p: Progressive update computed paint region %s repeat %d\n",
&mPaintedLayer, Stringify(regionToPaint).c_str(), repeat);
// There's no further work to be done.
if (regionToPaint.IsEmpty()) {
break;
}
isBufferChanged = true;
// Keep track of what we're about to refresh.
aOutDrawnRegion.OrWith(regionToPaint);
updatedValidRegion.OrWith(regionToPaint);
// aValidRegion may have been altered by InvalidateRegion, but we still
// want to display stale content until it gets progressively updated.
// Create a region that includes stale content.
nsIntRegion validOrStale;
validOrStale.Or(updatedValidRegion, aOldValidRegion);
// Paint the computed region and subtract it from the invalid region.
PaintThebes(validOrStale, regionToPaint, remainingInvalidRegion, aCallback,
aCallbackData, TilePaintFlags::Progressive);
remainingInvalidRegion.SubOut(regionToPaint);
} while (repeat);
TILING_LOG(
"TILING %p: Progressive update final valid region %s buffer changed %d\n",
&mPaintedLayer, Stringify(updatedValidRegion).c_str(), isBufferChanged);
TILING_LOG("TILING %p: Progressive update final invalid region %s\n",
&mPaintedLayer, Stringify(remainingInvalidRegion).c_str());
// Return false if nothing has been drawn, or give what has been drawn
// to the shadow layer to upload.
return isBufferChanged;
}
} // namespace layers
} // namespace mozilla