From 8c53ac60782d8ccd2ccd7706d41e60cda93e2988 Mon Sep 17 00:00:00 2001 From: Samuel Susla Date: Tue, 16 Apr 2024 12:19:11 -0700 Subject: [PATCH] setup an experiment to prevent double measure of text (#44120) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/44120 changelog: [internal] Prevent measuring the same text twice in `ParagraphShadowNode`. The current implementation calls `TextLayoutManager::measure` twice for a single `ParagraphShadowNode`. The first time to measure the node for Yoga. The second time inside `ParagraphShadowNode::layout`. I think the original author counted on the cache inside of `TextLayoutManager` to deal with this, but this is not always the case and `TextLayoutManager::measure` is called with two different available widths, leading to cache miss and fills the cache faster. Reviewed By: NickGerleman Differential Revision: D55757264 fbshipit-source-id: 0bf8b49f062f802a4e2f04cad1bf1d4bf001b870 --- .../featureflags/ReactNativeFeatureFlags.kt | 8 ++++- .../ReactNativeFeatureFlagsCxxAccessor.kt | 12 ++++++- .../ReactNativeFeatureFlagsCxxInterop.kt | 4 ++- .../ReactNativeFeatureFlagsDefaults.kt | 4 ++- .../ReactNativeFeatureFlagsLocalAccessor.kt | 13 +++++++- .../ReactNativeFeatureFlagsProvider.kt | 4 ++- .../JReactNativeFeatureFlagsCxxInterop.cpp | 16 +++++++++- .../JReactNativeFeatureFlagsCxxInterop.h | 5 ++- .../featureflags/ReactNativeFeatureFlags.cpp | 6 +++- .../featureflags/ReactNativeFeatureFlags.h | 7 +++- .../ReactNativeFeatureFlagsAccessor.cpp | 26 ++++++++++++--- .../ReactNativeFeatureFlagsAccessor.h | 6 ++-- .../ReactNativeFeatureFlagsDefaults.h | 6 +++- .../ReactNativeFeatureFlagsProvider.h | 3 +- .../NativeReactNativeFeatureFlags.cpp | 7 +++- .../NativeReactNativeFeatureFlags.h | 4 ++- .../components/text/ParagraphShadowNode.cpp | 32 +++++++++++++------ .../components/text/ParagraphShadowNode.h | 1 + .../ReactNativeFeatureFlags.config.js | 5 +++ .../featureflags/ReactNativeFeatureFlags.js | 7 +++- .../specs/NativeReactNativeFeatureFlags.js | 3 +- 21 files changed, 148 insertions(+), 31 deletions(-) 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 a10ff55dd8e..af6501993d1 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<<7c2d825e15fc7e442034a598eebb6e25>> + * @generated SignedSource<<61b929b9624eeaedfa12d2f390299447>> */ /** @@ -106,6 +106,12 @@ public object ReactNativeFeatureFlags { @JvmStatic public fun inspectorEnableModernCDPRegistry(): Boolean = accessor.inspectorEnableModernCDPRegistry() + /** + * When enabled, ParagraphShadowNode will no longer call measure twice. + */ + @JvmStatic + public fun preventDoubleTextMeasure(): Boolean = accessor.preventDoubleTextMeasure() + /** * When enabled, it uses the modern fork of RuntimeScheduler that allows scheduling tasks with priorities from any thread. */ 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 7488e94a0d6..9cb2521fb3a 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<> + * @generated SignedSource<<46e1a8dd088d588ea9eec703d7155710>> */ /** @@ -33,6 +33,7 @@ public class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAccesso private var forceBatchingMountItemsOnAndroidCache: Boolean? = null private var inspectorEnableCxxInspectorPackagerConnectionCache: Boolean? = null private var inspectorEnableModernCDPRegistryCache: Boolean? = null + private var preventDoubleTextMeasureCache: Boolean? = null private var useModernRuntimeSchedulerCache: Boolean? = null private var useNativeViewConfigsInBridgelessModeCache: Boolean? = null private var useStateAlignmentMechanismCache: Boolean? = null @@ -154,6 +155,15 @@ public class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAccesso return cached } + override fun preventDoubleTextMeasure(): Boolean { + var cached = preventDoubleTextMeasureCache + if (cached == null) { + cached = ReactNativeFeatureFlagsCxxInterop.preventDoubleTextMeasure() + preventDoubleTextMeasureCache = cached + } + return cached + } + override fun useModernRuntimeScheduler(): Boolean { var cached = useModernRuntimeSchedulerCache 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 a62bc3680f5..0cdc110abcc 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<<0b1377acc1a7a2bed8bd6448a54ec91a>> + * @generated SignedSource<<56ac4a5eaf65775fdb757fc10ed82908>> */ /** @@ -54,6 +54,8 @@ public object ReactNativeFeatureFlagsCxxInterop { @DoNotStrip @JvmStatic public external fun inspectorEnableModernCDPRegistry(): Boolean + @DoNotStrip @JvmStatic public external fun preventDoubleTextMeasure(): Boolean + @DoNotStrip @JvmStatic public external fun useModernRuntimeScheduler(): Boolean @DoNotStrip @JvmStatic public external fun useNativeViewConfigsInBridgelessMode(): 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 b58b7e73968..9c53c482101 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<<545ef5d7c16c2d6599f15a747c53cf3f>> + * @generated SignedSource<<6cc4fccd13a1e426a2d6a2a24530e5e6>> */ /** @@ -49,6 +49,8 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi override fun inspectorEnableModernCDPRegistry(): Boolean = false + override fun preventDoubleTextMeasure(): Boolean = false + override fun useModernRuntimeScheduler(): Boolean = false override fun useNativeViewConfigsInBridgelessMode(): 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 69e028208e6..901b7ab21a3 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<> + * @generated SignedSource<> */ /** @@ -37,6 +37,7 @@ public class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcces private var forceBatchingMountItemsOnAndroidCache: Boolean? = null private var inspectorEnableCxxInspectorPackagerConnectionCache: Boolean? = null private var inspectorEnableModernCDPRegistryCache: Boolean? = null + private var preventDoubleTextMeasureCache: Boolean? = null private var useModernRuntimeSchedulerCache: Boolean? = null private var useNativeViewConfigsInBridgelessModeCache: Boolean? = null private var useStateAlignmentMechanismCache: Boolean? = null @@ -171,6 +172,16 @@ public class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcces return cached } + override fun preventDoubleTextMeasure(): Boolean { + var cached = preventDoubleTextMeasureCache + if (cached == null) { + cached = currentProvider.preventDoubleTextMeasure() + accessedFeatureFlags.add("preventDoubleTextMeasure") + preventDoubleTextMeasureCache = cached + } + return cached + } + override fun useModernRuntimeScheduler(): Boolean { var cached = useModernRuntimeSchedulerCache 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 ef201023ea3..98f145c6a4d 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<> */ /** @@ -49,6 +49,8 @@ public interface ReactNativeFeatureFlagsProvider { @DoNotStrip public fun inspectorEnableModernCDPRegistry(): Boolean + @DoNotStrip public fun preventDoubleTextMeasure(): Boolean + @DoNotStrip public fun useModernRuntimeScheduler(): Boolean @DoNotStrip public fun useNativeViewConfigsInBridgelessMode(): 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 a7ddc0b3d78..adb70bb9ac1 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<<82163a04d1e3cb54d6c5125173ab807f>> + * @generated SignedSource<> */ /** @@ -117,6 +117,12 @@ class ReactNativeFeatureFlagsProviderHolder return method(javaProvider_); } + bool preventDoubleTextMeasure() override { + static const auto method = + getReactNativeFeatureFlagsProviderJavaClass()->getMethod("preventDoubleTextMeasure"); + return method(javaProvider_); + } + bool useModernRuntimeScheduler() override { static const auto method = getReactNativeFeatureFlagsProviderJavaClass()->getMethod("useModernRuntimeScheduler"); @@ -204,6 +210,11 @@ bool JReactNativeFeatureFlagsCxxInterop::inspectorEnableModernCDPRegistry( return ReactNativeFeatureFlags::inspectorEnableModernCDPRegistry(); } +bool JReactNativeFeatureFlagsCxxInterop::preventDoubleTextMeasure( + facebook::jni::alias_ref /*unused*/) { + return ReactNativeFeatureFlags::preventDoubleTextMeasure(); +} + bool JReactNativeFeatureFlagsCxxInterop::useModernRuntimeScheduler( facebook::jni::alias_ref /*unused*/) { return ReactNativeFeatureFlags::useModernRuntimeScheduler(); @@ -275,6 +286,9 @@ void JReactNativeFeatureFlagsCxxInterop::registerNatives() { makeNativeMethod( "inspectorEnableModernCDPRegistry", JReactNativeFeatureFlagsCxxInterop::inspectorEnableModernCDPRegistry), + makeNativeMethod( + "preventDoubleTextMeasure", + JReactNativeFeatureFlagsCxxInterop::preventDoubleTextMeasure), makeNativeMethod( "useModernRuntimeScheduler", JReactNativeFeatureFlagsCxxInterop::useModernRuntimeScheduler), 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 ab28c2198c3..f04beda6394 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<<48e337d37416ab988ea747050bf207f3>> + * @generated SignedSource<> */ /** @@ -69,6 +69,9 @@ class JReactNativeFeatureFlagsCxxInterop static bool inspectorEnableModernCDPRegistry( facebook::jni::alias_ref); + static bool preventDoubleTextMeasure( + facebook::jni::alias_ref); + static bool useModernRuntimeScheduler( 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 496dd16a361..016ef0049a2 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<<0fe73e0526dbc2f860029b8cb0fbcfe3>> */ /** @@ -73,6 +73,10 @@ bool ReactNativeFeatureFlags::inspectorEnableModernCDPRegistry() { return getAccessor().inspectorEnableModernCDPRegistry(); } +bool ReactNativeFeatureFlags::preventDoubleTextMeasure() { + return getAccessor().preventDoubleTextMeasure(); +} + bool ReactNativeFeatureFlags::useModernRuntimeScheduler() { return getAccessor().useModernRuntimeScheduler(); } diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h index ddc2631fd50..22547ef6673 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<<8804fc52ad516ba82c0db6fcdea5f110>> + * @generated SignedSource<> */ /** @@ -102,6 +102,11 @@ class ReactNativeFeatureFlags { */ RN_EXPORT static bool inspectorEnableModernCDPRegistry(); + /** + * When enabled, ParagraphShadowNode will no longer call measure twice. + */ + RN_EXPORT static bool preventDoubleTextMeasure(); + /** * When enabled, it uses the modern fork of RuntimeScheduler that allows scheduling tasks with priorities from any thread. */ diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp index 6b296c18949..d9708d02ea9 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<<9f5c5313e139b450449e2716735c26e7>> + * @generated SignedSource<> */ /** @@ -263,6 +263,24 @@ 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(13, "preventDoubleTextMeasure"); + + flagValue = currentProvider_->preventDoubleTextMeasure(); + preventDoubleTextMeasure_ = flagValue; + } + + return flagValue.value(); +} + bool ReactNativeFeatureFlagsAccessor::useModernRuntimeScheduler() { auto flagValue = useModernRuntimeScheduler_.load(); @@ -272,7 +290,7 @@ bool ReactNativeFeatureFlagsAccessor::useModernRuntimeScheduler() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(13, "useModernRuntimeScheduler"); + markFlagAsAccessed(14, "useModernRuntimeScheduler"); flagValue = currentProvider_->useModernRuntimeScheduler(); useModernRuntimeScheduler_ = flagValue; @@ -290,7 +308,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(14, "useNativeViewConfigsInBridgelessMode"); + markFlagAsAccessed(15, "useNativeViewConfigsInBridgelessMode"); flagValue = currentProvider_->useNativeViewConfigsInBridgelessMode(); useNativeViewConfigsInBridgelessMode_ = flagValue; @@ -308,7 +326,7 @@ bool ReactNativeFeatureFlagsAccessor::useStateAlignmentMechanism() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(15, "useStateAlignmentMechanism"); + markFlagAsAccessed(16, "useStateAlignmentMechanism"); flagValue = currentProvider_->useStateAlignmentMechanism(); useStateAlignmentMechanism_ = flagValue; diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h index f48b7c13eaa..80565113df0 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<<2ac50c51832dfbcebfc8875c02738727>> */ /** @@ -44,6 +44,7 @@ class ReactNativeFeatureFlagsAccessor { bool forceBatchingMountItemsOnAndroid(); bool inspectorEnableCxxInspectorPackagerConnection(); bool inspectorEnableModernCDPRegistry(); + bool preventDoubleTextMeasure(); bool useModernRuntimeScheduler(); bool useNativeViewConfigsInBridgelessMode(); bool useStateAlignmentMechanism(); @@ -57,7 +58,7 @@ class ReactNativeFeatureFlagsAccessor { std::unique_ptr currentProvider_; bool wasOverridden_; - std::array, 16> accessedFeatureFlags_; + std::array, 17> accessedFeatureFlags_; std::atomic> commonTestFlag_; std::atomic> batchRenderingUpdatesInEventLoop_; @@ -72,6 +73,7 @@ class ReactNativeFeatureFlagsAccessor { std::atomic> forceBatchingMountItemsOnAndroid_; std::atomic> inspectorEnableCxxInspectorPackagerConnection_; std::atomic> inspectorEnableModernCDPRegistry_; + std::atomic> preventDoubleTextMeasure_; std::atomic> useModernRuntimeScheduler_; std::atomic> useNativeViewConfigsInBridgelessMode_; std::atomic> useStateAlignmentMechanism_; diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h index ae8cd41420e..607c9fef018 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<<008818e7f2d8b9e295704cccc33758e7>> + * @generated SignedSource<> */ /** @@ -79,6 +79,10 @@ class ReactNativeFeatureFlagsDefaults : public ReactNativeFeatureFlagsProvider { return false; } + bool preventDoubleTextMeasure() override { + return false; + } + bool useModernRuntimeScheduler() override { return false; } diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h index 32e9bc271a1..164da5410b4 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<<270291da73cb3e4eddc7bd5cdc9a9470>> + * @generated SignedSource<<2ffedc198d97593ae8c5d5300aa640a2>> */ /** @@ -38,6 +38,7 @@ class ReactNativeFeatureFlagsProvider { virtual bool forceBatchingMountItemsOnAndroid() = 0; virtual bool inspectorEnableCxxInspectorPackagerConnection() = 0; virtual bool inspectorEnableModernCDPRegistry() = 0; + virtual bool preventDoubleTextMeasure() = 0; virtual bool useModernRuntimeScheduler() = 0; virtual bool useNativeViewConfigsInBridgelessMode() = 0; virtual bool useStateAlignmentMechanism() = 0; diff --git a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp index 60dea7a12db..e5a0cdbfb55 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<<1abf19f9d1352105511e86c0f217de7a>> + * @generated SignedSource<> */ /** @@ -102,6 +102,11 @@ bool NativeReactNativeFeatureFlags::inspectorEnableModernCDPRegistry( return ReactNativeFeatureFlags::inspectorEnableModernCDPRegistry(); } +bool NativeReactNativeFeatureFlags::preventDoubleTextMeasure( + jsi::Runtime& /*runtime*/) { + return ReactNativeFeatureFlags::preventDoubleTextMeasure(); +} + bool NativeReactNativeFeatureFlags::useModernRuntimeScheduler( jsi::Runtime& /*runtime*/) { return ReactNativeFeatureFlags::useModernRuntimeScheduler(); diff --git a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h index fcd022ff96a..9c18da7af77 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<<7a2a41f80549322a7f4255d193dc0b7b>> + * @generated SignedSource<<92830e364788db00fd06d4e152ef7727>> */ /** @@ -61,6 +61,8 @@ class NativeReactNativeFeatureFlags bool inspectorEnableModernCDPRegistry(jsi::Runtime& runtime); + bool preventDoubleTextMeasure(jsi::Runtime& runtime); + bool useModernRuntimeScheduler(jsi::Runtime& runtime); bool useNativeViewConfigsInBridgelessMode(jsi::Runtime& runtime); diff --git a/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphShadowNode.cpp b/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphShadowNode.cpp index 98fb4fce4de..55b37890b87 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphShadowNode.cpp +++ b/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphShadowNode.cpp @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -166,10 +167,10 @@ void ParagraphShadowNode::layout(LayoutContext layoutContext) { ensureUnsealed(); auto layoutMetrics = getLayoutMetrics(); - auto availableSize = layoutMetrics.getContentFrame().size; + auto size = layoutMetrics.getContentFrame().size; - auto layoutConstraints = LayoutConstraints{ - availableSize, availableSize, layoutMetrics.layoutDirection}; + auto layoutConstraints = + LayoutConstraints{size, size, layoutMetrics.layoutDirection}; auto content = getContentWithMeasuredAttachments(layoutContext, layoutConstraints); @@ -177,15 +178,19 @@ void ParagraphShadowNode::layout(LayoutContext layoutContext) { TextLayoutContext textLayoutContext{}; textLayoutContext.pointScaleFactor = layoutContext.pointScaleFactor; - auto measurement = textLayoutManager_->measure( - AttributedStringBox{content.attributedString}, - content.paragraphAttributes, - textLayoutContext, - layoutConstraints); + auto measurement = TextMeasurement{}; + + if (!ReactNativeFeatureFlags::preventDoubleTextMeasure()) { + measurement = textLayoutManager_->measure( + AttributedStringBox{content.attributedString}, + content.paragraphAttributes, + textLayoutContext, + layoutConstraints); + } if (getConcreteProps().onTextLayout) { auto linesMeasurements = textLayoutManager_->measureLines( - content.attributedString, content.paragraphAttributes, availableSize); + content.attributedString, content.paragraphAttributes, size); getConcreteEventEmitter().onTextLayout(linesMeasurements); } @@ -194,6 +199,15 @@ void ParagraphShadowNode::layout(LayoutContext layoutContext) { return; } + if (ReactNativeFeatureFlags::preventDoubleTextMeasure()) { + // Only measure if attachments are not empty. + measurement = textLayoutManager_->measure( + AttributedStringBox{content.attributedString}, + content.paragraphAttributes, + textLayoutContext, + layoutConstraints); + } + // Iterating on attachments, we clone shadow nodes and moving // `paragraphShadowNode` that represents clones of `this` object. auto paragraphShadowNode = static_cast(this); diff --git a/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphShadowNode.h b/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphShadowNode.h index 8366155cec4..a95b9d565b4 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphShadowNode.h +++ b/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphShadowNode.h @@ -63,6 +63,7 @@ class ParagraphShadowNode final : public ConcreteViewShadowNode< #pragma mark - LayoutableShadowNode void layout(LayoutContext layoutContext) override; + Size measureContent( const LayoutContext& layoutContext, const LayoutConstraints& layoutConstraints) const override; diff --git a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js index aba1b62f9d3..e0f43b4d6db 100644 --- a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js +++ b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js @@ -91,6 +91,11 @@ const definitions: FeatureFlagDefinitions = { description: 'Flag determining if the modern CDP backend should be enabled. This flag is global and should not be changed across React Host lifetimes.', }, + preventDoubleTextMeasure: { + defaultValue: false, + description: + 'When enabled, ParagraphShadowNode will no longer call measure twice.', + }, useModernRuntimeScheduler: { defaultValue: false, description: diff --git a/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js b/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js index 7b46e29f731..a148f352583 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<<39fd96ed670e0acae857e7fce3377452>> + * @generated SignedSource<<0e0858557eb27f3eebfecae18ee13c0b>> * @flow strict-local */ @@ -53,6 +53,7 @@ export type ReactNativeFeatureFlags = { forceBatchingMountItemsOnAndroid: Getter, inspectorEnableCxxInspectorPackagerConnection: Getter, inspectorEnableModernCDPRegistry: Getter, + preventDoubleTextMeasure: Getter, useModernRuntimeScheduler: Getter, useNativeViewConfigsInBridgelessMode: Getter, useStateAlignmentMechanism: Getter, @@ -150,6 +151,10 @@ export const inspectorEnableCxxInspectorPackagerConnection: Getter = cr * Flag determining if the modern CDP backend should be enabled. This flag is global and should not be changed across React Host lifetimes. */ export const inspectorEnableModernCDPRegistry: Getter = createNativeFlagGetter('inspectorEnableModernCDPRegistry', false); +/** + * When enabled, ParagraphShadowNode will no longer call measure twice. + */ +export const preventDoubleTextMeasure: Getter = createNativeFlagGetter('preventDoubleTextMeasure', false); /** * When enabled, it uses the modern fork of RuntimeScheduler that allows scheduling tasks with priorities from any thread. */ diff --git a/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js b/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js index 28a6de2e5c9..19e75d29927 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<<99ddb55dadff7adfb02c105984d47cbf>> + * @generated SignedSource<<4ba909c3b360c6a4fc9c6ed5996b6a13>> * @flow strict-local */ @@ -36,6 +36,7 @@ export interface Spec extends TurboModule { +forceBatchingMountItemsOnAndroid?: () => boolean; +inspectorEnableCxxInspectorPackagerConnection?: () => boolean; +inspectorEnableModernCDPRegistry?: () => boolean; + +preventDoubleTextMeasure?: () => boolean; +useModernRuntimeScheduler?: () => boolean; +useNativeViewConfigsInBridgelessMode?: () => boolean; +useStateAlignmentMechanism?: () => boolean;