Heuristics for skipping calls to the measure function

Introduced heuristics that enable css-layout to avoid
calling measure functions under a number of conditions.
This enables us to save time by skipping unnecessary
measurements.
This commit is contained in:
Adam Comella
2016-05-10 13:56:08 -07:00
parent 8779d942ea
commit 8177bfe702
11 changed files with 514 additions and 14 deletions

View File

@@ -281,7 +281,42 @@ namespace Facebook.CSSLayout
setPosition(node, node.layout.direction);
}
}
internal static boolean canUseCachedMeasurement(float availableWidth, float availableHeight,
float marginRow, float marginColumn,
CSSMeasureMode widthMeasureMode, CSSMeasureMode heightMeasureMode,
CSSCachedMeasurement cachedLayout)
{
// Is it an exact match?
if (FloatUtil.floatsEqual(cachedLayout.availableWidth, availableWidth) &&
FloatUtil.floatsEqual(cachedLayout.availableHeight, availableHeight) &&
cachedLayout.widthMeasureMode == widthMeasureMode &&
cachedLayout.heightMeasureMode == heightMeasureMode)
{
return true;
}
// If the width is an exact match, try a fuzzy match on the height.
if (FloatUtil.floatsEqual(cachedLayout.availableWidth, availableWidth) &&
cachedLayout.widthMeasureMode == widthMeasureMode &&
heightMeasureMode == CSSMeasureMode.Exactly &&
FloatUtil.floatsEqual(availableHeight - marginColumn, cachedLayout.computedHeight))
{
return true;
}
// If the height is an exact match, try a fuzzy match on the width.
if (FloatUtil.floatsEqual(cachedLayout.availableHeight, availableHeight) &&
cachedLayout.heightMeasureMode == heightMeasureMode &&
widthMeasureMode == CSSMeasureMode.Exactly &&
FloatUtil.floatsEqual(availableWidth - marginRow, cachedLayout.computedWidth))
{
return true;
}
return false;
}
//
// This is a wrapper around the layoutNodeImpl function. It determines
// whether the layout request is redundant and can be skipped.
@@ -312,7 +347,38 @@ namespace Facebook.CSSLayout
// 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.
if (performLayout)
// 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 (isMeasureDefined(node))
{
float marginAxisRow =
node.style.margin.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_ROW], leading[CSS_FLEX_DIRECTION_ROW]) +
node.style.margin.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_ROW], trailing[CSS_FLEX_DIRECTION_ROW]);
float marginAxisColumn =
node.style.margin.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_COLUMN], leading[CSS_FLEX_DIRECTION_COLUMN]) +
node.style.margin.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_COLUMN], trailing[CSS_FLEX_DIRECTION_COLUMN]);
// First, try to use the layout cache.
if (canUseCachedMeasurement(availableWidth, availableHeight, marginAxisRow, marginAxisColumn,
widthMeasureMode, heightMeasureMode, layout.cachedLayout))
{
cachedResults = layout.cachedLayout;
}
else
{
// Try to use the measurement cache.
for (int i = 0; i < layout.nextCachedMeasurementsIndex; i++)
{
if (canUseCachedMeasurement(availableWidth, availableHeight, marginAxisRow, marginAxisColumn,
widthMeasureMode, heightMeasureMode, layout.cachedMeasurements[i]))
{
cachedResults = layout.cachedMeasurements[i];
break;
}
}
}
}
else if (performLayout)
{
if (FloatUtil.floatsEqual(layout.cachedLayout.availableWidth, availableWidth) &&
FloatUtil.floatsEqual(layout.cachedLayout.availableHeight, availableHeight) &&