From 1f24750d81c66108c8a0192b09a3677a5b47e596 Mon Sep 17 00:00:00 2001 From: Samuel Susla Date: Wed, 2 Aug 2023 05:56:31 -0700 Subject: [PATCH] Mark Paragraph as clean if it hasn't changed (#38749) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/38749 changelog: [internal] # Request for comment I'll gate this and extend this to TextInput if we think this is appropriate solution. ## Problem Paragraph and TextInput have custom measure functions. They outsource measurements to the host platform. On Android, this means going through JNI (expensive). When we architected YogaLayoutableShadowNode, we made intentional decision to dirty those nodes on every clone. This was to lower the complexity. ## Solution One possible to solution is to just check if children, props or textAttributes have changed. If not, nothing has affected the layout and it is safe to mark the node as clean. Reviewed By: NickGerleman Differential Revision: D47914641 fbshipit-source-id: ab448fa18599eb2266984eba9f5d935caad1caed --- .../facebook/react/config/ReactFeatureFlags.java | 3 +++ .../src/main/jni/react/fabric/Binding.cpp | 2 ++ .../components/text/ParagraphShadowNode.cpp | 15 +++++++++++++++ .../components/text/ParagraphShadowNode.h | 4 ++++ .../ReactCommon/react/utils/CoreFeatures.cpp | 1 + .../ReactCommon/react/utils/CoreFeatures.h | 3 +++ 6 files changed, 28 insertions(+) diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/config/ReactFeatureFlags.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/config/ReactFeatureFlags.java index 82b05cec891ca0..b084ccbb43d1b7 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/config/ReactFeatureFlags.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/config/ReactFeatureFlags.java @@ -157,4 +157,7 @@ public class ReactFeatureFlags { /** Only swap left and right on Android in RTL scripts. */ public static boolean doNotSwapLeftAndRightOnAndroidInLTR = false; + + /** Clean yoga node when does not change. */ + public static boolean enableCleanParagraphYogaNode = false; } diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/fabric/Binding.cpp b/packages/react-native/ReactAndroid/src/main/jni/react/fabric/Binding.cpp index 65d6db392f8500..ea4cf210f4bb19 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/fabric/Binding.cpp +++ b/packages/react-native/ReactAndroid/src/main/jni/react/fabric/Binding.cpp @@ -433,6 +433,8 @@ void Binding::installFabricUIManager( CoreFeatures::enableMapBuffer = getFeatureFlagValue("useMapBufferProps"); CoreFeatures::doNotSwapLeftAndRightOnAndroidInLTR = getFeatureFlagValue("doNotSwapLeftAndRightOnAndroidInLTR"); + CoreFeatures::enableCleanParagraphYogaNode = + getFeatureFlagValue("enableCleanParagraphYogaNode"); // RemoveDelete mega-op ShadowViewMutation::PlatformSupportsRemoveDeleteTreeInstruction = 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 17cb7dd34ad1c1..274b9b4b36d548 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphShadowNode.cpp +++ b/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphShadowNode.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include "ParagraphState.h" @@ -25,6 +26,20 @@ using Content = ParagraphShadowNode::Content; char const ParagraphComponentName[] = "Paragraph"; +ParagraphShadowNode::ParagraphShadowNode( + ShadowNode const &sourceShadowNode, + ShadowNodeFragment const &fragment) + : ConcreteViewShadowNode(sourceShadowNode, fragment) { + if (CoreFeatures::enableCleanParagraphYogaNode) { + if (!fragment.children && !fragment.props) { + // This ParagraphShadowNode was cloned but did not change + // in a way that affects its layout. Let's mark it clean + // to stop Yoga from traversing it. + cleanLayout(); + } + } +} + Content const &ParagraphShadowNode::getContent( LayoutContext const &layoutContext) const { if (content_.has_value()) { 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 b44e8f416dc675..40f399b91fd6da 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphShadowNode.h +++ b/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphShadowNode.h @@ -34,6 +34,10 @@ class ParagraphShadowNode final : public ConcreteViewShadowNode< public: using ConcreteViewShadowNode::ConcreteViewShadowNode; + ParagraphShadowNode( + ShadowNode const &sourceShadowNode, + ShadowNodeFragment const &fragment); + static ShadowNodeTraits BaseTraits() { auto traits = ConcreteViewShadowNode::BaseTraits(); traits.set(ShadowNodeTraits::Trait::LeafYogaNode); diff --git a/packages/react-native/ReactCommon/react/utils/CoreFeatures.cpp b/packages/react-native/ReactCommon/react/utils/CoreFeatures.cpp index 2c3ce14e5ba61a..e44b019050176b 100644 --- a/packages/react-native/ReactCommon/react/utils/CoreFeatures.cpp +++ b/packages/react-native/ReactCommon/react/utils/CoreFeatures.cpp @@ -18,5 +18,6 @@ bool CoreFeatures::cancelImageDownloadsOnRecycle = false; bool CoreFeatures::enableGranularScrollViewStateUpdatesIOS = false; bool CoreFeatures::enableMountHooks = false; bool CoreFeatures::doNotSwapLeftAndRightOnAndroidInLTR = false; +bool CoreFeatures::enableCleanParagraphYogaNode = false; } // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/utils/CoreFeatures.h b/packages/react-native/ReactCommon/react/utils/CoreFeatures.h index 8070cb4006f909..2d693ffc68b0fc 100644 --- a/packages/react-native/ReactCommon/react/utils/CoreFeatures.h +++ b/packages/react-native/ReactCommon/react/utils/CoreFeatures.h @@ -53,6 +53,9 @@ class CoreFeatures { // Only swap left and right on Android in RTL scripts. static bool doNotSwapLeftAndRightOnAndroidInLTR; + + // Clean yoga node when does not change. + static bool enableCleanParagraphYogaNode; }; } // namespace facebook::react