Changeset 286710 in webkit
- Timestamp:
- Dec 8, 2021, 1:19:38 PM (5 years ago)
- Location:
- branches/safari-613.1.11-branch/Source/WebKit
- Files:
-
- 3 edited
-
ChangeLog (modified) (1 diff)
-
WebProcess/WebPage/MomentumEventDispatcher.cpp (modified) (10 diffs)
-
WebProcess/WebPage/MomentumEventDispatcher.h (modified) (5 diffs)
Legend:
- Unmodified
- Added
- Removed
-
branches/safari-613.1.11-branch/Source/WebKit/ChangeLog
r286675 r286710 1 2021-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 1 72 2021-12-08 Russell Epstein <repstein@apple.com> 2 73 -
branches/safari-613.1.11-branch/Source/WebKit/WebProcess/WebPage/MomentumEventDispatcher.cpp
r286674 r286710 113 113 114 114 auto combinedPhase = (event.phase() << 8) | (event.momentumPhase()); 115 m_currentLogState.latestEventPhase = combinedPhase;116 115 m_currentLogState.totalEventOffset += event.delta().height(); 117 116 if (!isMomentumEventDuringSyntheticGesture) { 118 117 // Log events that we don't block to the generated offsets log as well, 119 118 // even though we didn't technically generate them, just passed them through. 120 m_currentLogState.latestGeneratedPhase = combinedPhase;121 119 m_currentLogState.totalGeneratedOffset += event.delta().height(); 122 } 123 pushLogEntry(); 120 pushLogEntry(combinedPhase, combinedPhase); 121 } else 122 pushLogEntry(0, combinedPhase); 123 124 124 #endif 125 125 … … 175 175 176 176 #if ENABLE(MOMENTUM_EVENT_DISPATCHER_TEMPORARY_LOGGING) 177 m_currentLogState.latestGeneratedPhase = phase;178 177 m_currentLogState.totalGeneratedOffset += appKitAcceleratedDelta.height(); 179 pushLogEntry( );178 pushLogEntry(phase, 0); 180 179 #endif 181 180 } … … 215 214 216 215 #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()); 218 217 m_dispatcher.queue().dispatchAfter(1_s, [this] { 219 218 flushLog(); … … 289 288 WebCore::FloatSize MomentumEventDispatcher::consumeDeltaForCurrentTime() 290 289 { 290 WebCore::FloatSize delta; 291 291 292 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 } 300 302 301 303 m_currentGesture.currentOffset += delta; … … 358 360 void MomentumEventDispatcher::buildOffsetTableWithInitialDelta(WebCore::FloatSize initialUnacceleratedDelta) 359 361 { 360 #if ENABLE(MOMENTUM_EVENT_DISPATCHER_PREMATURE_ROUNDING)361 m_currentGesture.carryOffset = { };362 #endif363 362 m_currentGesture.offsetTable.clear(); 364 363 365 364 WebCore::FloatSize accumulatedOffset; 366 365 WebCore::FloatSize unacceleratedDelta = initialUnacceleratedDelta; 366 367 float physicalCurveMultiplier = idealCurveFrameRate / m_currentGesture.accelerationCurve->frameRate(); 368 bool inTail = false; 369 WebCore::FloatSize tailCarry; 367 370 368 371 do { … … 370 373 std::tie(unacceleratedDelta, acceleratedDelta) = computeNextDelta(unacceleratedDelta); 371 374 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 372 389 accumulatedOffset += acceleratedDelta; 373 390 m_currentGesture.offsetTable.append(accumulatedOffset); 391 374 392 } while (std::abs(unacceleratedDelta.width()) > 0.5 || std::abs(unacceleratedDelta.height()) > 0.5); 375 393 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 401 void 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); 379 480 } 380 481 … … 428 529 unacceleratedDelta.scale(decayRate); 429 530 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 #endif449 450 531 // The delta queue operates on pre-acceleration deltas, so insert the new event *before* accelerating. 451 didReceiveScrollEventWithInterval( quantizedUnacceleratedDelta, idealCurveFrameInterval);532 didReceiveScrollEventWithInterval(unacceleratedDelta, idealCurveFrameInterval); 452 533 453 534 auto accelerateAxis = [&] (HistoricalDeltas& deltas, float value) { … … 494 575 495 576 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()) 498 579 ); 499 580 … … 507 588 #if ENABLE(MOMENTUM_EVENT_DISPATCHER_TEMPORARY_LOGGING) 508 589 509 void MomentumEventDispatcher::pushLogEntry( )590 void MomentumEventDispatcher::pushLogEntry(uint32_t generatedPhase, uint32_t eventPhase) 510 591 { 511 592 m_currentLogState.time = MonotonicTime::now(); 593 m_currentLogState.generatedPhase = generatedPhase; 594 m_currentLogState.eventPhase = eventPhase; 512 595 m_log.append(m_currentLogState); 513 596 } … … 524 607 RELEASE_LOG(ScrollAnimations, "MomentumEventDispatcher event log: time,generatedOffset,generatedPhase,eventOffset,eventPhase"); 525 608 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); 527 610 528 611 m_log.clear(); -
branches/safari-613.1.11-branch/Source/WebKit/WebProcess/WebPage/MomentumEventDispatcher.h
r286512 r286710 28 28 #if ENABLE(MOMENTUM_EVENT_DISPATCHER) 29 29 30 // FIXME: Remove this once we decide which version we want.31 #define ENABLE_MOMENTUM_EVENT_DISPATCHER_PREMATURE_ROUNDING 032 30 #define ENABLE_MOMENTUM_EVENT_DISPATCHER_TEMPORARY_LOGGING 1 33 31 … … 81 79 82 80 void buildOffsetTableWithInitialDelta(WebCore::FloatSize); 81 void equalizeTailGaps(); 83 82 84 83 // Once consumed, this delta *must* be dispatched in an event. … … 92 91 93 92 #if ENABLE(MOMENTUM_EVENT_DISPATCHER_TEMPORARY_LOGGING) 94 void pushLogEntry( );93 void pushLogEntry(uint32_t generatedPhase, uint32_t eventPhase); 95 94 void flushLog(); 96 95 … … 103 102 float totalEventOffset { 0 }; 104 103 105 uint32_t latestGeneratedPhase { 0 };106 uint32_t latestEventPhase { 0 };104 uint32_t generatedPhase { 0 }; 105 uint32_t eventPhase { 0 }; 107 106 }; 108 107 LogEntry m_currentLogState; … … 134 133 MonotonicTime startTime; 135 134 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 }; 137 139 138 140 #if ENABLE(MOMENTUM_EVENT_DISPATCHER_TEMPORARY_LOGGING)
Note:
See TracChangeset
for help on using the changeset viewer.