From 0882a710d6154ee256833f36474f4b73145894bb Mon Sep 17 00:00:00 2001 From: Bartlomiej Bloniarz Date: Wed, 6 May 2026 09:50:02 -0700 Subject: [PATCH 1/2] Add optimizedAnimatedPropUpdates feature flag (#56466) Summary: Add a new feature flag to gate the optimized animated prop updates code path. Differential Revision: D101157450 --- .../featureflags/ReactNativeFeatureFlags.kt | 8 +- .../ReactNativeFeatureFlagsCxxAccessor.kt | 12 ++- .../ReactNativeFeatureFlagsCxxInterop.kt | 4 +- .../ReactNativeFeatureFlagsDefaults.kt | 4 +- .../ReactNativeFeatureFlagsLocalAccessor.kt | 13 +++- .../ReactNativeFeatureFlagsProvider.kt | 4 +- .../JReactNativeFeatureFlagsCxxInterop.cpp | 16 +++- .../JReactNativeFeatureFlagsCxxInterop.h | 5 +- .../featureflags/ReactNativeFeatureFlags.cpp | 6 +- .../featureflags/ReactNativeFeatureFlags.h | 7 +- .../ReactNativeFeatureFlagsAccessor.cpp | 76 ++++++++++++------- .../ReactNativeFeatureFlagsAccessor.h | 6 +- .../ReactNativeFeatureFlagsDefaults.h | 6 +- .../ReactNativeFeatureFlagsDynamicProvider.h | 11 ++- .../ReactNativeFeatureFlagsProvider.h | 3 +- .../NativeReactNativeFeatureFlags.cpp | 7 +- .../NativeReactNativeFeatureFlags.h | 4 +- .../ReactNativeFeatureFlags.config.js | 11 +++ .../featureflags/ReactNativeFeatureFlags.js | 7 +- .../specs/NativeReactNativeFeatureFlags.js | 3 +- 20 files changed, 165 insertions(+), 48 deletions(-) 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 c0a1a9a5cd73..b7dfde479b9f 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<<1eca66b21554b00725f2a9be894a0db9>> + * @generated SignedSource<> */ /** @@ -414,6 +414,12 @@ public object ReactNativeFeatureFlags { @JvmStatic public fun hideOffscreenVirtualViewsOnIOS(): Boolean = accessor.hideOffscreenVirtualViewsOnIOS() + /** + * 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. + */ + @JvmStatic + public fun optimizedAnimatedPropUpdates(): Boolean = accessor.optimizedAnimatedPropUpdates() + /** * Override props at mounting with synchronously mounted (i.e. direct manipulation) props from Native Animated. */ diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxAccessor.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxAccessor.kt index a138abe7be3c..9290d5b4cd7d 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxAccessor.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxAccessor.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<<76d977ea53cb2a37fc2ea8549e31cebd>> + * @generated SignedSource<<29996f3db4fa082767a0d7693b41e4d5>> */ /** @@ -84,6 +84,7 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces private var fuseboxNetworkInspectionEnabledCache: Boolean? = null private var fuseboxScreenshotCaptureEnabledCache: Boolean? = null private var hideOffscreenVirtualViewsOnIOSCache: Boolean? = null + private var optimizedAnimatedPropUpdatesCache: Boolean? = null private var overrideBySynchronousMountPropsAtMountingAndroidCache: Boolean? = null private var perfIssuesEnabledCache: Boolean? = null private var perfMonitorV2EnabledCache: Boolean? = null @@ -689,6 +690,15 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces return cached } + override fun optimizedAnimatedPropUpdates(): Boolean { + var cached = optimizedAnimatedPropUpdatesCache + if (cached == null) { + cached = ReactNativeFeatureFlagsCxxInterop.optimizedAnimatedPropUpdates() + optimizedAnimatedPropUpdatesCache = cached + } + return cached + } + override fun overrideBySynchronousMountPropsAtMountingAndroid(): Boolean { var cached = overrideBySynchronousMountPropsAtMountingAndroidCache if (cached == null) { diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxInterop.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxInterop.kt index 24abbbaa92ee..daa4d19ee2ff 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxInterop.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxInterop.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<<5fd97df97e56aaaf70e4ffac6845d409>> */ /** @@ -156,6 +156,8 @@ public object ReactNativeFeatureFlagsCxxInterop { @DoNotStrip @JvmStatic public external fun hideOffscreenVirtualViewsOnIOS(): Boolean + @DoNotStrip @JvmStatic public external fun optimizedAnimatedPropUpdates(): Boolean + @DoNotStrip @JvmStatic public external fun overrideBySynchronousMountPropsAtMountingAndroid(): Boolean @DoNotStrip @JvmStatic public external fun perfIssuesEnabled(): Boolean diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt index 4075e0ac58d1..58b224de07e2 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.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<<9e5b3192d1bec953c116d959ad63283d>> + * @generated SignedSource<<2b8f9b96c860d0a7f59446e1eb45972e>> */ /** @@ -151,6 +151,8 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi override fun hideOffscreenVirtualViewsOnIOS(): Boolean = false + override fun optimizedAnimatedPropUpdates(): Boolean = false + override fun overrideBySynchronousMountPropsAtMountingAndroid(): Boolean = true override fun perfIssuesEnabled(): Boolean = false diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsLocalAccessor.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsLocalAccessor.kt index 1284abcf7a4c..6f6c0f22a191 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsLocalAccessor.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsLocalAccessor.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<<378d6a8de497d26ebbbf55885be27a21>> + * @generated SignedSource<> */ /** @@ -88,6 +88,7 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc private var fuseboxNetworkInspectionEnabledCache: Boolean? = null private var fuseboxScreenshotCaptureEnabledCache: Boolean? = null private var hideOffscreenVirtualViewsOnIOSCache: Boolean? = null + private var optimizedAnimatedPropUpdatesCache: Boolean? = null private var overrideBySynchronousMountPropsAtMountingAndroidCache: Boolean? = null private var perfIssuesEnabledCache: Boolean? = null private var perfMonitorV2EnabledCache: Boolean? = null @@ -757,6 +758,16 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc return cached } + override fun optimizedAnimatedPropUpdates(): Boolean { + var cached = optimizedAnimatedPropUpdatesCache + if (cached == null) { + cached = currentProvider.optimizedAnimatedPropUpdates() + accessedFeatureFlags.add("optimizedAnimatedPropUpdates") + optimizedAnimatedPropUpdatesCache = cached + } + return cached + } + override fun overrideBySynchronousMountPropsAtMountingAndroid(): Boolean { var cached = overrideBySynchronousMountPropsAtMountingAndroidCache if (cached == null) { diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt index 13a3b26839b4..7503fdb2f5cf 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.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<<42e555a40da280b24f84e3ee5b45051d>> + * @generated SignedSource<<2a60862e8d3f078ab2d2d9ca405b65bd>> */ /** @@ -151,6 +151,8 @@ public interface ReactNativeFeatureFlagsProvider { @DoNotStrip public fun hideOffscreenVirtualViewsOnIOS(): Boolean + @DoNotStrip public fun optimizedAnimatedPropUpdates(): Boolean + @DoNotStrip public fun overrideBySynchronousMountPropsAtMountingAndroid(): Boolean @DoNotStrip public fun perfIssuesEnabled(): Boolean diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.cpp b/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.cpp index f586f2e3fcb5..d983200f665b 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.cpp +++ b/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.cpp @@ -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<<24e598146fa5cdf6528177f95620fc43>> */ /** @@ -423,6 +423,12 @@ class ReactNativeFeatureFlagsJavaProvider return method(javaProvider_); } + bool optimizedAnimatedPropUpdates() override { + static const auto method = + getReactNativeFeatureFlagsProviderJavaClass()->getMethod("optimizedAnimatedPropUpdates"); + return method(javaProvider_); + } + bool overrideBySynchronousMountPropsAtMountingAndroid() override { static const auto method = getReactNativeFeatureFlagsProviderJavaClass()->getMethod("overrideBySynchronousMountPropsAtMountingAndroid"); @@ -915,6 +921,11 @@ bool JReactNativeFeatureFlagsCxxInterop::hideOffscreenVirtualViewsOnIOS( return ReactNativeFeatureFlags::hideOffscreenVirtualViewsOnIOS(); } +bool JReactNativeFeatureFlagsCxxInterop::optimizedAnimatedPropUpdates( + facebook::jni::alias_ref /*unused*/) { + return ReactNativeFeatureFlags::optimizedAnimatedPropUpdates(); +} + bool JReactNativeFeatureFlagsCxxInterop::overrideBySynchronousMountPropsAtMountingAndroid( facebook::jni::alias_ref /*unused*/) { return ReactNativeFeatureFlags::overrideBySynchronousMountPropsAtMountingAndroid(); @@ -1278,6 +1289,9 @@ void JReactNativeFeatureFlagsCxxInterop::registerNatives() { makeNativeMethod( "hideOffscreenVirtualViewsOnIOS", JReactNativeFeatureFlagsCxxInterop::hideOffscreenVirtualViewsOnIOS), + makeNativeMethod( + "optimizedAnimatedPropUpdates", + JReactNativeFeatureFlagsCxxInterop::optimizedAnimatedPropUpdates), makeNativeMethod( "overrideBySynchronousMountPropsAtMountingAndroid", JReactNativeFeatureFlagsCxxInterop::overrideBySynchronousMountPropsAtMountingAndroid), diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.h b/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.h index 2b3741c71e80..f414a74ca313 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.h +++ b/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.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<> + * @generated SignedSource<<80ed914a27fee38c08bbddd927f5ee45>> */ /** @@ -222,6 +222,9 @@ class JReactNativeFeatureFlagsCxxInterop static bool hideOffscreenVirtualViewsOnIOS( facebook::jni::alias_ref); + static bool optimizedAnimatedPropUpdates( + facebook::jni::alias_ref); + static bool overrideBySynchronousMountPropsAtMountingAndroid( facebook::jni::alias_ref); diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp index a8bddab5889c..53c320a29e3c 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp @@ -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<> */ /** @@ -282,6 +282,10 @@ bool ReactNativeFeatureFlags::hideOffscreenVirtualViewsOnIOS() { return getAccessor().hideOffscreenVirtualViewsOnIOS(); } +bool ReactNativeFeatureFlags::optimizedAnimatedPropUpdates() { + return getAccessor().optimizedAnimatedPropUpdates(); +} + bool ReactNativeFeatureFlags::overrideBySynchronousMountPropsAtMountingAndroid() { return getAccessor().overrideBySynchronousMountPropsAtMountingAndroid(); } diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h index b0c8f5bb492b..b4b22dbb5c08 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<<8b4288e3f5a8b26951150a3c75ad4356>> + * @generated SignedSource<<8632731292cbd412541783cf562d5f0f>> */ /** @@ -359,6 +359,11 @@ class ReactNativeFeatureFlags { */ RN_EXPORT static bool hideOffscreenVirtualViewsOnIOS(); + /** + * 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. + */ + RN_EXPORT static bool optimizedAnimatedPropUpdates(); + /** * Override props at mounting with synchronously mounted (i.e. direct manipulation) props from Native Animated. */ diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp index 0f1a8ac663c3..a9f23aee5dd1 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp @@ -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<<2e9e9d0c3a313512fcd59c262fd3dc89>> */ /** @@ -1181,6 +1181,24 @@ bool ReactNativeFeatureFlagsAccessor::hideOffscreenVirtualViewsOnIOS() { return flagValue.value(); } +bool ReactNativeFeatureFlagsAccessor::optimizedAnimatedPropUpdates() { + auto flagValue = optimizedAnimatedPropUpdates_.load(); + + if (!flagValue.has_value()) { + // This block is not exclusive but it is not necessary. + // If multiple threads try to initialize the feature flag, we would only + // be accessing the provider multiple times but the end state of this + // instance and the returned flag value would be the same. + + markFlagAsAccessed(64, "optimizedAnimatedPropUpdates"); + + flagValue = currentProvider_->optimizedAnimatedPropUpdates(); + optimizedAnimatedPropUpdates_ = flagValue; + } + + return flagValue.value(); +} + bool ReactNativeFeatureFlagsAccessor::overrideBySynchronousMountPropsAtMountingAndroid() { auto flagValue = overrideBySynchronousMountPropsAtMountingAndroid_.load(); @@ -1190,7 +1208,7 @@ bool ReactNativeFeatureFlagsAccessor::overrideBySynchronousMountPropsAtMountingA // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(64, "overrideBySynchronousMountPropsAtMountingAndroid"); + markFlagAsAccessed(65, "overrideBySynchronousMountPropsAtMountingAndroid"); flagValue = currentProvider_->overrideBySynchronousMountPropsAtMountingAndroid(); overrideBySynchronousMountPropsAtMountingAndroid_ = flagValue; @@ -1208,7 +1226,7 @@ bool ReactNativeFeatureFlagsAccessor::perfIssuesEnabled() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(65, "perfIssuesEnabled"); + markFlagAsAccessed(66, "perfIssuesEnabled"); flagValue = currentProvider_->perfIssuesEnabled(); perfIssuesEnabled_ = flagValue; @@ -1226,7 +1244,7 @@ bool ReactNativeFeatureFlagsAccessor::perfMonitorV2Enabled() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(66, "perfMonitorV2Enabled"); + markFlagAsAccessed(67, "perfMonitorV2Enabled"); flagValue = currentProvider_->perfMonitorV2Enabled(); perfMonitorV2Enabled_ = flagValue; @@ -1244,7 +1262,7 @@ double ReactNativeFeatureFlagsAccessor::preparedTextCacheSize() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(67, "preparedTextCacheSize"); + markFlagAsAccessed(68, "preparedTextCacheSize"); flagValue = currentProvider_->preparedTextCacheSize(); preparedTextCacheSize_ = flagValue; @@ -1262,7 +1280,7 @@ bool ReactNativeFeatureFlagsAccessor::preventShadowTreeCommitExhaustion() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(68, "preventShadowTreeCommitExhaustion"); + markFlagAsAccessed(69, "preventShadowTreeCommitExhaustion"); flagValue = currentProvider_->preventShadowTreeCommitExhaustion(); preventShadowTreeCommitExhaustion_ = flagValue; @@ -1280,7 +1298,7 @@ bool ReactNativeFeatureFlagsAccessor::redBoxV2Android() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(69, "redBoxV2Android"); + markFlagAsAccessed(70, "redBoxV2Android"); flagValue = currentProvider_->redBoxV2Android(); redBoxV2Android_ = flagValue; @@ -1298,7 +1316,7 @@ bool ReactNativeFeatureFlagsAccessor::redBoxV2IOS() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(70, "redBoxV2IOS"); + markFlagAsAccessed(71, "redBoxV2IOS"); flagValue = currentProvider_->redBoxV2IOS(); redBoxV2IOS_ = flagValue; @@ -1316,7 +1334,7 @@ bool ReactNativeFeatureFlagsAccessor::shouldPressibilityUseW3CPointerEventsForHo // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(71, "shouldPressibilityUseW3CPointerEventsForHover"); + markFlagAsAccessed(72, "shouldPressibilityUseW3CPointerEventsForHover"); flagValue = currentProvider_->shouldPressibilityUseW3CPointerEventsForHover(); shouldPressibilityUseW3CPointerEventsForHover_ = flagValue; @@ -1334,7 +1352,7 @@ bool ReactNativeFeatureFlagsAccessor::shouldTriggerResponderTransferOnScrollAndr // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(72, "shouldTriggerResponderTransferOnScrollAndroid"); + markFlagAsAccessed(73, "shouldTriggerResponderTransferOnScrollAndroid"); flagValue = currentProvider_->shouldTriggerResponderTransferOnScrollAndroid(); shouldTriggerResponderTransferOnScrollAndroid_ = flagValue; @@ -1352,7 +1370,7 @@ bool ReactNativeFeatureFlagsAccessor::skipActivityIdentityAssertionOnHostPause() // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(73, "skipActivityIdentityAssertionOnHostPause"); + markFlagAsAccessed(74, "skipActivityIdentityAssertionOnHostPause"); flagValue = currentProvider_->skipActivityIdentityAssertionOnHostPause(); skipActivityIdentityAssertionOnHostPause_ = flagValue; @@ -1370,7 +1388,7 @@ bool ReactNativeFeatureFlagsAccessor::syncAndroidClipBoundsWithOverflow() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(74, "syncAndroidClipBoundsWithOverflow"); + markFlagAsAccessed(75, "syncAndroidClipBoundsWithOverflow"); flagValue = currentProvider_->syncAndroidClipBoundsWithOverflow(); syncAndroidClipBoundsWithOverflow_ = flagValue; @@ -1388,7 +1406,7 @@ bool ReactNativeFeatureFlagsAccessor::traceTurboModulePromiseRejectionsOnAndroid // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(75, "traceTurboModulePromiseRejectionsOnAndroid"); + markFlagAsAccessed(76, "traceTurboModulePromiseRejectionsOnAndroid"); flagValue = currentProvider_->traceTurboModulePromiseRejectionsOnAndroid(); traceTurboModulePromiseRejectionsOnAndroid_ = flagValue; @@ -1406,7 +1424,7 @@ bool ReactNativeFeatureFlagsAccessor::updateRuntimeShadowNodeReferencesOnCommit( // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(76, "updateRuntimeShadowNodeReferencesOnCommit"); + markFlagAsAccessed(77, "updateRuntimeShadowNodeReferencesOnCommit"); flagValue = currentProvider_->updateRuntimeShadowNodeReferencesOnCommit(); updateRuntimeShadowNodeReferencesOnCommit_ = flagValue; @@ -1424,7 +1442,7 @@ bool ReactNativeFeatureFlagsAccessor::updateRuntimeShadowNodeReferencesOnCommitT // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(77, "updateRuntimeShadowNodeReferencesOnCommitThread"); + markFlagAsAccessed(78, "updateRuntimeShadowNodeReferencesOnCommitThread"); flagValue = currentProvider_->updateRuntimeShadowNodeReferencesOnCommitThread(); updateRuntimeShadowNodeReferencesOnCommitThread_ = flagValue; @@ -1442,7 +1460,7 @@ bool ReactNativeFeatureFlagsAccessor::useAlwaysAvailableJSErrorHandling() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(78, "useAlwaysAvailableJSErrorHandling"); + markFlagAsAccessed(79, "useAlwaysAvailableJSErrorHandling"); flagValue = currentProvider_->useAlwaysAvailableJSErrorHandling(); useAlwaysAvailableJSErrorHandling_ = flagValue; @@ -1460,7 +1478,7 @@ bool ReactNativeFeatureFlagsAccessor::useFabricInterop() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(79, "useFabricInterop"); + markFlagAsAccessed(80, "useFabricInterop"); flagValue = currentProvider_->useFabricInterop(); useFabricInterop_ = flagValue; @@ -1478,7 +1496,7 @@ bool ReactNativeFeatureFlagsAccessor::useLISAlgorithmInDifferentiator() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(80, "useLISAlgorithmInDifferentiator"); + markFlagAsAccessed(81, "useLISAlgorithmInDifferentiator"); flagValue = currentProvider_->useLISAlgorithmInDifferentiator(); useLISAlgorithmInDifferentiator_ = flagValue; @@ -1496,7 +1514,7 @@ bool ReactNativeFeatureFlagsAccessor::useNativeViewConfigsInBridgelessMode() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(81, "useNativeViewConfigsInBridgelessMode"); + markFlagAsAccessed(82, "useNativeViewConfigsInBridgelessMode"); flagValue = currentProvider_->useNativeViewConfigsInBridgelessMode(); useNativeViewConfigsInBridgelessMode_ = flagValue; @@ -1514,7 +1532,7 @@ bool ReactNativeFeatureFlagsAccessor::useNestedScrollViewAndroid() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(82, "useNestedScrollViewAndroid"); + markFlagAsAccessed(83, "useNestedScrollViewAndroid"); flagValue = currentProvider_->useNestedScrollViewAndroid(); useNestedScrollViewAndroid_ = flagValue; @@ -1532,7 +1550,7 @@ bool ReactNativeFeatureFlagsAccessor::useOptimizedViewRegistryOnAndroid() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(83, "useOptimizedViewRegistryOnAndroid"); + markFlagAsAccessed(84, "useOptimizedViewRegistryOnAndroid"); flagValue = currentProvider_->useOptimizedViewRegistryOnAndroid(); useOptimizedViewRegistryOnAndroid_ = flagValue; @@ -1550,7 +1568,7 @@ bool ReactNativeFeatureFlagsAccessor::useSharedAnimatedBackend() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(84, "useSharedAnimatedBackend"); + markFlagAsAccessed(85, "useSharedAnimatedBackend"); flagValue = currentProvider_->useSharedAnimatedBackend(); useSharedAnimatedBackend_ = flagValue; @@ -1568,7 +1586,7 @@ bool ReactNativeFeatureFlagsAccessor::useTraitHiddenOnAndroid() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(85, "useTraitHiddenOnAndroid"); + markFlagAsAccessed(86, "useTraitHiddenOnAndroid"); flagValue = currentProvider_->useTraitHiddenOnAndroid(); useTraitHiddenOnAndroid_ = flagValue; @@ -1586,7 +1604,7 @@ bool ReactNativeFeatureFlagsAccessor::useTurboModuleInterop() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(86, "useTurboModuleInterop"); + markFlagAsAccessed(87, "useTurboModuleInterop"); flagValue = currentProvider_->useTurboModuleInterop(); useTurboModuleInterop_ = flagValue; @@ -1604,7 +1622,7 @@ bool ReactNativeFeatureFlagsAccessor::useTurboModules() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(87, "useTurboModules"); + markFlagAsAccessed(88, "useTurboModules"); flagValue = currentProvider_->useTurboModules(); useTurboModules_ = flagValue; @@ -1622,7 +1640,7 @@ bool ReactNativeFeatureFlagsAccessor::useUnorderedMapInDifferentiator() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(88, "useUnorderedMapInDifferentiator"); + markFlagAsAccessed(89, "useUnorderedMapInDifferentiator"); flagValue = currentProvider_->useUnorderedMapInDifferentiator(); useUnorderedMapInDifferentiator_ = flagValue; @@ -1640,7 +1658,7 @@ double ReactNativeFeatureFlagsAccessor::viewCullingOutsetRatio() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(89, "viewCullingOutsetRatio"); + markFlagAsAccessed(90, "viewCullingOutsetRatio"); flagValue = currentProvider_->viewCullingOutsetRatio(); viewCullingOutsetRatio_ = flagValue; @@ -1658,7 +1676,7 @@ bool ReactNativeFeatureFlagsAccessor::viewTransitionEnabled() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(90, "viewTransitionEnabled"); + markFlagAsAccessed(91, "viewTransitionEnabled"); flagValue = currentProvider_->viewTransitionEnabled(); viewTransitionEnabled_ = flagValue; @@ -1676,7 +1694,7 @@ double ReactNativeFeatureFlagsAccessor::virtualViewPrerenderRatio() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(91, "virtualViewPrerenderRatio"); + markFlagAsAccessed(92, "virtualViewPrerenderRatio"); flagValue = currentProvider_->virtualViewPrerenderRatio(); virtualViewPrerenderRatio_ = flagValue; diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h index 67a5012fe00e..7e363ac8dfc6 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.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<<17621e5f711352812633c125b7858537>> + * @generated SignedSource<<571c0a7b75f6177a49be8bfa0d3c20b8>> */ /** @@ -96,6 +96,7 @@ class ReactNativeFeatureFlagsAccessor { bool fuseboxNetworkInspectionEnabled(); bool fuseboxScreenshotCaptureEnabled(); bool hideOffscreenVirtualViewsOnIOS(); + bool optimizedAnimatedPropUpdates(); bool overrideBySynchronousMountPropsAtMountingAndroid(); bool perfIssuesEnabled(); bool perfMonitorV2Enabled(); @@ -135,7 +136,7 @@ class ReactNativeFeatureFlagsAccessor { std::unique_ptr currentProvider_; bool wasOverridden_; - std::array, 92> accessedFeatureFlags_; + std::array, 93> accessedFeatureFlags_; std::atomic> commonTestFlag_; std::atomic> cdpInteractionMetricsEnabled_; @@ -201,6 +202,7 @@ class ReactNativeFeatureFlagsAccessor { std::atomic> fuseboxNetworkInspectionEnabled_; std::atomic> fuseboxScreenshotCaptureEnabled_; std::atomic> hideOffscreenVirtualViewsOnIOS_; + std::atomic> optimizedAnimatedPropUpdates_; std::atomic> overrideBySynchronousMountPropsAtMountingAndroid_; std::atomic> perfIssuesEnabled_; std::atomic> perfMonitorV2Enabled_; diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h index ecc79d832fe4..e0311a3caa99 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.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<<3d1718afe5b2ed63a918ffe09a4a36b1>> + * @generated SignedSource<> */ /** @@ -283,6 +283,10 @@ class ReactNativeFeatureFlagsDefaults : public ReactNativeFeatureFlagsProvider { return false; } + bool optimizedAnimatedPropUpdates() override { + return false; + } + bool overrideBySynchronousMountPropsAtMountingAndroid() override { return true; } diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h index 2bc41176d59c..0d74e6420ad1 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.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<<23e79788b0820f61ba95288412367247>> + * @generated SignedSource<<538991d0d4eb7f79cbf8be382ea5bf95>> */ /** @@ -621,6 +621,15 @@ class ReactNativeFeatureFlagsDynamicProvider : public ReactNativeFeatureFlagsDef return ReactNativeFeatureFlagsDefaults::hideOffscreenVirtualViewsOnIOS(); } + bool optimizedAnimatedPropUpdates() override { + auto value = values_["optimizedAnimatedPropUpdates"]; + if (!value.isNull()) { + return value.getBool(); + } + + return ReactNativeFeatureFlagsDefaults::optimizedAnimatedPropUpdates(); + } + bool overrideBySynchronousMountPropsAtMountingAndroid() override { auto value = values_["overrideBySynchronousMountPropsAtMountingAndroid"]; if (!value.isNull()) { diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h index 400e4bea814d..761d64387e57 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.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<<87316e34516ebbbb34ae384e5a61376b>> + * @generated SignedSource<<2ca6d05a10b62b2a6b2c8999a74cc7c7>> */ /** @@ -89,6 +89,7 @@ class ReactNativeFeatureFlagsProvider { virtual bool fuseboxNetworkInspectionEnabled() = 0; virtual bool fuseboxScreenshotCaptureEnabled() = 0; virtual bool hideOffscreenVirtualViewsOnIOS() = 0; + virtual bool optimizedAnimatedPropUpdates() = 0; virtual bool overrideBySynchronousMountPropsAtMountingAndroid() = 0; virtual bool perfIssuesEnabled() = 0; virtual bool perfMonitorV2Enabled() = 0; diff --git a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp index 8c49f21652bd..a76b8831858c 100644 --- a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp +++ b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp @@ -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<<80e1d4551c51800c138f16228cfae9f6>> + * @generated SignedSource<> */ /** @@ -364,6 +364,11 @@ bool NativeReactNativeFeatureFlags::hideOffscreenVirtualViewsOnIOS( return ReactNativeFeatureFlags::hideOffscreenVirtualViewsOnIOS(); } +bool NativeReactNativeFeatureFlags::optimizedAnimatedPropUpdates( + jsi::Runtime& /*runtime*/) { + return ReactNativeFeatureFlags::optimizedAnimatedPropUpdates(); +} + bool NativeReactNativeFeatureFlags::overrideBySynchronousMountPropsAtMountingAndroid( jsi::Runtime& /*runtime*/) { return ReactNativeFeatureFlags::overrideBySynchronousMountPropsAtMountingAndroid(); diff --git a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h index a3703ec36073..cef4c0711abc 100644 --- a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h +++ b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.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<<1df283aec4b56f36271a187341e7103d>> + * @generated SignedSource<> */ /** @@ -164,6 +164,8 @@ class NativeReactNativeFeatureFlags bool hideOffscreenVirtualViewsOnIOS(jsi::Runtime& runtime); + bool optimizedAnimatedPropUpdates(jsi::Runtime& runtime); + bool overrideBySynchronousMountPropsAtMountingAndroid(jsi::Runtime& runtime); bool perfIssuesEnabled(jsi::Runtime& runtime); diff --git a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js index e930a4f00898..843442779f4a 100644 --- a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js +++ b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js @@ -731,6 +731,17 @@ const definitions: FeatureFlagDefinitions = { }, ossReleaseStage: 'none', }, + optimizedAnimatedPropUpdates: { + defaultValue: false, + 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.', + expectedReleaseValue: true, + purpose: 'experimentation', + }, + ossReleaseStage: 'none', + }, overrideBySynchronousMountPropsAtMountingAndroid: { defaultValue: true, metadata: { diff --git a/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js b/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js index 9be1938a6448..da75e09b1391 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<<349263e08085c42598f13da74ffbf584>> + * @generated SignedSource<<2db3058e40a26829fcd237956fa27b1b>> * @flow strict * @noformat */ @@ -111,6 +111,7 @@ export type ReactNativeFeatureFlags = $ReadOnly<{ fuseboxNetworkInspectionEnabled: Getter, fuseboxScreenshotCaptureEnabled: Getter, hideOffscreenVirtualViewsOnIOS: Getter, + optimizedAnimatedPropUpdates: Getter, overrideBySynchronousMountPropsAtMountingAndroid: Getter, perfIssuesEnabled: Getter, perfMonitorV2Enabled: Getter, @@ -461,6 +462,10 @@ export const fuseboxScreenshotCaptureEnabled: Getter = createNativeFlag * Hides offscreen VirtualViews on iOS by setting hidden = YES to avoid extra cost of views */ export const hideOffscreenVirtualViewsOnIOS: Getter = createNativeFlagGetter('hideOffscreenVirtualViewsOnIOS', 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. + */ +export const optimizedAnimatedPropUpdates: Getter = createNativeFlagGetter('optimizedAnimatedPropUpdates', false); /** * Override props at mounting with synchronously mounted (i.e. direct manipulation) props from Native Animated. */ diff --git a/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js b/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js index 77b4f560fdf2..0d8f99e4cfae 100644 --- a/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js +++ b/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.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<<1fe579457854f95d09dd24e4578dbc65>> + * @generated SignedSource<<9894c76f6aada1e19ee0490693020aaf>> * @flow strict * @noformat */ @@ -89,6 +89,7 @@ export interface Spec extends TurboModule { +fuseboxNetworkInspectionEnabled?: () => boolean; +fuseboxScreenshotCaptureEnabled?: () => boolean; +hideOffscreenVirtualViewsOnIOS?: () => boolean; + +optimizedAnimatedPropUpdates?: () => boolean; +overrideBySynchronousMountPropsAtMountingAndroid?: () => boolean; +perfIssuesEnabled?: () => boolean; +perfMonitorV2Enabled?: () => boolean; From 9c35a5a2a9854652028f0ce11e8c909839a44ca0 Mon Sep 17 00:00:00 2001 From: Bartlomiej Bloniarz Date: Wed, 6 May 2026 09:50:02 -0700 Subject: [PATCH 2/2] Add Performance Test example to rn-tester Animation Backend Summary: Adds a new performance test example to the Animation Backend section in rn-tester. The example renders a grid of animated views to stress-test the animation backend. Changelog: [Internal] Differential Revision: D104053337 --- .../AnimationBackend/AnimationBackendIndex.js | 5 +- .../PerformanceTestExample.js | 145 ++++++++++++++++++ 2 files changed, 149 insertions(+), 1 deletion(-) create mode 100644 packages/rn-tester/js/examples/AnimationBackend/PerformanceTestExample.js diff --git a/packages/rn-tester/js/examples/AnimationBackend/AnimationBackendIndex.js b/packages/rn-tester/js/examples/AnimationBackend/AnimationBackendIndex.js index 689123b02329..cf590e816fc8 100644 --- a/packages/rn-tester/js/examples/AnimationBackend/AnimationBackendIndex.js +++ b/packages/rn-tester/js/examples/AnimationBackend/AnimationBackendIndex.js @@ -11,6 +11,7 @@ import type {RNTesterModule} from '../../types/RNTesterTypes'; import PlaygroundExample from './ChessboardExample'; +import PerformanceTestExample from './PerformanceTestExample'; import SwipeableListExample from './SwipeableListExample'; import * as ReactNativeFeatureFlags from 'react-native/src/private/featureflags/ReactNativeFeatureFlags'; @@ -25,5 +26,7 @@ export default { category: 'UI', description: `Examples demonstrating the Animation Backend for layout-updating animations. ${canUseBackend ? '' : 'You need to enable c++ Animated and the Animation Backend to see these examples.'}`, showIndividualExamples: true, - examples: canUseBackend ? [PlaygroundExample, SwipeableListExample] : [], + examples: canUseBackend + ? [PlaygroundExample, SwipeableListExample, PerformanceTestExample] + : [], } as RNTesterModule; diff --git a/packages/rn-tester/js/examples/AnimationBackend/PerformanceTestExample.js b/packages/rn-tester/js/examples/AnimationBackend/PerformanceTestExample.js new file mode 100644 index 000000000000..4e0d5cf3d21d --- /dev/null +++ b/packages/rn-tester/js/examples/AnimationBackend/PerformanceTestExample.js @@ -0,0 +1,145 @@ +/** + * 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. + * + * @flow strict-local + * @format + */ + +import type {RNTesterModuleExample} from '../../types/RNTesterTypes'; + +import * as React from 'react'; +import {Animated, StyleSheet, Text, View} from 'react-native'; +import * as ReactNativeFeatureFlags from 'react-native/src/private/featureflags/ReactNativeFeatureFlags'; + +const optimizedAnimatedPropUpdatesEnabled = + ReactNativeFeatureFlags.optimizedAnimatedPropUpdates(); + +function FlagIndicator(): React.Node { + return ( + + optimizedAnimatedPropUpdates: + + {optimizedAnimatedPropUpdatesEnabled ? 'ON' : 'OFF'} + + + ); +} + +const ROWS = 20; +const COLS = 10; +const SIZE = 35; + +function PerformanceTest() { + const animatedValue = React.useRef(new Animated.Value(0)).current; + + React.useEffect(() => { + const animation = Animated.loop( + Animated.sequence([ + Animated.timing(animatedValue, { + toValue: 1, + duration: 500, + useNativeDriver: true, + }), + Animated.timing(animatedValue, { + toValue: 0, + duration: 500, + useNativeDriver: true, + }), + ]), + ); + animation.start(); + return () => animation.stop(); + }, [animatedValue]); + + const backgroundColor = animatedValue.interpolate({ + inputRange: [0, 1], + outputRange: ['red', 'lime'], + }); + + const borderRadius = animatedValue.interpolate({ + inputRange: [0, 1], + outputRange: [0, 25], + }); + + // Approximation of Math.pow(2, sv * 3 + 4.5) with piecewise linear interpolation + const perspective = animatedValue.interpolate({ + inputRange: [0, 0.25, 0.5, 0.75, 1], + outputRange: [22.63, 38.05, 64, 107.63, 181.02], + }); + + const animatedStyle = { + borderRadius, + backgroundColor, + transform: [{perspective}, {rotateY: '45deg'}], + }; + + return ( + + + {new Array(ROWS).fill().map((_row, i) => ( + + {new Array(COLS).fill().map((_col, j) => ( + + ))} + + ))} + + ); +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + alignItems: 'center', + justifyContent: 'center', + }, + row: { + flexDirection: 'row', + justifyContent: 'center', + }, + box: { + backgroundColor: 'navy', + borderColor: 'pink', + borderWidth: 1, + width: SIZE, + height: SIZE, + }, + flagRow: { + flexDirection: 'row', + alignItems: 'center', + paddingVertical: 8, + paddingHorizontal: 8, + marginBottom: 4, + backgroundColor: '#f3f4f6', + borderRadius: 4, + }, + flagLabel: { + fontSize: 12, + fontWeight: 'bold', + marginRight: 8, + }, + flagValue: { + fontSize: 12, + fontWeight: 'bold', + }, + flagOn: { + color: '#16a34a', + }, + flagOff: { + color: '#dc2626', + }, +}); + +export default { + title: 'Performance Test', + name: 'performance-test', + description: 'Test out new features and ideas.', + render: (): React.Node => , +} as RNTesterModuleExample;