diff --git a/ReactCommon/yoga/yoga/YGNode.cpp b/ReactCommon/yoga/yoga/YGNode.cpp index 223dd919111977..d44dce371c1466 100644 --- a/ReactCommon/yoga/yoga/YGNode.cpp +++ b/ReactCommon/yoga/yoga/YGNode.cpp @@ -572,6 +572,13 @@ void YGNode::markDirtyAndPropogate() { } } +void YGNode::markDirtyAndPropogateDownwards() { + isDirty_ = true; + for_each(children_.begin(), children_.end(), [](YGNodeRef childNode) { + childNode->markDirtyAndPropogateDownwards(); + }); +} + float YGNode::resolveFlexGrow() { // Root nodes flexGrow should always be 0 if (parent_ == nullptr) { @@ -666,3 +673,57 @@ float YGNode::getLeadingPaddingAndBorder( const float widthSize) { return getLeadingPadding(axis, widthSize) + getLeadingBorder(axis); } + +bool YGNode::didUseLegacyFlag() { + bool didUseLegacyFlag = layout_.didUseLegacyFlag; + if (didUseLegacyFlag) { + return true; + } + for (const auto& child : children_) { + if (child->layout_.didUseLegacyFlag) { + didUseLegacyFlag = true; + break; + } + } + return didUseLegacyFlag; +} + +void YGNode::setAndPropogateUseLegacyFlag(bool useLegacyFlag) { + config_->useLegacyStretchBehaviour = useLegacyFlag; + for_each(children_.begin(), children_.end(), [=](YGNodeRef childNode) { + childNode->getConfig()->useLegacyStretchBehaviour = useLegacyFlag; + }); +} + +void YGNode::setLayoutDoesLegacyFlagAffectsLayout( + bool doesLegacyFlagAffectsLayout) { + layout_.doesLegacyStretchFlagAffectsLayout = doesLegacyFlagAffectsLayout; +} + +void YGNode::setLayoutDidUseLegacyFlag(bool didUseLegacyFlag) { + layout_.didUseLegacyFlag = didUseLegacyFlag; +} + +bool YGNode::isLayoutTreeEqualToNode(const YGNode& node) const { + if (children_.size() != node.children_.size()) { + return false; + } + if (layout_ != node.layout_) { + return false; + } + if (children_.size() == 0) { + return true; + } + + bool isLayoutTreeEqual = true; + YGNodeRef otherNodeChildren = nullptr; + for (std::vector::size_type i = 0; i < children_.size(); ++i) { + otherNodeChildren = node.children_[i]; + isLayoutTreeEqual = + children_[i]->isLayoutTreeEqualToNode(*otherNodeChildren); + if (!isLayoutTreeEqual) { + return false; + } + } + return isLayoutTreeEqual; +} diff --git a/ReactCommon/yoga/yoga/YGNode.h b/ReactCommon/yoga/yoga/YGNode.h index a639bf901c54e8..79af81bcccac15 100644 --- a/ReactCommon/yoga/yoga/YGNode.h +++ b/ReactCommon/yoga/yoga/YGNode.h @@ -129,6 +129,10 @@ struct YGNode { const float mainSize, const float crossSize, const float parentWidth); + void setAndPropogateUseLegacyFlag(bool useLegacyFlag); + void setLayoutDoesLegacyFlagAffectsLayout(bool doesLegacyFlagAffectsLayout); + void setLayoutDidUseLegacyFlag(bool didUseLegacyFlag); + void markDirtyAndPropogateDownwards(); // Other methods YGValue marginLeadingValue(const YGFlexDirection axis) const; @@ -150,4 +154,6 @@ struct YGNode { float resolveFlexGrow(); float resolveFlexShrink(); bool isNodeFlexible(); + bool didUseLegacyFlag(); + bool isLayoutTreeEqualToNode(const YGNode& node) const; }; diff --git a/ReactCommon/yoga/yoga/Yoga-internal.h b/ReactCommon/yoga/yoga/Yoga-internal.h index 081c93445338cc..5106d971b64025 100644 --- a/ReactCommon/yoga/yoga/Yoga-internal.h +++ b/ReactCommon/yoga/yoga/Yoga-internal.h @@ -44,7 +44,7 @@ bool YGValueArrayEqual( return areEqual; } -typedef struct YGCachedMeasurement { +struct YGCachedMeasurement { float availableWidth; float availableHeight; YGMeasureMode widthMeasureMode; @@ -52,7 +52,35 @@ typedef struct YGCachedMeasurement { float computedWidth; float computedHeight; -} YGCachedMeasurement; + + bool operator==(YGCachedMeasurement measurement) const { + bool isEqual = widthMeasureMode == measurement.widthMeasureMode && + heightMeasureMode == measurement.heightMeasureMode; + + if (!std::isnan(availableWidth) || + !std::isnan(measurement.availableWidth)) { + isEqual = isEqual && availableWidth == measurement.availableWidth; + } + if (!std::isnan(availableHeight) || + !std::isnan(measurement.availableHeight)) { + isEqual = isEqual && availableHeight == measurement.availableHeight; + } + if (!std::isnan(computedWidth) || !std::isnan(measurement.computedWidth)) { + isEqual = isEqual && computedWidth == measurement.computedWidth; + } + if (!std::isnan( + computedHeight || !std::isnan(measurement.computedHeight))) { + isEqual = isEqual && computedHeight == measurement.computedHeight; + } + + return availableWidth == measurement.availableWidth && + availableHeight == measurement.availableHeight && + widthMeasureMode == measurement.widthMeasureMode && + heightMeasureMode == measurement.heightMeasureMode && + computedWidth == measurement.computedWidth && + computedHeight == measurement.computedHeight; + } +}; // This value was chosen based on empiracle data. Even the most complicated // layouts should not require more than 16 entries to fit within the cache. @@ -80,6 +108,44 @@ struct YGLayout { std::array measuredDimensions; YGCachedMeasurement cachedLayout; + bool didUseLegacyFlag; + bool doesLegacyStretchFlagAffectsLayout; + + bool operator==(YGLayout layout) const { + bool isEqual = position == layout.position && + dimensions == layout.dimensions && margin == layout.margin && + border == layout.border && padding == layout.padding && + direction == layout.direction && hadOverflow == layout.hadOverflow && + lastParentDirection == layout.lastParentDirection && + nextCachedMeasurementsIndex == layout.nextCachedMeasurementsIndex && + cachedLayout == layout.cachedLayout; + + for (uint32_t i = 0; i < YG_MAX_CACHED_RESULT_COUNT && isEqual; ++i) { + isEqual = + isEqual && cachedMeasurements[i] == layout.cachedMeasurements[i]; + } + + if (!YGFloatIsUndefined(computedFlexBasis) || + !YGFloatIsUndefined(layout.computedFlexBasis)) { + isEqual = isEqual && (computedFlexBasis == layout.computedFlexBasis); + } + if (!YGFloatIsUndefined(measuredDimensions[0]) || + !YGFloatIsUndefined(layout.measuredDimensions[0])) { + isEqual = + isEqual && (measuredDimensions[0] == layout.measuredDimensions[0]); + } + if (!YGFloatIsUndefined(measuredDimensions[1]) || + !YGFloatIsUndefined(layout.measuredDimensions[1])) { + isEqual = + isEqual && (measuredDimensions[1] == layout.measuredDimensions[1]); + } + + return isEqual; + } + + bool operator!=(YGLayout layout) const { + return !(*this == layout); + } }; struct YGStyle { @@ -150,7 +216,7 @@ struct YGStyle { } }; -typedef struct YGConfig { +struct YGConfig { bool experimentalFeatures[YGExperimentalFeatureCount + 1]; bool useWebDefaults; bool useLegacyStretchBehaviour; @@ -158,7 +224,7 @@ typedef struct YGConfig { YGLogger logger; YGNodeClonedFunc cloneNodeCallback; void* context; -} YGConfig; +}; #define YG_UNDEFINED_VALUES \ { .value = YGUndefined, .unit = YGUnitUndefined } @@ -272,6 +338,8 @@ static const YGLayout gYGNodeLayoutDefaults = { .computedWidth = -1, .computedHeight = -1, }, + .didUseLegacyFlag = false, + .doesLegacyStretchFlagAffectsLayout = false, }; extern bool YGFloatsEqual(const float a, const float b); diff --git a/ReactCommon/yoga/yoga/Yoga.cpp b/ReactCommon/yoga/yoga/Yoga.cpp index d31d63a4dd3cf5..c5e1501174fc77 100644 --- a/ReactCommon/yoga/yoga/Yoga.cpp +++ b/ReactCommon/yoga/yoga/Yoga.cpp @@ -211,6 +211,10 @@ bool YGNodeIsDirty(YGNodeRef node) { return node->isDirty(); } +bool YGNodeLayoutGetDidUseLegacyFlag(const YGNodeRef node) { + return node->didUseLegacyFlag(); +} + int32_t gNodeInstanceCount = 0; int32_t gConfigInstanceCount = 0; @@ -243,6 +247,29 @@ YGNodeRef YGNodeClone(YGNodeRef oldNode) { return node; } +static YGNodeRef YGNodeDeepClone(YGNodeRef oldNode) { + YGNodeRef node = YGNodeClone(oldNode); + YGVector vec = YGVector(); + vec.reserve(oldNode->getChildren().size()); + YGNodeRef childNode = nullptr; + for (auto& item : oldNode->getChildren()) { + childNode = YGNodeDeepClone(item); + childNode->setParent(node); + vec.push_back(childNode); + } + node->setChildren(vec); + + if (oldNode->getNextChild() != nullptr) { + node->setNextChild(YGNodeDeepClone(oldNode->getNextChild())); + } + + if (node->getConfig() != nullptr) { + node->setConfig(new YGConfig(*node->getConfig())); + } + + return node; +} + void YGNodeFree(const YGNodeRef node) { if (node->getParent()) { node->getParent()->removeChild(node); @@ -1975,10 +2002,15 @@ static void YGNodelayoutImpl(const YGNodeRef node, } else { if (!node->getConfig()->useLegacyStretchBehaviour && (totalFlexGrowFactors == 0 || node->resolveFlexGrow() == 0)) { - // If we don't have any children to flex or we can't flex the node itself, - // space we've used is all space we need. Root node also should be shrunk to minimum + // If we don't have any children to flex or we can't flex the node + // itself, space we've used is all space we need. Root node also + // should be shrunk to minimum availableInnerMainDim = sizeConsumedOnCurrentLine; } + + if (node->getConfig()->useLegacyStretchBehaviour) { + node->setLayoutDidUseLegacyFlag(true); + } sizeBasedOnContent = !node->getConfig()->useLegacyStretchBehaviour; } } @@ -3330,7 +3362,6 @@ void YGNodeCalculateLayout(const YGNodeRef node, // input // parameters don't change. gCurrentGenerationCount++; - node->resolveDimension(); float width = YGUndefined; YGMeasureMode widthMeasureMode = YGMeasureModeUndefined; @@ -3396,6 +3427,59 @@ void YGNodeCalculateLayout(const YGNodeRef node, YGPrintOptionsStyle)); } } + + bool didUseLegacyFlag = node->didUseLegacyFlag(); + + // We want to get rid off `useLegacyStretchBehaviour` from YGConfig. But we + // aren't sure whether client's of yoga have gotten rid off this flag or not. + // So logging this in YGLayout would help to find out the call sites depending + // on this flag. This check would be removed once we are sure no one is + // dependent on this flag anymore. + if (didUseLegacyFlag) { + const YGNodeRef originalNode = YGNodeDeepClone(node); + originalNode->resolveDimension(); + // Recursively mark nodes as dirty + originalNode->markDirtyAndPropogateDownwards(); + gCurrentGenerationCount++; + // Rerun the layout, and calculate the diff + originalNode->setAndPropogateUseLegacyFlag(false); + if (YGLayoutNodeInternal( + originalNode, + width, + height, + parentDirection, + widthMeasureMode, + heightMeasureMode, + parentWidth, + parentHeight, + true, + "initial", + originalNode->getConfig())) { + originalNode->setPosition( + originalNode->getLayout().direction, + parentWidth, + parentHeight, + parentWidth); + YGRoundToPixelGrid( + originalNode, + originalNode->getConfig()->pointScaleFactor, + 0.0f, + 0.0f); + + // Set whether the two layouts are different or not. + node->setLayoutDoesLegacyFlagAffectsLayout( + !originalNode->isLayoutTreeEqualToNode(*node)); + + if (gPrintTree) { + YGNodePrint( + originalNode, + (YGPrintOptions)( + YGPrintOptionsLayout | YGPrintOptionsChildren | + YGPrintOptionsStyle)); + } + } + YGNodeFreeRecursive(originalNode); + } } void YGConfigSetLogger(const YGConfigRef config, YGLogger logger) { diff --git a/ReactCommon/yoga/yoga/Yoga.h b/ReactCommon/yoga/yoga/Yoga.h index df16fce4cb3002..ab5e1f0469f32e 100644 --- a/ReactCommon/yoga/yoga/Yoga.h +++ b/ReactCommon/yoga/yoga/Yoga.h @@ -175,6 +175,7 @@ void YGNodeSetHasNewLayout(YGNodeRef node, bool hasNewLayout); YGNodeType YGNodeGetNodeType(YGNodeRef node); void YGNodeSetNodeType(YGNodeRef node, YGNodeType nodeType); bool YGNodeIsDirty(YGNodeRef node); +bool YGNodeLayoutGetDidUseLegacyFlag(const YGNodeRef node); YG_NODE_STYLE_PROPERTY(YGDirection, Direction, direction); YG_NODE_STYLE_PROPERTY(YGFlexDirection, FlexDirection, flexDirection);