diff --git a/packages/react-native/Libraries/Animated/__tests__/AnimatedBackend-itest.js b/packages/react-native/Libraries/Animated/__tests__/AnimatedBackend-itest.js index 15df90071365..df8472d8fe90 100644 --- a/packages/react-native/Libraries/Animated/__tests__/AnimatedBackend-itest.js +++ b/packages/react-native/Libraries/Animated/__tests__/AnimatedBackend-itest.js @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @fantom_flags useSharedAnimatedBackend:true updateRuntimeShadowNodeReferencesOnCommitThread:* + * @fantom_flags useSharedAnimatedBackend:true updateRuntimeShadowNodeReferencesOnCommitThread:* optimizedAnimatedPropUpdates:* * @flow strict-local * @format */ @@ -17,7 +17,7 @@ import * as Fantom from '@react-native/fantom'; import nullthrows from 'nullthrows'; import * as React from 'react'; import {Component, createRef, memo, useEffect, useMemo, useState} from 'react'; -import {Animated, View, useAnimatedValue} from 'react-native'; +import {Animated, Easing, View, useAnimatedValue} from 'react-native'; // marginLeft (and the other margin props) are only on the native animated // allowlist when the shared backend is enabled. This verifies the prop is @@ -77,6 +77,196 @@ test('animate marginLeft layout prop', () => { ); }); +test('non-layout props stay on the direct path while another view animates layout', () => { + const movingRef = createRef(); + + let _translateX; + let _translateXAnimation; + let _siblingHeight; + let _siblingHeightAnimation; + + function MyApp() { + const translateX = useAnimatedValue(0); + const siblingHeight = useAnimatedValue(10); + _translateX = translateX; + _siblingHeight = siblingHeight; + return ( + + + + + ); + } + + const root = Fantom.createRoot(); + + Fantom.runTask(() => { + root.render(); + }); + + Fantom.runTask(() => { + _translateXAnimation = Animated.timing(_translateX, { + toValue: 100, + duration: 200, + useNativeDriver: true, + }).start(); + _siblingHeightAnimation = Animated.timing(_siblingHeight, { + toValue: 110, + duration: 200, + useNativeDriver: true, + }).start(); + }); + + Fantom.unstable_produceFramesForDuration(100); + + // The sibling's height went through a commit; the transform did not. + expect(root.getRenderedOutput({props: ['height']}).toJSX()).toEqual( + + + + , + ); + expect( + Fantom.unstable_getDirectManipulationProps(nullthrows(movingRef.current)) + .transform, + ).toEqual([{translateX: 50}]); + + Fantom.runTask(() => { + _translateXAnimation?.stop(); + _siblingHeightAnimation?.stop(); + }); +}); + +test('direct-path props survive a re-render while another view animates layout', () => { + let _opacity; + let _opacityAnimation; + let _siblingHeight; + let _siblingHeightAnimation; + let _setWidth; + + function MyApp() { + const opacity = useAnimatedValue(0); + const siblingHeight = useAnimatedValue(10); + const [width, setWidth] = useState(100); + _opacity = opacity; + _siblingHeight = siblingHeight; + _setWidth = setWidth; + return ( + + + + + ); + } + + const root = Fantom.createRoot(); + + Fantom.runTask(() => { + root.render(); + }); + + Fantom.runTask(() => { + _opacityAnimation = Animated.timing(_opacity, { + toValue: 0.5, + duration: 1000, + useNativeDriver: true, + }).start(); + _siblingHeightAnimation = Animated.timing(_siblingHeight, { + toValue: 110, + duration: 1000, + useNativeDriver: true, + }).start(); + }); + + Fantom.unstable_produceFramesForDuration(500); + + Fantom.runTask(() => { + _setWidth(150); + }); + + expect( + root.getRenderedOutput({props: ['opacity', 'width', 'height']}).toJSX(), + ).toEqual( + + + + , + ); + + Fantom.runTask(() => { + _opacityAnimation?.stop(); + _siblingHeightAnimation?.stop(); + }); +}); + +test('a view keeps writing its props directly after it starts animating layout', () => { + const viewRef = createRef(); + + let _opacity; + let _opacityAnimation; + let _height; + let _heightAnimation; + + function MyApp() { + const opacity = useAnimatedValue(0); + const height = useAnimatedValue(10); + _opacity = opacity; + _height = height; + return ( + + ); + } + + const root = Fantom.createRoot(); + + Fantom.runTask(() => { + root.render(); + }); + + const viewElement = nullthrows(viewRef.current); + + Fantom.runTask(() => { + _opacityAnimation = Animated.timing(_opacity, { + toValue: 0.5, + duration: 1000, + easing: Easing.linear, + useNativeDriver: true, + }).start(); + }); + + Fantom.unstable_produceFramesForDuration(500); + + expect( + Fantom.unstable_getDirectManipulationProps(viewElement).opacity, + ).toBeCloseTo(0.25, 0.001); + + Fantom.runTask(() => { + _heightAnimation = Animated.timing(_height, { + toValue: 110, + duration: 1000, + easing: Easing.linear, + useNativeDriver: true, + }).start(); + }); + + Fantom.unstable_produceFramesForDuration(250); + + expect(root.getRenderedOutput({props: ['height']}).toJSX()).toEqual( + , + ); + expect( + Fantom.unstable_getDirectManipulationProps(viewElement).opacity, + ).toBeCloseTo(0.375, 0.001); + + Fantom.runTask(() => { + _opacityAnimation?.stop(); + _heightAnimation?.stop(); + }); +}); + test('animated opacity', () => { let _opacity; let _opacityAnimation; diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlags.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlags.kt index b4cf39c69be8..3194db3a7f53 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlags.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlags.kt @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<<5a3796afdf02ba39aa578d83dc3f8332>> */ /** @@ -409,7 +409,7 @@ public object ReactNativeFeatureFlags { public fun fuseboxWebSocketEventsEnabled(): Boolean = accessor.fuseboxWebSocketEventsEnabled() /** - * When enabled, uses optimized platform-specific paths to apply animated props synchronously. On Android, this uses a batched int/double buffer protocol with a single JNI call. On iOS, this passes AnimatedProps directly through the delegate chain and applies them via cloneProps, avoiding the folly::dynamic round-trip. + * When enabled, uses optimized platform-specific paths to apply animated props synchronously. On Android, this uses a batched int/double buffer protocol with a single JNI call. Other platforms apply the props of each view through the existing synchronous update. */ @JvmStatic public fun optimizedAnimatedPropUpdates(): Boolean = accessor.optimizedAnimatedPropUpdates() diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h index f16f57306b30..31d42cb06fb6 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<4c93a39dd2a8ee5ba24f3f8bc99ac1ae>> + * @generated SignedSource<<467f462832f58b37ed97105595fbc843>> */ /** @@ -357,7 +357,7 @@ class ReactNativeFeatureFlags { RN_EXPORT static bool fuseboxWebSocketEventsEnabled(); /** - * When enabled, uses optimized platform-specific paths to apply animated props synchronously. On Android, this uses a batched int/double buffer protocol with a single JNI call. On iOS, this passes AnimatedProps directly through the delegate chain and applies them via cloneProps, avoiding the folly::dynamic round-trip. + * When enabled, uses optimized platform-specific paths to apply animated props synchronously. On Android, this uses a batched int/double buffer protocol with a single JNI call. Other platforms apply the props of each view through the existing synchronous update. */ RN_EXPORT static bool optimizedAnimatedPropUpdates(); diff --git a/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimatedPropsRegistry.cpp b/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimatedPropsRegistry.cpp index 02fef88041cd..e8df4f4ca0e1 100644 --- a/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimatedPropsRegistry.cpp +++ b/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimatedPropsRegistry.cpp @@ -6,31 +6,40 @@ */ #include "AnimatedPropsRegistry.h" +#include #include #include "AnimatedProps.h" namespace facebook::react { +void mergeAnimatedRawProps( + folly::dynamic& target, + const folly::dynamic& source) { + if (!target.isObject() || !source.isObject()) { + target = source; + return; + } + for (const auto& [key, value] : source.items()) { + target[key] = value; + } +} + void AnimatedPropsRegistry::update( - const std::unordered_map& surfaceUpdates) { + const std::vector& batches) { auto lock = std::lock_guard(mutex_); - for (const auto& [surfaceId, updates] : surfaceUpdates) { - auto contextIt = surfaceContexts_.find(surfaceId); - if (contextIt == surfaceContexts_.end()) { - continue; - } - auto& surfaceContext = contextIt->second; - auto& pendingMap = surfaceContext.pendingMap; - auto& pendingFamilies = surfaceContext.pendingFamilies; - - auto& updatesMap = updates.propsMap; - auto& updatesFamilies = updates.families; - - for (auto& family : updatesFamilies) { - pendingFamilies.insert(family); - } - - for (auto& [tag, animatedProps] : updatesMap) { + for (const auto& mutations : batches) { + for (const auto& mutation : mutations.batch) { + const auto& family = mutation.family; + react_native_assert(family != nullptr); + auto contextIt = surfaceContexts_.find(family->getSurfaceId()); + if (contextIt == surfaceContexts_.end()) { + continue; + } + auto& surfaceContext = contextIt->second; + auto& pendingMap = surfaceContext.pendingMap; + surfaceContext.pendingFamilies.insert(family); + const auto tag = mutation.tag; + const auto& animatedProps = mutation.props; auto it = pendingMap.find(tag); if (it == pendingMap.end()) { it = pendingMap.insert_or_assign(tag, std::make_unique()) @@ -44,8 +53,7 @@ void AnimatedPropsRegistry::update( auto& currentRawProps = snapshot->rawProps; if (currentRawProps) { - auto newRawPropsDynamic = newRawProps.toDynamic(); - currentRawProps->merge_patch(newRawPropsDynamic); + mergeAnimatedRawProps(*currentRawProps, newRawProps.toDynamic()); } else { currentRawProps = std::make_unique(newRawProps.toDynamic()); @@ -83,7 +91,8 @@ AnimatedPropsRegistry::getMap(SurfaceId surfaceId) { auto& currentSnapshot = currentIt->second; if (propsSnapshot->rawProps) { if (currentSnapshot->rawProps) { - currentSnapshot->rawProps->merge_patch(*propsSnapshot->rawProps); + mergeAnimatedRawProps( + *currentSnapshot->rawProps, *propsSnapshot->rawProps); } else { currentSnapshot->rawProps = std::move(propsSnapshot->rawProps); } diff --git a/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimatedPropsRegistry.h b/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimatedPropsRegistry.h index 3c004ec9502f..da0e6e884248 100644 --- a/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimatedPropsRegistry.h +++ b/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimatedPropsRegistry.h @@ -15,6 +15,7 @@ #include #include #include "AnimatedProps.h" +#include "AnimationMutation.h" namespace facebook::react { @@ -29,17 +30,17 @@ struct SurfaceContext { std::unordered_set> pendingFamilies, families; }; -struct SurfaceUpdates { - std::unordered_set> families; - std::unordered_map propsMap; - bool hasLayoutUpdates{false}; -}; - using SnapshotMap = std::unordered_map>; +/* + * Merges raw animated props per key, like the props merged by the animation + * drivers. A null value is kept, so it still resets the prop on commit. + */ +void mergeAnimatedRawProps(folly::dynamic &target, const folly::dynamic &source); + class AnimatedPropsRegistry { public: - void update(const std::unordered_map &surfaceUpdates); + void update(const std::vector &batches); void initializeSurface(SurfaceId surfaceId); void clear(SurfaceId surfaceId); void clearOnSurfaceStop(SurfaceId surfaceId); diff --git a/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimationBackend.cpp b/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimationBackend.cpp index e38386a9e44a..5b10873950e9 100644 --- a/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimationBackend.cpp +++ b/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimationBackend.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -48,6 +49,26 @@ static inline Props::Shared cloneProps( return newProps; } +// Combines two mutations of the same view from one frame. +static void mergeMutation( + AnimationMutation& existing, + AnimationMutation&& incoming) { + auto& props = existing.props; + for (auto& animatedProp : incoming.props.props) { + props.props.push_back(std::move(animatedProp)); + } + if (incoming.props.rawProps) { + if (props.rawProps) { + auto merged = props.rawProps->toDynamic(); + mergeAnimatedRawProps(merged, incoming.props.rawProps->toDynamic()); + props.rawProps = std::make_unique(std::move(merged)); + } else { + props.rawProps = std::move(incoming.props.rawProps); + } + } + existing.hasLayoutUpdates |= incoming.hasLayoutUpdates; +} + AnimationBackend::AnimationBackend( std::shared_ptr animationChoreographer, std::shared_ptr uiManager) @@ -80,14 +101,13 @@ void AnimationBackend::unpackMutations( std::unordered_map& surfaceUpdates, std::set& asyncFlushSurfaces) { for (auto& mutation : mutations.batch) { - const auto family = mutation.family; - react_native_assert(family != nullptr); - - auto& [families, updates, hasLayoutUpdates] = - surfaceUpdates[family->getSurfaceId()]; - hasLayoutUpdates |= mutation.hasLayoutUpdates; - families.insert(family); - updates[mutation.tag] = std::move(mutation.props); + auto& updates = surfaceUpdates[mutation.family->getSurfaceId()]; + const auto tag = mutation.tag; + if (auto it = updates.find(tag); it != updates.end()) { + mergeMutation(it->second, std::move(mutation)); + } else { + updates.emplace(tag, std::move(mutation)); + } } asyncFlushSurfaces.merge(mutations.asyncFlushSurfaces); @@ -96,23 +116,42 @@ void AnimationBackend::unpackMutations( void AnimationBackend::applySurfaceUpdates( std::unordered_map& surfaceUpdates, const std::set& asyncFlushSurfaces) { - animatedPropsRegistry_->update(surfaceUpdates); - for (auto& [surfaceId, updates] : surfaceUpdates) { - if (updates.hasLayoutUpdates) { - commitUpdates(surfaceId, updates); - } else { - synchronouslyUpdateProps(updates.propsMap); + SurfaceUpdates layoutUpdates; + std::unordered_map directProps; + for (auto& [tag, mutation] : updates) { + if (mutation.hasLayoutUpdates) { + layoutUpdates.emplace(tag, std::move(mutation)); + } else { + directProps.emplace(tag, std::move(mutation.props)); + } + } + if (!layoutUpdates.empty()) { + // A platform may re-apply a view's earlier direct writes when mounting + // it, so committed views are written directly too. + if (auto uiManager = uiManager_.lock()) { + for (const auto& [tag, mutation] : layoutUpdates) { + uiManager->synchronouslyUpdateViewOnUIThread( + tag, animationbackend::packAnimatedProps(mutation.props)); + } + } + commitUpdates(surfaceId, layoutUpdates); + } + if (!directProps.empty()) { + synchronouslyUpdateProps(directProps); } } requestAsyncFlushForSurfaces(asyncFlushSurfaces); } -void AnimationBackend::applyMutations(AnimationMutations mutations) { +void AnimationBackend::applyMutations(std::vector batches) { + animatedPropsRegistry_->update(batches); std::unordered_map surfaceUpdates; std::set asyncFlushSurfaces; - unpackMutations(mutations, surfaceUpdates, asyncFlushSurfaces); + for (auto& mutations : batches) { + unpackMutations(mutations, surfaceUpdates, asyncFlushSurfaces); + } applySurfaceUpdates(surfaceUpdates, asyncFlushSurfaces); } @@ -124,13 +163,17 @@ void AnimationBackend::onAnimationFrame(AnimationTimestamp timestamp) { callbacksCopy = callbacks; } - std::unordered_map surfaceUpdates; - std::set asyncFlushSurfaces; - for (auto& callbackWithId : callbacksCopy) { - auto mutations = callbackWithId.callback(timestamp); - unpackMutations(mutations, surfaceUpdates, asyncFlushSurfaces); - } - applySurfaceUpdates(surfaceUpdates, asyncFlushSurfaces); + // Sized up front rather than grown: MSVC's std::set move isn't noexcept, so + // growing a vector of AnimationMutations would try to copy move-only props. + std::vector batches(callbacksCopy.size()); + std::transform( + callbacksCopy.begin(), + callbacksCopy.end(), + batches.begin(), + [timestamp](const CallbackWithId& callbackWithId) { + return callbackWithId.callback(timestamp); + }); + applyMutations(std::move(batches)); } CallbackId AnimationBackend::start(const Callback& callback) { @@ -169,8 +212,9 @@ void AnimationBackend::trigger() { void AnimationBackend::pushAnimationMutations(const Callback& callback) { auto timestamp = animationChoreographer_->now(); - auto mutations = callback(timestamp); - applyMutations(std::move(mutations)); + std::vector batches(1); + batches[0] = callback(timestamp); + applyMutations(std::move(batches)); } void AnimationBackend::commitUpdates( @@ -181,24 +225,28 @@ void AnimationBackend::commitUpdates( return; } - auto& surfaceFamilies = surfaceUpdates.families; - auto& updates = surfaceUpdates.propsMap; + std::unordered_set> surfaceFamilies; + for (const auto& [tag, mutation] : surfaceUpdates) { + surfaceFamilies.insert(mutation.family); + } uiManager->getShadowTreeRegistry().visit( - surfaceId, [&surfaceFamilies, &updates](const ShadowTree& shadowTree) { + surfaceId, + [&surfaceFamilies, &surfaceUpdates](const ShadowTree& shadowTree) { shadowTree.commit( [&surfaceFamilies, - &updates](const RootShadowNode& oldRootShadowNode) { + &surfaceUpdates](const RootShadowNode& oldRootShadowNode) { return std::static_pointer_cast( oldRootShadowNode.cloneMultiple( surfaceFamilies, - [&surfaceFamilies, &updates]( + [&surfaceFamilies, &surfaceUpdates]( const ShadowNode& shadowNode, const ShadowNodeFragment& fragment) { auto newProps = ShadowNodeFragment::propsPlaceholder(); if (surfaceFamilies.contains( shadowNode.getFamilyShared())) { - auto& animatedProps = updates.at(shadowNode.getTag()); + auto& animatedProps = + surfaceUpdates.at(shadowNode.getTag()).props; newProps = cloneProps(animatedProps, shadowNode); } return shadowNode.clone( @@ -213,6 +261,12 @@ void AnimationBackend::commitUpdates( void AnimationBackend::synchronouslyUpdateProps( const std::unordered_map& updates) { + if (ReactNativeFeatureFlags::optimizedAnimatedPropUpdates()) { + if (auto uiManager = uiManager_.lock()) { + uiManager->synchronouslyUpdateAnimatedProps(updates); + } + return; + } for (auto& [tag, animatedProps] : updates) { // TODO: We shouldn't repack it into dynamic, but for that a rewrite // of synchronouslyUpdateViewOnUIThread is needed diff --git a/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimationBackend.h b/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimationBackend.h index af4a37a9c260..33de5c50a0ef 100644 --- a/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimationBackend.h +++ b/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimationBackend.h @@ -21,23 +21,12 @@ #include "AnimatedPropsRegistry.h" #include "AnimationBackendCommitHook.h" #include "AnimationChoreographer.h" +#include "AnimationMutation.h" namespace facebook::react { class AnimationBackend; -struct AnimationMutation { - Tag tag; - std::shared_ptr family; - AnimatedProps props; - bool hasLayoutUpdates{false}; -}; - -struct AnimationMutations { - std::vector batch; - std::set asyncFlushSurfaces; -}; - using Callback = std::function; struct CallbackWithId { @@ -53,7 +42,6 @@ class AnimationBackend : public UIManagerAnimationBackend { AnimationBackend( 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; @@ -67,6 +55,9 @@ class AnimationBackend : public UIManagerAnimationBackend { void stop(CallbackId callbackId) override; private: + using SurfaceUpdates = std::unordered_map; + + void commitUpdates(SurfaceId surfaceId, SurfaceUpdates &surfaceUpdates); void unpackMutations( AnimationMutations &mutations, std::unordered_map &surfaceUpdates, @@ -74,7 +65,7 @@ class AnimationBackend : public UIManagerAnimationBackend { void applySurfaceUpdates( std::unordered_map &surfaceUpdates, const std::set &asyncFlushSurfaces); - void applyMutations(AnimationMutations mutations); + void applyMutations(std::vector batches); std::vector callbacks; std::shared_ptr animatedPropsRegistry_; std::shared_ptr animationChoreographer_; diff --git a/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimationMutation.h b/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimationMutation.h new file mode 100644 index 000000000000..fb491d290959 --- /dev/null +++ b/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimationMutation.h @@ -0,0 +1,32 @@ +/* + * 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 +#include +#include +#include "AnimatedProps.h" + +namespace facebook::react { + +struct AnimationMutation { + Tag tag; + std::shared_ptr family; + AnimatedProps props; + bool hasLayoutUpdates{false}; +}; + +struct AnimationMutations { + std::vector batch; + std::set asyncFlushSurfaces; +}; + +} // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/renderer/animationbackend/__docs__/AnimationBackend.md b/packages/react-native/ReactCommon/react/renderer/animationbackend/__docs__/AnimationBackend.md index 65414a372252..ddb1bc77cd63 100644 --- a/packages/react-native/ReactCommon/react/renderer/animationbackend/__docs__/AnimationBackend.md +++ b/packages/react-native/ReactCommon/react/renderer/animationbackend/__docs__/AnimationBackend.md @@ -6,10 +6,11 @@ Shared Animation Backend is a part of the React Native renderer that enables animation frameworks to update props of React components without going through React's JavaScript rendering pipeline. -Animation Backend allows for updates of both layout and non-layout props. If -there are no layout updates, the animations will go through the -`synchronouslyUpdateProps` path, otherwise a Fabric commit will be performed. To -synchronize the changes with React, we use the `AnimationBackendCommitHook`. +Animation Backend allows for updates of both layout and non-layout props. The +decision is made per view: views whose updates include layout props go through a +Fabric commit, and the remaining views go through the `synchronouslyUpdateProps` +path. To synchronize the changes with React, we use the +`AnimationBackendCommitHook`. ## 🚀 Usage @@ -42,10 +43,10 @@ components that make up the Shared Animation Backend. ### AnimationBackend This is the heart of the backend. This component is responsible for managing -user-provided callbacks, segregating props, and applying them either through the -`synchronouslyUpdateProps` path (when there are no layout updates), or through a -Fabric commit performed on the main thread. This component is exposed through -`UIManager` with the `UIManagerAnimationBackend` interface. +user-provided callbacks, segregating props, and applying them per view: through +a Fabric commit performed on the main thread for views with layout updates, and +through the `synchronouslyUpdateProps` path for the rest. This component is +exposed through `UIManager` with the `UIManagerAnimationBackend` interface. ### AnimatedProps diff --git a/packages/react-native/ReactCommon/react/renderer/core/RawProps.h b/packages/react-native/ReactCommon/react/renderer/core/RawProps.h index 8afc6f7ec890..8685ec6f575d 100644 --- a/packages/react-native/ReactCommon/react/renderer/core/RawProps.h +++ b/packages/react-native/ReactCommon/react/renderer/core/RawProps.h @@ -83,6 +83,12 @@ class RawProps final { */ folly::dynamic toDynamic(const std::function &filterObjectKeys = nullptr) const; + // The pointer is valid while this RawProps owns its dynamic source. + const folly::dynamic *getDynamic() const noexcept + { + return mode_ == Mode::Dynamic ? &dynamic_ : nullptr; + } + /* * Returns `true` if the object is empty. * Empty `RawProps` does not have any stored data. diff --git a/packages/react-native/ReactCommon/react/renderer/core/tests/RawPropsTest.cpp b/packages/react-native/ReactCommon/react/renderer/core/tests/RawPropsTest.cpp index caff5c160c32..b23760236f21 100644 --- a/packages/react-native/ReactCommon/react/renderer/core/tests/RawPropsTest.cpp +++ b/packages/react-native/ReactCommon/react/renderer/core/tests/RawPropsTest.cpp @@ -20,6 +20,29 @@ using namespace facebook; using namespace facebook::react; +TEST(RawPropsTest, dynamicSourceIsBorrowed) { + const auto rawProps = RawProps(folly::dynamic::object("opacity", 0.5)); + const auto* dynamic = rawProps.getDynamic(); + ASSERT_NE(dynamic, nullptr); + EXPECT_EQ(dynamic, rawProps.getDynamic()); + EXPECT_EQ(*dynamic, rawProps.toDynamic()); +} + +TEST(RawPropsTest, emptyPropsHaveNoDynamicSource) { + EXPECT_EQ(RawProps().getDynamic(), nullptr); + EXPECT_EQ(RawProps(nullptr).getDynamic(), nullptr); +} + +TEST(RawPropsTest, jsiPropsHaveNoDynamicSource) { + auto runtime = facebook::hermes::makeHermesRuntime(); + auto object = jsi::Object(*runtime); + object.setProperty(*runtime, "opacity", 0.5); + const auto rawProps = RawProps(*runtime, jsi::Value(*runtime, object)); + EXPECT_EQ(rawProps.getDynamic(), nullptr); + folly::dynamic expected = folly::dynamic::object("opacity", 0.5); + EXPECT_EQ(rawProps.toDynamic(), expected); +} + class PropsSingleFloat : public Props { public: PropsSingleFloat() = default; diff --git a/packages/react-native/ReactCommon/react/renderer/scheduler/Scheduler.cpp b/packages/react-native/ReactCommon/react/renderer/scheduler/Scheduler.cpp index 81d9de17660c..a278fb2b3400 100644 --- a/packages/react-native/ReactCommon/react/renderer/scheduler/Scheduler.cpp +++ b/packages/react-native/ReactCommon/react/renderer/scheduler/Scheduler.cpp @@ -378,6 +378,13 @@ void Scheduler::uiManagerShouldSynchronouslyUpdateViewOnUIThread( } } +void Scheduler::uiManagerShouldSynchronouslyUpdateAnimatedProps( + const std::unordered_map& updates) { + if (delegate_ != nullptr) { + delegate_->schedulerShouldSynchronouslyUpdateAnimatedProps(updates); + } +} + void Scheduler::uiManagerDidUpdateShadowTree( const std::unordered_map& tagToProps) { if (delegate_ != nullptr) { diff --git a/packages/react-native/ReactCommon/react/renderer/scheduler/Scheduler.h b/packages/react-native/ReactCommon/react/renderer/scheduler/Scheduler.h index 28c2d86462bf..929421f9ed8a 100644 --- a/packages/react-native/ReactCommon/react/renderer/scheduler/Scheduler.h +++ b/packages/react-native/ReactCommon/react/renderer/scheduler/Scheduler.h @@ -97,6 +97,7 @@ class Scheduler final : public UIManagerDelegate { bool isJSResponder, bool blockNativeResponder) override; void uiManagerShouldSynchronouslyUpdateViewOnUIThread(Tag tag, const folly::dynamic &props) override; + void uiManagerShouldSynchronouslyUpdateAnimatedProps(const std::unordered_map &updates) override; void uiManagerDidUpdateShadowTree(const std::unordered_map &tagToProps) override; void uiManagerDidCaptureViewSnapshot(Tag tag, SurfaceId surfaceId) override; void uiManagerDidSetViewSnapshot(Tag sourceTag, Tag targetTag, SurfaceId surfaceId) override; diff --git a/packages/react-native/ReactCommon/react/renderer/scheduler/SchedulerDelegate.cpp b/packages/react-native/ReactCommon/react/renderer/scheduler/SchedulerDelegate.cpp new file mode 100644 index 000000000000..52a9f38d50c1 --- /dev/null +++ b/packages/react-native/ReactCommon/react/renderer/scheduler/SchedulerDelegate.cpp @@ -0,0 +1,23 @@ +/* + * 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 "SchedulerDelegate.h" + +#include +#include + +namespace facebook::react { + +void SchedulerDelegate::schedulerShouldSynchronouslyUpdateAnimatedProps( + const std::unordered_map& updates) { + for (const auto& [tag, props] : updates) { + schedulerShouldSynchronouslyUpdateViewOnUIThread( + tag, animationbackend::packAnimatedProps(props)); + } +} + +} // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/renderer/scheduler/SchedulerDelegate.h b/packages/react-native/ReactCommon/react/renderer/scheduler/SchedulerDelegate.h index e6ca676fdbae..27c28495fe23 100644 --- a/packages/react-native/ReactCommon/react/renderer/scheduler/SchedulerDelegate.h +++ b/packages/react-native/ReactCommon/react/renderer/scheduler/SchedulerDelegate.h @@ -10,6 +10,7 @@ #include #include +#include #include #include @@ -17,6 +18,8 @@ namespace facebook::react { +struct AnimatedProps; + /* * Abstract class for Scheduler's delegate. */ @@ -66,6 +69,12 @@ class SchedulerDelegate { virtual void schedulerShouldSynchronouslyUpdateViewOnUIThread(Tag tag, const folly::dynamic &props) = 0; + /* + * Synchronous update of animated props for many views at once. By default, + * forwards each view to `schedulerShouldSynchronouslyUpdateViewOnUIThread`. + */ + virtual void schedulerShouldSynchronouslyUpdateAnimatedProps(const std::unordered_map &updates); + virtual void schedulerDidUpdateShadowTree(const std::unordered_map &tagToProps) = 0; // View transition bitmap snapshot capture and application. diff --git a/packages/react-native/ReactCommon/react/renderer/scheduler/tests/SchedulerDelegateInvalidationTest.cpp b/packages/react-native/ReactCommon/react/renderer/scheduler/tests/SchedulerDelegateInvalidationTest.cpp index 76c1646058f9..2f7957473128 100644 --- a/packages/react-native/ReactCommon/react/renderer/scheduler/tests/SchedulerDelegateInvalidationTest.cpp +++ b/packages/react-native/ReactCommon/react/renderer/scheduler/tests/SchedulerDelegateInvalidationTest.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -588,4 +589,36 @@ TEST_F( EXPECT_EQ(delegate_->shouldRenderTransactionsCount(), 1); } +TEST(SchedulerDelegateTest, animatedPropsUseExistingViewUpdatesByDefault) { + class PropsDelegate : public RecordingDelegate { + public: + std::unordered_map received; + + void schedulerShouldSynchronouslyUpdateViewOnUIThread( + Tag tag, + const folly::dynamic& props) override { + received.emplace(tag, props); + } + } delegate; + + std::unordered_map updates; + updates.emplace( + 10, + AnimatedProps{ + {}, + std::make_unique(folly::dynamic::object("opacity", 0.25))}); + AnimatedPropsBuilder builder; + builder.setOpacity(0.75); + updates.emplace(20, builder.get()); + + delegate.schedulerShouldSynchronouslyUpdateAnimatedProps(updates); + + ASSERT_EQ(delegate.received.size(), 2); + folly::dynamic expectedRaw = folly::dynamic::object("opacity", 0.25); + folly::dynamic expectedTyped = folly::dynamic::object("opacity", 0.75); + EXPECT_EQ(delegate.received.at(10), expectedRaw); + EXPECT_EQ(delegate.received.at(20), expectedTyped); + EXPECT_EQ(updates.at(10).rawProps->toDynamic(), delegate.received.at(10)); +} + } // 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 0cb62936c8b5..7eefe4606177 100644 --- a/packages/react-native/ReactCommon/react/renderer/uimanager/UIManager.cpp +++ b/packages/react-native/ReactCommon/react/renderer/uimanager/UIManager.cpp @@ -769,6 +769,13 @@ void UIManager::synchronouslyUpdateViewOnUIThread( } } +void UIManager::synchronouslyUpdateAnimatedProps( + const std::unordered_map& updates) { + if (delegate_ != nullptr) { + delegate_->uiManagerShouldSynchronouslyUpdateAnimatedProps(updates); + } +} + #pragma mark ContextContainer std::shared_ptr UIManager::getContextContainer() const { diff --git a/packages/react-native/ReactCommon/react/renderer/uimanager/UIManager.h b/packages/react-native/ReactCommon/react/renderer/uimanager/UIManager.h index aaf758bcdf6b..541d57a0b258 100644 --- a/packages/react-native/ReactCommon/react/renderer/uimanager/UIManager.h +++ b/packages/react-native/ReactCommon/react/renderer/uimanager/UIManager.h @@ -89,6 +89,8 @@ class UIManager final : public ShadowTreeDelegate { void synchronouslyUpdateViewOnUIThread(Tag tag, const folly::dynamic &props); + void synchronouslyUpdateAnimatedProps(const std::unordered_map &updates); + /* * Provides access to a UIManagerBinding. * The `callback` methods will not be called if the internal pointer to diff --git a/packages/react-native/ReactCommon/react/renderer/uimanager/UIManagerDelegate.h b/packages/react-native/ReactCommon/react/renderer/uimanager/UIManagerDelegate.h index 579989d8e01f..e431d6ee22b1 100644 --- a/packages/react-native/ReactCommon/react/renderer/uimanager/UIManagerDelegate.h +++ b/packages/react-native/ReactCommon/react/renderer/uimanager/UIManagerDelegate.h @@ -21,6 +21,8 @@ namespace facebook::react { +struct AnimatedProps; + /* * Abstract class for UIManager's delegate. */ @@ -71,6 +73,12 @@ class UIManagerDelegate { */ virtual void uiManagerShouldSynchronouslyUpdateViewOnUIThread(Tag tag, const folly::dynamic &props) = 0; + /* + * Synchronous update of animated props for many views at once. + */ + virtual void uiManagerShouldSynchronouslyUpdateAnimatedProps( + const std::unordered_map &updates) = 0; + /* * Called after updateShadowTree is invoked. */ diff --git a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js index 9d776f20b10e..b0ede5eca2ff 100644 --- a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js +++ b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js @@ -730,7 +730,7 @@ const definitions: FeatureFlagDefinitions = { metadata: { dateAdded: '2026-04-07', description: - 'When enabled, uses optimized platform-specific paths to apply animated props synchronously. On Android, this uses a batched int/double buffer protocol with a single JNI call. On iOS, this passes AnimatedProps directly through the delegate chain and applies them via cloneProps, avoiding the folly::dynamic round-trip.', + 'When enabled, uses optimized platform-specific paths to apply animated props synchronously. On Android, this uses a batched int/double buffer protocol with a single JNI call. Other platforms apply the props of each view through the existing synchronous update.', expectedReleaseValue: true, purpose: 'experimentation', }, diff --git a/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js b/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js index b06551330c29..7e995e47b1bf 100644 --- a/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js +++ b/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<6e73a99aafb36218e1d679637bb80c82>> + * @generated SignedSource<<6c4bab0fafa6367cebfd4f00797e88a9>> * @flow strict * @noformat */ @@ -467,7 +467,7 @@ export const fuseboxScreenshotCaptureEnabled: Getter = createNativeFlag */ export const fuseboxWebSocketEventsEnabled: Getter = createNativeFlagGetter('fuseboxWebSocketEventsEnabled', false); /** - * When enabled, uses optimized platform-specific paths to apply animated props synchronously. On Android, this uses a batched int/double buffer protocol with a single JNI call. On iOS, this passes AnimatedProps directly through the delegate chain and applies them via cloneProps, avoiding the folly::dynamic round-trip. + * When enabled, uses optimized platform-specific paths to apply animated props synchronously. On Android, this uses a batched int/double buffer protocol with a single JNI call. Other platforms apply the props of each view through the existing synchronous update. */ export const optimizedAnimatedPropUpdates: Getter = createNativeFlagGetter('optimizedAnimatedPropUpdates', false); /** diff --git a/scripts/cxx-api/api-snapshots/ReactAndroidDebugCxx.api b/scripts/cxx-api/api-snapshots/ReactAndroidDebugCxx.api index 458bb76080fb..0bea49a26427 100644 --- a/scripts/cxx-api/api-snapshots/ReactAndroidDebugCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactAndroidDebugCxx.api @@ -1151,6 +1151,7 @@ void facebook::react::fromRawValueShared(const facebook::react::ContextContainer void facebook::react::fromString(const std::string& string, facebook::react::AccessibilityTraits& result); void facebook::react::g_setNativeAnimatedNowTimestampFunction(facebook::react::TimePointFunction nowFunction); void facebook::react::handleJSError(facebook::jsi::Runtime& runtime, const facebook::jsi::JSError& error, bool isFatal); +void facebook::react::mergeAnimatedRawProps(folly::dynamic& target, const folly::dynamic& source); void facebook::react::parseProcessedBackgroundImage(const facebook::react::PropsParserContext& context, const facebook::react::RawValue& value, std::vector& result); void facebook::react::parseProcessedBoxShadow(const facebook::react::PropsParserContext& context, const facebook::react::RawValue& value, std::vector& result); void facebook::react::parseProcessedFilter(const facebook::react::PropsParserContext& context, const facebook::react::RawValue& value, std::vector& result); @@ -1579,7 +1580,7 @@ class facebook::react::AnimatedPropsRegistry { public void clear(facebook::react::SurfaceId surfaceId); public void clearOnSurfaceStop(facebook::react::SurfaceId surfaceId); public void initializeSurface(facebook::react::SurfaceId surfaceId); - public void update(const std::unordered_map& surfaceUpdates); + public void update(const std::vector& batches); } class facebook::react::AnimationBackend : public facebook::react::UIManagerAnimationBackend { @@ -1594,7 +1595,6 @@ class facebook::react::AnimationBackend : public facebook::react::UIManagerAnima public virtual void registerJSInvoker(std::shared_ptr jsInvoker) override; public virtual void stop(facebook::react::CallbackId callbackId) override; public virtual void trigger() override; - public void commitUpdates(facebook::react::SurfaceId surfaceId, facebook::react::SurfaceUpdates& surfaceUpdates); public void requestAsyncFlushForSurfaces(const std::set& surfaces); public void synchronouslyUpdateProps(const std::unordered_map& updates); } @@ -4258,6 +4258,7 @@ class facebook::react::RawProps { public bool isEmpty() const noexcept; public const facebook::react::RawValue* at(const char* name) const noexcept; public const facebook::react::RawValue* at(const char* name, const char* prefix, const char* suffix) const noexcept; + public const folly::dynamic* getDynamic() const noexcept; public facebook::react::RawProps& operator=(const facebook::react::RawProps& other) noexcept = delete; public facebook::react::RawProps& operator=(facebook::react::RawProps&& other) noexcept = delete; public folly::dynamic toDynamic(const std::function& filterObjectKeys = nullptr) const; @@ -4616,6 +4617,7 @@ class facebook::react::Scheduler : public facebook::react::UIManagerDelegate { public virtual void uiManagerShouldAddEventListener(std::shared_ptr listener) final; public virtual void uiManagerShouldAddOnSurfaceStartCallback(facebook::react::UIManagerDelegate::OnSurfaceStartCallback&& callback) override; public virtual void uiManagerShouldRemoveEventListener(const std::shared_ptr& listener) final; + public virtual void uiManagerShouldSynchronouslyUpdateAnimatedProps(const std::unordered_map& updates) override; public virtual void uiManagerShouldSynchronouslyUpdateViewOnUIThread(facebook::react::Tag tag, const folly::dynamic& props) override; public void addEventListener(std::shared_ptr listener); public void animationTick() const; @@ -4639,6 +4641,7 @@ class facebook::react::SchedulerDelegate { public virtual void schedulerDidUpdateShadowTree(const std::unordered_map& tagToProps) = 0; public virtual void schedulerShouldMergeReactRevision(facebook::react::SurfaceId surfaceId) = 0; public virtual void schedulerShouldRenderTransactions(const std::shared_ptr& mountingCoordinator) = 0; + public virtual void schedulerShouldSynchronouslyUpdateAnimatedProps(const std::unordered_map& updates); public virtual void schedulerShouldSynchronouslyUpdateViewOnUIThread(facebook::react::Tag tag, const folly::dynamic& props) = 0; public virtual ~SchedulerDelegate() noexcept = default; } @@ -5370,6 +5373,7 @@ class facebook::react::UIManager : public facebook::react::ShadowTreeDelegate { public void startEmptySurface(facebook::react::ShadowTree::Unique&& shadowTree) const noexcept; public void startSurface(facebook::react::ShadowTree::Unique&& shadowTree, std::string moduleName, folly::dynamic props, facebook::react::DisplayMode displayMode) const noexcept; public void stopSurfaceForAnimationDelegate(facebook::react::SurfaceId surfaceId) const; + public void synchronouslyUpdateAnimatedProps(const std::unordered_map& updates); public void synchronouslyUpdateViewOnUIThread(facebook::react::Tag tag, const folly::dynamic& props); public void unregisterCommitHook(facebook::react::UIManagerCommitHook& commitHook); public void unregisterMountHook(facebook::react::UIManagerMountHook& mountHook); @@ -5437,6 +5441,7 @@ class facebook::react::UIManagerDelegate { public virtual void uiManagerShouldAddEventListener(std::shared_ptr listener) = 0; public virtual void uiManagerShouldAddOnSurfaceStartCallback(facebook::react::UIManagerDelegate::OnSurfaceStartCallback&& callback) = 0; public virtual void uiManagerShouldRemoveEventListener(const std::shared_ptr& listener) = 0; + public virtual void uiManagerShouldSynchronouslyUpdateAnimatedProps(const std::unordered_map& updates) = 0; public virtual void uiManagerShouldSynchronouslyUpdateViewOnUIThread(facebook::react::Tag tag, const folly::dynamic& props) = 0; public virtual ~UIManagerDelegate() noexcept = default; } @@ -8111,12 +8116,6 @@ struct facebook::react::SurfaceContext { public std::unordered_set> pendingFamilies; } -struct facebook::react::SurfaceUpdates { - public bool hasLayoutUpdates; - public std::unordered_map propsMap; - public std::unordered_set> families; -} - struct facebook::react::SystraceSection : public facebook::react::DummyTraceSection { template public SystraceSection(const char* name, ConvertsToStringPiece &&... args); diff --git a/scripts/cxx-api/api-snapshots/ReactAndroidNewarchCxx.api b/scripts/cxx-api/api-snapshots/ReactAndroidNewarchCxx.api index 642535bf6bd7..2be07a561c10 100644 --- a/scripts/cxx-api/api-snapshots/ReactAndroidNewarchCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactAndroidNewarchCxx.api @@ -1147,6 +1147,7 @@ void facebook::react::fromRawValueShared(const facebook::react::ContextContainer void facebook::react::fromString(const std::string& string, facebook::react::AccessibilityTraits& result); void facebook::react::g_setNativeAnimatedNowTimestampFunction(facebook::react::TimePointFunction nowFunction); void facebook::react::handleJSError(facebook::jsi::Runtime& runtime, const facebook::jsi::JSError& error, bool isFatal); +void facebook::react::mergeAnimatedRawProps(folly::dynamic& target, const folly::dynamic& source); void facebook::react::parseProcessedBackgroundImage(const facebook::react::PropsParserContext& context, const facebook::react::RawValue& value, std::vector& result); void facebook::react::parseProcessedBoxShadow(const facebook::react::PropsParserContext& context, const facebook::react::RawValue& value, std::vector& result); void facebook::react::parseProcessedFilter(const facebook::react::PropsParserContext& context, const facebook::react::RawValue& value, std::vector& result); @@ -1574,7 +1575,7 @@ class facebook::react::AnimatedPropsRegistry { public void clear(facebook::react::SurfaceId surfaceId); public void clearOnSurfaceStop(facebook::react::SurfaceId surfaceId); public void initializeSurface(facebook::react::SurfaceId surfaceId); - public void update(const std::unordered_map& surfaceUpdates); + public void update(const std::vector& batches); } class facebook::react::AnimationBackend : public facebook::react::UIManagerAnimationBackend { @@ -1589,7 +1590,6 @@ class facebook::react::AnimationBackend : public facebook::react::UIManagerAnima public virtual void registerJSInvoker(std::shared_ptr jsInvoker) override; public virtual void stop(facebook::react::CallbackId callbackId) override; public virtual void trigger() override; - public void commitUpdates(facebook::react::SurfaceId surfaceId, facebook::react::SurfaceUpdates& surfaceUpdates); public void requestAsyncFlushForSurfaces(const std::set& surfaces); public void synchronouslyUpdateProps(const std::unordered_map& updates); } @@ -4102,6 +4102,7 @@ class facebook::react::RawProps { public bool isEmpty() const noexcept; public const facebook::react::RawValue* at(const char* name) const noexcept; public const facebook::react::RawValue* at(const char* name, const char* prefix, const char* suffix) const noexcept; + public const folly::dynamic* getDynamic() const noexcept; public facebook::react::RawProps& operator=(const facebook::react::RawProps& other) noexcept = delete; public facebook::react::RawProps& operator=(facebook::react::RawProps&& other) noexcept = delete; public folly::dynamic toDynamic(const std::function& filterObjectKeys = nullptr) const; @@ -4432,6 +4433,7 @@ class facebook::react::Scheduler : public facebook::react::UIManagerDelegate { public virtual void uiManagerShouldAddEventListener(std::shared_ptr listener) final; public virtual void uiManagerShouldAddOnSurfaceStartCallback(facebook::react::UIManagerDelegate::OnSurfaceStartCallback&& callback) override; public virtual void uiManagerShouldRemoveEventListener(const std::shared_ptr& listener) final; + public virtual void uiManagerShouldSynchronouslyUpdateAnimatedProps(const std::unordered_map& updates) override; public virtual void uiManagerShouldSynchronouslyUpdateViewOnUIThread(facebook::react::Tag tag, const folly::dynamic& props) override; public void addEventListener(std::shared_ptr listener); public void animationTick() const; @@ -4455,6 +4457,7 @@ class facebook::react::SchedulerDelegate { public virtual void schedulerDidUpdateShadowTree(const std::unordered_map& tagToProps) = 0; public virtual void schedulerShouldMergeReactRevision(facebook::react::SurfaceId surfaceId) = 0; public virtual void schedulerShouldRenderTransactions(const std::shared_ptr& mountingCoordinator) = 0; + public virtual void schedulerShouldSynchronouslyUpdateAnimatedProps(const std::unordered_map& updates); public virtual void schedulerShouldSynchronouslyUpdateViewOnUIThread(facebook::react::Tag tag, const folly::dynamic& props) = 0; public virtual ~SchedulerDelegate() noexcept = default; } @@ -5180,6 +5183,7 @@ class facebook::react::UIManager : public facebook::react::ShadowTreeDelegate { public void startEmptySurface(facebook::react::ShadowTree::Unique&& shadowTree) const noexcept; public void startSurface(facebook::react::ShadowTree::Unique&& shadowTree, std::string moduleName, folly::dynamic props, facebook::react::DisplayMode displayMode) const noexcept; public void stopSurfaceForAnimationDelegate(facebook::react::SurfaceId surfaceId) const; + public void synchronouslyUpdateAnimatedProps(const std::unordered_map& updates); public void synchronouslyUpdateViewOnUIThread(facebook::react::Tag tag, const folly::dynamic& props); public void unregisterCommitHook(facebook::react::UIManagerCommitHook& commitHook); public void unregisterMountHook(facebook::react::UIManagerMountHook& mountHook); @@ -5247,6 +5251,7 @@ class facebook::react::UIManagerDelegate { public virtual void uiManagerShouldAddEventListener(std::shared_ptr listener) = 0; public virtual void uiManagerShouldAddOnSurfaceStartCallback(facebook::react::UIManagerDelegate::OnSurfaceStartCallback&& callback) = 0; public virtual void uiManagerShouldRemoveEventListener(const std::shared_ptr& listener) = 0; + public virtual void uiManagerShouldSynchronouslyUpdateAnimatedProps(const std::unordered_map& updates) = 0; public virtual void uiManagerShouldSynchronouslyUpdateViewOnUIThread(facebook::react::Tag tag, const folly::dynamic& props) = 0; public virtual ~UIManagerDelegate() noexcept = default; } @@ -7871,12 +7876,6 @@ struct facebook::react::SurfaceContext { public std::unordered_set> pendingFamilies; } -struct facebook::react::SurfaceUpdates { - public bool hasLayoutUpdates; - public std::unordered_map propsMap; - public std::unordered_set> families; -} - struct facebook::react::SystraceSection : public facebook::react::DummyTraceSection { template public SystraceSection(const char* name, ConvertsToStringPiece &&... args); diff --git a/scripts/cxx-api/api-snapshots/ReactAndroidReleaseCxx.api b/scripts/cxx-api/api-snapshots/ReactAndroidReleaseCxx.api index a965c1b79d69..919f8f0b18fe 100644 --- a/scripts/cxx-api/api-snapshots/ReactAndroidReleaseCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactAndroidReleaseCxx.api @@ -1151,6 +1151,7 @@ void facebook::react::fromRawValueShared(const facebook::react::ContextContainer void facebook::react::fromString(const std::string& string, facebook::react::AccessibilityTraits& result); void facebook::react::g_setNativeAnimatedNowTimestampFunction(facebook::react::TimePointFunction nowFunction); void facebook::react::handleJSError(facebook::jsi::Runtime& runtime, const facebook::jsi::JSError& error, bool isFatal); +void facebook::react::mergeAnimatedRawProps(folly::dynamic& target, const folly::dynamic& source); void facebook::react::parseProcessedBackgroundImage(const facebook::react::PropsParserContext& context, const facebook::react::RawValue& value, std::vector& result); void facebook::react::parseProcessedBoxShadow(const facebook::react::PropsParserContext& context, const facebook::react::RawValue& value, std::vector& result); void facebook::react::parseProcessedFilter(const facebook::react::PropsParserContext& context, const facebook::react::RawValue& value, std::vector& result); @@ -1578,7 +1579,7 @@ class facebook::react::AnimatedPropsRegistry { public void clear(facebook::react::SurfaceId surfaceId); public void clearOnSurfaceStop(facebook::react::SurfaceId surfaceId); public void initializeSurface(facebook::react::SurfaceId surfaceId); - public void update(const std::unordered_map& surfaceUpdates); + public void update(const std::vector& batches); } class facebook::react::AnimationBackend : public facebook::react::UIManagerAnimationBackend { @@ -1593,7 +1594,6 @@ class facebook::react::AnimationBackend : public facebook::react::UIManagerAnima public virtual void registerJSInvoker(std::shared_ptr jsInvoker) override; public virtual void stop(facebook::react::CallbackId callbackId) override; public virtual void trigger() override; - public void commitUpdates(facebook::react::SurfaceId surfaceId, facebook::react::SurfaceUpdates& surfaceUpdates); public void requestAsyncFlushForSurfaces(const std::set& surfaces); public void synchronouslyUpdateProps(const std::unordered_map& updates); } @@ -4255,6 +4255,7 @@ class facebook::react::RawProps { public bool isEmpty() const noexcept; public const facebook::react::RawValue* at(const char* name) const noexcept; public const facebook::react::RawValue* at(const char* name, const char* prefix, const char* suffix) const noexcept; + public const folly::dynamic* getDynamic() const noexcept; public facebook::react::RawProps& operator=(const facebook::react::RawProps& other) noexcept = delete; public facebook::react::RawProps& operator=(facebook::react::RawProps&& other) noexcept = delete; public folly::dynamic toDynamic(const std::function& filterObjectKeys = nullptr) const; @@ -4613,6 +4614,7 @@ class facebook::react::Scheduler : public facebook::react::UIManagerDelegate { public virtual void uiManagerShouldAddEventListener(std::shared_ptr listener) final; public virtual void uiManagerShouldAddOnSurfaceStartCallback(facebook::react::UIManagerDelegate::OnSurfaceStartCallback&& callback) override; public virtual void uiManagerShouldRemoveEventListener(const std::shared_ptr& listener) final; + public virtual void uiManagerShouldSynchronouslyUpdateAnimatedProps(const std::unordered_map& updates) override; public virtual void uiManagerShouldSynchronouslyUpdateViewOnUIThread(facebook::react::Tag tag, const folly::dynamic& props) override; public void addEventListener(std::shared_ptr listener); public void animationTick() const; @@ -4636,6 +4638,7 @@ class facebook::react::SchedulerDelegate { public virtual void schedulerDidUpdateShadowTree(const std::unordered_map& tagToProps) = 0; public virtual void schedulerShouldMergeReactRevision(facebook::react::SurfaceId surfaceId) = 0; public virtual void schedulerShouldRenderTransactions(const std::shared_ptr& mountingCoordinator) = 0; + public virtual void schedulerShouldSynchronouslyUpdateAnimatedProps(const std::unordered_map& updates); public virtual void schedulerShouldSynchronouslyUpdateViewOnUIThread(facebook::react::Tag tag, const folly::dynamic& props) = 0; public virtual ~SchedulerDelegate() noexcept = default; } @@ -5361,6 +5364,7 @@ class facebook::react::UIManager : public facebook::react::ShadowTreeDelegate { public void startEmptySurface(facebook::react::ShadowTree::Unique&& shadowTree) const noexcept; public void startSurface(facebook::react::ShadowTree::Unique&& shadowTree, std::string moduleName, folly::dynamic props, facebook::react::DisplayMode displayMode) const noexcept; public void stopSurfaceForAnimationDelegate(facebook::react::SurfaceId surfaceId) const; + public void synchronouslyUpdateAnimatedProps(const std::unordered_map& updates); public void synchronouslyUpdateViewOnUIThread(facebook::react::Tag tag, const folly::dynamic& props); public void unregisterCommitHook(facebook::react::UIManagerCommitHook& commitHook); public void unregisterMountHook(facebook::react::UIManagerMountHook& mountHook); @@ -5428,6 +5432,7 @@ class facebook::react::UIManagerDelegate { public virtual void uiManagerShouldAddEventListener(std::shared_ptr listener) = 0; public virtual void uiManagerShouldAddOnSurfaceStartCallback(facebook::react::UIManagerDelegate::OnSurfaceStartCallback&& callback) = 0; public virtual void uiManagerShouldRemoveEventListener(const std::shared_ptr& listener) = 0; + public virtual void uiManagerShouldSynchronouslyUpdateAnimatedProps(const std::unordered_map& updates) = 0; public virtual void uiManagerShouldSynchronouslyUpdateViewOnUIThread(facebook::react::Tag tag, const folly::dynamic& props) = 0; public virtual ~UIManagerDelegate() noexcept = default; } @@ -8102,12 +8107,6 @@ struct facebook::react::SurfaceContext { public std::unordered_set> pendingFamilies; } -struct facebook::react::SurfaceUpdates { - public bool hasLayoutUpdates; - public std::unordered_map propsMap; - public std::unordered_set> families; -} - struct facebook::react::SystraceSection : public facebook::react::DummyTraceSection { template public SystraceSection(const char* name, ConvertsToStringPiece &&... args); diff --git a/scripts/cxx-api/api-snapshots/ReactAppleDebugCxx.api b/scripts/cxx-api/api-snapshots/ReactAppleDebugCxx.api index d0d014ec91cb..fdeda28b5c01 100644 --- a/scripts/cxx-api/api-snapshots/ReactAppleDebugCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactAppleDebugCxx.api @@ -3951,6 +3951,7 @@ void facebook::react::fromString(const std::string& string, facebook::react::Acc void facebook::react::g_setNativeAnimatedNowTimestampFunction(facebook::react::TimePointFunction nowFunction); void facebook::react::handleJSError(facebook::jsi::Runtime& runtime, const facebook::jsi::JSError& error, bool isFatal); void facebook::react::installLegacyUIManagerConstantsProviderBinding(facebook::jsi::Runtime& runtime); +void facebook::react::mergeAnimatedRawProps(folly::dynamic& target, const folly::dynamic& source); void facebook::react::parseProcessedBackgroundImage(const facebook::react::PropsParserContext& context, const facebook::react::RawValue& value, std::vector& result); void facebook::react::parseProcessedBoxShadow(const facebook::react::PropsParserContext& context, const facebook::react::RawValue& value, std::vector& result); void facebook::react::parseProcessedFilter(const facebook::react::PropsParserContext& context, const facebook::react::RawValue& value, std::vector& result); @@ -4128,7 +4129,7 @@ class facebook::react::AnimatedPropsRegistry { public void clear(facebook::react::SurfaceId surfaceId); public void clearOnSurfaceStop(facebook::react::SurfaceId surfaceId); public void initializeSurface(facebook::react::SurfaceId surfaceId); - public void update(const std::unordered_map& surfaceUpdates); + public void update(const std::vector& batches); } class facebook::react::AnimationBackend : public facebook::react::UIManagerAnimationBackend { @@ -4143,7 +4144,6 @@ class facebook::react::AnimationBackend : public facebook::react::UIManagerAnima public virtual void registerJSInvoker(std::shared_ptr jsInvoker) override; public virtual void stop(facebook::react::CallbackId callbackId) override; public virtual void trigger() override; - public void commitUpdates(facebook::react::SurfaceId surfaceId, facebook::react::SurfaceUpdates& surfaceUpdates); public void requestAsyncFlushForSurfaces(const std::set& surfaces); public void synchronouslyUpdateProps(const std::unordered_map& updates); } @@ -6429,6 +6429,7 @@ class facebook::react::RawProps { public bool isEmpty() const noexcept; public const facebook::react::RawValue* at(const char* name) const noexcept; public const facebook::react::RawValue* at(const char* name, const char* prefix, const char* suffix) const noexcept; + public const folly::dynamic* getDynamic() const noexcept; public facebook::react::RawProps& operator=(const facebook::react::RawProps& other) noexcept = delete; public facebook::react::RawProps& operator=(facebook::react::RawProps&& other) noexcept = delete; public folly::dynamic toDynamic(const std::function& filterObjectKeys = nullptr) const; @@ -6768,6 +6769,7 @@ class facebook::react::Scheduler : public facebook::react::UIManagerDelegate { public virtual void uiManagerShouldAddEventListener(std::shared_ptr listener) final; public virtual void uiManagerShouldAddOnSurfaceStartCallback(facebook::react::UIManagerDelegate::OnSurfaceStartCallback&& callback) override; public virtual void uiManagerShouldRemoveEventListener(const std::shared_ptr& listener) final; + public virtual void uiManagerShouldSynchronouslyUpdateAnimatedProps(const std::unordered_map& updates) override; public virtual void uiManagerShouldSynchronouslyUpdateViewOnUIThread(facebook::react::Tag tag, const folly::dynamic& props) override; public void addEventListener(std::shared_ptr listener); public void animationTick() const; @@ -6791,6 +6793,7 @@ class facebook::react::SchedulerDelegate { public virtual void schedulerDidUpdateShadowTree(const std::unordered_map& tagToProps) = 0; public virtual void schedulerShouldMergeReactRevision(facebook::react::SurfaceId surfaceId) = 0; public virtual void schedulerShouldRenderTransactions(const std::shared_ptr& mountingCoordinator) = 0; + public virtual void schedulerShouldSynchronouslyUpdateAnimatedProps(const std::unordered_map& updates); public virtual void schedulerShouldSynchronouslyUpdateViewOnUIThread(facebook::react::Tag tag, const folly::dynamic& props) = 0; public virtual ~SchedulerDelegate() noexcept = default; } @@ -7503,6 +7506,7 @@ class facebook::react::UIManager : public facebook::react::ShadowTreeDelegate { public void startEmptySurface(facebook::react::ShadowTree::Unique&& shadowTree) const noexcept; public void startSurface(facebook::react::ShadowTree::Unique&& shadowTree, std::string moduleName, folly::dynamic props, facebook::react::DisplayMode displayMode) const noexcept; public void stopSurfaceForAnimationDelegate(facebook::react::SurfaceId surfaceId) const; + public void synchronouslyUpdateAnimatedProps(const std::unordered_map& updates); public void synchronouslyUpdateViewOnUIThread(facebook::react::Tag tag, const folly::dynamic& props); public void unregisterCommitHook(facebook::react::UIManagerCommitHook& commitHook); public void unregisterMountHook(facebook::react::UIManagerMountHook& mountHook); @@ -7570,6 +7574,7 @@ class facebook::react::UIManagerDelegate { public virtual void uiManagerShouldAddEventListener(std::shared_ptr listener) = 0; public virtual void uiManagerShouldAddOnSurfaceStartCallback(facebook::react::UIManagerDelegate::OnSurfaceStartCallback&& callback) = 0; public virtual void uiManagerShouldRemoveEventListener(const std::shared_ptr& listener) = 0; + public virtual void uiManagerShouldSynchronouslyUpdateAnimatedProps(const std::unordered_map& updates) = 0; public virtual void uiManagerShouldSynchronouslyUpdateViewOnUIThread(facebook::react::Tag tag, const folly::dynamic& props) = 0; public virtual ~UIManagerDelegate() noexcept = default; } @@ -10078,12 +10083,6 @@ struct facebook::react::SurfaceContext { public std::unordered_set> pendingFamilies; } -struct facebook::react::SurfaceUpdates { - public bool hasLayoutUpdates; - public std::unordered_map propsMap; - public std::unordered_set> families; -} - struct facebook::react::SystraceSection : public facebook::react::DummyTraceSection { template public SystraceSection(const char* name, ConvertsToStringPiece &&... args); diff --git a/scripts/cxx-api/api-snapshots/ReactAppleNewarchCxx.api b/scripts/cxx-api/api-snapshots/ReactAppleNewarchCxx.api index 4290dcb0e425..6a561259e110 100644 --- a/scripts/cxx-api/api-snapshots/ReactAppleNewarchCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactAppleNewarchCxx.api @@ -3940,6 +3940,7 @@ void facebook::react::fromString(const std::string& string, facebook::react::Acc void facebook::react::g_setNativeAnimatedNowTimestampFunction(facebook::react::TimePointFunction nowFunction); void facebook::react::handleJSError(facebook::jsi::Runtime& runtime, const facebook::jsi::JSError& error, bool isFatal); void facebook::react::installLegacyUIManagerConstantsProviderBinding(facebook::jsi::Runtime& runtime); +void facebook::react::mergeAnimatedRawProps(folly::dynamic& target, const folly::dynamic& source); void facebook::react::parseProcessedBackgroundImage(const facebook::react::PropsParserContext& context, const facebook::react::RawValue& value, std::vector& result); void facebook::react::parseProcessedBoxShadow(const facebook::react::PropsParserContext& context, const facebook::react::RawValue& value, std::vector& result); void facebook::react::parseProcessedFilter(const facebook::react::PropsParserContext& context, const facebook::react::RawValue& value, std::vector& result); @@ -4116,7 +4117,7 @@ class facebook::react::AnimatedPropsRegistry { public void clear(facebook::react::SurfaceId surfaceId); public void clearOnSurfaceStop(facebook::react::SurfaceId surfaceId); public void initializeSurface(facebook::react::SurfaceId surfaceId); - public void update(const std::unordered_map& surfaceUpdates); + public void update(const std::vector& batches); } class facebook::react::AnimationBackend : public facebook::react::UIManagerAnimationBackend { @@ -4131,7 +4132,6 @@ class facebook::react::AnimationBackend : public facebook::react::UIManagerAnima public virtual void registerJSInvoker(std::shared_ptr jsInvoker) override; public virtual void stop(facebook::react::CallbackId callbackId) override; public virtual void trigger() override; - public void commitUpdates(facebook::react::SurfaceId surfaceId, facebook::react::SurfaceUpdates& surfaceUpdates); public void requestAsyncFlushForSurfaces(const std::set& surfaces); public void synchronouslyUpdateProps(const std::unordered_map& updates); } @@ -6305,6 +6305,7 @@ class facebook::react::RawProps { public bool isEmpty() const noexcept; public const facebook::react::RawValue* at(const char* name) const noexcept; public const facebook::react::RawValue* at(const char* name, const char* prefix, const char* suffix) const noexcept; + public const folly::dynamic* getDynamic() const noexcept; public facebook::react::RawProps& operator=(const facebook::react::RawProps& other) noexcept = delete; public facebook::react::RawProps& operator=(facebook::react::RawProps&& other) noexcept = delete; public folly::dynamic toDynamic(const std::function& filterObjectKeys = nullptr) const; @@ -6616,6 +6617,7 @@ class facebook::react::Scheduler : public facebook::react::UIManagerDelegate { public virtual void uiManagerShouldAddEventListener(std::shared_ptr listener) final; public virtual void uiManagerShouldAddOnSurfaceStartCallback(facebook::react::UIManagerDelegate::OnSurfaceStartCallback&& callback) override; public virtual void uiManagerShouldRemoveEventListener(const std::shared_ptr& listener) final; + public virtual void uiManagerShouldSynchronouslyUpdateAnimatedProps(const std::unordered_map& updates) override; public virtual void uiManagerShouldSynchronouslyUpdateViewOnUIThread(facebook::react::Tag tag, const folly::dynamic& props) override; public void addEventListener(std::shared_ptr listener); public void animationTick() const; @@ -6639,6 +6641,7 @@ class facebook::react::SchedulerDelegate { public virtual void schedulerDidUpdateShadowTree(const std::unordered_map& tagToProps) = 0; public virtual void schedulerShouldMergeReactRevision(facebook::react::SurfaceId surfaceId) = 0; public virtual void schedulerShouldRenderTransactions(const std::shared_ptr& mountingCoordinator) = 0; + public virtual void schedulerShouldSynchronouslyUpdateAnimatedProps(const std::unordered_map& updates); public virtual void schedulerShouldSynchronouslyUpdateViewOnUIThread(facebook::react::Tag tag, const folly::dynamic& props) = 0; public virtual ~SchedulerDelegate() noexcept = default; } @@ -7345,6 +7348,7 @@ class facebook::react::UIManager : public facebook::react::ShadowTreeDelegate { public void startEmptySurface(facebook::react::ShadowTree::Unique&& shadowTree) const noexcept; public void startSurface(facebook::react::ShadowTree::Unique&& shadowTree, std::string moduleName, folly::dynamic props, facebook::react::DisplayMode displayMode) const noexcept; public void stopSurfaceForAnimationDelegate(facebook::react::SurfaceId surfaceId) const; + public void synchronouslyUpdateAnimatedProps(const std::unordered_map& updates); public void synchronouslyUpdateViewOnUIThread(facebook::react::Tag tag, const folly::dynamic& props); public void unregisterCommitHook(facebook::react::UIManagerCommitHook& commitHook); public void unregisterMountHook(facebook::react::UIManagerMountHook& mountHook); @@ -7412,6 +7416,7 @@ class facebook::react::UIManagerDelegate { public virtual void uiManagerShouldAddEventListener(std::shared_ptr listener) = 0; public virtual void uiManagerShouldAddOnSurfaceStartCallback(facebook::react::UIManagerDelegate::OnSurfaceStartCallback&& callback) = 0; public virtual void uiManagerShouldRemoveEventListener(const std::shared_ptr& listener) = 0; + public virtual void uiManagerShouldSynchronouslyUpdateAnimatedProps(const std::unordered_map& updates) = 0; public virtual void uiManagerShouldSynchronouslyUpdateViewOnUIThread(facebook::react::Tag tag, const folly::dynamic& props) = 0; public virtual ~UIManagerDelegate() noexcept = default; } @@ -9894,12 +9899,6 @@ struct facebook::react::SurfaceContext { public std::unordered_set> pendingFamilies; } -struct facebook::react::SurfaceUpdates { - public bool hasLayoutUpdates; - public std::unordered_map propsMap; - public std::unordered_set> families; -} - struct facebook::react::SystraceSection : public facebook::react::DummyTraceSection { template public SystraceSection(const char* name, ConvertsToStringPiece &&... args); diff --git a/scripts/cxx-api/api-snapshots/ReactAppleReleaseCxx.api b/scripts/cxx-api/api-snapshots/ReactAppleReleaseCxx.api index 72a9805b05fd..ff5f5f926240 100644 --- a/scripts/cxx-api/api-snapshots/ReactAppleReleaseCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactAppleReleaseCxx.api @@ -3951,6 +3951,7 @@ void facebook::react::fromString(const std::string& string, facebook::react::Acc void facebook::react::g_setNativeAnimatedNowTimestampFunction(facebook::react::TimePointFunction nowFunction); void facebook::react::handleJSError(facebook::jsi::Runtime& runtime, const facebook::jsi::JSError& error, bool isFatal); void facebook::react::installLegacyUIManagerConstantsProviderBinding(facebook::jsi::Runtime& runtime); +void facebook::react::mergeAnimatedRawProps(folly::dynamic& target, const folly::dynamic& source); void facebook::react::parseProcessedBackgroundImage(const facebook::react::PropsParserContext& context, const facebook::react::RawValue& value, std::vector& result); void facebook::react::parseProcessedBoxShadow(const facebook::react::PropsParserContext& context, const facebook::react::RawValue& value, std::vector& result); void facebook::react::parseProcessedFilter(const facebook::react::PropsParserContext& context, const facebook::react::RawValue& value, std::vector& result); @@ -4127,7 +4128,7 @@ class facebook::react::AnimatedPropsRegistry { public void clear(facebook::react::SurfaceId surfaceId); public void clearOnSurfaceStop(facebook::react::SurfaceId surfaceId); public void initializeSurface(facebook::react::SurfaceId surfaceId); - public void update(const std::unordered_map& surfaceUpdates); + public void update(const std::vector& batches); } class facebook::react::AnimationBackend : public facebook::react::UIManagerAnimationBackend { @@ -4142,7 +4143,6 @@ class facebook::react::AnimationBackend : public facebook::react::UIManagerAnima public virtual void registerJSInvoker(std::shared_ptr jsInvoker) override; public virtual void stop(facebook::react::CallbackId callbackId) override; public virtual void trigger() override; - public void commitUpdates(facebook::react::SurfaceId surfaceId, facebook::react::SurfaceUpdates& surfaceUpdates); public void requestAsyncFlushForSurfaces(const std::set& surfaces); public void synchronouslyUpdateProps(const std::unordered_map& updates); } @@ -6426,6 +6426,7 @@ class facebook::react::RawProps { public bool isEmpty() const noexcept; public const facebook::react::RawValue* at(const char* name) const noexcept; public const facebook::react::RawValue* at(const char* name, const char* prefix, const char* suffix) const noexcept; + public const folly::dynamic* getDynamic() const noexcept; public facebook::react::RawProps& operator=(const facebook::react::RawProps& other) noexcept = delete; public facebook::react::RawProps& operator=(facebook::react::RawProps&& other) noexcept = delete; public folly::dynamic toDynamic(const std::function& filterObjectKeys = nullptr) const; @@ -6765,6 +6766,7 @@ class facebook::react::Scheduler : public facebook::react::UIManagerDelegate { public virtual void uiManagerShouldAddEventListener(std::shared_ptr listener) final; public virtual void uiManagerShouldAddOnSurfaceStartCallback(facebook::react::UIManagerDelegate::OnSurfaceStartCallback&& callback) override; public virtual void uiManagerShouldRemoveEventListener(const std::shared_ptr& listener) final; + public virtual void uiManagerShouldSynchronouslyUpdateAnimatedProps(const std::unordered_map& updates) override; public virtual void uiManagerShouldSynchronouslyUpdateViewOnUIThread(facebook::react::Tag tag, const folly::dynamic& props) override; public void addEventListener(std::shared_ptr listener); public void animationTick() const; @@ -6788,6 +6790,7 @@ class facebook::react::SchedulerDelegate { public virtual void schedulerDidUpdateShadowTree(const std::unordered_map& tagToProps) = 0; public virtual void schedulerShouldMergeReactRevision(facebook::react::SurfaceId surfaceId) = 0; public virtual void schedulerShouldRenderTransactions(const std::shared_ptr& mountingCoordinator) = 0; + public virtual void schedulerShouldSynchronouslyUpdateAnimatedProps(const std::unordered_map& updates); public virtual void schedulerShouldSynchronouslyUpdateViewOnUIThread(facebook::react::Tag tag, const folly::dynamic& props) = 0; public virtual ~SchedulerDelegate() noexcept = default; } @@ -7494,6 +7497,7 @@ class facebook::react::UIManager : public facebook::react::ShadowTreeDelegate { public void startEmptySurface(facebook::react::ShadowTree::Unique&& shadowTree) const noexcept; public void startSurface(facebook::react::ShadowTree::Unique&& shadowTree, std::string moduleName, folly::dynamic props, facebook::react::DisplayMode displayMode) const noexcept; public void stopSurfaceForAnimationDelegate(facebook::react::SurfaceId surfaceId) const; + public void synchronouslyUpdateAnimatedProps(const std::unordered_map& updates); public void synchronouslyUpdateViewOnUIThread(facebook::react::Tag tag, const folly::dynamic& props); public void unregisterCommitHook(facebook::react::UIManagerCommitHook& commitHook); public void unregisterMountHook(facebook::react::UIManagerMountHook& mountHook); @@ -7561,6 +7565,7 @@ class facebook::react::UIManagerDelegate { public virtual void uiManagerShouldAddEventListener(std::shared_ptr listener) = 0; public virtual void uiManagerShouldAddOnSurfaceStartCallback(facebook::react::UIManagerDelegate::OnSurfaceStartCallback&& callback) = 0; public virtual void uiManagerShouldRemoveEventListener(const std::shared_ptr& listener) = 0; + public virtual void uiManagerShouldSynchronouslyUpdateAnimatedProps(const std::unordered_map& updates) = 0; public virtual void uiManagerShouldSynchronouslyUpdateViewOnUIThread(facebook::react::Tag tag, const folly::dynamic& props) = 0; public virtual ~UIManagerDelegate() noexcept = default; } @@ -10069,12 +10074,6 @@ struct facebook::react::SurfaceContext { public std::unordered_set> pendingFamilies; } -struct facebook::react::SurfaceUpdates { - public bool hasLayoutUpdates; - public std::unordered_map propsMap; - public std::unordered_set> families; -} - struct facebook::react::SystraceSection : public facebook::react::DummyTraceSection { template public SystraceSection(const char* name, ConvertsToStringPiece &&... args); diff --git a/scripts/cxx-api/api-snapshots/ReactCommonDebugCxx.api b/scripts/cxx-api/api-snapshots/ReactCommonDebugCxx.api index 8b46097a89f9..1a1172791c85 100644 --- a/scripts/cxx-api/api-snapshots/ReactCommonDebugCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactCommonDebugCxx.api @@ -711,6 +711,7 @@ void facebook::react::fromRawValueShared(const facebook::react::ContextContainer void facebook::react::fromString(const std::string& string, facebook::react::AccessibilityTraits& result); void facebook::react::g_setNativeAnimatedNowTimestampFunction(facebook::react::TimePointFunction nowFunction); void facebook::react::handleJSError(facebook::jsi::Runtime& runtime, const facebook::jsi::JSError& error, bool isFatal); +void facebook::react::mergeAnimatedRawProps(folly::dynamic& target, const folly::dynamic& source); void facebook::react::parseProcessedBackgroundImage(const facebook::react::PropsParserContext& context, const facebook::react::RawValue& value, std::vector& result); void facebook::react::parseProcessedBoxShadow(const facebook::react::PropsParserContext& context, const facebook::react::RawValue& value, std::vector& result); void facebook::react::parseProcessedFilter(const facebook::react::PropsParserContext& context, const facebook::react::RawValue& value, std::vector& result); @@ -904,7 +905,7 @@ class facebook::react::AnimatedPropsRegistry { public void clear(facebook::react::SurfaceId surfaceId); public void clearOnSurfaceStop(facebook::react::SurfaceId surfaceId); public void initializeSurface(facebook::react::SurfaceId surfaceId); - public void update(const std::unordered_map& surfaceUpdates); + public void update(const std::vector& batches); } class facebook::react::AnimationBackend : public facebook::react::UIManagerAnimationBackend { @@ -919,7 +920,6 @@ class facebook::react::AnimationBackend : public facebook::react::UIManagerAnima public virtual void registerJSInvoker(std::shared_ptr jsInvoker) override; public virtual void stop(facebook::react::CallbackId callbackId) override; public virtual void trigger() override; - public void commitUpdates(facebook::react::SurfaceId surfaceId, facebook::react::SurfaceUpdates& surfaceUpdates); public void requestAsyncFlushForSurfaces(const std::set& surfaces); public void synchronouslyUpdateProps(const std::unordered_map& updates); } @@ -2830,6 +2830,7 @@ class facebook::react::RawProps { public bool isEmpty() const noexcept; public const facebook::react::RawValue* at(const char* name) const noexcept; public const facebook::react::RawValue* at(const char* name, const char* prefix, const char* suffix) const noexcept; + public const folly::dynamic* getDynamic() const noexcept; public facebook::react::RawProps& operator=(const facebook::react::RawProps& other) noexcept = delete; public facebook::react::RawProps& operator=(facebook::react::RawProps&& other) noexcept = delete; public folly::dynamic toDynamic(const std::function& filterObjectKeys = nullptr) const; @@ -3161,6 +3162,7 @@ class facebook::react::Scheduler : public facebook::react::UIManagerDelegate { public virtual void uiManagerShouldAddEventListener(std::shared_ptr listener) final; public virtual void uiManagerShouldAddOnSurfaceStartCallback(facebook::react::UIManagerDelegate::OnSurfaceStartCallback&& callback) override; public virtual void uiManagerShouldRemoveEventListener(const std::shared_ptr& listener) final; + public virtual void uiManagerShouldSynchronouslyUpdateAnimatedProps(const std::unordered_map& updates) override; public virtual void uiManagerShouldSynchronouslyUpdateViewOnUIThread(facebook::react::Tag tag, const folly::dynamic& props) override; public void addEventListener(std::shared_ptr listener); public void animationTick() const; @@ -3184,6 +3186,7 @@ class facebook::react::SchedulerDelegate { public virtual void schedulerDidUpdateShadowTree(const std::unordered_map& tagToProps) = 0; public virtual void schedulerShouldMergeReactRevision(facebook::react::SurfaceId surfaceId) = 0; public virtual void schedulerShouldRenderTransactions(const std::shared_ptr& mountingCoordinator) = 0; + public virtual void schedulerShouldSynchronouslyUpdateAnimatedProps(const std::unordered_map& updates); public virtual void schedulerShouldSynchronouslyUpdateViewOnUIThread(facebook::react::Tag tag, const folly::dynamic& props) = 0; public virtual ~SchedulerDelegate() noexcept = default; } @@ -3812,6 +3815,7 @@ class facebook::react::UIManager : public facebook::react::ShadowTreeDelegate { public void startEmptySurface(facebook::react::ShadowTree::Unique&& shadowTree) const noexcept; public void startSurface(facebook::react::ShadowTree::Unique&& shadowTree, std::string moduleName, folly::dynamic props, facebook::react::DisplayMode displayMode) const noexcept; public void stopSurfaceForAnimationDelegate(facebook::react::SurfaceId surfaceId) const; + public void synchronouslyUpdateAnimatedProps(const std::unordered_map& updates); public void synchronouslyUpdateViewOnUIThread(facebook::react::Tag tag, const folly::dynamic& props); public void unregisterCommitHook(facebook::react::UIManagerCommitHook& commitHook); public void unregisterMountHook(facebook::react::UIManagerMountHook& mountHook); @@ -3879,6 +3883,7 @@ class facebook::react::UIManagerDelegate { public virtual void uiManagerShouldAddEventListener(std::shared_ptr listener) = 0; public virtual void uiManagerShouldAddOnSurfaceStartCallback(facebook::react::UIManagerDelegate::OnSurfaceStartCallback&& callback) = 0; public virtual void uiManagerShouldRemoveEventListener(const std::shared_ptr& listener) = 0; + public virtual void uiManagerShouldSynchronouslyUpdateAnimatedProps(const std::unordered_map& updates) = 0; public virtual void uiManagerShouldSynchronouslyUpdateViewOnUIThread(facebook::react::Tag tag, const folly::dynamic& props) = 0; public virtual ~UIManagerDelegate() noexcept = default; } @@ -6228,12 +6233,6 @@ struct facebook::react::SurfaceContext { public std::unordered_set> pendingFamilies; } -struct facebook::react::SurfaceUpdates { - public bool hasLayoutUpdates; - public std::unordered_map propsMap; - public std::unordered_set> families; -} - struct facebook::react::SystraceSection : public facebook::react::DummyTraceSection { template public SystraceSection(const char* name, ConvertsToStringPiece &&... args); diff --git a/scripts/cxx-api/api-snapshots/ReactCommonNewarchCxx.api b/scripts/cxx-api/api-snapshots/ReactCommonNewarchCxx.api index 692b5064e583..c980e32d8d4b 100644 --- a/scripts/cxx-api/api-snapshots/ReactCommonNewarchCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactCommonNewarchCxx.api @@ -708,6 +708,7 @@ void facebook::react::fromRawValueShared(const facebook::react::ContextContainer void facebook::react::fromString(const std::string& string, facebook::react::AccessibilityTraits& result); void facebook::react::g_setNativeAnimatedNowTimestampFunction(facebook::react::TimePointFunction nowFunction); void facebook::react::handleJSError(facebook::jsi::Runtime& runtime, const facebook::jsi::JSError& error, bool isFatal); +void facebook::react::mergeAnimatedRawProps(folly::dynamic& target, const folly::dynamic& source); void facebook::react::parseProcessedBackgroundImage(const facebook::react::PropsParserContext& context, const facebook::react::RawValue& value, std::vector& result); void facebook::react::parseProcessedBoxShadow(const facebook::react::PropsParserContext& context, const facebook::react::RawValue& value, std::vector& result); void facebook::react::parseProcessedFilter(const facebook::react::PropsParserContext& context, const facebook::react::RawValue& value, std::vector& result); @@ -900,7 +901,7 @@ class facebook::react::AnimatedPropsRegistry { public void clear(facebook::react::SurfaceId surfaceId); public void clearOnSurfaceStop(facebook::react::SurfaceId surfaceId); public void initializeSurface(facebook::react::SurfaceId surfaceId); - public void update(const std::unordered_map& surfaceUpdates); + public void update(const std::vector& batches); } class facebook::react::AnimationBackend : public facebook::react::UIManagerAnimationBackend { @@ -915,7 +916,6 @@ class facebook::react::AnimationBackend : public facebook::react::UIManagerAnima public virtual void registerJSInvoker(std::shared_ptr jsInvoker) override; public virtual void stop(facebook::react::CallbackId callbackId) override; public virtual void trigger() override; - public void commitUpdates(facebook::react::SurfaceId surfaceId, facebook::react::SurfaceUpdates& surfaceUpdates); public void requestAsyncFlushForSurfaces(const std::set& surfaces); public void synchronouslyUpdateProps(const std::unordered_map& updates); } @@ -2714,6 +2714,7 @@ class facebook::react::RawProps { public bool isEmpty() const noexcept; public const facebook::react::RawValue* at(const char* name) const noexcept; public const facebook::react::RawValue* at(const char* name, const char* prefix, const char* suffix) const noexcept; + public const folly::dynamic* getDynamic() const noexcept; public facebook::react::RawProps& operator=(const facebook::react::RawProps& other) noexcept = delete; public facebook::react::RawProps& operator=(facebook::react::RawProps&& other) noexcept = delete; public folly::dynamic toDynamic(const std::function& filterObjectKeys = nullptr) const; @@ -3017,6 +3018,7 @@ class facebook::react::Scheduler : public facebook::react::UIManagerDelegate { public virtual void uiManagerShouldAddEventListener(std::shared_ptr listener) final; public virtual void uiManagerShouldAddOnSurfaceStartCallback(facebook::react::UIManagerDelegate::OnSurfaceStartCallback&& callback) override; public virtual void uiManagerShouldRemoveEventListener(const std::shared_ptr& listener) final; + public virtual void uiManagerShouldSynchronouslyUpdateAnimatedProps(const std::unordered_map& updates) override; public virtual void uiManagerShouldSynchronouslyUpdateViewOnUIThread(facebook::react::Tag tag, const folly::dynamic& props) override; public void addEventListener(std::shared_ptr listener); public void animationTick() const; @@ -3040,6 +3042,7 @@ class facebook::react::SchedulerDelegate { public virtual void schedulerDidUpdateShadowTree(const std::unordered_map& tagToProps) = 0; public virtual void schedulerShouldMergeReactRevision(facebook::react::SurfaceId surfaceId) = 0; public virtual void schedulerShouldRenderTransactions(const std::shared_ptr& mountingCoordinator) = 0; + public virtual void schedulerShouldSynchronouslyUpdateAnimatedProps(const std::unordered_map& updates); public virtual void schedulerShouldSynchronouslyUpdateViewOnUIThread(facebook::react::Tag tag, const folly::dynamic& props) = 0; public virtual ~SchedulerDelegate() noexcept = default; } @@ -3662,6 +3665,7 @@ class facebook::react::UIManager : public facebook::react::ShadowTreeDelegate { public void startEmptySurface(facebook::react::ShadowTree::Unique&& shadowTree) const noexcept; public void startSurface(facebook::react::ShadowTree::Unique&& shadowTree, std::string moduleName, folly::dynamic props, facebook::react::DisplayMode displayMode) const noexcept; public void stopSurfaceForAnimationDelegate(facebook::react::SurfaceId surfaceId) const; + public void synchronouslyUpdateAnimatedProps(const std::unordered_map& updates); public void synchronouslyUpdateViewOnUIThread(facebook::react::Tag tag, const folly::dynamic& props); public void unregisterCommitHook(facebook::react::UIManagerCommitHook& commitHook); public void unregisterMountHook(facebook::react::UIManagerMountHook& mountHook); @@ -3729,6 +3733,7 @@ class facebook::react::UIManagerDelegate { public virtual void uiManagerShouldAddEventListener(std::shared_ptr listener) = 0; public virtual void uiManagerShouldAddOnSurfaceStartCallback(facebook::react::UIManagerDelegate::OnSurfaceStartCallback&& callback) = 0; public virtual void uiManagerShouldRemoveEventListener(const std::shared_ptr& listener) = 0; + public virtual void uiManagerShouldSynchronouslyUpdateAnimatedProps(const std::unordered_map& updates) = 0; public virtual void uiManagerShouldSynchronouslyUpdateViewOnUIThread(facebook::react::Tag tag, const folly::dynamic& props) = 0; public virtual ~UIManagerDelegate() noexcept = default; } @@ -6052,12 +6057,6 @@ struct facebook::react::SurfaceContext { public std::unordered_set> pendingFamilies; } -struct facebook::react::SurfaceUpdates { - public bool hasLayoutUpdates; - public std::unordered_map propsMap; - public std::unordered_set> families; -} - struct facebook::react::SystraceSection : public facebook::react::DummyTraceSection { template public SystraceSection(const char* name, ConvertsToStringPiece &&... args); diff --git a/scripts/cxx-api/api-snapshots/ReactCommonReleaseCxx.api b/scripts/cxx-api/api-snapshots/ReactCommonReleaseCxx.api index ff0ef95bbfdb..626894e8b909 100644 --- a/scripts/cxx-api/api-snapshots/ReactCommonReleaseCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactCommonReleaseCxx.api @@ -711,6 +711,7 @@ void facebook::react::fromRawValueShared(const facebook::react::ContextContainer void facebook::react::fromString(const std::string& string, facebook::react::AccessibilityTraits& result); void facebook::react::g_setNativeAnimatedNowTimestampFunction(facebook::react::TimePointFunction nowFunction); void facebook::react::handleJSError(facebook::jsi::Runtime& runtime, const facebook::jsi::JSError& error, bool isFatal); +void facebook::react::mergeAnimatedRawProps(folly::dynamic& target, const folly::dynamic& source); void facebook::react::parseProcessedBackgroundImage(const facebook::react::PropsParserContext& context, const facebook::react::RawValue& value, std::vector& result); void facebook::react::parseProcessedBoxShadow(const facebook::react::PropsParserContext& context, const facebook::react::RawValue& value, std::vector& result); void facebook::react::parseProcessedFilter(const facebook::react::PropsParserContext& context, const facebook::react::RawValue& value, std::vector& result); @@ -903,7 +904,7 @@ class facebook::react::AnimatedPropsRegistry { public void clear(facebook::react::SurfaceId surfaceId); public void clearOnSurfaceStop(facebook::react::SurfaceId surfaceId); public void initializeSurface(facebook::react::SurfaceId surfaceId); - public void update(const std::unordered_map& surfaceUpdates); + public void update(const std::vector& batches); } class facebook::react::AnimationBackend : public facebook::react::UIManagerAnimationBackend { @@ -918,7 +919,6 @@ class facebook::react::AnimationBackend : public facebook::react::UIManagerAnima public virtual void registerJSInvoker(std::shared_ptr jsInvoker) override; public virtual void stop(facebook::react::CallbackId callbackId) override; public virtual void trigger() override; - public void commitUpdates(facebook::react::SurfaceId surfaceId, facebook::react::SurfaceUpdates& surfaceUpdates); public void requestAsyncFlushForSurfaces(const std::set& surfaces); public void synchronouslyUpdateProps(const std::unordered_map& updates); } @@ -2827,6 +2827,7 @@ class facebook::react::RawProps { public bool isEmpty() const noexcept; public const facebook::react::RawValue* at(const char* name) const noexcept; public const facebook::react::RawValue* at(const char* name, const char* prefix, const char* suffix) const noexcept; + public const folly::dynamic* getDynamic() const noexcept; public facebook::react::RawProps& operator=(const facebook::react::RawProps& other) noexcept = delete; public facebook::react::RawProps& operator=(facebook::react::RawProps&& other) noexcept = delete; public folly::dynamic toDynamic(const std::function& filterObjectKeys = nullptr) const; @@ -3158,6 +3159,7 @@ class facebook::react::Scheduler : public facebook::react::UIManagerDelegate { public virtual void uiManagerShouldAddEventListener(std::shared_ptr listener) final; public virtual void uiManagerShouldAddOnSurfaceStartCallback(facebook::react::UIManagerDelegate::OnSurfaceStartCallback&& callback) override; public virtual void uiManagerShouldRemoveEventListener(const std::shared_ptr& listener) final; + public virtual void uiManagerShouldSynchronouslyUpdateAnimatedProps(const std::unordered_map& updates) override; public virtual void uiManagerShouldSynchronouslyUpdateViewOnUIThread(facebook::react::Tag tag, const folly::dynamic& props) override; public void addEventListener(std::shared_ptr listener); public void animationTick() const; @@ -3181,6 +3183,7 @@ class facebook::react::SchedulerDelegate { public virtual void schedulerDidUpdateShadowTree(const std::unordered_map& tagToProps) = 0; public virtual void schedulerShouldMergeReactRevision(facebook::react::SurfaceId surfaceId) = 0; public virtual void schedulerShouldRenderTransactions(const std::shared_ptr& mountingCoordinator) = 0; + public virtual void schedulerShouldSynchronouslyUpdateAnimatedProps(const std::unordered_map& updates); public virtual void schedulerShouldSynchronouslyUpdateViewOnUIThread(facebook::react::Tag tag, const folly::dynamic& props) = 0; public virtual ~SchedulerDelegate() noexcept = default; } @@ -3803,6 +3806,7 @@ class facebook::react::UIManager : public facebook::react::ShadowTreeDelegate { public void startEmptySurface(facebook::react::ShadowTree::Unique&& shadowTree) const noexcept; public void startSurface(facebook::react::ShadowTree::Unique&& shadowTree, std::string moduleName, folly::dynamic props, facebook::react::DisplayMode displayMode) const noexcept; public void stopSurfaceForAnimationDelegate(facebook::react::SurfaceId surfaceId) const; + public void synchronouslyUpdateAnimatedProps(const std::unordered_map& updates); public void synchronouslyUpdateViewOnUIThread(facebook::react::Tag tag, const folly::dynamic& props); public void unregisterCommitHook(facebook::react::UIManagerCommitHook& commitHook); public void unregisterMountHook(facebook::react::UIManagerMountHook& mountHook); @@ -3870,6 +3874,7 @@ class facebook::react::UIManagerDelegate { public virtual void uiManagerShouldAddEventListener(std::shared_ptr listener) = 0; public virtual void uiManagerShouldAddOnSurfaceStartCallback(facebook::react::UIManagerDelegate::OnSurfaceStartCallback&& callback) = 0; public virtual void uiManagerShouldRemoveEventListener(const std::shared_ptr& listener) = 0; + public virtual void uiManagerShouldSynchronouslyUpdateAnimatedProps(const std::unordered_map& updates) = 0; public virtual void uiManagerShouldSynchronouslyUpdateViewOnUIThread(facebook::react::Tag tag, const folly::dynamic& props) = 0; public virtual ~UIManagerDelegate() noexcept = default; } @@ -6219,12 +6224,6 @@ struct facebook::react::SurfaceContext { public std::unordered_set> pendingFamilies; } -struct facebook::react::SurfaceUpdates { - public bool hasLayoutUpdates; - public std::unordered_map propsMap; - public std::unordered_set> families; -} - struct facebook::react::SystraceSection : public facebook::react::DummyTraceSection { template public SystraceSection(const char* name, ConvertsToStringPiece &&... args);