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
This commit is contained in:
Bartlomiej Bloniarz
2026-01-20 02:28:23 -08:00
committed by meta-codesync[bot]
parent 5a70daa80d
commit f8564b47fd
24 changed files with 244 additions and 101 deletions
@@ -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
$<TARGET_OBJECTS:react_newarchdefaults>
$<TARGET_OBJECTS:react_performance_cdpmetrics>
$<TARGET_OBJECTS:react_performance_timeline>
$<TARGET_OBJECTS:react_renderer_animationbackend>
$<TARGET_OBJECTS:react_renderer_animations>
$<TARGET_OBJECTS:react_renderer_attributedstring>
$<TARGET_OBJECTS:react_renderer_componentregistry>
@@ -293,6 +295,7 @@ target_include_directories(reactnative
$<TARGET_PROPERTY:react_newarchdefaults,INTERFACE_INCLUDE_DIRECTORIES>
$<TARGET_PROPERTY:react_performance_cdpmetrics,INTERFACE_INCLUDE_DIRECTORIES>
$<TARGET_PROPERTY:react_performance_timeline,INTERFACE_INCLUDE_DIRECTORIES>
$<TARGET_PROPERTY:react_renderer_animationbackend,INTERFACE_INCLUDE_DIRECTORIES>
$<TARGET_PROPERTY:react_renderer_animations,INTERFACE_INCLUDE_DIRECTORIES>
$<TARGET_PROPERTY:react_renderer_attributedstring,INTERFACE_INCLUDE_DIRECTORIES>
$<TARGET_PROPERTY:react_renderer_componentregistry,INTERFACE_INCLUDE_DIRECTORIES>
@@ -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"
@@ -35,10 +35,6 @@
#include <react/renderer/animated/nodes/ValueAnimatedNode.h>
#include <react/renderer/core/EventEmitter.h>
#ifdef RN_USE_ANIMATION_BACKEND
#include <react/renderer/animationbackend/AnimatedPropsBuilder.h>
#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>(animationBackend)
->start(
[this](float /*f*/) { return pullAnimationMutations(); },
isAsync);
animationBackend->start(
[this](float /*f*/) { return pullAnimationMutations(); }, isAsync);
}
#endif
@@ -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<AnimationBackend>(
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<NativeAnimatedNodesManager>(animationBackend_);
std::make_shared<NativeAnimatedNodesManager>(animationBackend);
nativeAnimatedDelegate_ =
std::make_shared<UIManagerNativeAnimatedDelegateBackendImpl>(
animationBackend_);
uiManager->unstable_setAnimationBackend(animationBackend_);
animationBackend);
#endif
} else {
nativeAnimatedNodesManager_ =
@@ -32,7 +32,6 @@ class NativeAnimatedNodesManagerProvider {
std::shared_ptr<EventEmitterListener> getEventEmitterListener();
private:
std::shared_ptr<UIManagerAnimationBackend> animationBackend_;
std::shared_ptr<NativeAnimatedNodesManager> nativeAnimatedNodesManager_;
std::shared_ptr<EventEmitterListenerContainer> eventEmitterListenerContainer_;
@@ -6,11 +6,13 @@
*/
#include "AnimationBackend.h"
#include "AnimatedPropsRegistry.h"
#include <react/debug/react_native_assert.h>
#include <react/renderer/animationbackend/AnimatedPropsSerializer.h>
#include <react/renderer/graphics/Color.h>
#include <chrono>
#include "AnimatedPropsRegistry.h"
#include <utility>
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<CallInvoker> jsInvoker)
: startOnRenderCallback_(std::move(startOnRenderCallback)),
stopOnRenderCallback_(std::move(stopOnRenderCallback)),
directManipulationCallback_(std::move(directManipulationCallback)),
fabricCommitCallback_(std::move(fabricCommitCallback)),
animatedPropsRegistry_(std::make_shared<AnimatedPropsRegistry>()),
uiManager_(uiManager),
jsInvoker_(std::move(jsInvoker)),
commitHook_(uiManager, animatedPropsRegistry_) {}
std::shared_ptr<AnimationChoreographer> animationChoreographer,
std::shared_ptr<UIManager> uiManager)
: animatedPropsRegistry_(std::make_shared<AnimatedPropsRegistry>()),
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<SurfaceId, SurfaceUpdates> 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<Tag, AnimatedProps>& 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<SurfaceId>& 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<CallInvoker> jsInvoker) {
if (!jsInvoker_) {
jsInvoker_ = jsInvoker;
}
}
} // namespace facebook::react
@@ -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<AnimationMutations(float)>;
using StartOnRenderCallback = std::function<void(std::function<void()> &&, bool /* isAsync */)>;
using StopOnRenderCallback = std::function<void(bool /* isAsync */)>;
using DirectManipulationCallback = std::function<void(Tag, const folly::dynamic &)>;
using FabricCommitCallback = std::function<void(std::unordered_map<Tag, folly::dynamic> &)>;
using ResumeCallback = std::function<void()>;
using PauseCallback = std::function<void()>;
std::vector<Callback> callbacks;
const StartOnRenderCallback startOnRenderCallback_;
const StopOnRenderCallback stopOnRenderCallback_;
const DirectManipulationCallback directManipulationCallback_;
const FabricCommitCallback fabricCommitCallback_;
std::shared_ptr<AnimatedPropsRegistry> animatedPropsRegistry_;
UIManager *uiManager_;
std::shared_ptr<CallInvoker> jsInvoker_;
std::shared_ptr<AnimationChoreographer> animationChoreographer_;
AnimationBackendCommitHook commitHook_;
std::weak_ptr<UIManager> uiManager_;
std::shared_ptr<CallInvoker> jsInvoker_;
bool isRenderCallbackStarted_{false};
AnimationBackend(
StartOnRenderCallback &&startOnRenderCallback,
StopOnRenderCallback &&stopOnRenderCallback,
DirectManipulationCallback &&directManipulationCallback,
FabricCommitCallback &&fabricCommitCallback,
UIManager *uiManager,
std::shared_ptr<CallInvoker> jsInvoker);
std::shared_ptr<AnimationChoreographer> animationChoreographer,
std::shared_ptr<UIManager> uiManager);
void commitUpdates(SurfaceId surfaceId, SurfaceUpdates &surfaceUpdates);
void synchronouslyUpdateProps(const std::unordered_map<Tag, AnimatedProps> &updates);
void requestAsyncFlushForSurfaces(const std::set<SurfaceId> &surfaces);
void clearRegistry(SurfaceId surfaceId) override;
void registerJSInvoker(std::shared_ptr<CallInvoker> 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
@@ -7,13 +7,15 @@
#include <react/renderer/animationbackend/AnimationBackendCommitHook.h>
#include <utility>
namespace facebook::react {
AnimationBackendCommitHook::AnimationBackendCommitHook(
UIManager* uiManager,
UIManager& uiManager,
std::shared_ptr<AnimatedPropsRegistry> animatedPropsRegistry)
: animatedPropsRegistry_(std::move(animatedPropsRegistry)) {
uiManager->registerCommitHook(*this);
uiManager.registerCommitHook(*this);
}
RootShadowNode::Unshared AnimationBackendCommitHook::shadowTreeWillCommit(
@@ -19,7 +19,7 @@ class AnimationBackendCommitHook : public UIManagerCommitHook {
std::shared_ptr<AnimatedPropsRegistry> animatedPropsRegistry_;
public:
AnimationBackendCommitHook(UIManager *uiManager, std::shared_ptr<AnimatedPropsRegistry> animatedPropsRegistry);
AnimationBackendCommitHook(UIManager &uiManager, std::shared_ptr<AnimatedPropsRegistry> animatedPropsRegistry);
RootShadowNode::Unshared shadowTreeWillCommit(
const ShadowTree &shadowTree,
const RootShadowNode::Shared &oldRootShadowNode,
@@ -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 <react/renderer/uimanager/UIManagerAnimationBackend.h>
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<UIManagerAnimationBackend> animationBackend)
{
animationBackend_ = animationBackend;
}
void onAnimationFrame(float timestamp) const
{
if (auto animationBackend = animationBackend_.lock()) {
animationBackend->onAnimationFrame(timestamp);
}
}
private:
std::weak_ptr<UIManagerAnimationBackend> animationBackend_;
};
} // namespace facebook::react
@@ -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
)
@@ -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)
@@ -21,6 +21,9 @@
#include <react/renderer/runtimescheduler/RuntimeScheduler.h>
#include <react/renderer/uimanager/UIManager.h>
#include <react/renderer/uimanager/UIManagerBinding.h>
#ifdef RN_USE_ANIMATION_BACKEND
#include <react/renderer/animationbackend/AnimationBackend.h>
#endif
namespace facebook::react {
@@ -55,6 +58,18 @@ Scheduler::Scheduler(
auto uiManager =
std::make_shared<UIManager>(runtimeExecutor_, contextContainer_);
if (ReactNativeFeatureFlags::useSharedAnimatedBackend()) {
#ifdef RN_USE_ANIMATION_BACKEND
auto animationBackend = std::make_shared<AnimationBackend>(
schedulerToolbox.animationChoreographer, uiManager);
schedulerToolbox.animationChoreographer->setAnimationBackend(
animationBackend);
uiManager->unstable_setAnimationBackend(animationBackend);
#endif
}
auto eventOwnerBox = std::make_shared<EventBeat::OwnerBox>();
eventOwnerBox->owner = eventDispatcher_;
@@ -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;
@@ -10,6 +10,7 @@
#include <memory>
#include <ReactCommon/RuntimeExecutor.h>
#include <react/renderer/animationbackend/AnimationChoreographer.h>
#include <react/renderer/componentregistry/ComponentDescriptorFactory.h>
#include <react/renderer/core/EventBeat.h>
#include <react/renderer/leakchecker/LeakChecker.h>
@@ -58,6 +59,12 @@ struct SchedulerToolbox final {
* A list of `UIManagerCommitHook`s that should be registered in `UIManager`.
*/
std::vector<std::shared_ptr<UIManagerCommitHook>> commitHooks;
/*
* Platform-specific choreographer for scheduling animation frame
* callbacks. Required when useSharedAnimatedBackend() is enabled.
*/
std::shared_ptr<AnimationChoreographer> animationChoreographer;
};
} // namespace facebook::react
@@ -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<folly::dynamic>(mergeDynamicProps(
*family.nativeProps_DEPRECATED,
@@ -529,9 +527,9 @@ std::shared_ptr<const ShadowNode> 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<UIManagerAnimationBackend> animationBackend) {
std::shared_ptr<UIManagerAnimationBackend> animationBackend) {
animationBackend_ = animationBackend;
}
@@ -64,7 +64,7 @@ class UIManager final : public ShadowTreeDelegate {
/**
* Sets and gets UIManager's AnimationBackend reference.
*/
void unstable_setAnimationBackend(std::weak_ptr<UIManagerAnimationBackend> animationBackend);
void unstable_setAnimationBackend(std::shared_ptr<UIManagerAnimationBackend> animationBackend);
std::weak_ptr<UIManagerAnimationBackend> unstable_getAnimationBackend();
/**
@@ -248,7 +248,7 @@ class UIManager final : public ShadowTreeDelegate {
std::unique_ptr<LazyShadowTreeRevisionConsistencyManager> lazyShadowTreeRevisionConsistencyManager_;
std::weak_ptr<UIManagerAnimationBackend> animationBackend_;
std::shared_ptr<UIManagerAnimationBackend> animationBackend_;
};
} // namespace facebook::react
@@ -7,20 +7,26 @@
#pragma once
#include <ReactCommon/CallInvoker.h>
#include <react/renderer/components/view/BaseViewProps.h>
#include <react/renderer/core/ShadowNodeFamily.h>
namespace facebook::react {
struct AnimationMutations;
class UIManagerAnimationBackend {
public:
using Callback = std::function<AnimationMutations(float)>;
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<CallInvoker> jsInvoker) = 0;
};
} // namespace facebook::react
@@ -18,6 +18,7 @@
#include <react/devsupport/IDevUIDelegate.h>
#include <react/devsupport/PackagerConnection.h>
#include <react/devsupport/inspector/Inspector.h>
#include <react/featureflags/ReactNativeFeatureFlags.h>
#include <react/http/IHttpClient.h>
#include <react/http/IWebSocketClient.h>
#include <react/io/ResourceLoader.h>
@@ -52,6 +53,7 @@ struct ReactInstanceData {
std::shared_ptr<NativeAnimatedNodesManagerProvider>
animatedNodesManagerProvider;
ReactInstance::BindingsInstallFunc bindingsInstallFunc;
std::shared_ptr<AnimationChoreographer> animationChoreographer;
};
ReactHost::ReactHost(
@@ -66,7 +68,8 @@ ReactHost::ReactHost(
std::shared_ptr<SurfaceDelegate> logBoxSurfaceDelegate,
std::shared_ptr<NativeAnimatedNodesManagerProvider>
animatedNodesManagerProvider,
ReactInstance::BindingsInstallFunc bindingsInstallFunc)
ReactInstance::BindingsInstallFunc bindingsInstallFunc,
std::shared_ptr<AnimationChoreographer> 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<MessageQueueThreadFactory>(MessageQueueThreadFactoryKey)
.has_value()) {
@@ -223,11 +227,13 @@ void ReactHost::createReactInstance() {
return runLoopObserverManager->createEventBeat(
ownerBox, *runtimeScheduler);
};
toolbox.animationChoreographer = reactInstanceData_->animationChoreographer;
schedulerDelegate_ = std::make_unique<SchedulerDelegateImpl>(
reactInstanceData_->mountingManager);
scheduler_ =
std::make_unique<Scheduler>(toolbox, nullptr, schedulerDelegate_.get());
surfaceManager_ = std::make_unique<SurfaceManager>(*scheduler_);
reactInstanceData_->mountingManager->setSchedulerTaskExecutor(
@@ -15,6 +15,7 @@
#include <react/logging/DefaultLogger.h>
#include <react/nativemodule/JavaScriptModule.h>
#include <react/nativemodule/TurboModuleProvider.h>
#include <react/renderer/animationbackend/AnimationChoreographer.h>
#include <react/renderer/scheduler/Scheduler.h>
#include <react/runtime/ReactInstance.h>
#include <react/utils/RunLoopObserverManager.h>
@@ -51,7 +52,8 @@ class ReactHost {
TurboModuleProviders turboModuleProviders = {},
std::shared_ptr<SurfaceDelegate> logBoxSurfaceDelegate = nullptr,
std::shared_ptr<NativeAnimatedNodesManagerProvider> animatedNodesManagerProvider = nullptr,
ReactInstance::BindingsInstallFunc bindingsInstallFunc = nullptr);
ReactInstance::BindingsInstallFunc bindingsInstallFunc = nullptr,
std::shared_ptr<AnimationChoreographer> animationChoreographer = nullptr);
ReactHost(const ReactHost &) = delete;
ReactHost &operator=(const ReactHost &) = delete;
ReactHost(ReactHost &&) noexcept = delete;
@@ -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 <react/renderer/core/ReactPrimitives.h>
#include <react/runtime/ReactInstanceConfig.h>
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
@@ -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 <react/renderer/animationbackend/AnimationChoreographer.h>
#include <react/renderer/core/ReactPrimitives.h>
#include <react/runtime/ReactInstanceConfig.h>
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
@@ -18,6 +18,7 @@
#include <folly/json.h>
#include <glog/logging.h>
#include <logger/react_native_log.h>
#include <react/featureflags/ReactNativeFeatureFlags.h>
#include <react/io/ImageLoaderModule.h>
#include <react/logging/DefaultOnJsErrorHandler.h>
#include <react/nativemodule/cputime/NativeCPUTime.h>
@@ -109,11 +110,19 @@ TesterAppDelegate::TesterAppDelegate(
g_setNativeAnimatedNowTimestampFunction(StubClock::now);
auto provider = std::make_shared<NativeAnimatedNodesManagerProvider>(
[this](std::function<void()>&& onRender, bool /*isAsync*/) {
onAnimationRender_ = std::move(onRender);
},
[this](bool /*isAsync*/) { onAnimationRender_ = nullptr; });
std::shared_ptr<NativeAnimatedNodesManagerProvider> provider;
if (ReactNativeFeatureFlags::useSharedAnimatedBackend()) {
provider = std::make_shared<NativeAnimatedNodesManagerProvider>();
} else {
provider = std::make_shared<NativeAnimatedNodesManagerProvider>(
[this](std::function<void()>&& onRender, bool /*isAsync*/) {
onAnimationRender_ = std::move(onRender);
},
[this](bool /*isAsync*/) { onAnimationRender_ = nullptr; });
}
animationChoreographer_ = std::make_shared<TesterAnimationChoreographer>();
reactHost_ = std::make_unique<ReactHost>(
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<std::chrono::microseconds>(
StubClock::now().time_since_epoch())
.count();
animationChoreographer_->runUITick(static_cast<float>(microseconds) / 1000);
} else if (onAnimationRender_) {
onAnimationRender_();
}
}
@@ -13,6 +13,7 @@
#include <string>
#include <vector>
#include "TesterAnimationChoreographer.h"
#include "TesterMountingManager.h"
namespace facebook::jsi {
@@ -72,6 +73,8 @@ class TesterAppDelegate {
std::shared_ptr<TesterMountingManager> mountingManager_;
std::shared_ptr<TesterAnimationChoreographer> animationChoreographer_;
private:
void runUITick();