/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "ContainerLayerComposite.h" #include // for min #include "apz/src/AsyncPanZoomController.h" // for AsyncPanZoomController #include "FrameMetrics.h" // for FrameMetrics #include "Units.h" // for LayerRect, LayerPixel, etc #include "gfxEnv.h" // for gfxEnv #include "gfxPrefs.h" // for gfxPrefs #include "mozilla/Assertions.h" // for MOZ_ASSERT, etc #include "mozilla/RefPtr.h" // for RefPtr #include "mozilla/UniquePtr.h" // for UniquePtr #include "mozilla/gfx/BaseRect.h" // for BaseRect #include "mozilla/gfx/Matrix.h" // for Matrix4x4 #include "mozilla/gfx/Point.h" // for Point, IntPoint #include "mozilla/gfx/Rect.h" // for IntRect, Rect #include "mozilla/layers/Compositor.h" // for Compositor, etc #include "mozilla/layers/CompositorTypes.h" // for DiagnosticFlags::CONTAINER #include "mozilla/layers/Effects.h" // for Effect, EffectChain, etc #include "mozilla/layers/TextureHost.h" // for CompositingRenderTarget #include "mozilla/layers/AsyncCompositionManager.h" // for ViewTransform #include "mozilla/layers/LayerMetricsWrapper.h" // for LayerMetricsWrapper #include "mozilla/mozalloc.h" // for operator delete, etc #include "mozilla/RefPtr.h" // for nsRefPtr #include "nsDebug.h" // for NS_ASSERTION #include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc #include "nsISupportsUtils.h" // for NS_ADDREF, NS_RELEASE #include "nsRegion.h" // for nsIntRegion #include "nsTArray.h" // for nsAutoTArray #include "TextRenderer.h" // for TextRenderer #include #include "GeckoProfiler.h" // for GeckoProfiler #ifdef MOZ_ENABLE_PROFILER_SPS #include "ProfilerMarkers.h" // for ProfilerMarkers #endif #define CULLING_LOG(...) // #define CULLING_LOG(...) printf_stderr("CULLING: " __VA_ARGS__) #define DUMP(...) do { if (gfxEnv::DumpDebug()) { printf_stderr(__VA_ARGS__); } } while(0) #define XYWH(k) (k).x, (k).y, (k).width, (k).height #define XY(k) (k).x, (k).y #define WH(k) (k).width, (k).height namespace mozilla { namespace layers { using namespace gfx; static bool LayerHasCheckerboardingAPZC(Layer* aLayer, Color* aOutColor) { for (LayerMetricsWrapper i(aLayer, LayerMetricsWrapper::StartAt::BOTTOM); i; i = i.GetParent()) { if (!i.Metrics().IsScrollable()) { continue; } if (i.GetApzc() && i.GetApzc()->IsCurrentlyCheckerboarding()) { if (aOutColor) { *aOutColor = i.Metrics().GetBackgroundColor(); } return true; } break; } return false; } static void DrawLayerInfo(const RenderTargetIntRect& aClipRect, LayerManagerComposite* aManager, Layer* aLayer) { if (aLayer->GetType() == Layer::LayerType::TYPE_CONTAINER) { // XXX - should figure out a way to render this, but for now this // is hard to do, since it will often get superimposed over the first // child of the layer, which is bad. return; } std::stringstream ss; aLayer->PrintInfo(ss, ""); LayerIntRegion visibleRegion = aLayer->GetVisibleRegion(); uint32_t maxWidth = std::min(visibleRegion.GetBounds().width, 500); IntPoint topLeft = visibleRegion.ToUnknownRegion().GetBounds().TopLeft(); aManager->GetTextRenderer()->RenderText(ss.str().c_str(), topLeft, aLayer->GetEffectiveTransform(), 16, maxWidth); } template static gfx::IntRect ContainerVisibleRect(ContainerT* aContainer) { gfx::IntRect surfaceRect = aContainer->GetEffectiveVisibleRegion().ToUnknownRegion().GetBounds(); return surfaceRect; } static void PrintUniformityInfo(Layer* aLayer) { #ifdef MOZ_ENABLE_PROFILER_SPS if (!profiler_is_active()) { return; } // Don't want to print a log for smaller layers if (aLayer->GetEffectiveVisibleRegion().GetBounds().width < 300 || aLayer->GetEffectiveVisibleRegion().GetBounds().height < 300) { return; } Matrix4x4 transform = aLayer->AsLayerComposite()->GetShadowTransform(); if (!transform.Is2D()) { return; } Point translation = transform.As2D().GetTranslation(); LayerTranslationPayload* payload = new LayerTranslationPayload(aLayer, translation); PROFILER_MARKER_PAYLOAD("LayerTranslation", payload); #endif } /* all of the per-layer prepared data we need to maintain */ struct PreparedLayer { PreparedLayer(Layer *aLayer, RenderTargetIntRect aClipRect) : mLayer(aLayer), mClipRect(aClipRect) {} RefPtr mLayer; RenderTargetIntRect mClipRect; }; template void ContainerRenderVR(ContainerT* aContainer, LayerManagerComposite* aManager, const gfx::IntRect& aClipRect, gfx::VRHMDInfo* aHMD) { RefPtr surface; Compositor* compositor = aManager->GetCompositor(); RefPtr previousTarget = compositor->GetCurrentRenderTarget(); float opacity = aContainer->GetEffectiveOpacity(); // The size of each individual eye surface gfx::IntSize eyeResolution = aHMD->SuggestedEyeResolution(); gfx::IntRect eyeRect[2]; eyeRect[0] = gfx::IntRect(0, 0, eyeResolution.width, eyeResolution.height); eyeRect[1] = gfx::IntRect(eyeResolution.width, 0, eyeResolution.width, eyeResolution.height); // The intermediate surface size; we're going to assume that we're not going to run // into max texture size limits gfx::IntRect surfaceRect = gfx::IntRect(0, 0, eyeResolution.width * 2, eyeResolution.height); int32_t maxTextureSize = compositor->GetMaxTextureSize(); surfaceRect.width = std::min(maxTextureSize, surfaceRect.width); surfaceRect.height = std::min(maxTextureSize, surfaceRect.height); gfx::VRHMDRenderingSupport *vrRendering = aHMD->GetRenderingSupport(); if (gfxEnv::NoVRRendering()) vrRendering = nullptr; if (vrRendering) { if (!aContainer->mVRRenderTargetSet || aContainer->mVRRenderTargetSet->size != surfaceRect.Size()) { aContainer->mVRRenderTargetSet = vrRendering->CreateRenderTargetSet(compositor, surfaceRect.Size()); } if (!aContainer->mVRRenderTargetSet) { NS_WARNING("CreateRenderTargetSet failed"); return; } surface = aContainer->mVRRenderTargetSet->GetNextRenderTarget(); if (!surface) { NS_WARNING("GetNextRenderTarget failed"); return; } } else { surface = compositor->CreateRenderTarget(surfaceRect, INIT_MODE_CLEAR); if (!surface) { return; } } gfx::IntRect rtBounds = previousTarget->GetRect(); DUMP("eyeResolution: %d %d targetRT: %d %d %d %d\n", WH(eyeResolution), XYWH(rtBounds)); compositor->SetRenderTarget(surface); nsAutoTArray children; aContainer->SortChildrenBy3DZOrder(children); gfx::Matrix4x4 origTransform = aContainer->GetEffectiveTransform(); for (uint32_t i = 0; i < children.Length(); i++) { LayerComposite* layerToRender = static_cast(children.ElementAt(i)->ImplData()); Layer* layer = layerToRender->GetLayer(); uint32_t contentFlags = layer->GetContentFlags(); if (layer->GetEffectiveVisibleRegion().IsEmpty() && !layer->AsContainerLayer()) { continue; } // We flip between pre-rendered and Gecko-rendered VR based on whether // the child layer of this VR container layer has PRESERVE_3D or not. if ((contentFlags & Layer::CONTENT_PRESERVE_3D) == 0) { // This layer is native VR DUMP("%p Switching to pre-rendered VR\n", aContainer); // XXX we still need depth test here, but we have no way of preserving // depth anyway in native VR layers until we have a way to save them // from WebGL (and maybe depth video?) compositor->SetRenderTarget(surface); aContainer->ReplaceEffectiveTransform(origTransform); // If this native-VR child layer does not have sizes that match // the eye resolution (that is, returned by the recommended // render rect from the HMD device), then we need to scale it // up/down. Rect layerBounds; // XXX this is a hack! Canvas layers aren't reporting the // proper bounds here (visible region bounds are 0,0,0,0) // and I'm not sure if this is the bounds we want anyway. if (layer->GetType() == Layer::TYPE_CANVAS) { layerBounds = IntRectToRect(static_cast(layer)->GetBounds()); } else { layerBounds = IntRectToRect(layer->GetEffectiveVisibleRegion().ToUnknownRegion().GetBounds()); } const gfx::Matrix4x4 childTransform = layer->GetEffectiveTransform(); layerBounds = childTransform.TransformBounds(layerBounds); DUMP(" layer %p [type %d] bounds [%f %f %f %f] surfaceRect [%d %d %d %d]\n", layer, (int) layer->GetType(), XYWH(layerBounds), XYWH(surfaceRect)); bool restoreTransform = false; if ((layerBounds.width != 0 && layerBounds.height != 0) && (layerBounds.width != surfaceRect.width || layerBounds.height != surfaceRect.height)) { DUMP(" layer %p doesn't match, prescaling by %f %f\n", layer, surfaceRect.width / float(layerBounds.width), surfaceRect.height / float(layerBounds.height)); gfx::Matrix4x4 scaledChildTransform(childTransform); scaledChildTransform.PreScale(surfaceRect.width / layerBounds.width, surfaceRect.height / layerBounds.height, 1.0f); layer->ReplaceEffectiveTransform(scaledChildTransform); restoreTransform = true; } // XXX these are both clip rects, which end up as scissor rects in the compositor. So we just // pass the full target surface rect here. layerToRender->Prepare(RenderTargetIntRect(surfaceRect.x, surfaceRect.y, surfaceRect.width, surfaceRect.height)); layerToRender->RenderLayer(surfaceRect); if (restoreTransform) { layer->ReplaceEffectiveTransform(childTransform); } } else { // Gecko-rendered CSS VR -- not supported yet, so just don't render this layer! } } DUMP(" -- ContainerRenderVR [%p] after child layers\n", aContainer); // Now put back the original transfom on this container aContainer->ReplaceEffectiveTransform(origTransform); // then bind the original target and draw with distortion compositor->SetRenderTarget(previousTarget); if (vrRendering) { vrRendering->SubmitFrame(aContainer->mVRRenderTargetSet); DUMP("<<< ContainerRenderVR [used vrRendering] [%p]\n", aContainer); if (!gfxPrefs::VRMirrorTextures()) { return; } } gfx::Rect rect(surfaceRect.x, surfaceRect.y, surfaceRect.width, surfaceRect.height); gfx::Rect clipRect(aClipRect.x, aClipRect.y, aClipRect.width, aClipRect.height); // The VR geometry may not cover the entire area; we need to fill with a solid color // first. // XXX should DrawQuad handle this on its own? Is there a time where we wouldn't want // to do this? (e.g. something like Cardboard would not require distortion so will fill // the entire rect) EffectChain solidEffect(aContainer); solidEffect.mPrimaryEffect = new EffectSolidColor(Color(0.0, 0.0, 0.0, 1.0)); aManager->GetCompositor()->DrawQuad(rect, rect, solidEffect, 1.0, gfx::Matrix4x4()); // draw the temporary surface with VR distortion to the original destination EffectChain vrEffect(aContainer); bool skipDistortion = vrRendering || gfxEnv::VRNoDistortion(); if (skipDistortion) { vrEffect.mPrimaryEffect = new EffectRenderTarget(surface); } else { vrEffect.mPrimaryEffect = new EffectVRDistortion(aHMD, surface); } gfx::Matrix4x4 scaleTransform = aContainer->GetEffectiveTransform(); scaleTransform.PreScale(rtBounds.width / float(surfaceRect.width), rtBounds.height / float(surfaceRect.height), 1.0f); // XXX we shouldn't use visibleRect here -- the VR distortion needs to know the // full rect, not just the visible one. Luckily, right now, VR distortion is only // rendered when the element is fullscreen, so the visibleRect will be right anyway. aManager->GetCompositor()->DrawQuad(rect, clipRect, vrEffect, opacity, scaleTransform); DUMP("<<< ContainerRenderVR [%p]\n", aContainer); } /* all of the prepared data that we need in RenderLayer() */ struct PreparedData { RefPtr mTmpTarget; nsAutoTArray mLayers; bool mNeedsSurfaceCopy; }; // ContainerPrepare is shared between RefLayer and ContainerLayer template void ContainerPrepare(ContainerT* aContainer, LayerManagerComposite* aManager, const RenderTargetIntRect& aClipRect) { aContainer->mPrepared = MakeUnique(); aContainer->mPrepared->mNeedsSurfaceCopy = false; gfx::VRHMDInfo *hmdInfo = aContainer->GetVRHMDInfo(); if (hmdInfo && hmdInfo->GetConfiguration().IsValid()) { // we're not going to do anything here; instead, we'll do it all in ContainerRender. // XXX fix this; we can win with the same optimizations. Specifically, we // want to render thebes layers only once and then composite the intermeidate surfaces // with different transforms twice. return; } /** * Determine which layers to draw. */ nsAutoTArray children; aContainer->SortChildrenBy3DZOrder(children); for (uint32_t i = 0; i < children.Length(); i++) { LayerComposite* layerToRender = static_cast(children.ElementAt(i)->ImplData()); RenderTargetIntRect clipRect = layerToRender->GetLayer()-> CalculateScissorRect(aClipRect); // We don't want to skip container layers because otherwise their mPrepared // may be null which is not allowed. if (!layerToRender->GetLayer()->AsContainerLayer()) { if (layerToRender->GetLayer()->GetEffectiveVisibleRegion().IsEmpty()) { CULLING_LOG("Sublayer %p has no effective visible region\n", layerToRender->GetLayer()); continue; } if (clipRect.IsEmpty()) { CULLING_LOG("Sublayer %p has an empty world clip rect\n", layerToRender->GetLayer()); continue; } } CULLING_LOG("Preparing sublayer %p\n", layerToRender->GetLayer()); layerToRender->Prepare(clipRect); aContainer->mPrepared->mLayers.AppendElement(PreparedLayer(layerToRender->GetLayer(), clipRect)); } CULLING_LOG("Preparing container layer %p\n", aContainer->GetLayer()); /** * Setup our temporary surface for rendering the contents of this container. */ gfx::IntRect surfaceRect = ContainerVisibleRect(aContainer); if (surfaceRect.IsEmpty()) { return; } bool surfaceCopyNeeded; // DefaultComputeSupportsComponentAlphaChildren can mutate aContainer so call it unconditionally aContainer->DefaultComputeSupportsComponentAlphaChildren(&surfaceCopyNeeded); if (aContainer->UseIntermediateSurface()) { if (!surfaceCopyNeeded) { RefPtr surface = nullptr; RefPtr& lastSurf = aContainer->mLastIntermediateSurface; if (lastSurf && !aContainer->mChildrenChanged && lastSurf->GetRect().IsEqualEdges(surfaceRect)) { surface = lastSurf; } if (!surface) { // If we don't need a copy we can render to the intermediate now to avoid // unecessary render target switching. This brings a big perf boost on mobile gpus. surface = CreateOrRecycleTarget(aContainer, aManager); MOZ_PERFORMANCE_WARNING("gfx", "[%p] Container layer requires intermediate surface rendering\n", aContainer); RenderIntermediate(aContainer, aManager, aClipRect.ToUnknownRect(), surface); aContainer->SetChildrenChanged(false); } aContainer->mPrepared->mTmpTarget = surface; } else { MOZ_PERFORMANCE_WARNING("gfx", "[%p] Container layer requires intermediate surface copy\n", aContainer); aContainer->mPrepared->mNeedsSurfaceCopy = true; aContainer->mLastIntermediateSurface = nullptr; } } else { aContainer->mLastIntermediateSurface = nullptr; } } template void RenderMinimap(ContainerT* aContainer, LayerManagerComposite* aManager, const RenderTargetIntRect& aClipRect, Layer* aLayer) { Compositor* compositor = aManager->GetCompositor(); if (aLayer->GetFrameMetricsCount() < 1) { return; } AsyncPanZoomController* controller = aLayer->GetAsyncPanZoomController(0); if (!controller) { return; } ViewTransform asyncTransformWithoutOverscroll; ParentLayerPoint scrollOffset; controller->SampleContentTransformForFrame(&asyncTransformWithoutOverscroll, scrollOffset); // Options const int verticalPadding = 10; const int horizontalPadding = 5; gfx::Color backgroundColor(0.3f, 0.3f, 0.3f, 0.3f); gfx::Color tileActiveColor(1, 1, 1, 0.5f); gfx::Color tileBorderColor(0, 0, 0, 0.1f); gfx::Color pageBorderColor(0, 0, 0); gfx::Color displayPortColor(0, 1.f, 0); gfx::Color viewPortColor(0, 0, 1.f); // Rects const FrameMetrics& fm = aLayer->GetFrameMetrics(0); ParentLayerRect compositionBounds = fm.GetCompositionBounds(); LayerRect scrollRect = fm.GetScrollableRect() * fm.LayersPixelsPerCSSPixel(); LayerRect viewRect = ParentLayerRect(scrollOffset, compositionBounds.Size()) / LayerToParentLayerScale(1); LayerRect dp = (fm.GetDisplayPort() + fm.GetScrollOffset()) * fm.LayersPixelsPerCSSPixel(); // Don't render trivial minimap. They can show up from textboxes and other tiny frames. if (viewRect.width < 64 && viewRect.height < 64) { return; } // Compute a scale with an appropriate aspect ratio // We allocate up to 100px of width and the height of this layer. float scaleFactor; float scaleFactorX; float scaleFactorY; scaleFactorX = 100.f / scrollRect.width; scaleFactorY = ((viewRect.height) - 2 * verticalPadding) / scrollRect.height; scaleFactor = std::min(scaleFactorX, scaleFactorY); Matrix4x4 transform = Matrix4x4::Scaling(scaleFactor, scaleFactor, 1); transform.PostTranslate(horizontalPadding + compositionBounds.x, verticalPadding + compositionBounds.y, 0); Rect clipRect = aContainer->GetEffectiveTransform().TransformBounds( transform.TransformBounds(scrollRect.ToUnknownRect())); clipRect.width++; clipRect.height++; Rect r; r = transform.TransformBounds(scrollRect.ToUnknownRect()); compositor->FillRect(r, backgroundColor, clipRect, aContainer->GetEffectiveTransform()); /* Disabled because on long pages SlowDrawRect becomes a bottleneck. int tileW = gfxPrefs::LayersTileWidth(); int tileH = gfxPrefs::LayersTileHeight(); for (int x = scrollRect.x; x < scrollRect.XMost(); x += tileW) { for (int y = scrollRect.y; y < scrollRect.YMost(); y += tileH) { LayerRect tileRect = LayerRect(x - x % tileW, y - y % tileH, tileW, tileH); r = transform.TransformBounds(tileRect.ToUnknownRect()); if (tileRect.Intersects(dp)) { compositor->FillRect(r, tileActiveColor, clipRect, aContainer->GetEffectiveTransform()); } compositor->SlowDrawRect(r, tileBorderColor, clipRect, aContainer->GetEffectiveTransform()); } } */ r = transform.TransformBounds(scrollRect.ToUnknownRect()); compositor->SlowDrawRect(r, pageBorderColor, clipRect, aContainer->GetEffectiveTransform()); r = transform.TransformBounds(dp.ToUnknownRect()); compositor->FillRect(r, tileActiveColor, clipRect, aContainer->GetEffectiveTransform()); r = transform.TransformBounds(dp.ToUnknownRect()); compositor->SlowDrawRect(r, displayPortColor, clipRect, aContainer->GetEffectiveTransform()); r = transform.TransformBounds(viewRect.ToUnknownRect()); compositor->SlowDrawRect(r, viewPortColor, clipRect, aContainer->GetEffectiveTransform(), 2); } template void RenderLayers(ContainerT* aContainer, LayerManagerComposite* aManager, const RenderTargetIntRect& aClipRect) { Compositor* compositor = aManager->GetCompositor(); for (size_t i = 0u; i < aContainer->mPrepared->mLayers.Length(); i++) { PreparedLayer& preparedData = aContainer->mPrepared->mLayers[i]; LayerComposite* layerToRender = static_cast(preparedData.mLayer->ImplData()); const RenderTargetIntRect& clipRect = preparedData.mClipRect; Layer* layer = layerToRender->GetLayer(); Color color; if ((layer->GetContentFlags() & Layer::CONTENT_OPAQUE) && layer->IsOpaqueForVisibility() && LayerHasCheckerboardingAPZC(layer, &color)) { if (gfxPrefs::APZHighlightCheckerboardedAreas()) { color = Color(255 / 255.f, 188 / 255.f, 217 / 255.f, 1.f); // "Cotton Candy" } // Ideally we would want to intersect the checkerboard region from the APZ with the layer bounds // and only fill in that area. However the layer bounds takes into account the base translation // for the painted layer whereas the checkerboard region does not. One does not simply // intersect areas in different coordinate spaces. So we do this a little more permissively // and only fill in the background when we know there is checkerboard, which in theory // should only occur transiently. gfx::IntRect layerBounds = layer->GetLayerBounds(); EffectChain effectChain(layer); effectChain.mPrimaryEffect = new EffectSolidColor(color); aManager->GetCompositor()->DrawQuad(gfx::Rect(layerBounds.x, layerBounds.y, layerBounds.width, layerBounds.height), gfx::Rect(clipRect.ToUnknownRect()), effectChain, layer->GetEffectiveOpacity(), layer->GetEffectiveTransform()); } if (layerToRender->HasLayerBeenComposited()) { // Composer2D will compose this layer so skip GPU composition // this time & reset composition flag for next composition phase layerToRender->SetLayerComposited(false); gfx::IntRect clearRect = layerToRender->GetClearRect(); if (!clearRect.IsEmpty()) { // Clear layer's visible rect on FrameBuffer with transparent pixels gfx::Rect fbRect(clearRect.x, clearRect.y, clearRect.width, clearRect.height); compositor->ClearRect(fbRect); layerToRender->SetClearRect(gfx::IntRect(0, 0, 0, 0)); } } else { layerToRender->RenderLayer(clipRect.ToUnknownRect()); } if (gfxPrefs::UniformityInfo()) { PrintUniformityInfo(layer); } if (gfxPrefs::DrawLayerInfo()) { DrawLayerInfo(clipRect, aManager, layer); } // Draw a border around scrollable layers. // A layer can be scrolled by multiple scroll frames. Draw a border // for each. // Within the list of scroll frames for a layer, the layer border for a // scroll frame lower down is affected by the async transforms on scroll // frames higher up, so loop from the top down, and accumulate an async // transform as we go along. Matrix4x4 asyncTransform; for (uint32_t i = layer->GetFrameMetricsCount(); i > 0; --i) { if (layer->GetFrameMetrics(i - 1).IsScrollable()) { // Since the composition bounds are in the parent layer's coordinates, // use the parent's effective transform rather than the layer's own. ParentLayerRect compositionBounds = layer->GetFrameMetrics(i - 1).GetCompositionBounds(); aManager->GetCompositor()->DrawDiagnostics(DiagnosticFlags::CONTAINER, compositionBounds.ToUnknownRect(), gfx::Rect(aClipRect.ToUnknownRect()), asyncTransform * aContainer->GetEffectiveTransform()); if (AsyncPanZoomController* apzc = layer->GetAsyncPanZoomController(i - 1)) { asyncTransform = apzc->GetCurrentAsyncTransformWithOverscroll() * asyncTransform; } } } if (gfxPrefs::APZMinimap()) { RenderMinimap(aContainer, aManager, aClipRect, layer); } // invariant: our GL context should be current here, I don't think we can // assert it though } } template RefPtr CreateOrRecycleTarget(ContainerT* aContainer, LayerManagerComposite* aManager) { Compositor* compositor = aManager->GetCompositor(); SurfaceInitMode mode = INIT_MODE_CLEAR; gfx::IntRect surfaceRect = ContainerVisibleRect(aContainer); if (aContainer->GetEffectiveVisibleRegion().GetNumRects() == 1 && (aContainer->GetContentFlags() & Layer::CONTENT_OPAQUE)) { mode = INIT_MODE_NONE; } RefPtr& lastSurf = aContainer->mLastIntermediateSurface; if (lastSurf && lastSurf->GetRect().IsEqualEdges(surfaceRect)) { if (mode == INIT_MODE_CLEAR) { lastSurf->ClearOnBind(); } return lastSurf; } else { lastSurf = compositor->CreateRenderTarget(surfaceRect, mode); return lastSurf; } } template RefPtr CreateTemporaryTargetAndCopyFromBackground(ContainerT* aContainer, LayerManagerComposite* aManager) { Compositor* compositor = aManager->GetCompositor(); gfx::IntRect visibleRect = aContainer->GetEffectiveVisibleRegion().ToUnknownRegion().GetBounds(); RefPtr previousTarget = compositor->GetCurrentRenderTarget(); gfx::IntRect surfaceRect = gfx::IntRect(visibleRect.x, visibleRect.y, visibleRect.width, visibleRect.height); gfx::IntPoint sourcePoint = gfx::IntPoint(visibleRect.x, visibleRect.y); gfx::Matrix4x4 transform = aContainer->GetEffectiveTransform(); DebugOnly transform2d; MOZ_ASSERT(transform.Is2D(&transform2d) && !gfx::ThebesMatrix(transform2d).HasNonIntegerTranslation()); sourcePoint += gfx::IntPoint(transform._41, transform._42); sourcePoint -= compositor->GetCurrentRenderTarget()->GetOrigin(); return compositor->CreateRenderTargetFromSource(surfaceRect, previousTarget, sourcePoint); } template void RenderIntermediate(ContainerT* aContainer, LayerManagerComposite* aManager, const gfx::IntRect& aClipRect, RefPtr surface) { Compositor* compositor = aManager->GetCompositor(); RefPtr previousTarget = compositor->GetCurrentRenderTarget(); if (!surface) { return; } compositor->SetRenderTarget(surface); // pre-render all of the layers into our temporary RenderLayers(aContainer, aManager, RenderTargetIntRect::FromUnknownRect(aClipRect)); // Unbind the current surface and rebind the previous one. compositor->SetRenderTarget(previousTarget); } template void ContainerRender(ContainerT* aContainer, LayerManagerComposite* aManager, const gfx::IntRect& aClipRect) { MOZ_ASSERT(aContainer->mPrepared); gfx::VRHMDInfo *hmdInfo = aContainer->GetVRHMDInfo(); if (hmdInfo && hmdInfo->GetConfiguration().IsValid()) { ContainerRenderVR(aContainer, aManager, aClipRect, hmdInfo); aContainer->mPrepared = nullptr; return; } if (aContainer->UseIntermediateSurface()) { RefPtr surface; if (aContainer->mPrepared->mNeedsSurfaceCopy) { // we needed to copy the background so we waited until now to render the intermediate surface = CreateTemporaryTargetAndCopyFromBackground(aContainer, aManager); RenderIntermediate(aContainer, aManager, aClipRect, surface); } else { surface = aContainer->mPrepared->mTmpTarget; } if (!surface) { aContainer->mPrepared = nullptr; return; } gfx::Rect visibleRect(aContainer->GetEffectiveVisibleRegion().ToUnknownRegion().GetBounds()); RefPtr compositor = aManager->GetCompositor(); #ifdef MOZ_DUMP_PAINTING if (gfxEnv::DumpCompositorTextures()) { RefPtr surf = surface->Dump(compositor); if (surf) { WriteSnapshotToDumpFile(aContainer, surf); } } #endif RefPtr container = aContainer; RenderWithAllMasks(aContainer, compositor, aClipRect, [&, surface, compositor, container](EffectChain& effectChain, const Rect& clipRect) { effectChain.mPrimaryEffect = new EffectRenderTarget(surface); compositor->DrawQuad(visibleRect, clipRect, effectChain, container->GetEffectiveOpacity(), container->GetEffectiveTransform()); }); } else { RenderLayers(aContainer, aManager, RenderTargetIntRect::FromUnknownRect(aClipRect)); } aContainer->mPrepared = nullptr; // If it is a scrollable container layer with no child layers, and one of the APZCs // attached to it has a nonempty async transform, then that transform is not applied // to any visible content. Display a warning box (conditioned on the FPS display being // enabled). if (gfxPrefs::LayersDrawFPS() && aContainer->IsScrollInfoLayer()) { // Since aContainer doesn't have any children we can just iterate from the top metrics // on it down to the bottom using GetFirstChild and not worry about walking onto another // underlying layer. for (LayerMetricsWrapper i(aContainer); i; i = i.GetFirstChild()) { if (AsyncPanZoomController* apzc = i.GetApzc()) { if (!apzc->GetAsyncTransformAppliedToContent() && !Matrix4x4(apzc->GetCurrentAsyncTransform()).IsIdentity()) { aManager->UnusedApzTransformWarning(); break; } } } } } ContainerLayerComposite::ContainerLayerComposite(LayerManagerComposite *aManager) : ContainerLayer(aManager, nullptr) , LayerComposite(aManager) { MOZ_COUNT_CTOR(ContainerLayerComposite); mImplData = static_cast(this); } ContainerLayerComposite::~ContainerLayerComposite() { MOZ_COUNT_DTOR(ContainerLayerComposite); // We don't Destroy() on destruction here because this destructor // can be called after remote content has crashed, and it may not be // safe to free the IPC resources of our children. Those resources // are automatically cleaned up by IPDL-generated code. // // In the common case of normal shutdown, either // LayerManagerComposite::Destroy(), a parent // *ContainerLayerComposite::Destroy(), or Disconnect() will trigger // cleanup of our resources. while (mFirstChild) { RemoveChild(mFirstChild); } } void ContainerLayerComposite::Destroy() { if (!mDestroyed) { while (mFirstChild) { static_cast(GetFirstChild()->ImplData())->Destroy(); RemoveChild(mFirstChild); } mDestroyed = true; } } LayerComposite* ContainerLayerComposite::GetFirstChildComposite() { if (!mFirstChild) { return nullptr; } return static_cast(mFirstChild->ImplData()); } void ContainerLayerComposite::RenderLayer(const gfx::IntRect& aClipRect) { ContainerRender(this, mCompositeManager, aClipRect); } void ContainerLayerComposite::Prepare(const RenderTargetIntRect& aClipRect) { ContainerPrepare(this, mCompositeManager, aClipRect); } void ContainerLayerComposite::CleanupResources() { mLastIntermediateSurface = nullptr; for (Layer* l = GetFirstChild(); l; l = l->GetNextSibling()) { LayerComposite* layerToCleanup = static_cast(l->ImplData()); layerToCleanup->CleanupResources(); } } RefLayerComposite::RefLayerComposite(LayerManagerComposite* aManager) : RefLayer(aManager, nullptr) , LayerComposite(aManager) { mImplData = static_cast(this); } RefLayerComposite::~RefLayerComposite() { Destroy(); } void RefLayerComposite::Destroy() { MOZ_ASSERT(!mFirstChild); mDestroyed = true; } LayerComposite* RefLayerComposite::GetFirstChildComposite() { if (!mFirstChild) { return nullptr; } return static_cast(mFirstChild->ImplData()); } void RefLayerComposite::RenderLayer(const gfx::IntRect& aClipRect) { ContainerRender(this, mCompositeManager, aClipRect); } void RefLayerComposite::Prepare(const RenderTargetIntRect& aClipRect) { ContainerPrepare(this, mCompositeManager, aClipRect); } void RefLayerComposite::CleanupResources() { mLastIntermediateSurface = nullptr; } } // namespace layers } // namespace mozilla