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 888770cfd9f..f80e83c7a4c 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 3e774560998..91e1225c67a 100644 --- a/packages/react-native/React/Fabric/RCTScheduler.mm +++ b/packages/react-native/React/Fabric/RCTScheduler.mm @@ -30,6 +30,12 @@ class SchedulerDelegateProxy : public SchedulerDelegate { [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 4c1e048c406..80ff067dc4a 100644 --- a/packages/react-native/React/Fabric/RCTSurfacePresenter.mm +++ b/packages/react-native/React/Fabric/RCTSurfacePresenter.mm @@ -354,6 +354,11 @@ static BackgroundExecutor RCTGetBackgroundExecutor() #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 487f43d409c..242557e325f 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 3e38e9f251c..d7c768c7f30 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 d3518e6cba0..07dd950fe85 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 e75ba65f44d..20a6fb3300c 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 2fcc689a55e..dca198e5274 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 2d4561cc3e6..3211bb55c2f 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 b19acb9108a..c3a0c8c226d 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 b310f3bd296..dec5855b096 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 15061428a2a..5a5ffe1ce97 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 ff7881e09e4..1f430fc7104 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 caec5678dd7..e64540e127f 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 8f82ea19201..248cac686bf 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 d46d461bfd4..3a9977ead0a 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,8 @@ 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. @@ -74,7 +128,25 @@ 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(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. + // 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(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 b177bfe53b6..f27cd809a5b 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 ce2aadae3b6..3b5eb1a4bab 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 f6f55b5fc15..35bb7d42c9c 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 67f799f556c..344ec4a97c5 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 203dfce52c0..bbfcd70a665 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 f858acafc6d..d7dd1bcd916 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 3d90076ce8b..277e9f4e307 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 ee4ed0a1f40..a70abde5cf7 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 d103bf850ec..338d637da94 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 ef91f0f6e35..b79879e130e 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 93243673eba..04000941c9d 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 214dc80c030..c224753733c 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). */