diff --git a/packages/react-native/ReactCommon/react/renderer/animated/NativeAnimatedNodesManager.cpp b/packages/react-native/ReactCommon/react/renderer/animated/NativeAnimatedNodesManager.cpp index bf56e2fee4dd..355e69ac7010 100644 --- a/packages/react-native/ReactCommon/react/renderer/animated/NativeAnimatedNodesManager.cpp +++ b/packages/react-native/ReactCommon/react/renderer/animated/NativeAnimatedNodesManager.cpp @@ -519,6 +519,17 @@ NativeAnimatedNodesManager::ensureEventEmitterListener() noexcept { } void NativeAnimatedNodesManager::startRenderCallbackIfNeeded(bool isAsync) { + // This method can be called from either the UI thread or JavaScript thread. + // It ensures `startOnRenderCallback_` is called exactly once using atomic + // operations. We use std::atomic_bool rather than std::mutex to avoid + // potential deadlocks that could occur if we called external code while + // holding a mutex. + auto isRenderCallbackStarted = isRenderCallbackStarted_.exchange(true); + if (isRenderCallbackStarted) { + // onRender callback is already started. + return; + } + if (ReactNativeFeatureFlags::useSharedAnimatedBackend()) { #ifdef RN_USE_ANIMATION_BACKEND if (auto animationBackend = animationBackend_.lock()) { @@ -531,16 +542,6 @@ void NativeAnimatedNodesManager::startRenderCallbackIfNeeded(bool isAsync) { return; } - // This method can be called from either the UI thread or JavaScript thread. - // It ensures `startOnRenderCallback_` is called exactly once using atomic - // operations. We use std::atomic_bool rather than std::mutex to avoid - // potential deadlocks that could occur if we called external code while - // holding a mutex. - auto isRenderCallbackStarted = isRenderCallbackStarted_.exchange(true); - if (isRenderCallbackStarted) { - // onRender callback is already started. - return; - } if (startOnRenderCallback_) { startOnRenderCallback_([this]() { onRender(); }, isAsync); @@ -549,18 +550,21 @@ void NativeAnimatedNodesManager::startRenderCallbackIfNeeded(bool isAsync) { void NativeAnimatedNodesManager::stopRenderCallbackIfNeeded( bool isAsync) noexcept { - if (ReactNativeFeatureFlags::useSharedAnimatedBackend()) { - if (auto animationBackend = animationBackend_.lock()) { - animationBackend->stop(isAsync); - } - return; - } // When multiple threads reach this point, only one thread should call // stopOnRenderCallback_. This synchronization is primarily needed during // destruction of NativeAnimatedNodesManager. In normal operation, // stopRenderCallbackIfNeeded is always called from the UI thread. auto isRenderCallbackStarted = isRenderCallbackStarted_.exchange(false); + if (ReactNativeFeatureFlags::useSharedAnimatedBackend()) { + if (isRenderCallbackStarted) { + if (auto animationBackend = animationBackend_.lock()) { + animationBackend->stop(isAsync); + } + } + return; + } + if (isRenderCallbackStarted) { if (stopOnRenderCallback_) { stopOnRenderCallback_(isAsync); @@ -988,6 +992,7 @@ AnimationMutations NativeAnimatedNodesManager::pullAnimationMutations() { AnimationMutation{tag, nullptr, propsBuilder.get()}); containsChange = true; } + updateViewPropsDirect_.clear(); } if (!containsChange) { diff --git a/packages/react-native/ReactCommon/react/renderer/animated/NativeAnimatedNodesManagerProvider.cpp b/packages/react-native/ReactCommon/react/renderer/animated/NativeAnimatedNodesManagerProvider.cpp index b92bf481d471..ed571e15019b 100644 --- a/packages/react-native/ReactCommon/react/renderer/animated/NativeAnimatedNodesManagerProvider.cpp +++ b/packages/react-native/ReactCommon/react/renderer/animated/NativeAnimatedNodesManagerProvider.cpp @@ -80,12 +80,16 @@ NativeAnimatedNodesManagerProvider::getOrCreate( std::move(directManipulationCallback), std::move(fabricCommitCallback), uiManager); -#endif nativeAnimatedNodesManager_ = std::make_shared(animationBackend_); + nativeAnimatedDelegate_ = + std::make_shared( + animationBackend_); + uiManager->unstable_setAnimationBackend(animationBackend_); +#endif } else { nativeAnimatedNodesManager_ = std::make_shared( @@ -93,6 +97,10 @@ NativeAnimatedNodesManagerProvider::getOrCreate( std::move(fabricCommitCallback), std::move(startOnRenderCallback_), std::move(stopOnRenderCallback_)); + + nativeAnimatedDelegate_ = + std::make_shared( + nativeAnimatedNodesManager_); } addEventEmitterListener( @@ -117,10 +125,6 @@ NativeAnimatedNodesManagerProvider::getOrCreate( return false; })); - nativeAnimatedDelegate_ = - std::make_shared( - nativeAnimatedNodesManager_); - uiManager->setNativeAnimatedDelegate(nativeAnimatedDelegate_); // TODO: remove force casting. diff --git a/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimationBackend.cpp b/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimationBackend.cpp index c9a9b062ee32..2aaa449bb05a 100644 --- a/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimationBackend.cpp +++ b/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimationBackend.cpp @@ -10,6 +10,18 @@ namespace facebook::react { +UIManagerNativeAnimatedDelegateBackendImpl:: + UIManagerNativeAnimatedDelegateBackendImpl( + std::weak_ptr animationBackend) + : animationBackend_(std::move(animationBackend)) {} + +void UIManagerNativeAnimatedDelegateBackendImpl::runAnimationFrame() { + if (auto animationBackendStrong = animationBackend_.lock()) { + animationBackendStrong->onAnimationFrame( + std::chrono::steady_clock::now().time_since_epoch().count() / 1000); + } +} + static inline Props::Shared cloneProps( AnimatedProps& animatedProps, const ShadowNode& shadowNode) { @@ -108,15 +120,20 @@ void AnimationBackend::onAnimationFrame(double timestamp) { void AnimationBackend::start(const Callback& callback, bool isAsync) { callbacks.push_back(callback); // TODO: startOnRenderCallback_ should provide the timestamp from the platform - startOnRenderCallback_( - [this]() { - onAnimationFrame( - std::chrono::steady_clock::now().time_since_epoch().count() / 1000); - }, - isAsync); + if (startOnRenderCallback_) { + startOnRenderCallback_( + [this]() { + onAnimationFrame( + std::chrono::steady_clock::now().time_since_epoch().count() / + 1000); + }, + isAsync); + } } void AnimationBackend::stop(bool isAsync) { - stopOnRenderCallback_(isAsync); + if (stopOnRenderCallback_) { + stopOnRenderCallback_(isAsync); + } callbacks.clear(); } diff --git a/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimationBackend.h b/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimationBackend.h index 9236370c3829..544beae7b16a 100644 --- a/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimationBackend.h +++ b/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimationBackend.h @@ -18,6 +18,18 @@ namespace facebook::react { +class AnimationBackend; + +class UIManagerNativeAnimatedDelegateBackendImpl : public UIManagerNativeAnimatedDelegate { + public: + explicit UIManagerNativeAnimatedDelegateBackendImpl(std::weak_ptr animationBackend); + + void runAnimationFrame() override; + + private: + std::weak_ptr animationBackend_; +}; + struct AnimationMutation { Tag tag; const ShadowNodeFamily *family;