SimpleTiledLayerTile SimpleTiledLayerBuffer::ValidateTile(SimpleTiledLayerTile aTile, const nsIntPoint& aTileOrigin, const nsIntRegion& aDirtyRegion) { PROFILER_LABEL("SimpleTiledLayerBuffer", "ValidateTile"); static gfx::IntSize kTileSize(TILEDLAYERBUFFER_TILE_SIZE, TILEDLAYERBUFFER_TILE_SIZE); gfx::SurfaceFormat tileFormat = gfxPlatform::GetPlatform()->Optimal2DFormatForContent(GetContentType()); // if this is true, we're using a separate buffer to do our drawing first bool doBufferedDrawing = true; bool fullPaint = false; RefPtr<TextureClient> textureClient = mManager->GetSimpleTileTexturePool(tileFormat)->GetTextureClientWithAutoRecycle(); if (!textureClient) { NS_WARNING("TextureClient allocation failed"); return SimpleTiledLayerTile(); } if (!textureClient->Lock(OPEN_WRITE)) { NS_WARNING("TextureClient lock failed"); return SimpleTiledLayerTile(); } TextureClientSurface *textureClientSurf = textureClient->AsTextureClientSurface(); if (!textureClientSurf) { doBufferedDrawing = false; } RefPtr<DrawTarget> drawTarget; nsRefPtr<gfxImageSurface> clientAsImageSurface; unsigned char *bufferData = nullptr; // these are set/updated differently based on doBufferedDrawing nsIntRect drawBounds; nsIntRegion drawRegion; nsIntRegion invalidateRegion; if (doBufferedDrawing) { // try to obtain the TextureClient as an ImageSurface, so that we can // access the pixels directly nsRefPtr<gfxASurface> asurf = textureClientSurf->GetAsSurface(); clientAsImageSurface = asurf ? asurf->GetAsImageSurface() : nullptr; if (clientAsImageSurface) { int32_t bufferStride = clientAsImageSurface->Stride(); if (!aTile.mCachedBuffer) { aTile.mCachedBuffer = SharedBuffer::Create(clientAsImageSurface->GetDataSize()); fullPaint = true; } bufferData = (unsigned char*) aTile.mCachedBuffer->Data(); drawTarget = gfxPlatform::GetPlatform()->CreateDrawTargetForData(bufferData, kTileSize, bufferStride, tileFormat); if (fullPaint) { drawBounds = nsIntRect(aTileOrigin.x, aTileOrigin.y, GetScaledTileLength(), GetScaledTileLength()); drawRegion = nsIntRegion(drawBounds); } else { drawBounds = aDirtyRegion.GetBounds(); drawRegion = nsIntRegion(drawBounds); if (GetContentType() == gfxContentType::COLOR_ALPHA) drawTarget->ClearRect(Rect(drawBounds.x - aTileOrigin.x, drawBounds.y - aTileOrigin.y, drawBounds.width, drawBounds.height)); } } else { // failed to obtain the client as an ImageSurface doBufferedDrawing = false; } } // this might get set above if we couldn't extract out a buffer if (!doBufferedDrawing) { drawTarget = textureClient->AsTextureClientDrawTarget()->GetAsDrawTarget(); fullPaint = true; drawBounds = nsIntRect(aTileOrigin.x, aTileOrigin.y, GetScaledTileLength(), GetScaledTileLength()); drawRegion = nsIntRegion(drawBounds); if (GetContentType() == gfxContentType::COLOR_ALPHA) drawTarget->ClearRect(Rect(0, 0, drawBounds.width, drawBounds.height)); } // do the drawing RefPtr<gfxContext> ctxt = new gfxContext(drawTarget); ctxt->Scale(mResolution, mResolution); ctxt->Translate(gfxPoint(-aTileOrigin.x, -aTileOrigin.y)); mCallback(mThebesLayer, ctxt, drawRegion, fullPaint ? DrawRegionClip::CLIP_NONE : DrawRegionClip::DRAW_SNAPPED, // XXX DRAW or DRAW_SNAPPED? invalidateRegion, mCallbackData); ctxt = nullptr; drawTarget = nullptr; if (doBufferedDrawing) { memcpy(clientAsImageSurface->Data(), bufferData, clientAsImageSurface->GetDataSize()); clientAsImageSurface = nullptr; bufferData = nullptr; } textureClient->Unlock(); if (!mCompositableClient->AddTextureClient(textureClient)) { NS_WARNING("Failed to add tile TextureClient [simple]"); return SimpleTiledLayerTile(); } // aTile.mCachedBuffer was set earlier aTile.mTileBuffer = textureClient; aTile.mManager = mManager; aTile.mLastUpdate = TimeStamp::Now(); return aTile; }
bool ClientTiledLayerBuffer::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); // 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. ParentLayerRect compositionBounds; CSSToParentLayerScale zoom; #if defined(MOZ_WIDGET_ANDROID) bool abortPaint = mManager->ProgressiveUpdateCallback(!staleRegion.Contains(aInvalidRegion), compositionBounds, zoom, !drawingLowPrecision); #else MOZ_ASSERT(mSharedFrameMetricsHelper); ContainerLayer* parent = mThebesLayer->AsLayer()->GetParent(); bool abortPaint = mSharedFrameMetricsHelper->UpdateFromCompositorFrameMetrics( parent, !staleRegion.Contains(aInvalidRegion), drawingLowPrecision, compositionBounds, zoom); #endif 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) { PROFILER_LABEL("ContentClient", "Abort painting"); aRegionToPaint.SetEmpty(); return aIsRepeated; } } // Transform the screen coordinates into transformed layout device coordinates. LayoutDeviceRect transformedCompositionBounds = TransformCompositionBounds(compositionBounds, zoom, aPaintData->mScrollOffset, aPaintData->mResolution, aPaintData->mTransformParentLayerToLayout); // Paint tiles that have stale content or that intersected with the screen // at the time of issuing the draw command in a single transaction first. // This is to avoid rendering glitches on animated page content, and when // layers change size/shape. LayoutDeviceRect coherentUpdateRect = transformedCompositionBounds.Intersect(aPaintData->mCompositionBounds); nsIntRect roundedCoherentUpdateRect = LayoutDeviceIntRect::ToUntyped(RoundedOut(coherentUpdateRect)); aRegionToPaint.And(aInvalidRegion, roundedCoherentUpdateRect); aRegionToPaint.Or(aRegionToPaint, staleRegion); bool drawingStale = !aRegionToPaint.IsEmpty(); if (!drawingStale) { aRegionToPaint = aInvalidRegion; } // Prioritise tiles that are currently visible on the screen. bool paintVisible = false; if (aRegionToPaint.Intersects(roundedCoherentUpdateRect)) { aRegionToPaint.And(aRegionToPaint, roundedCoherentUpdateRect); paintVisible = true; } // Paint area that's visible and overlaps previously valid content to avoid // visible glitches in animated elements, such as gifs. bool paintInSingleTransaction = paintVisible && (drawingStale || aPaintData->mFirstPaint); // 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!"); nsIntRect paintBounds = aRegionToPaint.GetBounds(); int startX, incX, startY, incY; int tileLength = GetScaledTileLength(); if (aPaintData->mScrollOffset.x >= aPaintData->mLastScrollOffset.x) { startX = RoundDownToTileEdge(paintBounds.x); incX = tileLength; } else { startX = RoundDownToTileEdge(paintBounds.XMost() - 1); incX = -tileLength; } if (aPaintData->mScrollOffset.y >= aPaintData->mLastScrollOffset.y) { startY = RoundDownToTileEdge(paintBounds.y); incY = tileLength; } else { startY = RoundDownToTileEdge(paintBounds.YMost() - 1); incY = -tileLength; } // Find a tile to draw. nsIntRect tileBounds(startX, startY, tileLength, tileLength); 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()) { break; } if (Abs(scrollDiffY) >= Abs(scrollDiffX)) { tileBounds.x += incX; } else { tileBounds.y += 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. if (!drawingLowPrecision && paintInSingleTransaction) { return true; } mManager->SetRepeatTransaction(); return false; } // 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; }