in vnext/ReactCommon/Yoga.cpp [3753:4019]
bool YGLayoutNodeInternal(
const YGNodeRef node,
const float availableWidth,
const float availableHeight,
const YGDirection ownerDirection,
const YGMeasureMode widthMeasureMode,
const YGMeasureMode heightMeasureMode,
const float ownerWidth,
const float ownerHeight,
const bool performLayout,
const LayoutPassReason reason,
const YGConfigRef config,
LayoutData &layoutMarkerData,
void *const layoutContext,
uint32_t depth,
const uint32_t generationCount) {
YGLayout *layout = &node->getLayout();
depth++;
const bool needToVisitNode =
(node->isDirty() && layout->generationCount != generationCount) ||
layout->lastOwnerDirection != ownerDirection;
if (needToVisitNode) {
// Invalidate the cached results.
layout->nextCachedMeasurementsIndex = 0;
layout->cachedLayout.availableWidth = -1;
layout->cachedLayout.availableHeight = -1;
layout->cachedLayout.widthMeasureMode = YGMeasureModeUndefined;
layout->cachedLayout.heightMeasureMode = YGMeasureModeUndefined;
layout->cachedLayout.computedWidth = -1;
layout->cachedLayout.computedHeight = -1;
}
YGCachedMeasurement *cachedResults = nullptr;
// Determine whether the results are already cached. We maintain a separate
// cache for layouts and measurements. A layout operation modifies the
// positions and dimensions for nodes in the subtree. The algorithm assumes
// that each node gets layed out a maximum of one time per tree layout, but
// multiple measurements may be required to resolve all of the flex
// dimensions. We handle nodes with measure functions specially here because
// they are the most expensive to measure, so it's worth avoiding redundant
// measurements if at all possible.
if (node->hasMeasureFunc()) {
const float marginAxisRow =
node->getMarginForAxis(YGFlexDirectionRow, ownerWidth).unwrap();
const float marginAxisColumn =
node->getMarginForAxis(YGFlexDirectionColumn, ownerWidth).unwrap();
// First, try to use the layout cache.
if (YGNodeCanUseCachedMeasurement(
widthMeasureMode,
availableWidth,
heightMeasureMode,
availableHeight,
layout->cachedLayout.widthMeasureMode,
layout->cachedLayout.availableWidth,
layout->cachedLayout.heightMeasureMode,
layout->cachedLayout.availableHeight,
layout->cachedLayout.computedWidth,
layout->cachedLayout.computedHeight,
marginAxisRow,
marginAxisColumn,
config)) {
cachedResults = &layout->cachedLayout;
} else {
// Try to use the measurement cache.
for (uint32_t i = 0; i < layout->nextCachedMeasurementsIndex; i++) {
if (YGNodeCanUseCachedMeasurement(
widthMeasureMode,
availableWidth,
heightMeasureMode,
availableHeight,
layout->cachedMeasurements[i].widthMeasureMode,
layout->cachedMeasurements[i].availableWidth,
layout->cachedMeasurements[i].heightMeasureMode,
layout->cachedMeasurements[i].availableHeight,
layout->cachedMeasurements[i].computedWidth,
layout->cachedMeasurements[i].computedHeight,
marginAxisRow,
marginAxisColumn,
config)) {
cachedResults = &layout->cachedMeasurements[i];
break;
}
}
}
} else if (performLayout) {
if (YGFloatsEqual(layout->cachedLayout.availableWidth, availableWidth) &&
YGFloatsEqual(layout->cachedLayout.availableHeight, availableHeight) &&
layout->cachedLayout.widthMeasureMode == widthMeasureMode &&
layout->cachedLayout.heightMeasureMode == heightMeasureMode) {
cachedResults = &layout->cachedLayout;
}
} else {
for (uint32_t i = 0; i < layout->nextCachedMeasurementsIndex; i++) {
if (YGFloatsEqual(
layout->cachedMeasurements[i].availableWidth, availableWidth) &&
YGFloatsEqual(
layout->cachedMeasurements[i].availableHeight, availableHeight) &&
layout->cachedMeasurements[i].widthMeasureMode == widthMeasureMode &&
layout->cachedMeasurements[i].heightMeasureMode ==
heightMeasureMode) {
cachedResults = &layout->cachedMeasurements[i];
break;
}
}
}
if (!needToVisitNode && cachedResults != nullptr) {
layout->measuredDimensions[YGDimensionWidth] = cachedResults->computedWidth;
layout->measuredDimensions[YGDimensionHeight] =
cachedResults->computedHeight;
(performLayout ? layoutMarkerData.cachedLayouts
: layoutMarkerData.cachedMeasures) += 1;
if (gPrintChanges && gPrintSkips) {
Log::log(
node,
YGLogLevelVerbose,
nullptr,
"%s%d.{[skipped] ",
YGSpacer(depth),
depth);
node->print(layoutContext);
Log::log(
node,
YGLogLevelVerbose,
nullptr,
"wm: %s, hm: %s, aw: %f ah: %f => d: (%f, %f) %s\n",
YGMeasureModeName(widthMeasureMode, performLayout),
YGMeasureModeName(heightMeasureMode, performLayout),
availableWidth,
availableHeight,
cachedResults->computedWidth,
cachedResults->computedHeight,
LayoutPassReasonToString(reason));
}
} else {
if (gPrintChanges) {
Log::log(
node,
YGLogLevelVerbose,
nullptr,
"%s%d.{%s",
YGSpacer(depth),
depth,
needToVisitNode ? "*" : "");
node->print(layoutContext);
Log::log(
node,
YGLogLevelVerbose,
nullptr,
"wm: %s, hm: %s, aw: %f ah: %f %s\n",
YGMeasureModeName(widthMeasureMode, performLayout),
YGMeasureModeName(heightMeasureMode, performLayout),
availableWidth,
availableHeight,
LayoutPassReasonToString(reason));
}
YGNodelayoutImpl(
node,
availableWidth,
availableHeight,
ownerDirection,
widthMeasureMode,
heightMeasureMode,
ownerWidth,
ownerHeight,
performLayout,
config,
layoutMarkerData,
layoutContext,
depth,
generationCount,
reason);
if (gPrintChanges) {
Log::log(
node,
YGLogLevelVerbose,
nullptr,
"%s%d.}%s",
YGSpacer(depth),
depth,
needToVisitNode ? "*" : "");
node->print(layoutContext);
Log::log(
node,
YGLogLevelVerbose,
nullptr,
"wm: %s, hm: %s, d: (%f, %f) %s\n",
YGMeasureModeName(widthMeasureMode, performLayout),
YGMeasureModeName(heightMeasureMode, performLayout),
layout->measuredDimensions[YGDimensionWidth],
layout->measuredDimensions[YGDimensionHeight],
LayoutPassReasonToString(reason));
}
layout->lastOwnerDirection = ownerDirection;
if (cachedResults == nullptr) {
if (layout->nextCachedMeasurementsIndex + 1 >
(uint32_t)layoutMarkerData.maxMeasureCache) {
layoutMarkerData.maxMeasureCache =
layout->nextCachedMeasurementsIndex + 1;
}
if (layout->nextCachedMeasurementsIndex == YG_MAX_CACHED_RESULT_COUNT) {
if (gPrintChanges) {
Log::log(node, YGLogLevelVerbose, nullptr, "Out of cache entries!\n");
}
layout->nextCachedMeasurementsIndex = 0;
}
YGCachedMeasurement *newCacheEntry;
if (performLayout) {
// Use the single layout cache entry.
newCacheEntry = &layout->cachedLayout;
} else {
// Allocate a new measurement cache entry.
newCacheEntry =
&layout->cachedMeasurements[layout->nextCachedMeasurementsIndex];
layout->nextCachedMeasurementsIndex++;
}
newCacheEntry->availableWidth = availableWidth;
newCacheEntry->availableHeight = availableHeight;
newCacheEntry->widthMeasureMode = widthMeasureMode;
newCacheEntry->heightMeasureMode = heightMeasureMode;
newCacheEntry->computedWidth =
layout->measuredDimensions[YGDimensionWidth];
newCacheEntry->computedHeight =
layout->measuredDimensions[YGDimensionHeight];
}
}
if (performLayout) {
node->setLayoutDimension(
node->getLayout().measuredDimensions[YGDimensionWidth],
YGDimensionWidth);
node->setLayoutDimension(
node->getLayout().measuredDimensions[YGDimensionHeight],
YGDimensionHeight);
node->setHasNewLayout(true);
node->setDirty(false);
}
layout->generationCount = generationCount;
LayoutType layoutType;
if (performLayout) {
layoutType = !needToVisitNode && cachedResults == &layout->cachedLayout
? LayoutType::kCachedLayout
: LayoutType::kLayout;
} else {
layoutType = cachedResults != nullptr ? LayoutType::kCachedMeasure
: LayoutType::kMeasure;
}
Event::publish<Event::NodeLayout>(node, {layoutType, layoutContext});
return (needToVisitNode || cachedResults == nullptr);
}