Changeset 120395 in webkit


Ignore:
Timestamp:
Jun 14, 2012 9:17:39 PM (12 years ago)
Author:
jchaffraix@webkit.org
Message:

RenderLayer subtrees without any self-painting layer shouldn't be walked during painting
https://bugs.webkit.org/show_bug.cgi?id=88888

Reviewed by Simon Fraser.

Performance optimization, covered by existing tests.

The gist of this change is to add a has-self-painting-layer-descendant flag (including an
invalidation logic) that is used to avoid walking subtrees without any self-painting layer.

On http://dglazkov.github.com/performance-tests/biggrid.html with a 100,000 rows
by 100 columns table, it brings the paint time during scrolling from ~45ms to ~6ms
on my machine. The test case is a pathologic example here but the optimization should
apply in other cases.

The new update logic piggy-backs on top of the existing updateVisibilityStatus() one that
got repurposed and renamed as part of this change.

  • rendering/RenderLayer.cpp:

(WebCore::RenderLayer::RenderLayer):
(WebCore::RenderLayer::addChild):
(WebCore::RenderLayer::removeChild):
(WebCore::RenderLayer::styleChanged):
These functions were updated to dirty / set the new flag.

(WebCore::RenderLayer::dirtyAncestorChainHasSelfPaintingLayerDescendantStatus):
(WebCore::RenderLayer::setAncestorChainHasSelfPaintingLayerDescendant):
Added those functions to handle setting / invalidating the new flag.

(WebCore::RenderLayer::updateSelfPaintingLayerAfterStyleChange):
Added this function to handle style update.

(WebCore::RenderLayer::paintLayer):
(WebCore::RenderLayer::paintLayerContentsAndReflection):
(WebCore::RenderLayer::paintLayerContents):
(WebCore::RenderLayer::paintList):
Changed this logic to bail out if we have no self-painting descendants. This is what
is giving the performance improvement. Also added some performance ASSERTs to ensure
the methods are not called when they shouldn't.

(WebCore::RenderLayer::updateDescendantDependentFlags):
Renamed from updateVisibilityStatus to account for the new usage.

(WebCore::RenderLayer::updateLayerPositions):
(WebCore::RenderLayer::updateLayerPositionsAfterScroll):
(WebCore::RenderLayer::collectLayers):

  • rendering/RenderLayerBacking.cpp:

(WebCore::RenderLayerBacking::updateGraphicsLayerGeometry):
Updated after updateVisibilityStatus rename.

  • rendering/RenderLayer.h:

(WebCore::RenderLayer::hasSelfPaintingLayerDescendant):
Added the declaration of the new functions as well as the new flag and dirty bit.

