From 68ce28a0eea2ef9ffd4586333f6c6afd5ef821ed Mon Sep 17 00:00:00 2001 From: Nick Gerleman Date: Mon, 8 May 2023 16:30:06 -0700 Subject: [PATCH] Fix YGNodeSetConfig causing excessive dirtying (#37316) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/37316 In Fabric, a ShadowNode may re-initialize YGConfig several times throughout the lifetime of a perpetually cloned Yoga Node. RN sets `pointScaleFactor` lazily, when laying out the rootview. So right now it initializes a config to `pointScaleFactor` of 1.0, sets it, sets a new `pointScaleFactor` on the config, then repeats. This cycles the config between two `pointScaleFactor` values and will excessively dirty the node now that `YGNodeSetConfig` dirties on config change (D45505089) This change makes it so that we retain previously used `pointScaleFactor` when cloning the Yoga nodes. Changelog: [Internal] Reviewed By: rozele Differential Revision: D45669878 fbshipit-source-id: bfd2e185d9264a1cda64e59132960060385e16f1 --- .../components/view/YogaLayoutableShadowNode.cpp | 15 +++++++++++++-- .../components/view/YogaLayoutableShadowNode.h | 4 +++- .../react-native/ReactCommon/yoga/yoga/Yoga.cpp | 4 ++++ .../react-native/ReactCommon/yoga/yoga/Yoga.h | 1 + 4 files changed, 21 insertions(+), 3 deletions(-) diff --git a/packages/react-native/ReactCommon/react/renderer/components/view/YogaLayoutableShadowNode.cpp b/packages/react-native/ReactCommon/react/renderer/components/view/YogaLayoutableShadowNode.cpp index 75e6c1b15ee..fb4e57deaff 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/view/YogaLayoutableShadowNode.cpp +++ b/packages/react-native/ReactCommon/react/renderer/components/view/YogaLayoutableShadowNode.cpp @@ -120,9 +120,13 @@ YogaLayoutableShadowNode::YogaLayoutableShadowNode( } } + YGConfigRef previousConfig = YGNodeGetConfig( + &static_cast(sourceShadowNode) + .yogaNode_); + yogaNode_.setContext(this); yogaNode_.setOwner(nullptr); - yogaNode_.setConfig(&initializeYogaConfig(yogaConfig_)); + yogaNode_.setConfig(&initializeYogaConfig(yogaConfig_, previousConfig)); updateYogaChildrenOwnersIfNeeded(); // This is the only legit place where we can dirty cloned Yoga node. @@ -742,10 +746,17 @@ YogaLayoutableShadowNode &YogaLayoutableShadowNode::shadowNodeFromContext( *static_cast(yogaNode->getContext())); } -YGConfig &YogaLayoutableShadowNode::initializeYogaConfig(YGConfig &config) { +YGConfig &YogaLayoutableShadowNode::initializeYogaConfig( + YGConfig &config, + const YGConfigRef previousConfig) { YGConfigSetCloneNodeFunc( &config, YogaLayoutableShadowNode::yogaNodeCloneCallbackConnector); YGConfigSetErrata(&config, YGErrataAll); + if (previousConfig != nullptr) { + YGConfigSetPointScaleFactor( + &config, YGConfigGetPointScaleFactor(previousConfig)); + } + #ifdef RN_DEBUG_YOGA_LOGGER YGConfigSetPrintTreeFlag(&config, true); #endif diff --git a/packages/react-native/ReactCommon/react/renderer/components/view/YogaLayoutableShadowNode.h b/packages/react-native/ReactCommon/react/renderer/components/view/YogaLayoutableShadowNode.h index 9ad7f28cf03..9456b0ccc0b 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/view/YogaLayoutableShadowNode.h +++ b/packages/react-native/ReactCommon/react/renderer/components/view/YogaLayoutableShadowNode.h @@ -132,7 +132,9 @@ class YogaLayoutableShadowNode : public LayoutableShadowNode { */ void adoptYogaChild(size_t index); - static YGConfig &initializeYogaConfig(YGConfig &config); + static YGConfig &initializeYogaConfig( + YGConfig &config, + YGConfigRef previousConfig = nullptr); static YGNode *yogaNodeCloneCallbackConnector( YGNode *oldYogaNode, YGNode *parentYogaNode, diff --git a/packages/react-native/ReactCommon/yoga/yoga/Yoga.cpp b/packages/react-native/ReactCommon/yoga/yoga/Yoga.cpp index d9863a2f19d..8e2fdb4de56 100644 --- a/packages/react-native/ReactCommon/yoga/yoga/Yoga.cpp +++ b/packages/react-native/ReactCommon/yoga/yoga/Yoga.cpp @@ -4075,6 +4075,10 @@ YOGA_EXPORT void YGConfigSetPointScaleFactor( } } +YOGA_EXPORT float YGConfigGetPointScaleFactor(const YGConfigRef config) { + return config->getPointScaleFactor(); +} + static void YGRoundToPixelGrid( const YGNodeRef node, const double pointScaleFactor, diff --git a/packages/react-native/ReactCommon/yoga/yoga/Yoga.h b/packages/react-native/ReactCommon/yoga/yoga/Yoga.h index 1beb320e08a..de07aeac8b7 100644 --- a/packages/react-native/ReactCommon/yoga/yoga/Yoga.h +++ b/packages/react-native/ReactCommon/yoga/yoga/Yoga.h @@ -317,6 +317,7 @@ WIN_EXPORT void YGAssertWithConfig( WIN_EXPORT void YGConfigSetPointScaleFactor( YGConfigRef config, float pixelsInPoint); +WIN_EXPORT float YGConfigGetPointScaleFactor(YGConfigRef config); // Yoga previously had an error where containers would take the maximum space // possible instead of the minimum like they are supposed to. In practice this