fune/gfx/layers/client/MultiTiledContentClient.cpp
Jamie Nicol c0b13f02f6 Bug 1484101 - Ensure DrawTargetTiled::PadEdges is called with region in device space. r=rhunt
In MultiTiledContentClient we can create a DrawTargetTiled with a
different origin than the layer we are painting. We must therefore
ensure when edge-padding that we provide the valid region in the draw
target's device-space rather than layer-space. Not doing so was
causing us to pad out in incorrect directions, causing visible seams.

Differential Revision: https://phabricator.services.mozilla.com/D3993

--HG--
extra : moz-landing-system : lando
2018-08-22 16:29:52 +00:00

679 lines
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/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(gfxPrefs::LowPrecisionResolution());
mHasLowPrecision = gfxPrefs::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.size() == 0);
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.size() == 0);
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.empty()) {
// Create a tiled draw target
gfx::TileSet tileset;
for (size_t i = 0; i < mPaintTiles.size(); ++i) {
mPaintTiles[i].mTileOrigin -= mTilingOrigin;
}
tileset.mTiles = &mPaintTiles[0];
tileset.mTileCount = mPaintTiles.size();
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 (gfxPrefs::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;
for (const auto& state : mPaintTasks) {
if (!state->mCapture->IsEmpty()) {
PaintThread::Get()->QueuePaintTask(state);
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)
{
AUTO_PROFILER_LABEL("ClientMultiTiledLayerBuffer::ValidateTile", GRAPHICS);
#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.push_back(paintTile);
if (aFlags & TilePaintFlags::Async) {
RefPtr<PaintTask> task = new PaintTask();
task->mCapture = backBuffer->mCapture;
task->mTarget = backBuffer->mBackBuffer;
task->mClients = std::move(backBuffer->mTextureClients);
mPaintTasks.push_back(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