⚠ Archived content — this site is no longer maintained.   Current WebKit documentation is at docs.webkit.org.

Changeset 286932 in webkit


Ignore:
Timestamp:
Dec 12, 2021, 6:42:35 PM (5 years ago)
Author:
Simon Fraser
Message:

Ensure that the scrolling thread always commits layer position changes to reduce scrolling stutters
https://bugs.webkit.org/show_bug.cgi?id=234213

Reviewed by Tim Horton.

On a page where CA commits on the main thread take a long time (e.g. because of expensive
painting), it's possible that the main thread has updated the scrolling layer position, and
then the scrolling thread detects that the commit is taking a long time and attempts to
trigger its own commit, but because the layer position property doesn't change, no commit
occurs.

Work around this by setting the layer position to 0,0 and back when we're on the scrolling
thread. Only do this if the scroll position changed since the last display refresh to avoid
triggering redundant commits.

Ideally we'd traverse the scrolling tree and do this for every scrolling node, but scrolling
trees can get large so for now just apply this to the root node.

  • page/scrolling/ScrollingTreeScrollingNode.cpp:

(WebCore::ScrollingTreeScrollingNode::updateScrollPositionAtLastDisplayRefresh):

  • page/scrolling/ScrollingTreeScrollingNode.h:
  • page/scrolling/ThreadedScrollingTree.cpp:

(WebCore::ThreadedScrollingTree::displayDidRefreshOnScrollingThread):
(WebCore::ThreadedScrollingTree::storeScrollPositionsAtLastDisplayRefresh):

  • page/scrolling/ThreadedScrollingTree.h:
  • page/scrolling/mac/ScrollingTreeFrameScrollingNodeMac.mm:

(WebCore::ScrollingTreeFrameScrollingNodeMac::repositionScrollingLayers):

