bool ClientTiledPaintedLayer::UseFastPath() { LayerMetricsWrapper scrollAncestor; GetAncestorLayers(&scrollAncestor, nullptr); if (!scrollAncestor) { return true; } const FrameMetrics& parentMetrics = scrollAncestor.Metrics(); bool multipleTransactionsNeeded = gfxPlatform::GetPlatform()->UseProgressivePaint() || gfxPrefs::UseLowPrecisionBuffer() || !parentMetrics.GetCriticalDisplayPort().IsEmpty(); bool isFixed = GetIsFixedPosition() || GetParent()->GetIsFixedPosition(); return !multipleTransactionsNeeded || isFixed || parentMetrics.GetDisplayPort().IsEmpty(); }
static gfx::Matrix4x4 GetTransformToAncestorsParentLayer(Layer* aStart, const LayerMetricsWrapper& aAncestor) { gfx::Matrix4x4 transform; const LayerMetricsWrapper& ancestorParent = aAncestor.GetParent(); for (LayerMetricsWrapper iter(aStart, LayerMetricsWrapper::StartAt::BOTTOM); ancestorParent ? iter != ancestorParent : iter.IsValid(); iter = iter.GetParent()) { transform = transform * iter.GetTransform(); if (gfxPrefs::LayoutUseContainersForRootFrames()) { // When scrolling containers, layout adds a post-scale into the transform // of the displayport-ancestor (which we pick up in GetTransform() above) // to cancel out the pres shell resolution (for historical reasons). The // compositor in turn cancels out this post-scale (i.e., scales by the // pres shell resolution), and to get correct calculations, we need to do // so here, too. // // With containerless scrolling, the offending post-scale is on the // parent layer of the displayport-ancestor, which we don't reach in this // loop, so we don't need to worry about it. const FrameMetrics& metrics = iter.Metrics(); transform.PostScale(metrics.GetPresShellResolution(), metrics.GetPresShellResolution(), 1.f); } } return transform; }
bool ClientTiledPaintedLayer::UseProgressiveDraw() { if (!gfxPlatform::GetPlatform()->UseProgressivePaint()) { // pref is disabled, so never do progressive return false; } if (!mContentClient->GetTiledBuffer()->SupportsProgressiveUpdate()) { return false; } if (ClientManager()->HasShadowTarget()) { // This condition is true when we are in a reftest scenario. We don't want // to draw progressively here because it can cause intermittent reftest // failures because the harness won't wait for all the tiles to be drawn. return false; } if (mPaintData.mCriticalDisplayPort.IsEmpty()) { // This catches three scenarios: // 1) This layer doesn't have a scrolling ancestor // 2) This layer is subject to OMTA transforms // 3) Low-precision painting is disabled // In all of these cases, we don't want to draw this layer progressively. return false; } if (GetIsFixedPosition() || GetParent()->GetIsFixedPosition()) { // This layer is fixed-position and so even if it does have a scrolling // ancestor it will likely be entirely on-screen all the time, so we // should draw it all at once return false; } if (ClientManager()->AsyncPanZoomEnabled()) { LayerMetricsWrapper scrollAncestor; GetAncestorLayers(&scrollAncestor, nullptr, nullptr); MOZ_ASSERT(scrollAncestor); // because mPaintData.mCriticalDisplayPort is non-empty const FrameMetrics& parentMetrics = scrollAncestor.Metrics(); if (!IsScrollingOnCompositor(parentMetrics)) { return false; } } return true; }
bool ClientTiledPaintedLayer::UseProgressiveDraw() { if (!gfxPrefs::ProgressivePaint()) { // pref is disabled, so never do progressive return false; } if (!mContentClient->GetTiledBuffer()->SupportsProgressiveUpdate()) { return false; } if (ClientManager()->HasShadowTarget()) { // This condition is true when we are in a reftest scenario. We don't want // to draw progressively here because it can cause intermittent reftest // failures because the harness won't wait for all the tiles to be drawn. return false; } if (GetIsFixedPosition() || GetParent()->GetIsFixedPosition()) { // This layer is fixed-position and so even if it does have a scrolling // ancestor it will likely be entirely on-screen all the time, so we // should draw it all at once return false; } if (mPaintData.mHasTransformAnimation) { // The compositor is going to animate this somehow, so we want it all // on the screen at once. return false; } if (ClientManager()->AsyncPanZoomEnabled()) { LayerMetricsWrapper scrollAncestor; GetAncestorLayers(&scrollAncestor, nullptr, nullptr); MOZ_ASSERT(scrollAncestor); // because mPaintData.mCriticalDisplayPort is set if (!scrollAncestor) { return false; } const FrameMetrics& parentMetrics = scrollAncestor.Metrics(); if (!IsScrollingOnCompositor(parentMetrics)) { return false; } } return true; }
bool ClientTiledPaintedLayer::UseFastPath() { LayerMetricsWrapper scrollAncestor; GetAncestorLayers(&scrollAncestor, nullptr); if (!scrollAncestor) { return true; } const FrameMetrics& parentMetrics = scrollAncestor.Metrics(); bool multipleTransactionsNeeded = gfxPlatform::GetPlatform()->UseProgressivePaint() || gfxPrefs::UseLowPrecisionBuffer() || !parentMetrics.GetCriticalDisplayPort().IsEmpty(); bool isFixed = GetIsFixedPosition() || GetParent()->GetIsFixedPosition(); bool isScrollable = parentMetrics.IsScrollable(); return !multipleTransactionsNeeded || isFixed || !isScrollable #if !defined(MOZ_WIDGET_ANDROID) || defined(MOZ_ANDROID_APZ) || !IsScrollingOnCompositor(parentMetrics) #endif ; }
static gfx::Matrix4x4 GetTransformToAncestorsParentLayer(Layer* aStart, const LayerMetricsWrapper& aAncestor) { gfx::Matrix4x4 transform; const LayerMetricsWrapper& ancestorParent = aAncestor.GetParent(); for (LayerMetricsWrapper iter(aStart, LayerMetricsWrapper::StartAt::BOTTOM); ancestorParent ? iter != ancestorParent : iter.IsValid(); iter = iter.GetParent()) { transform = transform * iter.GetTransform(); // If the layer has a non-transient async transform then we need to apply it here // because it will get applied by the APZ in the compositor as well const FrameMetrics& metrics = iter.Metrics(); transform.PostScale(metrics.mPresShellResolution, metrics.mPresShellResolution, 1.f); } return transform; }
void ClientTiledPaintedLayer::BeginPaint() { mPaintData.mLowPrecisionPaintCount = 0; mPaintData.mPaintFinished = false; mPaintData.mCompositionBounds.SetEmpty(); mPaintData.mCriticalDisplayPort.SetEmpty(); if (!GetBaseTransform().Is2D()) { // Give up if there is a complex CSS transform on the layer. We might // eventually support these but for now it's too complicated to handle // given that it's a pretty rare scenario. return; } // Get the metrics of the nearest scrollable layer and the nearest layer // with a displayport. LayerMetricsWrapper scrollAncestor; LayerMetricsWrapper displayPortAncestor; GetAncestorLayers(&scrollAncestor, &displayPortAncestor); if (!displayPortAncestor || !scrollAncestor) { // No displayport or scroll ancestor, so we can't do progressive rendering. #if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_B2G) // Both Android and b2g are guaranteed to have a displayport set, so this // should never happen. NS_WARNING("Tiled PaintedLayer with no scrollable container ancestor"); #endif return; } TILING_LOG("TILING %p: Found scrollAncestor %p and displayPortAncestor %p\n", this, scrollAncestor.GetLayer(), displayPortAncestor.GetLayer()); const FrameMetrics& scrollMetrics = scrollAncestor.Metrics(); const FrameMetrics& displayportMetrics = displayPortAncestor.Metrics(); // Calculate the transform required to convert ParentLayer space of our // display port ancestor to the Layer space of this layer. gfx::Matrix4x4 transformDisplayPortToLayer = GetTransformToAncestorsParentLayer(this, displayPortAncestor); transformDisplayPortToLayer.Invert(); // Compute the critical display port that applies to this layer in the // LayoutDevice space of this layer. ParentLayerRect criticalDisplayPort = (displayportMetrics.GetCriticalDisplayPort() * displayportMetrics.GetZoom()) + displayportMetrics.mCompositionBounds.TopLeft(); mPaintData.mCriticalDisplayPort = RoundedOut( ApplyParentLayerToLayerTransform(transformDisplayPortToLayer, criticalDisplayPort)); TILING_LOG("TILING %p: Critical displayport %s\n", this, Stringify(mPaintData.mCriticalDisplayPort).c_str()); // Store the resolution from the displayport ancestor layer. Because this is Gecko-side, // before any async transforms have occurred, we can use the zoom for this. mPaintData.mResolution = displayportMetrics.GetZoom(); TILING_LOG("TILING %p: Resolution %f\n", this, mPaintData.mPresShellResolution.scale); // Store the applicable composition bounds in this layer's Layer units. mPaintData.mTransformToCompBounds = GetTransformToAncestorsParentLayer(this, scrollAncestor); gfx::Matrix4x4 transformToBounds = mPaintData.mTransformToCompBounds; transformToBounds.Invert(); mPaintData.mCompositionBounds = ApplyParentLayerToLayerTransform( transformToBounds, scrollMetrics.mCompositionBounds); TILING_LOG("TILING %p: Composition bounds %s\n", this, Stringify(mPaintData.mCompositionBounds).c_str()); // Calculate the scroll offset since the last transaction mPaintData.mScrollOffset = displayportMetrics.GetScrollOffset() * displayportMetrics.GetZoom(); TILING_LOG("TILING %p: Scroll offset %s\n", this, Stringify(mPaintData.mScrollOffset).c_str()); }
void ClientTiledPaintedLayer::BeginPaint() { mPaintData.ResetPaintData(); if (!GetBaseTransform().Is2D()) { // Give up if there is a complex CSS transform on the layer. We might // eventually support these but for now it's too complicated to handle // given that it's a pretty rare scenario. return; } // Get the metrics of the nearest scrollable layer and the nearest layer // with a displayport. LayerMetricsWrapper scrollAncestor; LayerMetricsWrapper displayPortAncestor; bool hasTransformAnimation; GetAncestorLayers(&scrollAncestor, &displayPortAncestor, &hasTransformAnimation); if (!displayPortAncestor || !scrollAncestor) { // No displayport or scroll ancestor, so we can't do progressive rendering. #if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK) // Both Android and b2g on phones are guaranteed to have a displayport set, so this // should never happen. NS_WARNING("Tiled PaintedLayer with no scrollable container ancestor"); #endif return; } TILING_LOG("TILING %p: Found scrollAncestor %p, displayPortAncestor %p, transform %d\n", this, scrollAncestor.GetLayer(), displayPortAncestor.GetLayer(), hasTransformAnimation); const FrameMetrics& scrollMetrics = scrollAncestor.Metrics(); const FrameMetrics& displayportMetrics = displayPortAncestor.Metrics(); // Calculate the transform required to convert ParentLayer space of our // display port ancestor to the Layer space of this layer. gfx::Matrix4x4 transformDisplayPortToLayer = GetTransformToAncestorsParentLayer(this, displayPortAncestor); transformDisplayPortToLayer.Invert(); LayerRect layerBounds = ViewAs<LayerPixel>(Rect(GetLayerBounds())); // Compute the critical display port that applies to this layer in the // LayoutDevice space of this layer, but only if there is no OMT animation // on this layer. If there is an OMT animation then we need to draw the whole // visible region of this layer as determined by layout, because we don't know // what parts of it might move into view in the compositor. if (!hasTransformAnimation && mContentClient->GetLowPrecisionTiledBuffer()) { ParentLayerRect criticalDisplayPort = (displayportMetrics.GetCriticalDisplayPort() * displayportMetrics.GetZoom()) + displayportMetrics.GetCompositionBounds().TopLeft(); Maybe<LayerRect> criticalDisplayPortTransformed = ApplyParentLayerToLayerTransform(transformDisplayPortToLayer, criticalDisplayPort, layerBounds); if (!criticalDisplayPortTransformed) { mPaintData.ResetPaintData(); return; } mPaintData.mCriticalDisplayPort = RoundedToInt(*criticalDisplayPortTransformed); } TILING_LOG("TILING %p: Critical displayport %s\n", this, Stringify(mPaintData.mCriticalDisplayPort).c_str()); // Store the resolution from the displayport ancestor layer. Because this is Gecko-side, // before any async transforms have occurred, we can use the zoom for this. mPaintData.mResolution = displayportMetrics.GetZoom(); TILING_LOG("TILING %p: Resolution %s\n", this, Stringify(mPaintData.mResolution).c_str()); // Store the applicable composition bounds in this layer's Layer units. mPaintData.mTransformToCompBounds = GetTransformToAncestorsParentLayer(this, scrollAncestor); gfx::Matrix4x4 transformToBounds = mPaintData.mTransformToCompBounds; transformToBounds.Invert(); Maybe<LayerRect> compositionBoundsTransformed = ApplyParentLayerToLayerTransform( transformToBounds, scrollMetrics.GetCompositionBounds(), layerBounds); if (!compositionBoundsTransformed) { mPaintData.ResetPaintData(); return; } mPaintData.mCompositionBounds = *compositionBoundsTransformed; TILING_LOG("TILING %p: Composition bounds %s\n", this, Stringify(mPaintData.mCompositionBounds).c_str()); // Calculate the scroll offset since the last transaction mPaintData.mScrollOffset = displayportMetrics.GetScrollOffset() * displayportMetrics.GetZoom(); TILING_LOG("TILING %p: Scroll offset %s\n", this, Stringify(mPaintData.mScrollOffset).c_str()); }
void LayerManagerComposite::Render(const nsIntRegion& aInvalidRegion, const nsIntRegion& aOpaqueRegion) { PROFILER_LABEL("LayerManagerComposite", "Render", js::ProfileEntry::Category::GRAPHICS); if (mDestroyed || !mCompositor || mCompositor->IsDestroyed()) { NS_WARNING("Call on destroyed layer manager"); return; } ClearLayerFlags(mRoot); // At this time, it doesn't really matter if these preferences change // during the execution of the function; we should be safe in all // permutations. However, may as well just get the values onces and // then use them, just in case the consistency becomes important in // the future. bool invertVal = gfxPrefs::LayersEffectInvert(); bool grayscaleVal = gfxPrefs::LayersEffectGrayscale(); float contrastVal = gfxPrefs::LayersEffectContrast(); bool haveLayerEffects = (invertVal || grayscaleVal || contrastVal != 0.0); // Set LayerScope begin/end frame LayerScopeAutoFrame frame(PR_Now()); // Dump to console if (gfxPrefs::LayersDump()) { this->Dump(/* aSorted= */true); } else if (profiler_feature_active("layersdump")) { std::stringstream ss; Dump(ss); profiler_log(ss.str().c_str()); } // Dump to LayerScope Viewer if (LayerScope::CheckSendable()) { // Create a LayersPacket, dump Layers into it and transfer the // packet('s ownership) to LayerScope. auto packet = MakeUnique<layerscope::Packet>(); layerscope::LayersPacket* layersPacket = packet->mutable_layers(); this->Dump(layersPacket); LayerScope::SendLayerDump(Move(packet)); } mozilla::widget::WidgetRenderingContext widgetContext; #if defined(XP_MACOSX) widgetContext.mLayerManager = this; #elif defined(MOZ_WIDGET_ANDROID) widgetContext.mCompositor = GetCompositor(); #endif { PROFILER_LABEL("LayerManagerComposite", "PreRender", js::ProfileEntry::Category::GRAPHICS); if (!mCompositor->GetWidget()->PreRender(&widgetContext)) { return; } } ParentLayerIntRect clipRect; IntRect bounds(mRenderBounds.x, mRenderBounds.y, mRenderBounds.width, mRenderBounds.height); IntRect actualBounds; CompositorBench(mCompositor, bounds); MOZ_ASSERT(mRoot->GetOpacity() == 1); #if defined(MOZ_WIDGET_ANDROID) LayerMetricsWrapper wrapper = GetRootContentLayer(); if (wrapper) { mCompositor->SetClearColor(wrapper.Metadata().GetBackgroundColor()); } else { mCompositor->SetClearColorToDefault(); } #endif if (mRoot->GetClipRect()) { clipRect = *mRoot->GetClipRect(); IntRect rect(clipRect.x, clipRect.y, clipRect.width, clipRect.height); mCompositor->BeginFrame(aInvalidRegion, &rect, bounds, aOpaqueRegion, nullptr, &actualBounds); } else { gfx::IntRect rect; mCompositor->BeginFrame(aInvalidRegion, nullptr, bounds, aOpaqueRegion, &rect, &actualBounds); clipRect = ParentLayerIntRect(rect.x, rect.y, rect.width, rect.height); } if (actualBounds.IsEmpty()) { mCompositor->GetWidget()->PostRender(&widgetContext); return; } // Allow widget to render a custom background. mCompositor->GetWidget()->DrawWindowUnderlay( &widgetContext, LayoutDeviceIntRect::FromUnknownRect(actualBounds)); RefPtr<CompositingRenderTarget> previousTarget; if (haveLayerEffects) { previousTarget = PushGroupForLayerEffects(); } else { mTwoPassTmpTarget = nullptr; } // Render our layers. RootLayer()->Prepare(ViewAs<RenderTargetPixel>(clipRect, PixelCastJustification::RenderTargetIsParentLayerForRoot)); RootLayer()->RenderLayer(clipRect.ToUnknownRect()); if (!mRegionToClear.IsEmpty()) { for (auto iter = mRegionToClear.RectIter(); !iter.Done(); iter.Next()) { const IntRect& r = iter.Get(); mCompositor->ClearRect(Rect(r.x, r.y, r.width, r.height)); } } if (mTwoPassTmpTarget) { MOZ_ASSERT(haveLayerEffects); PopGroupForLayerEffects(previousTarget, clipRect.ToUnknownRect(), grayscaleVal, invertVal, contrastVal); } // Allow widget to render a custom foreground. mCompositor->GetWidget()->DrawWindowOverlay( &widgetContext, LayoutDeviceIntRect::FromUnknownRect(actualBounds)); // Debugging RenderDebugOverlay(actualBounds); { PROFILER_LABEL("LayerManagerComposite", "EndFrame", js::ProfileEntry::Category::GRAPHICS); mCompositor->EndFrame(); // Call after EndFrame() mCompositor->SetDispAcquireFence(mRoot); } mCompositor->GetWidget()->PostRender(&widgetContext); RecordFrame(); }