diff --git a/packages/react-native/Libraries/Animated/__tests__/AnimatedBackend-itest.js b/packages/react-native/Libraries/Animated/__tests__/AnimatedBackend-itest.js
index e36beafdfe382f..3886f2ac320866 100644
--- a/packages/react-native/Libraries/Animated/__tests__/AnimatedBackend-itest.js
+++ b/packages/react-native/Libraries/Animated/__tests__/AnimatedBackend-itest.js
@@ -70,16 +70,6 @@ test('animated opacity', () => {
_opacityAnimation?.stop();
});
- // TODO: T246961305 rendered output should be at this point
- expect(root.getRenderedOutput({props: ['opacity']}).toJSX()).toEqual(
- ,
- );
-
- // Re-render
- Fantom.runTask(() => {
- root.render();
- });
-
expect(root.getRenderedOutput({props: ['opacity']}).toJSX()).toEqual(
,
);
diff --git a/packages/react-native/ReactCommon/react/renderer/animated/NativeAnimatedNodesManager.cpp b/packages/react-native/ReactCommon/react/renderer/animated/NativeAnimatedNodesManager.cpp
index 7341352e0cc336..6a92aa05602328 100644
--- a/packages/react-native/ReactCommon/react/renderer/animated/NativeAnimatedNodesManager.cpp
+++ b/packages/react-native/ReactCommon/react/renderer/animated/NativeAnimatedNodesManager.cpp
@@ -244,8 +244,7 @@ void NativeAnimatedNodesManager::connectAnimatedNodeToShadowNodeFamily(
react_native_assert(propsNodeTag);
auto node = getAnimatedNode(propsNodeTag);
if (node != nullptr && family != nullptr) {
- std::lock_guard lock(tagToShadowNodeFamilyMutex_);
- tagToShadowNodeFamily_[family->getTag()] = family;
+ node->connectToShadowNodeFamily(family);
} else {
LOG(WARNING)
<< "Cannot ConnectAnimatedNodeToShadowNodeFamily, animated node has to be props type";
@@ -261,14 +260,13 @@ void NativeAnimatedNodesManager::disconnectAnimatedNodeFromView(
auto node = getAnimatedNode(propsNodeTag);
if (node != nullptr) {
node->disconnectFromView(viewTag);
+ if (ReactNativeFeatureFlags::useSharedAnimatedBackend()) {
+ node->disconnectFromShadowNodeFamily();
+ }
{
std::lock_guard lock(connectedAnimatedNodesMutex_);
connectedAnimatedNodes_.erase(viewTag);
}
- {
- std::lock_guard lock(tagToShadowNodeFamilyMutex_);
- tagToShadowNodeFamily_.erase(viewTag);
- }
updatedNodeTags_.insert(node->tag());
onManagedPropsRemoved(viewTag);
@@ -907,13 +905,17 @@ void NativeAnimatedNodesManager::schedulePropsCommit(
Tag viewTag,
const folly::dynamic& props,
bool layoutStyleUpdated,
- bool forceFabricCommit) noexcept {
+ bool forceFabricCommit,
+ ShadowNodeFamily::Weak shadowNodeFamily) noexcept {
if (ReactNativeFeatureFlags::useSharedAnimatedBackend()) {
- if (layoutStyleUpdated) {
- mergeObjects(updateViewProps_[viewTag], props);
- } else {
- mergeObjects(updateViewPropsDirect_[viewTag], props);
+ if (forceFabricCommit) {
+ shouldRequestAsyncFlush_.insert(viewTag);
}
+ auto& current = layoutStyleUpdated
+ ? updateViewPropsForBackend_[viewTag]
+ : updateViewPropsDirectForBackend_[viewTag];
+ current.first = std::move(shadowNodeFamily);
+ mergeObjects(current.second, props);
return;
}
@@ -940,6 +942,32 @@ void NativeAnimatedNodesManager::schedulePropsCommit(
}
#ifdef RN_USE_ANIMATION_BACKEND
+
+void NativeAnimatedNodesManager::insertMutations(
+ std::unordered_map>&
+ updates,
+ AnimationMutations& mutations,
+ AnimatedPropsBuilder& propsBuilder,
+ bool hasLayoutUpdates) {
+ for (auto& [tag, update] : updates) {
+ auto weakFamily = update.first;
+
+ if (auto family = weakFamily.lock()) {
+ propsBuilder.storeDynamic(update.second);
+ if (shouldRequestAsyncFlush_.contains(tag)) {
+ mutations.asyncFlushSurfaces.insert(family->getSurfaceId());
+ }
+ mutations.batch.push_back(
+ AnimationMutation{
+ .tag = tag,
+ .family = family,
+ .props = propsBuilder.get(),
+ .hasLayoutUpdates = hasLayoutUpdates,
+ });
+ }
+ }
+}
+
AnimationMutations NativeAnimatedNodesManager::pullAnimationMutations() {
if (!ReactNativeFeatureFlags::useSharedAnimatedBackend()) {
return {};
@@ -962,7 +990,7 @@ AnimationMutations NativeAnimatedNodesManager::pullAnimationMutations() {
task();
}
- AnimationMutations mutations;
+ AnimationMutations mutations{};
// Step through the animation loop
if (isAnimationUpdateNeeded()) {
@@ -1007,49 +1035,18 @@ AnimationMutations NativeAnimatedNodesManager::pullAnimationMutations() {
}
}
- {
- std::lock_guard lock(tagToShadowNodeFamilyMutex_);
- for (auto& [tag, props] : updateViewPropsDirect_) {
- auto familyIt = tagToShadowNodeFamily_.find(tag);
- if (familyIt == tagToShadowNodeFamily_.end()) {
- continue;
- }
+ insertMutations(
+ updateViewPropsDirectForBackend_, mutations, propsBuilder);
- auto weakFamily = familyIt->second;
- if (auto family = weakFamily.lock()) {
- propsBuilder.storeDynamic(props);
- mutations.batch.push_back(
- AnimationMutation{
- .tag = tag,
- .family = family,
- .props = propsBuilder.get(),
- });
- }
- containsChange = true;
- }
- for (auto& [tag, props] : updateViewProps_) {
- auto familyIt = tagToShadowNodeFamily_.find(tag);
- if (familyIt == tagToShadowNodeFamily_.end()) {
- continue;
- }
+ insertMutations(
+ updateViewPropsForBackend_, mutations, propsBuilder, true);
+
+ containsChange = !updateViewPropsForBackend_.empty() ||
+ !updateViewPropsDirectForBackend_.empty();
- auto weakFamily = familyIt->second;
- if (auto family = weakFamily.lock()) {
- propsBuilder.storeDynamic(props);
- mutations.batch.push_back(
- AnimationMutation{
- .tag = tag,
- .family = family,
- .props = propsBuilder.get(),
- .hasLayoutUpdates = true,
- });
- }
- containsChange = true;
- }
- }
if (containsChange) {
- updateViewPropsDirect_.clear();
- updateViewProps_.clear();
+ updateViewPropsDirectForBackend_.clear();
+ updateViewPropsForBackend_.clear();
}
}
@@ -1076,51 +1073,20 @@ AnimationMutations NativeAnimatedNodesManager::pullAnimationMutations() {
isEventAnimationInProgress_ = false;
- {
- std::lock_guard lock(tagToShadowNodeFamilyMutex_);
- for (auto& [tag, props] : updateViewPropsDirect_) {
- auto familyIt = tagToShadowNodeFamily_.find(tag);
- if (familyIt == tagToShadowNodeFamily_.end()) {
- continue;
- }
+ insertMutations(
+ updateViewPropsDirectForBackend_, mutations, propsBuilder);
- auto weakFamily = familyIt->second;
- if (auto family = weakFamily.lock()) {
- propsBuilder.storeDynamic(props);
- mutations.batch.push_back(
- AnimationMutation{
- .tag = tag,
- .family = family,
- .props = propsBuilder.get(),
- });
- }
- }
- for (auto& [tag, props] : updateViewProps_) {
- auto familyIt = tagToShadowNodeFamily_.find(tag);
- if (familyIt == tagToShadowNodeFamily_.end()) {
- continue;
- }
+ insertMutations(
+ updateViewPropsForBackend_, mutations, propsBuilder, true);
- auto weakFamily = familyIt->second;
- if (auto family = weakFamily.lock()) {
- propsBuilder.storeDynamic(props);
- mutations.batch.push_back(
- AnimationMutation{
- .tag = tag,
- .family = family,
- .props = propsBuilder.get(),
- .hasLayoutUpdates = true,
- });
- }
- }
- }
- updateViewProps_.clear();
- updateViewPropsDirect_.clear();
+ updateViewPropsForBackend_.clear();
+ updateViewPropsDirectForBackend_.clear();
}
} else {
// There is no active animation. Stop the render callback.
stopRenderCallbackIfNeeded(false);
}
+ shouldRequestAsyncFlush_.clear();
return mutations;
}
#endif
diff --git a/packages/react-native/ReactCommon/react/renderer/animated/NativeAnimatedNodesManager.h b/packages/react-native/ReactCommon/react/renderer/animated/NativeAnimatedNodesManager.h
index 40632cbc1dcf71..9a40a66497f3ab 100644
--- a/packages/react-native/ReactCommon/react/renderer/animated/NativeAnimatedNodesManager.h
+++ b/packages/react-native/ReactCommon/react/renderer/animated/NativeAnimatedNodesManager.h
@@ -121,6 +121,11 @@ class NativeAnimatedNodesManager {
void setAnimatedNodeOffset(Tag tag, double offset);
#ifdef RN_USE_ANIMATION_BACKEND
+ void insertMutations(
+ std::unordered_map> &updates,
+ AnimationMutations &mutations,
+ AnimatedPropsBuilder &propsBuilder,
+ bool hasLayoutUpdates = false);
AnimationMutations pullAnimationMutations();
#endif
@@ -153,7 +158,8 @@ class NativeAnimatedNodesManager {
Tag viewTag,
const folly::dynamic &props,
bool layoutStyleUpdated,
- bool forceFabricCommit) noexcept;
+ bool forceFabricCommit,
+ ShadowNodeFamily::Weak shadowNodeFamily = {}) noexcept;
/**
* Commits all pending animated property updates to their respective views.
@@ -260,10 +266,9 @@ class NativeAnimatedNodesManager {
std::unordered_map updateViewProps_{};
std::unordered_map updateViewPropsDirect_{};
-
- mutable std::mutex tagToShadowNodeFamilyMutex_;
- std::unordered_map> tagToShadowNodeFamily_{};
-
+ std::unordered_map> updateViewPropsForBackend_{};
+ std::unordered_map> updateViewPropsDirectForBackend_{};
+ std::unordered_set shouldRequestAsyncFlush_{};
/*
* Sometimes a view is not longer connected to a PropsAnimatedNode, but
* NativeAnimated has previously changed the view's props via direct
diff --git a/packages/react-native/ReactCommon/react/renderer/animated/NativeAnimatedNodesManagerProvider.cpp b/packages/react-native/ReactCommon/react/renderer/animated/NativeAnimatedNodesManagerProvider.cpp
index bf8bad41ba96c2..b0d08ce645fe6c 100644
--- a/packages/react-native/ReactCommon/react/renderer/animated/NativeAnimatedNodesManagerProvider.cpp
+++ b/packages/react-native/ReactCommon/react/renderer/animated/NativeAnimatedNodesManagerProvider.cpp
@@ -95,7 +95,8 @@ NativeAnimatedNodesManagerProvider::getOrCreate(
std::move(stopOnRenderCallback_),
std::move(directManipulationCallback),
std::move(fabricCommitCallback),
- uiManager);
+ uiManager,
+ jsInvoker);
nativeAnimatedNodesManager_ =
std::make_shared(animationBackend_);
diff --git a/packages/react-native/ReactCommon/react/renderer/animated/nodes/PropsAnimatedNode.cpp b/packages/react-native/ReactCommon/react/renderer/animated/nodes/PropsAnimatedNode.cpp
index 48660d81531d6d..5cee6b457e4a0c 100644
--- a/packages/react-native/ReactCommon/react/renderer/animated/nodes/PropsAnimatedNode.cpp
+++ b/packages/react-native/ReactCommon/react/renderer/animated/nodes/PropsAnimatedNode.cpp
@@ -12,6 +12,7 @@
#include "PropsAnimatedNode.h"
#include
+#include
#include
#include
#include
@@ -55,6 +56,15 @@ PropsAnimatedNode::PropsAnimatedNode(
}
}
+void PropsAnimatedNode::connectToShadowNodeFamily(
+ ShadowNodeFamily::Weak shadowNodeFamily) {
+ shadowNodeFamily_ = std::move(shadowNodeFamily);
+}
+
+void PropsAnimatedNode::disconnectFromShadowNodeFamily() {
+ shadowNodeFamily_.reset();
+}
+
void PropsAnimatedNode::connectToView(Tag viewTag) {
react_native_assert(
connectedViewTag_ == animated::undefinedAnimatedNodeIdentifier &&
@@ -74,8 +84,17 @@ void PropsAnimatedNode::disconnectFromView(Tag viewTag) {
void PropsAnimatedNode::restoreDefaultValues() {
// If node is already disconnected from View, we cannot restore default values
if (connectedViewTag_ != animated::undefinedAnimatedNodeIdentifier) {
- manager_->schedulePropsCommit(
- connectedViewTag_, folly::dynamic::object(), false, false);
+ if (ReactNativeFeatureFlags::useSharedAnimatedBackend()) {
+ manager_->schedulePropsCommit(
+ connectedViewTag_,
+ folly::dynamic::object(),
+ false,
+ false,
+ shadowNodeFamily_);
+ } else {
+ manager_->schedulePropsCommit(
+ connectedViewTag_, folly::dynamic::object(), false, false);
+ }
}
}
@@ -147,8 +166,17 @@ void PropsAnimatedNode::update(bool forceFabricCommit) {
layoutStyleUpdated_ = isLayoutStyleUpdated(getConfig()["props"], *manager_);
- manager_->schedulePropsCommit(
- connectedViewTag_, props_, layoutStyleUpdated_, forceFabricCommit);
+ if (ReactNativeFeatureFlags::useSharedAnimatedBackend()) {
+ manager_->schedulePropsCommit(
+ connectedViewTag_,
+ props_,
+ layoutStyleUpdated_,
+ forceFabricCommit,
+ shadowNodeFamily_);
+ } else {
+ manager_->schedulePropsCommit(
+ connectedViewTag_, props_, layoutStyleUpdated_, forceFabricCommit);
+ }
}
} // namespace facebook::react
diff --git a/packages/react-native/ReactCommon/react/renderer/animated/nodes/PropsAnimatedNode.h b/packages/react-native/ReactCommon/react/renderer/animated/nodes/PropsAnimatedNode.h
index 3da4db587d46ac..51b1ac76660432 100644
--- a/packages/react-native/ReactCommon/react/renderer/animated/nodes/PropsAnimatedNode.h
+++ b/packages/react-native/ReactCommon/react/renderer/animated/nodes/PropsAnimatedNode.h
@@ -21,6 +21,11 @@ namespace facebook::react {
class PropsAnimatedNode final : public AnimatedNode {
public:
PropsAnimatedNode(Tag tag, const folly::dynamic &config, NativeAnimatedNodesManager &manager);
+
+ // Only called when `useSharedAnimatedBackend`==true
+ void connectToShadowNodeFamily(ShadowNodeFamily::Weak shadowNodeFamily);
+ void disconnectFromShadowNodeFamily();
+
void connectToView(Tag viewTag);
void disconnectFromView(Tag viewTag);
void restoreDefaultValues();
@@ -51,6 +56,7 @@ class PropsAnimatedNode final : public AnimatedNode {
bool layoutStyleUpdated_{false};
Tag connectedViewTag_{animated::undefinedAnimatedNodeIdentifier};
+ ShadowNodeFamily::Weak shadowNodeFamily_;
// Needed for PlatformColor resolver
SurfaceId connectedRootTag_{animated::undefinedAnimatedNodeIdentifier};
diff --git a/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimationBackend.cpp b/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimationBackend.cpp
index 6cb8a707c4e0ac..ed353a38f999b0 100644
--- a/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimationBackend.cpp
+++ b/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimationBackend.cpp
@@ -68,21 +68,25 @@ AnimationBackend::AnimationBackend(
StopOnRenderCallback&& stopOnRenderCallback,
DirectManipulationCallback&& directManipulationCallback,
FabricCommitCallback&& fabricCommitCallback,
- UIManager* uiManager)
+ 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_) {}
void AnimationBackend::onAnimationFrame(double timestamp) {
std::unordered_map surfaceUpdates;
+ std::set asyncFlushSurfaces;
for (auto& callback : callbacks) {
- auto muatations = callback(static_cast(timestamp));
- for (auto& mutation : muatations.batch) {
+ auto mutations = callback(static_cast(timestamp));
+ asyncFlushSurfaces.merge(mutations.asyncFlushSurfaces);
+ for (auto& mutation : mutations.batch) {
const auto family = mutation.family;
react_native_assert(family != nullptr);
@@ -103,6 +107,8 @@ void AnimationBackend::onAnimationFrame(double timestamp) {
synchronouslyUpdateProps(updates.propsMap);
}
}
+
+ requestAsyncFlushForSurfaces(asyncFlushSurfaces);
}
void AnimationBackend::start(const Callback& callback, bool isAsync) {
@@ -168,6 +174,25 @@ void AnimationBackend::synchronouslyUpdateProps(
}
}
+void AnimationBackend::requestAsyncFlushForSurfaces(
+ const std::set& surfaces) {
+ 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(
+ surfaceId, [](const ShadowTree& shadowTree) {
+ shadowTree.commit(
+ [](const RootShadowNode& oldRootShadowNode) {
+ return std::static_pointer_cast(
+ oldRootShadowNode.ShadowNode::clone({}));
+ },
+ {.source = ShadowTreeCommitSource::AnimationEndSync});
+ });
+ });
+ }
+}
+
void AnimationBackend::clearRegistry(SurfaceId surfaceId) {
animatedPropsRegistry_->clear(surfaceId);
}
diff --git a/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimationBackend.h b/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimationBackend.h
index 7e0bd9bbe81fb8..b8394f908b663a 100644
--- a/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimationBackend.h
+++ b/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimationBackend.h
@@ -7,11 +7,14 @@
#pragma once
+#include
#include
#include
#include
#include
#include
+#include
+#include
#include
#include "AnimatedProps.h"
#include "AnimatedPropsBuilder.h"
@@ -41,6 +44,7 @@ struct AnimationMutation {
struct AnimationMutations {
std::vector batch;
+ std::set asyncFlushSurfaces;
};
class AnimationBackend : public UIManagerAnimationBackend {
@@ -58,6 +62,7 @@ class AnimationBackend : public UIManagerAnimationBackend {
const FabricCommitCallback fabricCommitCallback_;
std::shared_ptr animatedPropsRegistry_;
UIManager *uiManager_;
+ std::shared_ptr jsInvoker_;
AnimationBackendCommitHook commitHook_;
AnimationBackend(
@@ -65,9 +70,11 @@ class AnimationBackend : public UIManagerAnimationBackend {
StopOnRenderCallback &&stopOnRenderCallback,
DirectManipulationCallback &&directManipulationCallback,
FabricCommitCallback &&fabricCommitCallback,
- UIManager *uiManager);
+ UIManager *uiManager,
+ std::shared_ptr jsInvoker);
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 onAnimationFrame(double timestamp) override;
diff --git a/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimationBackendCommitHook.cpp b/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimationBackendCommitHook.cpp
index 6d359ea8c1c38e..2058d3d5caa747 100644
--- a/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimationBackendCommitHook.cpp
+++ b/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimationBackendCommitHook.cpp
@@ -21,7 +21,8 @@ RootShadowNode::Unshared AnimationBackendCommitHook::shadowTreeWillCommit(
const RootShadowNode::Shared& oldRootShadowNode,
const RootShadowNode::Unshared& newRootShadowNode,
const ShadowTreeCommitOptions& commitOptions) noexcept {
- if (commitOptions.source != ShadowTreeCommitSource::React) {
+ if (commitOptions.source != ShadowTreeCommitSource::React &&
+ commitOptions.source != ShadowTreeCommitSource::AnimationEndSync) {
return newRootShadowNode;
}
diff --git a/packages/react-native/ReactCommon/react/renderer/mounting/ShadowTree.h b/packages/react-native/ReactCommon/react/renderer/mounting/ShadowTree.h
index 3b44d6e49ca470..c1f51fc1a91e82 100644
--- a/packages/react-native/ReactCommon/react/renderer/mounting/ShadowTree.h
+++ b/packages/react-native/ReactCommon/react/renderer/mounting/ShadowTree.h
@@ -50,6 +50,7 @@ enum class ShadowTreeCommitMode {
enum class ShadowTreeCommitSource {
Unknown,
React,
+ AnimationEndSync,
};
struct ShadowTreeCommitOptions {