blob: 44727edf222c79faa5fc90060f6f76b9bf2608f8 [file] [log] [blame]
/*
* Copyright (C) 2009, 2010 Apple Inc. All rights reserved.
* Copyright (C) 2014 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "core/rendering/compositing/CompositingLayerAssigner.h"
#include "core/inspector/InspectorTraceEvents.h"
#include "core/rendering/compositing/CompositedLayerMapping.h"
#include "platform/TraceEvent.h"
namespace blink {
// We will only allow squashing if the bbox-area:squashed-area doesn't exceed
// the ratio |gSquashingSparsityTolerance|:1.
static uint64_t gSquashingSparsityTolerance = 6;
CompositingLayerAssigner::CompositingLayerAssigner(RenderLayerCompositor* compositor)
: m_compositor(compositor)
, m_layerSquashingEnabled(compositor->layerSquashingEnabled())
, m_layersChanged(false)
{
}
CompositingLayerAssigner::~CompositingLayerAssigner()
{
}
void CompositingLayerAssigner::assign(RenderLayer* updateRoot, Vector<RenderLayer*>& layersNeedingPaintInvalidation)
{
TRACE_EVENT0("blink", "CompositingLayerAssigner::assign");
SquashingState squashingState;
assignLayersToBackingsInternal(updateRoot, squashingState, layersNeedingPaintInvalidation);
if (squashingState.hasMostRecentMapping)
squashingState.mostRecentMapping->finishAccumulatingSquashingLayers(squashingState.nextSquashedLayerIndex);
}
void CompositingLayerAssigner::SquashingState::updateSquashingStateForNewMapping(CompositedLayerMapping* newCompositedLayerMapping, bool hasNewCompositedLayerMapping)
{
// The most recent backing is done accumulating any more squashing layers.
if (hasMostRecentMapping)
mostRecentMapping->finishAccumulatingSquashingLayers(nextSquashedLayerIndex);
nextSquashedLayerIndex = 0;
boundingRect = IntRect();
mostRecentMapping = newCompositedLayerMapping;
hasMostRecentMapping = hasNewCompositedLayerMapping;
haveAssignedBackingsToEntireSquashingLayerSubtree = false;
}
bool CompositingLayerAssigner::squashingWouldExceedSparsityTolerance(const RenderLayer* candidate, const CompositingLayerAssigner::SquashingState& squashingState)
{
IntRect bounds = candidate->clippedAbsoluteBoundingBox();
IntRect newBoundingRect = squashingState.boundingRect;
newBoundingRect.unite(bounds);
const uint64_t newBoundingRectArea = newBoundingRect.size().area();
const uint64_t newSquashedArea = squashingState.totalAreaOfSquashedRects + bounds.size().area();
return newBoundingRectArea > gSquashingSparsityTolerance * newSquashedArea;
}
bool CompositingLayerAssigner::needsOwnBacking(const RenderLayer* layer) const
{
if (!m_compositor->canBeComposited(layer))
return false;
// If squashing is disabled, then layers that would have been squashed should just be separately composited.
bool needsOwnBackingForDisabledSquashing = !m_layerSquashingEnabled && requiresSquashing(layer->compositingReasons());
return requiresCompositing(layer->compositingReasons()) || needsOwnBackingForDisabledSquashing || (m_compositor->staleInCompositingMode() && layer->isRootLayer());
}
CompositingStateTransitionType CompositingLayerAssigner::computeCompositedLayerUpdate(RenderLayer* layer)
{
CompositingStateTransitionType update = NoCompositingStateChange;
if (needsOwnBacking(layer)) {
if (!layer->hasCompositedLayerMapping()) {
update = AllocateOwnCompositedLayerMapping;
}
} else {
if (layer->hasCompositedLayerMapping())
update = RemoveOwnCompositedLayerMapping;
if (m_layerSquashingEnabled) {
if (!layer->subtreeIsInvisible() && requiresSquashing(layer->compositingReasons())) {
// We can't compute at this time whether the squashing layer update is a no-op,
// since that requires walking the render layer tree.
update = PutInSquashingLayer;
} else if (layer->groupedMapping() || layer->lostGroupedMapping()) {
update = RemoveFromSquashingLayer;
}
}
}
return update;
}
CompositingReasons CompositingLayerAssigner::getReasonsPreventingSquashing(const RenderLayer* layer, const CompositingLayerAssigner::SquashingState& squashingState)
{
if (!squashingState.haveAssignedBackingsToEntireSquashingLayerSubtree)
return CompositingReasonSquashingWouldBreakPaintOrder;
ASSERT(squashingState.hasMostRecentMapping);
const RenderLayer& squashingLayer = squashingState.mostRecentMapping->owningLayer();
// FIXME: this special case for video exists only to deal with corner cases
// where a RenderVideo does not report that it needs to be directly composited.
// Video does not currently support sharing a backing, but this could be
// generalized in the future. The following layout tests fail if we permit the
// video to share a backing with other layers.
//
// compositing/video/video-controls-layer-creation.html
if (layer->renderer()->isVideo() || squashingLayer.renderer()->isVideo())
return CompositingReasonSquashingVideoIsDisallowed;
// Don't squash iframes, frames or plugins.
// FIXME: this is only necessary because there is frame code that assumes that composited frames are not squashed.
if (layer->renderer()->isRenderPart() || squashingLayer.renderer()->isRenderPart())
return CompositingReasonSquashingRenderPartIsDisallowed;
if (layer->reflectionInfo())
return CompositingReasonSquashingReflectionIsDisallowed;
if (squashingWouldExceedSparsityTolerance(layer, squashingState))
return CompositingReasonSquashingSparsityExceeded;
if (layer->renderer()->hasBlendMode())
return CompositingReasonSquashingBlendingIsDisallowed;
// FIXME: this is not efficient, since it walks up the tree. We should store these values on the CompositingInputsCache.
if (layer->clippingContainer() != squashingLayer.clippingContainer() && !squashingLayer.compositedLayerMapping()->containingSquashedLayer(layer->clippingContainer(), squashingState.nextSquashedLayerIndex))
return CompositingReasonSquashingClippingContainerMismatch;
// Composited descendants need to be clipped by a child containment graphics layer, which would not be available if the layer is
// squashed (and therefore has no CLM nor a child containment graphics layer).
if (m_compositor->clipsCompositingDescendants(layer))
return CompositingReasonSquashedLayerClipsCompositingDescendants;
if (layer->scrollsWithRespectTo(&squashingLayer))
return CompositingReasonScrollsWithRespectToSquashingLayer;
const RenderLayer::AncestorDependentCompositingInputs& compositingInputs = layer->ancestorDependentCompositingInputs();
const RenderLayer::AncestorDependentCompositingInputs& squashingLayerCompositingInputs = squashingLayer.ancestorDependentCompositingInputs();
if (compositingInputs.opacityAncestor != squashingLayerCompositingInputs.opacityAncestor)
return CompositingReasonSquashingOpacityAncestorMismatch;
if (compositingInputs.transformAncestor != squashingLayerCompositingInputs.transformAncestor)
return CompositingReasonSquashingTransformAncestorMismatch;
if (layer->hasFilter() || compositingInputs.filterAncestor != squashingLayerCompositingInputs.filterAncestor)
return CompositingReasonSquashingFilterMismatch;
return CompositingReasonNone;
}
void CompositingLayerAssigner::updateSquashingAssignment(RenderLayer* layer, SquashingState& squashingState, const CompositingStateTransitionType compositedLayerUpdate,
Vector<RenderLayer*>& layersNeedingPaintInvalidation)
{
// NOTE: In the future as we generalize this, the background of this layer may need to be assigned to a different backing than
// the squashed RenderLayer's own primary contents. This would happen when we have a composited negative z-index element that needs
// to paint on top of the background, but below the layer's main contents. For now, because we always composite layers
// when they have a composited negative z-index child, such layers will never need squashing so it is not yet an issue.
if (compositedLayerUpdate == PutInSquashingLayer) {
// A layer that is squashed with other layers cannot have its own CompositedLayerMapping.
ASSERT(!layer->hasCompositedLayerMapping());
ASSERT(squashingState.hasMostRecentMapping);
bool changedSquashingLayer =
squashingState.mostRecentMapping->updateSquashingLayerAssignment(layer, squashingState.mostRecentMapping->owningLayer(), squashingState.nextSquashedLayerIndex);
if (!changedSquashingLayer)
return;
// If we've modified the collection of squashed layers, we must update
// the graphics layer geometry.
squashingState.mostRecentMapping->setNeedsGraphicsLayerUpdate(GraphicsLayerUpdateSubtree);
layer->clipper().clearClipRectsIncludingDescendants();
// Issue a paint invalidation, since |layer| may have been added to an already-existing squashing layer.
TRACE_LAYER_INVALIDATION(layer, InspectorLayerInvalidationTrackingEvent::AddedToSquashingLayer);
layersNeedingPaintInvalidation.append(layer);
m_layersChanged = true;
} else if (compositedLayerUpdate == RemoveFromSquashingLayer) {
if (layer->groupedMapping()) {
// Before removing |layer| from an already-existing squashing layer that may have other content, issue a paint invalidation.
m_compositor->paintInvalidationOnCompositingChange(layer);
layer->groupedMapping()->setNeedsGraphicsLayerUpdate(GraphicsLayerUpdateSubtree);
layer->setGroupedMapping(0);
}
// If we need to issue paint invalidations, do so now that we've removed it from a squashed layer.
TRACE_LAYER_INVALIDATION(layer, InspectorLayerInvalidationTrackingEvent::RemovedFromSquashingLayer);
layersNeedingPaintInvalidation.append(layer);
m_layersChanged = true;
layer->setLostGroupedMapping(false);
}
}
void CompositingLayerAssigner::assignLayersToBackingsForReflectionLayer(RenderLayer* reflectionLayer, Vector<RenderLayer*>& layersNeedingPaintInvalidation)
{
CompositingStateTransitionType compositedLayerUpdate = computeCompositedLayerUpdate(reflectionLayer);
if (compositedLayerUpdate != NoCompositingStateChange) {
TRACE_LAYER_INVALIDATION(reflectionLayer, InspectorLayerInvalidationTrackingEvent::ReflectionLayerChanged);
layersNeedingPaintInvalidation.append(reflectionLayer);
m_layersChanged = true;
m_compositor->allocateOrClearCompositedLayerMapping(reflectionLayer, compositedLayerUpdate);
}
m_compositor->updateDirectCompositingReasons(reflectionLayer);
// FIXME: Why do we updateGraphicsLayerConfiguration here instead of in the GraphicsLayerUpdater?
if (reflectionLayer->hasCompositedLayerMapping())
reflectionLayer->compositedLayerMapping()->updateGraphicsLayerConfiguration();
}
void CompositingLayerAssigner::assignLayersToBackingsInternal(RenderLayer* layer, SquashingState& squashingState, Vector<RenderLayer*>& layersNeedingPaintInvalidation)
{
if (m_layerSquashingEnabled && requiresSquashing(layer->compositingReasons())) {
CompositingReasons reasonsPreventingSquashing = getReasonsPreventingSquashing(layer, squashingState);
if (reasonsPreventingSquashing)
layer->setCompositingReasons(layer->compositingReasons() | reasonsPreventingSquashing);
}
CompositingStateTransitionType compositedLayerUpdate = computeCompositedLayerUpdate(layer);
if (m_compositor->allocateOrClearCompositedLayerMapping(layer, compositedLayerUpdate)) {
TRACE_LAYER_INVALIDATION(layer, InspectorLayerInvalidationTrackingEvent::NewCompositedLayer);
layersNeedingPaintInvalidation.append(layer);
m_layersChanged = true;
}
// FIXME: special-casing reflection layers here is not right.
if (layer->reflectionInfo())
assignLayersToBackingsForReflectionLayer(layer->reflectionInfo()->reflectionLayer(), layersNeedingPaintInvalidation);
// Add this layer to a squashing backing if needed.
if (m_layerSquashingEnabled) {
updateSquashingAssignment(layer, squashingState, compositedLayerUpdate, layersNeedingPaintInvalidation);
const bool layerIsSquashed = compositedLayerUpdate == PutInSquashingLayer || (compositedLayerUpdate == NoCompositingStateChange && layer->groupedMapping());
if (layerIsSquashed) {
squashingState.nextSquashedLayerIndex++;
IntRect layerBounds = layer->clippedAbsoluteBoundingBox();
squashingState.totalAreaOfSquashedRects += layerBounds.size().area();
squashingState.boundingRect.unite(layerBounds);
}
}
if (layer->stackingNode()->isStackingContext()) {
RenderLayerStackingNodeIterator iterator(*layer->stackingNode(), NegativeZOrderChildren);
while (RenderLayerStackingNode* curNode = iterator.next())
assignLayersToBackingsInternal(curNode->layer(), squashingState, layersNeedingPaintInvalidation);
}
if (m_layerSquashingEnabled) {
// At this point, if the layer is to be separately composited, then its backing becomes the most recent in paint-order.
if (layer->compositingState() == PaintsIntoOwnBacking) {
ASSERT(!requiresSquashing(layer->compositingReasons()));
squashingState.updateSquashingStateForNewMapping(layer->compositedLayerMapping(), layer->hasCompositedLayerMapping());
}
}
if (layer->scrollParent())
layer->scrollParent()->scrollableArea()->setTopmostScrollChild(layer);
if (layer->needsCompositedScrolling())
layer->scrollableArea()->setTopmostScrollChild(0);
RenderLayerStackingNodeIterator iterator(*layer->stackingNode(), NormalFlowChildren | PositiveZOrderChildren);
while (RenderLayerStackingNode* curNode = iterator.next())
assignLayersToBackingsInternal(curNode->layer(), squashingState, layersNeedingPaintInvalidation);
if (squashingState.hasMostRecentMapping && &squashingState.mostRecentMapping->owningLayer() == layer)
squashingState.haveAssignedBackingsToEntireSquashingLayerSubtree = true;
}
}