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 23e9b1b68f8..3b878a5511f 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<<697babc37818f427f02fd3ab4c53febd>> */ /** @@ -294,6 +294,12 @@ public object ReactNativeFeatureFlags { @JvmStatic public fun fuseboxNetworkInspectionEnabled(): Boolean = accessor.fuseboxNetworkInspectionEnabled() + /** + * Number cached PreparedLayouts in TextLayoutManager cache + */ + @JvmStatic + public fun preparedTextCacheSize(): Double = accessor.preparedTextCacheSize() + /** * Enables storing js caller stack when creating promise in native module. This is useful in case of Promise rejection and tracing the cause. */ 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 f8790cf6fb8..be12735e721 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<<0c6c54413dc51e692605811e89d567dc>> + * @generated SignedSource<<275296a2e051e0ae2a17d80836ae9ee1>> */ /** @@ -64,6 +64,7 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces private var fixMappingOfEventPrioritiesBetweenFabricAndReactCache: Boolean? = null private var fuseboxEnabledReleaseCache: Boolean? = null private var fuseboxNetworkInspectionEnabledCache: Boolean? = null + private var preparedTextCacheSizeCache: Double? = null private var traceTurboModulePromiseRejectionsOnAndroidCache: Boolean? = null private var updateRuntimeShadowNodeReferencesOnCommitCache: Boolean? = null private var useAlwaysAvailableJSErrorHandlingCache: Boolean? = null @@ -472,6 +473,15 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces return cached } + override fun preparedTextCacheSize(): Double { + var cached = preparedTextCacheSizeCache + if (cached == null) { + cached = ReactNativeFeatureFlagsCxxInterop.preparedTextCacheSize() + preparedTextCacheSizeCache = cached + } + return cached + } + override fun traceTurboModulePromiseRejectionsOnAndroid(): Boolean { var cached = traceTurboModulePromiseRejectionsOnAndroidCache 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 f327c64f018..776e82c485f 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<<4c47c4c6977880efee8b45bbcd4d999d>> + * @generated SignedSource<<25ee348b038042f2f45efdb526949f1b>> */ /** @@ -116,6 +116,8 @@ public object ReactNativeFeatureFlagsCxxInterop { @DoNotStrip @JvmStatic public external fun fuseboxNetworkInspectionEnabled(): Boolean + @DoNotStrip @JvmStatic public external fun preparedTextCacheSize(): Double + @DoNotStrip @JvmStatic public external fun traceTurboModulePromiseRejectionsOnAndroid(): Boolean @DoNotStrip @JvmStatic public external fun updateRuntimeShadowNodeReferencesOnCommit(): 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 160275f2d75..533c82cf5a5 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<<33af38489ce1077f24a639db52213953>> + * @generated SignedSource<> */ /** @@ -111,6 +111,8 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi override fun fuseboxNetworkInspectionEnabled(): Boolean = false + override fun preparedTextCacheSize(): Double = 200.0 + override fun traceTurboModulePromiseRejectionsOnAndroid(): Boolean = false override fun updateRuntimeShadowNodeReferencesOnCommit(): 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 88020903206..e9e5d239a80 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<<97d032014047ab05c6d9fc39665a4b3e>> + * @generated SignedSource<> */ /** @@ -68,6 +68,7 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc private var fixMappingOfEventPrioritiesBetweenFabricAndReactCache: Boolean? = null private var fuseboxEnabledReleaseCache: Boolean? = null private var fuseboxNetworkInspectionEnabledCache: Boolean? = null + private var preparedTextCacheSizeCache: Double? = null private var traceTurboModulePromiseRejectionsOnAndroidCache: Boolean? = null private var updateRuntimeShadowNodeReferencesOnCommitCache: Boolean? = null private var useAlwaysAvailableJSErrorHandlingCache: Boolean? = null @@ -520,6 +521,16 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc return cached } + override fun preparedTextCacheSize(): Double { + var cached = preparedTextCacheSizeCache + if (cached == null) { + cached = currentProvider.preparedTextCacheSize() + accessedFeatureFlags.add("preparedTextCacheSize") + preparedTextCacheSizeCache = cached + } + return cached + } + override fun traceTurboModulePromiseRejectionsOnAndroid(): Boolean { var cached = traceTurboModulePromiseRejectionsOnAndroidCache 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 a37ca47eb6e..21e83a1612c 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<<8a607ad40a3e991943c32bc120fcd66f>> + * @generated SignedSource<<10197755f82d5b7251563ce1517c8fac>> */ /** @@ -111,6 +111,8 @@ public interface ReactNativeFeatureFlagsProvider { @DoNotStrip public fun fuseboxNetworkInspectionEnabled(): Boolean + @DoNotStrip public fun preparedTextCacheSize(): Double + @DoNotStrip public fun traceTurboModulePromiseRejectionsOnAndroid(): Boolean @DoNotStrip public fun updateRuntimeShadowNodeReferencesOnCommit(): Boolean 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 2398650a289..638f87f03a2 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<<9a0a41bc27ee271578e74664951c5217>> + * @generated SignedSource<<53e68ed98b80c867e3045902e1618c9b>> */ /** @@ -303,6 +303,12 @@ class ReactNativeFeatureFlagsJavaProvider return method(javaProvider_); } + double preparedTextCacheSize() override { + static const auto method = + getReactNativeFeatureFlagsProviderJavaClass()->getMethod("preparedTextCacheSize"); + return method(javaProvider_); + } + bool traceTurboModulePromiseRejectionsOnAndroid() override { static const auto method = getReactNativeFeatureFlagsProviderJavaClass()->getMethod("traceTurboModulePromiseRejectionsOnAndroid"); @@ -593,6 +599,11 @@ bool JReactNativeFeatureFlagsCxxInterop::fuseboxNetworkInspectionEnabled( return ReactNativeFeatureFlags::fuseboxNetworkInspectionEnabled(); } +double JReactNativeFeatureFlagsCxxInterop::preparedTextCacheSize( + facebook::jni::alias_ref /*unused*/) { + return ReactNativeFeatureFlags::preparedTextCacheSize(); +} + bool JReactNativeFeatureFlagsCxxInterop::traceTurboModulePromiseRejectionsOnAndroid( facebook::jni::alias_ref /*unused*/) { return ReactNativeFeatureFlags::traceTurboModulePromiseRejectionsOnAndroid(); @@ -811,6 +822,9 @@ void JReactNativeFeatureFlagsCxxInterop::registerNatives() { makeNativeMethod( "fuseboxNetworkInspectionEnabled", JReactNativeFeatureFlagsCxxInterop::fuseboxNetworkInspectionEnabled), + makeNativeMethod( + "preparedTextCacheSize", + JReactNativeFeatureFlagsCxxInterop::preparedTextCacheSize), makeNativeMethod( "traceTurboModulePromiseRejectionsOnAndroid", JReactNativeFeatureFlagsCxxInterop::traceTurboModulePromiseRejectionsOnAndroid), 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 2c5cae300e7..629c42816fe 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<<1d3320eb050bd827c0109319afb92991>> + * @generated SignedSource<<70de16b94b14f0e7de235f72e1566abe>> */ /** @@ -162,6 +162,9 @@ class JReactNativeFeatureFlagsCxxInterop static bool fuseboxNetworkInspectionEnabled( facebook::jni::alias_ref); + static double preparedTextCacheSize( + facebook::jni::alias_ref); + static bool traceTurboModulePromiseRejectionsOnAndroid( 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 9142e4e943b..d2fae4a4d0b 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<> + * @generated SignedSource<<2206721fac3452fd4d9ca3053cc7bc32>> */ /** @@ -202,6 +202,10 @@ bool ReactNativeFeatureFlags::fuseboxNetworkInspectionEnabled() { return getAccessor().fuseboxNetworkInspectionEnabled(); } +double ReactNativeFeatureFlags::preparedTextCacheSize() { + return getAccessor().preparedTextCacheSize(); +} + bool ReactNativeFeatureFlags::traceTurboModulePromiseRejectionsOnAndroid() { return getAccessor().traceTurboModulePromiseRejectionsOnAndroid(); } diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h index 3cb66d455b9..7358e78e2e3 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<<61381e3eb65214c519a01283366a53c6>> + * @generated SignedSource<> */ /** @@ -259,6 +259,11 @@ class ReactNativeFeatureFlags { */ RN_EXPORT static bool fuseboxNetworkInspectionEnabled(); + /** + * Number cached PreparedLayouts in TextLayoutManager cache + */ + RN_EXPORT static double preparedTextCacheSize(); + /** * Enables storing js caller stack when creating promise in native module. This is useful in case of Promise rejection and tracing the cause. */ diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp index f1294fbea51..8e7b096dc16 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<<02ed1adf133035a71192f6af88d0214e>> + * @generated SignedSource<<00faab987d6e11584cfd01a88552b434>> */ /** @@ -821,6 +821,24 @@ bool ReactNativeFeatureFlagsAccessor::fuseboxNetworkInspectionEnabled() { return flagValue.value(); } +double ReactNativeFeatureFlagsAccessor::preparedTextCacheSize() { + auto flagValue = preparedTextCacheSize_.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(44, "preparedTextCacheSize"); + + flagValue = currentProvider_->preparedTextCacheSize(); + preparedTextCacheSize_ = flagValue; + } + + return flagValue.value(); +} + bool ReactNativeFeatureFlagsAccessor::traceTurboModulePromiseRejectionsOnAndroid() { auto flagValue = traceTurboModulePromiseRejectionsOnAndroid_.load(); @@ -830,7 +848,7 @@ bool ReactNativeFeatureFlagsAccessor::traceTurboModulePromiseRejectionsOnAndroid // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(44, "traceTurboModulePromiseRejectionsOnAndroid"); + markFlagAsAccessed(45, "traceTurboModulePromiseRejectionsOnAndroid"); flagValue = currentProvider_->traceTurboModulePromiseRejectionsOnAndroid(); traceTurboModulePromiseRejectionsOnAndroid_ = flagValue; @@ -848,7 +866,7 @@ bool ReactNativeFeatureFlagsAccessor::updateRuntimeShadowNodeReferencesOnCommit( // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(45, "updateRuntimeShadowNodeReferencesOnCommit"); + markFlagAsAccessed(46, "updateRuntimeShadowNodeReferencesOnCommit"); flagValue = currentProvider_->updateRuntimeShadowNodeReferencesOnCommit(); updateRuntimeShadowNodeReferencesOnCommit_ = flagValue; @@ -866,7 +884,7 @@ bool ReactNativeFeatureFlagsAccessor::useAlwaysAvailableJSErrorHandling() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(46, "useAlwaysAvailableJSErrorHandling"); + markFlagAsAccessed(47, "useAlwaysAvailableJSErrorHandling"); flagValue = currentProvider_->useAlwaysAvailableJSErrorHandling(); useAlwaysAvailableJSErrorHandling_ = flagValue; @@ -884,7 +902,7 @@ bool ReactNativeFeatureFlagsAccessor::useFabricInterop() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(47, "useFabricInterop"); + markFlagAsAccessed(48, "useFabricInterop"); flagValue = currentProvider_->useFabricInterop(); useFabricInterop_ = flagValue; @@ -902,7 +920,7 @@ bool ReactNativeFeatureFlagsAccessor::useNativeViewConfigsInBridgelessMode() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(48, "useNativeViewConfigsInBridgelessMode"); + markFlagAsAccessed(49, "useNativeViewConfigsInBridgelessMode"); flagValue = currentProvider_->useNativeViewConfigsInBridgelessMode(); useNativeViewConfigsInBridgelessMode_ = flagValue; @@ -920,7 +938,7 @@ bool ReactNativeFeatureFlagsAccessor::useOptimizedEventBatchingOnAndroid() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(49, "useOptimizedEventBatchingOnAndroid"); + markFlagAsAccessed(50, "useOptimizedEventBatchingOnAndroid"); flagValue = currentProvider_->useOptimizedEventBatchingOnAndroid(); useOptimizedEventBatchingOnAndroid_ = flagValue; @@ -938,7 +956,7 @@ bool ReactNativeFeatureFlagsAccessor::useRawPropsJsiValue() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(50, "useRawPropsJsiValue"); + markFlagAsAccessed(51, "useRawPropsJsiValue"); flagValue = currentProvider_->useRawPropsJsiValue(); useRawPropsJsiValue_ = flagValue; @@ -956,7 +974,7 @@ bool ReactNativeFeatureFlagsAccessor::useShadowNodeStateOnClone() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(51, "useShadowNodeStateOnClone"); + markFlagAsAccessed(52, "useShadowNodeStateOnClone"); flagValue = currentProvider_->useShadowNodeStateOnClone(); useShadowNodeStateOnClone_ = flagValue; @@ -974,7 +992,7 @@ bool ReactNativeFeatureFlagsAccessor::useTurboModuleInterop() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(52, "useTurboModuleInterop"); + markFlagAsAccessed(53, "useTurboModuleInterop"); flagValue = currentProvider_->useTurboModuleInterop(); useTurboModuleInterop_ = flagValue; @@ -992,7 +1010,7 @@ bool ReactNativeFeatureFlagsAccessor::useTurboModules() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(53, "useTurboModules"); + markFlagAsAccessed(54, "useTurboModules"); flagValue = currentProvider_->useTurboModules(); useTurboModules_ = flagValue; @@ -1010,7 +1028,7 @@ double ReactNativeFeatureFlagsAccessor::virtualViewPrerenderRatio() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(54, "virtualViewPrerenderRatio"); + markFlagAsAccessed(55, "virtualViewPrerenderRatio"); flagValue = currentProvider_->virtualViewPrerenderRatio(); virtualViewPrerenderRatio_ = flagValue; diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h index 67e93a44e5c..9796b7b0fde 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<> + * @generated SignedSource<<8f9118c510588cd6a5f0402870b53eaf>> */ /** @@ -76,6 +76,7 @@ class ReactNativeFeatureFlagsAccessor { bool fixMappingOfEventPrioritiesBetweenFabricAndReact(); bool fuseboxEnabledRelease(); bool fuseboxNetworkInspectionEnabled(); + double preparedTextCacheSize(); bool traceTurboModulePromiseRejectionsOnAndroid(); bool updateRuntimeShadowNodeReferencesOnCommit(); bool useAlwaysAvailableJSErrorHandling(); @@ -98,7 +99,7 @@ class ReactNativeFeatureFlagsAccessor { std::unique_ptr currentProvider_; bool wasOverridden_; - std::array, 55> accessedFeatureFlags_; + std::array, 56> accessedFeatureFlags_; std::atomic> commonTestFlag_; std::atomic> animatedShouldSignalBatch_; @@ -144,6 +145,7 @@ class ReactNativeFeatureFlagsAccessor { std::atomic> fixMappingOfEventPrioritiesBetweenFabricAndReact_; std::atomic> fuseboxEnabledRelease_; std::atomic> fuseboxNetworkInspectionEnabled_; + std::atomic> preparedTextCacheSize_; std::atomic> traceTurboModulePromiseRejectionsOnAndroid_; std::atomic> updateRuntimeShadowNodeReferencesOnCommit_; std::atomic> useAlwaysAvailableJSErrorHandling_; diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h index 8118de5cbab..4f180703c75 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<<24eb62c6e8538db6b5aa4f2394c21132>> + * @generated SignedSource<<04ec71486d4052ac5ec1c6362bfd143c>> */ /** @@ -203,6 +203,10 @@ class ReactNativeFeatureFlagsDefaults : public ReactNativeFeatureFlagsProvider { return false; } + double preparedTextCacheSize() override { + return 200.0; + } + bool traceTurboModulePromiseRejectionsOnAndroid() override { return false; } diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h index 855c6b3e535..f1438d1ebcc 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<66d1d0ee39bc491fc95958b45e4e8797>> + * @generated SignedSource<<78e883d9ea6e9e44a035216b8aa7ae98>> */ /** @@ -441,6 +441,15 @@ class ReactNativeFeatureFlagsDynamicProvider : public ReactNativeFeatureFlagsDef return ReactNativeFeatureFlagsDefaults::fuseboxNetworkInspectionEnabled(); } + double preparedTextCacheSize() override { + auto value = values_["preparedTextCacheSize"]; + if (!value.isNull()) { + return value.getDouble(); + } + + return ReactNativeFeatureFlagsDefaults::preparedTextCacheSize(); + } + bool traceTurboModulePromiseRejectionsOnAndroid() override { auto value = values_["traceTurboModulePromiseRejectionsOnAndroid"]; if (!value.isNull()) { diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h index 613b1e55d49..c17c6d32fb6 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<<9358aab32b6368b205da9726a6e57981>> */ /** @@ -69,6 +69,7 @@ class ReactNativeFeatureFlagsProvider { virtual bool fixMappingOfEventPrioritiesBetweenFabricAndReact() = 0; virtual bool fuseboxEnabledRelease() = 0; virtual bool fuseboxNetworkInspectionEnabled() = 0; + virtual double preparedTextCacheSize() = 0; virtual bool traceTurboModulePromiseRejectionsOnAndroid() = 0; virtual bool updateRuntimeShadowNodeReferencesOnCommit() = 0; virtual bool useAlwaysAvailableJSErrorHandling() = 0; diff --git a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp index 8dd0302dd4b..5a0e0fe08de 100644 --- a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp +++ b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<<563e9d9ae11c6ec231664126666e9922>> */ /** @@ -264,6 +264,11 @@ bool NativeReactNativeFeatureFlags::fuseboxNetworkInspectionEnabled( return ReactNativeFeatureFlags::fuseboxNetworkInspectionEnabled(); } +double NativeReactNativeFeatureFlags::preparedTextCacheSize( + jsi::Runtime& /*runtime*/) { + return ReactNativeFeatureFlags::preparedTextCacheSize(); +} + bool NativeReactNativeFeatureFlags::traceTurboModulePromiseRejectionsOnAndroid( jsi::Runtime& /*runtime*/) { return ReactNativeFeatureFlags::traceTurboModulePromiseRejectionsOnAndroid(); diff --git a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h index c657ba900fa..5cc77680d3b 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<<933ffc3782b46f4c4df1b0a9e30ad1d4>> + * @generated SignedSource<> */ /** @@ -124,6 +124,8 @@ class NativeReactNativeFeatureFlags bool fuseboxNetworkInspectionEnabled(jsi::Runtime& runtime); + double preparedTextCacheSize(jsi::Runtime& runtime); + bool traceTurboModulePromiseRejectionsOnAndroid(jsi::Runtime& runtime); bool updateRuntimeShadowNodeReferencesOnCommit(jsi::Runtime& runtime); diff --git a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/TextMeasureCache.h b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/TextMeasureCache.h index ecd6798f47f..962eca68005 100644 --- a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/TextMeasureCache.h +++ b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/TextMeasureCache.h @@ -83,6 +83,17 @@ class LineMeasureCacheKey final { Size size{}; }; +/** + * Cache key, mapping an AttributedString under given constraints, to a prepared + * (laid out and drawable) representation of the text. + */ +class PreparedTextCacheKey final { + public: + AttributedString attributedString{}; + ParagraphAttributes paragraphAttributes{}; + LayoutConstraints layoutConstraints{}; +}; + /* * Maximum size of the Cache. * The number was empirically chosen based on approximation of an average amount @@ -167,6 +178,17 @@ inline bool areAttributedStringFragmentsEquivalentLayoutWise( rhs.parentShadowView.layoutMetrics)); } +inline bool areAttributedStringFragmentsEquivalentDisplayWise( + const AttributedString::Fragment& lhs, + const AttributedString::Fragment& rhs) { + return lhs.isContentEqual(rhs) && + // LayoutMetrics of an attachment fragment affects the size of a measured + // attributed string. + (!lhs.isAttachment() || + (lhs.parentShadowView.layoutMetrics == + rhs.parentShadowView.layoutMetrics)); +} + inline size_t attributedStringFragmentHashLayoutWise( const AttributedString::Fragment& fragment) { // Here we are not taking `isAttachment` and `layoutMetrics` into account @@ -176,6 +198,15 @@ inline size_t attributedStringFragmentHashLayoutWise( fragment.string, textAttributesHashLayoutWise(fragment.textAttributes)); } +inline size_t attributedStringFragmentHashDisplayWise( + const AttributedString::Fragment& fragment) { + // Here we are not taking `isAttachment` and `layoutMetrics` into account + // because they are logically interdependent and this can break an invariant + // between hash and equivalence functions (and cause cache misses). + return facebook::react::hash_combine( + fragment.string, fragment.textAttributes); +} + inline bool areAttributedStringsEquivalentLayoutWise( const AttributedString& lhs, const AttributedString& rhs) { @@ -197,6 +228,27 @@ inline bool areAttributedStringsEquivalentLayoutWise( return true; } +inline bool areAttributedStringsEquivalentDisplayWise( + const AttributedString& lhs, + const AttributedString& rhs) { + auto& lhsFragment = lhs.getFragments(); + auto& rhsFragment = rhs.getFragments(); + + if (lhsFragment.size() != rhsFragment.size()) { + return false; + } + + auto size = lhsFragment.size(); + for (size_t i = 0; i < size; i++) { + if (!areAttributedStringFragmentsEquivalentDisplayWise( + lhsFragment.at(i), rhsFragment.at(i))) { + return false; + } + } + + return true; +} + inline size_t attributedStringHashLayoutWise( const AttributedString& attributedString) { auto seed = size_t{0}; @@ -209,6 +261,18 @@ inline size_t attributedStringHashLayoutWise( return seed; } +inline size_t attributedStringHashDisplayWise( + const AttributedString& attributedString) { + size_t seed = 0; + + for (const auto& fragment : attributedString.getFragments()) { + facebook::react::hash_combine( + seed, attributedStringFragmentHashDisplayWise(fragment)); + } + + return seed; +} + inline bool operator==( const TextMeasureCacheKey& lhs, const TextMeasureCacheKey& rhs) { @@ -218,12 +282,6 @@ inline bool operator==( lhs.layoutConstraints == rhs.layoutConstraints; } -inline bool operator!=( - const TextMeasureCacheKey& lhs, - const TextMeasureCacheKey& rhs) { - return !(lhs == rhs); -} - inline bool operator==( const LineMeasureCacheKey& lhs, const LineMeasureCacheKey& rhs) { @@ -233,10 +291,13 @@ inline bool operator==( lhs.size == rhs.size; } -inline bool operator!=( - const LineMeasureCacheKey& lhs, - const LineMeasureCacheKey& rhs) { - return !(lhs == rhs); +inline bool operator==( + const PreparedTextCacheKey& lhs, + const PreparedTextCacheKey& rhs) { + return areAttributedStringsEquivalentDisplayWise( + lhs.attributedString, rhs.attributedString) && + lhs.paragraphAttributes == rhs.paragraphAttributes && + lhs.layoutConstraints == rhs.layoutConstraints; } } // namespace facebook::react @@ -263,4 +324,14 @@ struct hash { } }; +template <> +struct hash { + size_t operator()(const facebook::react::PreparedTextCacheKey& key) const { + return facebook::react::hash_combine( + attributedStringHashDisplayWise(key.attributedString), + key.paragraphAttributes, + key.layoutConstraints); + } +}; + } // namespace std diff --git a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/android/react/renderer/textlayoutmanager/TextLayoutManager.cpp b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/android/react/renderer/textlayoutmanager/TextLayoutManager.cpp index 3ce824fa8aa..9525394f993 100644 --- a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/android/react/renderer/textlayoutmanager/TextLayoutManager.cpp +++ b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/android/react/renderer/textlayoutmanager/TextLayoutManager.cpp @@ -158,7 +158,9 @@ TextLayoutManager::TextLayoutManager( const ContextContainer::Shared& contextContainer) : contextContainer_(contextContainer), textMeasureCache_(kSimpleThreadSafeCacheSizeCap), - lineMeasureCache_(kSimpleThreadSafeCacheSizeCap) {} + lineMeasureCache_(kSimpleThreadSafeCacheSizeCap), + preparedTextCache_(static_cast( + ReactNativeFeatureFlags::preparedTextCacheSize())) {} TextMeasurement TextLayoutManager::measure( const AttributedStringBox& attributedStringBox, @@ -297,9 +299,6 @@ TextLayoutManager::PreparedLayout TextLayoutManager::prepareLayout( const ParagraphAttributes& paragraphAttributes, const TextLayoutContext& layoutContext, const LayoutConstraints& layoutConstraints) const { - const auto& fabricUIManager = - contextContainer_->at>("FabricUIManager"); - static auto prepareTextLayout = jni::findClassStatic("com/facebook/react/fabric/FabricUIManager") ->getMethod("prepareTextLayout"); - auto attributedStringMB = - JReadableMapBuffer::createWithContents(toMapBuffer(attributedString)); - auto paragraphAttributesMB = - JReadableMapBuffer::createWithContents(toMapBuffer(paragraphAttributes)); + return preparedTextCache_.get( + {.attributedString = attributedString, + .paragraphAttributes = paragraphAttributes, + .layoutConstraints = layoutConstraints}, + [&] { + const auto& fabricUIManager = + contextContainer_->at>("FabricUIManager"); - auto minimumSize = layoutConstraints.minimumSize; - auto maximumSize = layoutConstraints.maximumSize; + auto attributedStringMB = JReadableMapBuffer::createWithContents( + toMapBuffer(attributedString)); + auto paragraphAttributesMB = JReadableMapBuffer::createWithContents( + toMapBuffer(paragraphAttributes)); - // T222682416: We don't have any global cache here. We should investigate - // whether that is desirable - return {jni::make_global(prepareTextLayout( - fabricUIManager, - layoutContext.surfaceId, - attributedStringMB.get(), - paragraphAttributesMB.get(), - minimumSize.width, - maximumSize.width, - minimumSize.height, - maximumSize.height))}; + auto minimumSize = layoutConstraints.minimumSize; + auto maximumSize = layoutConstraints.maximumSize; + + return PreparedLayout{jni::make_global(prepareTextLayout( + fabricUIManager, + layoutContext.surfaceId, + attributedStringMB.get(), + paragraphAttributesMB.get(), + minimumSize.width, + maximumSize.width, + minimumSize.height, + maximumSize.height))}; + }); } TextMeasurement TextLayoutManager::measurePreparedLayout( diff --git a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/android/react/renderer/textlayoutmanager/TextLayoutManager.h b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/android/react/renderer/textlayoutmanager/TextLayoutManager.h index 249db1eba11..71ab0488614 100644 --- a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/android/react/renderer/textlayoutmanager/TextLayoutManager.h +++ b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/android/react/renderer/textlayoutmanager/TextLayoutManager.h @@ -95,6 +95,11 @@ class TextLayoutManager { std::shared_ptr contextContainer_; TextMeasureCache textMeasureCache_; LineMeasureCache lineMeasureCache_; + SimpleThreadSafeCache< + PreparedTextCacheKey, + PreparedLayout, + -1 /* Set dynamically*/> + preparedTextCache_; }; } // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/utils/SimpleThreadSafeCache.h b/packages/react-native/ReactCommon/react/utils/SimpleThreadSafeCache.h index 67468619ad9..3dc71ac2810 100644 --- a/packages/react-native/ReactCommon/react/utils/SimpleThreadSafeCache.h +++ b/packages/react-native/ReactCommon/react/utils/SimpleThreadSafeCache.h @@ -20,6 +20,9 @@ concept CacheGeneratorFunction = std::invocable && /* * Simple thread-safe LRU cache. + * + * TODO T228961279: The maxSize template parameter should be removed, since it + * may be overriden by the constructor. */ template class SimpleThreadSafeCache { diff --git a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js index ec833899d12..1a549e2e4e6 100644 --- a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js +++ b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js @@ -511,6 +511,16 @@ const definitions: FeatureFlagDefinitions = { }, ossReleaseStage: 'none', }, + preparedTextCacheSize: { + defaultValue: 200, + metadata: { + dateAdded: '2025-06-25', + description: 'Number cached PreparedLayouts in TextLayoutManager cache', + expectedReleaseValue: 200, + purpose: 'experimentation', + }, + ossReleaseStage: 'none', + }, traceTurboModulePromiseRejectionsOnAndroid: { defaultValue: false, metadata: { diff --git a/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js b/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js index a2459528371..211a1c4d67d 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<<194e0f47db33d3b9167f58ed186a331c>> + * @generated SignedSource<<731dbe289a0c7cf5bec2c8033150ebb9>> * @flow strict * @noformat */ @@ -94,6 +94,7 @@ export type ReactNativeFeatureFlags = $ReadOnly<{ fixMappingOfEventPrioritiesBetweenFabricAndReact: Getter, fuseboxEnabledRelease: Getter, fuseboxNetworkInspectionEnabled: Getter, + preparedTextCacheSize: Getter, traceTurboModulePromiseRejectionsOnAndroid: Getter, updateRuntimeShadowNodeReferencesOnCommit: Getter, useAlwaysAvailableJSErrorHandling: Getter, @@ -362,6 +363,10 @@ export const fuseboxEnabledRelease: Getter = createNativeFlagGetter('fu * Enable network inspection support in the React Native DevTools CDP backend. Requires `enableBridgelessArchitecture`. This flag is global and should not be changed across React Host lifetimes. */ export const fuseboxNetworkInspectionEnabled: Getter = createNativeFlagGetter('fuseboxNetworkInspectionEnabled', false); +/** + * Number cached PreparedLayouts in TextLayoutManager cache + */ +export const preparedTextCacheSize: Getter = createNativeFlagGetter('preparedTextCacheSize', 200); /** * Enables storing js caller stack when creating promise in native module. This is useful in case of Promise rejection and tracing the cause. */ diff --git a/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js b/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js index cb8a470712b..08d3f9a3bb0 100644 --- a/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js +++ b/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<543998b20611a8f6adb0840784d1a5a7>> + * @generated SignedSource<> * @flow strict * @noformat */ @@ -69,6 +69,7 @@ export interface Spec extends TurboModule { +fixMappingOfEventPrioritiesBetweenFabricAndReact?: () => boolean; +fuseboxEnabledRelease?: () => boolean; +fuseboxNetworkInspectionEnabled?: () => boolean; + +preparedTextCacheSize?: () => number; +traceTurboModulePromiseRejectionsOnAndroid?: () => boolean; +updateRuntimeShadowNodeReferencesOnCommit?: () => boolean; +useAlwaysAvailableJSErrorHandling?: () => boolean;