Location:
trunk/Source/WebCore
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebCore/ChangeLog

    r120393 r120395  
     12012-06-14  Julien Chaffraix  <jchaffraix@webkit.org>
     2
     3        RenderLayer subtrees without any self-painting layer shouldn't be walked during painting
     4        https://bugs.webkit.org/show_bug.cgi?id=88888
     5
     6        Reviewed by Simon Fraser.
     7
     8        Performance optimization, covered by existing tests.
     9
     10        The gist of this change is to add a has-self-painting-layer-descendant flag (including an
     11        invalidation logic) that is used to avoid walking subtrees without any self-painting layer.
     12
     13        On http://dglazkov.github.com/performance-tests/biggrid.html with a 100,000 rows
     14        by 100 columns table, it brings the paint time during scrolling from ~45ms to ~6ms
     15        on my machine. The test case is a pathologic example here but the optimization should
     16        apply in other cases.
     17
     18        The new update logic piggy-backs on top of the existing updateVisibilityStatus() one that
     19        got repurposed and renamed as part of this change.
     20
     21        * rendering/RenderLayer.cpp:
     22        (WebCore::RenderLayer::RenderLayer):
     23        (WebCore::RenderLayer::addChild):
     24        (WebCore::RenderLayer::removeChild):
     25        (WebCore::RenderLayer::styleChanged):
     26        These functions were updated to dirty / set the new flag.
     27
     28        (WebCore::RenderLayer::dirtyAncestorChainHasSelfPaintingLayerDescendantStatus):
     29        (WebCore::RenderLayer::setAncestorChainHasSelfPaintingLayerDescendant):
     30        Added those functions to handle setting / invalidating the new flag.
     31
     32        (WebCore::RenderLayer::updateSelfPaintingLayerAfterStyleChange):
     33        Added this function to handle style update.
     34
     35        (WebCore::RenderLayer::paintLayer):
     36        (WebCore::RenderLayer::paintLayerContentsAndReflection):
     37        (WebCore::RenderLayer::paintLayerContents):
     38        (WebCore::RenderLayer::paintList):
     39        Changed this logic to bail out if we have no self-painting descendants. This is what
     40        is giving the performance improvement. Also added some performance ASSERTs to ensure
     41        the methods are not called when they shouldn't.
     42
     43        (WebCore::RenderLayer::updateDescendantDependentFlags):
     44        Renamed from updateVisibilityStatus to account for the new usage.
     45
     46        (WebCore::RenderLayer::updateLayerPositions):
     47        (WebCore::RenderLayer::updateLayerPositionsAfterScroll):
     48        (WebCore::RenderLayer::collectLayers):
     49        * rendering/RenderLayerBacking.cpp:
     50        (WebCore::RenderLayerBacking::updateGraphicsLayerGeometry):
     51        Updated after updateVisibilityStatus rename.
     52
     53        * rendering/RenderLayer.h:
     54        (WebCore::RenderLayer::hasSelfPaintingLayerDescendant):
     55        Added the declaration of the new functions as well as the new flag and dirty bit.
     56
    1572012-06-14  Tony Payne  <tpayne@chromium.org>
    258
  • trunk/Source/WebCore/rendering/RenderLayer.cpp

    r119893 r120395  
    130130    , m_scrollDimensionsDirty(true)
    131131    , m_normalFlowListDirty(true)
     132    , m_hasSelfPaintingLayerDescendant(false)
     133    , m_hasSelfPaintingLayerDescendantDirty(false)
    132134    , m_isRootLayer(renderer->isRenderView())
    133135    , m_usedTransparency(false)
     
    364366    positionOverflowControls(toSize(roundedIntPoint(offset)));
    365367
    366     updateVisibilityStatus();
     368    updateDescendantDependentFlags();
    367369
    368370    if (flags & UpdatePagination)
     
    443445}
    444446
     447void RenderLayer::setAncestorChainHasSelfPaintingLayerDescendant()
     448{
     449    for (RenderLayer* layer = this; layer; layer = layer->parent()) {
     450        if (!layer->m_hasSelfPaintingLayerDescendantDirty && layer->hasSelfPaintingLayerDescendant())
     451            break;
     452
     453        layer->m_hasSelfPaintingLayerDescendantDirty = false;
     454        layer->m_hasSelfPaintingLayerDescendant = true;
     455    }
     456}
     457
     458void RenderLayer::dirtyAncestorChainHasSelfPaintingLayerDescendantStatus()
     459{
     460    for (RenderLayer* layer = this; layer; layer = layer->parent()) {
     461        layer->m_hasSelfPaintingLayerDescendantDirty = true;
     462        // If we have reached a self-painting layer, we know our parent should have a self-painting descendant
     463        // in this case, there is no need to dirty our ancestors further.
     464        if (layer->isSelfPaintingLayer()) {
     465            ASSERT(!parent() || parent()->m_hasSelfPaintingLayerDescendantDirty || parent()->hasSelfPaintingLayerDescendant());
     466            break;
     467        }
     468    }
     469}
     470
    445471void RenderLayer::computeRepaintRects(LayoutPoint* offsetFromRoot)
    446472{
     
    465491    // FIXME: This shouldn't be needed, but there are some corner cases where
    466492    // these flags are still dirty. Update so that the check below is valid.
    467     updateVisibilityStatus();
     493    updateDescendantDependentFlags();
    468494
    469495    // If we have no visible content and no visible descendants, there is no point recomputing
     
    657683}
    658684
    659 void RenderLayer::updateVisibilityStatus()
    660 {
    661     if (m_visibleDescendantStatusDirty) {
     685void RenderLayer::updateDescendantDependentFlags()
     686{
     687    if (m_visibleDescendantStatusDirty || m_hasSelfPaintingLayerDescendantDirty) {
    662688        m_hasVisibleDescendant = false;
     689        m_hasSelfPaintingLayerDescendant = false;
    663690        for (RenderLayer* child = firstChild(); child; child = child->nextSibling()) {
    664             child->updateVisibilityStatus();       
    665             if (child->m_hasVisibleContent || child->m_hasVisibleDescendant) {
    666                 m_hasVisibleDescendant = true;
     691            child->updateDescendantDependentFlags();
     692
     693            bool hasVisibleDescendant = child->m_hasVisibleContent || child->m_hasVisibleDescendant;
     694            bool hasSelfPaintingLayerDescendant = child->isSelfPaintingLayer() || child->hasSelfPaintingLayerDescendant();
     695
     696            m_hasVisibleDescendant |= hasVisibleDescendant;
     697            m_hasSelfPaintingLayerDescendant |= hasSelfPaintingLayerDescendant;
     698
     699            if (m_hasVisibleDescendant && m_hasSelfPaintingLayerDescendant)
    667700                break;
    668             }
    669701        }
    670702        m_visibleDescendantStatusDirty = false;
     703        m_hasSelfPaintingLayerDescendantDirty = false;
    671704    }
    672705
     
    12431276    }
    12441277
    1245     child->updateVisibilityStatus();
     1278    child->updateDescendantDependentFlags();
    12461279    if (child->m_hasVisibleContent || child->m_hasVisibleDescendant)
    12471280        childVisibilityChanged(true);
    1248    
     1281
     1282    if (child->isSelfPaintingLayer() || child->hasSelfPaintingLayerDescendant())
     1283        setAncestorChainHasSelfPaintingLayerDescendant();
     1284
    12491285#if USE(ACCELERATED_COMPOSITING)
    12501286    compositor()->layerWasAdded(this, child);
     
    12831319    oldChild->setParent(0);
    12841320   
    1285     oldChild->updateVisibilityStatus();
     1321    oldChild->updateDescendantDependentFlags();
    12861322    if (oldChild->m_hasVisibleContent || oldChild->m_hasVisibleDescendant)
    12871323        childVisibilityChanged(false);
    1288    
     1324
     1325    if (oldChild->isSelfPaintingLayer() || oldChild->hasSelfPaintingLayerDescendant())
     1326        dirtyAncestorChainHasSelfPaintingLayerDescendantStatus();
     1327
    12891328    return oldChild;
    12901329}
     
    28832922
    28842923    // Non self-painting leaf layers don't need to be painted as their renderer() should properly paint itself.
    2885     if (!isSelfPaintingLayer() && !firstChild())
     2924    if (!isSelfPaintingLayer() && !hasSelfPaintingLayerDescendant())
    28862925        return;
    28872926
     
    29532992                        PaintLayerFlags paintFlags)
    29542993{
     2994    ASSERT(isSelfPaintingLayer() || hasSelfPaintingLayerDescendant());
     2995
    29552996    PaintLayerFlags localPaintFlags = paintFlags & ~(PaintLayerAppliedTransform);
    29562997
     
    29723013                        PaintLayerFlags paintFlags)
    29733014{
     3015    ASSERT(isSelfPaintingLayer() || hasSelfPaintingLayerDescendant());
     3016
    29743017    PaintLayerFlags localPaintFlags = paintFlags & ~(PaintLayerAppliedTransform);
    29753018    bool haveTransparency = localPaintFlags & PaintLayerHaveTransparency;
     
    31643207{
    31653208    if (!list)
     3209        return;
     3210
     3211    if (!hasSelfPaintingLayerDescendant())
    31663212        return;
    31673213
     
    45394585void RenderLayer::collectLayers(bool includeHiddenLayers, Vector<RenderLayer*>*& posBuffer, Vector<RenderLayer*>*& negBuffer)
    45404586{
    4541     updateVisibilityStatus();
     4587    updateDescendantDependentFlags();
    45424588
    45434589    // Overflow layers are just painted by their enclosing layers, so they don't get put in zorder lists.
     
    46754721}
    46764722
     4723void RenderLayer::updateSelfPaintingLayerAfterStyleChange(const RenderStyle*)
     4724{
     4725    bool isSelfPaintingLayer = shouldBeSelfPaintingLayer();
     4726    if (m_isSelfPaintingLayer == isSelfPaintingLayer)
     4727        return;
     4728
     4729    m_isSelfPaintingLayer = isSelfPaintingLayer;
     4730    if (!parent())
     4731        return;
     4732    if (isSelfPaintingLayer)
     4733        parent()->setAncestorChainHasSelfPaintingLayerDescendant();
     4734    else
     4735        parent()->dirtyAncestorChainHasSelfPaintingLayerDescendantStatus();
     4736}
     4737
    46774738void RenderLayer::updateStackingContextsAfterStyleChange(const RenderStyle* oldStyle)
    46784739{
     
    47454806    }
    47464807
    4747     m_isSelfPaintingLayer = shouldBeSelfPaintingLayer();
    4748 
    47494808    if (renderer()->style()->overflowX() == OMARQUEE && renderer()->style()->marqueeBehavior() != MNONE && renderer()->isBox()) {
    47504809        if (!m_marquee)
     
    47574816    }
    47584817
     4818    updateSelfPaintingLayerAfterStyleChange(oldStyle);
    47594819    updateStackingContextsAfterStyleChange(oldStyle);
    47604820    updateScrollbarsAfterStyleChange(oldStyle);
     
    47824842
    47834843#if USE(ACCELERATED_COMPOSITING)
    4784     updateVisibilityStatus();
     4844    updateDescendantDependentFlags();
    47854845    updateTransform();
    47864846
  • trunk/Source/WebCore/rendering/RenderLayer.h

    r119996 r120395  
    445445    void dirtyVisibleContentStatus();
    446446
     447    // FIXME: We should ASSERT(!m_hasSelfPaintingLayerDescendantDirty); here but we hit the same bugs as visible content above.
     448    // Part of the issue is with subtree relayout: we don't check if our ancestors have some descendant flags dirty, missing some updates.
     449    bool hasSelfPaintingLayerDescendant() const { return m_hasSelfPaintingLayerDescendant; }
     450
    447451    // Gets the nearest enclosing positioned ancestor layer (also includes
    448452    // the <html> layer and the root layer).
     
    652656    bool isDirtyStackingContext() const { return m_zOrderListsDirty && isStackingContext(); }
    653657
     658    void setAncestorChainHasSelfPaintingLayerDescendant();
     659    void dirtyAncestorChainHasSelfPaintingLayerDescendantStatus();
     660
    654661    void computeRepaintRects(LayoutPoint* offsetFromRoot = 0);
    655662    void clearRepaintRects();
     
    661668    bool shouldRepaintAfterLayout() const;
    662669
     670    void updateSelfPaintingLayerAfterStyleChange(const RenderStyle* oldStyle);
    663671    void updateStackingContextsAfterStyleChange(const RenderStyle* oldStyle);
    664672
     
    777785    void childVisibilityChanged(bool newVisibility);
    778786    void dirtyVisibleDescendantStatus();
    779     void updateVisibilityStatus();
     787
     788    void updateDescendantDependentFlags();
    780789
    781790    // This flag is computed by RenderLayerCompositor, which knows more about 3d hierarchies than we do.
     
    873882    bool m_isSelfPaintingLayer : 1;
    874883
     884    // If have no self-painting descendants, we don't have to walk our children during painting. This can lead to
     885    // significant savings, especially if the tree has lots of non-self-painting layers grouped together (e.g. table cells).
     886    bool m_hasSelfPaintingLayerDescendant : 1;
     887    bool m_hasSelfPaintingLayerDescendantDirty : 1;
     888
    875889    const bool m_isRootLayer : 1;
    876890
  • trunk/Source/WebCore/rendering/RenderLayerBacking.cpp

    r120340 r120395  
    425425#endif
    426426   
    427     m_owningLayer->updateVisibilityStatus();
     427    m_owningLayer->updateDescendantDependentFlags();
    428428
    429429    // m_graphicsLayer is the corresponding GraphicsLayer for this RenderLayer and its non-compositing
Note: See TracChangeset for help on using the changeset viewer.