void ClientTiledThebesLayer::GetAncestorLayers(Layer** aOutScrollAncestor, Layer** aOutDisplayPortAncestor) { Layer* scrollAncestor = nullptr; Layer* displayPortAncestor = nullptr; for (Layer* ancestor = this; ancestor; ancestor = ancestor->GetParent()) { const FrameMetrics& metrics = ancestor->GetFrameMetrics(); if (!scrollAncestor && metrics.GetScrollId() != FrameMetrics::NULL_SCROLL_ID) { scrollAncestor = ancestor; } if (!metrics.mDisplayPort.IsEmpty()) { displayPortAncestor = ancestor; // Any layer that has a displayport must be scrollable, so we can break // here. break; } } if (aOutScrollAncestor) { *aOutScrollAncestor = scrollAncestor; } if (aOutDisplayPortAncestor) { *aOutDisplayPortAncestor = displayPortAncestor; } }
static gfx::Matrix4x4 GetTransformToAncestorsParentLayer(Layer* aStart, Layer* aAncestor) { gfx::Matrix4x4 transform; Layer* ancestorParent = aAncestor->GetParent(); for (Layer* iter = aStart; iter != ancestorParent; 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->GetFrameMetrics(); transform = transform * gfx::Matrix4x4().Scale(metrics.mResolution.scale, metrics.mResolution.scale, 1.f); } return transform; }
void ClientTiledThebesLayer::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. Layer* scrollAncestor = nullptr; Layer* displayPortAncestor = nullptr; 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 Thebes layer with no scrollable container ancestor"); #endif return; } TILING_LOG("TILING %p: Found scrollAncestor %p and displayPortAncestor %p\n", this, scrollAncestor, displayPortAncestor); const FrameMetrics& scrollMetrics = scrollAncestor->GetFrameMetrics(); const FrameMetrics& displayportMetrics = displayPortAncestor->GetFrameMetrics(); // 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(); // Note that below we use GetZoomToParent() in a number of places. Because this // code runs on the client side, the mTransformScale field of the FrameMetrics // will not have been set. This can result in incorrect values being returned // by GetZoomToParent() when we have CSS transforms set on some of these layers. // This code should be audited and updated as part of fixing bug 993525. // Compute the critical display port that applies to this layer in the // LayoutDevice space of this layer. ParentLayerRect criticalDisplayPort = (displayportMetrics.mCriticalDisplayPort * displayportMetrics.GetZoomToParent()) + 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.GetZoomToParent(); TILING_LOG("TILING %p: Resolution %f\n", this, mPaintData.mResolution.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.GetZoomToParent(); TILING_LOG("TILING %p: Scroll offset %s\n", this, Stringify(mPaintData.mScrollOffset).c_str()); }
float LayerManagerComposite::ComputeRenderIntegrity() { // We only ever have incomplete rendering when progressive tiles are enabled. Layer* root = GetRoot(); if (!gfxPrefs::UseProgressiveTilePainting() || !root) { return 1.f; } const FrameMetrics& rootMetrics = root->GetFrameMetrics(); ParentLayerIntRect bounds = RoundedToInt(rootMetrics.mCompositionBounds); nsIntRect screenRect(bounds.x, bounds.y, bounds.width, bounds.height); float lowPrecisionMultiplier = 1.0f; float highPrecisionMultiplier = 1.0f; #ifdef MOZ_ANDROID_OMTC // Use the transform on the primary scrollable layer and its FrameMetrics // to find out how much of the viewport the current displayport covers Layer* primaryScrollable = GetPrimaryScrollableLayer(); if (primaryScrollable) { // This is derived from the code in // AsyncCompositionManager::TransformScrollableLayer const FrameMetrics& metrics = primaryScrollable->GetFrameMetrics(); Matrix4x4 transform = primaryScrollable->GetEffectiveTransform(); transform.ScalePost(metrics.mResolution.scale, metrics.mResolution.scale, 1); // Clip the screen rect to the document bounds Rect documentBounds = transform.TransformBounds(Rect(metrics.mScrollableRect.x - metrics.GetScrollOffset().x, metrics.mScrollableRect.y - metrics.GetScrollOffset().y, metrics.mScrollableRect.width, metrics.mScrollableRect.height)); documentBounds.RoundOut(); screenRect = screenRect.Intersect(nsIntRect(documentBounds.x, documentBounds.y, documentBounds.width, documentBounds.height)); // If the screen rect is empty, the user has scrolled entirely into // over-scroll and so we can be considered to have full integrity. if (screenRect.IsEmpty()) { return 1.0f; } // Work out how much of the critical display-port covers the screen bool hasLowPrecision = false; if (!metrics.mCriticalDisplayPort.IsEmpty()) { hasLowPrecision = true; highPrecisionMultiplier = GetDisplayportCoverage(metrics.mCriticalDisplayPort, transform, screenRect); } // Work out how much of the display-port covers the screen if (!metrics.mDisplayPort.IsEmpty()) { if (hasLowPrecision) { lowPrecisionMultiplier = GetDisplayportCoverage(metrics.mDisplayPort, transform, screenRect); } else { lowPrecisionMultiplier = highPrecisionMultiplier = GetDisplayportCoverage(metrics.mDisplayPort, transform, screenRect); } } } // If none of the screen is covered, we have zero integrity. if (highPrecisionMultiplier <= 0.0f && lowPrecisionMultiplier <= 0.0f) { return 0.0f; } #endif // MOZ_ANDROID_OMTC nsIntRegion screenRegion(screenRect); nsIntRegion lowPrecisionScreenRegion(screenRect); Matrix4x4 transform; ComputeRenderIntegrityInternal(root, screenRegion, lowPrecisionScreenRegion, transform); if (!screenRegion.IsEqual(screenRect)) { // Calculate the area of the region. All rects in an nsRegion are // non-overlapping. float screenArea = screenRect.width * screenRect.height; float highPrecisionIntegrity = screenRegion.Area() / screenArea; float lowPrecisionIntegrity = 1.f; if (!lowPrecisionScreenRegion.IsEqual(screenRect)) { lowPrecisionIntegrity = lowPrecisionScreenRegion.Area() / screenArea; } return ((highPrecisionIntegrity * highPrecisionMultiplier) + (lowPrecisionIntegrity * lowPrecisionMultiplier)) / 2; } return 1.f; }