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; }
void ClientTiledLayerBuffer::PaintThebes(const nsIntRegion& aNewValidRegion, const nsIntRegion& aPaintRegion, LayerManager::DrawThebesLayerCallback aCallback, void* aCallbackData) { mCallback = aCallback; mCallbackData = aCallbackData; #ifdef GFX_TILEDLAYER_PREF_WARNINGS long start = PR_IntervalNow(); #endif // If this region is empty XMost() - 1 will give us a negative value. NS_ASSERTION(!aPaintRegion.GetBounds().IsEmpty(), "Empty paint region\n"); bool useSinglePaintBuffer = UseSinglePaintBuffer(); // XXX The single-tile case doesn't work at the moment, see bug 850396 /* if (useSinglePaintBuffer) { // Check if the paint only spans a single tile. If that's // the case there's no point in using a single paint buffer. nsIntRect paintBounds = aPaintRegion.GetBounds(); useSinglePaintBuffer = GetTileStart(paintBounds.x) != GetTileStart(paintBounds.XMost() - 1) || GetTileStart(paintBounds.y) != GetTileStart(paintBounds.YMost() - 1); } */ if (useSinglePaintBuffer) { nsRefPtr<gfxContext> ctxt; const nsIntRect bounds = aPaintRegion.GetBounds(); { PROFILER_LABEL("ClientTiledLayerBuffer", "PaintThebesSingleBufferAlloc"); gfxImageFormat format = gfxPlatform::GetPlatform()->OptimalFormatForContent( GetContentType()); mSinglePaintDrawTarget = gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget( gfx::IntSize(ceilf(bounds.width * mResolution), ceilf(bounds.height * mResolution)), gfx::ImageFormatToSurfaceFormat(format)); ctxt = new gfxContext(mSinglePaintDrawTarget); mSinglePaintBufferOffset = nsIntPoint(bounds.x, bounds.y); } ctxt->NewPath(); ctxt->Scale(mResolution, mResolution); ctxt->Translate(gfxPoint(-bounds.x, -bounds.y)); #ifdef GFX_TILEDLAYER_PREF_WARNINGS if (PR_IntervalNow() - start > 3) { printf_stderr("Slow alloc %i\n", PR_IntervalNow() - start); } start = PR_IntervalNow(); #endif PROFILER_LABEL("ClientTiledLayerBuffer", "PaintThebesSingleBufferDraw"); mCallback(mThebesLayer, ctxt, aPaintRegion, DrawRegionClip::CLIP_NONE, nsIntRegion(), mCallbackData); } #ifdef GFX_TILEDLAYER_PREF_WARNINGS if (PR_IntervalNow() - start > 30) { const nsIntRect 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"); nsIntRegionRectIterator it(aPaintRegion); for (const nsIntRect* rect = it.Next(); rect != nullptr; rect = it.Next()) { printf_stderr(" rect %i, %i, %i, %i\n", rect->x, rect->y, rect->width, rect->height); } } } start = PR_IntervalNow(); #endif PROFILER_LABEL("ClientTiledLayerBuffer", "PaintThebesUpdate"); Update(aNewValidRegion, aPaintRegion); #ifdef GFX_TILEDLAYER_PREF_WARNINGS if (PR_IntervalNow() - start > 10) { const nsIntRect 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 mLastPaintOpaque = mThebesLayer->CanUseOpaqueSurface(); mCallback = nullptr; mCallbackData = nullptr; mSinglePaintDrawTarget = nullptr; }