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

Changeset 286710 in webkit


Ignore:
Timestamp:
Dec 8, 2021, 1:19:38 PM (5 years ago)
Author:
Russell Epstein
Message:

Cherry-pick r286671. rdar://problem/86226565

Momentum Event Dispatcher: Momentum tail should have montonically decreasing deltas and tail gaps
https://bugs.webkit.org/show_bug.cgi?id=233993
<rdar://problem/86118367>

Reviewed by Simon Fraser.

  • WebProcess/WebPage/MomentumEventDispatcher.cpp: (WebKit::MomentumEventDispatcher::consumeDeltaForCurrentTime): (WebKit::MomentumEventDispatcher::buildOffsetTableWithInitialDelta): (WebKit::MomentumEventDispatcher::computeNextDelta): In order to avoid visual stutter due to integral rounding of the deltas we dispatch in the momentum phase, switch to a table of deltas instead of interpolating along the offset curve during the tail end of the animation (when the rounding makes up a large proportion of each delta).

(WebKit::MomentumEventDispatcher::equalizeTailGaps):
Sort the deltas up until the first zero to ensure a lack of unexpected
perceptual acceleration, and then inject skipped frames in order to
ensure that frames that have effective scroll movement (non-zero deltas)
are always spaced equally-or-further apart than earlier ones, but
never closer together (which, again, would be percieved as acceleration).

(WebKit::MomentumEventDispatcher::handleWheelEvent):
(WebKit::MomentumEventDispatcher::pushLogEntry):
(WebKit::MomentumEventDispatcher::flushLog):
Lastly, adjust some logging to make it easier to tell which row in
the output corresponds to an event delta or generated delta, so it's
easier to find skipped frames.

  • WebProcess/WebPage/MomentumEventDispatcher.h:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@286671 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Location:
