From b338a004676ba67b90cfa4d52ff15650cd73bbfa Mon Sep 17 00:00:00 2001 From: Samuel Susla Date: Wed, 4 Jun 2025 05:05:19 -0700 Subject: [PATCH] make per UI tick calculation more predictable (#51802) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/51802 changelog: [internal] avoid conversions when dealing with time in C++ Animated. This makes tests more predictable. Reviewed By: christophpurrer Differential Revision: D75813200 fbshipit-source-id: b8934848237e5ea7c350d9a5f0175ac0f9202ffd --- .../Libraries/Animated/__tests__/Animated-itest.js | 3 +-- .../renderer/animated/NativeAnimatedNodesManager.cpp | 10 +++++----- .../renderer/animated/NativeAnimatedNodesManager.h | 2 +- .../renderer/animated/drivers/AnimationDriver.cpp | 2 +- .../react/renderer/animated/drivers/AnimationDriver.h | 2 +- .../renderer/animated/drivers/FrameAnimationDriver.cpp | 2 +- 6 files changed, 10 insertions(+), 11 deletions(-) diff --git a/packages/react-native/Libraries/Animated/__tests__/Animated-itest.js b/packages/react-native/Libraries/Animated/__tests__/Animated-itest.js index f534c28ccb9..b0365caf224 100644 --- a/packages/react-native/Libraries/Animated/__tests__/Animated-itest.js +++ b/packages/react-native/Libraries/Animated/__tests__/Animated-itest.js @@ -61,8 +61,7 @@ test('moving box by 100 points', () => { }).start(); }); - // TODO: this fails with any value below 1022, even though anything above 1000 should be enough. - Fantom.unstable_advanceAnimationsByTime(1022); + Fantom.unstable_advanceAnimationsByTime(1000); boundingClientRect = viewElement.getBoundingClientRect(); expect(boundingClientRect.x).toBe(100); diff --git a/packages/react-native/ReactCxxPlatform/react/renderer/animated/NativeAnimatedNodesManager.cpp b/packages/react-native/ReactCxxPlatform/react/renderer/animated/NativeAnimatedNodesManager.cpp index 20870546ecb..e3f8cffa8c5 100644 --- a/packages/react-native/ReactCxxPlatform/react/renderer/animated/NativeAnimatedNodesManager.cpp +++ b/packages/react-native/ReactCxxPlatform/react/renderer/animated/NativeAnimatedNodesManager.cpp @@ -610,7 +610,7 @@ void NativeAnimatedNodesManager::updateNodes( updatedNodeTags_.clear(); } -bool NativeAnimatedNodesManager::onAnimationFrame(uint64_t timestamp) { +bool NativeAnimatedNodesManager::onAnimationFrame(double timestamp) { // Run all active animations auto hasFinishedAnimations = false; std::set finishedAnimationValueNodes; @@ -723,12 +723,12 @@ void NativeAnimatedNodesManager::onRender() { // Step through the animation loop if (isAnimationUpdateNeeded()) { - auto ms = std::chrono::duration_cast( - g_now().time_since_epoch()) - .count(); + auto microseconds = std::chrono::duration_cast( + g_now().time_since_epoch()) + .count(); auto containsChange = - onAnimationFrame(static_cast(ms * TicksPerMs)); + onAnimationFrame(static_cast(microseconds) / 1000.0); if (!containsChange) { // The last animation tick didn't result in any changes to the UI. diff --git a/packages/react-native/ReactCxxPlatform/react/renderer/animated/NativeAnimatedNodesManager.h b/packages/react-native/ReactCxxPlatform/react/renderer/animated/NativeAnimatedNodesManager.h index 1fe11ddf4ab..bcc40e7ed56 100644 --- a/packages/react-native/ReactCxxPlatform/react/renderer/animated/NativeAnimatedNodesManager.h +++ b/packages/react-native/ReactCxxPlatform/react/renderer/animated/NativeAnimatedNodesManager.h @@ -175,7 +175,7 @@ class NativeAnimatedNodesManager { private: void stopRenderCallbackIfNeeded() noexcept; - bool onAnimationFrame(uint64_t timestamp); + bool onAnimationFrame(double timestamp); bool isAnimationUpdateNeeded() const noexcept; diff --git a/packages/react-native/ReactCxxPlatform/react/renderer/animated/drivers/AnimationDriver.cpp b/packages/react-native/ReactCxxPlatform/react/renderer/animated/drivers/AnimationDriver.cpp index d22b3d758a0..086c09a169a 100644 --- a/packages/react-native/ReactCxxPlatform/react/renderer/animated/drivers/AnimationDriver.cpp +++ b/packages/react-native/ReactCxxPlatform/react/renderer/animated/drivers/AnimationDriver.cpp @@ -70,7 +70,7 @@ void AnimationDriver::runAnimationStep(double renderingTime) { } // ticks are 100 nanoseconds, divide by 10000 to get milliseconds. - const auto frameTimeMs = renderingTime / TicksPerMs; + const auto frameTimeMs = renderingTime; auto restarting = false; if (startFrameTimeMs_ < 0) { startFrameTimeMs_ = frameTimeMs; diff --git a/packages/react-native/ReactCxxPlatform/react/renderer/animated/drivers/AnimationDriver.h b/packages/react-native/ReactCxxPlatform/react/renderer/animated/drivers/AnimationDriver.h index 900efb44918..fdf07cd84f0 100644 --- a/packages/react-native/ReactCxxPlatform/react/renderer/animated/drivers/AnimationDriver.h +++ b/packages/react-native/ReactCxxPlatform/react/renderer/animated/drivers/AnimationDriver.h @@ -50,7 +50,7 @@ class AnimationDriver { return isComplete_; } - virtual void runAnimationStep(double renderingTime); + void runAnimationStep(double renderingTime); virtual void updateConfig(folly::dynamic config); diff --git a/packages/react-native/ReactCxxPlatform/react/renderer/animated/drivers/FrameAnimationDriver.cpp b/packages/react-native/ReactCxxPlatform/react/renderer/animated/drivers/FrameAnimationDriver.cpp index 12409b58237..7bcec731ec2 100644 --- a/packages/react-native/ReactCxxPlatform/react/renderer/animated/drivers/FrameAnimationDriver.cpp +++ b/packages/react-native/ReactCxxPlatform/react/renderer/animated/drivers/FrameAnimationDriver.cpp @@ -58,7 +58,7 @@ bool FrameAnimationDriver::update(double timeDeltaMs, bool /*restarting*/) { } const auto startIndex = - static_cast(timeDeltaMs / SingleFrameIntervalMs); + static_cast(std::ceil(timeDeltaMs / SingleFrameIntervalMs)); assert(startIndex >= 0); const auto nextIndex = startIndex + 1;