From ff4b20e823d2838b3f72e550e7a89daa08551a72 Mon Sep 17 00:00:00 2001 From: Pieter De Baets Date: Wed, 24 Apr 2024 03:54:10 -0700 Subject: [PATCH] Split scheduler commit and flush delegate methods (#44188) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/44188 The current approach used for `batchRenderingUpdatesInEventLoop` is not compatible with Android due to limitations in its props processing model. The raw props changeset is passed through to Android, and must be available for the Android mounting layer to correctly apply changes. We have some logic to merge these payloads when multiple ShadowNode clones take place but were previously assuming that a ShadowTree commit was a safe state to synchronize. In the current implementation this means that two commits driven from layout effects (triggering states A → B → C) may cause Android to observe only the B → C props change, and miss out on any props changed in A → B. Changelog: [Android][Fixed] Cascading renders were not mounting correctly when `batchRenderingUpdatesInEventLoop` is enabled. Reviewed By: rubennorte Differential Revision: D56414689 fbshipit-source-id: 7c74d81620db0f8b7bd67e640168afc795c7a1d7 --- .../react-native/React/Fabric/RCTScheduler.h | 2 + .../react-native/React/Fabric/RCTScheduler.mm | 6 + .../React/Fabric/RCTSurfacePresenter.mm | 5 + .../featureflags/ReactNativeFeatureFlags.kt | 20 ++- .../ReactNativeFeatureFlagsCxxAccessor.kt | 54 +++++- .../ReactNativeFeatureFlagsCxxInterop.kt | 8 +- .../ReactNativeFeatureFlagsDefaults.kt | 8 +- .../ReactNativeFeatureFlagsLocalAccessor.kt | 59 +++++- .../ReactNativeFeatureFlagsProvider.kt | 8 +- .../src/main/jni/react/fabric/Binding.cpp | 41 ++++- .../src/main/jni/react/fabric/Binding.h | 8 + .../JReactNativeFeatureFlagsCxxInterop.cpp | 44 ++++- .../JReactNativeFeatureFlagsCxxInterop.h | 11 +- .../featureflags/ReactNativeFeatureFlags.cpp | 14 +- .../featureflags/ReactNativeFeatureFlags.h | 19 +- .../ReactNativeFeatureFlagsAccessor.cpp | 168 ++++++++++++++++-- .../ReactNativeFeatureFlagsAccessor.h | 10 +- .../ReactNativeFeatureFlagsDefaults.h | 14 +- .../ReactNativeFeatureFlagsProvider.h | 5 +- .../NativeReactNativeFeatureFlags.cpp | 17 +- .../NativeReactNativeFeatureFlags.h | 8 +- .../renderer/mounting/MountingTransaction.cpp | 13 ++ .../renderer/mounting/MountingTransaction.h | 9 + .../react/renderer/scheduler/Scheduler.cpp | 9 +- .../renderer/scheduler/SchedulerDelegate.h | 11 ++ .../ReactNativeFeatureFlags.config.js | 15 ++ .../NativeReactNativeFeatureFlags.js | 5 +- .../featureflags/ReactNativeFeatureFlags.js | 17 +- 28 files changed, 572 insertions(+), 36 deletions(-) diff --git a/packages/react-native/React/Fabric/RCTScheduler.h b/packages/react-native/React/Fabric/RCTScheduler.h index 888770cfd9fea0..f80e83c7a4cbad 100644 --- a/packages/react-native/React/Fabric/RCTScheduler.h +++ b/packages/react-native/React/Fabric/RCTScheduler.h @@ -30,6 +30,8 @@ NS_ASSUME_NONNULL_BEGIN - (void)schedulerDidFinishTransaction:(facebook::react::MountingCoordinator::Shared)mountingCoordinator; +- (void)schedulerShouldRenderTransactions:(facebook::react::MountingCoordinator::Shared)mountingCoordinator; + - (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 3e774560998eb1..91e1225c67a84b 100644 --- a/packages/react-native/React/Fabric/RCTScheduler.mm +++ b/packages/react-native/React/Fabric/RCTScheduler.mm @@ -30,6 +30,12 @@ void schedulerDidFinishTransaction(const MountingCoordinator::Shared &mountingCo [scheduler.delegate schedulerDidFinishTransaction:mountingCoordinator]; } + void schedulerShouldRenderTransactions(const MountingCoordinator::Shared &mountingCoordinator) override + { + RCTScheduler *scheduler = (__bridge RCTScheduler *)scheduler_; + [scheduler.delegate schedulerShouldRenderTransactions:mountingCoordinator]; + } + void schedulerDidRequestPreliminaryViewAllocation(SurfaceId surfaceId, const ShadowNode &shadowNode) override { // Does nothing. diff --git a/packages/react-native/React/Fabric/RCTSurfacePresenter.mm b/packages/react-native/React/Fabric/RCTSurfacePresenter.mm index 4c1e048c4063f2..80ff067dc4ab37 100644 --- a/packages/react-native/React/Fabric/RCTSurfacePresenter.mm +++ b/packages/react-native/React/Fabric/RCTSurfacePresenter.mm @@ -354,6 +354,11 @@ - (void)_applicationWillTerminate #pragma mark - RCTSchedulerDelegate - (void)schedulerDidFinishTransaction:(MountingCoordinator::Shared)mountingCoordinator +{ + // no-op, we will flush the transaction from schedulerShouldRenderTransactions +} + +- (void)schedulerShouldRenderTransactions:(MountingCoordinator::Shared)mountingCoordinator { [_mountingManager scheduleTransaction:mountingCoordinator]; } 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 487f43d409c9a4..242557e325fab5 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<> */ /** @@ -34,6 +34,24 @@ public object ReactNativeFeatureFlags { @JvmStatic public fun commonTestFlag(): Boolean = accessor.commonTestFlag() + /** + * To be used with batchRenderingUpdatesInEventLoop. When enbled, the Android mounting layer will concatenate pending transactions to ensure they're applied atomatically + */ + @JvmStatic + public fun androidEnablePendingFabricTransactions(): Boolean = accessor.androidEnablePendingFabricTransactions() + + /** + * When enabled, the RuntimeScheduler processing the event loop will batch all rendering updates and dispatch them together at the end of each iteration of the loop. + */ + @JvmStatic + public fun batchRenderingUpdatesInEventLoop(): Boolean = accessor.batchRenderingUpdatesInEventLoop() + + /** + * When enabled, ReactInstanceManager will clean up Fabric surfaces on destroy(). + */ + @JvmStatic + public fun destroyFabricSurfacesInReactInstanceManager(): Boolean = accessor.destroyFabricSurfacesInReactInstanceManager() + /** * Enables the use of a background executor to compute layout and commit updates on Fabric (this system is deprecated and should not be used). */ 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 3e38e9f251cbd8..d7c768c7f301a0 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<<11824621ee7ca5dbdf2f09bdf1a1f983>> + * @generated SignedSource<<40668dcd951123da7c0b4ddde23f94c9>> */ /** @@ -21,6 +21,9 @@ package com.facebook.react.internal.featureflags public class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAccessor { private var commonTestFlagCache: Boolean? = null + private var androidEnablePendingFabricTransactionsCache: Boolean? = null + private var batchRenderingUpdatesInEventLoopCache: Boolean? = null + private var destroyFabricSurfacesInReactInstanceManagerCache: Boolean? = null private var enableBackgroundExecutorCache: Boolean? = null private var useModernRuntimeSchedulerCache: Boolean? = null private var enableMicrotasksCache: Boolean? = null @@ -40,6 +43,55 @@ public class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAccesso return cached } +<<<<<<< HEAD +||||||| parent of 849da2146ca (Split scheduler commit and flush delegate methods (#44188)) + override fun batchRenderingUpdatesInEventLoop(): Boolean { + var cached = batchRenderingUpdatesInEventLoopCache + if (cached == null) { + cached = ReactNativeFeatureFlagsCxxInterop.batchRenderingUpdatesInEventLoop() + batchRenderingUpdatesInEventLoopCache = cached + } + return cached + } + + override fun destroyFabricSurfacesInReactInstanceManager(): Boolean { + var cached = destroyFabricSurfacesInReactInstanceManagerCache + if (cached == null) { + cached = ReactNativeFeatureFlagsCxxInterop.destroyFabricSurfacesInReactInstanceManager() + destroyFabricSurfacesInReactInstanceManagerCache = cached + } + return cached + } + +======= + override fun androidEnablePendingFabricTransactions(): Boolean { + var cached = androidEnablePendingFabricTransactionsCache + if (cached == null) { + cached = ReactNativeFeatureFlagsCxxInterop.androidEnablePendingFabricTransactions() + androidEnablePendingFabricTransactionsCache = cached + } + return cached + } + + override fun batchRenderingUpdatesInEventLoop(): Boolean { + var cached = batchRenderingUpdatesInEventLoopCache + if (cached == null) { + cached = ReactNativeFeatureFlagsCxxInterop.batchRenderingUpdatesInEventLoop() + batchRenderingUpdatesInEventLoopCache = cached + } + return cached + } + + override fun destroyFabricSurfacesInReactInstanceManager(): Boolean { + var cached = destroyFabricSurfacesInReactInstanceManagerCache + if (cached == null) { + cached = ReactNativeFeatureFlagsCxxInterop.destroyFabricSurfacesInReactInstanceManager() + destroyFabricSurfacesInReactInstanceManagerCache = cached + } + return cached + } + +>>>>>>> 849da2146ca (Split scheduler commit and flush delegate methods (#44188)) override fun enableBackgroundExecutor(): Boolean { var cached = enableBackgroundExecutorCache 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 d3518e6cba082d..07dd950fe8540c 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<> */ /** @@ -30,6 +30,12 @@ public object ReactNativeFeatureFlagsCxxInterop { @DoNotStrip @JvmStatic public external fun commonTestFlag(): Boolean + @DoNotStrip @JvmStatic public external fun androidEnablePendingFabricTransactions(): Boolean + + @DoNotStrip @JvmStatic public external fun batchRenderingUpdatesInEventLoop(): Boolean + + @DoNotStrip @JvmStatic public external fun destroyFabricSurfacesInReactInstanceManager(): Boolean + @DoNotStrip @JvmStatic public external fun enableBackgroundExecutor(): Boolean @DoNotStrip @JvmStatic public external fun useModernRuntimeScheduler(): 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 e75ba65f44d08b..20a6fb3300c92a 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<> */ /** @@ -25,6 +25,12 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi override fun commonTestFlag(): Boolean = false + override fun androidEnablePendingFabricTransactions(): Boolean = false + + override fun batchRenderingUpdatesInEventLoop(): Boolean = false + + override fun destroyFabricSurfacesInReactInstanceManager(): Boolean = false + override fun enableBackgroundExecutor(): Boolean = false override fun useModernRuntimeScheduler(): 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 2fcc689a55e16c..dca198e5274d93 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<<63356ad414e641eae11ca07b1a876fd3>> + * @generated SignedSource<> */ /** @@ -25,6 +25,9 @@ public class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcces private val accessedFeatureFlags = mutableSetOf() private var commonTestFlagCache: Boolean? = null + private var androidEnablePendingFabricTransactionsCache: Boolean? = null + private var batchRenderingUpdatesInEventLoopCache: Boolean? = null + private var destroyFabricSurfacesInReactInstanceManagerCache: Boolean? = null private var enableBackgroundExecutorCache: Boolean? = null private var useModernRuntimeSchedulerCache: Boolean? = null private var enableMicrotasksCache: Boolean? = null @@ -45,6 +48,60 @@ public class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcces return cached } +<<<<<<< HEAD +||||||| parent of 849da2146ca (Split scheduler commit and flush delegate methods (#44188)) + override fun batchRenderingUpdatesInEventLoop(): Boolean { + var cached = batchRenderingUpdatesInEventLoopCache + if (cached == null) { + cached = currentProvider.batchRenderingUpdatesInEventLoop() + accessedFeatureFlags.add("batchRenderingUpdatesInEventLoop") + batchRenderingUpdatesInEventLoopCache = cached + } + return cached + } + + override fun destroyFabricSurfacesInReactInstanceManager(): Boolean { + var cached = destroyFabricSurfacesInReactInstanceManagerCache + if (cached == null) { + cached = currentProvider.destroyFabricSurfacesInReactInstanceManager() + accessedFeatureFlags.add("destroyFabricSurfacesInReactInstanceManager") + destroyFabricSurfacesInReactInstanceManagerCache = cached + } + return cached + } + +======= + override fun androidEnablePendingFabricTransactions(): Boolean { + var cached = androidEnablePendingFabricTransactionsCache + if (cached == null) { + cached = currentProvider.androidEnablePendingFabricTransactions() + accessedFeatureFlags.add("androidEnablePendingFabricTransactions") + androidEnablePendingFabricTransactionsCache = cached + } + return cached + } + + override fun batchRenderingUpdatesInEventLoop(): Boolean { + var cached = batchRenderingUpdatesInEventLoopCache + if (cached == null) { + cached = currentProvider.batchRenderingUpdatesInEventLoop() + accessedFeatureFlags.add("batchRenderingUpdatesInEventLoop") + batchRenderingUpdatesInEventLoopCache = cached + } + return cached + } + + override fun destroyFabricSurfacesInReactInstanceManager(): Boolean { + var cached = destroyFabricSurfacesInReactInstanceManagerCache + if (cached == null) { + cached = currentProvider.destroyFabricSurfacesInReactInstanceManager() + accessedFeatureFlags.add("destroyFabricSurfacesInReactInstanceManager") + destroyFabricSurfacesInReactInstanceManagerCache = cached + } + return cached + } + +>>>>>>> 849da2146ca (Split scheduler commit and flush delegate methods (#44188)) override fun enableBackgroundExecutor(): Boolean { var cached = enableBackgroundExecutorCache 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 2d4561cc3e6420..3211bb55c2f360 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<> */ /** @@ -25,6 +25,12 @@ import com.facebook.proguard.annotations.DoNotStrip public interface ReactNativeFeatureFlagsProvider { @DoNotStrip public fun commonTestFlag(): Boolean + @DoNotStrip public fun androidEnablePendingFabricTransactions(): Boolean + + @DoNotStrip public fun batchRenderingUpdatesInEventLoop(): Boolean + + @DoNotStrip public fun destroyFabricSurfacesInReactInstanceManager(): Boolean + @DoNotStrip public fun enableBackgroundExecutor(): Boolean @DoNotStrip public fun useModernRuntimeScheduler(): Boolean diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/fabric/Binding.cpp b/packages/react-native/ReactAndroid/src/main/jni/react/fabric/Binding.cpp index b19acb9108ace0..c3a0c8c226ded1 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/fabric/Binding.cpp +++ b/packages/react-native/ReactAndroid/src/main/jni/react/fabric/Binding.cpp @@ -474,8 +474,7 @@ std::shared_ptr Binding::getMountingManager( void Binding::schedulerDidFinishTransaction( const MountingCoordinator::Shared& mountingCoordinator) { - auto mountingManager = getMountingManager("schedulerDidFinishTransaction"); - if (!mountingManager) { + if (!ReactNativeFeatureFlags::androidEnablePendingFabricTransactions()) { return; } @@ -483,7 +482,43 @@ void Binding::schedulerDidFinishTransaction( if (!mountingTransaction.has_value()) { return; } - mountingManager->executeMount(*mountingTransaction); + + std::unique_lock lock(pendingTransactionsMutex_); + auto pendingTransaction = std::find_if( + pendingTransactions_.begin(), + pendingTransactions_.end(), + [&](const auto& transaction) { + return transaction.getSurfaceId() == + mountingTransaction->getSurfaceId(); + }); + + if (pendingTransaction != pendingTransactions_.end()) { + pendingTransaction->mergeWith(std::move(*mountingTransaction)); + } else { + pendingTransactions_.push_back(std::move(*mountingTransaction)); + } +} + +void Binding::schedulerShouldRenderTransactions( + const MountingCoordinator::Shared& mountingCoordinator) { + auto mountingManager = + getMountingManager("schedulerShouldRenderTransactions"); + if (!mountingManager) { + return; + } + + if (ReactNativeFeatureFlags::androidEnablePendingFabricTransactions()) { + std::unique_lock lock(pendingTransactionsMutex_); + for (auto& transaction : pendingTransactions_) { + mountingManager->executeMount(transaction); + } + pendingTransactions_.clear(); + } else { + auto mountingTransaction = mountingCoordinator->pullTransaction(); + if (mountingTransaction.has_value()) { + mountingManager->executeMount(*mountingTransaction); + } + } } void Binding::schedulerDidRequestPreliminaryViewAllocation( diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/fabric/Binding.h b/packages/react-native/ReactAndroid/src/main/jni/react/fabric/Binding.h index b310f3bd2962be..dec5855b096fda 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/fabric/Binding.h +++ b/packages/react-native/ReactAndroid/src/main/jni/react/fabric/Binding.h @@ -8,6 +8,7 @@ #pragma once #include +#include #include #include @@ -101,6 +102,9 @@ class Binding : public jni::HybridClass, void schedulerDidFinishTransaction( const MountingCoordinator::Shared& mountingCoordinator) override; + void schedulerShouldRenderTransactions( + const MountingCoordinator::Shared& mountingCoordinator) override; + void schedulerDidRequestPreliminaryViewAllocation( const SurfaceId surfaceId, const ShadowNode& shadowNode) override; @@ -146,6 +150,10 @@ class Binding : public jni::HybridClass, std::shared_mutex surfaceHandlerRegistryMutex_; // Protects `surfaceHandlerRegistry_`. + // Track pending transactions, one per surfaceId + std::mutex pendingTransactionsMutex_; + std::vector pendingTransactions_; + float pointScaleFactor_ = 1; std::shared_ptr reactNativeConfig_{nullptr}; 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 15061428a2a2df..5a5ffe1ce975df 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<> */ /** @@ -45,6 +45,24 @@ class ReactNativeFeatureFlagsProviderHolder return method(javaProvider_); } + bool androidEnablePendingFabricTransactions() override { + static const auto method = + getReactNativeFeatureFlagsProviderJavaClass()->getMethod("androidEnablePendingFabricTransactions"); + return method(javaProvider_); + } + + bool batchRenderingUpdatesInEventLoop() override { + static const auto method = + getReactNativeFeatureFlagsProviderJavaClass()->getMethod("batchRenderingUpdatesInEventLoop"); + return method(javaProvider_); + } + + bool destroyFabricSurfacesInReactInstanceManager() override { + static const auto method = + getReactNativeFeatureFlagsProviderJavaClass()->getMethod("destroyFabricSurfacesInReactInstanceManager"); + return method(javaProvider_); + } + bool enableBackgroundExecutor() override { static const auto method = getReactNativeFeatureFlagsProviderJavaClass()->getMethod("enableBackgroundExecutor"); @@ -108,6 +126,21 @@ bool JReactNativeFeatureFlagsCxxInterop::commonTestFlag( return ReactNativeFeatureFlags::commonTestFlag(); } +bool JReactNativeFeatureFlagsCxxInterop::androidEnablePendingFabricTransactions( + facebook::jni::alias_ref /*unused*/) { + return ReactNativeFeatureFlags::androidEnablePendingFabricTransactions(); +} + +bool JReactNativeFeatureFlagsCxxInterop::batchRenderingUpdatesInEventLoop( + facebook::jni::alias_ref /*unused*/) { + return ReactNativeFeatureFlags::batchRenderingUpdatesInEventLoop(); +} + +bool JReactNativeFeatureFlagsCxxInterop::destroyFabricSurfacesInReactInstanceManager( + facebook::jni::alias_ref /*unused*/) { + return ReactNativeFeatureFlags::destroyFabricSurfacesInReactInstanceManager(); +} + bool JReactNativeFeatureFlagsCxxInterop::enableBackgroundExecutor( facebook::jni::alias_ref /*unused*/) { return ReactNativeFeatureFlags::enableBackgroundExecutor(); @@ -173,6 +206,15 @@ void JReactNativeFeatureFlagsCxxInterop::registerNatives() { makeNativeMethod( "commonTestFlag", JReactNativeFeatureFlagsCxxInterop::commonTestFlag), + makeNativeMethod( + "androidEnablePendingFabricTransactions", + JReactNativeFeatureFlagsCxxInterop::androidEnablePendingFabricTransactions), + makeNativeMethod( + "batchRenderingUpdatesInEventLoop", + JReactNativeFeatureFlagsCxxInterop::batchRenderingUpdatesInEventLoop), + makeNativeMethod( + "destroyFabricSurfacesInReactInstanceManager", + JReactNativeFeatureFlagsCxxInterop::destroyFabricSurfacesInReactInstanceManager), makeNativeMethod( "enableBackgroundExecutor", JReactNativeFeatureFlagsCxxInterop::enableBackgroundExecutor), 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 ff7881e09e417c..1f430fc71041a6 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<<3a6ff4e2f6d4056d903542cc620e07a9>> */ /** @@ -33,6 +33,15 @@ class JReactNativeFeatureFlagsCxxInterop static bool commonTestFlag( facebook::jni::alias_ref); + static bool androidEnablePendingFabricTransactions( + facebook::jni::alias_ref); + + static bool batchRenderingUpdatesInEventLoop( + facebook::jni::alias_ref); + + static bool destroyFabricSurfacesInReactInstanceManager( + facebook::jni::alias_ref); + static bool enableBackgroundExecutor( 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 caec5678dd7b3c..e64540e127f733 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<<82f226df2b3824d03b755a042b20bec5>> + * @generated SignedSource<<6305ea7c2cb59caeaf2ea9cea69b8203>> */ /** @@ -25,6 +25,18 @@ bool ReactNativeFeatureFlags::commonTestFlag() { return getAccessor().commonTestFlag(); } +bool ReactNativeFeatureFlags::androidEnablePendingFabricTransactions() { + return getAccessor().androidEnablePendingFabricTransactions(); +} + +bool ReactNativeFeatureFlags::batchRenderingUpdatesInEventLoop() { + return getAccessor().batchRenderingUpdatesInEventLoop(); +} + +bool ReactNativeFeatureFlags::destroyFabricSurfacesInReactInstanceManager() { + return getAccessor().destroyFabricSurfacesInReactInstanceManager(); +} + bool ReactNativeFeatureFlags::enableBackgroundExecutor() { return getAccessor().enableBackgroundExecutor(); } diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h index 8f82ea192014c9..248cac686bfe90 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<<3bfae310dfd28040f4b80d1a1df8b7b3>> + * @generated SignedSource<<5a5c6772253f49b0b768cd7ef090af14>> */ /** @@ -36,7 +36,22 @@ class ReactNativeFeatureFlags { /** * Common flag for testing. Do NOT modify. */ - static bool commonTestFlag(); + RN_EXPORT static bool commonTestFlag(); + + /** + * To be used with batchRenderingUpdatesInEventLoop. When enbled, the Android mounting layer will concatenate pending transactions to ensure they're applied atomatically + */ + RN_EXPORT static bool androidEnablePendingFabricTransactions(); + + /** + * When enabled, the RuntimeScheduler processing the event loop will batch all rendering updates and dispatch them together at the end of each iteration of the loop. + */ + RN_EXPORT static bool batchRenderingUpdatesInEventLoop(); + + /** + * When enabled, ReactInstanceManager will clean up Fabric surfaces on destroy(). + */ + RN_EXPORT static bool destroyFabricSurfacesInReactInstanceManager(); /** * Enables the use of a background executor to compute layout and commit updates on Fabric (this system is deprecated and should not be used). diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp index d46d461bfd4261..3a9977ead0a7d0 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<<2154292f89306f25289583537833a065>> + * @generated SignedSource<> */ /** @@ -47,6 +47,60 @@ bool ReactNativeFeatureFlagsAccessor::commonTestFlag() { return flagValue.value(); } +bool ReactNativeFeatureFlagsAccessor::androidEnablePendingFabricTransactions() { + auto flagValue = androidEnablePendingFabricTransactions_.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(1, "androidEnablePendingFabricTransactions"); + + flagValue = currentProvider_->androidEnablePendingFabricTransactions(); + androidEnablePendingFabricTransactions_ = flagValue; + } + + return flagValue.value(); +} + +bool ReactNativeFeatureFlagsAccessor::batchRenderingUpdatesInEventLoop() { + auto flagValue = batchRenderingUpdatesInEventLoop_.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(2, "batchRenderingUpdatesInEventLoop"); + + flagValue = currentProvider_->batchRenderingUpdatesInEventLoop(); + batchRenderingUpdatesInEventLoop_ = flagValue; + } + + return flagValue.value(); +} + +bool ReactNativeFeatureFlagsAccessor::destroyFabricSurfacesInReactInstanceManager() { + auto flagValue = destroyFabricSurfacesInReactInstanceManager_.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(3, "destroyFabricSurfacesInReactInstanceManager"); + + flagValue = currentProvider_->destroyFabricSurfacesInReactInstanceManager(); + destroyFabricSurfacesInReactInstanceManager_ = flagValue; + } + + return flagValue.value(); +} + bool ReactNativeFeatureFlagsAccessor::enableBackgroundExecutor() { auto flagValue = enableBackgroundExecutor_.load(); @@ -56,7 +110,7 @@ bool ReactNativeFeatureFlagsAccessor::enableBackgroundExecutor() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(1, "enableBackgroundExecutor"); + markFlagAsAccessed(4, "enableBackgroundExecutor"); flagValue = currentProvider_->enableBackgroundExecutor(); enableBackgroundExecutor_ = flagValue; @@ -65,8 +119,26 @@ bool ReactNativeFeatureFlagsAccessor::enableBackgroundExecutor() { return flagValue.value(); } -bool ReactNativeFeatureFlagsAccessor::useModernRuntimeScheduler() { - auto flagValue = useModernRuntimeScheduler_.load(); +bool ReactNativeFeatureFlagsAccessor::enableCleanTextInputYogaNode() { + auto flagValue = enableCleanTextInputYogaNode_.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(5, "enableCleanTextInputYogaNode"); + + flagValue = currentProvider_->enableCleanTextInputYogaNode(); + enableCleanTextInputYogaNode_ = flagValue; + } + + return flagValue.value(); +} + +bool ReactNativeFeatureFlagsAccessor::enableCustomDrawOrderFabric() { + auto flagValue = enableCustomDrawOrderFabric_.load(); if (!flagValue.has_value()) { // This block is not exclusive but it is not necessary. @@ -74,7 +146,7 @@ bool ReactNativeFeatureFlagsAccessor::useModernRuntimeScheduler() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(2, "useModernRuntimeScheduler"); + markFlagAsAccessed(6, "enableCustomDrawOrderFabric"); flagValue = currentProvider_->useModernRuntimeScheduler(); useModernRuntimeScheduler_ = flagValue; @@ -92,7 +164,7 @@ bool ReactNativeFeatureFlagsAccessor::enableMicrotasks() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(3, "enableMicrotasks"); + markFlagAsAccessed(7, "enableMicrotasks"); flagValue = currentProvider_->enableMicrotasks(); enableMicrotasks_ = flagValue; @@ -110,7 +182,7 @@ bool ReactNativeFeatureFlagsAccessor::batchRenderingUpdatesInEventLoop() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(4, "batchRenderingUpdatesInEventLoop"); + markFlagAsAccessed(8, "enableSpannableBuildingUnification"); flagValue = currentProvider_->batchRenderingUpdatesInEventLoop(); batchRenderingUpdatesInEventLoop_ = flagValue; @@ -128,7 +200,7 @@ bool ReactNativeFeatureFlagsAccessor::enableSpannableBuildingUnification() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(5, "enableSpannableBuildingUnification"); + markFlagAsAccessed(9, "enableSynchronousStateUpdates"); flagValue = currentProvider_->enableSpannableBuildingUnification(); enableSpannableBuildingUnification_ = flagValue; @@ -146,7 +218,7 @@ bool ReactNativeFeatureFlagsAccessor::enableCustomDrawOrderFabric() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(6, "enableCustomDrawOrderFabric"); + markFlagAsAccessed(10, "enableUIConsistency"); flagValue = currentProvider_->enableCustomDrawOrderFabric(); enableCustomDrawOrderFabric_ = flagValue; @@ -164,7 +236,7 @@ bool ReactNativeFeatureFlagsAccessor::enableFixForClippedSubviewsCrash() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(7, "enableFixForClippedSubviewsCrash"); + markFlagAsAccessed(11, "forceBatchingMountItemsOnAndroid"); flagValue = currentProvider_->enableFixForClippedSubviewsCrash(); enableFixForClippedSubviewsCrash_ = flagValue; @@ -182,7 +254,7 @@ bool ReactNativeFeatureFlagsAccessor::inspectorEnableCxxInspectorPackagerConnect // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(8, "inspectorEnableCxxInspectorPackagerConnection"); + markFlagAsAccessed(12, "inspectorEnableCxxInspectorPackagerConnection"); flagValue = currentProvider_->inspectorEnableCxxInspectorPackagerConnection(); inspectorEnableCxxInspectorPackagerConnection_ = flagValue; @@ -200,7 +272,7 @@ bool ReactNativeFeatureFlagsAccessor::inspectorEnableModernCDPRegistry() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(9, "inspectorEnableModernCDPRegistry"); + markFlagAsAccessed(13, "inspectorEnableModernCDPRegistry"); flagValue = currentProvider_->inspectorEnableModernCDPRegistry(); inspectorEnableModernCDPRegistry_ = flagValue; @@ -209,6 +281,78 @@ bool ReactNativeFeatureFlagsAccessor::inspectorEnableModernCDPRegistry() { return flagValue.value(); } +bool ReactNativeFeatureFlagsAccessor::preventDoubleTextMeasure() { + auto flagValue = preventDoubleTextMeasure_.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(14, "preventDoubleTextMeasure"); + + flagValue = currentProvider_->preventDoubleTextMeasure(); + preventDoubleTextMeasure_ = flagValue; + } + + return flagValue.value(); +} + +bool ReactNativeFeatureFlagsAccessor::useModernRuntimeScheduler() { + auto flagValue = useModernRuntimeScheduler_.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(15, "useModernRuntimeScheduler"); + + flagValue = currentProvider_->useModernRuntimeScheduler(); + useModernRuntimeScheduler_ = flagValue; + } + + return flagValue.value(); +} + +bool ReactNativeFeatureFlagsAccessor::useNativeViewConfigsInBridgelessMode() { + auto flagValue = useNativeViewConfigsInBridgelessMode_.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(16, "useNativeViewConfigsInBridgelessMode"); + + flagValue = currentProvider_->useNativeViewConfigsInBridgelessMode(); + useNativeViewConfigsInBridgelessMode_ = flagValue; + } + + return flagValue.value(); +} + +bool ReactNativeFeatureFlagsAccessor::useStateAlignmentMechanism() { + auto flagValue = useStateAlignmentMechanism_.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(17, "useStateAlignmentMechanism"); + + flagValue = currentProvider_->useStateAlignmentMechanism(); + useStateAlignmentMechanism_ = flagValue; + } + + return flagValue.value(); +} + void ReactNativeFeatureFlagsAccessor::override( std::unique_ptr provider) { if (wasOverridden_) { diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h index b177bfe53b6b33..f27cd809a5bf7e 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<<95487968b66d40e1ec53936b06084931>> + * @generated SignedSource<<9be1a64b5efca2421dfb093dbcc1f793>> */ /** @@ -32,6 +32,9 @@ class ReactNativeFeatureFlagsAccessor { ReactNativeFeatureFlagsAccessor(); bool commonTestFlag(); + bool androidEnablePendingFabricTransactions(); + bool batchRenderingUpdatesInEventLoop(); + bool destroyFabricSurfacesInReactInstanceManager(); bool enableBackgroundExecutor(); bool useModernRuntimeScheduler(); bool enableMicrotasks(); @@ -51,9 +54,12 @@ class ReactNativeFeatureFlagsAccessor { std::unique_ptr currentProvider_; bool wasOverridden_; - std::array, 10> accessedFeatureFlags_; + std::array, 18> accessedFeatureFlags_; std::atomic> commonTestFlag_; + std::atomic> androidEnablePendingFabricTransactions_; + std::atomic> batchRenderingUpdatesInEventLoop_; + std::atomic> destroyFabricSurfacesInReactInstanceManager_; std::atomic> enableBackgroundExecutor_; std::atomic> useModernRuntimeScheduler_; std::atomic> enableMicrotasks_; diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h index ce2aadae3b63ce..3b5eb1a4bab114 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<<4832483bb3648380f2bb9312311f579c>> + * @generated SignedSource<> */ /** @@ -31,6 +31,18 @@ class ReactNativeFeatureFlagsDefaults : public ReactNativeFeatureFlagsProvider { return false; } + bool androidEnablePendingFabricTransactions() override { + return false; + } + + bool batchRenderingUpdatesInEventLoop() override { + return false; + } + + bool destroyFabricSurfacesInReactInstanceManager() override { + return false; + } + bool enableBackgroundExecutor() override { return false; } diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h index f6f55b5fc15e4a..35bb7d42c9cffe 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<<3f99a52c114d6f96edb2b2b2f549b6a6>> */ /** @@ -26,6 +26,9 @@ class ReactNativeFeatureFlagsProvider { virtual ~ReactNativeFeatureFlagsProvider() = default; virtual bool commonTestFlag() = 0; + virtual bool androidEnablePendingFabricTransactions() = 0; + virtual bool batchRenderingUpdatesInEventLoop() = 0; + virtual bool destroyFabricSurfacesInReactInstanceManager() = 0; virtual bool enableBackgroundExecutor() = 0; virtual bool useModernRuntimeScheduler() = 0; virtual bool enableMicrotasks() = 0; diff --git a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp index 67f799f556c1ee..344ec4a97c59c5 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<<2466af777a1d69da2c3810e5a2d53a70>> + * @generated SignedSource<<5285ee6c09bf4614cb5098952aac8c87>> */ /** @@ -40,6 +40,21 @@ bool NativeReactNativeFeatureFlags::commonTestFlag( return ReactNativeFeatureFlags::commonTestFlag(); } +bool NativeReactNativeFeatureFlags::androidEnablePendingFabricTransactions( + jsi::Runtime& /*runtime*/) { + return ReactNativeFeatureFlags::androidEnablePendingFabricTransactions(); +} + +bool NativeReactNativeFeatureFlags::batchRenderingUpdatesInEventLoop( + jsi::Runtime& /*runtime*/) { + return ReactNativeFeatureFlags::batchRenderingUpdatesInEventLoop(); +} + +bool NativeReactNativeFeatureFlags::destroyFabricSurfacesInReactInstanceManager( + jsi::Runtime& /*runtime*/) { + return ReactNativeFeatureFlags::destroyFabricSurfacesInReactInstanceManager(); +} + bool NativeReactNativeFeatureFlags::enableBackgroundExecutor( jsi::Runtime& /*runtime*/) { return ReactNativeFeatureFlags::enableBackgroundExecutor(); diff --git a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h index 203dfce52c07e1..bbfcd70a665b0c 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<<30481dc57cc7f389d1ed87d097caedda>> + * @generated SignedSource<<86afdf79a8a49da18eddf53bc9d41051>> */ /** @@ -32,6 +32,12 @@ class NativeReactNativeFeatureFlags bool commonTestFlag(jsi::Runtime& runtime); + bool androidEnablePendingFabricTransactions(jsi::Runtime& runtime); + + bool batchRenderingUpdatesInEventLoop(jsi::Runtime& runtime); + + bool destroyFabricSurfacesInReactInstanceManager(jsi::Runtime& runtime); + bool enableBackgroundExecutor(jsi::Runtime& runtime); bool useModernRuntimeScheduler(jsi::Runtime& runtime); diff --git a/packages/react-native/ReactCommon/react/renderer/mounting/MountingTransaction.cpp b/packages/react-native/ReactCommon/react/renderer/mounting/MountingTransaction.cpp index f858acafc6da70..d7dd1bcd916404 100644 --- a/packages/react-native/ReactCommon/react/renderer/mounting/MountingTransaction.cpp +++ b/packages/react-native/ReactCommon/react/renderer/mounting/MountingTransaction.cpp @@ -41,4 +41,17 @@ Number MountingTransaction::getNumber() const { return number_; } +void MountingTransaction::mergeWith(MountingTransaction&& transaction) { + react_native_assert(transaction.getSurfaceId() == surfaceId_); + number_ = transaction.getNumber(); + mutations_.insert( + mutations_.end(), + std::make_move_iterator(transaction.mutations_.begin()), + std::make_move_iterator(transaction.mutations_.end())); + + // TODO T186641819: Telemetry for merged transactions is not supported, use + // the latest instance + telemetry_ = std::move(transaction.telemetry_); +} + } // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/renderer/mounting/MountingTransaction.h b/packages/react-native/ReactCommon/react/renderer/mounting/MountingTransaction.h index 3d90076ce8b3a6..277e9f4e307c1e 100644 --- a/packages/react-native/ReactCommon/react/renderer/mounting/MountingTransaction.h +++ b/packages/react-native/ReactCommon/react/renderer/mounting/MountingTransaction.h @@ -76,6 +76,15 @@ class MountingTransaction final { */ Number getNumber() const; + /* + * Merges the given transaction in the current transaction, so they + * can be executed atomatically as a single transaction. + * + * This is required for Android UI, which needs to separately apply + * each ShadowTree mutation due to differences in props representation. + */ + void mergeWith(MountingTransaction&& transaction); + private: SurfaceId surfaceId_; Number number_; diff --git a/packages/react-native/ReactCommon/react/renderer/scheduler/Scheduler.cpp b/packages/react-native/ReactCommon/react/renderer/scheduler/Scheduler.cpp index ee4ed0a1f40e01..a70abde5cf72aa 100644 --- a/packages/react-native/ReactCommon/react/renderer/scheduler/Scheduler.cpp +++ b/packages/react-native/ReactCommon/react/renderer/scheduler/Scheduler.cpp @@ -273,6 +273,10 @@ void Scheduler::uiManagerDidFinishTransaction( SystraceSection s("Scheduler::uiManagerDidFinishTransaction"); if (delegate_ != nullptr) { + // This is no-op on all platforms except for Android where we need to + // observe each transaction to be able to mount correctly. + delegate_->schedulerDidFinishTransaction(mountingCoordinator); + auto weakRuntimeScheduler = contextContainer_->find>( "RuntimeScheduler"); @@ -283,13 +287,14 @@ void Scheduler::uiManagerDidFinishTransaction( runtimeScheduler->scheduleRenderingUpdate( [delegate = delegate_, mountingCoordinator = std::move(mountingCoordinator)]() { - delegate->schedulerDidFinishTransaction(mountingCoordinator); + delegate->schedulerShouldRenderTransactions(mountingCoordinator); }); } else { - delegate_->schedulerDidFinishTransaction(mountingCoordinator); + delegate_->schedulerShouldRenderTransactions(mountingCoordinator); } } } + void Scheduler::uiManagerDidCreateShadowNode(const ShadowNode& shadowNode) { SystraceSection s("Scheduler::uiManagerDidCreateShadowNode"); diff --git a/packages/react-native/ReactCommon/react/renderer/scheduler/SchedulerDelegate.h b/packages/react-native/ReactCommon/react/renderer/scheduler/SchedulerDelegate.h index d103bf850ec12f..338d637da94488 100644 --- a/packages/react-native/ReactCommon/react/renderer/scheduler/SchedulerDelegate.h +++ b/packages/react-native/ReactCommon/react/renderer/scheduler/SchedulerDelegate.h @@ -28,6 +28,17 @@ class SchedulerDelegate { virtual void schedulerDidFinishTransaction( const MountingCoordinator::Shared& mountingCoordinator) = 0; + /* + * Called when the runtime scheduler decides that one-or-more previously + * finished transactions should now be flushed to the screen (atomically). + * + * This is a separate callback from didFinishTransaction as the Android UI + * mounting layer needs to be able toobserve each created ShadowTree to + * correctly apply changes, due to changes in Props representation. + */ + virtual void schedulerShouldRenderTransactions( + const MountingCoordinator::Shared& mountingCoordinator) = 0; + /* * Called right after a new ShadowNode was created. */ diff --git a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js index ef91f0f6e352c5..b79879e130e77a 100644 --- a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js +++ b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js @@ -18,6 +18,21 @@ const definitions: FeatureFlagDefinitions = { defaultValue: false, }, + androidEnablePendingFabricTransactions: { + defaultValue: false, + description: + "To be used with batchRenderingUpdatesInEventLoop. When enbled, the Android mounting layer will concatenate pending transactions to ensure they're applied atomatically", + }, + batchRenderingUpdatesInEventLoop: { + defaultValue: false, + description: + 'When enabled, the RuntimeScheduler processing the event loop will batch all rendering updates and dispatch them together at the end of each iteration of the loop.', + }, + destroyFabricSurfacesInReactInstanceManager: { + defaultValue: false, + description: + 'When enabled, ReactInstanceManager will clean up Fabric surfaces on destroy().', + }, enableBackgroundExecutor: { description: 'Enables the use of a background executor to compute layout and commit updates on Fabric (this system is deprecated and should not be used).', diff --git a/packages/react-native/src/private/featureflags/NativeReactNativeFeatureFlags.js b/packages/react-native/src/private/featureflags/NativeReactNativeFeatureFlags.js index 93243673eba9d2..04000941c9d5df 100644 --- a/packages/react-native/src/private/featureflags/NativeReactNativeFeatureFlags.js +++ b/packages/react-native/src/private/featureflags/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<<8509d5ee87efb5aa8da7efcd2085d0a2>> + * @generated SignedSource<> * @flow strict-local */ @@ -24,6 +24,9 @@ import * as TurboModuleRegistry from '../../../Libraries/TurboModule/TurboModule export interface Spec extends TurboModule { +commonTestFlag?: () => boolean; + +androidEnablePendingFabricTransactions?: () => boolean; + +batchRenderingUpdatesInEventLoop?: () => boolean; + +destroyFabricSurfacesInReactInstanceManager?: () => boolean; +enableBackgroundExecutor?: () => boolean; +useModernRuntimeScheduler?: () => boolean; +enableMicrotasks?: () => boolean; diff --git a/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js b/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js index 214dc80c030845..c224753733c223 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<<7c83d5613c3be517efe48378e6356e79>> + * @generated SignedSource<<6b90f52915db22d4077011a55a519b20>> * @flow strict-local */ @@ -41,6 +41,9 @@ export type ReactNativeFeatureFlagsJsOnlyOverrides = Partial, + androidEnablePendingFabricTransactions: Getter, + batchRenderingUpdatesInEventLoop: Getter, + destroyFabricSurfacesInReactInstanceManager: Getter, enableBackgroundExecutor: Getter, useModernRuntimeScheduler: Getter, enableMicrotasks: Getter, @@ -96,6 +99,18 @@ export const shouldUseRemoveClippedSubviewsAsDefaultOnIOS: Getter = cre * Common flag for testing. Do NOT modify. */ export const commonTestFlag: Getter = createNativeFlagGetter('commonTestFlag', false); +/** + * To be used with batchRenderingUpdatesInEventLoop. When enbled, the Android mounting layer will concatenate pending transactions to ensure they're applied atomatically + */ +export const androidEnablePendingFabricTransactions: Getter = createNativeFlagGetter('androidEnablePendingFabricTransactions', false); +/** + * When enabled, the RuntimeScheduler processing the event loop will batch all rendering updates and dispatch them together at the end of each iteration of the loop. + */ +export const batchRenderingUpdatesInEventLoop: Getter = createNativeFlagGetter('batchRenderingUpdatesInEventLoop', false); +/** + * When enabled, ReactInstanceManager will clean up Fabric surfaces on destroy(). + */ +export const destroyFabricSurfacesInReactInstanceManager: Getter = createNativeFlagGetter('destroyFabricSurfacesInReactInstanceManager', false); /** * Enables the use of a background executor to compute layout and commit updates on Fabric (this system is deprecated and should not be used). */