branches/safari-613.1.11-branch/Source/WebKit
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • branches/safari-613.1.11-branch/Source/WebKit/ChangeLog

    r286675 r286710  
     12021-12-08  Russell Epstein  <repstein@apple.com>
     2
     3        Cherry-pick r286671. rdar://problem/86226565
     4
     5    Momentum Event Dispatcher: Momentum tail should have montonically decreasing deltas and tail gaps
     6    https://bugs.webkit.org/show_bug.cgi?id=233993
     7    <rdar://problem/86118367>
     8   
     9    Reviewed by Simon Fraser.
     10   
     11    * WebProcess/WebPage/MomentumEventDispatcher.cpp:
     12    (WebKit::MomentumEventDispatcher::consumeDeltaForCurrentTime):
     13    (WebKit::MomentumEventDispatcher::buildOffsetTableWithInitialDelta):
     14    (WebKit::MomentumEventDispatcher::computeNextDelta):
     15    In order to avoid visual stutter due to integral rounding of the
     16    deltas we dispatch in the momentum phase, switch to a table of
     17    deltas instead of interpolating along the offset curve during the
     18    tail end of the animation (when the rounding makes up a large
     19    proportion of each delta).
     20   
     21    (WebKit::MomentumEventDispatcher::equalizeTailGaps):
     22    Sort the deltas up until the first zero to ensure a lack of unexpected
     23    perceptual acceleration, and then inject skipped frames in order to
     24    ensure that frames that have effective scroll movement (non-zero deltas)
     25    are always spaced equally-or-further apart than earlier ones, but
     26    never closer together (which, again, would be percieved as acceleration).
     27   
     28    (WebKit::MomentumEventDispatcher::handleWheelEvent):
     29    (WebKit::MomentumEventDispatcher::pushLogEntry):
     30    (WebKit::MomentumEventDispatcher::flushLog):
     31    Lastly, adjust some logging to make it easier to tell which row in
     32    the output corresponds to an event delta or generated delta, so it's
     33    easier to find skipped frames.
     34    * WebProcess/WebPage/MomentumEventDispatcher.h:
     35   
     36   
     37    git-svn-id: https://svn.webkit.org/repository/webkit/trunk@286671 268f45cc-cd09-0410-ab3c-d52691b4dbfc
     38
     39    2021-12-08  Tim Horton  <timothy_horton@apple.com>
     40
     41            Momentum Event Dispatcher: Momentum tail should have montonically decreasing deltas and tail gaps
     42            https://bugs.webkit.org/show_bug.cgi?id=233993
     43            <rdar://problem/86118367>
     44
     45            Reviewed by Simon Fraser.
     46
     47            * WebProcess/WebPage/MomentumEventDispatcher.cpp:
     48            (WebKit::MomentumEventDispatcher::consumeDeltaForCurrentTime):
     49            (WebKit::MomentumEventDispatcher::buildOffsetTableWithInitialDelta):
     50            (WebKit::MomentumEventDispatcher::computeNextDelta):
     51            In order to avoid visual stutter due to integral rounding of the
     52            deltas we dispatch in the momentum phase, switch to a table of
     53            deltas instead of interpolating along the offset curve during the
     54            tail end of the animation (when the rounding makes up a large
     55            proportion of each delta).
     56
     57            (WebKit::MomentumEventDispatcher::equalizeTailGaps):
     58            Sort the deltas up until the first zero to ensure a lack of unexpected
     59            perceptual acceleration, and then inject skipped frames in order to
     60            ensure that frames that have effective scroll movement (non-zero deltas)
     61            are always spaced equally-or-further apart than earlier ones, but
     62            never closer together (which, again, would be percieved as acceleration).
     63
     64            (WebKit::MomentumEventDispatcher::handleWheelEvent):
     65            (WebKit::MomentumEventDispatcher::pushLogEntry):
     66            (WebKit::MomentumEventDispatcher::flushLog):
     67            Lastly, adjust some logging to make it easier to tell which row in
     68            the output corresponds to an event delta or generated delta, so it's
     69            easier to find skipped frames.
     70            * WebProcess/WebPage/MomentumEventDispatcher.h:
     71
    1722021-12-08  Russell Epstein  <repstein@apple.com>
    273
  • branches/safari-613.1.11-branch/Source/WebKit/WebProcess/WebPage/MomentumEventDispatcher.cpp

    r286674 r286710  
    113113
    114114    auto combinedPhase = (event.phase() << 8) | (event.momentumPhase());
    115     m_currentLogState.latestEventPhase = combinedPhase;
    116115    m_currentLogState.totalEventOffset += event.delta().height();
    117116    if (!isMomentumEventDuringSyntheticGesture) {
    118117        // Log events that we don't block to the generated offsets log as well,
    119118        // even though we didn't technically generate them, just passed them through.
    120         m_currentLogState.latestGeneratedPhase = combinedPhase;
    121119        m_currentLogState.totalGeneratedOffset += event.delta().height();
    122     }
    123     pushLogEntry();
     120        pushLogEntry(combinedPhase, combinedPhase);
     121    } else
     122        pushLogEntry(0, combinedPhase);
     123   
    124124#endif
    125125
     
    175175
    176176#if ENABLE(MOMENTUM_EVENT_DISPATCHER_TEMPORARY_LOGGING)
    177     m_currentLogState.latestGeneratedPhase = phase;
    178177    m_currentLogState.totalGeneratedOffset += appKitAcceleratedDelta.height();
    179     pushLogEntry();
     178    pushLogEntry(phase, 0);
    180179#endif
    181180}
     
    215214
    216215#if ENABLE(MOMENTUM_EVENT_DISPATCHER_TEMPORARY_LOGGING)
    217     RELEASE_LOG(ScrollAnimations, "MomentumEventDispatcher saw momentum end phase with total offset %.1f %.1f, duration %f (event offset would have been %.1f %.1f)", m_currentGesture.currentOffset.width(), m_currentGesture.currentOffset.height(), (MonotonicTime::now() - m_currentGesture.startTime).seconds(), m_currentGesture.accumulatedEventOffset.width(), m_currentGesture.accumulatedEventOffset.height());
     216    RELEASE_LOG(ScrollAnimations, "MomentumEventDispatcher saw momentum end phase with total offset %.1f %.1f, duration %f (event offset would have been %.1f %.1f) (tail index %d of %zu)", m_currentGesture.currentOffset.width(), m_currentGesture.currentOffset.height(), (MonotonicTime::now() - m_currentGesture.startTime).seconds(), m_currentGesture.accumulatedEventOffset.width(), m_currentGesture.accumulatedEventOffset.height(), m_currentGesture.currentTailDeltaIndex, m_currentGesture.tailDeltaTable.size());
    218217    m_dispatcher.queue().dispatchAfter(1_s, [this] {
    219218        flushLog();
     
    289288WebCore::FloatSize MomentumEventDispatcher::consumeDeltaForCurrentTime()
    290289{
     290    WebCore::FloatSize delta;
     291
    291292    auto animationTime = MonotonicTime::now() - m_currentGesture.startTime;
    292     auto desiredOffset = offsetAtTime(animationTime);
    293 
    294 #if !ENABLE(MOMENTUM_EVENT_DISPATCHER_PREMATURE_ROUNDING)
    295     // Intentional delta rounding (but at the end!).
    296     WebCore::FloatSize delta = roundedIntSize(desiredOffset - m_currentGesture.currentOffset);
    297 #else
    298     WebCore::FloatSize delta = desiredOffset - m_currentGesture.currentOffset;
    299 #endif
     293    if (animationTime < m_currentGesture.tailStartDelay) {
     294        auto desiredOffset = offsetAtTime(animationTime);
     295        delta = roundedIntSize(desiredOffset - m_currentGesture.currentOffset);
     296    } else {
     297        if (m_currentGesture.currentTailDeltaIndex < m_currentGesture.tailDeltaTable.size())
     298            delta = -m_currentGesture.tailDeltaTable[m_currentGesture.currentTailDeltaIndex++];
     299        else
     300            delta = { };
     301    }
    300302
    301303    m_currentGesture.currentOffset += delta;
     
    358360void MomentumEventDispatcher::buildOffsetTableWithInitialDelta(WebCore::FloatSize initialUnacceleratedDelta)
    359361{
    360 #if ENABLE(MOMENTUM_EVENT_DISPATCHER_PREMATURE_ROUNDING)
    361     m_currentGesture.carryOffset = { };
    362 #endif
    363362    m_currentGesture.offsetTable.clear();
    364363
    365364    WebCore::FloatSize accumulatedOffset;
    366365    WebCore::FloatSize unacceleratedDelta = initialUnacceleratedDelta;
     366
     367    float physicalCurveMultiplier = idealCurveFrameRate / m_currentGesture.accelerationCurve->frameRate();
     368    bool inTail = false;
     369    WebCore::FloatSize tailCarry;
    367370
    368371    do {
     
    370373        std::tie(unacceleratedDelta, acceleratedDelta) = computeNextDelta(unacceleratedDelta);
    371374
     375        const float tailStartUnacceleratedDelta = 6.f;
     376        if (!inTail && std::abs(unacceleratedDelta.width()) < tailStartUnacceleratedDelta && std::abs(unacceleratedDelta.height()) < tailStartUnacceleratedDelta) {
     377            inTail = true;
     378            m_currentGesture.tailStartDelay = idealCurveFrameInterval * m_currentGesture.offsetTable.size();
     379        }
     380
     381        if (inTail) {
     382            auto tailDelta = acceleratedDelta * physicalCurveMultiplier;
     383            auto deltaWithCarry = tailDelta + tailCarry;
     384            auto quantizedDelta = roundedIntSize(deltaWithCarry);
     385            tailCarry = deltaWithCarry - quantizedDelta;
     386            m_currentGesture.tailDeltaTable.append(quantizedDelta);
     387        }
     388
    372389        accumulatedOffset += acceleratedDelta;
    373390        m_currentGesture.offsetTable.append(accumulatedOffset);
     391
    374392    } while (std::abs(unacceleratedDelta.width()) > 0.5 || std::abs(unacceleratedDelta.height()) > 0.5);
    375393
    376 #if ENABLE(MOMENTUM_EVENT_DISPATCHER_TEMPORARY_LOGGING)
    377     RELEASE_LOG(ScrollAnimations, "MomentumEventDispatcher built table with %ld frames, initial delta %f %f, distance %f %f (initial delta from last changed event %f %f)", m_currentGesture.offsetTable.size(), initialUnacceleratedDelta.width(), initialUnacceleratedDelta.height(), accumulatedOffset.width(), accumulatedOffset.height(), m_lastActivePhaseDelta.width(), m_lastActivePhaseDelta.height());
    378 #endif
     394    equalizeTailGaps();
     395
     396#if ENABLE(MOMENTUM_EVENT_DISPATCHER_TEMPORARY_LOGGING)
     397    RELEASE_LOG(ScrollAnimations, "MomentumEventDispatcher built table with %ld frames, %.1f seconds (%ld tail frames, %.1f seconds, starting at %.1f), initial delta %.1f %.1f, distance %.1f %.1f (initial delta from last changed event %.1f %.1f)", m_currentGesture.offsetTable.size(), idealCurveFrameInterval.seconds() * m_currentGesture.offsetTable.size(), m_currentGesture.tailDeltaTable.size(), m_currentGesture.tailDeltaTable.size() * (1.f / m_currentGesture.accelerationCurve->frameRate()), m_currentGesture.tailStartDelay.seconds(), initialUnacceleratedDelta.width(), initialUnacceleratedDelta.height(), accumulatedOffset.width(), accumulatedOffset.height(), m_lastActivePhaseDelta.width(), m_lastActivePhaseDelta.height());
     398#endif
     399}
     400
     401void MomentumEventDispatcher::equalizeTailGaps()
     402{
     403    // Sort the deltas up until the first zero to ensure a lack of unexpected
     404    // perceptual acceleration, and then inject skipped frames in order to
     405    // ensure that frames that have effective scroll movement (non-zero deltas)
     406    // are always spaced equally-or-further apart than earlier ones, but
     407    // never closer together.
     408
     409    auto& table = m_currentGesture.tailDeltaTable;
     410    size_t initialTableSize = table.size();
     411
     412    enum Axis { Horizontal, Vertical };
     413    Vector<float> deltas[2];
     414    unsigned firstZeroIndex[2] = { 0, 0 };
     415    deltas[Horizontal].reserveInitialCapacity(initialTableSize);
     416    deltas[Vertical].reserveInitialCapacity(initialTableSize);
     417    for (unsigned i = 0; i < initialTableSize; i++) {
     418        deltas[Horizontal].uncheckedAppend(table[i].width());
     419        if (!firstZeroIndex[Horizontal] && !table[i].width())
     420            firstZeroIndex[Horizontal] = i;
     421
     422        deltas[Vertical].uncheckedAppend(table[i].height());
     423        if (!firstZeroIndex[Vertical] && !table[i].height())
     424            firstZeroIndex[Vertical] = i;
     425    }
     426   
     427    if (auto index = firstZeroIndex[Horizontal])
     428        std::sort(deltas[Horizontal].begin(), std::next(deltas[Horizontal].begin(), index));
     429    if (auto index = firstZeroIndex[Vertical])
     430        std::sort(deltas[Vertical].begin(), std::next(deltas[Vertical].begin(), index));
     431
     432    // GapSize is a count of contiguous frames with zero deltas.
     433    typedef unsigned GapSize[2];
     434    GapSize minimumGap = { 0, 0 };
     435    GapSize currentGap = { 0, 0 };
     436    GapSize remainingGapToGenerate = { 0, 0 };
     437    unsigned originalTableIndex[2] = { 0, 0 };
     438
     439    auto takeNextDelta = [&] (uint8_t axis) -> float {
     440        if (originalTableIndex[axis] >= initialTableSize)
     441            return 0.f;
     442
     443        if (remainingGapToGenerate[axis]) {
     444            --remainingGapToGenerate[axis];
     445            ++currentGap[axis];
     446            return 0.f;
     447        }
     448
     449        auto value = deltas[axis][originalTableIndex[axis]];
     450        if (value) {
     451            minimumGap[axis] = std::max(minimumGap[axis], currentGap[axis]);
     452            remainingGapToGenerate[axis] = minimumGap[axis] - currentGap[axis];
     453            if (remainingGapToGenerate[axis]) {
     454                --remainingGapToGenerate[axis];
     455                ++currentGap[axis];
     456                return 0.f;
     457            }
     458
     459            currentGap[axis] = 0;
     460        } else
     461            ++currentGap[axis];
     462
     463        ++originalTableIndex[axis];
     464
     465        return value;
     466    };
     467
     468    size_t finalTableSize = 0;
     469    table.shrink(0);
     470
     471    while (originalTableIndex[Horizontal] < initialTableSize || originalTableIndex[Vertical] < initialTableSize) {
     472        WebCore::FloatSize delta(takeNextDelta(Horizontal), takeNextDelta(Vertical));
     473        table.append(delta);
     474
     475        if (!delta.isZero())
     476            finalTableSize = table.size();
     477    }
     478
     479    table.shrink(finalTableSize);
    379480}
    380481
     
    428529    unacceleratedDelta.scale(decayRate);
    429530
    430     auto quantizedUnacceleratedDelta = unacceleratedDelta;
    431 
    432 #if ENABLE(MOMENTUM_EVENT_DISPATCHER_PREMATURE_ROUNDING)
    433     // Round and carry.
    434     int32_t quantizedX = std::round(quantizedUnacceleratedDelta.width());
    435     int32_t quantizedY = std::round(quantizedUnacceleratedDelta.height());
    436 
    437     if (std::abs(quantizedUnacceleratedDelta.width()) < 1 && std::abs(quantizedUnacceleratedDelta.height()) < 1) {
    438         float deltaXIncludingCarry = quantizedUnacceleratedDelta.width() + m_currentGesture.carryOffset.width();
    439         float deltaYIncludingCarry = quantizedUnacceleratedDelta.height() + m_currentGesture.carryOffset.height();
    440 
    441         // Intentional truncation.
    442         quantizedX = deltaXIncludingCarry;
    443         quantizedY = deltaYIncludingCarry;
    444         m_currentGesture.carryOffset = { deltaXIncludingCarry - quantizedX, deltaYIncludingCarry - quantizedY };
    445     }
    446 
    447     quantizedUnacceleratedDelta = { static_cast<float>(quantizedX), static_cast<float>(quantizedY) };
    448 #endif
    449 
    450531    // The delta queue operates on pre-acceleration deltas, so insert the new event *before* accelerating.
    451     didReceiveScrollEventWithInterval(quantizedUnacceleratedDelta, idealCurveFrameInterval);
     532    didReceiveScrollEventWithInterval(unacceleratedDelta, idealCurveFrameInterval);
    452533
    453534    auto accelerateAxis = [&] (HistoricalDeltas& deltas, float value) {
     
    494575
    495576    WebCore::FloatSize acceleratedDelta(
    496         accelerateAxis(m_deltaHistoryX, quantizedUnacceleratedDelta.width()),
    497         accelerateAxis(m_deltaHistoryY, quantizedUnacceleratedDelta.height())
     577        accelerateAxis(m_deltaHistoryX, unacceleratedDelta.width()),
     578        accelerateAxis(m_deltaHistoryY, unacceleratedDelta.height())
    498579    );
    499580
     
    507588#if ENABLE(MOMENTUM_EVENT_DISPATCHER_TEMPORARY_LOGGING)
    508589
    509 void MomentumEventDispatcher::pushLogEntry()
     590void MomentumEventDispatcher::pushLogEntry(uint32_t generatedPhase, uint32_t eventPhase)
    510591{
    511592    m_currentLogState.time = MonotonicTime::now();
     593    m_currentLogState.generatedPhase = generatedPhase;
     594    m_currentLogState.eventPhase = eventPhase;
    512595    m_log.append(m_currentLogState);
    513596}
     
    524607    RELEASE_LOG(ScrollAnimations, "MomentumEventDispatcher event log: time,generatedOffset,generatedPhase,eventOffset,eventPhase");
    525608    for (const auto& entry : m_log)
    526         RELEASE_LOG(ScrollAnimations, "MomentumEventDispatcher event log: %f,%f,%d,%f,%d", (entry.time - startTime).seconds(), entry.totalGeneratedOffset, entry.latestGeneratedPhase, entry.totalEventOffset, entry.latestEventPhase);
     609        RELEASE_LOG(ScrollAnimations, "MomentumEventDispatcher event log: %f,%f,%d,%f,%d", (entry.time - startTime).seconds(), entry.totalGeneratedOffset, entry.generatedPhase, entry.totalEventOffset, entry.eventPhase);
    527610
    528611    m_log.clear();
  • branches/safari-613.1.11-branch/Source/WebKit/WebProcess/WebPage/MomentumEventDispatcher.h

    r286512 r286710  
    2828#if ENABLE(MOMENTUM_EVENT_DISPATCHER)
    2929
    30 // FIXME: Remove this once we decide which version we want.
    31 #define ENABLE_MOMENTUM_EVENT_DISPATCHER_PREMATURE_ROUNDING 0
    3230#define ENABLE_MOMENTUM_EVENT_DISPATCHER_TEMPORARY_LOGGING 1
    3331
     
    8179
    8280    void buildOffsetTableWithInitialDelta(WebCore::FloatSize);
     81    void equalizeTailGaps();
    8382
    8483    // Once consumed, this delta *must* be dispatched in an event.
     
    9291
    9392#if ENABLE(MOMENTUM_EVENT_DISPATCHER_TEMPORARY_LOGGING)
    94     void pushLogEntry();
     93    void pushLogEntry(uint32_t generatedPhase, uint32_t eventPhase);
    9594    void flushLog();
    9695
     
    103102        float totalEventOffset { 0 };
    104103
    105         uint32_t latestGeneratedPhase { 0 };
    106         uint32_t latestEventPhase { 0 };
     104        uint32_t generatedPhase { 0 };
     105        uint32_t eventPhase { 0 };
    107106    };
    108107    LogEntry m_currentLogState;
     
    134133        MonotonicTime startTime;
    135134
    136         Vector<WebCore::FloatSize> offsetTable;
     135        Vector<WebCore::FloatSize> offsetTable; // Always at 60Hz intervals.
     136        Vector<WebCore::FloatSize> tailDeltaTable; // Always at event dispatch intervals.
     137        Seconds tailStartDelay;
     138        unsigned currentTailDeltaIndex { 0 };
    137139
    138140#if ENABLE(MOMENTUM_EVENT_DISPATCHER_TEMPORARY_LOGGING)
Note: See TracChangeset for help on using the changeset viewer.