Avoid too many tiles and out of tile memory for huge layer with tiny scale
Some web sites create huge layers with tiny scale. Previously because
the scale was clamped to minimum scale, causing too many tiles with too
big scale and out of tile memory.
This CL contains the following changes:
1. Remove LayerTreeSettings::minimum_contents_scale. It was added when
we used layer coordinates in PicturePile and needed to expand record /
invalidation rects by 1/scale pixels to ensure all affected texels were
covereda. The minimum scale avoided problems caused by too many pixels
to expand when the ideal scale was too small. Now we use texel
coordinates in PictureLayerTiling and all rects in layer coordinates
will be mapped into texel coordinates by scaling the rect then getting
the enclosing int rect, which ensures coverage of affected texels, so
the minimum scale is no longer useful.
2. Adjust scale clamping for will-change:transform.
a) If the ideal scale is much smaller than the native scale, don't clamp
to the native scale;
b) Update raster contents scale only if it's too small compared to the
ideal contents scale. Without this, we would update tiny raster contents
scale when the ideal scale changes which would defeat the optimization
for will-change:transform.
Bug: 1132991
Change-Id: I3a31f654fe4a5ccc925aaad3a2b75748c4e37164
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2438756
Reviewed-by: vmpstr <vmpstr@chromium.org>
Commit-Queue: Xianzhu Wang <wangxianzhu@chromium.org>
Cr-Commit-Position: refs/heads/master@{#812867}
diff --git a/cc/layers/picture_layer_impl.cc b/cc/layers/picture_layer_impl.cc
index bd6923c..928508b1 100644
--- a/cc/layers/picture_layer_impl.cc
+++ b/cc/layers/picture_layer_impl.cc
@@ -60,6 +60,10 @@
// scales.
const float kMaxIdealContentsScale = 10000.f;
+// We try to avoid raster scale adjustment for will-change:transform for
+// performance, unless the scale is too small compared to the ideal scale.
+const float kMinScaleRatioForWillChangeTransform = 0.1f;
+
// Intersect rects which may have right() and bottom() that overflow integer
// boundaries. This code is similar to gfx::Rect::Intersect with the exception
// that the types are promoted to int64_t when there is a chance of overflow.
@@ -1343,9 +1347,10 @@
}
// Don't update will-change: transform layers if the raster contents scale is
- // at least the native scale (otherwise, we'd need to clamp it).
+ // bigger than the minimum scale.
if (HasWillChangeTransformHint() &&
- raster_contents_scale_ >= raster_page_scale_ * raster_device_scale_) {
+ raster_contents_scale_ >=
+ MinimumRasterContentsScaleForWillChangeTransform()) {
return false;
}
@@ -1504,13 +1509,10 @@
}
}
- // Clamp will-change: transform layers to be at least the native scale.
if (HasWillChangeTransformHint()) {
- float min_desired_scale = raster_device_scale_ * raster_page_scale_;
- if (raster_contents_scale_ < min_desired_scale) {
- raster_contents_scale_ = min_desired_scale;
- raster_page_scale_ = 1.f;
- }
+ raster_contents_scale_ =
+ std::max(raster_contents_scale_,
+ MinimumRasterContentsScaleForWillChangeTransform());
}
raster_contents_scale_ =
@@ -1570,6 +1572,23 @@
SanityCheckTilingState();
}
+float PictureLayerImpl::MinimumRasterContentsScaleForWillChangeTransform()
+ const {
+ DCHECK(HasWillChangeTransformHint());
+ // Don't let the scale too small compared to the ideal scale.
+ float min_scale =
+ ideal_contents_scale_ * kMinScaleRatioForWillChangeTransform;
+ float native_scale = ideal_device_scale_ * ideal_page_scale_;
+ // Clamp will-change: transform layers to be at least the native scale,
+ // unless the scale is too small to avoid too many tiles using too much tile
+ // memory.
+ if (ideal_contents_scale_ <
+ native_scale * kMinScaleRatioForWillChangeTransform) {
+ return min_scale;
+ }
+ return std::max(native_scale, min_scale);
+}
+
bool PictureLayerImpl::CalculateRasterTranslation(
gfx::Vector2dF& raster_translation) const {
// If this setting is set, the client (e.g. the Chromium UI) is sure that it
@@ -1631,24 +1650,13 @@
}
float PictureLayerImpl::MinimumContentsScale() const {
- float setting_min = layer_tree_impl()->settings().minimum_contents_scale;
-
// If the contents scale is less than 1 / width (also for height),
// then it will end up having less than one pixel of content in that
// dimension. Bump the minimum contents scale up in this case to prevent
// this from happening.
int min_dimension = std::min(raster_source_->GetSize().width(),
raster_source_->GetSize().height());
- if (!min_dimension)
- return setting_min;
-
- // Directly composited images may result in contents scales that are
- // less than the configured setting. We allow this lower scale so that we
- // can raster at the intrinsic image size.
- const float inverse_min_dimension = 1.f / min_dimension;
- return (directly_composited_image_size_.has_value())
- ? inverse_min_dimension
- : std::max(inverse_min_dimension, setting_min);
+ return min_dimension ? 1.f / min_dimension : 1.f;
}
float PictureLayerImpl::MaximumContentsScale() const {
diff --git a/cc/layers/picture_layer_impl.h b/cc/layers/picture_layer_impl.h
index ea8bcab..be63a23 100644
--- a/cc/layers/picture_layer_impl.h
+++ b/cc/layers/picture_layer_impl.h
@@ -177,6 +177,7 @@
void AddLowResolutionTilingIfNeeded();
bool ShouldAdjustRasterScale() const;
void RecalculateRasterScales();
+ float MinimumRasterContentsScaleForWillChangeTransform() const;
// Returns false if raster translation is not applicable.
bool CalculateRasterTranslation(gfx::Vector2dF& raster_translation) const;
void CleanUpTilingsOnActiveLayer(
diff --git a/cc/layers/picture_layer_impl_unittest.cc b/cc/layers/picture_layer_impl_unittest.cc
index b41fb25a..627e929 100644
--- a/cc/layers/picture_layer_impl_unittest.cc
+++ b/cc/layers/picture_layer_impl_unittest.cc
@@ -846,7 +846,7 @@
EXPECT_FLOAT_EQ(
0.24f, active_layer()->tilings()->tiling_at(0)->contents_scale_key());
EXPECT_FLOAT_EQ(
- 0.0625f, active_layer()->tilings()->tiling_at(1)->contents_scale_key());
+ 0.06f, active_layer()->tilings()->tiling_at(1)->contents_scale_key());
// Ensure UpdateTiles won't remove any tilings.
active_layer()->MarkAllTilingsUsed();
@@ -868,7 +868,7 @@
EXPECT_FLOAT_EQ(
0.12f, active_layer()->tilings()->tiling_at(1)->contents_scale_key());
EXPECT_FLOAT_EQ(
- 0.0625, active_layer()->tilings()->tiling_at(2)->contents_scale_key());
+ 0.06f, active_layer()->tilings()->tiling_at(2)->contents_scale_key());
// Ensure UpdateTiles won't remove any tilings.
active_layer()->MarkAllTilingsUsed();
@@ -878,10 +878,12 @@
SetContentsScaleOnBothLayers(0.1f, 1.0f, 0.1f, 1.0f, 0.f, false);
ASSERT_EQ(3u, active_layer()->tilings()->num_tilings());
- // Zoom in. 0.25(desired_scale) should be snapped to 0.24 during zoom-in
- // because 0.25(desired_scale) is within the ratio(1.2).
- SetContentsScaleOnBothLayers(0.25f, 1.0f, 0.25f, 1.0f, 0.f, false);
+ // Zoom in. 0.22(desired_scale) should be snapped to 0.24 during zoom-in
+ // because 0.22(desired_scale) is within the ratio(1.2).
+ SetContentsScaleOnBothLayers(0.22f, 1.0f, 0.22f, 1.0f, 0.f, false);
ASSERT_EQ(3u, active_layer()->tilings()->num_tilings());
+ EXPECT_FLOAT_EQ(
+ 0.24f, active_layer()->tilings()->tiling_at(0)->contents_scale_key());
// Zoom in a lot. Since we move in factors of two, we should get a scale that
// is a power of 2 times 0.24.
@@ -1320,8 +1322,7 @@
// Resize even larger, so that the scale would be smaller than the minimum
// contents scale. Then the layer should no longer have any tiling.
- float min_contents_scale = host_impl()->settings().minimum_contents_scale;
- gfx::Size extra_huge_bounds(max_texture_size / min_contents_scale + 1, 10);
+ gfx::Size extra_huge_bounds(max_texture_size * 10 + 1, 10);
scoped_refptr<FakeRasterSource> extra_huge_raster_source =
FakeRasterSource::CreateFilled(extra_huge_bounds);
@@ -3526,6 +3527,22 @@
starting_animation_scale, animating_transform);
EXPECT_BOTH_EQ(HighResTiling()->contents_scale_key(), 1.5f);
+ // ... unless the difference is very big.
+ contents_scale = 20.f;
+
+ SetContentsScaleOnBothLayers(contents_scale, device_scale, page_scale,
+ maximum_animation_scale,
+ starting_animation_scale, animating_transform);
+ EXPECT_BOTH_EQ(HighResTiling()->contents_scale_key(), 20.f);
+
+ // And we don't downscale from a higher scale.
+ contents_scale = 2.f;
+
+ SetContentsScaleOnBothLayers(contents_scale, device_scale, page_scale,
+ maximum_animation_scale,
+ starting_animation_scale, animating_transform);
+ EXPECT_BOTH_EQ(HighResTiling()->contents_scale_key(), 20.f);
+
// Disabling the will-change hint will once again make the raster scale update
// with the ideal scale.
GetTransformNode(active_layer())->will_change_transform = false;
@@ -3539,6 +3556,62 @@
EXPECT_BOTH_EQ(HighResTiling()->contents_scale_key(), 3.f);
}
+TEST_F(LegacySWPictureLayerImplTest, TinyRasterScale) {
+ gfx::Size tile_size(host_impl()->settings().default_tile_size);
+ SetupDefaultTrees(tile_size);
+
+ ResetTilingsAndRasterScales();
+
+ float contents_scale = 0.01f;
+ float device_scale = 1.5f;
+ float page_scale = 1.f;
+ float maximum_animation_scale = 1.f;
+ float starting_animation_scale = 0.f;
+ bool animating_transform = false;
+
+ SetContentsScaleOnBothLayers(contents_scale, device_scale, page_scale,
+ maximum_animation_scale,
+ starting_animation_scale, animating_transform);
+ EXPECT_BOTH_EQ(HighResTiling()->contents_scale_key(), 0.01f);
+
+ // If we change the layer contents scale after setting will change
+ // will, then it will be updated if it's below the minimum scale (page scale *
+ // device scale).
+ GetTransformNode(active_layer())->will_change_transform = true;
+ GetTransformNode(pending_layer())->will_change_transform = true;
+
+ SetContentsScaleOnBothLayers(contents_scale, device_scale, page_scale,
+ maximum_animation_scale,
+ starting_animation_scale, animating_transform);
+ // The scale is clamped to the native scale.
+ EXPECT_BOTH_EQ(HighResTiling()->contents_scale_key(), 0.01f);
+
+ // Further changes to the source scale will no longer be reflected in the
+ // contents scale.
+ contents_scale = 0.02f;
+
+ SetContentsScaleOnBothLayers(contents_scale, device_scale, page_scale,
+ maximum_animation_scale,
+ starting_animation_scale, animating_transform);
+ EXPECT_BOTH_EQ(HighResTiling()->contents_scale_key(), 0.01f);
+
+ // ... unless the difference is very big.
+ contents_scale = 0.12f;
+
+ SetContentsScaleOnBothLayers(contents_scale, device_scale, page_scale,
+ maximum_animation_scale,
+ starting_animation_scale, animating_transform);
+ EXPECT_BOTH_EQ(HighResTiling()->contents_scale_key(), 0.12f);
+
+ // Bigger scale will be clamped to the native scale.
+ contents_scale = 0.5f;
+
+ SetContentsScaleOnBothLayers(contents_scale, device_scale, page_scale,
+ maximum_animation_scale,
+ starting_animation_scale, animating_transform);
+ EXPECT_BOTH_EQ(HighResTiling()->contents_scale_key(), 1.5f);
+}
+
TEST_F(LegacySWPictureLayerImplTest,
AnimationChangeRespectsWillChangeTransformHint) {}
diff --git a/cc/trees/layer_tree_host_unittest.cc b/cc/trees/layer_tree_host_unittest.cc
index ddb268c..e09aea2 100644
--- a/cc/trees/layer_tree_host_unittest.cc
+++ b/cc/trees/layer_tree_host_unittest.cc
@@ -2897,13 +2897,6 @@
// its damage is preserved until the next time it is drawn.
class LayerTreeHostTestUndrawnLayersDamageLater : public LayerTreeHostTest {
public:
- void InitializeSettings(LayerTreeSettings* settings) override {
- // If we don't set the minimum contents scale, it's harder to verify whether
- // the damage we get is correct. For other scale amounts, please see
- // LayerTreeHostTestDamageWithScale.
- settings->minimum_contents_scale = 1.f;
- }
-
void SetupTree() override {
root_layer_ = FakePictureLayer::Create(&client_);
root_layer_->SetIsDrawable(true);
diff --git a/cc/trees/layer_tree_settings.h b/cc/trees/layer_tree_settings.h
index e5a5865..64fe3c1 100644
--- a/cc/trees/layer_tree_settings.h
+++ b/cc/trees/layer_tree_settings.h
@@ -64,7 +64,6 @@
base::TimeDelta scroll_animation_duration_for_testing;
bool timeout_and_draw_when_animation_checkerboards = true;
bool layers_always_allowed_lcd_text = false;
- float minimum_contents_scale = 0.0625f;
float low_res_contents_scale_factor = 0.25f;
float top_controls_show_threshold = 0.5f;
float top_controls_hide_threshold = 0.5f;