From 82854fd8870d7a30a64bbc9f2fd4824fba608481 Mon Sep 17 00:00:00 2001 From: Bartlomiej Bloniarz Date: Thu, 7 May 2026 04:27:51 -0700 Subject: [PATCH 1/2] Add optimizedAnimatedPropUpdates feature flag Summary: Add a new React Native feature flag, `optimizedAnimatedPropUpdates`, which gates an upcoming optimized code path for applying animated property updates from the C++ animation backend. The flag defaults to disabled and has no behavioural impact on its own. Changelog: [General][Added] - Add `optimizedAnimatedPropUpdates` feature flag 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 58fef2240b5..8a86ca45fad 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<<9fa8df8bd4c6ef4efeddff62e8518472>> */ /** @@ -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 af835e326c9..3eeb48a5ce8 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<<189cc2dd3b42cd736191f13b442e7317>> + * @generated SignedSource<> */ /** @@ -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 4d938732cdf..29de9f2cbb4 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<<81c6672ae539af203fae7e0f9cf541a6>> + * @generated SignedSource<> */ /** @@ -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 da6ecfa2152..24501f74e56 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<<053d67947ccd1a3556c8f6e1b6af5a14>> + * @generated SignedSource<<008cbcccd695935f1d4ca17e69bce718>> */ /** @@ -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 62e08a41871..2c439e1f589 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<<24972f077ba738a8191a2956be61b491>> + * @generated SignedSource<<0d50d159540c35fa0ae015093a483442>> */ /** @@ -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 5b6f5a0134a..8262e9f6e14 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<> + * @generated SignedSource<<0c9e61dd9a6102da81fa394d8a6401e2>> */ /** @@ -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 550dea7bf2e..fb40ea9257f 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<<74f0602ce337a86730efe638ab8e966d>> + * @generated SignedSource<<1f63c355480af133d9245dad20da704c>> */ /** @@ -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 ae047522710..74b16fcd002 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<<2e867941f693c14c0c52475d790cfed4>> + * @generated SignedSource<> */ /** @@ -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 c2df5fcedb2..69777eb04d3 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<<9c49f2931ab219997bc29969de95805d>> + * @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 b951c735944..ebd8c0c3a4e 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<> + * @generated SignedSource<<58cb57cd6fc7d708db8f496744608f53>> */ /** @@ -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 cedf28e78a4..60d10eea694 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<<51ffddd6283069a1846336c65d40338c>> + * @generated SignedSource<<3c16a4403b6b202ef32dcf57c8da4cf6>> */ /** @@ -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::useNativeViewConfigsInBridgelessMode() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(80, "useNativeViewConfigsInBridgelessMode"); + markFlagAsAccessed(81, "useNativeViewConfigsInBridgelessMode"); flagValue = currentProvider_->useNativeViewConfigsInBridgelessMode(); useNativeViewConfigsInBridgelessMode_ = flagValue; @@ -1496,7 +1514,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(81, "useNestedScrollViewAndroid"); + markFlagAsAccessed(82, "useNestedScrollViewAndroid"); flagValue = currentProvider_->useNestedScrollViewAndroid(); useNestedScrollViewAndroid_ = flagValue; @@ -1514,7 +1532,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(82, "useOptimizedViewRegistryOnAndroid"); + markFlagAsAccessed(83, "useOptimizedViewRegistryOnAndroid"); flagValue = currentProvider_->useOptimizedViewRegistryOnAndroid(); useOptimizedViewRegistryOnAndroid_ = flagValue; @@ -1532,7 +1550,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(83, "useSharedAnimatedBackend"); + markFlagAsAccessed(84, "useSharedAnimatedBackend"); flagValue = currentProvider_->useSharedAnimatedBackend(); useSharedAnimatedBackend_ = flagValue; @@ -1550,7 +1568,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(84, "useTraitHiddenOnAndroid"); + markFlagAsAccessed(85, "useTraitHiddenOnAndroid"); flagValue = currentProvider_->useTraitHiddenOnAndroid(); useTraitHiddenOnAndroid_ = flagValue; @@ -1568,7 +1586,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(85, "useTurboModuleInterop"); + markFlagAsAccessed(86, "useTurboModuleInterop"); flagValue = currentProvider_->useTurboModuleInterop(); useTurboModuleInterop_ = flagValue; @@ -1586,7 +1604,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(86, "useTurboModules"); + markFlagAsAccessed(87, "useTurboModules"); flagValue = currentProvider_->useTurboModules(); useTurboModules_ = flagValue; @@ -1604,7 +1622,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(87, "useUnorderedMapInDifferentiator"); + markFlagAsAccessed(88, "useUnorderedMapInDifferentiator"); flagValue = currentProvider_->useUnorderedMapInDifferentiator(); useUnorderedMapInDifferentiator_ = flagValue; @@ -1622,7 +1640,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(88, "viewCullingOutsetRatio"); + markFlagAsAccessed(89, "viewCullingOutsetRatio"); flagValue = currentProvider_->viewCullingOutsetRatio(); viewCullingOutsetRatio_ = flagValue; @@ -1640,7 +1658,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(89, "viewTransitionEnabled"); + markFlagAsAccessed(90, "viewTransitionEnabled"); flagValue = currentProvider_->viewTransitionEnabled(); viewTransitionEnabled_ = flagValue; @@ -1658,7 +1676,7 @@ bool ReactNativeFeatureFlagsAccessor::viewTransitionUseHardwareBitmapAndroid() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(90, "viewTransitionUseHardwareBitmapAndroid"); + markFlagAsAccessed(91, "viewTransitionUseHardwareBitmapAndroid"); flagValue = currentProvider_->viewTransitionUseHardwareBitmapAndroid(); viewTransitionUseHardwareBitmapAndroid_ = 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 745dae1b7fa..fd2f70f7890 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<<46d2d87deec97afaa3d5b89d22fc9ff7>> + * @generated SignedSource<> */ /** @@ -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 2e03064e81b..80037580ee3 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<<3e3db9d13a20d62c9cfb80330de6b4e0>> + * @generated SignedSource<<631d9805d5deb3b9d8fa721c25f41f75>> */ /** @@ -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 f613a6c53cc..32b6ae7b336 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<<963ea85be18e1df973142b74addf3b16>> + * @generated SignedSource<<27b6f1fcb4de7d5338b72043f16c6a54>> */ /** @@ -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 34c682a21cf..a55c2f2f62a 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<> + * @generated SignedSource<<193caba6826abcb01603ae7a96faf434>> */ /** @@ -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 35e34080830..9c8b0aaa681 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<<9061422bb5437e737602f33e74453779>> + * @generated SignedSource<<82af9618cc1a09d5e5c93c9a6cacfcb2>> */ /** @@ -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 03256727f72..93026d08c03 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<<295f75a120dcf079af2846c5cabb8bb4>> + * @generated SignedSource<<9ad664ffda3606d894339dd64e364374>> */ /** @@ -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 2ffbc02a78a..d45fb009475 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 852d4662350..4b11e19ed08 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<<72c610dc5a8efc1eba2121949e1c24a0>> + * @generated SignedSource<<34db44248449a69453a4d0cad69b051d>> * @flow strict * @noformat */ @@ -110,6 +110,7 @@ export type ReactNativeFeatureFlags = $ReadOnly<{ fuseboxNetworkInspectionEnabled: Getter, fuseboxScreenshotCaptureEnabled: Getter, hideOffscreenVirtualViewsOnIOS: Getter, + optimizedAnimatedPropUpdates: Getter, overrideBySynchronousMountPropsAtMountingAndroid: Getter, perfIssuesEnabled: Getter, perfMonitorV2Enabled: Getter, @@ -455,6 +456,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 8e118718a57..f7d338d7ee9 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<<198728d86480c873bf44fc0d8d1633ef>> + * @generated SignedSource<> * @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 f62521c437010c1fd685d2e0eeda821048ad55ae Mon Sep 17 00:00:00 2001 From: Bartlomiej Bloniarz Date: Mon, 11 May 2026 05:24:45 -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: [General][Added] - Add Performance Test example to the rn-tester Animation Backend section Reviewed By: zeyap 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 689123b0232..cf590e816fc 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 00000000000..4e0d5cf3d21 --- /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;