blob: 2ce493472a6a077e0ba7c0f0d1c104d1f9ed32c2 [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/core/paint/compositing/compositing_reason_finder.h"
#include "third_party/blink/renderer/core/animation/scroll_timeline.h"
#include "third_party/blink/renderer/core/css/css_property_names.h"
#include "third_party/blink/renderer/core/dom/document.h"
#include "third_party/blink/renderer/core/frame/local_frame_view.h"
#include "third_party/blink/renderer/core/frame/settings.h"
#include "third_party/blink/renderer/core/layout/layout_view.h"
#include "third_party/blink/renderer/core/page/page.h"
#include "third_party/blink/renderer/core/page/scrolling/root_scroller_util.h"
#include "third_party/blink/renderer/core/paint/paint_layer.h"
#include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h"
#include "third_party/blink/public/platform/platform.h"
namespace blink {
CompositingReasons CompositingReasonFinder::DirectReasons(
const PaintLayer& layer,
bool ignore_lcd_text) {
if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled())
return CompositingReason::kNone;
DCHECK_EQ(PotentialCompositingReasonsFromStyle(layer.GetLayoutObject()),
layer.PotentialCompositingReasonsFromStyle());
CompositingReasons style_determined_direct_compositing_reasons =
layer.PotentialCompositingReasonsFromStyle() &
CompositingReason::kComboAllDirectStyleDeterminedReasons;
return style_determined_direct_compositing_reasons |
NonStyleDeterminedDirectReasons(layer, ignore_lcd_text);
}
bool CompositingReasonFinder::RequiresCompositingForScrollableFrame(
const LayoutView& layout_view) {
// Need this done first to determine overflow.
DCHECK(!layout_view.NeedsLayout());
if (layout_view.GetDocument().IsInMainFrame())
return false;
const auto& settings = *layout_view.GetDocument().GetSettings();
if (!settings.GetPreferCompositingToLCDTextEnabled())
return false;
if (layout_view.GetFrameView()->Size().IsEmpty())
return false;
return layout_view.GetFrameView()->LayoutViewport()->ScrollsOverflow();
}
CompositingReasons
CompositingReasonFinder::PotentialCompositingReasonsFromStyle(
const LayoutObject& layout_object) {
if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled())
return CompositingReason::kNone;
CompositingReasons reasons = CompositingReason::kNone;
const ComputedStyle& style = layout_object.StyleRef();
if (RequiresCompositingForTransform(layout_object))
reasons |= CompositingReason::k3DTransform;
if (style.BackfaceVisibility() == EBackfaceVisibility::kHidden)
reasons |= CompositingReason::kBackfaceVisibilityHidden;
reasons |= CompositingReasonsForAnimation(style);
if (style.HasWillChangeCompositingHint() &&
!style.SubtreeWillChangeContents())
reasons |= CompositingReason::kWillChangeCompositingHint;
if (style.UsedTransformStyle3D() == ETransformStyle3D::kPreserve3d)
reasons |= CompositingReason::kPreserve3DWith3DDescendants;
if (style.HasPerspective())
reasons |= CompositingReason::kPerspectiveWith3DDescendants;
// If the implementation of CreatesGroup changes, we need to be aware of that
// in this part of code.
DCHECK((style.HasOpacity() || layout_object.HasMask() ||
layout_object.HasClipPath() ||
layout_object.HasFilterInducingProperty() || style.HasBlendMode()) ==
layout_object.CreatesGroup());
if (style.HasMask() || style.ClipPath())
reasons |= CompositingReason::kMaskWithCompositedDescendants;
if (style.HasFilterInducingProperty())
reasons |= CompositingReason::kFilterWithCompositedDescendants;
if (style.HasBackdropFilter())
reasons |= CompositingReason::kBackdropFilter;
// See Layer::updateTransform for an explanation of why we check both.
if (layout_object.HasTransformRelatedProperty() && style.HasTransform())
reasons |= CompositingReason::kTransformWithCompositedDescendants;
if (style.HasOpacity())
reasons |= CompositingReason::kOpacityWithCompositedDescendants;
if (style.HasBlendMode())
reasons |= CompositingReason::kBlendingWithCompositedDescendants;
if (layout_object.HasReflection())
reasons |= CompositingReason::kReflectionWithCompositedDescendants;
if (layout_object.HasClipRelatedProperty())
reasons |= CompositingReason::kClipsCompositingDescendants;
DCHECK(!(reasons & ~CompositingReason::kComboAllStyleDeterminedReasons));
return reasons;
}
bool CompositingReasonFinder::RequiresCompositingForTransform(
const LayoutObject& layout_object) {
// Note that we ask the layoutObject if it has a transform, because the style
// may have transforms, but the layoutObject may be an inline that doesn't
// support them.
return layout_object.HasTransformRelatedProperty() &&
layout_object.StyleRef().Has3DTransform() &&
// Don't composite "trivial" 3D transforms such as translateZ(0) on
// low-end devices. These devices are much more sensitive to memory
// and per-composited-layer commit overhead.
(!Platform::Current()->IsLowEndDevice() ||
layout_object.StyleRef().Transform().HasNonTrivial3DComponent());
}
CompositingReasons CompositingReasonFinder::NonStyleDeterminedDirectReasons(
const PaintLayer& layer,
bool ignore_lcd_text) {
CompositingReasons direct_reasons = CompositingReason::kNone;
LayoutObject& layout_object = layer.GetLayoutObject();
// TODO(chrishtr): remove this hammer in favor of something more targeted.
// See crbug.com/749349.
if (layer.ClipParent() && layer.GetLayoutObject().IsOutOfFlowPositioned())
direct_reasons |= CompositingReason::kOutOfFlowClipping;
if (RequiresCompositingForRootScroller(layer))
direct_reasons |= CompositingReason::kRootScroller;
// Composite |layer| if it is inside of an ancestor scrolling layer, but that
// scrolling layer is not on the stacking context ancestor chain of |layer|.
// See the definition of the scrollParent property in Layer for more detail.
if (const PaintLayer* scrolling_ancestor = layer.AncestorScrollingLayer()) {
if (scrolling_ancestor->NeedsCompositedScrolling() && layer.ScrollParent())
direct_reasons |= CompositingReason::kOverflowScrollingParent;
}
if (RequiresCompositingForScrollDependentPosition(layer, ignore_lcd_text))
direct_reasons |= CompositingReason::kScrollDependentPosition;
// TODO(crbug.com/839341): Remove once we support main-thread AnimationWorklet
// and don't need to promote the scroll-source.
if (layer.GetScrollableArea() && layer.GetLayoutObject().GetNode() &&
ScrollTimeline::HasActiveScrollTimeline(
layer.GetLayoutObject().GetNode())) {
direct_reasons |= CompositingReason::kScrollTimelineTarget;
}
// 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 (layer.CompositingContainer() &&
layer.CompositingContainer()->GetLayoutObject().IsVideo())
direct_reasons |= CompositingReason::kVideoOverlay;
if (layer.IsRootLayer() &&
(RequiresCompositingForScrollableFrame(*layout_object.View()) ||
layout_object.GetFrame()->IsLocalRoot())) {
direct_reasons |= CompositingReason::kRoot;
}
direct_reasons |= layout_object.AdditionalCompositingReasons();
DCHECK(
!(direct_reasons & CompositingReason::kComboAllStyleDeterminedReasons));
return direct_reasons;
}
CompositingReasons CompositingReasonFinder::CompositingReasonsForAnimation(
const ComputedStyle& style) {
CompositingReasons reasons = CompositingReason::kNone;
if (RequiresCompositingForTransformAnimation(style))
reasons |= CompositingReason::kActiveTransformAnimation;
if (RequiresCompositingForOpacityAnimation(style))
reasons |= CompositingReason::kActiveOpacityAnimation;
if (RequiresCompositingForFilterAnimation(style))
reasons |= CompositingReason::kActiveFilterAnimation;
if (RequiresCompositingForBackdropFilterAnimation(style))
reasons |= CompositingReason::kActiveBackdropFilterAnimation;
return reasons;
}
bool CompositingReasonFinder::RequiresCompositingForOpacityAnimation(
const ComputedStyle& style) {
return style.SubtreeWillChangeContents()
? style.IsRunningOpacityAnimationOnCompositor()
: style.HasCurrentOpacityAnimation();
}
bool CompositingReasonFinder::RequiresCompositingForFilterAnimation(
const ComputedStyle& style) {
return style.SubtreeWillChangeContents()
? style.IsRunningFilterAnimationOnCompositor()
: style.HasCurrentFilterAnimation();
}
bool CompositingReasonFinder::RequiresCompositingForBackdropFilterAnimation(
const ComputedStyle& style) {
return style.SubtreeWillChangeContents()
? style.IsRunningBackdropFilterAnimationOnCompositor()
: style.HasCurrentBackdropFilterAnimation();
}
bool CompositingReasonFinder::RequiresCompositingForTransformAnimation(
const ComputedStyle& style) {
return style.SubtreeWillChangeContents()
? style.IsRunningTransformAnimationOnCompositor()
: style.HasCurrentTransformAnimation();
}
bool CompositingReasonFinder::RequiresCompositingForRootScroller(
const PaintLayer& layer) {
// The root scroller needs composited scrolling layers even if it doesn't
// actually have scrolling since CC has these assumptions baked in for the
// viewport. Because this is only needed for CC, we can skip it if compositing
// is not enabled.
const auto& settings = *layer.GetLayoutObject().GetDocument().GetSettings();
if (!settings.GetAcceleratedCompositingEnabled())
return false;
return layer.GetLayoutObject().IsGlobalRootScroller();
}
bool CompositingReasonFinder::RequiresCompositingForScrollDependentPosition(
const PaintLayer& layer,
bool ignore_lcd_text) {
const auto& layout_object = layer.GetLayoutObject();
if (!layout_object.StyleRef().HasViewportConstrainedPosition() &&
!layout_object.StyleRef().HasStickyConstrainedPosition())
return false;
const auto& settings = *layout_object.GetDocument().GetSettings();
if (settings.GetPreferCompositingToLCDTextEnabled())
ignore_lcd_text = true;
if (!ignore_lcd_text) {
if (!RuntimeEnabledFeatures::CompositeOpaqueFixedPositionEnabled())
return false;
if (!layer.BackgroundIsKnownToBeOpaqueInRect(
LayoutRect(layer.BoundingBoxForCompositing()), true)) {
return false;
}
if (layer.CompositesWithTransform() || layer.CompositesWithOpacity())
return false;
}
// Don't promote fixed position elements that are descendants of a non-view
// container, e.g. transformed elements. They will stay fixed wrt the
// container rather than the enclosing frame.
EPosition position = layout_object.StyleRef().GetPosition();
if (position == EPosition::kFixed) {
return layer.FixedToViewport() &&
layout_object.GetFrameView()->LayoutViewport()->ScrollsOverflow();
}
DCHECK_EQ(position, EPosition::kSticky);
// Don't promote sticky position elements that cannot move with scrolls.
if (!layer.SticksToScroller())
return false;
return layer.AncestorOverflowLayer()->ScrollsOverflow();
}
} // namespace blink