From f8564b47fd742bce3dc68b129ea0d4ae04e61bc3 Mon Sep 17 00:00:00 2001 From: Bartlomiej Bloniarz Date: Tue, 20 Jan 2026 02:28:23 -0800 Subject: [PATCH] Move AnimationBackend initialization from Animated (#55103) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/55103 This diff decouples AnimationBackend from Animated. Now the backend is intialized in the Scheduler, from where it's passed to UIManager. Animation frontends (such as Animated) can then obtain a reference to the backend, and use it to schedule animation frame updates. # Changelog [General] [Changed] - Moved AnimationBackend initiailzation to `Scheduler` [General] [Added] - `AnimationChoreographer` interface with an implementation for fantom tests Reviewed By: zeyap Differential Revision: D89663251 fbshipit-source-id: 428a83d78ef92a89095989fe3121c578cf8d3983 --- .../ReactAndroid/src/main/jni/CMakeLists.txt | 3 + .../ReactCommon/React-Fabric.podspec | 1 + .../animated/NativeAnimatedNodesManager.cpp | 10 +-- .../NativeAnimatedNodesManagerProvider.cpp | 19 ++--- .../NativeAnimatedNodesManagerProvider.h | 1 - .../animationbackend/AnimationBackend.cpp | 81 +++++++++++-------- .../animationbackend/AnimationBackend.h | 28 +++---- .../AnimationBackendCommitHook.cpp | 6 +- .../AnimationBackendCommitHook.h | 2 +- .../animationbackend/AnimationChoreographer.h | 39 +++++++++ .../renderer/animationbackend/CMakeLists.txt | 1 - .../react/renderer/scheduler/CMakeLists.txt | 2 + .../react/renderer/scheduler/Scheduler.cpp | 15 ++++ .../react/renderer/scheduler/Scheduler.h | 2 - .../renderer/scheduler/SchedulerToolbox.h | 7 ++ .../react/renderer/uimanager/UIManager.cpp | 16 ++-- .../react/renderer/uimanager/UIManager.h | 4 +- .../uimanager/UIManagerAnimationBackend.h | 8 +- .../react/runtime/ReactHost.cpp | 10 ++- .../react/runtime/ReactHost.h | 4 +- .../src/TesterAnimationChoreographer.cpp | 27 +++++++ .../tester/src/TesterAnimationChoreographer.h | 26 ++++++ .../tester/src/TesterAppDelegate.cpp | 30 +++++-- .../tester/src/TesterAppDelegate.h | 3 + 24 files changed, 244 insertions(+), 101 deletions(-) create mode 100644 packages/react-native/ReactCommon/react/renderer/animationbackend/AnimationChoreographer.h create mode 100644 private/react-native-fantom/tester/src/TesterAnimationChoreographer.cpp create mode 100644 private/react-native-fantom/tester/src/TesterAnimationChoreographer.h diff --git a/packages/react-native/ReactAndroid/src/main/jni/CMakeLists.txt b/packages/react-native/ReactAndroid/src/main/jni/CMakeLists.txt index dc7447d202c..979ec533075 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/CMakeLists.txt +++ b/packages/react-native/ReactAndroid/src/main/jni/CMakeLists.txt @@ -82,6 +82,7 @@ add_react_common_subdir(react/debug) add_react_common_subdir(react/featureflags) add_react_common_subdir(react/performance/cdpmetrics) add_react_common_subdir(react/performance/timeline) +add_react_common_subdir(react/renderer/animationbackend) add_react_common_subdir(react/renderer/animations) add_react_common_subdir(react/renderer/attributedstring) add_react_common_subdir(react/renderer/componentregistry) @@ -200,6 +201,7 @@ add_library(reactnative $ $ $ + $ $ $ $ @@ -293,6 +295,7 @@ target_include_directories(reactnative $ $ $ + $ $ $ $ diff --git a/packages/react-native/ReactCommon/React-Fabric.podspec b/packages/react-native/ReactCommon/React-Fabric.podspec index 3e31bdd04e0..7b452f6e4cf 100644 --- a/packages/react-native/ReactCommon/React-Fabric.podspec +++ b/packages/react-native/ReactCommon/React-Fabric.podspec @@ -157,6 +157,7 @@ Pod::Spec.new do |s| ss.source_files = podspec_sources("react/renderer/scheduler/**/*.{m,mm,cpp,h}", "react/renderer/scheduler/**/*.h") ss.header_dir = "react/renderer/scheduler" + ss.dependency "React-Fabric/animationbackend" ss.dependency "React-performancecdpmetrics" ss.dependency "React-performancetimeline" ss.dependency "React-Fabric/observers/events" diff --git a/packages/react-native/ReactCommon/react/renderer/animated/NativeAnimatedNodesManager.cpp b/packages/react-native/ReactCommon/react/renderer/animated/NativeAnimatedNodesManager.cpp index 762c5eb6b49..26c272e37b8 100644 --- a/packages/react-native/ReactCommon/react/renderer/animated/NativeAnimatedNodesManager.cpp +++ b/packages/react-native/ReactCommon/react/renderer/animated/NativeAnimatedNodesManager.cpp @@ -35,10 +35,6 @@ #include #include -#ifdef RN_USE_ANIMATION_BACKEND -#include -#endif - namespace facebook::react { // Global function pointer for getting current time. Current time @@ -559,10 +555,8 @@ void NativeAnimatedNodesManager::startRenderCallbackIfNeeded(bool isAsync) { if (ReactNativeFeatureFlags::useSharedAnimatedBackend()) { #ifdef RN_USE_ANIMATION_BACKEND if (auto animationBackend = animationBackend_.lock()) { - std::static_pointer_cast(animationBackend) - ->start( - [this](float /*f*/) { return pullAnimationMutations(); }, - isAsync); + animationBackend->start( + [this](float /*f*/) { return pullAnimationMutations(); }, isAsync); } #endif diff --git a/packages/react-native/ReactCommon/react/renderer/animated/NativeAnimatedNodesManagerProvider.cpp b/packages/react-native/ReactCommon/react/renderer/animated/NativeAnimatedNodesManagerProvider.cpp index b0d08ce645f..dcfd6eaa33f 100644 --- a/packages/react-native/ReactCommon/react/renderer/animated/NativeAnimatedNodesManagerProvider.cpp +++ b/packages/react-native/ReactCommon/react/renderer/animated/NativeAnimatedNodesManagerProvider.cpp @@ -88,24 +88,17 @@ NativeAnimatedNodesManagerProvider::getOrCreate( if (ReactNativeFeatureFlags::useSharedAnimatedBackend()) { #ifdef RN_USE_ANIMATION_BACKEND - // TODO: this should be initialized outside of animated, but for now it - // was convenient to do it here - animationBackend_ = std::make_shared( - std::move(startOnRenderCallback_), - std::move(stopOnRenderCallback_), - std::move(directManipulationCallback), - std::move(fabricCommitCallback), - uiManager, - jsInvoker); + auto animationBackend = uiManager->unstable_getAnimationBackend().lock(); + react_native_assert( + animationBackend != nullptr && "animationBackend is nullptr"); + animationBackend->registerJSInvoker(jsInvoker); nativeAnimatedNodesManager_ = - std::make_shared(animationBackend_); + std::make_shared(animationBackend); nativeAnimatedDelegate_ = std::make_shared( - animationBackend_); - - uiManager->unstable_setAnimationBackend(animationBackend_); + animationBackend); #endif } else { nativeAnimatedNodesManager_ = diff --git a/packages/react-native/ReactCommon/react/renderer/animated/NativeAnimatedNodesManagerProvider.h b/packages/react-native/ReactCommon/react/renderer/animated/NativeAnimatedNodesManagerProvider.h index de8263df1ec..9b1164d3278 100644 --- a/packages/react-native/ReactCommon/react/renderer/animated/NativeAnimatedNodesManagerProvider.h +++ b/packages/react-native/ReactCommon/react/renderer/animated/NativeAnimatedNodesManagerProvider.h @@ -32,7 +32,6 @@ class NativeAnimatedNodesManagerProvider { std::shared_ptr getEventEmitterListener(); private: - std::shared_ptr animationBackend_; std::shared_ptr nativeAnimatedNodesManager_; std::shared_ptr eventEmitterListenerContainer_; diff --git a/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimationBackend.cpp b/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimationBackend.cpp index 0b0d25e9d37..b83e3a7a650 100644 --- a/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimationBackend.cpp +++ b/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimationBackend.cpp @@ -6,11 +6,13 @@ */ #include "AnimationBackend.h" +#include "AnimatedPropsRegistry.h" + #include #include #include #include -#include "AnimatedPropsRegistry.h" +#include namespace facebook::react { @@ -51,20 +53,14 @@ static inline Props::Shared cloneProps( } AnimationBackend::AnimationBackend( - StartOnRenderCallback&& startOnRenderCallback, - StopOnRenderCallback&& stopOnRenderCallback, - DirectManipulationCallback&& directManipulationCallback, - FabricCommitCallback&& fabricCommitCallback, - UIManager* uiManager, - std::shared_ptr jsInvoker) - : startOnRenderCallback_(std::move(startOnRenderCallback)), - stopOnRenderCallback_(std::move(stopOnRenderCallback)), - directManipulationCallback_(std::move(directManipulationCallback)), - fabricCommitCallback_(std::move(fabricCommitCallback)), - animatedPropsRegistry_(std::make_shared()), - uiManager_(uiManager), - jsInvoker_(std::move(jsInvoker)), - commitHook_(uiManager, animatedPropsRegistry_) {} + std::shared_ptr animationChoreographer, + std::shared_ptr uiManager) + : animatedPropsRegistry_(std::make_shared()), + animationChoreographer_(std::move(animationChoreographer)), + commitHook_(*uiManager, animatedPropsRegistry_), + uiManager_(std::move(uiManager)) { + react_native_assert(uiManager_.expired() == false); +} void AnimationBackend::onAnimationFrame(double timestamp) { std::unordered_map surfaceUpdates; @@ -98,23 +94,18 @@ void AnimationBackend::onAnimationFrame(double timestamp) { requestAsyncFlushForSurfaces(asyncFlushSurfaces); } -void AnimationBackend::start(const Callback& callback, bool isAsync) { +void AnimationBackend::start(const Callback& callback, bool /*isAsync*/) { callbacks.push_back(callback); - // TODO: startOnRenderCallback_ should provide the timestamp from the - // platform - if (startOnRenderCallback_) { - startOnRenderCallback_( - [this]() { - onAnimationFrame( - std::chrono::steady_clock::now().time_since_epoch().count() / - 1000); - }, - isAsync); + if (!isRenderCallbackStarted_) { + animationChoreographer_->resume(); + isRenderCallbackStarted_ = true; } } -void AnimationBackend::stop(bool isAsync) { - if (stopOnRenderCallback_) { - stopOnRenderCallback_(isAsync); + +void AnimationBackend::stop(bool /*isAsync*/) { + if (isRenderCallbackStarted_) { + animationChoreographer_->pause(); + isRenderCallbackStarted_ = false; } callbacks.clear(); } @@ -127,9 +118,15 @@ void AnimationBackend::trigger() { void AnimationBackend::commitUpdates( SurfaceId surfaceId, SurfaceUpdates& surfaceUpdates) { + auto uiManager = uiManager_.lock(); + if (!uiManager) { + return; + } + auto& surfaceFamilies = surfaceUpdates.families; auto& updates = surfaceUpdates.propsMap; - uiManager_->getShadowTreeRegistry().visit( + + uiManager->getShadowTreeRegistry().visit( surfaceId, [&surfaceFamilies, &updates](const ShadowTree& shadowTree) { shadowTree.commit( [&surfaceFamilies, @@ -160,19 +157,28 @@ void AnimationBackend::synchronouslyUpdateProps( const std::unordered_map& updates) { for (auto& [tag, animatedProps] : updates) { // TODO: We shouldn't repack it into dynamic, but for that a rewrite - // of directManipulationCallback_ is needed + // of synchronouslyUpdateViewOnUIThread is needed auto dyn = animationbackend::packAnimatedProps(animatedProps); - directManipulationCallback_(tag, std::move(dyn)); + if (auto uiManager = uiManager_.lock()) { + uiManager->synchronouslyUpdateViewOnUIThread(tag, dyn); + } } } void AnimationBackend::requestAsyncFlushForSurfaces( const std::set& surfaces) { + react_native_assert( + jsInvoker_ != nullptr || + surfaces.empty() && "jsInvoker_ was not provided"); for (const auto& surfaceId : surfaces) { // perform an empty commit on the js thread, to force the commit hook to // push updated shadow nodes to react through RSNRU - jsInvoker_->invokeAsync([this, surfaceId]() { - uiManager_->getShadowTreeRegistry().visit( + jsInvoker_->invokeAsync([weakUIManager = uiManager_, surfaceId]() { + auto uiManager = weakUIManager.lock(); + if (!uiManager) { + return; + } + uiManager->getShadowTreeRegistry().visit( surfaceId, [](const ShadowTree& shadowTree) { shadowTree.commit( [](const RootShadowNode& oldRootShadowNode) { @@ -189,4 +195,11 @@ void AnimationBackend::clearRegistry(SurfaceId surfaceId) { animatedPropsRegistry_->clear(surfaceId); } +void AnimationBackend::registerJSInvoker( + std::shared_ptr jsInvoker) { + if (!jsInvoker_) { + jsInvoker_ = jsInvoker; + } +} + } // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimationBackend.h b/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimationBackend.h index 62030edff94..bd64b95f381 100644 --- a/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimationBackend.h +++ b/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimationBackend.h @@ -19,6 +19,7 @@ #include "AnimatedProps.h" #include "AnimatedPropsRegistry.h" #include "AnimationBackendCommitHook.h" +#include "AnimationChoreographer.h" namespace facebook::react { @@ -49,36 +50,29 @@ struct AnimationMutations { class AnimationBackend : public UIManagerAnimationBackend { public: using Callback = std::function; - using StartOnRenderCallback = std::function &&, bool /* isAsync */)>; - using StopOnRenderCallback = std::function; - using DirectManipulationCallback = std::function; - using FabricCommitCallback = std::function &)>; + using ResumeCallback = std::function; + using PauseCallback = std::function; std::vector callbacks; - const StartOnRenderCallback startOnRenderCallback_; - const StopOnRenderCallback stopOnRenderCallback_; - const DirectManipulationCallback directManipulationCallback_; - const FabricCommitCallback fabricCommitCallback_; std::shared_ptr animatedPropsRegistry_; - UIManager *uiManager_; - std::shared_ptr jsInvoker_; + std::shared_ptr animationChoreographer_; AnimationBackendCommitHook commitHook_; + std::weak_ptr uiManager_; + std::shared_ptr jsInvoker_; + bool isRenderCallbackStarted_{false}; AnimationBackend( - StartOnRenderCallback &&startOnRenderCallback, - StopOnRenderCallback &&stopOnRenderCallback, - DirectManipulationCallback &&directManipulationCallback, - FabricCommitCallback &&fabricCommitCallback, - UIManager *uiManager, - std::shared_ptr jsInvoker); + std::shared_ptr animationChoreographer, + std::shared_ptr uiManager); void commitUpdates(SurfaceId surfaceId, SurfaceUpdates &surfaceUpdates); void synchronouslyUpdateProps(const std::unordered_map &updates); void requestAsyncFlushForSurfaces(const std::set &surfaces); void clearRegistry(SurfaceId surfaceId) override; + void registerJSInvoker(std::shared_ptr jsInvoker) override; void onAnimationFrame(double timestamp) override; void trigger() override; - void start(const Callback &callback, bool isAsync); + void start(const Callback &callback, bool isAsync) override; void stop(bool isAsync) override; }; } // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimationBackendCommitHook.cpp b/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimationBackendCommitHook.cpp index 2058d3d5caa..19898d233e6 100644 --- a/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimationBackendCommitHook.cpp +++ b/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimationBackendCommitHook.cpp @@ -7,13 +7,15 @@ #include +#include + namespace facebook::react { AnimationBackendCommitHook::AnimationBackendCommitHook( - UIManager* uiManager, + UIManager& uiManager, std::shared_ptr animatedPropsRegistry) : animatedPropsRegistry_(std::move(animatedPropsRegistry)) { - uiManager->registerCommitHook(*this); + uiManager.registerCommitHook(*this); } RootShadowNode::Unshared AnimationBackendCommitHook::shadowTreeWillCommit( diff --git a/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimationBackendCommitHook.h b/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimationBackendCommitHook.h index 7d6c723d663..83b9bcf7899 100644 --- a/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimationBackendCommitHook.h +++ b/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimationBackendCommitHook.h @@ -19,7 +19,7 @@ class AnimationBackendCommitHook : public UIManagerCommitHook { std::shared_ptr animatedPropsRegistry_; public: - AnimationBackendCommitHook(UIManager *uiManager, std::shared_ptr animatedPropsRegistry); + AnimationBackendCommitHook(UIManager &uiManager, std::shared_ptr animatedPropsRegistry); RootShadowNode::Unshared shadowTreeWillCommit( const ShadowTree &shadowTree, const RootShadowNode::Shared &oldRootShadowNode, diff --git a/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimationChoreographer.h b/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimationChoreographer.h new file mode 100644 index 00000000000..43179eba644 --- /dev/null +++ b/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimationChoreographer.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include + +namespace facebook::react { + +/* + * This class serves as an interface for native animation frame scheduling that can be used as abstraction in + * ReactCxxPlatform. + */ +class AnimationChoreographer { + public: + virtual ~AnimationChoreographer() = default; + + virtual void resume() = 0; + virtual void pause() = 0; + void setAnimationBackend(std::weak_ptr animationBackend) + { + animationBackend_ = animationBackend; + } + void onAnimationFrame(float timestamp) const + { + if (auto animationBackend = animationBackend_.lock()) { + animationBackend->onAnimationFrame(timestamp); + } + } + + private: + std::weak_ptr animationBackend_; +}; + +} // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/renderer/animationbackend/CMakeLists.txt b/packages/react-native/ReactCommon/react/renderer/animationbackend/CMakeLists.txt index f44356c3ce7..aea055997c5 100644 --- a/packages/react-native/ReactCommon/react/renderer/animationbackend/CMakeLists.txt +++ b/packages/react-native/ReactCommon/react/renderer/animationbackend/CMakeLists.txt @@ -20,7 +20,6 @@ target_link_libraries(react_renderer_animationbackend react_renderer_graphics react_renderer_mounting react_renderer_uimanager - react_renderer_scheduler glog folly_runtime ) diff --git a/packages/react-native/ReactCommon/react/renderer/scheduler/CMakeLists.txt b/packages/react-native/ReactCommon/react/renderer/scheduler/CMakeLists.txt index 27263be7474..2563bc12c6e 100644 --- a/packages/react-native/ReactCommon/react/renderer/scheduler/CMakeLists.txt +++ b/packages/react-native/ReactCommon/react/renderer/scheduler/CMakeLists.txt @@ -21,6 +21,7 @@ target_link_libraries(react_renderer_scheduler react_featureflags react_performance_cdpmetrics react_performance_timeline + react_renderer_animationbackend react_renderer_componentregistry react_renderer_core react_renderer_debug @@ -36,3 +37,4 @@ target_link_libraries(react_renderer_scheduler ) target_compile_reactnative_options(react_renderer_scheduler PRIVATE) target_compile_options(react_renderer_scheduler PRIVATE -Wpedantic) +target_compile_definitions(react_renderer_scheduler PRIVATE RN_USE_ANIMATION_BACKEND) diff --git a/packages/react-native/ReactCommon/react/renderer/scheduler/Scheduler.cpp b/packages/react-native/ReactCommon/react/renderer/scheduler/Scheduler.cpp index 8c42afc5296..268cffdc191 100644 --- a/packages/react-native/ReactCommon/react/renderer/scheduler/Scheduler.cpp +++ b/packages/react-native/ReactCommon/react/renderer/scheduler/Scheduler.cpp @@ -21,6 +21,9 @@ #include #include #include +#ifdef RN_USE_ANIMATION_BACKEND +#include +#endif namespace facebook::react { @@ -55,6 +58,18 @@ Scheduler::Scheduler( auto uiManager = std::make_shared(runtimeExecutor_, contextContainer_); + if (ReactNativeFeatureFlags::useSharedAnimatedBackend()) { +#ifdef RN_USE_ANIMATION_BACKEND + auto animationBackend = std::make_shared( + schedulerToolbox.animationChoreographer, uiManager); + + schedulerToolbox.animationChoreographer->setAnimationBackend( + animationBackend); + + uiManager->unstable_setAnimationBackend(animationBackend); +#endif + } + auto eventOwnerBox = std::make_shared(); eventOwnerBox->owner = eventDispatcher_; diff --git a/packages/react-native/ReactCommon/react/renderer/scheduler/Scheduler.h b/packages/react-native/ReactCommon/react/renderer/scheduler/Scheduler.h index c324655da72..80be380a48e 100644 --- a/packages/react-native/ReactCommon/react/renderer/scheduler/Scheduler.h +++ b/packages/react-native/ReactCommon/react/renderer/scheduler/Scheduler.h @@ -46,8 +46,6 @@ class Scheduler final : public UIManagerDelegate { /* * Registers and unregisters a `SurfaceHandler` object in the `Scheduler`. - * All registered `SurfaceHandler` objects must be unregistered - * (with the same `Scheduler`) before their deallocation. */ void registerSurface(const SurfaceHandler &surfaceHandler) const noexcept; void unregisterSurface(const SurfaceHandler &surfaceHandler) const noexcept; diff --git a/packages/react-native/ReactCommon/react/renderer/scheduler/SchedulerToolbox.h b/packages/react-native/ReactCommon/react/renderer/scheduler/SchedulerToolbox.h index f1446cf4c0d..d38e85303ed 100644 --- a/packages/react-native/ReactCommon/react/renderer/scheduler/SchedulerToolbox.h +++ b/packages/react-native/ReactCommon/react/renderer/scheduler/SchedulerToolbox.h @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -58,6 +59,12 @@ struct SchedulerToolbox final { * A list of `UIManagerCommitHook`s that should be registered in `UIManager`. */ std::vector> commitHooks; + + /* + * Platform-specific choreographer for scheduling animation frame + * callbacks. Required when useSharedAnimatedBackend() is enabled. + */ + std::shared_ptr animationChoreographer; }; } // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/renderer/uimanager/UIManager.cpp b/packages/react-native/ReactCommon/react/renderer/uimanager/UIManager.cpp index 931bfce1c1e..e3f3fb5c967 100644 --- a/packages/react-native/ReactCommon/react/renderer/uimanager/UIManager.cpp +++ b/packages/react-native/ReactCommon/react/renderer/uimanager/UIManager.cpp @@ -207,9 +207,7 @@ void UIManager::completeSurface( surfaceId, shadowTree.getCurrentRevision().rootShadowNode); if (ReactNativeFeatureFlags::useSharedAnimatedBackend()) { - if (auto animationBackend = animationBackend_.lock()) { - animationBackend->clearRegistry(surfaceId); - } + animationBackend_->clearRegistry(surfaceId); } } }); @@ -437,8 +435,8 @@ void UIManager::setNativeProps_DEPRECATED( if (family.nativeProps_DEPRECATED) { // Values in `rawProps` patch (take precedence over) // `nativeProps_DEPRECATED`. For example, if both `nativeProps_DEPRECATED` - // and `rawProps` contain key 'A'. Value from `rawProps` overrides what was - // previously in `nativeProps_DEPRECATED`. + // and `rawProps` contain key 'A'. Value from `rawProps` overrides what + // was previously in `nativeProps_DEPRECATED`. family.nativeProps_DEPRECATED = std::make_unique(mergeDynamicProps( *family.nativeProps_DEPRECATED, @@ -529,9 +527,9 @@ std::shared_ptr UIManager::findShadowNodeByTag_DEPRECATED( // pointer to a root node because of the possible data race. // To work around this, we ask for a commit and immediately cancel it // returning `nullptr` instead of a new shadow tree. - // We don't want to add a way to access a stored pointer to a root node - // because this `findShadowNodeByTag` is deprecated. It is only added - // to make migration to the new architecture easier. + // We don't want to add a way to access a stored pointer to a root + // node because this `findShadowNodeByTag` is deprecated. It is only + // added to make migration to the new architecture easier. shadowTree.tryCommit( [&](const RootShadowNode& oldRootShadowNode) { rootShadowNode = &oldRootShadowNode; @@ -687,7 +685,7 @@ void UIManager::setNativeAnimatedDelegate( } void UIManager::unstable_setAnimationBackend( - std::weak_ptr animationBackend) { + std::shared_ptr animationBackend) { animationBackend_ = animationBackend; } diff --git a/packages/react-native/ReactCommon/react/renderer/uimanager/UIManager.h b/packages/react-native/ReactCommon/react/renderer/uimanager/UIManager.h index 794a5559a55..eac4b5d9a49 100644 --- a/packages/react-native/ReactCommon/react/renderer/uimanager/UIManager.h +++ b/packages/react-native/ReactCommon/react/renderer/uimanager/UIManager.h @@ -64,7 +64,7 @@ class UIManager final : public ShadowTreeDelegate { /** * Sets and gets UIManager's AnimationBackend reference. */ - void unstable_setAnimationBackend(std::weak_ptr animationBackend); + void unstable_setAnimationBackend(std::shared_ptr animationBackend); std::weak_ptr unstable_getAnimationBackend(); /** @@ -248,7 +248,7 @@ class UIManager final : public ShadowTreeDelegate { std::unique_ptr lazyShadowTreeRevisionConsistencyManager_; - std::weak_ptr animationBackend_; + std::shared_ptr animationBackend_; }; } // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/renderer/uimanager/UIManagerAnimationBackend.h b/packages/react-native/ReactCommon/react/renderer/uimanager/UIManagerAnimationBackend.h index 75b62328104..2cecaa09313 100644 --- a/packages/react-native/ReactCommon/react/renderer/uimanager/UIManagerAnimationBackend.h +++ b/packages/react-native/ReactCommon/react/renderer/uimanager/UIManagerAnimationBackend.h @@ -7,20 +7,26 @@ #pragma once +#include #include #include namespace facebook::react { +struct AnimationMutations; + class UIManagerAnimationBackend { public: + using Callback = std::function; + virtual ~UIManagerAnimationBackend() = default; virtual void onAnimationFrame(double timestamp) = 0; - // TODO: T240293839 Move over start() function and mutation types + virtual void start(const Callback &callback, bool isAsync) = 0; virtual void stop(bool isAsync) = 0; virtual void clearRegistry(SurfaceId surfaceId) = 0; virtual void trigger() = 0; + virtual void registerJSInvoker(std::shared_ptr jsInvoker) = 0; }; } // namespace facebook::react diff --git a/packages/react-native/ReactCxxPlatform/react/runtime/ReactHost.cpp b/packages/react-native/ReactCxxPlatform/react/runtime/ReactHost.cpp index ca1f38300f3..7affd88dca0 100644 --- a/packages/react-native/ReactCxxPlatform/react/runtime/ReactHost.cpp +++ b/packages/react-native/ReactCxxPlatform/react/runtime/ReactHost.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -52,6 +53,7 @@ struct ReactInstanceData { std::shared_ptr animatedNodesManagerProvider; ReactInstance::BindingsInstallFunc bindingsInstallFunc; + std::shared_ptr animationChoreographer; }; ReactHost::ReactHost( @@ -66,7 +68,8 @@ ReactHost::ReactHost( std::shared_ptr logBoxSurfaceDelegate, std::shared_ptr animatedNodesManagerProvider, - ReactInstance::BindingsInstallFunc bindingsInstallFunc) + ReactInstance::BindingsInstallFunc bindingsInstallFunc, + std::shared_ptr animationChoreographer) : reactInstanceConfig_(std::move(reactInstanceConfig)) { auto componentRegistryFactory = mountingManager->getComponentRegistryFactory(); @@ -82,7 +85,8 @@ ReactHost::ReactHost( .turboModuleProviders = std::move(turboModuleProviders), .logBoxSurfaceDelegate = logBoxSurfaceDelegate, .animatedNodesManagerProvider = animatedNodesManagerProvider, - .bindingsInstallFunc = std::move(bindingsInstallFunc)}); + .bindingsInstallFunc = std::move(bindingsInstallFunc), + .animationChoreographer = std::move(animationChoreographer)}); if (!reactInstanceData_->contextContainer ->find(MessageQueueThreadFactoryKey) .has_value()) { @@ -223,11 +227,13 @@ void ReactHost::createReactInstance() { return runLoopObserverManager->createEventBeat( ownerBox, *runtimeScheduler); }; + toolbox.animationChoreographer = reactInstanceData_->animationChoreographer; schedulerDelegate_ = std::make_unique( reactInstanceData_->mountingManager); scheduler_ = std::make_unique(toolbox, nullptr, schedulerDelegate_.get()); + surfaceManager_ = std::make_unique(*scheduler_); reactInstanceData_->mountingManager->setSchedulerTaskExecutor( diff --git a/packages/react-native/ReactCxxPlatform/react/runtime/ReactHost.h b/packages/react-native/ReactCxxPlatform/react/runtime/ReactHost.h index f589d16bb4b..47645e4a119 100644 --- a/packages/react-native/ReactCxxPlatform/react/runtime/ReactHost.h +++ b/packages/react-native/ReactCxxPlatform/react/runtime/ReactHost.h @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -51,7 +52,8 @@ class ReactHost { TurboModuleProviders turboModuleProviders = {}, std::shared_ptr logBoxSurfaceDelegate = nullptr, std::shared_ptr animatedNodesManagerProvider = nullptr, - ReactInstance::BindingsInstallFunc bindingsInstallFunc = nullptr); + ReactInstance::BindingsInstallFunc bindingsInstallFunc = nullptr, + std::shared_ptr animationChoreographer = nullptr); ReactHost(const ReactHost &) = delete; ReactHost &operator=(const ReactHost &) = delete; ReactHost(ReactHost &&) noexcept = delete; diff --git a/private/react-native-fantom/tester/src/TesterAnimationChoreographer.cpp b/private/react-native-fantom/tester/src/TesterAnimationChoreographer.cpp new file mode 100644 index 00000000000..0c9f9a5d07a --- /dev/null +++ b/private/react-native-fantom/tester/src/TesterAnimationChoreographer.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "TesterAnimationChoreographer.h" +#include +#include + +namespace facebook::react { + +void TesterAnimationChoreographer::resume() { + isPaused_ = false; +} +void TesterAnimationChoreographer::pause() { + isPaused_ = true; +} + +void TesterAnimationChoreographer::runUITick(float timestamp) { + if (!isPaused_) { + onAnimationFrame(timestamp); + } +} + +} // namespace facebook::react diff --git a/private/react-native-fantom/tester/src/TesterAnimationChoreographer.h b/private/react-native-fantom/tester/src/TesterAnimationChoreographer.h new file mode 100644 index 00000000000..d60c8e8a0b5 --- /dev/null +++ b/private/react-native-fantom/tester/src/TesterAnimationChoreographer.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include +#include + +namespace facebook::react { + +class TesterAnimationChoreographer : public AnimationChoreographer { + public: + void resume() override; + void pause() override; + void runUITick(float timestamp); + + private: + bool isPaused_{false}; +}; + +} // namespace facebook::react diff --git a/private/react-native-fantom/tester/src/TesterAppDelegate.cpp b/private/react-native-fantom/tester/src/TesterAppDelegate.cpp index 77a6cd17376..35afbb35aab 100644 --- a/private/react-native-fantom/tester/src/TesterAppDelegate.cpp +++ b/private/react-native-fantom/tester/src/TesterAppDelegate.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -109,11 +110,19 @@ TesterAppDelegate::TesterAppDelegate( g_setNativeAnimatedNowTimestampFunction(StubClock::now); - auto provider = std::make_shared( - [this](std::function&& onRender, bool /*isAsync*/) { - onAnimationRender_ = std::move(onRender); - }, - [this](bool /*isAsync*/) { onAnimationRender_ = nullptr; }); + std::shared_ptr provider; + + if (ReactNativeFeatureFlags::useSharedAnimatedBackend()) { + provider = std::make_shared(); + } else { + provider = std::make_shared( + [this](std::function&& onRender, bool /*isAsync*/) { + onAnimationRender_ = std::move(onRender); + }, + [this](bool /*isAsync*/) { onAnimationRender_ = nullptr; }); + } + + animationChoreographer_ = std::make_shared(); reactHost_ = std::make_unique( reactInstanceConfig, @@ -125,7 +134,9 @@ TesterAppDelegate::TesterAppDelegate( nullptr, turboModuleProviders, nullptr, - std::move(provider)); + std::move(provider), + nullptr, + animationChoreographer_); // Ensure that the ReactHost initialisation is completed. // This will call `setupJSNativeFantom`. @@ -253,7 +264,12 @@ void TesterAppDelegate::produceFramesForDuration(double milliseconds) { } void TesterAppDelegate::runUITick() { - if (onAnimationRender_) { + if (ReactNativeFeatureFlags::useSharedAnimatedBackend()) { + auto microseconds = std::chrono::duration_cast( + StubClock::now().time_since_epoch()) + .count(); + animationChoreographer_->runUITick(static_cast(microseconds) / 1000); + } else if (onAnimationRender_) { onAnimationRender_(); } } diff --git a/private/react-native-fantom/tester/src/TesterAppDelegate.h b/private/react-native-fantom/tester/src/TesterAppDelegate.h index b8dc581348d..d754f958570 100644 --- a/private/react-native-fantom/tester/src/TesterAppDelegate.h +++ b/private/react-native-fantom/tester/src/TesterAppDelegate.h @@ -13,6 +13,7 @@ #include #include +#include "TesterAnimationChoreographer.h" #include "TesterMountingManager.h" namespace facebook::jsi { @@ -72,6 +73,8 @@ class TesterAppDelegate { std::shared_ptr mountingManager_; + std::shared_ptr animationChoreographer_; + private: void runUITick();