void DeprecatedPaintLayerPainter::paintChildren(unsigned childrenToVisit, GraphicsContext* context, const DeprecatedPaintLayerPaintingInfo& paintingInfo, PaintLayerFlags paintFlags) { if (!m_paintLayer.hasSelfPaintingLayerDescendant()) return; #if ENABLE(ASSERT) LayerListMutationDetector mutationChecker(m_paintLayer.stackingNode()); #endif IntSize scrollOffsetAccumulation = paintingInfo.scrollOffsetAccumulation; if (m_paintLayer.layoutObject()->hasOverflowClip()) scrollOffsetAccumulation += m_paintLayer.layoutBox()->scrolledContentOffset(); DeprecatedPaintLayerStackingNodeIterator iterator(*m_paintLayer.stackingNode(), childrenToVisit); while (DeprecatedPaintLayerStackingNode* child = iterator.next()) { DeprecatedPaintLayerPainter childPainter(*child->layer()); // If this Layer should paint into its own backing or a grouped backing, that will be done via CompositedDeprecatedPaintLayerMapping::paintContents() // and CompositedDeprecatedPaintLayerMapping::doPaintTask(). if (!childPainter.shouldPaintLayerInSoftwareMode(paintingInfo.globalPaintFlags(), paintFlags)) continue; DeprecatedPaintLayerPaintingInfo childPaintingInfo = paintingInfo; childPaintingInfo.scrollOffsetAccumulation = scrollOffsetAccumulation; // Rare case: accumulate scroll offset of non-stacking-context ancestors up to m_paintLayer. for (DeprecatedPaintLayer* parentLayer = child->layer()->parent(); parentLayer != &m_paintLayer; parentLayer = parentLayer->parent()) { if (parentLayer->layoutObject()->hasOverflowClip()) childPaintingInfo.scrollOffsetAccumulation += parentLayer->layoutBox()->scrolledContentOffset(); } childPainter.paintLayer(context, childPaintingInfo, paintFlags); } }
void GraphicsLayerTreeBuilder::rebuild(PaintLayer& layer, AncestorInfo info) { // Make the layer compositing if necessary, and set up clipping and content layers. // Note that we can only do work here that is independent of whether the descendant layers // have been processed. computeCompositingRequirements() will already have done the paint invalidation if necessary. layer.stackingNode()->updateLayerListsIfNeeded(); const bool hasCompositedLayerMapping = layer.hasCompositedLayerMapping(); CompositedLayerMapping* currentCompositedLayerMapping = layer.compositedLayerMapping(); // If this layer has a compositedLayerMapping, then that is where we place subsequent children GraphicsLayers. // Otherwise children continue to append to the child list of the enclosing layer. GraphicsLayerVector layerChildren; AncestorInfo infoForChildren(info); if (hasCompositedLayerMapping) { infoForChildren.childLayersOfEnclosingCompositedLayer = &layerChildren; infoForChildren.enclosingCompositedLayer = &layer; } #if ENABLE(ASSERT) LayerListMutationDetector mutationChecker(layer.stackingNode()); #endif if (layer.stackingNode()->isStackingContext()) { PaintLayerStackingNodeIterator iterator(*layer.stackingNode(), NegativeZOrderChildren); while (PaintLayerStackingNode* curNode = iterator.next()) rebuild(*curNode->layer(), infoForChildren); // If a negative z-order child is compositing, we get a foreground layer which needs to get parented. if (hasCompositedLayerMapping && currentCompositedLayerMapping->foregroundLayer()) infoForChildren.childLayersOfEnclosingCompositedLayer->append(currentCompositedLayerMapping->foregroundLayer()); } PaintLayerStackingNodeIterator iterator(*layer.stackingNode(), NormalFlowChildren | PositiveZOrderChildren); while (PaintLayerStackingNode* curNode = iterator.next()) rebuild(*curNode->layer(), infoForChildren); if (hasCompositedLayerMapping) { bool parented = false; if (layer.layoutObject()->isLayoutPart()) parented = PaintLayerCompositor::attachFrameContentLayersToIframeLayer(toLayoutPart(layer.layoutObject())); if (!parented) currentCompositedLayerMapping->setSublayers(layerChildren); if (shouldAppendLayer(layer)) info.childLayersOfEnclosingCompositedLayer->append(currentCompositedLayerMapping->childForSuperlayers()); } if (layer.scrollParent() && layer.scrollParent()->hasCompositedLayerMapping() && layer.scrollParent()->compositedLayerMapping()->needsToReparentOverflowControls() && layer.scrollParent()->getScrollableArea()->topmostScrollChild() == &layer) info.childLayersOfEnclosingCompositedLayer->append(layer.scrollParent()->compositedLayerMapping()->detachLayerForOverflowControls(*info.enclosingCompositedLayer)); }
void GraphicsLayerTreeBuilder::rebuild(RenderLayer& layer, GraphicsLayerVector& childLayersOfEnclosingLayer) { // Make the layer compositing if necessary, and set up clipping and content layers. // Note that we can only do work here that is independent of whether the descendant layers // have been processed. computeCompositingRequirements() will already have done the repaint if necessary. layer.stackingNode()->updateLayerListsIfNeeded(); const bool hasCompositedLayerMapping = layer.hasCompositedLayerMapping(); CompositedLayerMappingPtr currentCompositedLayerMapping = layer.compositedLayerMapping(); // If this layer has a compositedLayerMapping, then that is where we place subsequent children GraphicsLayers. // Otherwise children continue to append to the child list of the enclosing layer. GraphicsLayerVector layerChildren; GraphicsLayerVector& childList = hasCompositedLayerMapping ? layerChildren : childLayersOfEnclosingLayer; #if !ASSERT_DISABLED LayerListMutationDetector mutationChecker(layer.stackingNode()); #endif if (layer.stackingNode()->isStackingContext()) { RenderLayerStackingNodeIterator iterator(*layer.stackingNode(), NegativeZOrderChildren); while (RenderLayerStackingNode* curNode = iterator.next()) rebuild(*curNode->layer(), childList); // If a negative z-order child is compositing, we get a foreground layer which needs to get parented. if (hasCompositedLayerMapping && currentCompositedLayerMapping->foregroundLayer()) childList.append(currentCompositedLayerMapping->foregroundLayer()); } RenderLayerStackingNodeIterator iterator(*layer.stackingNode(), NormalFlowChildren | PositiveZOrderChildren); while (RenderLayerStackingNode* curNode = iterator.next()) rebuild(*curNode->layer(), childList); if (hasCompositedLayerMapping) { bool parented = false; if (layer.renderer()->isRenderPart()) parented = RenderLayerCompositor::parentFrameContentLayers(toRenderPart(layer.renderer())); if (!parented) currentCompositedLayerMapping->parentForSublayers()->setChildren(layerChildren); // If the layer has a clipping layer the overflow controls layers will be siblings of the clipping layer. // Otherwise, the overflow control layers are normal children. if (!currentCompositedLayerMapping->hasClippingLayer() && !currentCompositedLayerMapping->hasScrollingLayer()) { if (GraphicsLayer* overflowControlLayer = currentCompositedLayerMapping->layerForHorizontalScrollbar()) { overflowControlLayer->removeFromParent(); currentCompositedLayerMapping->parentForSublayers()->addChild(overflowControlLayer); } if (GraphicsLayer* overflowControlLayer = currentCompositedLayerMapping->layerForVerticalScrollbar()) { overflowControlLayer->removeFromParent(); currentCompositedLayerMapping->parentForSublayers()->addChild(overflowControlLayer); } if (GraphicsLayer* overflowControlLayer = currentCompositedLayerMapping->layerForScrollCorner()) { overflowControlLayer->removeFromParent(); currentCompositedLayerMapping->parentForSublayers()->addChild(overflowControlLayer); } } if (shouldAppendLayer(layer)) childLayersOfEnclosingLayer.append(currentCompositedLayerMapping->childForSuperlayers()); } }
void CompositingRequirementsUpdater::updateRecursive(PaintLayer* ancestorLayer, PaintLayer* layer, OverlapMap& overlapMap, RecursionData& currentRecursionData, bool& descendantHas3DTransform, Vector<PaintLayer*>& unclippedDescendants, IntRect& absoluteDescendantBoundingBox) { PaintLayerCompositor* compositor = m_layoutView.compositor(); layer->stackingNode()->updateLayerListsIfNeeded(); CompositingReasons reasonsToComposite = CompositingReasonNone; CompositingReasons directReasons = m_compositingReasonFinder.directReasons(layer); // Video is special. It's the only PaintLayer type that can both have // PaintLayer children and whose children can't use its backing to render // into. These children (the controls) always need to be promoted into their // own layers to draw on top of the accelerated video. if (currentRecursionData.m_compositingAncestor && currentRecursionData.m_compositingAncestor->layoutObject()->isVideo()) directReasons |= CompositingReasonVideoOverlay; if (currentRecursionData.m_hasCompositedScrollingAncestor && layer->layoutObject()->styleRef().hasViewportConstrainedPosition()) directReasons |= CompositingReasonPositionFixed; bool canBeComposited = compositor->canBeComposited(layer); if (canBeComposited) { reasonsToComposite |= directReasons; if (layer->isRootLayer() && compositor->rootShouldAlwaysComposite()) reasonsToComposite |= CompositingReasonRoot; if (reasonsToComposite && layer->scrollsOverflow() && !layer->needsCompositedScrolling()) { // We will only set needsCompositedScrolling if we don't care about // the LCD text hit, we may be able to switch to the compositor // driven path if we're alread composited for other reasons and are // therefore using grayscale AA. // // FIXME: it should also be possible to promote if the layer can // still use LCD text when promoted, but detecting when the // compositor can do this is tricky. Currently, the layer must be // both opaque and may only have an integer translation as its // transform. Both opacity and screen space transform are inherited // properties, so this cannot be determined from local information. layer->scrollableArea()->updateNeedsCompositedScrolling(PaintLayerScrollableArea::IgnoreLCDText); if (layer->needsCompositedScrolling()) reasonsToComposite |= CompositingReasonOverflowScrollingTouch; } } if ((reasonsToComposite & CompositingReasonOverflowScrollingTouch) && !layer->isRootLayer()) currentRecursionData.m_hasCompositedScrollingAncestor = true; // Next, accumulate reasons related to overlap. // If overlap testing is used, this reason will be overridden. If overlap testing is not // used, we must assume we overlap if there is anything composited behind us in paint-order. CompositingReasons overlapCompositingReason = currentRecursionData.m_subtreeIsCompositing ? CompositingReasonAssumedOverlap : CompositingReasonNone; if (currentRecursionData.m_hasCompositedScrollingAncestor) { Vector<size_t> unclippedDescendantsToRemove; for (size_t i = 0; i < unclippedDescendants.size(); i++) { PaintLayer* unclippedDescendant = unclippedDescendants.at(i); // If we've reached the containing block of one of the unclipped // descendants, that element is no longer relevant to whether or not we // should opt in. Unfortunately we can't easily remove from the list // while we're iterating, so we have to store it for later removal. if (unclippedDescendant->layoutObject()->containingBlock() == layer->layoutObject()) { unclippedDescendantsToRemove.append(i); continue; } if (layer->scrollsWithRespectTo(unclippedDescendant)) reasonsToComposite |= CompositingReasonAssumedOverlap; } // Remove irrelevant unclipped descendants in reverse order so our stored // indices remain valid. for (size_t i = 0; i < unclippedDescendantsToRemove.size(); i++) unclippedDescendants.remove(unclippedDescendantsToRemove.at(unclippedDescendantsToRemove.size() - i - 1)); if (reasonsToComposite & CompositingReasonOutOfFlowClipping) unclippedDescendants.append(layer); } const IntRect& absBounds = layer->clippedAbsoluteBoundingBox(); absoluteDescendantBoundingBox = absBounds; if (currentRecursionData.m_testingOverlap && !requiresCompositingOrSquashing(directReasons)) overlapCompositingReason = overlapMap.overlapsLayers(absBounds) ? CompositingReasonOverlap : CompositingReasonNone; reasonsToComposite |= overlapCompositingReason; // The children of this layer don't need to composite, unless there is // a compositing layer among them, so start by inheriting the compositing // ancestor with m_subtreeIsCompositing set to false. RecursionData childRecursionData = currentRecursionData; childRecursionData.m_subtreeIsCompositing = false; bool willBeCompositedOrSquashed = canBeComposited && requiresCompositingOrSquashing(reasonsToComposite); if (willBeCompositedOrSquashed) { // This layer now acts as the ancestor for kids. childRecursionData.m_compositingAncestor = layer; // Here we know that all children and the layer's own contents can blindly paint into // this layer's backing, until a descendant is composited. So, we don't need to check // for overlap with anything behind this layer. overlapMap.beginNewOverlapTestingContext(); // This layer is going to be composited, so children can safely ignore the fact that there's an // animation running behind this layer, meaning they can rely on the overlap map testing again. childRecursionData.m_testingOverlap = true; } #if ENABLE(ASSERT) LayerListMutationDetector mutationChecker(layer->stackingNode()); #endif bool anyDescendantHas3DTransform = false; bool willHaveForegroundLayer = false; if (layer->stackingNode()->isStackingContext()) { PaintLayerStackingNodeIterator iterator(*layer->stackingNode(), NegativeZOrderChildren); while (PaintLayerStackingNode* curNode = iterator.next()) { IntRect absoluteChildDescendantBoundingBox; updateRecursive(layer, curNode->layer(), overlapMap, childRecursionData, anyDescendantHas3DTransform, unclippedDescendants, absoluteChildDescendantBoundingBox); absoluteDescendantBoundingBox.unite(absoluteChildDescendantBoundingBox); // If we have to make a layer for this child, make one now so we can have a contents layer // (since we need to ensure that the -ve z-order child renders underneath our contents). if (childRecursionData.m_subtreeIsCompositing) { reasonsToComposite |= CompositingReasonNegativeZIndexChildren; if (!willBeCompositedOrSquashed) { // make layer compositing childRecursionData.m_compositingAncestor = layer; overlapMap.beginNewOverlapTestingContext(); willBeCompositedOrSquashed = true; willHaveForegroundLayer = true; // FIXME: temporary solution for the first negative z-index composited child: // re-compute the absBounds for the child so that we can add the // negative z-index child's bounds to the new overlap context. overlapMap.beginNewOverlapTestingContext(); overlapMap.add(curNode->layer(), curNode->layer()->clippedAbsoluteBoundingBox()); overlapMap.finishCurrentOverlapTestingContext(); } } } } if (willHaveForegroundLayer) { ASSERT(willBeCompositedOrSquashed); // A foreground layer effectively is a new backing for all subsequent children, so // we don't need to test for overlap with anything behind this. So, we can finish // the previous context that was accumulating rects for the negative z-index // children, and start with a fresh new empty context. overlapMap.finishCurrentOverlapTestingContext(); overlapMap.beginNewOverlapTestingContext(); // This layer is going to be composited, so children can safely ignore the fact that there's an // animation running behind this layer, meaning they can rely on the overlap map testing again childRecursionData.m_testingOverlap = true; } PaintLayerStackingNodeIterator iterator(*layer->stackingNode(), NormalFlowChildren | PositiveZOrderChildren); while (PaintLayerStackingNode* curNode = iterator.next()) { IntRect absoluteChildDescendantBoundingBox; updateRecursive(layer, curNode->layer(), overlapMap, childRecursionData, anyDescendantHas3DTransform, unclippedDescendants, absoluteChildDescendantBoundingBox); absoluteDescendantBoundingBox.unite(absoluteChildDescendantBoundingBox); } // Now that the subtree has been traversed, we can check for compositing reasons that depended on the state of the subtree. if (layer->stackingNode()->isStackingContext()) { layer->setShouldIsolateCompositedDescendants(childRecursionData.m_hasUnisolatedCompositedBlendingDescendant); } else { layer->setShouldIsolateCompositedDescendants(false); currentRecursionData.m_hasUnisolatedCompositedBlendingDescendant = childRecursionData.m_hasUnisolatedCompositedBlendingDescendant; } // Subsequent layers in the parent's stacking context may also need to composite. if (childRecursionData.m_subtreeIsCompositing) currentRecursionData.m_subtreeIsCompositing = true; // Set the flag to say that this SC has compositing children. layer->setHasCompositingDescendant(childRecursionData.m_subtreeIsCompositing); if (layer->isRootLayer()) { // The root layer needs to be composited if anything else in the tree is composited. // Otherwise, we can disable compositing entirely. if (childRecursionData.m_subtreeIsCompositing || requiresCompositingOrSquashing(reasonsToComposite) || compositor->rootShouldAlwaysComposite()) { reasonsToComposite |= CompositingReasonRoot; currentRecursionData.m_subtreeIsCompositing = true; } else { compositor->setCompositingModeEnabled(false); reasonsToComposite = CompositingReasonNone; } } else { // All layers (even ones that aren't being composited) need to get added to // the overlap map. Layers that are not separately composited will paint into their // compositing ancestor's backing, and so are still considered for overlap. if (childRecursionData.m_compositingAncestor && !childRecursionData.m_compositingAncestor->isRootLayer()) overlapMap.add(layer, absBounds); // Now check for reasons to become composited that depend on the state of descendant layers. CompositingReasons subtreeCompositingReasons = subtreeReasonsForCompositing(layer, childRecursionData.m_subtreeIsCompositing, anyDescendantHas3DTransform); reasonsToComposite |= subtreeCompositingReasons; if (!willBeCompositedOrSquashed && canBeComposited && requiresCompositingOrSquashing(subtreeCompositingReasons)) { childRecursionData.m_compositingAncestor = layer; // FIXME: this context push is effectively a no-op but needs to exist for // now, because the code is designed to push overlap information to the // second-from-top context of the stack. overlapMap.beginNewOverlapTestingContext(); overlapMap.add(layer, absoluteDescendantBoundingBox); willBeCompositedOrSquashed = true; } if (willBeCompositedOrSquashed) reasonsToComposite |= layer->potentialCompositingReasonsFromStyle() & CompositingReasonInlineTransform; // If the original layer is composited, the reflection needs to be, too. if (layer->reflectionInfo()) { // FIXME: Shouldn't we call computeCompositingRequirements to handle a reflection overlapping with another layoutObject? PaintLayer* reflectionLayer = layer->reflectionInfo()->reflectionLayer(); CompositingReasons reflectionCompositingReason = willBeCompositedOrSquashed ? CompositingReasonReflectionOfCompositedParent : CompositingReasonNone; reflectionLayer->setCompositingReasons(reflectionCompositingReason, CompositingReasonReflectionOfCompositedParent); } if (willBeCompositedOrSquashed && layer->layoutObject()->style()->hasBlendMode()) currentRecursionData.m_hasUnisolatedCompositedBlendingDescendant = true; // Tell the parent it has compositing descendants. if (willBeCompositedOrSquashed) currentRecursionData.m_subtreeIsCompositing = true; // Turn overlap testing off for later layers if it's already off, or if we have an animating transform. // Note that if the layer clips its descendants, there's no reason to propagate the child animation to the parent layers. That's because // we know for sure the animation is contained inside the clipping rectangle, which is already added to the overlap map. bool isCompositedClippingLayer = canBeComposited && (reasonsToComposite & CompositingReasonClipsCompositingDescendants); bool isCompositedWithInlineTransform = reasonsToComposite & CompositingReasonInlineTransform; if ((!childRecursionData.m_testingOverlap && !isCompositedClippingLayer) || layer->layoutObject()->style()->hasCurrentTransformAnimation() || isCompositedWithInlineTransform) currentRecursionData.m_testingOverlap = false; if (childRecursionData.m_compositingAncestor == layer) overlapMap.finishCurrentOverlapTestingContext(); descendantHas3DTransform |= anyDescendantHas3DTransform || layer->has3DTransform(); } // At this point we have finished collecting all reasons to composite this layer. layer->setCompositingReasons(reasonsToComposite); }
PaintLayerPainter::PaintResult PaintLayerPainter::paintChildren(unsigned childrenToVisit, GraphicsContext* context, const PaintLayerPaintingInfo& paintingInfo, PaintLayerFlags paintFlags) { PaintResult result = FullyPainted; if (!m_paintLayer.hasSelfPaintingLayerDescendant()) return result; #if ENABLE(ASSERT) LayerListMutationDetector mutationChecker(m_paintLayer.stackingNode()); #endif PaintLayerStackingNodeIterator iterator(*m_paintLayer.stackingNode(), childrenToVisit); PaintLayerStackingNode* child = iterator.next(); if (!child) return result; DisplayItem::Type subsequenceType; if (childrenToVisit == NegativeZOrderChildren) { subsequenceType = DisplayItem::SubsequenceNegativeZOrder; } else { ASSERT(childrenToVisit == (NormalFlowChildren | PositiveZOrderChildren)); subsequenceType = DisplayItem::SubsequenceNormalFlowAndPositiveZOrder; } Optional<SubsequenceRecorder> subsequenceRecorder; if (!paintingInfo.disableSubsequenceCache && !(paintingInfo.globalPaintFlags() & GlobalPaintFlattenCompositingLayers) && !(paintFlags & PaintLayerPaintingReflection) && !(paintFlags & PaintLayerPaintingRootBackgroundOnly)) { if (!m_paintLayer.needsRepaint() && paintingInfo.scrollOffsetAccumulation == m_paintLayer.previousScrollOffsetAccumulationForPainting() && SubsequenceRecorder::useCachedSubsequenceIfPossible(*context, m_paintLayer, subsequenceType)) return result; subsequenceRecorder.emplace(*context, m_paintLayer, subsequenceType); } IntSize scrollOffsetAccumulationForChildren = paintingInfo.scrollOffsetAccumulation; if (m_paintLayer.layoutObject()->hasOverflowClip()) scrollOffsetAccumulationForChildren += m_paintLayer.layoutBox()->scrolledContentOffset(); bool disableChildSubsequenceCache = !RuntimeEnabledFeatures::slimmingPaintV2Enabled() && (m_paintLayer.layoutObject()->hasOverflowClip() || m_paintLayer.layoutObject()->hasClip()); for (; child; child = iterator.next()) { PaintLayerPainter childPainter(*child->layer()); // If this Layer should paint into its own backing or a grouped backing, that will be done via CompositedLayerMapping::paintContents() // and CompositedLayerMapping::doPaintTask(). if (!childPainter.shouldPaintLayerInSoftwareMode(paintingInfo.globalPaintFlags(), paintFlags)) continue; PaintLayerPaintingInfo childPaintingInfo = paintingInfo; childPaintingInfo.disableSubsequenceCache = disableChildSubsequenceCache; childPaintingInfo.scrollOffsetAccumulation = scrollOffsetAccumulationForChildren; // Rare case: accumulate scroll offset of non-stacking-context ancestors up to m_paintLayer. for (PaintLayer* parentLayer = child->layer()->parent(); parentLayer != &m_paintLayer; parentLayer = parentLayer->parent()) { if (parentLayer->layoutObject()->hasOverflowClip()) childPaintingInfo.scrollOffsetAccumulation += parentLayer->layoutBox()->scrolledContentOffset(); } if (childPainter.paintLayer(context, childPaintingInfo, paintFlags) == MaybeNotFullyPainted) result = MaybeNotFullyPainted; } // Set subsequence not cacheable if the bounding box of this layer and descendants is not fully contained // by paintRect, because later paintRect changes may expose new contents which will need repainting. if (result == MaybeNotFullyPainted && subsequenceRecorder) subsequenceRecorder->setUncacheable(); return result; }