Improve heuristic for cache re-use

This commit is contained in:
Emil Sjolander
2016-06-03 15:38:08 +01:00
parent 45e595e6ae
commit 7b0c008300
12 changed files with 425 additions and 133 deletions

88
dist/css-layout.h vendored
View File

@@ -180,6 +180,7 @@ struct css_node {
void (*print)(void *context);
struct css_node* (*get_child)(void *context, int i);
bool (*is_dirty)(void *context);
bool (*is_text_node)(void *context);
void *context;
};
@@ -1752,35 +1753,80 @@ static const char* getModeName(css_measure_mode_t mode, bool performLayout) {
return performLayout? kLayoutModeNames[mode] : kMeasureModeNames[mode];
}
static bool canUseCachedMeasurement(float availableWidth, float availableHeight,
float marginRow, float marginColumn,
css_measure_mode_t widthMeasureMode, css_measure_mode_t heightMeasureMode,
css_cached_measurement_t cachedLayout) {
static bool canUseCachedMeasurement(
bool is_text_node,
float available_width,
float available_height,
float margin_row,
float margin_column,
css_measure_mode_t width_measure_mode,
css_measure_mode_t height_measure_mode,
css_cached_measurement_t cached_layout) {
// Is it an exact match?
if (eq(cachedLayout.available_width, availableWidth) &&
eq(cachedLayout.available_height, availableHeight) &&
cachedLayout.width_measure_mode == widthMeasureMode &&
cachedLayout.height_measure_mode == heightMeasureMode) {
bool is_height_same =
(cached_layout.height_measure_mode == CSS_MEASURE_MODE_UNDEFINED && height_measure_mode == CSS_MEASURE_MODE_UNDEFINED) ||
(cached_layout.height_measure_mode == height_measure_mode && eq(cached_layout.available_height, available_height));
bool is_width_same =
(cached_layout.width_measure_mode == CSS_MEASURE_MODE_UNDEFINED && width_measure_mode == CSS_MEASURE_MODE_UNDEFINED) ||
(cached_layout.width_measure_mode == width_measure_mode && eq(cached_layout.available_width, available_width));
if (is_height_same && is_width_same) {
return true;
}
// If the width is an exact match, try a fuzzy match on the height.
if (cachedLayout.width_measure_mode == widthMeasureMode &&
eq(cachedLayout.available_width, availableWidth) &&
heightMeasureMode == CSS_MEASURE_MODE_EXACTLY &&
eq(availableHeight - marginColumn, cachedLayout.computed_height)) {
bool is_height_valid =
(cached_layout.height_measure_mode == CSS_MEASURE_MODE_UNDEFINED && height_measure_mode == CSS_MEASURE_MODE_AT_MOST && cached_layout.computed_height <= (available_height - margin_column)) ||
(height_measure_mode == CSS_MEASURE_MODE_EXACTLY && eq(cached_layout.computed_height, available_height - margin_column));
if (is_width_same && is_height_valid) {
return true;
}
// If the height is an exact match, try a fuzzy match on the width.
if (cachedLayout.height_measure_mode == heightMeasureMode &&
eq(cachedLayout.available_height, availableHeight) &&
widthMeasureMode == CSS_MEASURE_MODE_EXACTLY &&
eq(availableWidth - marginRow, cachedLayout.computed_width)) {
bool is_width_valid =
(cached_layout.width_measure_mode == CSS_MEASURE_MODE_UNDEFINED && width_measure_mode == CSS_MEASURE_MODE_AT_MOST && cached_layout.computed_width <= (available_width - margin_row)) ||
(width_measure_mode == CSS_MEASURE_MODE_EXACTLY && eq(cached_layout.computed_width, available_width - margin_row));
if (is_height_same && is_width_valid) {
return true;
}
if (is_height_valid && is_width_valid) {
return true;
}
// We know this to be text so we can apply some more specialized heuristics.
if (is_text_node) {
if (is_width_same) {
if (height_measure_mode == CSS_MEASURE_MODE_UNDEFINED) {
// Width is the same and height is not restricted. Re-use cahced value.
return true;
}
if (height_measure_mode == CSS_MEASURE_MODE_AT_MOST &&
cached_layout.computed_height < (available_height - margin_column)) {
// Width is the same and height restriction is greater than the cached height. Re-use cached value.
return true;
}
// Width is the same but height restriction imposes smaller height than previously measured.
// Update the cached value to respect the new height restriction.
cached_layout.computed_height = available_height - margin_column;
return true;
}
if (cached_layout.width_measure_mode == CSS_MEASURE_MODE_UNDEFINED) {
if (width_measure_mode == CSS_MEASURE_MODE_UNDEFINED ||
(width_measure_mode == CSS_MEASURE_MODE_AT_MOST &&
cached_layout.computed_width <= (available_width - margin_row))) {
// Previsouly this text was measured with no width restriction, if width is now restricted
// but to a larger value than the previsouly measured width we can re-use the measurement
// as we know it will fit.
return true;
}
}
}
return false;
}
@@ -1822,13 +1868,13 @@ bool layoutNodeInternal(css_node_t* node, float availableWidth, float availableH
float marginAxisColumn = getMarginAxis(node, CSS_FLEX_DIRECTION_COLUMN);
// First, try to use the layout cache.
if (canUseCachedMeasurement(availableWidth, availableHeight, marginAxisRow, marginAxisColumn,
if (canUseCachedMeasurement(node->is_text_node && node->is_text_node(node->context), availableWidth, availableHeight, marginAxisRow, marginAxisColumn,
widthMeasureMode, heightMeasureMode, layout->cached_layout)) {
cachedResults = &layout->cached_layout;
} else {
// Try to use the measurement cache.
for (int i = 0; i < layout->next_cached_measurements_index; i++) {
if (canUseCachedMeasurement(availableWidth, availableHeight, marginAxisRow, marginAxisColumn,
if (canUseCachedMeasurement(node->is_text_node && node->is_text_node(node->context), availableWidth, availableHeight, marginAxisRow, marginAxisColumn,
widthMeasureMode, heightMeasureMode, layout->cached_measurements[i])) {
cachedResults = &layout->cached_measurements[i];
break;

BIN
dist/css-layout.jar vendored

Binary file not shown.

87
dist/css-layout.js vendored
View File

@@ -1481,35 +1481,80 @@ var computeLayout = (function() {
}
}
function canUseCachedMeasurement(availableWidth, availableHeight,
marginRow, marginColumn,
widthMeasureMode, heightMeasureMode,
cachedLayout) {
function canUseCachedMeasurement(
isTextNode,
availableWidth,
availableHeight,
marginRow,
marginColumn,
widthMeasureMode,
heightMeasureMode,
cachedLayout) {
// Is it an exact match?
if (cachedLayout.availableWidth === availableWidth &&
cachedLayout.availableHeight === availableHeight &&
cachedLayout.widthMeasureMode === widthMeasureMode &&
cachedLayout.heightMeasureMode === heightMeasureMode) {
var isHeightSame =
(cachedLayout.heightMeasureMode == CSS_MEASURE_MODE_UNDEFINED && heightMeasureMode == CSS_MEASURE_MODE_UNDEFINED) ||
(cachedLayout.heightMeasureMode == heightMeasureMode && cachedLayout.availableHeight == availableHeight);
var isWidthSame =
(cachedLayout.widthMeasureMode == CSS_MEASURE_MODE_UNDEFINED && widthMeasureMode == CSS_MEASURE_MODE_UNDEFINED) ||
(cachedLayout.widthMeasureMode == widthMeasureMode && cachedLayout.availableWidth == availableWidth);
if (isHeightSame && isWidthSame) {
return true;
}
// If the width is an exact match, try a fuzzy match on the height.
if (cachedLayout.availableWidth === availableWidth &&
cachedLayout.widthMeasureMode === widthMeasureMode &&
heightMeasureMode === CSS_MEASURE_MODE_EXACTLY &&
availableHeight - marginColumn === cachedLayout.computedHeight) {
var isHeightValid =
(cachedLayout.heightMeasureMode == CSS_MEASURE_MODE_UNDEFINED && heightMeasureMode == CSS_MEASURE_MODE_AT_MOST && cachedLayout.computedHeight <= (availableHeight - marginColumn)) ||
(heightMeasureMode == CSS_MEASURE_MODE_EXACTLY && cachedLayout.computedHeight == (availableHeight - marginColumn));
if (isWidthSame && isHeightValid) {
return true;
}
// If the height is an exact match, try a fuzzy match on the width.
if (cachedLayout.availableHeight === availableHeight &&
cachedLayout.heightMeasureMode === heightMeasureMode &&
widthMeasureMode === CSS_MEASURE_MODE_EXACTLY &&
availableWidth - marginRow === cachedLayout.computedWidth) {
var isWidthValid =
(cachedLayout.widthMeasureMode == CSS_MEASURE_MODE_UNDEFINED && widthMeasureMode == CSS_MEASURE_MODE_AT_MOST && cachedLayout.computedWidth <= (availableWidth - marginRow)) ||
(widthMeasureMode == CSS_MEASURE_MODE_EXACTLY && cachedLayout.computedWidth == (availableWidth - marginRow));
if (isHeightSame && isWidthValid) {
return true;
}
if (isHeightValid && isWidthValid) {
return true;
}
// We know this to be text so we can apply some more specialized heuristics.
if (isTextNode) {
if (isWidthSame) {
if (heightMeasureMode == CSS_MEASURE_MODE_UNDEFINED) {
// Width is the same and height is not restricted. Re-use cahced value.
return true;
}
if (heightMeasureMode == CSS_MEASURE_MODE_AT_MOST &&
cachedLayout.computedHeight < (availableHeight - marginColumn)) {
// Width is the same and height restriction is greater than the cached height. Re-use cached value.
return true;
}
// Width is the same but height restriction imposes smaller height than previously measured.
// Update the cached value to respect the new height restriction.
cachedLayout.computedHeight = availableHeight - marginColumn;
return true;
}
if (cachedLayout.widthMeasureMode == CSS_MEASURE_MODE_UNDEFINED) {
if (widthMeasureMode == CSS_MEASURE_MODE_UNDEFINED ||
(widthMeasureMode == CSS_MEASURE_MODE_AT_MOST &&
cachedLayout.computedWidth <= (availableWidth - marginRow))) {
// Previsouly this text was measured with no width restriction, if width is now restricted
// but to a larger value than the previsouly measured width we can re-use the measurement
// as we know it will fit.
return true;
}
}
}
return false;
}
@@ -1556,13 +1601,13 @@ var computeLayout = (function() {
// First, try to use the layout cache.
if (layout.cachedLayout &&
canUseCachedMeasurement(availableWidth, availableHeight, marginAxisRow, marginAxisColumn,
canUseCachedMeasurement(node.isTextNode, availableWidth, availableHeight, marginAxisRow, marginAxisColumn,
widthMeasureMode, heightMeasureMode, layout.cachedLayout)) {
cachedResults = layout.cachedLayout;
} else if (layout.cachedMeasurements) {
// Try to use the measurement cache.
for (i = 0, len = layout.cachedMeasurements.length; i < len; i++) {
if (canUseCachedMeasurement(availableWidth, availableHeight, marginAxisRow, marginAxisColumn,
if (canUseCachedMeasurement(node.isTextNode, availableWidth, availableHeight, marginAxisRow, marginAxisColumn,
widthMeasureMode, heightMeasureMode, layout.cachedMeasurements[i])) {
cachedResults = layout.cachedMeasurements[i];
break;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long