From 09730ea1d1ddc12fb3a2dade00d583a9746aa4eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20Norte?= Date: Thu, 29 Aug 2024 16:51:06 -0700 Subject: [PATCH 1/3] Remove unnecessary null check from FabricEventDispatcher Differential Revision: D62004021 --- .../react/uimanager/events/FabricEventDispatcher.java | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/FabricEventDispatcher.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/FabricEventDispatcher.java index ce8520ebcaac..73a092651f6b 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/FabricEventDispatcher.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/FabricEventDispatcher.java @@ -89,16 +89,7 @@ public void dispatchAllEvents() { } private void maybePostFrameCallbackFromNonUI() { - if (mReactEventEmitter != null) { - // If the host activity is paused, the frame callback may not be currently - // posted. Ensure that it is so that this event gets delivered promptly. - mCurrentFrameCallback.maybePostFromNonUI(); - } else { - // No JS application has started yet, or resumed. This can happen when a ReactRootView is - // added to view hierarchy, but ReactContext creation has not completed yet. In this case, any - // touch event dispatch will hit this codepath, and we simply queue them so that they - // are dispatched once ReactContext creation completes and JS app is running. - } + mCurrentFrameCallback.maybePostFromNonUI(); } /** Add a listener to this EventDispatcher. */ From 86837589f1fa44637af0ae145fff95bfa9cccf98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20Norte?= Date: Thu, 29 Aug 2024 16:51:06 -0700 Subject: [PATCH 2/3] Improve naming of methods in FabricEventDispatcher Differential Revision: D62004020 --- .../events/FabricEventDispatcher.java | 42 +++++++++---------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/FabricEventDispatcher.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/FabricEventDispatcher.java index 73a092651f6b..0e4ba72f731f 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/FabricEventDispatcher.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/FabricEventDispatcher.java @@ -54,7 +54,7 @@ public void dispatchEvent(Event event) { } event.dispose(); - maybePostFrameCallbackFromNonUI(); + scheduleDispatchOfBatchedEvents(); } private void dispatchSynchronous(Event event) { @@ -85,11 +85,11 @@ private void dispatchSynchronous(Event event) { } public void dispatchAllEvents() { - maybePostFrameCallbackFromNonUI(); + scheduleDispatchOfBatchedEvents(); } - private void maybePostFrameCallbackFromNonUI() { - mCurrentFrameCallback.maybePostFromNonUI(); + private void scheduleDispatchOfBatchedEvents() { + mCurrentFrameCallback.maybeScheduleDispatchOfBatchedEvents(); } /** Add a listener to this EventDispatcher. */ @@ -112,17 +112,17 @@ public void removeBatchEventDispatchedListener(BatchEventDispatchedListener list @Override public void onHostResume() { - maybePostFrameCallbackFromNonUI(); + scheduleDispatchOfBatchedEvents(); } @Override public void onHostPause() { - stopFrameCallback(); + cancelDispatchOfBatchedEvents(); } @Override public void onHostDestroy() { - stopFrameCallback(); + cancelDispatchOfBatchedEvents(); } public void onCatalystInstanceDestroyed() { @@ -130,12 +130,12 @@ public void onCatalystInstanceDestroyed() { new Runnable() { @Override public void run() { - stopFrameCallback(); + cancelDispatchOfBatchedEvents(); } }); } - private void stopFrameCallback() { + private void cancelDispatchOfBatchedEvents() { UiThreadUtil.assertOnUiThread(); mCurrentFrameCallback.stop(); } @@ -154,7 +154,7 @@ public void unregisterEventEmitter(@UIManagerType int uiManagerType) { } private class ScheduleDispatchFrameCallback implements Choreographer.FrameCallback { - private volatile boolean mIsPosted = false; + private volatile boolean mIsDispatchScheduled = false; private boolean mShouldStop = false; @Override @@ -162,9 +162,9 @@ public void doFrame(long frameTimeNanos) { UiThreadUtil.assertOnUiThread(); if (mShouldStop) { - mIsPosted = false; + mIsDispatchScheduled = false; } else { - post(); + dispatchBatchedEvents(); } Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "BatchEventDispatchedListeners"); @@ -181,32 +181,32 @@ public void stop() { mShouldStop = true; } - public void maybePost() { - if (!mIsPosted) { - mIsPosted = true; - post(); + public void maybeDispatchBatchedEvents() { + if (!mIsDispatchScheduled) { + mIsDispatchScheduled = true; + dispatchBatchedEvents(); } } - private void post() { + private void dispatchBatchedEvents() { ReactChoreographer.getInstance() .postFrameCallback(ReactChoreographer.CallbackType.TIMERS_EVENTS, mCurrentFrameCallback); } - public void maybePostFromNonUI() { - if (mIsPosted) { + public void maybeScheduleDispatchOfBatchedEvents() { + if (mIsDispatchScheduled) { return; } // We should only hit this slow path when we receive events while the host activity is paused. if (mReactContext.isOnUiQueueThread()) { - maybePost(); + maybeDispatchBatchedEvents(); } else { mReactContext.runOnUiQueueThread( new Runnable() { @Override public void run() { - maybePost(); + maybeDispatchBatchedEvents(); } }); } From 32ed9fb22d6597a3543ede73dfe180e1c678fb05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20Norte?= Date: Thu, 29 Aug 2024 17:18:17 -0700 Subject: [PATCH 3/3] Implement optimized event batching on Android Summary: Changelog: [internal] When analyzing traces, I noticed that we were using the choreographer to batch events in the native layer on Android. This approach isn't very efficient for 2 reasons: 1. The choreographer runs in specific intervals and there could be some delay between receiving the events and dispatching them from the choreographer. 2. A slow mount operation in the choreographer after receiving the event would completely block the dispatch for the whole duration of the operation. This could take as long as 100s of ms, so it can be very significant. This is especially relevant with layout events, which are dispatched using the same mechanism as input events. In this case, there are instances where we delay rendering in JS because we're doing an expensive mount in the UI thread. It makes sense to batch events in native so we don't do unnecessary work in JS to process them, but there's a better mechanism to do this. Instead of posting a frame callback in the choreographer, we can batch events using a new task in an Android handler running on the UI thread. This would run immediately after the job where the events are dispatched, after all the events are dispatched. This implements that mechanism behind a feature flag. Differential Revision: D62004018 --- .../featureflags/ReactNativeFeatureFlags.kt | 8 +++- .../ReactNativeFeatureFlagsCxxAccessor.kt | 12 +++++- .../ReactNativeFeatureFlagsCxxInterop.kt | 4 +- .../ReactNativeFeatureFlagsDefaults.kt | 4 +- .../ReactNativeFeatureFlagsLocalAccessor.kt | 13 ++++++- .../ReactNativeFeatureFlagsProvider.kt | 4 +- .../events/FabricEventDispatcher.java | 39 ++++++++++++++++++- .../JReactNativeFeatureFlagsCxxInterop.cpp | 16 +++++++- .../JReactNativeFeatureFlagsCxxInterop.h | 5 ++- .../featureflags/ReactNativeFeatureFlags.cpp | 6 ++- .../featureflags/ReactNativeFeatureFlags.h | 7 +++- .../ReactNativeFeatureFlagsAccessor.cpp | 28 ++++++++++--- .../ReactNativeFeatureFlagsAccessor.h | 6 ++- .../ReactNativeFeatureFlagsDefaults.h | 6 ++- .../ReactNativeFeatureFlagsProvider.h | 3 +- .../NativeReactNativeFeatureFlags.cpp | 7 +++- .../NativeReactNativeFeatureFlags.h | 4 +- .../ReactNativeFeatureFlags.config.js | 5 +++ .../featureflags/ReactNativeFeatureFlags.js | 7 +++- .../specs/NativeReactNativeFeatureFlags.js | 3 +- 20 files changed, 162 insertions(+), 25 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 c9e5e731a7b8..ee53e7efa48f 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<> */ /** @@ -280,6 +280,12 @@ public object ReactNativeFeatureFlags { @JvmStatic public fun useOptimisedViewPreallocationOnAndroid(): Boolean = accessor.useOptimisedViewPreallocationOnAndroid() + /** + * Uses an optimized mechanism for event batching on Android that does not need to wait for a Choreographer frame callback. + */ + @JvmStatic + public fun useOptimizedEventBatchingOnAndroid(): Boolean = accessor.useOptimizedEventBatchingOnAndroid() + /** * When enabled, cloning shadow nodes within react native will update the reference held by the current JS fiber tree. */ 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 e00a8b171176..9ef2ffa78f39 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<> + * @generated SignedSource<<47dbdb6ffcd04bb7b2d2d01c4c1e7ace>> */ /** @@ -62,6 +62,7 @@ public class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAccesso private var useNativeViewConfigsInBridgelessModeCache: Boolean? = null private var useNewReactImageViewBackgroundDrawingCache: Boolean? = null private var useOptimisedViewPreallocationOnAndroidCache: Boolean? = null + private var useOptimizedEventBatchingOnAndroidCache: Boolean? = null private var useRuntimeShadowNodeReferenceUpdateCache: Boolean? = null private var useRuntimeShadowNodeReferenceUpdateOnLayoutCache: Boolean? = null private var useStateAlignmentMechanismCache: Boolean? = null @@ -445,6 +446,15 @@ public class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAccesso return cached } + override fun useOptimizedEventBatchingOnAndroid(): Boolean { + var cached = useOptimizedEventBatchingOnAndroidCache + if (cached == null) { + cached = ReactNativeFeatureFlagsCxxInterop.useOptimizedEventBatchingOnAndroid() + useOptimizedEventBatchingOnAndroidCache = cached + } + return cached + } + override fun useRuntimeShadowNodeReferenceUpdate(): Boolean { var cached = useRuntimeShadowNodeReferenceUpdateCache 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 d221e1420b82..949027d48977 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<<580db8acae42a65aeb678d2d6ef4630c>> + * @generated SignedSource<<93ce8e0026e125192af1ad86730941c0>> */ /** @@ -112,6 +112,8 @@ public object ReactNativeFeatureFlagsCxxInterop { @DoNotStrip @JvmStatic public external fun useOptimisedViewPreallocationOnAndroid(): Boolean + @DoNotStrip @JvmStatic public external fun useOptimizedEventBatchingOnAndroid(): Boolean + @DoNotStrip @JvmStatic public external fun useRuntimeShadowNodeReferenceUpdate(): Boolean @DoNotStrip @JvmStatic public external fun useRuntimeShadowNodeReferenceUpdateOnLayout(): 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 f537181cd654..a1c8ff270e48 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<<00aae631e9d9b146fae9be838e04e165>> + * @generated SignedSource<> */ /** @@ -107,6 +107,8 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi override fun useOptimisedViewPreallocationOnAndroid(): Boolean = false + override fun useOptimizedEventBatchingOnAndroid(): Boolean = false + override fun useRuntimeShadowNodeReferenceUpdate(): Boolean = false override fun useRuntimeShadowNodeReferenceUpdateOnLayout(): 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 4f41507f1b11..65b3dc38c756 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<> + * @generated SignedSource<<7727737ae31a239d6b68397fafadbfea>> */ /** @@ -66,6 +66,7 @@ public class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcces private var useNativeViewConfigsInBridgelessModeCache: Boolean? = null private var useNewReactImageViewBackgroundDrawingCache: Boolean? = null private var useOptimisedViewPreallocationOnAndroidCache: Boolean? = null + private var useOptimizedEventBatchingOnAndroidCache: Boolean? = null private var useRuntimeShadowNodeReferenceUpdateCache: Boolean? = null private var useRuntimeShadowNodeReferenceUpdateOnLayoutCache: Boolean? = null private var useStateAlignmentMechanismCache: Boolean? = null @@ -491,6 +492,16 @@ public class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcces return cached } + override fun useOptimizedEventBatchingOnAndroid(): Boolean { + var cached = useOptimizedEventBatchingOnAndroidCache + if (cached == null) { + cached = currentProvider.useOptimizedEventBatchingOnAndroid() + accessedFeatureFlags.add("useOptimizedEventBatchingOnAndroid") + useOptimizedEventBatchingOnAndroidCache = cached + } + return cached + } + override fun useRuntimeShadowNodeReferenceUpdate(): Boolean { var cached = useRuntimeShadowNodeReferenceUpdateCache 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 2226963318ce..b6bfbc6cf98f 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<<2c8c28515aa6929b975ef7193c8bf72e>> + * @generated SignedSource<> */ /** @@ -107,6 +107,8 @@ public interface ReactNativeFeatureFlagsProvider { @DoNotStrip public fun useOptimisedViewPreallocationOnAndroid(): Boolean + @DoNotStrip public fun useOptimizedEventBatchingOnAndroid(): Boolean + @DoNotStrip public fun useRuntimeShadowNodeReferenceUpdate(): Boolean @DoNotStrip public fun useRuntimeShadowNodeReferenceUpdateOnLayout(): Boolean diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/FabricEventDispatcher.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/FabricEventDispatcher.java index 0e4ba72f731f..b7675134bd1b 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/FabricEventDispatcher.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/FabricEventDispatcher.java @@ -7,6 +7,7 @@ package com.facebook.react.uimanager.events; +import android.os.Handler; import android.view.Choreographer; import com.facebook.infer.annotation.Nullsafe; import com.facebook.react.bridge.LifecycleEventListener; @@ -15,6 +16,7 @@ import com.facebook.react.bridge.ReactSoftExceptionLogger; import com.facebook.react.bridge.UIManager; import com.facebook.react.bridge.UiThreadUtil; +import com.facebook.react.internal.featureflags.ReactNativeFeatureFlags; import com.facebook.react.modules.core.ReactChoreographer; import com.facebook.react.uimanager.UIManagerHelper; import com.facebook.react.uimanager.common.UIManagerType; @@ -27,6 +29,8 @@ */ @Nullsafe(Nullsafe.Mode.LOCAL) public class FabricEventDispatcher implements EventDispatcher, LifecycleEventListener { + private static final Handler uiThreadHandler = UiThreadUtil.getUiThreadHandler(); + private final ReactEventEmitter mReactEventEmitter; private final ReactApplicationContext mReactContext; private final CopyOnWriteArrayList mListeners = @@ -36,6 +40,25 @@ public class FabricEventDispatcher implements EventDispatcher, LifecycleEventLis private final FabricEventDispatcher.ScheduleDispatchFrameCallback mCurrentFrameCallback = new FabricEventDispatcher.ScheduleDispatchFrameCallback(); + private boolean mIsDispatchScheduled = false; + private final Runnable mDispatchEventsRunnable = + new Runnable() { + @Override + public void run() { + mIsDispatchScheduled = false; + + Systrace.beginSection( + Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "BatchEventDispatchedListeners"); + try { + for (BatchEventDispatchedListener listener : mPostEventDispatchListeners) { + listener.onBatchEventDispatched(); + } + } finally { + Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE); + } + } + }; + public FabricEventDispatcher(ReactApplicationContext reactContext) { mReactContext = reactContext; mReactContext.addLifecycleEventListener(this); @@ -89,7 +112,14 @@ public void dispatchAllEvents() { } private void scheduleDispatchOfBatchedEvents() { - mCurrentFrameCallback.maybeScheduleDispatchOfBatchedEvents(); + if (ReactNativeFeatureFlags.useOptimizedEventBatchingOnAndroid()) { + if (!mIsDispatchScheduled) { + mIsDispatchScheduled = true; + uiThreadHandler.postAtFrontOfQueue(mDispatchEventsRunnable); + } + } else { + mCurrentFrameCallback.maybeScheduleDispatchOfBatchedEvents(); + } } /** Add a listener to this EventDispatcher. */ @@ -137,7 +167,12 @@ public void run() { private void cancelDispatchOfBatchedEvents() { UiThreadUtil.assertOnUiThread(); - mCurrentFrameCallback.stop(); + if (ReactNativeFeatureFlags.useOptimizedEventBatchingOnAndroid()) { + mIsDispatchScheduled = false; + uiThreadHandler.removeCallbacks(mDispatchEventsRunnable); + } else { + mCurrentFrameCallback.stop(); + } } public void registerEventEmitter(@UIManagerType int uiManagerType, RCTEventEmitter eventEmitter) { 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 aff927743127..b4b62e99d11c 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<<35f5003b77104e68e32741e4626e2ba6>> + * @generated SignedSource<> */ /** @@ -291,6 +291,12 @@ class ReactNativeFeatureFlagsProviderHolder return method(javaProvider_); } + bool useOptimizedEventBatchingOnAndroid() override { + static const auto method = + getReactNativeFeatureFlagsProviderJavaClass()->getMethod("useOptimizedEventBatchingOnAndroid"); + return method(javaProvider_); + } + bool useRuntimeShadowNodeReferenceUpdate() override { static const auto method = getReactNativeFeatureFlagsProviderJavaClass()->getMethod("useRuntimeShadowNodeReferenceUpdate"); @@ -529,6 +535,11 @@ bool JReactNativeFeatureFlagsCxxInterop::useOptimisedViewPreallocationOnAndroid( return ReactNativeFeatureFlags::useOptimisedViewPreallocationOnAndroid(); } +bool JReactNativeFeatureFlagsCxxInterop::useOptimizedEventBatchingOnAndroid( + facebook::jni::alias_ref /*unused*/) { + return ReactNativeFeatureFlags::useOptimizedEventBatchingOnAndroid(); +} + bool JReactNativeFeatureFlagsCxxInterop::useRuntimeShadowNodeReferenceUpdate( facebook::jni::alias_ref /*unused*/) { return ReactNativeFeatureFlags::useRuntimeShadowNodeReferenceUpdate(); @@ -692,6 +703,9 @@ void JReactNativeFeatureFlagsCxxInterop::registerNatives() { makeNativeMethod( "useOptimisedViewPreallocationOnAndroid", JReactNativeFeatureFlagsCxxInterop::useOptimisedViewPreallocationOnAndroid), + makeNativeMethod( + "useOptimizedEventBatchingOnAndroid", + JReactNativeFeatureFlagsCxxInterop::useOptimizedEventBatchingOnAndroid), makeNativeMethod( "useRuntimeShadowNodeReferenceUpdate", JReactNativeFeatureFlagsCxxInterop::useRuntimeShadowNodeReferenceUpdate), 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 760d549343c2..133f5786b652 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<<0e82efb1fac0a62802f5f09e96de31d0>> */ /** @@ -156,6 +156,9 @@ class JReactNativeFeatureFlagsCxxInterop static bool useOptimisedViewPreallocationOnAndroid( facebook::jni::alias_ref); + static bool useOptimizedEventBatchingOnAndroid( + facebook::jni::alias_ref); + static bool useRuntimeShadowNodeReferenceUpdate( 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 a336b0da56a8..5d603e20a93e 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<<31c0fac0172a6d3f9ab84a77d4967f5e>> */ /** @@ -189,6 +189,10 @@ bool ReactNativeFeatureFlags::useOptimisedViewPreallocationOnAndroid() { return getAccessor().useOptimisedViewPreallocationOnAndroid(); } +bool ReactNativeFeatureFlags::useOptimizedEventBatchingOnAndroid() { + return getAccessor().useOptimizedEventBatchingOnAndroid(); +} + bool ReactNativeFeatureFlags::useRuntimeShadowNodeReferenceUpdate() { return getAccessor().useRuntimeShadowNodeReferenceUpdate(); } diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h index 63271e91427b..d371a4736180 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<<748622d87b1051bbb1cd79fb479ab0e2>> + * @generated SignedSource<<56c3d93945f6c320e268c9ba32cbde0f>> */ /** @@ -247,6 +247,11 @@ class ReactNativeFeatureFlags { */ RN_EXPORT static bool useOptimisedViewPreallocationOnAndroid(); + /** + * Uses an optimized mechanism for event batching on Android that does not need to wait for a Choreographer frame callback. + */ + RN_EXPORT static bool useOptimizedEventBatchingOnAndroid(); + /** * When enabled, cloning shadow nodes within react native will update the reference held by the current JS fiber tree. */ diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp index 312b2e36eb03..4356721714c3 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<<90f05e6a6a414704176234724ca1fc04>> + * @generated SignedSource<<73c8cf1d23aa58b4b60e4018c0ddc12e>> */ /** @@ -785,6 +785,24 @@ bool ReactNativeFeatureFlagsAccessor::useOptimisedViewPreallocationOnAndroid() { return flagValue.value(); } +bool ReactNativeFeatureFlagsAccessor::useOptimizedEventBatchingOnAndroid() { + auto flagValue = useOptimizedEventBatchingOnAndroid_.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(42, "useOptimizedEventBatchingOnAndroid"); + + flagValue = currentProvider_->useOptimizedEventBatchingOnAndroid(); + useOptimizedEventBatchingOnAndroid_ = flagValue; + } + + return flagValue.value(); +} + bool ReactNativeFeatureFlagsAccessor::useRuntimeShadowNodeReferenceUpdate() { auto flagValue = useRuntimeShadowNodeReferenceUpdate_.load(); @@ -794,7 +812,7 @@ bool ReactNativeFeatureFlagsAccessor::useRuntimeShadowNodeReferenceUpdate() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(42, "useRuntimeShadowNodeReferenceUpdate"); + markFlagAsAccessed(43, "useRuntimeShadowNodeReferenceUpdate"); flagValue = currentProvider_->useRuntimeShadowNodeReferenceUpdate(); useRuntimeShadowNodeReferenceUpdate_ = flagValue; @@ -812,7 +830,7 @@ bool ReactNativeFeatureFlagsAccessor::useRuntimeShadowNodeReferenceUpdateOnLayou // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(43, "useRuntimeShadowNodeReferenceUpdateOnLayout"); + markFlagAsAccessed(44, "useRuntimeShadowNodeReferenceUpdateOnLayout"); flagValue = currentProvider_->useRuntimeShadowNodeReferenceUpdateOnLayout(); useRuntimeShadowNodeReferenceUpdateOnLayout_ = flagValue; @@ -830,7 +848,7 @@ bool ReactNativeFeatureFlagsAccessor::useStateAlignmentMechanism() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(44, "useStateAlignmentMechanism"); + markFlagAsAccessed(45, "useStateAlignmentMechanism"); flagValue = currentProvider_->useStateAlignmentMechanism(); useStateAlignmentMechanism_ = flagValue; @@ -848,7 +866,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(45, "useTurboModuleInterop"); + markFlagAsAccessed(46, "useTurboModuleInterop"); flagValue = currentProvider_->useTurboModuleInterop(); useTurboModuleInterop_ = flagValue; diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h index 45b9d9bbc04b..e0c66cd1d72a 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<> + * @generated SignedSource<<94a2acd88ff16cfe528e0ec9baf6a383>> */ /** @@ -73,6 +73,7 @@ class ReactNativeFeatureFlagsAccessor { bool useNativeViewConfigsInBridgelessMode(); bool useNewReactImageViewBackgroundDrawing(); bool useOptimisedViewPreallocationOnAndroid(); + bool useOptimizedEventBatchingOnAndroid(); bool useRuntimeShadowNodeReferenceUpdate(); bool useRuntimeShadowNodeReferenceUpdateOnLayout(); bool useStateAlignmentMechanism(); @@ -87,7 +88,7 @@ class ReactNativeFeatureFlagsAccessor { std::unique_ptr currentProvider_; bool wasOverridden_; - std::array, 46> accessedFeatureFlags_; + std::array, 47> accessedFeatureFlags_; std::atomic> commonTestFlag_; std::atomic> allowRecursiveCommitsWithSynchronousMountOnAndroid_; @@ -131,6 +132,7 @@ class ReactNativeFeatureFlagsAccessor { std::atomic> useNativeViewConfigsInBridgelessMode_; std::atomic> useNewReactImageViewBackgroundDrawing_; std::atomic> useOptimisedViewPreallocationOnAndroid_; + std::atomic> useOptimizedEventBatchingOnAndroid_; std::atomic> useRuntimeShadowNodeReferenceUpdate_; std::atomic> useRuntimeShadowNodeReferenceUpdateOnLayout_; std::atomic> useStateAlignmentMechanism_; diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h index 90513ef62287..f999b7c36e98 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<> + * @generated SignedSource<<602c539c5cf438a04a1fb4b6ce05cd3a>> */ /** @@ -195,6 +195,10 @@ class ReactNativeFeatureFlagsDefaults : public ReactNativeFeatureFlagsProvider { return false; } + bool useOptimizedEventBatchingOnAndroid() override { + return false; + } + bool useRuntimeShadowNodeReferenceUpdate() override { return false; } diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h index 8bf060efdcc5..e68a77bb4af7 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<<6bfd567b419f4dbb1a72470265fae271>> + * @generated SignedSource<> */ /** @@ -67,6 +67,7 @@ class ReactNativeFeatureFlagsProvider { virtual bool useNativeViewConfigsInBridgelessMode() = 0; virtual bool useNewReactImageViewBackgroundDrawing() = 0; virtual bool useOptimisedViewPreallocationOnAndroid() = 0; + virtual bool useOptimizedEventBatchingOnAndroid() = 0; virtual bool useRuntimeShadowNodeReferenceUpdate() = 0; virtual bool useRuntimeShadowNodeReferenceUpdateOnLayout() = 0; virtual bool useStateAlignmentMechanism() = 0; diff --git a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp index f33a4a9be696..4015c22847d0 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<<903710df33dd5666f8f2ddce79f66660>> + * @generated SignedSource<<1461fc4acff2a28056df4faa4d622a16>> */ /** @@ -247,6 +247,11 @@ bool NativeReactNativeFeatureFlags::useOptimisedViewPreallocationOnAndroid( return ReactNativeFeatureFlags::useOptimisedViewPreallocationOnAndroid(); } +bool NativeReactNativeFeatureFlags::useOptimizedEventBatchingOnAndroid( + jsi::Runtime& /*runtime*/) { + return ReactNativeFeatureFlags::useOptimizedEventBatchingOnAndroid(); +} + bool NativeReactNativeFeatureFlags::useRuntimeShadowNodeReferenceUpdate( jsi::Runtime& /*runtime*/) { return ReactNativeFeatureFlags::useRuntimeShadowNodeReferenceUpdate(); diff --git a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h index 166b4071f135..225467de77f8 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<<6d1f1233f646704d1403f984dabbb2e4>> + * @generated SignedSource<<9f06e832566c4c1271eaf9bb5cfdf886>> */ /** @@ -119,6 +119,8 @@ class NativeReactNativeFeatureFlags bool useOptimisedViewPreallocationOnAndroid(jsi::Runtime& runtime); + bool useOptimizedEventBatchingOnAndroid(jsi::Runtime& runtime); + bool useRuntimeShadowNodeReferenceUpdate(jsi::Runtime& runtime); bool useRuntimeShadowNodeReferenceUpdateOnLayout(jsi::Runtime& runtime); diff --git a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js index 09b3f412d4a0..e4f1b270a4d5 100644 --- a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js +++ b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js @@ -240,6 +240,11 @@ const definitions: FeatureFlagDefinitions = { description: 'Moves more of the work in view preallocation to the main thread to free up JS thread.', }, + useOptimizedEventBatchingOnAndroid: { + defaultValue: false, + description: + 'Uses an optimized mechanism for event batching on Android that does not need to wait for a Choreographer frame callback.', + }, useRuntimeShadowNodeReferenceUpdate: { defaultValue: false, description: diff --git a/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js b/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js index b6e642d9b100..4eef09446f33 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<<1f82c87c0c011c0bf2e76a6e46bb1d3e>> * @flow strict-local */ @@ -88,6 +88,7 @@ export type ReactNativeFeatureFlags = { useNativeViewConfigsInBridgelessMode: Getter, useNewReactImageViewBackgroundDrawing: Getter, useOptimisedViewPreallocationOnAndroid: Getter, + useOptimizedEventBatchingOnAndroid: Getter, useRuntimeShadowNodeReferenceUpdate: Getter, useRuntimeShadowNodeReferenceUpdateOnLayout: Getter, useStateAlignmentMechanism: Getter, @@ -332,6 +333,10 @@ export const useNewReactImageViewBackgroundDrawing: Getter = createNati * Moves more of the work in view preallocation to the main thread to free up JS thread. */ export const useOptimisedViewPreallocationOnAndroid: Getter = createNativeFlagGetter('useOptimisedViewPreallocationOnAndroid', false); +/** + * Uses an optimized mechanism for event batching on Android that does not need to wait for a Choreographer frame callback. + */ +export const useOptimizedEventBatchingOnAndroid: Getter = createNativeFlagGetter('useOptimizedEventBatchingOnAndroid', false); /** * When enabled, cloning shadow nodes within react native will update the reference held by the current JS fiber tree. */ diff --git a/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js b/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js index 98c7d4b774b6..8db23a6537e1 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<<0f11ba941f22bed8f3b43d1332dc4557>> + * @generated SignedSource<<8370ce5bfcfa27bfdaab6549ae54b6d4>> * @flow strict-local */ @@ -65,6 +65,7 @@ export interface Spec extends TurboModule { +useNativeViewConfigsInBridgelessMode?: () => boolean; +useNewReactImageViewBackgroundDrawing?: () => boolean; +useOptimisedViewPreallocationOnAndroid?: () => boolean; + +useOptimizedEventBatchingOnAndroid?: () => boolean; +useRuntimeShadowNodeReferenceUpdate?: () => boolean; +useRuntimeShadowNodeReferenceUpdateOnLayout?: () => boolean; +useStateAlignmentMechanism?: () => boolean;