void BasicPaintedLayer::Validate(LayerManager::DrawPaintedLayerCallback aCallback, void* aCallbackData, ReadbackProcessor* aReadback) { if (!mContentClient) { // This client will have a null Forwarder, which means it will not have // a ContentHost on the other side. mContentClient = new ContentClientBasic(mBackend); } if (!BasicManager()->IsRetained()) { return; } nsTArray<ReadbackProcessor::Update> readbackUpdates; if (aReadback && UsedForReadback()) { aReadback->GetPaintedLayerUpdates(this, &readbackUpdates); } uint32_t flags = 0; #ifndef MOZ_WIDGET_ANDROID if (BasicManager()->CompositorMightResample()) { flags |= RotatedContentBuffer::PAINT_WILL_RESAMPLE; } if (!(flags & RotatedContentBuffer::PAINT_WILL_RESAMPLE)) { if (MayResample()) { flags |= RotatedContentBuffer::PAINT_WILL_RESAMPLE; } } #endif if (mDrawAtomically) { flags |= RotatedContentBuffer::PAINT_NO_ROTATION; } PaintState state = mContentClient->BeginPaintBuffer(this, flags); SubtractFromValidRegion(state.mRegionToInvalidate); DrawTarget* target = mContentClient->BorrowDrawTargetForPainting(state); if (target && target->IsValid()) { // The area that became invalid and is visible needs to be repainted // (this could be the whole visible area if our buffer switched // from RGB to RGBA, because we might need to repaint with // subpixel AA) state.mRegionToInvalidate.And(state.mRegionToInvalidate, GetLocalVisibleRegion().ToUnknownRegion()); SetAntialiasingFlags(this, target); RenderTraceInvalidateStart(this, "FFFF00", state.mRegionToDraw.GetBounds()); RefPtr<gfxContext> ctx = gfxContext::CreatePreservingTransformOrNull(target); MOZ_ASSERT(ctx); // already checked the target above PaintBuffer(ctx, state.mRegionToDraw, state.mRegionToDraw, state.mRegionToInvalidate, state.mDidSelfCopy, state.mClip, aCallback, aCallbackData); MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) PaintThebes", this)); Mutated(); ctx = nullptr; mContentClient->ReturnDrawTargetToBuffer(target); target = nullptr; RenderTraceInvalidateEnd(this, "FFFF00"); } else { if (target) { mContentClient->ReturnDrawTargetToBuffer(target); target = nullptr; } // It's possible that state.mRegionToInvalidate is nonempty here, // if we are shrinking the valid region to nothing. So use mRegionToDraw // instead. NS_WARNING_ASSERTION( state.mRegionToDraw.IsEmpty(), "No context when we have something to draw, resource exhaustion?"); } for (uint32_t i = 0; i < readbackUpdates.Length(); ++i) { ReadbackProcessor::Update& update = readbackUpdates[i]; nsIntPoint offset = update.mLayer->GetBackgroundLayerOffset(); RefPtr<DrawTarget> dt = update.mLayer->GetSink()->BeginUpdate(update.mUpdateRect + offset, update.mSequenceCounter); if (dt) { NS_ASSERTION(GetEffectiveOpacity() == 1.0, "Should only read back opaque layers"); NS_ASSERTION(!GetMaskLayer(), "Should only read back layers without masks"); dt->SetTransform(dt->GetTransform().PreTranslate(offset.x, offset.y)); mContentClient->DrawTo(this, dt, 1.0, CompositionOp::OP_OVER, nullptr, nullptr); update.mLayer->GetSink()->EndUpdate(update.mUpdateRect + offset); } } }
/*** * If we can, let's paint this ClientPaintedLayer's contents off the main thread. * The essential idea is that we ask the ContentClient for a DrawTarget and record * the moz2d commands. On the Paint Thread, we replay those commands to the * destination draw target. There are a couple of lifetime issues here though: * * 1) TextureClient owns the underlying buffer and DrawTarget. Because of this * we have to keep the TextureClient and DrawTarget alive but trick the * TextureClient into thinking it's already returned the DrawTarget * since we iterate through different Rects to get DrawTargets*. If * the TextureClient goes away, the DrawTarget and thus buffer can too. * 2) When ContentClient::EndPaint happens, it flushes the DrawTarget. We have * to Reflush on the Paint Thread * 3) DrawTarget API is NOT thread safe. We get around this by recording * on the main thread and painting on the paint thread. Logically, * ClientLayerManager will force a flushed paint and block the main thread * if we have another transaction. Thus we have a gap between when the main * thread records, the paint thread paints, and we block the main thread * from trying to paint again. The underlying API however is NOT thread safe. * 4) We have both "sync" and "async" OMTP. Sync OMTP means we paint on the main thread * but block the main thread while the paint thread paints. Async OMTP doesn't block * the main thread. Sync OMTP is only meant to be used as a debugging tool. */ bool ClientPaintedLayer::PaintOffMainThread() { mContentClient->BeginAsyncPaint(); uint32_t flags = GetPaintFlags(); PaintState state = mContentClient->BeginPaintBuffer(this, flags); if (!UpdatePaintRegion(state)) { return false; } bool didUpdate = false; RotatedContentBuffer::DrawIterator iter; // Debug Protip: Change to BorrowDrawTargetForPainting if using sync OMTP. while (RefPtr<CapturedPaintState> captureState = mContentClient->BorrowDrawTargetForRecording(state, &iter)) { DrawTarget* target = captureState->mTarget; if (!target || !target->IsValid()) { if (target) { mContentClient->ReturnDrawTargetToBuffer(target); } continue; } RefPtr<DrawTargetCapture> captureDT = Factory::CreateCaptureDrawTarget(target->GetBackendType(), target->GetSize(), target->GetFormat()); captureDT->SetTransform(captureState->mTargetTransform); SetAntialiasingFlags(this, captureDT); RefPtr<gfxContext> ctx = gfxContext::CreatePreservingTransformOrNull(captureDT); MOZ_ASSERT(ctx); // already checked the target above ClientManager()->GetPaintedLayerCallback()(this, ctx, iter.mDrawRegion, iter.mDrawRegion, state.mClip, state.mRegionToInvalidate, ClientManager()->GetPaintedLayerCallbackData()); ctx = nullptr; captureState->mCapture = captureDT.forget(); PaintThread::Get()->PaintContents(captureState, RotatedContentBuffer::PrepareDrawTargetForPainting); mContentClient->ReturnDrawTargetToBuffer(target); didUpdate = true; } mContentClient->EndPaint(nullptr); if (didUpdate) { UpdateContentClient(state); } return true; }