From bd322e9d15626b5f4a9933580cee8ed8c70cf3fe Mon Sep 17 00:00:00 2001 From: Zeya Peng Date: Wed, 24 Jun 2026 06:08:32 -0700 Subject: [PATCH] Disable single-op batching under C++ native animated, otherwise enable by default (#57316) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary: Single-op batching (`queueAndExecuteBatchedOperations`) is incompatible with the C++ native animated backend. Move that guard out of per-product feature-flag overrides and into `NativeAnimatedHelper`: `isSingleOpBatching` is now gated on `Platform.OS === 'android'`, `queueAndExecuteBatchedOperations` being available, and `!cxxNativeAnimatedEnabled()`. With the invariant centralized, the redundant `animatedShouldUseSingleOp` overrides in the panel-app and igvr override files are removed (igvr keeps a passthrough override file wired into IGVRPrelude). **Single-op batching is most meaningful only on Android**: it collapses many per-operation JNI crossings into a single call, a significant win on the legacy bridge path. On other platforms (and on the C++/JSI path) the calls do not cross JNI, so batching would help performance only marginally while hurting debuggability — the ops are packed into an opaque serialized buffer and callbacks are rerouted through the device event emitter. That trade-off is why it stays Android-only and off under C++ native animated. Behavior note: with C++ native animated enabled (the default), this is a no-op — single-op was already forced off. Where C++ native animated is disabled on Android, single-op is now on by default (it was previously gated by the `animatedShouldUseSingleOp` flag, which defaulted to off). Changelog:[Internal] Reviewed By: christophpurrer Differential Revision: D109468772 --- .../scripts/featureflags/ReactNativeFeatureFlags.config.js | 2 +- .../react-native/src/private/animated/NativeAnimatedHelper.js | 3 ++- .../src/private/featureflags/ReactNativeFeatureFlags.js | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js index 1ac72cc86ee0..7519e7d58989 100644 --- a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js +++ b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js @@ -993,7 +993,7 @@ const definitions: FeatureFlagDefinitions = { ossReleaseStage: 'none', }, animatedShouldUseSingleOp: { - defaultValue: false, + defaultValue: true, metadata: { dateAdded: '2024-02-05', description: diff --git a/packages/react-native/src/private/animated/NativeAnimatedHelper.js b/packages/react-native/src/private/animated/NativeAnimatedHelper.js index 74a76280692a..eaaefb5b9a0b 100644 --- a/packages/react-native/src/private/animated/NativeAnimatedHelper.js +++ b/packages/react-native/src/private/animated/NativeAnimatedHelper.js @@ -56,7 +56,8 @@ const singleOpQueue: Array = []; const isSingleOpBatching = Platform.OS === 'android' && NativeAnimatedModule?.queueAndExecuteBatchedOperations != null && - ReactNativeFeatureFlags.animatedShouldUseSingleOp(); + ReactNativeFeatureFlags.animatedShouldUseSingleOp() && + !ReactNativeFeatureFlags.cxxNativeAnimatedEnabled(); let flushQueueImmediate = null; const eventListenerGetValueCallbacks: { diff --git a/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js b/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js index ce445d0dc309..918b6b821aa1 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<> + * @generated SignedSource<> * @flow strict * @noformat */ @@ -164,7 +164,7 @@ export const animatedShouldSyncValueBeforeStartCallback: Getter = creat /** * Enables an experimental mega-operation for Animated.js that replaces many calls to native with a single call into native, to reduce JSI/JNI traffic. */ -export const animatedShouldUseSingleOp: Getter = createJavaScriptFlagGetter('animatedShouldUseSingleOp', false); +export const animatedShouldUseSingleOp: Getter = createJavaScriptFlagGetter('animatedShouldUseSingleOp', true); /** * Use the deferred cell render update mechanism for focus change in FlatList.