Location:
trunk/Source/WebCore
Files:
6 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebCore/ChangeLog

    r286931 r286932  
     12021-12-12  Simon Fraser  <simon.fraser@apple.com>
     2
     3        Ensure that the scrolling thread always commits layer position changes to reduce scrolling stutters
     4        https://bugs.webkit.org/show_bug.cgi?id=234213
     5
     6        Reviewed by Tim Horton.
     7
     8        On a page where CA commits on the main thread take a long time (e.g. because of expensive
     9        painting), it's possible that the main thread has updated the scrolling layer position, and
     10        then the scrolling thread detects that the commit is taking a long time and attempts to
     11        trigger its own commit, but because the layer position property doesn't change, no commit
     12        occurs.
     13
     14        Work around this by setting the layer position to 0,0 and back when we're on the scrolling
     15        thread. Only do this if the scroll position changed since the last display refresh to avoid
     16        triggering redundant commits.
     17
     18        Ideally we'd traverse the scrolling tree and do this for every scrolling node, but scrolling
     19        trees can get large so for now just apply this to the root node.
     20
     21        * page/scrolling/ScrollingTreeScrollingNode.cpp:
     22        (WebCore::ScrollingTreeScrollingNode::updateScrollPositionAtLastDisplayRefresh):
     23        * page/scrolling/ScrollingTreeScrollingNode.h:
     24        * page/scrolling/ThreadedScrollingTree.cpp:
     25        (WebCore::ThreadedScrollingTree::displayDidRefreshOnScrollingThread):
     26        (WebCore::ThreadedScrollingTree::storeScrollPositionsAtLastDisplayRefresh):
     27        * page/scrolling/ThreadedScrollingTree.h:
     28        * page/scrolling/mac/ScrollingTreeFrameScrollingNodeMac.mm:
     29        (WebCore::ScrollingTreeFrameScrollingNodeMac::repositionScrollingLayers):
     30
    1312021-12-12  Alan Bujtas  <zalan@apple.com>
    232
  • trunk/Source/WebCore/page/scrolling/ScrollingTreeScrollingNode.cpp

    r286351 r286932  
    304304}
    305305
     306void ScrollingTreeScrollingNode::updateScrollPositionAtLastDisplayRefresh()
     307{
     308    m_scrollPositionAtLastDisplayRefresh = m_currentScrollPosition;
     309}
     310
    306311bool ScrollingTreeScrollingNode::scrollPositionAndLayoutViewportMatch(const FloatPoint& position, std::optional<FloatRect>)
    307312{
  • trunk/Source/WebCore/page/scrolling/ScrollingTreeScrollingNode.h

    r286352 r286932  
    142142    virtual void repositionScrollingLayers() { }
    143143    virtual void repositionRelatedLayers() { }
     144   
     145    void updateScrollPositionAtLastDisplayRefresh();
     146    std::optional<FloatPoint> scrollPositionAtLastDisplayRefresh() { return m_scrollPositionAtLastDisplayRefresh; };
    144147
    145148    void applyLayerPositions() override;
     
    175178    std::optional<unsigned> m_currentVerticalSnapPointIndex;
    176179    ScrollableAreaParameters m_scrollableAreaParameters;
     180    std::optional<FloatPoint> m_scrollPositionAtLastDisplayRefresh;
    177181#if ENABLE(SCROLLING_THREAD)
    178182    OptionSet<SynchronousScrollingReason> m_synchronousScrollingReasons;
  • trunk/Source/WebCore/page/scrolling/ThreadedScrollingTree.cpp

    r286905 r286932  
    501501        break;
    502502    }
     503   
     504    storeScrollPositionsAtLastDisplayRefresh();
    503505}
    504506
     
    526528}
    527529
     530void ThreadedScrollingTree::storeScrollPositionsAtLastDisplayRefresh()
     531{
     532    // Ideally this would be a tree walk for every scrolling node, but scrolling trees can get big so for now just do this for the root;
     533    // ScrollingTreeFrameScrollingNodeMac::repositionScrollingLayers() uses the state to know whether it should force a commit.
     534    if (auto* rootNode = this->rootNode())
     535        rootNode->updateScrollPositionAtLastDisplayRefresh();
     536}
     537
    528538} // namespace WebCore
    529539
  • trunk/Source/WebCore/page/scrolling/ThreadedScrollingTree.h

    r286905 r286932  
    108108    void hasNodeWithAnimatedScrollChanged(bool) final;
    109109   
     110    void storeScrollPositionsAtLastDisplayRefresh() WTF_REQUIRES_LOCK(m_treeLock);
     111   
    110112    void serviceScrollAnimations(MonotonicTime) WTF_REQUIRES_LOCK(m_treeLock);
    111113
  • trunk/Source/WebCore/page/scrolling/mac/ScrollingTreeFrameScrollingNodeMac.mm

    r286352 r286932  
    3636#import "ScrollingCoordinator.h"
    3737#import "ScrollingStateTree.h"
     38#import "ScrollingThread.h"
    3839#import "ScrollingTree.h"
    3940#import "TileController.h"
     
    175176{
    176177    BEGIN_BLOCK_OBJC_EXCEPTIONS
     178
     179    auto* layer = static_cast<CALayer*>(scrolledContentsLayer());
     180    if (ScrollingThread::isCurrentThread()) {
     181        // If we're committing on the scrolling thread, it means that ThreadedScrollingTree is in "desynchronized" mode.
     182        // The main thread may already have set the same layer position, but here we need to trigger a scrolling thread commit to
     183        // ensure that the scroll happens even when the main thread commit is taking a long time. So make sure the layer property changes
     184        // when there has been a scroll position change.
     185        if (scrollPositionAtLastDisplayRefresh() && scrollPositionAtLastDisplayRefresh().value() != currentScrollPosition())
     186            layer.position = CGPointZero;
     187    }
     188
    177189    // We use scroll position here because the root content layer is offset to account for scrollOrigin (see FrameView::positionForRootContentLayer).
    178     static_cast<CALayer*>(scrolledContentsLayer()).position = -currentScrollPosition();
     190    layer.position = -currentScrollPosition();
    179191    END_BLOCK_OBJC_EXCEPTIONS
    180192}
Note: See TracChangeset for help on using the changeset viewer.