diff --git a/packages/react-native/React/Fabric/RCTScheduler.h b/packages/react-native/React/Fabric/RCTScheduler.h index ed585390890b1d..8b809580c10c59 100644 --- a/packages/react-native/React/Fabric/RCTScheduler.h +++ b/packages/react-native/React/Fabric/RCTScheduler.h @@ -31,6 +31,8 @@ NS_ASSUME_NONNULL_BEGIN - (void)schedulerShouldRenderTransactions: (std::shared_ptr)mountingCoordinator; +- (void)schedulerShouldMergeReactRevision:(facebook::react::SurfaceId)surfaceId; + - (void)schedulerDidDispatchCommand:(const facebook::react::ShadowView &)shadowView commandName:(const std::string &)commandName args:(const folly::dynamic &)args; diff --git a/packages/react-native/React/Fabric/RCTScheduler.mm b/packages/react-native/React/Fabric/RCTScheduler.mm index 6cc4e5b6feb730..d4bf3d1bed98dd 100644 --- a/packages/react-native/React/Fabric/RCTScheduler.mm +++ b/packages/react-native/React/Fabric/RCTScheduler.mm @@ -36,6 +36,12 @@ void schedulerShouldRenderTransactions(const std::shared_ptrgetShadowTreeRegistry().visit( + surfaceId, [](const ShadowTree &shadowTree) { shadowTree.mergeReactRevision(); }); + }); +} + - (void)schedulerDidDispatchCommand:(const ShadowView &)shadowView commandName:(const std::string &)commandName args:(const folly::dynamic &)args diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java index a4cb1c2dd5ed47..1c65bf2d91815f 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java @@ -959,6 +959,19 @@ public void runGuarded() { } } + @SuppressWarnings("unused") + @AnyThread + @ThreadConfined(ANY) + private void scheduleReactRevisionMerge(int surfaceId) { + UiThreadUtil.runOnUiThread( + () -> { + FabricUIManagerBinding binding = mBinding; + if (binding != null) { + binding.mergeReactRevision(surfaceId); + } + }); + } + /** * This method initiates preloading of an image specified by ImageSource. It can later be consumed * by an ImageView. diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManagerBinding.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManagerBinding.kt index 55818f21103314..b67a9c907d8e31 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManagerBinding.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManagerBinding.kt @@ -83,6 +83,8 @@ internal class FabricUIManagerBinding : HybridClassBase() { external fun reportMount(surfaceId: Int) + external fun mergeReactRevision(surfaceId: Int) + fun register( runtimeExecutor: RuntimeExecutor, runtimeScheduler: RuntimeScheduler, 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 36f3a7a2115280..9f7f692fbc55d5 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<<41608741730ad508aab1b24e0183ef85>> */ /** @@ -168,6 +168,12 @@ public object ReactNativeFeatureFlags { @JvmStatic public fun enableExclusivePropsUpdateAndroid(): Boolean = accessor.enableExclusivePropsUpdateAndroid() + /** + * Enables Fabric commit branching to fix starvation problems and atomic JS updates. + */ + @JvmStatic + public fun enableFabricCommitBranching(): Boolean = accessor.enableFabricCommitBranching() + /** * This feature flag enables logs for Fabric. */ 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 9e44e5af3424e4..bb8808cc117129 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<<3ebb3c265193b4f61e5bf43082c5be54>> + * @generated SignedSource<> */ /** @@ -43,6 +43,7 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces private var enableEagerMainQueueModulesOnIOSCache: Boolean? = null private var enableEagerRootViewAttachmentCache: Boolean? = null private var enableExclusivePropsUpdateAndroidCache: Boolean? = null + private var enableFabricCommitBranchingCache: Boolean? = null private var enableFabricLogsCache: Boolean? = null private var enableFabricRendererCache: Boolean? = null private var enableFontScaleChangesUpdatingLayoutCache: Boolean? = null @@ -318,6 +319,15 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces return cached } + override fun enableFabricCommitBranching(): Boolean { + var cached = enableFabricCommitBranchingCache + if (cached == null) { + cached = ReactNativeFeatureFlagsCxxInterop.enableFabricCommitBranching() + enableFabricCommitBranchingCache = cached + } + return cached + } + override fun enableFabricLogs(): Boolean { var cached = enableFabricLogsCache 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 b93484cae3c165..281da7668cbfd6 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<<1547e15c25a3a1fe1a6223c77e60d874>> */ /** @@ -74,6 +74,8 @@ public object ReactNativeFeatureFlagsCxxInterop { @DoNotStrip @JvmStatic public external fun enableExclusivePropsUpdateAndroid(): Boolean + @DoNotStrip @JvmStatic public external fun enableFabricCommitBranching(): Boolean + @DoNotStrip @JvmStatic public external fun enableFabricLogs(): Boolean @DoNotStrip @JvmStatic public external fun enableFabricRenderer(): 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 4bec0969003759..460747006a791b 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<> + * @generated SignedSource<<9bc1dd6111373452e7246ee3682f7e7c>> */ /** @@ -69,6 +69,8 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi override fun enableExclusivePropsUpdateAndroid(): Boolean = false + override fun enableFabricCommitBranching(): Boolean = false + override fun enableFabricLogs(): Boolean = false override fun enableFabricRenderer(): 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 409ca8c96efdf9..c3f39c8f4becdb 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<<48bf1cbc2c442a6faa0bf64dea60b046>> */ /** @@ -47,6 +47,7 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc private var enableEagerMainQueueModulesOnIOSCache: Boolean? = null private var enableEagerRootViewAttachmentCache: Boolean? = null private var enableExclusivePropsUpdateAndroidCache: Boolean? = null + private var enableFabricCommitBranchingCache: Boolean? = null private var enableFabricLogsCache: Boolean? = null private var enableFabricRendererCache: Boolean? = null private var enableFontScaleChangesUpdatingLayoutCache: Boolean? = null @@ -345,6 +346,16 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc return cached } + override fun enableFabricCommitBranching(): Boolean { + var cached = enableFabricCommitBranchingCache + if (cached == null) { + cached = currentProvider.enableFabricCommitBranching() + accessedFeatureFlags.add("enableFabricCommitBranching") + enableFabricCommitBranchingCache = cached + } + return cached + } + override fun enableFabricLogs(): Boolean { var cached = enableFabricLogsCache 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 ef5b6f3efbd867..52986e6cef8079 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<<09cef8cedd515f477a32c2ed77d86fc4>> + * @generated SignedSource<> */ /** @@ -69,6 +69,8 @@ public interface ReactNativeFeatureFlagsProvider { @DoNotStrip public fun enableExclusivePropsUpdateAndroid(): Boolean + @DoNotStrip public fun enableFabricCommitBranching(): Boolean + @DoNotStrip public fun enableFabricLogs(): Boolean @DoNotStrip public fun enableFabricRenderer(): Boolean diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/fabric/FabricMountingManager.cpp b/packages/react-native/ReactAndroid/src/main/jni/react/fabric/FabricMountingManager.cpp index 8b966236899cae..95ddd80e6d9cc3 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/fabric/FabricMountingManager.cpp +++ b/packages/react-native/ReactAndroid/src/main/jni/react/fabric/FabricMountingManager.cpp @@ -1206,4 +1206,11 @@ void FabricMountingManager::synchronouslyUpdateViewOnUIThread( synchronouslyUpdateViewOnUIThreadJNI(javaUIManager_, viewTag, propsMap); } +void FabricMountingManager::scheduleReactRevisionMerge(SurfaceId surfaceId) { + static const auto scheduleReactRevisionMerge = + JFabricUIManager::javaClassStatic()->getMethod( + "scheduleReactRevisionMerge"); + scheduleReactRevisionMerge(javaUIManager_, surfaceId); +} + } // namespace facebook::react diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/fabric/FabricMountingManager.h b/packages/react-native/ReactAndroid/src/main/jni/react/fabric/FabricMountingManager.h index 9fd87538d165f5..98a66775baeeaa 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/fabric/FabricMountingManager.h +++ b/packages/react-native/ReactAndroid/src/main/jni/react/fabric/FabricMountingManager.h @@ -54,6 +54,8 @@ class FabricMountingManager final { void synchronouslyUpdateViewOnUIThread(Tag viewTag, const folly::dynamic &props); + void scheduleReactRevisionMerge(SurfaceId surfaceId); + private: bool isOnMainThread(); diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/fabric/FabricUIManagerBinding.cpp b/packages/react-native/ReactAndroid/src/main/jni/react/fabric/FabricUIManagerBinding.cpp index 3b2be76a229812..a205ef34ff1529 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/fabric/FabricUIManagerBinding.cpp +++ b/packages/react-native/ReactAndroid/src/main/jni/react/fabric/FabricUIManagerBinding.cpp @@ -685,6 +685,23 @@ void FabricUIManagerBinding::schedulerShouldRenderTransactions( } } +void FabricUIManagerBinding::schedulerShouldMergeReactRevision( + SurfaceId surfaceId) { + std::shared_lock lock(installMutex_); + auto mountingManager = + getMountingManager("schedulerShouldMergeReactRevision"); + if (mountingManager) { + mountingManager->scheduleReactRevisionMerge(surfaceId); + } +} + +void FabricUIManagerBinding::mergeReactRevision(SurfaceId surfaceId) { + std::shared_lock lock(installMutex_); + scheduler_->getUIManager()->getShadowTreeRegistry().visit( + surfaceId, + [](const ShadowTree& shadowTree) { shadowTree.mergeReactRevision(); }); +} + void FabricUIManagerBinding::schedulerDidRequestPreliminaryViewAllocation( const ShadowNode& shadowNode) { using namespace std::literals::string_view_literals; @@ -816,6 +833,8 @@ void FabricUIManagerBinding::registerNatives() { makeNativeMethod( "getRelativeAncestorList", FabricUIManagerBinding::getRelativeAncestorList), + makeNativeMethod( + "mergeReactRevision", FabricUIManagerBinding::mergeReactRevision), }); } diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/fabric/FabricUIManagerBinding.h b/packages/react-native/ReactAndroid/src/main/jni/react/fabric/FabricUIManagerBinding.h index 29adfde41cebb6..65ce2eb987b5c1 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/fabric/FabricUIManagerBinding.h +++ b/packages/react-native/ReactAndroid/src/main/jni/react/fabric/FabricUIManagerBinding.h @@ -96,6 +96,10 @@ class FabricUIManagerBinding : public jni::HybridClass, void schedulerShouldRenderTransactions( const std::shared_ptr &mountingCoordinator) override; + void schedulerShouldMergeReactRevision(SurfaceId surfaceId) override; + + void mergeReactRevision(SurfaceId surfaceId); + void schedulerDidRequestPreliminaryViewAllocation(const ShadowNode &shadowNode) override; void schedulerDidDispatchCommand( 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 36dca784579f64..49cd23b0799934 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<<74926c76bdfbb63f36baca7804123cfb>> */ /** @@ -177,6 +177,12 @@ class ReactNativeFeatureFlagsJavaProvider return method(javaProvider_); } + bool enableFabricCommitBranching() override { + static const auto method = + getReactNativeFeatureFlagsProviderJavaClass()->getMethod("enableFabricCommitBranching"); + return method(javaProvider_); + } + bool enableFabricLogs() override { static const auto method = getReactNativeFeatureFlagsProviderJavaClass()->getMethod("enableFabricLogs"); @@ -698,6 +704,11 @@ bool JReactNativeFeatureFlagsCxxInterop::enableExclusivePropsUpdateAndroid( return ReactNativeFeatureFlags::enableExclusivePropsUpdateAndroid(); } +bool JReactNativeFeatureFlagsCxxInterop::enableFabricCommitBranching( + facebook::jni::alias_ref /*unused*/) { + return ReactNativeFeatureFlags::enableFabricCommitBranching(); +} + bool JReactNativeFeatureFlagsCxxInterop::enableFabricLogs( facebook::jni::alias_ref /*unused*/) { return ReactNativeFeatureFlags::enableFabricLogs(); @@ -1133,6 +1144,9 @@ void JReactNativeFeatureFlagsCxxInterop::registerNatives() { makeNativeMethod( "enableExclusivePropsUpdateAndroid", JReactNativeFeatureFlagsCxxInterop::enableExclusivePropsUpdateAndroid), + makeNativeMethod( + "enableFabricCommitBranching", + JReactNativeFeatureFlagsCxxInterop::enableFabricCommitBranching), makeNativeMethod( "enableFabricLogs", JReactNativeFeatureFlagsCxxInterop::enableFabricLogs), 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 ab6b78c47b3216..780c61a1bb314c 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<> */ /** @@ -99,6 +99,9 @@ class JReactNativeFeatureFlagsCxxInterop static bool enableExclusivePropsUpdateAndroid( facebook::jni::alias_ref); + static bool enableFabricCommitBranching( + facebook::jni::alias_ref); + static bool enableFabricLogs( 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 850df3438b0465..030025d6507fee 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<<08d50062a88a227aa8800e549b6dfe57>> + * @generated SignedSource<> */ /** @@ -118,6 +118,10 @@ bool ReactNativeFeatureFlags::enableExclusivePropsUpdateAndroid() { return getAccessor().enableExclusivePropsUpdateAndroid(); } +bool ReactNativeFeatureFlags::enableFabricCommitBranching() { + return getAccessor().enableFabricCommitBranching(); +} + bool ReactNativeFeatureFlags::enableFabricLogs() { return getAccessor().enableFabricLogs(); } diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h index f4fad0d031568a..1149f3239f3827 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<<4f6913d92515fd817a167ef2a505c777>> + * @generated SignedSource<<5ae45db772734aa6c657de764f5ebcf8>> */ /** @@ -154,6 +154,11 @@ class ReactNativeFeatureFlags { */ RN_EXPORT static bool enableExclusivePropsUpdateAndroid(); + /** + * Enables Fabric commit branching to fix starvation problems and atomic JS updates. + */ + RN_EXPORT static bool enableFabricCommitBranching(); + /** * This feature flag enables logs for Fabric. */ diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp index 0d9ecf5e38afed..9aadd946a49cbf 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<<36d9fe361a0dffc7c15a4cb1faddb61d>> */ /** @@ -443,6 +443,24 @@ bool ReactNativeFeatureFlagsAccessor::enableExclusivePropsUpdateAndroid() { return flagValue.value(); } +bool ReactNativeFeatureFlagsAccessor::enableFabricCommitBranching() { + auto flagValue = enableFabricCommitBranching_.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(23, "enableFabricCommitBranching"); + + flagValue = currentProvider_->enableFabricCommitBranching(); + enableFabricCommitBranching_ = flagValue; + } + + return flagValue.value(); +} + bool ReactNativeFeatureFlagsAccessor::enableFabricLogs() { auto flagValue = enableFabricLogs_.load(); @@ -452,7 +470,7 @@ bool ReactNativeFeatureFlagsAccessor::enableFabricLogs() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(23, "enableFabricLogs"); + markFlagAsAccessed(24, "enableFabricLogs"); flagValue = currentProvider_->enableFabricLogs(); enableFabricLogs_ = flagValue; @@ -470,7 +488,7 @@ bool ReactNativeFeatureFlagsAccessor::enableFabricRenderer() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(24, "enableFabricRenderer"); + markFlagAsAccessed(25, "enableFabricRenderer"); flagValue = currentProvider_->enableFabricRenderer(); enableFabricRenderer_ = flagValue; @@ -488,7 +506,7 @@ bool ReactNativeFeatureFlagsAccessor::enableFontScaleChangesUpdatingLayout() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(25, "enableFontScaleChangesUpdatingLayout"); + markFlagAsAccessed(26, "enableFontScaleChangesUpdatingLayout"); flagValue = currentProvider_->enableFontScaleChangesUpdatingLayout(); enableFontScaleChangesUpdatingLayout_ = flagValue; @@ -506,7 +524,7 @@ bool ReactNativeFeatureFlagsAccessor::enableIOSTextBaselineOffsetPerLine() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(26, "enableIOSTextBaselineOffsetPerLine"); + markFlagAsAccessed(27, "enableIOSTextBaselineOffsetPerLine"); flagValue = currentProvider_->enableIOSTextBaselineOffsetPerLine(); enableIOSTextBaselineOffsetPerLine_ = flagValue; @@ -524,7 +542,7 @@ bool ReactNativeFeatureFlagsAccessor::enableIOSViewClipToPaddingBox() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(27, "enableIOSViewClipToPaddingBox"); + markFlagAsAccessed(28, "enableIOSViewClipToPaddingBox"); flagValue = currentProvider_->enableIOSViewClipToPaddingBox(); enableIOSViewClipToPaddingBox_ = flagValue; @@ -542,7 +560,7 @@ bool ReactNativeFeatureFlagsAccessor::enableImagePrefetchingAndroid() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(28, "enableImagePrefetchingAndroid"); + markFlagAsAccessed(29, "enableImagePrefetchingAndroid"); flagValue = currentProvider_->enableImagePrefetchingAndroid(); enableImagePrefetchingAndroid_ = flagValue; @@ -560,7 +578,7 @@ bool ReactNativeFeatureFlagsAccessor::enableImagePrefetchingJNIBatchingAndroid() // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(29, "enableImagePrefetchingJNIBatchingAndroid"); + markFlagAsAccessed(30, "enableImagePrefetchingJNIBatchingAndroid"); flagValue = currentProvider_->enableImagePrefetchingJNIBatchingAndroid(); enableImagePrefetchingJNIBatchingAndroid_ = flagValue; @@ -578,7 +596,7 @@ bool ReactNativeFeatureFlagsAccessor::enableImagePrefetchingOnUiThreadAndroid() // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(30, "enableImagePrefetchingOnUiThreadAndroid"); + markFlagAsAccessed(31, "enableImagePrefetchingOnUiThreadAndroid"); flagValue = currentProvider_->enableImagePrefetchingOnUiThreadAndroid(); enableImagePrefetchingOnUiThreadAndroid_ = flagValue; @@ -596,7 +614,7 @@ bool ReactNativeFeatureFlagsAccessor::enableImmediateUpdateModeForContentOffsetC // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(31, "enableImmediateUpdateModeForContentOffsetChanges"); + markFlagAsAccessed(32, "enableImmediateUpdateModeForContentOffsetChanges"); flagValue = currentProvider_->enableImmediateUpdateModeForContentOffsetChanges(); enableImmediateUpdateModeForContentOffsetChanges_ = flagValue; @@ -614,7 +632,7 @@ bool ReactNativeFeatureFlagsAccessor::enableImperativeFocus() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(32, "enableImperativeFocus"); + markFlagAsAccessed(33, "enableImperativeFocus"); flagValue = currentProvider_->enableImperativeFocus(); enableImperativeFocus_ = flagValue; @@ -632,7 +650,7 @@ bool ReactNativeFeatureFlagsAccessor::enableInteropViewManagerClassLookUpOptimiz // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(33, "enableInteropViewManagerClassLookUpOptimizationIOS"); + markFlagAsAccessed(34, "enableInteropViewManagerClassLookUpOptimizationIOS"); flagValue = currentProvider_->enableInteropViewManagerClassLookUpOptimizationIOS(); enableInteropViewManagerClassLookUpOptimizationIOS_ = flagValue; @@ -650,7 +668,7 @@ bool ReactNativeFeatureFlagsAccessor::enableIntersectionObserverByDefault() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(34, "enableIntersectionObserverByDefault"); + markFlagAsAccessed(35, "enableIntersectionObserverByDefault"); flagValue = currentProvider_->enableIntersectionObserverByDefault(); enableIntersectionObserverByDefault_ = flagValue; @@ -668,7 +686,7 @@ bool ReactNativeFeatureFlagsAccessor::enableKeyEvents() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(35, "enableKeyEvents"); + markFlagAsAccessed(36, "enableKeyEvents"); flagValue = currentProvider_->enableKeyEvents(); enableKeyEvents_ = flagValue; @@ -686,7 +704,7 @@ bool ReactNativeFeatureFlagsAccessor::enableLayoutAnimationsOnAndroid() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(36, "enableLayoutAnimationsOnAndroid"); + markFlagAsAccessed(37, "enableLayoutAnimationsOnAndroid"); flagValue = currentProvider_->enableLayoutAnimationsOnAndroid(); enableLayoutAnimationsOnAndroid_ = flagValue; @@ -704,7 +722,7 @@ bool ReactNativeFeatureFlagsAccessor::enableLayoutAnimationsOnIOS() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(37, "enableLayoutAnimationsOnIOS"); + markFlagAsAccessed(38, "enableLayoutAnimationsOnIOS"); flagValue = currentProvider_->enableLayoutAnimationsOnIOS(); enableLayoutAnimationsOnIOS_ = flagValue; @@ -722,7 +740,7 @@ bool ReactNativeFeatureFlagsAccessor::enableMainQueueCoordinatorOnIOS() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(38, "enableMainQueueCoordinatorOnIOS"); + markFlagAsAccessed(39, "enableMainQueueCoordinatorOnIOS"); flagValue = currentProvider_->enableMainQueueCoordinatorOnIOS(); enableMainQueueCoordinatorOnIOS_ = flagValue; @@ -740,7 +758,7 @@ bool ReactNativeFeatureFlagsAccessor::enableModuleArgumentNSNullConversionIOS() // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(39, "enableModuleArgumentNSNullConversionIOS"); + markFlagAsAccessed(40, "enableModuleArgumentNSNullConversionIOS"); flagValue = currentProvider_->enableModuleArgumentNSNullConversionIOS(); enableModuleArgumentNSNullConversionIOS_ = flagValue; @@ -758,7 +776,7 @@ bool ReactNativeFeatureFlagsAccessor::enableNativeCSSParsing() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(40, "enableNativeCSSParsing"); + markFlagAsAccessed(41, "enableNativeCSSParsing"); flagValue = currentProvider_->enableNativeCSSParsing(); enableNativeCSSParsing_ = flagValue; @@ -776,7 +794,7 @@ bool ReactNativeFeatureFlagsAccessor::enableNetworkEventReporting() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(41, "enableNetworkEventReporting"); + markFlagAsAccessed(42, "enableNetworkEventReporting"); flagValue = currentProvider_->enableNetworkEventReporting(); enableNetworkEventReporting_ = flagValue; @@ -794,7 +812,7 @@ bool ReactNativeFeatureFlagsAccessor::enablePreparedTextLayout() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(42, "enablePreparedTextLayout"); + markFlagAsAccessed(43, "enablePreparedTextLayout"); flagValue = currentProvider_->enablePreparedTextLayout(); enablePreparedTextLayout_ = flagValue; @@ -812,7 +830,7 @@ bool ReactNativeFeatureFlagsAccessor::enablePropsUpdateReconciliationAndroid() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(43, "enablePropsUpdateReconciliationAndroid"); + markFlagAsAccessed(44, "enablePropsUpdateReconciliationAndroid"); flagValue = currentProvider_->enablePropsUpdateReconciliationAndroid(); enablePropsUpdateReconciliationAndroid_ = flagValue; @@ -830,7 +848,7 @@ bool ReactNativeFeatureFlagsAccessor::enableSwiftUIBasedFilters() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(44, "enableSwiftUIBasedFilters"); + markFlagAsAccessed(45, "enableSwiftUIBasedFilters"); flagValue = currentProvider_->enableSwiftUIBasedFilters(); enableSwiftUIBasedFilters_ = flagValue; @@ -848,7 +866,7 @@ bool ReactNativeFeatureFlagsAccessor::enableViewCulling() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(45, "enableViewCulling"); + markFlagAsAccessed(46, "enableViewCulling"); flagValue = currentProvider_->enableViewCulling(); enableViewCulling_ = flagValue; @@ -866,7 +884,7 @@ bool ReactNativeFeatureFlagsAccessor::enableViewRecycling() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(46, "enableViewRecycling"); + markFlagAsAccessed(47, "enableViewRecycling"); flagValue = currentProvider_->enableViewRecycling(); enableViewRecycling_ = flagValue; @@ -884,7 +902,7 @@ bool ReactNativeFeatureFlagsAccessor::enableViewRecyclingForImage() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(47, "enableViewRecyclingForImage"); + markFlagAsAccessed(48, "enableViewRecyclingForImage"); flagValue = currentProvider_->enableViewRecyclingForImage(); enableViewRecyclingForImage_ = flagValue; @@ -902,7 +920,7 @@ bool ReactNativeFeatureFlagsAccessor::enableViewRecyclingForScrollView() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(48, "enableViewRecyclingForScrollView"); + markFlagAsAccessed(49, "enableViewRecyclingForScrollView"); flagValue = currentProvider_->enableViewRecyclingForScrollView(); enableViewRecyclingForScrollView_ = flagValue; @@ -920,7 +938,7 @@ bool ReactNativeFeatureFlagsAccessor::enableViewRecyclingForText() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(49, "enableViewRecyclingForText"); + markFlagAsAccessed(50, "enableViewRecyclingForText"); flagValue = currentProvider_->enableViewRecyclingForText(); enableViewRecyclingForText_ = flagValue; @@ -938,7 +956,7 @@ bool ReactNativeFeatureFlagsAccessor::enableViewRecyclingForView() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(50, "enableViewRecyclingForView"); + markFlagAsAccessed(51, "enableViewRecyclingForView"); flagValue = currentProvider_->enableViewRecyclingForView(); enableViewRecyclingForView_ = flagValue; @@ -956,7 +974,7 @@ bool ReactNativeFeatureFlagsAccessor::enableVirtualViewContainerStateExperimenta // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(51, "enableVirtualViewContainerStateExperimental"); + markFlagAsAccessed(52, "enableVirtualViewContainerStateExperimental"); flagValue = currentProvider_->enableVirtualViewContainerStateExperimental(); enableVirtualViewContainerStateExperimental_ = flagValue; @@ -974,7 +992,7 @@ bool ReactNativeFeatureFlagsAccessor::enableVirtualViewDebugFeatures() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(52, "enableVirtualViewDebugFeatures"); + markFlagAsAccessed(53, "enableVirtualViewDebugFeatures"); flagValue = currentProvider_->enableVirtualViewDebugFeatures(); enableVirtualViewDebugFeatures_ = flagValue; @@ -992,7 +1010,7 @@ bool ReactNativeFeatureFlagsAccessor::enableVirtualViewRenderState() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(53, "enableVirtualViewRenderState"); + markFlagAsAccessed(54, "enableVirtualViewRenderState"); flagValue = currentProvider_->enableVirtualViewRenderState(); enableVirtualViewRenderState_ = flagValue; @@ -1010,7 +1028,7 @@ bool ReactNativeFeatureFlagsAccessor::enableVirtualViewWindowFocusDetection() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(54, "enableVirtualViewWindowFocusDetection"); + markFlagAsAccessed(55, "enableVirtualViewWindowFocusDetection"); flagValue = currentProvider_->enableVirtualViewWindowFocusDetection(); enableVirtualViewWindowFocusDetection_ = flagValue; @@ -1028,7 +1046,7 @@ bool ReactNativeFeatureFlagsAccessor::enableWebPerformanceAPIsByDefault() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(55, "enableWebPerformanceAPIsByDefault"); + markFlagAsAccessed(56, "enableWebPerformanceAPIsByDefault"); flagValue = currentProvider_->enableWebPerformanceAPIsByDefault(); enableWebPerformanceAPIsByDefault_ = flagValue; @@ -1046,7 +1064,7 @@ bool ReactNativeFeatureFlagsAccessor::fixMappingOfEventPrioritiesBetweenFabricAn // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(56, "fixMappingOfEventPrioritiesBetweenFabricAndReact"); + markFlagAsAccessed(57, "fixMappingOfEventPrioritiesBetweenFabricAndReact"); flagValue = currentProvider_->fixMappingOfEventPrioritiesBetweenFabricAndReact(); fixMappingOfEventPrioritiesBetweenFabricAndReact_ = flagValue; @@ -1064,7 +1082,7 @@ bool ReactNativeFeatureFlagsAccessor::fixTextClippingAndroid15useBoundsForWidth( // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(57, "fixTextClippingAndroid15useBoundsForWidth"); + markFlagAsAccessed(58, "fixTextClippingAndroid15useBoundsForWidth"); flagValue = currentProvider_->fixTextClippingAndroid15useBoundsForWidth(); fixTextClippingAndroid15useBoundsForWidth_ = flagValue; @@ -1082,7 +1100,7 @@ bool ReactNativeFeatureFlagsAccessor::fuseboxAssertSingleHostState() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(58, "fuseboxAssertSingleHostState"); + markFlagAsAccessed(59, "fuseboxAssertSingleHostState"); flagValue = currentProvider_->fuseboxAssertSingleHostState(); fuseboxAssertSingleHostState_ = flagValue; @@ -1100,7 +1118,7 @@ bool ReactNativeFeatureFlagsAccessor::fuseboxEnabledRelease() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(59, "fuseboxEnabledRelease"); + markFlagAsAccessed(60, "fuseboxEnabledRelease"); flagValue = currentProvider_->fuseboxEnabledRelease(); fuseboxEnabledRelease_ = flagValue; @@ -1118,7 +1136,7 @@ bool ReactNativeFeatureFlagsAccessor::fuseboxNetworkInspectionEnabled() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(60, "fuseboxNetworkInspectionEnabled"); + markFlagAsAccessed(61, "fuseboxNetworkInspectionEnabled"); flagValue = currentProvider_->fuseboxNetworkInspectionEnabled(); fuseboxNetworkInspectionEnabled_ = flagValue; @@ -1136,7 +1154,7 @@ bool ReactNativeFeatureFlagsAccessor::hideOffscreenVirtualViewsOnIOS() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(61, "hideOffscreenVirtualViewsOnIOS"); + markFlagAsAccessed(62, "hideOffscreenVirtualViewsOnIOS"); flagValue = currentProvider_->hideOffscreenVirtualViewsOnIOS(); hideOffscreenVirtualViewsOnIOS_ = flagValue; @@ -1154,7 +1172,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(62, "overrideBySynchronousMountPropsAtMountingAndroid"); + markFlagAsAccessed(63, "overrideBySynchronousMountPropsAtMountingAndroid"); flagValue = currentProvider_->overrideBySynchronousMountPropsAtMountingAndroid(); overrideBySynchronousMountPropsAtMountingAndroid_ = flagValue; @@ -1172,7 +1190,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(63, "perfIssuesEnabled"); + markFlagAsAccessed(64, "perfIssuesEnabled"); flagValue = currentProvider_->perfIssuesEnabled(); perfIssuesEnabled_ = flagValue; @@ -1190,7 +1208,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(64, "perfMonitorV2Enabled"); + markFlagAsAccessed(65, "perfMonitorV2Enabled"); flagValue = currentProvider_->perfMonitorV2Enabled(); perfMonitorV2Enabled_ = flagValue; @@ -1208,7 +1226,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(65, "preparedTextCacheSize"); + markFlagAsAccessed(66, "preparedTextCacheSize"); flagValue = currentProvider_->preparedTextCacheSize(); preparedTextCacheSize_ = flagValue; @@ -1226,7 +1244,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(66, "preventShadowTreeCommitExhaustion"); + markFlagAsAccessed(67, "preventShadowTreeCommitExhaustion"); flagValue = currentProvider_->preventShadowTreeCommitExhaustion(); preventShadowTreeCommitExhaustion_ = flagValue; @@ -1244,7 +1262,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(67, "shouldPressibilityUseW3CPointerEventsForHover"); + markFlagAsAccessed(68, "shouldPressibilityUseW3CPointerEventsForHover"); flagValue = currentProvider_->shouldPressibilityUseW3CPointerEventsForHover(); shouldPressibilityUseW3CPointerEventsForHover_ = flagValue; @@ -1262,7 +1280,7 @@ bool ReactNativeFeatureFlagsAccessor::shouldResetClickableWhenRecyclingView() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(68, "shouldResetClickableWhenRecyclingView"); + markFlagAsAccessed(69, "shouldResetClickableWhenRecyclingView"); flagValue = currentProvider_->shouldResetClickableWhenRecyclingView(); shouldResetClickableWhenRecyclingView_ = flagValue; @@ -1280,7 +1298,7 @@ bool ReactNativeFeatureFlagsAccessor::shouldResetOnClickListenerWhenRecyclingVie // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(69, "shouldResetOnClickListenerWhenRecyclingView"); + markFlagAsAccessed(70, "shouldResetOnClickListenerWhenRecyclingView"); flagValue = currentProvider_->shouldResetOnClickListenerWhenRecyclingView(); shouldResetOnClickListenerWhenRecyclingView_ = flagValue; @@ -1298,7 +1316,7 @@ bool ReactNativeFeatureFlagsAccessor::shouldSetEnabledBasedOnAccessibilityState( // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(70, "shouldSetEnabledBasedOnAccessibilityState"); + markFlagAsAccessed(71, "shouldSetEnabledBasedOnAccessibilityState"); flagValue = currentProvider_->shouldSetEnabledBasedOnAccessibilityState(); shouldSetEnabledBasedOnAccessibilityState_ = flagValue; @@ -1316,7 +1334,7 @@ bool ReactNativeFeatureFlagsAccessor::shouldSetIsClickableByDefault() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(71, "shouldSetIsClickableByDefault"); + markFlagAsAccessed(72, "shouldSetIsClickableByDefault"); flagValue = currentProvider_->shouldSetIsClickableByDefault(); shouldSetIsClickableByDefault_ = 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::traceTurboModulePromiseRejectionsOnAndroid // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(74, "traceTurboModulePromiseRejectionsOnAndroid"); + markFlagAsAccessed(75, "traceTurboModulePromiseRejectionsOnAndroid"); flagValue = currentProvider_->traceTurboModulePromiseRejectionsOnAndroid(); traceTurboModulePromiseRejectionsOnAndroid_ = flagValue; @@ -1388,7 +1406,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(75, "updateRuntimeShadowNodeReferencesOnCommit"); + markFlagAsAccessed(76, "updateRuntimeShadowNodeReferencesOnCommit"); flagValue = currentProvider_->updateRuntimeShadowNodeReferencesOnCommit(); updateRuntimeShadowNodeReferencesOnCommit_ = flagValue; @@ -1406,7 +1424,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(76, "useAlwaysAvailableJSErrorHandling"); + markFlagAsAccessed(77, "useAlwaysAvailableJSErrorHandling"); flagValue = currentProvider_->useAlwaysAvailableJSErrorHandling(); useAlwaysAvailableJSErrorHandling_ = flagValue; @@ -1424,7 +1442,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(77, "useFabricInterop"); + markFlagAsAccessed(78, "useFabricInterop"); flagValue = currentProvider_->useFabricInterop(); useFabricInterop_ = flagValue; @@ -1442,7 +1460,7 @@ bool ReactNativeFeatureFlagsAccessor::useNativeEqualsInNativeReadableArrayAndroi // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(78, "useNativeEqualsInNativeReadableArrayAndroid"); + markFlagAsAccessed(79, "useNativeEqualsInNativeReadableArrayAndroid"); flagValue = currentProvider_->useNativeEqualsInNativeReadableArrayAndroid(); useNativeEqualsInNativeReadableArrayAndroid_ = flagValue; @@ -1460,7 +1478,7 @@ bool ReactNativeFeatureFlagsAccessor::useNativeTransformHelperAndroid() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(79, "useNativeTransformHelperAndroid"); + markFlagAsAccessed(80, "useNativeTransformHelperAndroid"); flagValue = currentProvider_->useNativeTransformHelperAndroid(); useNativeTransformHelperAndroid_ = 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::useRawPropsJsiValue() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(81, "useRawPropsJsiValue"); + markFlagAsAccessed(82, "useRawPropsJsiValue"); flagValue = currentProvider_->useRawPropsJsiValue(); useRawPropsJsiValue_ = flagValue; @@ -1514,7 +1532,7 @@ bool ReactNativeFeatureFlagsAccessor::useShadowNodeStateOnClone() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(82, "useShadowNodeStateOnClone"); + markFlagAsAccessed(83, "useShadowNodeStateOnClone"); flagValue = currentProvider_->useShadowNodeStateOnClone(); useShadowNodeStateOnClone_ = 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 @@ 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(87, "viewCullingOutsetRatio"); + markFlagAsAccessed(88, "viewCullingOutsetRatio"); flagValue = currentProvider_->viewCullingOutsetRatio(); viewCullingOutsetRatio_ = flagValue; @@ -1622,7 +1640,7 @@ double ReactNativeFeatureFlagsAccessor::virtualViewHysteresisRatio() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(88, "virtualViewHysteresisRatio"); + markFlagAsAccessed(89, "virtualViewHysteresisRatio"); flagValue = currentProvider_->virtualViewHysteresisRatio(); virtualViewHysteresisRatio_ = flagValue; @@ -1640,7 +1658,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(89, "virtualViewPrerenderRatio"); + markFlagAsAccessed(90, "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 ce266b6cad7539..3df8f8bf59dbc5 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<<1bcf3e91c74400c90689e350fab7b4ce>> + * @generated SignedSource<> */ /** @@ -55,6 +55,7 @@ class ReactNativeFeatureFlagsAccessor { bool enableEagerMainQueueModulesOnIOS(); bool enableEagerRootViewAttachment(); bool enableExclusivePropsUpdateAndroid(); + bool enableFabricCommitBranching(); bool enableFabricLogs(); bool enableFabricRenderer(); bool enableFontScaleChangesUpdatingLayout(); @@ -133,7 +134,7 @@ class ReactNativeFeatureFlagsAccessor { std::unique_ptr currentProvider_; bool wasOverridden_; - std::array, 90> accessedFeatureFlags_; + std::array, 91> accessedFeatureFlags_; std::atomic> commonTestFlag_; std::atomic> cdpInteractionMetricsEnabled_; @@ -158,6 +159,7 @@ class ReactNativeFeatureFlagsAccessor { std::atomic> enableEagerMainQueueModulesOnIOS_; std::atomic> enableEagerRootViewAttachment_; std::atomic> enableExclusivePropsUpdateAndroid_; + std::atomic> enableFabricCommitBranching_; std::atomic> enableFabricLogs_; std::atomic> enableFabricRenderer_; std::atomic> enableFontScaleChangesUpdatingLayout_; diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h index 6b13cd5577bf65..9e9e1bd89b1c51 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<<33cacaaeb6994f78f5a2d9379618e2a0>> + * @generated SignedSource<<06b786f98c25c4d63129083757577538>> */ /** @@ -119,6 +119,10 @@ class ReactNativeFeatureFlagsDefaults : public ReactNativeFeatureFlagsProvider { return false; } + bool enableFabricCommitBranching() override { + return false; + } + bool enableFabricLogs() override { return false; } diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h index 4c17230456b663..4549f9dd112c27 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<<543338d7b54991244389bca238b2078d>> + * @generated SignedSource<<725b9bd19186eaedb472e8b4fb16151e>> */ /** @@ -252,6 +252,15 @@ class ReactNativeFeatureFlagsDynamicProvider : public ReactNativeFeatureFlagsDef return ReactNativeFeatureFlagsDefaults::enableExclusivePropsUpdateAndroid(); } + bool enableFabricCommitBranching() override { + auto value = values_["enableFabricCommitBranching"]; + if (!value.isNull()) { + return value.getBool(); + } + + return ReactNativeFeatureFlagsDefaults::enableFabricCommitBranching(); + } + bool enableFabricLogs() override { auto value = values_["enableFabricLogs"]; if (!value.isNull()) { diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h index ff57c05cc67b28..fe2a5d08b7178a 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<<4c2b82eb2fc7f82b09b7b0b489f3d447>> + * @generated SignedSource<<6579e33e13da5521becb153b7129e9c2>> */ /** @@ -48,6 +48,7 @@ class ReactNativeFeatureFlagsProvider { virtual bool enableEagerMainQueueModulesOnIOS() = 0; virtual bool enableEagerRootViewAttachment() = 0; virtual bool enableExclusivePropsUpdateAndroid() = 0; + virtual bool enableFabricCommitBranching() = 0; virtual bool enableFabricLogs() = 0; virtual bool enableFabricRenderer() = 0; virtual bool enableFontScaleChangesUpdatingLayout() = 0; diff --git a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp index a5aadbbde5f874..49af9e91a90d37 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<> + * @generated SignedSource<<52b4024e0915926bb4915455197b646a>> */ /** @@ -159,6 +159,11 @@ bool NativeReactNativeFeatureFlags::enableExclusivePropsUpdateAndroid( return ReactNativeFeatureFlags::enableExclusivePropsUpdateAndroid(); } +bool NativeReactNativeFeatureFlags::enableFabricCommitBranching( + jsi::Runtime& /*runtime*/) { + return ReactNativeFeatureFlags::enableFabricCommitBranching(); +} + bool NativeReactNativeFeatureFlags::enableFabricLogs( jsi::Runtime& /*runtime*/) { return ReactNativeFeatureFlags::enableFabricLogs(); diff --git a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h index 204f309cbb2066..3777ea00694f9c 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<<3caa7ab068af35b4b198bce5925dea1d>> + * @generated SignedSource<<551f6d4ccc15614296b7c0ab1b6140f2>> */ /** @@ -82,6 +82,8 @@ class NativeReactNativeFeatureFlags bool enableExclusivePropsUpdateAndroid(jsi::Runtime& runtime); + bool enableFabricCommitBranching(jsi::Runtime& runtime); + bool enableFabricLogs(jsi::Runtime& runtime); bool enableFabricRenderer(jsi::Runtime& runtime); diff --git a/packages/react-native/ReactCommon/react/renderer/mounting/ShadowTree.cpp b/packages/react-native/ReactCommon/react/renderer/mounting/ShadowTree.cpp index 58c5bbaa4e0844..d8ffb5d40543f3 100644 --- a/packages/react-native/ReactCommon/react/renderer/mounting/ShadowTree.cpp +++ b/packages/react-native/ReactCommon/react/renderer/mounting/ShadowTree.cpp @@ -286,18 +286,32 @@ CommitStatus ShadowTree::tryCommit( const CommitOptions& commitOptions) const { TraceSection s("ShadowTree::commit"); + auto isReactBranch = ReactNativeFeatureFlags::enableFabricCommitBranching() && + commitOptions.source == CommitSource::React; + + // Commits on the JS branch are never synchronous. + react_native_assert(!isReactBranch || !commitOptions.mountSynchronously); + auto telemetry = TransactionTelemetry{}; telemetry.willCommit(); CommitMode commitMode; auto oldRevision = ShadowTreeRevision{}; + auto oldRevisionForStateProgression = ShadowTreeRevision{}; auto newRevision = ShadowTreeRevision{}; { // Reading `currentRevision_` in shared manner. SharedLock lock = sharedCommitLock(); commitMode = commitMode_; - oldRevision = currentRevision_; + + if (isReactBranch && currentReactRevision_.has_value()) { + oldRevision = currentReactRevision_.value(); + } else { + oldRevision = currentRevision_; + } + + oldRevisionForStateProgression = currentRevision_; } const auto& oldRootShadowNode = oldRevision.rootShadowNode; @@ -308,8 +322,8 @@ CommitStatus ShadowTree::tryCommit( } if (commitOptions.enableStateReconciliation) { - auto updatedNewRootShadowNode = - progressState(*newRootShadowNode, *oldRootShadowNode); + auto updatedNewRootShadowNode = progressState( + *newRootShadowNode, *oldRevisionForStateProgression.rootShadowNode); if (updatedNewRootShadowNode) { newRootShadowNode = std::static_pointer_cast(updatedNewRootShadowNode); @@ -336,9 +350,10 @@ CommitStatus ShadowTree::tryCommit( { // Updating `currentRevision_` in unique manner if it hasn't changed. - UniqueLock lock = uniqueCommitLock(); + UniqueLock lock = uniqueCommitLock( + /*defer*/ isReactBranch); - if (currentRevision_.number != oldRevision.number) { + if (!isReactBranch && currentRevision_.number != oldRevision.number) { return CommitStatus::Failed; } @@ -364,12 +379,18 @@ CommitStatus ShadowTree::tryCommit( .number = newRevisionNumber, .telemetry = telemetry}; - currentRevision_ = newRevision; + if (isReactBranch) { + currentReactRevision_ = newRevision; + } else { + currentRevision_ = newRevision; + } } emitLayoutEvents(affectedLayoutableNodes); - if (commitMode == CommitMode::Normal) { + if (isReactBranch) { + scheduleReactRevisionMerge(); + } else if (commitMode == CommitMode::Normal) { mount(std::move(newRevision), commitOptions.mountSynchronously); } @@ -381,6 +402,10 @@ ShadowTreeRevision ShadowTree::getCurrentRevision() const { return currentRevision_; } +std::optional ShadowTree::getCurrentReactRevision() const { + return currentReactRevision_; +} + void ShadowTree::mount(ShadowTreeRevision revision, bool mountSynchronously) const { mountingCoordinator_->push(std::move(revision)); @@ -388,6 +413,28 @@ void ShadowTree::mount(ShadowTreeRevision revision, bool mountSynchronously) mountingCoordinator_, mountSynchronously); } +void ShadowTree::mergeReactRevision() const { + if (!currentReactRevision_.has_value()) { + return; + } + + this->commit( + [revision = std::exchange(currentReactRevision_, std::nullopt).value()]( + const RootShadowNode& /*oldRootShadowNode*/) { + return std::make_shared( + *revision.rootShadowNode, ShadowNodeFragment{}); + }, + { + .enableStateReconciliation = true, + .mountSynchronously = true, + .source = CommitSource::ReactRevisionMerge, + }); +} + +void ShadowTree::scheduleReactRevisionMerge() const { + delegate_.shadowTreeDidFinishReactCommit(*this); +} + void ShadowTree::commitEmptyTree() const { commit( [](const RootShadowNode& oldRootShadowNode) @@ -426,11 +473,13 @@ void ShadowTree::notifyDelegatesOfUpdates() const { delegate_.shadowTreeDidFinishTransaction(mountingCoordinator_, true); } -inline ShadowTree::UniqueLock ShadowTree::uniqueCommitLock() const { +inline ShadowTree::UniqueLock ShadowTree::uniqueCommitLock(bool defer) const { if (ReactNativeFeatureFlags::preventShadowTreeCommitExhaustion()) { - return std::unique_lock{commitMutexRecursive_}; + return defer ? std::unique_lock{commitMutexRecursive_, std::defer_lock} + : std::unique_lock{commitMutexRecursive_}; } else { - return std::unique_lock{commitMutex_}; + return defer ? std::unique_lock{commitMutex_, std::defer_lock} + : std::unique_lock{commitMutex_}; } } diff --git a/packages/react-native/ReactCommon/react/renderer/mounting/ShadowTree.h b/packages/react-native/ReactCommon/react/renderer/mounting/ShadowTree.h index 3b44d6e49ca470..fffe617363e5b5 100644 --- a/packages/react-native/ReactCommon/react/renderer/mounting/ShadowTree.h +++ b/packages/react-native/ReactCommon/react/renderer/mounting/ShadowTree.h @@ -50,6 +50,7 @@ enum class ShadowTreeCommitMode { enum class ShadowTreeCommitSource { Unknown, React, + ReactRevisionMerge, }; struct ShadowTreeCommitOptions { @@ -123,6 +124,12 @@ class ShadowTree final { */ ShadowTreeRevision getCurrentRevision() const; + /* + * Returns a `ShadowTreeRevision` representing the momentary state of + * the `ShadowTree` in the JS thread. + */ + std::optional getCurrentReactRevision() const; + /* * Commit an empty tree (a new `RootShadowNode` with no children). */ @@ -136,6 +143,12 @@ class ShadowTree final { std::shared_ptr getMountingCoordinator() const; + /** + * Commits the current JS revision to the "main" branch of the ShadowTree. + * No-op if the JS revision doesn't exist. + */ + void mergeReactRevision() const; + private: constexpr static ShadowTreeRevision::Number INITIAL_REVISION{0}; @@ -143,18 +156,21 @@ class ShadowTree final { void emitLayoutEvents(std::vector &affectedLayoutableNodes) const; + void scheduleReactRevisionMerge() const; + const SurfaceId surfaceId_; const ShadowTreeDelegate &delegate_; mutable std::shared_mutex commitMutex_; mutable std::recursive_mutex commitMutexRecursive_; mutable CommitMode commitMode_{CommitMode::Normal}; // Protected by `commitMutex_`. mutable ShadowTreeRevision currentRevision_; // Protected by `commitMutex_`. + mutable std::optional currentReactRevision_; // Only updated from JS thread. std::shared_ptr mountingCoordinator_; using UniqueLock = std::variant, std::unique_lock>; using SharedLock = std::variant, std::unique_lock>; - inline UniqueLock uniqueCommitLock() const; + inline UniqueLock uniqueCommitLock(bool defer = false) const; inline SharedLock sharedCommitLock() const; }; diff --git a/packages/react-native/ReactCommon/react/renderer/mounting/ShadowTreeDelegate.h b/packages/react-native/ReactCommon/react/renderer/mounting/ShadowTreeDelegate.h index 650d898a6f3453..40e610911a28e8 100644 --- a/packages/react-native/ReactCommon/react/renderer/mounting/ShadowTreeDelegate.h +++ b/packages/react-native/ReactCommon/react/renderer/mounting/ShadowTreeDelegate.h @@ -38,6 +38,11 @@ class ShadowTreeDelegate { std::shared_ptr mountingCoordinator, bool mountSynchronously) const = 0; + /* + * Called right after Shadow Tree commits a new JS revision of the tree. + */ + virtual void shadowTreeDidFinishReactCommit(const ShadowTree &shadowTree) const = 0; + virtual ~ShadowTreeDelegate() noexcept = default; }; diff --git a/packages/react-native/ReactCommon/react/renderer/mounting/tests/StateReconciliationTest.cpp b/packages/react-native/ReactCommon/react/renderer/mounting/tests/StateReconciliationTest.cpp index 7290d76cc637cb..a9c553e4f84fd9 100644 --- a/packages/react-native/ReactCommon/react/renderer/mounting/tests/StateReconciliationTest.cpp +++ b/packages/react-native/ReactCommon/react/renderer/mounting/tests/StateReconciliationTest.cpp @@ -37,6 +37,9 @@ class DummyShadowTreeDelegate : public ShadowTreeDelegate { void shadowTreeDidFinishTransaction( std::shared_ptr mountingCoordinator, bool /*mountSynchronously*/) const override {} + + void shadowTreeDidFinishReactCommit( + const ShadowTree& /*shadowTree*/) const override {} }; namespace { diff --git a/packages/react-native/ReactCommon/react/renderer/scheduler/Scheduler.cpp b/packages/react-native/ReactCommon/react/renderer/scheduler/Scheduler.cpp index 8c42afc52964cd..dd4d2b4461d33f 100644 --- a/packages/react-native/ReactCommon/react/renderer/scheduler/Scheduler.cpp +++ b/packages/react-native/ReactCommon/react/renderer/scheduler/Scheduler.cpp @@ -355,6 +355,16 @@ void Scheduler::uiManagerShouldRemoveEventListener( removeEventListener(listener); } +void Scheduler::uiManagerDidFinishJSCommit(const ShadowTree& shadowTree) { + if (delegate_ != nullptr) { + SurfaceId surfaceId = shadowTree.getSurfaceId(); + runtimeScheduler_->scheduleRenderingUpdate( + shadowTree.getSurfaceId(), [delegate = delegate_, surfaceId]() { + delegate->schedulerShouldMergeReactRevision(surfaceId); + }); + } +} + void Scheduler::uiManagerDidStartSurface(const ShadowTree& shadowTree) { std::shared_lock lock(onSurfaceStartCallbackMutex_); if (onSurfaceStartCallback_) { diff --git a/packages/react-native/ReactCommon/react/renderer/scheduler/Scheduler.h b/packages/react-native/ReactCommon/react/renderer/scheduler/Scheduler.h index c324655da7255a..611faa69eca34e 100644 --- a/packages/react-native/ReactCommon/react/renderer/scheduler/Scheduler.h +++ b/packages/react-native/ReactCommon/react/renderer/scheduler/Scheduler.h @@ -97,6 +97,7 @@ class Scheduler final : public UIManagerDelegate { void uiManagerDidUpdateShadowTree(const std::unordered_map &tagToProps) override; void uiManagerShouldAddEventListener(std::shared_ptr listener) final; void uiManagerShouldRemoveEventListener(const std::shared_ptr &listener) final; + void uiManagerDidFinishJSCommit(const ShadowTree &shadowTree) override; void uiManagerDidStartSurface(const ShadowTree &shadowTree) override; #pragma mark - ContextContainer diff --git a/packages/react-native/ReactCommon/react/renderer/scheduler/SchedulerDelegate.h b/packages/react-native/ReactCommon/react/renderer/scheduler/SchedulerDelegate.h index afa2d57de6574d..fafb5f90f29704 100644 --- a/packages/react-native/ReactCommon/react/renderer/scheduler/SchedulerDelegate.h +++ b/packages/react-native/ReactCommon/react/renderer/scheduler/SchedulerDelegate.h @@ -38,6 +38,12 @@ class SchedulerDelegate { virtual void schedulerShouldRenderTransactions( const std::shared_ptr &mountingCoordinator) = 0; + /* + * Called at the end of the event loop if there were commits to the JS + * during the pass and JS branch should be "merged" to the main revision. + */ + virtual void schedulerShouldMergeReactRevision(SurfaceId surfaceId) = 0; + /* * Called right after a new ShadowNode was created. */ diff --git a/packages/react-native/ReactCommon/react/renderer/uimanager/UIManager.cpp b/packages/react-native/ReactCommon/react/renderer/uimanager/UIManager.cpp index 931bfce1c1e10a..f7b2f46fd8646d 100644 --- a/packages/react-native/ReactCommon/react/renderer/uimanager/UIManager.cpp +++ b/packages/react-native/ReactCommon/react/renderer/uimanager/UIManager.cpp @@ -188,8 +188,10 @@ void UIManager::completeSurface( ShadowTree::CommitOptions commitOptions) { TraceSection s("UIManager::completeSurface", "surfaceId", surfaceId); + ShadowTree::CommitStatus result; + shadowTreeRegistry_.visit(surfaceId, [&](const ShadowTree& shadowTree) { - auto result = shadowTree.commit( + result = shadowTree.commit( [&](const RootShadowNode& oldRootShadowNode) { return std::make_shared( oldRootShadowNode, @@ -199,20 +201,19 @@ void UIManager::completeSurface( }); }, commitOptions); + }); - if (result == ShadowTree::CommitStatus::Succeeded) { - // It's safe to update the visible revision of the shadow tree immediately - // after we commit a specific one. - lazyShadowTreeRevisionConsistencyManager_->updateCurrentRevision( - surfaceId, shadowTree.getCurrentRevision().rootShadowNode); + if (result == ShadowTree::CommitStatus::Succeeded) { + // It's safe to update the visible revision of the shadow tree immediately + // after we commit a specific one. + lazyShadowTreeRevisionConsistencyManager_->updateCurrentRevision(surfaceId); - if (ReactNativeFeatureFlags::useSharedAnimatedBackend()) { - if (auto animationBackend = animationBackend_.lock()) { - animationBackend->clearRegistry(surfaceId); - } + if (ReactNativeFeatureFlags::useSharedAnimatedBackend()) { + if (auto animationBackend = animationBackend_.lock()) { + animationBackend->clearRegistry(surfaceId); } } - }); + } } void UIManager::setIsJSResponder( @@ -645,6 +646,13 @@ void UIManager::shadowTreeDidFinishTransaction( } } +void UIManager::shadowTreeDidFinishReactCommit( + const ShadowTree& shadowTree) const { + if (delegate_ != nullptr) { + delegate_->uiManagerDidFinishJSCommit(shadowTree); + } +} + void UIManager::reportMount(SurfaceId surfaceId) const { TraceSection s("UIManager::reportMount"); diff --git a/packages/react-native/ReactCommon/react/renderer/uimanager/UIManager.h b/packages/react-native/ReactCommon/react/renderer/uimanager/UIManager.h index 794a5559a553fe..e61ed6c6c2b718 100644 --- a/packages/react-native/ReactCommon/react/renderer/uimanager/UIManager.h +++ b/packages/react-native/ReactCommon/react/renderer/uimanager/UIManager.h @@ -135,6 +135,8 @@ class UIManager final : public ShadowTreeDelegate { const RootShadowNode::Unshared &newRootShadowNode, const ShadowTree::CommitOptions &commitOptions) const override; + void shadowTreeDidFinishReactCommit(const ShadowTree &shadowTree) const override; + std::shared_ptr createNode( Tag tag, const std::string &componentName, diff --git a/packages/react-native/ReactCommon/react/renderer/uimanager/UIManagerDelegate.h b/packages/react-native/ReactCommon/react/renderer/uimanager/UIManagerDelegate.h index 32df5c92f10992..13d71d9d2859c5 100644 --- a/packages/react-native/ReactCommon/react/renderer/uimanager/UIManagerDelegate.h +++ b/packages/react-native/ReactCommon/react/renderer/uimanager/UIManagerDelegate.h @@ -84,6 +84,11 @@ class UIManagerDelegate { */ virtual void uiManagerDidStartSurface(const ShadowTree &shadowTree) = 0; + /* + * Called after a new JS revision of the shadow tree is committed. + */ + virtual void uiManagerDidFinishJSCommit(const ShadowTree &shadowTree) = 0; + using OnSurfaceStartCallback = std::function; virtual void uiManagerShouldSetOnSurfaceStartCallback(OnSurfaceStartCallback &&callback) = 0; diff --git a/packages/react-native/ReactCommon/react/renderer/uimanager/consistency/LazyShadowTreeRevisionConsistencyManager.cpp b/packages/react-native/ReactCommon/react/renderer/uimanager/consistency/LazyShadowTreeRevisionConsistencyManager.cpp index 85cd9cd5158a23..cf11c7f9d28e64 100644 --- a/packages/react-native/ReactCommon/react/renderer/uimanager/consistency/LazyShadowTreeRevisionConsistencyManager.cpp +++ b/packages/react-native/ReactCommon/react/renderer/uimanager/consistency/LazyShadowTreeRevisionConsistencyManager.cpp @@ -15,22 +15,34 @@ LazyShadowTreeRevisionConsistencyManager:: ShadowTreeRegistry& shadowTreeRegistry) : shadowTreeRegistry_(shadowTreeRegistry) {} -void LazyShadowTreeRevisionConsistencyManager::updateCurrentRevision( - SurfaceId surfaceId, - RootShadowNode::Shared rootShadowNode) { +std::shared_ptr +LazyShadowTreeRevisionConsistencyManager::updateCurrentRevision( + SurfaceId surfaceId) { + // This method is only going to be called from JS, so we don't need to protect + // the access to the shadow tree registry as well. + // If this was multi-threaded, we would need to protect it to avoid capturing + // root shadow nodes concurrently. + RootShadowNode::Shared rootShadowNode; + shadowTreeRegistry_.visit(surfaceId, [&](const ShadowTree& shadowTree) { + auto reactRevision = shadowTree.getCurrentReactRevision(); + rootShadowNode = + reactRevision.value_or(shadowTree.getCurrentRevision()).rootShadowNode; + }); + std::unique_lock lock(capturedRootShadowNodesForConsistencyMutex_); // We don't need to store the revision if we haven't locked. // We can resolve lazily when requested. if (lockCount > 0) { - capturedRootShadowNodesForConsistency_[surfaceId] = - std::move(rootShadowNode); + capturedRootShadowNodesForConsistency_[surfaceId] = rootShadowNode; } + + return rootShadowNode; } #pragma mark - ShadowTreeRevisionProvider -RootShadowNode::Shared +std::shared_ptr LazyShadowTreeRevisionConsistencyManager::getCurrentRevision( SurfaceId surfaceId) { { @@ -43,23 +55,7 @@ LazyShadowTreeRevisionConsistencyManager::getCurrentRevision( } } - // This method is only going to be called from JS, so we don't need to protect - // the access to the shadow tree registry as well. - // If this was multi-threaded, we would need to protect it to avoid capturing - // root shadow nodes concurrently. - RootShadowNode::Shared rootShadowNode; - shadowTreeRegistry_.visit(surfaceId, [&](const ShadowTree& shadowTree) { - rootShadowNode = shadowTree.getCurrentRevision().rootShadowNode; - }); - - { - std::unique_lock lock(capturedRootShadowNodesForConsistencyMutex_); - if (lockCount > 0) { - capturedRootShadowNodesForConsistency_[surfaceId] = rootShadowNode; - } - } - - return rootShadowNode; + return updateCurrentRevision(surfaceId); } #pragma mark - ConsistentShadowTreeRevisionProvider diff --git a/packages/react-native/ReactCommon/react/renderer/uimanager/consistency/LazyShadowTreeRevisionConsistencyManager.h b/packages/react-native/ReactCommon/react/renderer/uimanager/consistency/LazyShadowTreeRevisionConsistencyManager.h index 96c7537ed28d2c..c7ab5af7fac2ad 100644 --- a/packages/react-native/ReactCommon/react/renderer/uimanager/consistency/LazyShadowTreeRevisionConsistencyManager.h +++ b/packages/react-native/ReactCommon/react/renderer/uimanager/consistency/LazyShadowTreeRevisionConsistencyManager.h @@ -13,7 +13,6 @@ #include #include #include -#include namespace facebook::react { @@ -28,11 +27,11 @@ class LazyShadowTreeRevisionConsistencyManager : public ShadowTreeRevisionConsis public: explicit LazyShadowTreeRevisionConsistencyManager(ShadowTreeRegistry &shadowTreeRegistry); - void updateCurrentRevision(SurfaceId surfaceId, RootShadowNode::Shared rootShadowNode); + std::shared_ptr updateCurrentRevision(SurfaceId surfaceId); #pragma mark - ShadowTreeRevisionProvider - RootShadowNode::Shared getCurrentRevision(SurfaceId surfaceId) override; + std::shared_ptr getCurrentRevision(SurfaceId surfaceId) override; #pragma mark - ShadowTreeRevisionConsistencyManager diff --git a/packages/react-native/ReactCommon/react/renderer/uimanager/consistency/tests/LazyShadowTreeRevisionConsistencyManagerTest.cpp b/packages/react-native/ReactCommon/react/renderer/uimanager/consistency/tests/LazyShadowTreeRevisionConsistencyManagerTest.cpp index 01d43d096c55c8..d17ce99c0c17c1 100644 --- a/packages/react-native/ReactCommon/react/renderer/uimanager/consistency/tests/LazyShadowTreeRevisionConsistencyManagerTest.cpp +++ b/packages/react-native/ReactCommon/react/renderer/uimanager/consistency/tests/LazyShadowTreeRevisionConsistencyManagerTest.cpp @@ -27,6 +27,9 @@ class FakeShadowTreeDelegate : public ShadowTreeDelegate { void shadowTreeDidFinishTransaction( std::shared_ptr mountingCoordinator, bool /*mountSynchronously*/) const override {} + + void shadowTreeDidFinishReactCommit( + const ShadowTree& /*shadowTree*/) const override {} }; class LazyShadowTreeRevisionConsistencyManagerTest : public ::testing::Test { @@ -141,7 +144,7 @@ TEST_F( EXPECT_EQ(consistencyManager_.getCurrentRevision(0), nullptr); - consistencyManager_.updateCurrentRevision(0, newRootShadowNode); + consistencyManager_.updateCurrentRevision(0); EXPECT_NE(consistencyManager_.getCurrentRevision(0), nullptr); EXPECT_EQ( @@ -176,7 +179,7 @@ TEST_F( EXPECT_EQ(consistencyManager_.getCurrentRevision(0), nullptr); - consistencyManager_.updateCurrentRevision(0, newRootShadowNode); + consistencyManager_.updateCurrentRevision(0); EXPECT_EQ( consistencyManager_.getCurrentRevision(0).get(), newRootShadowNode.get()); @@ -192,7 +195,7 @@ TEST_F( {}); }); - consistencyManager_.updateCurrentRevision(0, newRootShadowNode2); + consistencyManager_.updateCurrentRevision(0); EXPECT_EQ( consistencyManager_.getCurrentRevision(0).get(), @@ -265,7 +268,7 @@ TEST_F( EXPECT_EQ( consistencyManager_.getCurrentRevision(0).get(), newRootShadowNode.get()); - consistencyManager_.updateCurrentRevision(0, newRootShadowNode2); + consistencyManager_.updateCurrentRevision(0); // Updated EXPECT_EQ( @@ -344,7 +347,9 @@ TEST_F(LazyShadowTreeRevisionConsistencyManagerTest, testUpdateToUnmounted) { EXPECT_EQ( consistencyManager_.getCurrentRevision(0).get(), newRootShadowNode.get()); - consistencyManager_.updateCurrentRevision(0, nullptr); + shadowTreeRegistry_.remove(0); + + consistencyManager_.updateCurrentRevision(0); // Updated EXPECT_EQ(consistencyManager_.getCurrentRevision(0).get(), nullptr); diff --git a/packages/react-native/ReactCxxPlatform/react/renderer/scheduler/SchedulerDelegateImpl.cpp b/packages/react-native/ReactCxxPlatform/react/renderer/scheduler/SchedulerDelegateImpl.cpp index ad9ca80551be87..dcc1df6a5cdfef 100644 --- a/packages/react-native/ReactCxxPlatform/react/renderer/scheduler/SchedulerDelegateImpl.cpp +++ b/packages/react-native/ReactCxxPlatform/react/renderer/scheduler/SchedulerDelegateImpl.cpp @@ -6,6 +6,7 @@ */ #include "SchedulerDelegateImpl.h" +#include namespace facebook::react { @@ -13,6 +14,11 @@ SchedulerDelegateImpl::SchedulerDelegateImpl( std::shared_ptr mountingManager) noexcept : mountingManager_(std::move(mountingManager)) {} +void SchedulerDelegateImpl::setUIManager( + std::shared_ptr uiManager) noexcept { + uiManager_ = uiManager; +} + void SchedulerDelegateImpl::schedulerDidFinishTransaction( const std::shared_ptr& /*mountingCoordinator*/) { // no-op, we will flush the transaction from schedulerShouldRenderTransactions @@ -30,6 +36,13 @@ void SchedulerDelegateImpl::schedulerShouldRenderTransactions( } } +void SchedulerDelegateImpl::schedulerShouldMergeReactRevision( + SurfaceId surfaceId) { + uiManager_->getShadowTreeRegistry().visit( + surfaceId, + [](const ShadowTree& shadowTree) { shadowTree.mergeReactRevision(); }); +} + void SchedulerDelegateImpl::schedulerDidRequestPreliminaryViewAllocation( const ShadowNode& shadowNode) {} diff --git a/packages/react-native/ReactCxxPlatform/react/renderer/scheduler/SchedulerDelegateImpl.h b/packages/react-native/ReactCxxPlatform/react/renderer/scheduler/SchedulerDelegateImpl.h index 2167e5fe143121..b60f33962ce95d 100644 --- a/packages/react-native/ReactCxxPlatform/react/renderer/scheduler/SchedulerDelegateImpl.h +++ b/packages/react-native/ReactCxxPlatform/react/renderer/scheduler/SchedulerDelegateImpl.h @@ -25,12 +25,16 @@ class SchedulerDelegateImpl : public SchedulerDelegate { SchedulerDelegateImpl(const SchedulerDelegateImpl &) = delete; SchedulerDelegateImpl &operator=(const SchedulerDelegateImpl &) = delete; + void setUIManager(std::shared_ptr uiManager) noexcept; + private: void schedulerDidFinishTransaction(const std::shared_ptr &mountingCoordinator) override; void schedulerShouldRenderTransactions( const std::shared_ptr &mountingCoordinator) override; + void schedulerShouldMergeReactRevision(SurfaceId surfaceId) override; + void schedulerDidRequestPreliminaryViewAllocation(const ShadowNode &shadowNode) override; void schedulerDidDispatchCommand( @@ -48,6 +52,7 @@ class SchedulerDelegateImpl : public SchedulerDelegate { void schedulerDidUpdateShadowTree(const std::unordered_map &tagToProps) override; std::shared_ptr mountingManager_; + std::shared_ptr uiManager_; }; }; // namespace facebook::react diff --git a/packages/react-native/ReactCxxPlatform/react/runtime/ReactHost.cpp b/packages/react-native/ReactCxxPlatform/react/runtime/ReactHost.cpp index ca1f38300f3824..ac1fe7b81407f3 100644 --- a/packages/react-native/ReactCxxPlatform/react/runtime/ReactHost.cpp +++ b/packages/react-native/ReactCxxPlatform/react/runtime/ReactHost.cpp @@ -224,11 +224,13 @@ void ReactHost::createReactInstance() { ownerBox, *runtimeScheduler); }; - schedulerDelegate_ = std::make_unique( + auto schedulerDelegate = std::make_unique( reactInstanceData_->mountingManager); scheduler_ = - std::make_unique(toolbox, nullptr, schedulerDelegate_.get()); + std::make_unique(toolbox, nullptr, schedulerDelegate.get()); surfaceManager_ = std::make_unique(*scheduler_); + schedulerDelegate->setUIManager(scheduler_->getUIManager()); + schedulerDelegate_ = std::move(schedulerDelegate); reactInstanceData_->mountingManager->setSchedulerTaskExecutor( [this](SchedulerTask&& task) { runOnScheduler(std::move(task)); }); @@ -305,6 +307,9 @@ void ReactHost::destroyReactInstance() { reactInstanceData_->contextContainer->erase(RuntimeSchedulerKey); reactInstanceData_->mountingManager->setSchedulerTaskExecutor(nullptr); + reactInstanceData_->mountingManager = nullptr; + reactInstanceData_->contextContainer = nullptr; + reactInstanceData_->turboModuleProviders.clear(); reactInstance_ = nullptr; reactInstanceData_->messageQueueThread = nullptr; } diff --git a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js index d2aff9a2ae0125..59f4533e30cae5 100644 --- a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js +++ b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js @@ -297,6 +297,16 @@ const definitions: FeatureFlagDefinitions = { }, ossReleaseStage: 'none', }, + enableFabricCommitBranching: { + defaultValue: false, + metadata: { + description: + 'Enables Fabric commit branching to fix starvation problems and atomic JS updates.', + expectedReleaseValue: true, + purpose: 'release', + }, + ossReleaseStage: 'none', + }, enableFabricLogs: { defaultValue: false, metadata: { diff --git a/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js b/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js index 7e396451dda291..9aa22b8014ee46 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<<6b37c02e334fb00f22ce27bb787104cc>> + * @generated SignedSource<> * @flow strict * @noformat */ @@ -74,6 +74,7 @@ export type ReactNativeFeatureFlags = $ReadOnly<{ enableEagerMainQueueModulesOnIOS: Getter, enableEagerRootViewAttachment: Getter, enableExclusivePropsUpdateAndroid: Getter, + enableFabricCommitBranching: Getter, enableFabricLogs: Getter, enableFabricRenderer: Getter, enableFontScaleChangesUpdatingLayout: Getter, @@ -319,6 +320,10 @@ export const enableEagerRootViewAttachment: Getter = createNativeFlagGe * When enabled, Android will disable Props 1.5 raw value merging when Props 2.0 is available. */ export const enableExclusivePropsUpdateAndroid: Getter = createNativeFlagGetter('enableExclusivePropsUpdateAndroid', false); +/** + * Enables Fabric commit branching to fix starvation problems and atomic JS updates. + */ +export const enableFabricCommitBranching: Getter = createNativeFlagGetter('enableFabricCommitBranching', false); /** * This feature flag enables logs for Fabric. */ diff --git a/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js b/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js index 688654d961f0fb..d907e28c5068d7 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<> + * @generated SignedSource<<66267ab4b5d8ebcc0021814822672f78>> * @flow strict * @noformat */ @@ -48,6 +48,7 @@ export interface Spec extends TurboModule { +enableEagerMainQueueModulesOnIOS?: () => boolean; +enableEagerRootViewAttachment?: () => boolean; +enableExclusivePropsUpdateAndroid?: () => boolean; + +enableFabricCommitBranching?: () => boolean; +enableFabricLogs?: () => boolean; +enableFabricRenderer?: () => boolean; +enableFontScaleChangesUpdatingLayout?: () => boolean;