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<<c526fb1c44f00f5b032684396246e4d4>> + * @generated SignedSource<<f3d26e8e345068bc7ef4e156be460e09>> */ /** @@ -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<<dfbe9bcab657e4c66dd104788639448d>> + * @generated SignedSource<<c3f02dff409c10aa7922141cef3a6990>> */ /** @@ -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<<cc8e437bf2f486949f256a19d3d73a1e>> + * @generated SignedSource<<d12888990ebca7c6199f4b51802ee59b>> */ /** @@ -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<<c06b3b34cea24459f6ade0ec5665dae7>> */ /** @@ -25,6 +25,9 @@ public class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcces private val accessedFeatureFlags = mutableSetOf<String>() 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<<c5388e841a06044520f6c89fcca27286>> + * @generated SignedSource<<dceb20e62a9ddbb98c872a24fab9765c>> */ /** @@ -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<FabricMountingManager> 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<std::mutex> 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<std::mutex> 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 <memory> +#include <mutex> #include <shared_mutex> #include <unordered_map> @@ -101,6 +102,9 @@ class Binding : public jni::HybridClass<Binding, JBinding>, 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<Binding, JBinding>, std::shared_mutex surfaceHandlerRegistryMutex_; // Protects `surfaceHandlerRegistry_`. + // Track pending transactions, one per surfaceId + std::mutex pendingTransactionsMutex_; + std::vector<MountingTransaction> pendingTransactions_; + float pointScaleFactor_ = 1; std::shared_ptr<const ReactNativeConfig> 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<<f40d52242f2c758ed616f1e5d8c3a082>> + * @generated SignedSource<<ec76fca802fcc6f2c2357de21f482cb3>> */ /** @@ -45,6 +45,24 @@ class ReactNativeFeatureFlagsProviderHolder return method(javaProvider_); } + bool androidEnablePendingFabricTransactions() override { + static const auto method = + getReactNativeFeatureFlagsProviderJavaClass()->getMethod<jboolean()>("androidEnablePendingFabricTransactions"); + return method(javaProvider_); + } + + bool batchRenderingUpdatesInEventLoop() override { + static const auto method = + getReactNativeFeatureFlagsProviderJavaClass()->getMethod<jboolean()>("batchRenderingUpdatesInEventLoop"); + return method(javaProvider_); + } + + bool destroyFabricSurfacesInReactInstanceManager() override { + static const auto method = + getReactNativeFeatureFlagsProviderJavaClass()->getMethod<jboolean()>("destroyFabricSurfacesInReactInstanceManager"); + return method(javaProvider_); + } + bool enableBackgroundExecutor() override { static const auto method = getReactNativeFeatureFlagsProviderJavaClass()->getMethod<jboolean()>("enableBackgroundExecutor"); @@ -108,6 +126,21 @@ bool JReactNativeFeatureFlagsCxxInterop::commonTestFlag( return ReactNativeFeatureFlags::commonTestFlag(); } +bool JReactNativeFeatureFlagsCxxInterop::androidEnablePendingFabricTransactions( + facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop> /*unused*/) { + return ReactNativeFeatureFlags::androidEnablePendingFabricTransactions(); +} + +bool JReactNativeFeatureFlagsCxxInterop::batchRenderingUpdatesInEventLoop( + facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop> /*unused*/) { + return ReactNativeFeatureFlags::batchRenderingUpdatesInEventLoop(); +} + +bool JReactNativeFeatureFlagsCxxInterop::destroyFabricSurfacesInReactInstanceManager( + facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop> /*unused*/) { + return ReactNativeFeatureFlags::destroyFabricSurfacesInReactInstanceManager(); +} + bool JReactNativeFeatureFlagsCxxInterop::enableBackgroundExecutor( facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop> /*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<<a860f8ca5806886a2e7b0b9a508482a1>> + * @generated SignedSource<<3a6ff4e2f6d4056d903542cc620e07a9>> */ /** @@ -33,6 +33,15 @@ class JReactNativeFeatureFlagsCxxInterop static bool commonTestFlag( facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>); + static bool androidEnablePendingFabricTransactions( + facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>); + + static bool batchRenderingUpdatesInEventLoop( + facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>); + + static bool destroyFabricSurfacesInReactInstanceManager( + facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>); + static bool enableBackgroundExecutor( facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>); 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<<b402923520c287a015cd57bd75af47cf>> */ /** @@ -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<ReactNativeFeatureFlagsProvider> 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<ReactNativeFeatureFlagsProvider> currentProvider_; bool wasOverridden_; - std::array<std::atomic<const char*>, 10> accessedFeatureFlags_; + std::array<std::atomic<const char*>, 18> accessedFeatureFlags_; std::atomic<std::optional<bool>> commonTestFlag_; + std::atomic<std::optional<bool>> androidEnablePendingFabricTransactions_; + std::atomic<std::optional<bool>> batchRenderingUpdatesInEventLoop_; + std::atomic<std::optional<bool>> destroyFabricSurfacesInReactInstanceManager_; std::atomic<std::optional<bool>> enableBackgroundExecutor_; std::atomic<std::optional<bool>> useModernRuntimeScheduler_; std::atomic<std::optional<bool>> 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<<a1530f969f47f31b8588109e48f527a1>> */ /** @@ -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<<f707d15cf978d7342cdf5aab18444219>> + * @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<std::weak_ptr<RuntimeScheduler>>( "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<<b7f42eb3b24f3feed4274df8398c3c94>> * @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<ReactNativeFeatureF export type ReactNativeFeatureFlags = { ...ReactNativeFeatureFlagsJsOnly, commonTestFlag: Getter<boolean>, + androidEnablePendingFabricTransactions: Getter<boolean>, + batchRenderingUpdatesInEventLoop: Getter<boolean>, + destroyFabricSurfacesInReactInstanceManager: Getter<boolean>, enableBackgroundExecutor: Getter<boolean>, useModernRuntimeScheduler: Getter<boolean>, enableMicrotasks: Getter<boolean>, @@ -96,6 +99,18 @@ export const shouldUseRemoveClippedSubviewsAsDefaultOnIOS: Getter<boolean> = cre * Common flag for testing. Do NOT modify. */ export const commonTestFlag: Getter<boolean> = 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<boolean> = 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<boolean> = createNativeFlagGetter('batchRenderingUpdatesInEventLoop', false); +/** + * When enabled, ReactInstanceManager will clean up Fabric surfaces on destroy(). + */ +export const destroyFabricSurfacesInReactInstanceManager: Getter<boolean> = 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). */