Home | History | Annotate | Download | only in compositing
      1 // Copyright 2014 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "config.h"
      6 #include "core/rendering/compositing/CompositingReasonFinder.h"
      7 
      8 #include "core/frame/FrameView.h"
      9 #include "core/frame/Settings.h"
     10 #include "core/page/Page.h"
     11 #include "core/rendering/RenderView.h"
     12 #include "core/rendering/compositing/RenderLayerCompositor.h"
     13 
     14 namespace WebCore {
     15 
     16 CompositingReasonFinder::CompositingReasonFinder(RenderView& renderView)
     17     : m_renderView(renderView)
     18     , m_compositingTriggers(static_cast<CompositingTriggerFlags>(AllCompositingTriggers))
     19 {
     20     updateTriggers();
     21 }
     22 
     23 void CompositingReasonFinder::updateTriggers()
     24 {
     25     m_compositingTriggers = 0;
     26 
     27     Settings& settings = m_renderView.document().page()->settings();
     28     if (settings.acceleratedCompositingForVideoEnabled())
     29         m_compositingTriggers |= VideoTrigger;
     30     if (settings.acceleratedCompositingForCanvasEnabled())
     31         m_compositingTriggers |= CanvasTrigger;
     32     if (settings.compositedScrollingForFramesEnabled())
     33         m_compositingTriggers |= ScrollableInnerFrameTrigger;
     34     if (settings.acceleratedCompositingForFiltersEnabled())
     35         m_compositingTriggers |= FilterTrigger;
     36 
     37     // We map both these settings to universal overlow scrolling.
     38     // FIXME: Replace these settings with a generic compositing setting for HighDPI.
     39     if (settings.acceleratedCompositingForOverflowScrollEnabled() || settings.compositorDrivenAcceleratedScrollingEnabled())
     40         m_compositingTriggers |= OverflowScrollTrigger;
     41 
     42     // FIXME: acceleratedCompositingForFixedPositionEnabled should be renamed acceleratedCompositingForViewportConstrainedPositionEnabled().
     43     // Or the sticky and fixed position elements should be behind different flags.
     44     if (settings.acceleratedCompositingForFixedPositionEnabled())
     45         m_compositingTriggers |= ViewportConstrainedPositionedTrigger;
     46 }
     47 
     48 bool CompositingReasonFinder::hasOverflowScrollTrigger() const
     49 {
     50     return m_compositingTriggers & OverflowScrollTrigger;
     51 }
     52 
     53 bool CompositingReasonFinder::isMainFrame() const
     54 {
     55     // FIXME: LocalFrame::isMainFrame() is probably better.
     56     return !m_renderView.document().ownerElement();
     57 }
     58 
     59 CompositingReasons CompositingReasonFinder::directReasons(const RenderLayer* layer) const
     60 {
     61     CompositingReasons styleReasons = layer->styleDeterminedCompositingReasons();
     62     ASSERT(styleDeterminedReasons(layer->renderer()) == styleReasons);
     63     return styleReasons | nonStyleDeterminedDirectReasons(layer);
     64 }
     65 
     66 // This information doesn't appear to be incorporated into CompositingReasons.
     67 bool CompositingReasonFinder::requiresCompositingForScrollableFrame() const
     68 {
     69     // Need this done first to determine overflow.
     70     ASSERT(!m_renderView.needsLayout());
     71     if (isMainFrame())
     72         return false;
     73 
     74     if (!(m_compositingTriggers & ScrollableInnerFrameTrigger))
     75         return false;
     76 
     77     return m_renderView.frameView()->isScrollable();
     78 }
     79 
     80 CompositingReasons CompositingReasonFinder::styleDeterminedReasons(RenderObject* renderer) const
     81 {
     82     CompositingReasons directReasons = CompositingReasonNone;
     83 
     84     RenderStyle* style = renderer->style();
     85 
     86     if (requiresCompositingForTransform(renderer))
     87         directReasons |= CompositingReason3DTransform;
     88 
     89     if (requiresCompositingForFilters(renderer))
     90         directReasons |= CompositingReasonFilters;
     91 
     92     if (style->backfaceVisibility() == BackfaceVisibilityHidden)
     93         directReasons |= CompositingReasonBackfaceVisibilityHidden;
     94 
     95     if (requiresCompositingForAnimation(style))
     96         directReasons |= CompositingReasonActiveAnimation;
     97 
     98     if (style->hasWillChangeCompositingHint() && !style->subtreeWillChangeContents())
     99         directReasons |= CompositingReasonWillChangeCompositingHint;
    100 
    101     ASSERT(!(directReasons & ~CompositingReasonComboAllStyleDeterminedReasons));
    102     return directReasons;
    103 }
    104 
    105 bool CompositingReasonFinder::requiresCompositingForTransform(RenderObject* renderer) const
    106 {
    107     // Note that we ask the renderer if it has a transform, because the style may have transforms,
    108     // but the renderer may be an inline that doesn't suppport them.
    109     return renderer->hasTransform() && renderer->style()->transform().has3DOperation();
    110 }
    111 
    112 bool CompositingReasonFinder::requiresCompositingForFilters(RenderObject* renderer) const
    113 {
    114     if (!(m_compositingTriggers & FilterTrigger))
    115         return false;
    116 
    117     return renderer->hasFilter();
    118 }
    119 
    120 CompositingReasons CompositingReasonFinder::nonStyleDeterminedDirectReasons(const RenderLayer* layer) const
    121 {
    122     CompositingReasons directReasons = CompositingReasonNone;
    123     RenderObject* renderer = layer->renderer();
    124 
    125     if (hasOverflowScrollTrigger()) {
    126         // IsUnclippedDescendant is only actually stale during the chicken/egg code path.
    127         // FIXME: Use compositingInputs().isUnclippedDescendant to ASSERT that
    128         // this value isn't stale.
    129         if (layer->compositingInputs().isUnclippedDescendant)
    130             directReasons |= CompositingReasonOutOfFlowClipping;
    131 
    132         if (layer->scrollParent())
    133             directReasons |= CompositingReasonOverflowScrollingParent;
    134 
    135         if (layer->needsCompositedScrolling())
    136             directReasons |= CompositingReasonOverflowScrollingTouch;
    137     }
    138 
    139     if (requiresCompositingForPositionFixed(renderer, layer, 0))
    140         directReasons |= CompositingReasonPositionFixed;
    141 
    142     directReasons |= renderer->additionalCompositingReasons(m_compositingTriggers);
    143 
    144     ASSERT(!(directReasons & CompositingReasonComboAllStyleDeterminedReasons));
    145     return directReasons;
    146 }
    147 
    148 bool CompositingReasonFinder::requiresCompositingForAnimation(RenderStyle* style) const
    149 {
    150     if (style->subtreeWillChangeContents())
    151         return style->isRunningAnimationOnCompositor();
    152 
    153     return style->shouldCompositeForCurrentAnimations();
    154 }
    155 
    156 bool CompositingReasonFinder::requiresCompositingForPositionFixed(RenderObject* renderer, const RenderLayer* layer, RenderLayer::ViewportConstrainedNotCompositedReason* viewportConstrainedNotCompositedReason) const
    157 {
    158     if (!(m_compositingTriggers & ViewportConstrainedPositionedTrigger))
    159         return false;
    160 
    161     if (renderer->style()->position() != FixedPosition)
    162         return false;
    163 
    164     RenderObject* container = renderer->container();
    165     // If the renderer is not hooked up yet then we have to wait until it is.
    166     if (!container) {
    167         ASSERT(m_renderView.document().lifecycle().state() < DocumentLifecycle::InCompositingUpdate);
    168         // FIXME: Remove this and ASSERT(container) once we get rid of the incremental
    169         // allocateOrClearCompositedLayerMapping compositing update. This happens when
    170         // adding the renderer to the tree because we setStyle before addChild in
    171         // createRendererForElementIfNeeded.
    172         return false;
    173     }
    174 
    175     // Don't promote fixed position elements that are descendants of a non-view container, e.g. transformed elements.
    176     // They will stay fixed wrt the container rather than the enclosing frame.
    177     if (container != &m_renderView) {
    178         if (viewportConstrainedNotCompositedReason)
    179             *viewportConstrainedNotCompositedReason = RenderLayer::NotCompositedForNonViewContainer;
    180         return false;
    181     }
    182 
    183     // If the fixed-position element does not have any scrollable ancestor between it and
    184     // its container, then we do not need to spend compositor resources for it. Start by
    185     // assuming we can opt-out (i.e. no scrollable ancestor), and refine the answer below.
    186     bool hasScrollableAncestor = false;
    187 
    188     // The FrameView has the scrollbars associated with the top level viewport, so we have to
    189     // check the FrameView in addition to the hierarchy of ancestors.
    190     FrameView* frameView = m_renderView.frameView();
    191     if (frameView && frameView->isScrollable())
    192         hasScrollableAncestor = true;
    193 
    194     RenderLayer* ancestor = layer->parent();
    195     while (ancestor && !hasScrollableAncestor) {
    196         if (ancestor->scrollsOverflow())
    197             hasScrollableAncestor = true;
    198         if (ancestor->renderer() == &m_renderView)
    199             break;
    200         ancestor = ancestor->parent();
    201     }
    202 
    203     if (!hasScrollableAncestor) {
    204         if (viewportConstrainedNotCompositedReason)
    205             *viewportConstrainedNotCompositedReason = RenderLayer::NotCompositedForUnscrollableAncestors;
    206         return false;
    207     }
    208 
    209     // Subsequent tests depend on layout. If we can't tell now, just keep things the way they are until layout is done.
    210     // FIXME: Get rid of this codepath once we get rid of the incremental compositing update in RenderLayer::styleChanged.
    211     if (m_renderView.document().lifecycle().state() < DocumentLifecycle::LayoutClean)
    212         return layer->hasCompositedLayerMapping();
    213 
    214     bool paintsContent = layer->isVisuallyNonEmpty() || layer->hasVisibleDescendant();
    215     if (!paintsContent) {
    216         if (viewportConstrainedNotCompositedReason)
    217             *viewportConstrainedNotCompositedReason = RenderLayer::NotCompositedForNoVisibleContent;
    218         return false;
    219     }
    220 
    221     // Fixed position elements that are invisible in the current view don't get their own layer.
    222     if (FrameView* frameView = m_renderView.frameView()) {
    223         ASSERT(m_renderView.document().lifecycle().state() == DocumentLifecycle::InCompositingUpdate);
    224         LayoutRect viewBounds = frameView->viewportConstrainedVisibleContentRect();
    225         LayoutRect layerBounds = layer->boundingBoxForCompositing(layer->compositor()->rootRenderLayer(), RenderLayer::ApplyBoundsChickenEggHacks);
    226         if (!viewBounds.intersects(enclosingIntRect(layerBounds))) {
    227             if (viewportConstrainedNotCompositedReason)
    228                 *viewportConstrainedNotCompositedReason = RenderLayer::NotCompositedForBoundsOutOfView;
    229             return false;
    230         }
    231     }
    232 
    233     return true;
    234 }
    235 
    236 }
    237