Split scheduler commit and flush delegate methods (#44188)

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
This commit is contained in:
Pieter De Baets
2024-04-30 15:27:00 +01:00
committed by Riccardo Cipolleschi
parent b440672468
commit ff4b20e823
28 changed files with 572 additions and 36 deletions
@@ -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;
@@ -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.
@@ -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];
}
@@ -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).
*/
@@ -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) {
@@ -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
@@ -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
@@ -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) {
@@ -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
@@ -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(
@@ -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};
@@ -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),
@@ -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>);
@@ -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();
}
@@ -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).
@@ -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,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<ReactNativeFeatureFlagsProvider> provider) {
if (wasOverridden_) {
@@ -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_;
@@ -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;
}
@@ -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;
@@ -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();
@@ -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);
@@ -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
@@ -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_;
@@ -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");
@@ -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.
*/
@@ -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).',
@@ -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;
@@ -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).
*/