TileHost TiledLayerBufferComposite::ValidateTile(TileHost aTile, const nsIntPoint& aTileOrigin, const nsIntRegion& aDirtyRect) { if (aTile.IsPlaceholderTile()) { NS_WARNING("Placeholder tile encountered in painted region"); return aTile; } #ifdef GFX_TILEDLAYER_PREF_WARNINGS printf_stderr("Upload tile %i, %i\n", aTileOrigin.x, aTileOrigin.y); long start = PR_IntervalNow(); #endif MOZ_ASSERT(aTile.mTextureHost->GetFlags() & TextureFlags::IMMEDIATE_UPLOAD); #ifdef MOZ_GFX_OPTIMIZE_MOBILE MOZ_ASSERT(!aTile.mTextureHostOnWhite); // We possibly upload the entire texture contents here. This is a purposeful // decision, as sub-image upload can often be slow and/or unreliable, but // we may want to reevaluate this in the future. // For !HasInternalBuffer() textures, this is likely a no-op. aTile.mTextureHost->Updated(nullptr); #else nsIntRegion tileUpdated = aDirtyRect.MovedBy(-aTileOrigin); aTile.mTextureHost->Updated(&tileUpdated); if (aTile.mTextureHostOnWhite) { aTile.mTextureHostOnWhite->Updated(&tileUpdated); } #endif #ifdef GFX_TILEDLAYER_PREF_WARNINGS if (PR_IntervalNow() - start > 1) { printf_stderr("Tile Time to upload %i\n", PR_IntervalNow() - start); } #endif return aTile; }
void ClientSingleTiledLayerBuffer::PaintThebes( const nsIntRegion& aNewValidRegion, const nsIntRegion& aPaintRegion, const nsIntRegion& aDirtyRegion, LayerManager::DrawPaintedLayerCallback aCallback, void* aCallbackData, TilePaintFlags aFlags) { mWasLastPaintProgressive = !!(aFlags & TilePaintFlags::Progressive); bool asyncPaint = !!(aFlags & TilePaintFlags::Async); // Compare layer valid region size to current backbuffer size, discard if not // matching. gfx::IntSize size = aNewValidRegion.GetBounds().Size(); gfx::IntPoint origin = aNewValidRegion.GetBounds().TopLeft(); nsIntRegion paintRegion = aPaintRegion; RefPtr<TextureClient> discardedFrontBuffer = nullptr; RefPtr<TextureClient> discardedFrontBufferOnWhite = nullptr; nsIntRegion discardedValidRegion; if (mSize != size || mTilingOrigin != origin) { discardedFrontBuffer = mTile.mFrontBuffer; discardedFrontBufferOnWhite = mTile.mFrontBufferOnWhite; discardedValidRegion = mValidRegion; TILING_LOG( "TILING %p: Single-tile valid region changed. Discarding buffers.\n", &mPaintedLayer); ResetPaintedAndValidState(); mSize = size; mTilingOrigin = origin; paintRegion = aNewValidRegion; } SurfaceMode mode; gfxContentType content = GetContentType(&mode); mFormat = gfxPlatform::GetPlatform()->OptimalFormatForContent(content); if (mTile.IsPlaceholderTile()) { mTile.SetTextureAllocator(this); } if (mManager->AsShadowForwarder()->SupportsTextureDirectMapping()) { AutoTArray<uint64_t, 2> syncTextureSerials; mTile.GetSyncTextureSerials(mode, syncTextureSerials); if (syncTextureSerials.Length() > 0) { mManager->AsShadowForwarder()->SyncTextures(syncTextureSerials); } } // The dirty region relative to the top-left of the tile. nsIntRegion tileVisibleRegion = aNewValidRegion.MovedBy(-mTilingOrigin); nsIntRegion tileDirtyRegion = paintRegion.MovedBy(-mTilingOrigin); Maybe<AcquiredBackBuffer> backBuffer = mTile.AcquireBackBuffer(mCompositableClient, tileDirtyRegion, tileVisibleRegion, content, mode, aFlags); if (!backBuffer) { return; } // Mark the area we need to paint in the back buffer as invalid in the // front buffer as they will become out of sync. mTile.mInvalidFront.OrWith(tileDirtyRegion); // Add backbuffer's invalid region to the dirty region to be painted. // This will be empty if we were able to copy from the front in to the back. nsIntRegion tileInvalidRegion = mTile.mInvalidBack; tileInvalidRegion.AndWith(tileVisibleRegion); paintRegion.OrWith(tileInvalidRegion.MovedBy(mTilingOrigin)); tileDirtyRegion.OrWith(tileInvalidRegion); // Mark the region we will be painting and the region we copied from the front // buffer as needing to be uploaded to the compositor mTile.mUpdateRect = tileDirtyRegion.GetBounds().Union(backBuffer->mUpdatedRect); // If the old frontbuffer was discarded then attempt to copy what we // can from it to the new backbuffer. if (discardedFrontBuffer) { nsIntRegion copyableRegion; copyableRegion.And(aNewValidRegion, discardedValidRegion); copyableRegion.SubOut(aDirtyRegion); OpenMode readMode = asyncPaint ? OpenMode::OPEN_READ_ASYNC : OpenMode::OPEN_READ; DualTextureClientAutoLock discardedBuffer( discardedFrontBuffer, discardedFrontBufferOnWhite, readMode); if (discardedBuffer.Succeeded()) { RefPtr<gfx::SourceSurface> discardedSurface = discardedBuffer->Snapshot(); for (auto iter = copyableRegion.RectIter(); !iter.Done(); iter.Next()) { const gfx::IntRect src = iter.Get() - discardedValidRegion.GetBounds().TopLeft(); const gfx::IntPoint dest = iter.Get().TopLeft() - mTilingOrigin; backBuffer->mTarget->CopySurface(discardedSurface, src, dest); } TILING_LOG("TILING %p: Region copied from discarded frontbuffer %s\n", &mPaintedLayer, Stringify(copyableRegion).c_str()); // We don't need to repaint valid content that was just copied. paintRegion.SubOut(copyableRegion); copyableRegion.MoveBy(-mTilingOrigin); tileDirtyRegion.SubOut(copyableRegion); } else { gfxWarning() << "[Tiling:Client] Failed to aquire the discarded front " "buffer's draw target"; } } 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); } } // Paint into the target { RefPtr<gfxContext> ctx = gfxContext::CreateOrNull(backBuffer->mTarget); if (!ctx) { gfxDevCrash(gfx::LogReason::InvalidContext) << "SingleTiledContextClient context problem " << gfx::hexa(backBuffer->mTarget); return; } ctx->SetMatrix( ctx->CurrentMatrix().PreTranslate(-mTilingOrigin.x, -mTilingOrigin.y)); aCallback(&mPaintedLayer, ctx, paintRegion, paintRegion, DrawRegionClip::DRAW, nsIntRegion(), aCallbackData); } if (asyncPaint) { if (!backBuffer->mCapture->IsEmpty()) { UniquePtr<PaintTask> task(new PaintTask()); task->mCapture = backBuffer->mCapture; task->mTarget = backBuffer->mBackBuffer; task->mClients = std::move(backBuffer->mTextureClients); if (discardedFrontBuffer) { task->mClients.AppendElement(discardedFrontBuffer); } if (discardedFrontBufferOnWhite) { task->mClients.AppendElement(discardedFrontBufferOnWhite); } // The target is an alias for the capture, and the paint thread expects // to be the only one with a reference to the capture backBuffer->mTarget = nullptr; backBuffer->mCapture = nullptr; PaintThread::Get()->QueuePaintTask(std::move(task)); mManager->SetQueuedAsyncPaints(); } } else { MOZ_ASSERT(backBuffer->mTarget == backBuffer->mBackBuffer); MOZ_ASSERT(!backBuffer->mCapture); } // The new buffer is now validated, remove the dirty region from it. mTile.mInvalidBack.SubOut(tileDirtyRegion); backBuffer = Nothing(); mTile.Flip(); UnlockTile(mTile); mValidRegion = aNewValidRegion; mLastPaintSurfaceMode = mode; mLastPaintContentType = content; }
TileClient ClientTiledLayerBuffer::ValidateTile(TileClient aTile, const nsIntPoint& aTileOrigin, const nsIntRegion& aDirtyRegion) { PROFILER_LABEL("ClientTiledLayerBuffer", "ValidateTile"); #ifdef GFX_TILEDLAYER_PREF_WARNINGS if (aDirtyRegion.IsComplex()) { printf_stderr("Complex region\n"); } #endif if (aTile.IsPlaceholderTile()) { aTile.SetLayerManager(mManager); } // Discard our front and backbuffers if our contents changed. In this case // the calling code will already have taken care of invalidating the entire // layer. if (HasFormatChanged()) { aTile.DiscardBackBuffer(); aTile.DiscardFrontBuffer(); } bool createdTextureClient = false; nsIntRegion offsetDirtyRegion = aDirtyRegion.MovedBy(-aTileOrigin); bool usingSinglePaintBuffer = !!mSinglePaintDrawTarget; RefPtr<TextureClient> backBuffer = aTile.GetBackBuffer(offsetDirtyRegion, mManager->GetTexturePool(gfxPlatform::GetPlatform()->Optimal2DFormatForContent(GetContentType())), &createdTextureClient, !usingSinglePaintBuffer); if (!backBuffer->Lock(OPEN_READ_WRITE)) { NS_WARNING("Failed to lock tile TextureClient for updating."); aTile.DiscardFrontBuffer(); return aTile; } // We must not keep a reference to the DrawTarget after it has been unlocked, // make sure these are null'd before unlocking as destruction of the context // may cause the target to be flushed. RefPtr<DrawTarget> drawTarget = backBuffer->AsTextureClientDrawTarget()->GetAsDrawTarget(); drawTarget->SetTransform(Matrix()); RefPtr<gfxContext> ctxt = new gfxContext(drawTarget); if (usingSinglePaintBuffer) { // XXX Perhaps we should just copy the bounding rectangle here? RefPtr<gfx::SourceSurface> source = mSinglePaintDrawTarget->Snapshot(); nsIntRegionRectIterator it(aDirtyRegion); for (const nsIntRect* dirtyRect = it.Next(); dirtyRect != nullptr; dirtyRect = it.Next()) { #ifdef GFX_TILEDLAYER_PREF_WARNINGS printf_stderr(" break into subdirtyRect %i, %i, %i, %i\n", dirtyRect->x, dirtyRect->y, dirtyRect->width, dirtyRect->height); #endif gfx::Rect drawRect(dirtyRect->x - aTileOrigin.x, dirtyRect->y - aTileOrigin.y, dirtyRect->width, dirtyRect->height); drawRect.Scale(mResolution); gfx::IntRect copyRect(NS_roundf((dirtyRect->x - mSinglePaintBufferOffset.x) * mResolution), NS_roundf((dirtyRect->y - mSinglePaintBufferOffset.y) * mResolution), drawRect.width, drawRect.height); gfx::IntPoint copyTarget(NS_roundf(drawRect.x), NS_roundf(drawRect.y)); drawTarget->CopySurface(source, copyRect, copyTarget); // Mark the newly updated area as invalid in the front buffer aTile.mInvalidFront.Or(aTile.mInvalidFront, nsIntRect(copyTarget.x, copyTarget.y, copyRect.width, copyRect.height)); } // The new buffer is now validated, remove the dirty region from it. aTile.mInvalidBack.Sub(nsIntRect(0, 0, TILEDLAYERBUFFER_TILE_SIZE, TILEDLAYERBUFFER_TILE_SIZE), offsetDirtyRegion); } else { // Area of the full tile... nsIntRegion tileRegion = nsIntRect(aTileOrigin.x, aTileOrigin.y, TILEDLAYERBUFFER_TILE_SIZE, TILEDLAYERBUFFER_TILE_SIZE); // Intersect this area with the portion that's dirty. tileRegion = tileRegion.Intersect(aDirtyRegion); // Move invalid areas into layer space. aTile.mInvalidFront.MoveBy(aTileOrigin); aTile.mInvalidBack.MoveBy(aTileOrigin); // Add the area that's going to be redrawn to the invalid area of the // front region. aTile.mInvalidFront.Or(aTile.mInvalidFront, tileRegion); // Add invalid areas of the backbuffer to the area to redraw. tileRegion.Or(tileRegion, aTile.mInvalidBack); // Move invalid areas back into tile space. aTile.mInvalidFront.MoveBy(-aTileOrigin); // This will be validated now. aTile.mInvalidBack.SetEmpty(); nsIntRect bounds = tileRegion.GetBounds(); bounds.ScaleRoundOut(mResolution, mResolution); bounds.MoveBy(-aTileOrigin); if (GetContentType() != gfxContentType::COLOR) { drawTarget->ClearRect(Rect(bounds.x, bounds.y, bounds.width, bounds.height)); } ctxt->NewPath(); ctxt->Clip(gfxRect(bounds.x, bounds.y, bounds.width, bounds.height)); ctxt->Scale(mResolution, mResolution); ctxt->Translate(gfxPoint(-aTileOrigin.x, -aTileOrigin.y)); mCallback(mThebesLayer, ctxt, tileRegion.GetBounds(), DrawRegionClip::CLIP_NONE, nsIntRegion(), mCallbackData); } #ifdef GFX_TILEDLAYER_DEBUG_OVERLAY DrawDebugOverlay(drawTarget, aTileOrigin.x * mResolution, aTileOrigin.y * mResolution, GetTileLength(), GetTileLength()); #endif ctxt = nullptr; drawTarget = nullptr; backBuffer->Unlock(); aTile.Flip(); if (createdTextureClient) { if (!mCompositableClient->AddTextureClient(backBuffer)) { NS_WARNING("Failed to add tile TextureClient."); aTile.DiscardFrontBuffer(); aTile.DiscardBackBuffer(); return aTile; } } // Note, we don't call UpdatedTexture. The Updated function is called manually // by the TiledContentHost before composition. if (backBuffer->HasInternalBuffer()) { // If our new buffer has an internal buffer, we don't want to keep another // TextureClient around unnecessarily, so discard the back-buffer. aTile.DiscardBackBuffer(); } return aTile; }