bool WebRenderPaintedLayer::UpdateImageClient() { MOZ_ASSERT(WrManager()->GetPaintedLayerCallback()); LayerIntRegion visibleRegion = GetVisibleRegion(); LayerIntRect bounds = visibleRegion.GetBounds(); LayerIntSize size = bounds.Size(); IntSize imageSize(size.width, size.height); UpdateImageHelper helper(mImageContainer, mImageClient, imageSize); { RefPtr<DrawTarget> target = helper.GetDrawTarget(); if (!target) { return false; } target->ClearRect(Rect(0, 0, imageSize.width, imageSize.height)); target->SetTransform(Matrix().PreTranslate(-bounds.x, -bounds.y)); RefPtr<gfxContext> ctx = gfxContext::CreatePreservingTransformOrNull(target); MOZ_ASSERT(ctx); // already checked the target above WrManager()->GetPaintedLayerCallback()(this, ctx, visibleRegion.ToUnknownRegion(), visibleRegion.ToUnknownRegion(), DrawRegionClip::DRAW, nsIntRegion(), WrManager()->GetPaintedLayerCallbackData()); if (gfxPrefs::WebRenderHighlightPaintedLayers()) { target->SetTransform(Matrix()); target->FillRect(Rect(0, 0, imageSize.width, imageSize.height), ColorPattern(Color(1.0, 0.0, 0.0, 0.5))); } } if (!helper.UpdateImage()) { return false; } return true; }
void RenderViewMLGPU::PrepareClears() { // We don't do any clearing if we're copying from a source backdrop. if (mContainer && mContainer->NeedsSurfaceCopy()) { return; } // Get the list of rects to clear. If using the depth buffer, we don't // care if it's accurate since the GPU will do occlusion testing for us. // If not using the depth buffer, we subtract out the occluded region. LayerIntRegion region = LayerIntRect::FromUnknownRect(mInvalidBounds); if (!mUseDepthBuffer) { // Don't let the clear region become too complicated. region.SubOut(mOccludedRegion); region.SimplifyOutward(kMaxClearViewRects); } Maybe<int32_t> sortIndex; if (mUseDepthBuffer) { // Note that we use the lowest available sorting index, to ensure that when // using the z-buffer, we don't draw over already-drawn content. sortIndex = Some(mNextSortIndex++); } nsTArray<IntRect> rects = ToRectArray(region); mDevice->PrepareClearRegion(&mPreClear, std::move(rects), sortIndex); if (!mPostClearRegion.IsEmpty()) { // Prepare the final clear as well. Note that we always do this clear at the // very end, even when the depth buffer is enabled, so we don't bother // setting a useful sorting index. If and when we try to ship the depth // buffer, we would execute this clear earlier in the pipeline and give it // the closest possible z-ordering to the screen. nsTArray<IntRect> rects = ToRectArray(mPostClearRegion); mDevice->PrepareClearRegion(&mPostClear, std::move(rects), Nothing()); } }
void LayerManagerComposite::PostProcessLayers(Layer* aLayer, nsIntRegion& aOpaqueRegion, LayerIntRegion& aVisibleRegion, const Maybe<ParentLayerIntRect>& aClipFromAncestors) { if (aLayer->Extend3DContext()) { // For layers participating 3D rendering context, their visible // region should be empty (invisible), so we pass through them // without doing anything. // Direct children of the establisher may have a clip, becaue the // item containing it; ex. of nsHTMLScrollFrame, may give it one. Maybe<ParentLayerIntRect> layerClip = aLayer->AsHostLayer()->GetShadowClipRect(); Maybe<ParentLayerIntRect> ancestorClipForChildren = IntersectMaybeRects(layerClip, aClipFromAncestors); MOZ_ASSERT(!layerClip || !aLayer->Combines3DTransformWithAncestors(), "Only direct children of the establisher could have a clip"); for (Layer* child = aLayer->GetLastChild(); child; child = child->GetPrevSibling()) { PostProcessLayers(child, aOpaqueRegion, aVisibleRegion, ancestorClipForChildren); } return; } nsIntRegion localOpaque; // Treat layers on the path to the root of the 3D rendering context as // a giant layer if it is a leaf. Matrix4x4 transform = GetAccTransformIn3DContext(aLayer); Matrix transform2d; Maybe<IntPoint> integerTranslation; // If aLayer has a simple transform (only an integer translation) then we // can easily convert aOpaqueRegion into pre-transform coordinates and include // that region. if (transform.Is2D(&transform2d)) { if (transform2d.IsIntegerTranslation()) { integerTranslation = Some(IntPoint::Truncate(transform2d.GetTranslation())); localOpaque = aOpaqueRegion; localOpaque.MoveBy(-*integerTranslation); } } // Compute a clip that's the combination of our layer clip with the clip // from our ancestors. LayerComposite* composite = static_cast<LayerComposite*>(aLayer->AsHostLayer()); Maybe<ParentLayerIntRect> layerClip = composite->GetShadowClipRect(); MOZ_ASSERT(!layerClip || !aLayer->Combines3DTransformWithAncestors(), "The layer with a clip should not participate " "a 3D rendering context"); Maybe<ParentLayerIntRect> outsideClip = IntersectMaybeRects(layerClip, aClipFromAncestors); // Convert the combined clip into our pre-transform coordinate space, so // that it can later be intersected with our visible region. // If our transform is a perspective, there's no meaningful insideClip rect // we can compute (it would need to be a cone). Maybe<LayerIntRect> insideClip; if (outsideClip && !transform.HasPerspectiveComponent()) { Matrix4x4 inverse = transform; if (inverse.Invert()) { Maybe<LayerRect> insideClipFloat = UntransformBy(ViewAs<ParentLayerToLayerMatrix4x4>(inverse), ParentLayerRect(*outsideClip), LayerRect::MaxIntRect()); if (insideClipFloat) { insideClipFloat->RoundOut(); LayerIntRect insideClipInt; if (insideClipFloat->ToIntRect(&insideClipInt)) { insideClip = Some(insideClipInt); } } } } Maybe<ParentLayerIntRect> ancestorClipForChildren; if (insideClip) { ancestorClipForChildren = Some(ViewAs<ParentLayerPixel>(*insideClip, PixelCastJustification::MovingDownToChildren)); } // Save the value of localOpaque, which currently stores the region obscured // by siblings (and uncles and such), before our descendants contribute to it. nsIntRegion obscured = localOpaque; // Recurse on our descendants, in front-to-back order. In this process: // - Occlusions are computed for them, and they contribute to localOpaque. // - They recalculate their visible regions, taking ancestorClipForChildren // into account, and accumulate them into descendantsVisibleRegion. LayerIntRegion descendantsVisibleRegion; bool hasPreserve3DChild = false; for (Layer* child = aLayer->GetLastChild(); child; child = child->GetPrevSibling()) { PostProcessLayers(child, localOpaque, descendantsVisibleRegion, ancestorClipForChildren); if (child->Extend3DContext()) { hasPreserve3DChild = true; } } // Recalculate our visible region. LayerIntRegion visible = composite->GetShadowVisibleRegion(); // If we have descendants, throw away the visible region stored on this // layer, and use the region accumulated by our descendants instead. if (aLayer->GetFirstChild() && !hasPreserve3DChild) { visible = descendantsVisibleRegion; } // Subtract any areas that we know to be opaque. if (!obscured.IsEmpty()) { visible.SubOut(LayerIntRegion::FromUnknownRegion(obscured)); } // Clip the visible region using the combined clip. if (insideClip) { visible.AndWith(*insideClip); } composite->SetShadowVisibleRegion(visible); // Transform the newly calculated visible region into our parent's space, // apply our clip to it (if any), and accumulate it into |aVisibleRegion| // for the caller to use. ParentLayerIntRegion visibleParentSpace = TransformBy( ViewAs<LayerToParentLayerMatrix4x4>(transform), visible); if (const Maybe<ParentLayerIntRect>& clipRect = composite->GetShadowClipRect()) { visibleParentSpace.AndWith(*clipRect); } aVisibleRegion.OrWith(ViewAs<LayerPixel>(visibleParentSpace, PixelCastJustification::MovingDownToChildren)); // If we have a simple transform, then we can add our opaque area into // aOpaqueRegion. if (integerTranslation && !aLayer->HasMaskLayers() && aLayer->IsOpaqueForVisibility()) { if (aLayer->IsOpaque()) { localOpaque.OrWith(composite->GetFullyRenderedRegion()); } localOpaque.MoveBy(*integerTranslation); if (layerClip) { localOpaque.AndWith(layerClip->ToUnknownRect()); } aOpaqueRegion.OrWith(localOpaque); } }
void WebRenderPaintedLayer::RenderLayer(wr::DisplayListBuilder& aBuilder) { // XXX We won't keep using ContentClient for WebRenderPaintedLayer in the future and // there is a crash problem for ContentClient on MacOS. So replace ContentClient with // ImageClient. See bug 1341001. //RenderLayerWithReadback(nullptr); if (!mImageContainer) { mImageContainer = LayerManager::CreateImageContainer(); } if (!mImageClient) { mImageClient = ImageClient::CreateImageClient(CompositableType::IMAGE, WrBridge(), TextureFlags::DEFAULT); if (!mImageClient) { return; } mImageClient->Connect(); } if (!mExternalImageId) { mExternalImageId = WrBridge()->AllocExternalImageIdForCompositable(mImageClient); MOZ_ASSERT(mExternalImageId); } LayerIntRegion visibleRegion = GetVisibleRegion(); LayerIntRect bounds = visibleRegion.GetBounds(); LayerIntSize size = bounds.Size(); if (size.IsEmpty()) { if (gfxPrefs::LayersDump()) { printf_stderr("PaintedLayer %p skipping\n", this->GetLayer()); } return; } IntSize imageSize(size.width, size.height); RefPtr<TextureClient> texture = mImageClient->GetTextureClientRecycler()->CreateOrRecycle(SurfaceFormat::B8G8R8A8, imageSize, BackendSelector::Content, TextureFlags::DEFAULT); if (!texture) { return; } { TextureClientAutoLock autoLock(texture, OpenMode::OPEN_WRITE_ONLY); if (!autoLock.Succeeded()) { return; } RefPtr<DrawTarget> target = texture->BorrowDrawTarget(); if (!target) { return; } target->ClearRect(Rect(0, 0, imageSize.width, imageSize.height)); target->SetTransform(Matrix().PreTranslate(-bounds.x, -bounds.y)); RefPtr<gfxContext> ctx = gfxContext::CreatePreservingTransformOrNull(target); MOZ_ASSERT(ctx); // already checked the target above Manager()->GetPaintedLayerCallback()(this, ctx, visibleRegion.ToUnknownRegion(), visibleRegion.ToUnknownRegion(), DrawRegionClip::DRAW, nsIntRegion(), Manager()->GetPaintedLayerCallbackData()); } RefPtr<TextureWrapperImage> image = new TextureWrapperImage(texture, IntRect(IntPoint(0, 0), imageSize)); mImageContainer->SetCurrentImageInTransaction(image); if (!mImageClient->UpdateImage(mImageContainer, /* unused */0)) { return; } gfx::Matrix4x4 transform = GetTransform(); gfx::Rect relBounds = GetWrRelBounds(); gfx::Rect overflow(0, 0, relBounds.width, relBounds.height); gfx::Rect rect(0, 0, size.width, size.height); gfx::Rect clipRect = GetWrClipRect(rect); Maybe<WrImageMask> mask = BuildWrMaskLayer(); WrClipRegion clip = aBuilder.BuildClipRegion(wr::ToWrRect(clipRect)); wr::MixBlendMode mixBlendMode = wr::ToWrMixBlendMode(GetMixBlendMode()); DumpLayerInfo("PaintedLayer", rect); WrImageKey key; key.mNamespace = WrBridge()->GetNamespace(); key.mHandle = WrBridge()->GetNextResourceId(); WrBridge()->AddWebRenderParentCommand(OpAddExternalImage(mExternalImageId, key)); aBuilder.PushStackingContext(wr::ToWrRect(relBounds), wr::ToWrRect(overflow), mask.ptrOr(nullptr), 1.0f, //GetAnimations(), transform, mixBlendMode); aBuilder.PushImage(wr::ToWrRect(rect), clip, wr::ImageRendering::Auto, key); aBuilder.PopStackingContext(); }
bool RenderViewMLGPU::UpdateVisibleRegion(ItemInfo& aItem) { // If the item has some kind of complex transform, we perform a very // simple occlusion test and move on. We using a depth buffer we skip // CPU-based occlusion culling as well, since the GPU will do most of our // culling work for us. if (mUseDepthBuffer || !aItem.translation || !gfxPrefs::AdvancedLayersEnableCPUOcclusion()) { // Update the render region even if we won't compute visibility, since some // layer types (like Canvas and Image) need to have the visible region // clamped. LayerIntRegion region = aItem.layer->GetShadowVisibleRegion(); aItem.layer->SetRenderRegion(std::move(region)); AL_LOG("RenderView %p simple occlusion test, bounds=%s, translation?=%d\n", this, Stringify(aItem.bounds).c_str(), aItem.translation ? 1 : 0); return mInvalidBounds.Intersects(aItem.bounds); } MOZ_ASSERT(aItem.rectilinear); AL_LOG("RenderView %p starting visibility tests:\n", this); AL_LOG(" occluded=%s\n", Stringify(mOccludedRegion).c_str()); // Compute the translation into render target space. LayerIntPoint translation = LayerIntPoint::FromUnknownPoint( aItem.translation.value() - mTargetOffset); AL_LOG(" translation=%s\n", Stringify(translation).c_str()); IntRect clip = aItem.layer->GetComputedClipRect().ToUnknownRect(); AL_LOG(" clip=%s\n", Stringify(translation).c_str()); LayerIntRegion region = aItem.layer->GetShadowVisibleRegion(); region.MoveBy(translation); AL_LOG(" effective-visible=%s\n", Stringify(region).c_str()); region.SubOut(mOccludedRegion); region.AndWith(LayerIntRect::FromUnknownRect(mInvalidBounds)); region.AndWith(LayerIntRect::FromUnknownRect(clip)); if (region.IsEmpty()) { return false; } // Move the visible region back into layer space. region.MoveBy(-translation); AL_LOG(" new-local-visible=%s\n", Stringify(region).c_str()); aItem.layer->SetRenderRegion(std::move(region)); // Apply the new occluded area. We do another dance with the translation to // avoid copying the region. We do this after the SetRegionToRender call to // accomodate the possiblity of a layer changing its visible region. if (aItem.opaque) { mOccludedRegion.MoveBy(-translation); mOccludedRegion.OrWith(aItem.layer->GetRenderRegion()); mOccludedRegion.MoveBy(translation); AL_LOG(" new-occluded=%s\n", Stringify(mOccludedRegion).c_str()); // If the occluded region gets too complicated, we reset it. if (mOccludedRegion.GetNumRects() >= 32) { mOccludedRegion.SetEmpty(); AL_LOG(" clear-occluded, too many rects\n"); } } return true; }
void LayerManagerComposite::PostProcessLayers(Layer* aLayer, nsIntRegion& aOpaqueRegion, LayerIntRegion& aVisibleRegion) { nsIntRegion localOpaque; Matrix transform2d; Maybe<nsIntPoint> integerTranslation; // If aLayer has a simple transform (only an integer translation) then we // can easily convert aOpaqueRegion into pre-transform coordinates and include // that region. if (aLayer->GetLocalTransform().Is2D(&transform2d)) { if (transform2d.IsIntegerTranslation()) { integerTranslation = Some(TruncatedToInt(transform2d.GetTranslation())); localOpaque = aOpaqueRegion; localOpaque.MoveBy(-*integerTranslation); } } // Save the value of localOpaque, which currently stores the region obscured // by siblings (and uncles and such), before our descendants contribute to it. nsIntRegion obscured = localOpaque; // Recurse on our descendants, in front-to-back order. In this process: // - Occlusions are computed for them, and they contribute to localOpaque. // - They recalculate their visible regions, and accumulate them into // descendantsVisibleRegion. LayerIntRegion descendantsVisibleRegion; for (Layer* child = aLayer->GetLastChild(); child; child = child->GetPrevSibling()) { PostProcessLayers(child, localOpaque, descendantsVisibleRegion); } // Recalculate our visible region. LayerComposite* composite = aLayer->AsLayerComposite(); LayerIntRegion visible = composite->GetShadowVisibleRegion(); // If we have descendants, throw away the visible region stored on this // layer, and use the region accumulated by our descendants instead. if (aLayer->GetFirstChild()) { visible = descendantsVisibleRegion; } // Subtract any areas that we know to be opaque. if (!obscured.IsEmpty()) { visible.SubOut(LayerIntRegion::FromUnknownRegion(obscured)); } composite->SetShadowVisibleRegion(visible); // Transform the newly calculated visible region into our parent's space, // apply our clip to it (if any), and accumulate it into |aVisibleRegion| // for the caller to use. ParentLayerIntRegion visibleParentSpace = TransformTo<ParentLayerPixel>( aLayer->GetLocalTransform(), visible); if (const Maybe<ParentLayerIntRect>& clipRect = composite->GetShadowClipRect()) { visibleParentSpace.AndWith(*clipRect); } aVisibleRegion.OrWith(ViewAs<LayerPixel>(visibleParentSpace, PixelCastJustification::MovingDownToChildren)); // If we have a simple transform, then we can add our opaque area into // aOpaqueRegion. if (integerTranslation && !aLayer->HasMaskLayers() && aLayer->IsOpaqueForVisibility()) { if (aLayer->GetContentFlags() & Layer::CONTENT_OPAQUE) { localOpaque.OrWith(composite->GetFullyRenderedRegion()); } localOpaque.MoveBy(*integerTranslation); const Maybe<ParentLayerIntRect>& clip = aLayer->GetEffectiveClipRect(); if (clip) { localOpaque.AndWith(clip->ToUnknownRect()); } aOpaqueRegion.OrWith(localOpaque); } }
void WebRenderPaintedLayerBlob::RenderLayer(wr::DisplayListBuilder& aBuilder, const StackingContextHelper& aSc) { LayerIntRegion visibleRegion = GetVisibleRegion(); LayerIntRect bounds = visibleRegion.GetBounds(); LayerIntSize size = bounds.Size(); if (visibleRegion.IsEmpty()) { if (gfxPrefs::LayersDump()) { printf_stderr("PaintedLayer %p skipping\n", this->GetLayer()); } return; } nsIntRegion regionToPaint; regionToPaint.Sub(mVisibleRegion.ToUnknownRegion(), mValidRegion); // We have something to paint but can't. This usually happens only in // empty transactions if (!regionToPaint.IsEmpty() && !WrManager()->GetPaintedLayerCallback()) { WrManager()->SetTransactionIncomplete(); return; } IntSize imageSize(size.ToUnknownSize()); if (!regionToPaint.IsEmpty() && WrManager()->GetPaintedLayerCallback()) { RefPtr<gfx::DrawEventRecorderMemory> recorder = MakeAndAddRef<gfx::DrawEventRecorderMemory>(); RefPtr<gfx::DrawTarget> dummyDt = gfx::Factory::CreateDrawTarget(gfx::BackendType::SKIA, imageSize, gfx::SurfaceFormat::B8G8R8X8); RefPtr<gfx::DrawTarget> dt = gfx::Factory::CreateRecordingDrawTarget(recorder, dummyDt); dt->ClearRect(Rect(0, 0, imageSize.width, imageSize.height)); dt->SetTransform(Matrix().PreTranslate(-bounds.x, -bounds.y)); RefPtr<gfxContext> ctx = gfxContext::CreatePreservingTransformOrNull(dt); MOZ_ASSERT(ctx); // already checked the target above WrManager()->GetPaintedLayerCallback()(this, ctx, visibleRegion.ToUnknownRegion(), visibleRegion.ToUnknownRegion(), DrawRegionClip::DRAW, nsIntRegion(), WrManager()->GetPaintedLayerCallbackData()); if (gfxPrefs::WebRenderHighlightPaintedLayers()) { dt->SetTransform(Matrix()); dt->FillRect(Rect(0, 0, imageSize.width, imageSize.height), ColorPattern(Color(1.0, 0.0, 0.0, 0.5))); } wr::ByteBuffer bytes; bytes.Allocate(recorder->RecordingSize()); DebugOnly<bool> ok = recorder->CopyRecording((char*)bytes.AsSlice().begin().get(), bytes.AsSlice().length()); MOZ_ASSERT(ok); //XXX: We should switch to updating the blob image instead of adding a new one // That will get rid of this discard bit if (mImageKey.isSome()) { WrManager()->AddImageKeyForDiscard(mImageKey.value()); } mImageKey = Some(GetImageKey()); WrBridge()->SendAddBlobImage(mImageKey.value(), imageSize, size.width * 4, dt->GetFormat(), bytes); } else { MOZ_ASSERT(GetInvalidRegion().IsEmpty()); } ScrollingLayersHelper scroller(this, aBuilder, aSc); StackingContextHelper sc(aSc, aBuilder, this); LayerRect rect = Bounds(); DumpLayerInfo("PaintedLayer", rect); LayerRect clipRect = ClipRect().valueOr(rect); Maybe<WrImageMask> mask = BuildWrMaskLayer(&sc); WrClipRegionToken clip = aBuilder.PushClipRegion( sc.ToRelativeWrRect(clipRect), mask.ptrOr(nullptr)); aBuilder.PushImage(sc.ToRelativeWrRect(rect), clip, wr::ImageRendering::Auto, mImageKey.value()); }