cc: Make full-pile invalidations cheap.
A PicturePile can be really huge. Normally invalidations should be
relatively small anyways. But some times we have to SetNeedsDisplay()
to invalidate the whole layer. We can make these cases fast by only
iterating through the hash_map to find the recorded tiles that exist
and invalidate them.
This takes the record time for a 20 million squared layer from over a
minute to under a millisecond.
R=enne, vmpstr
BUG=420944
Review URL: https://codereview.chromium.org/643363002
Cr-Commit-Position: refs/heads/master@{#299520}
diff --git a/cc/resources/picture_pile.cc b/cc/resources/picture_pile.cc
index 4858d24..1b6a50cd 100644
--- a/cc/resources/picture_pile.cc
+++ b/cc/resources/picture_pile.cc
@@ -378,46 +378,56 @@
}
}
- Region invalidation_expanded_to_full_tiles;
- for (Region::Iterator i(*invalidation); i.has_rect(); i.next()) {
- gfx::Rect invalid_rect = i.rect();
-
+ // Detect cases where the full pile is invalidated, in this situation we
+ // can just drop/invalidate everything.
+ if (invalidation->Contains(gfx::Rect(old_tiling_size)) ||
+ invalidation->Contains(gfx::Rect(tiling_size()))) {
+ for (auto& it : picture_map_)
+ updated = it.second.Invalidate(frame_number) || updated;
+ } else {
// Expand invalidation that is outside tiles that intersect the interest
// rect. These tiles are no longer valid and should be considerered fully
// invalid, so we can know to not keep around raster tiles that intersect
// with these recording tiles.
- gfx::Rect invalid_rect_outside_interest_rect_tiles = invalid_rect;
- // TODO(danakj): We should have a Rect-subtract-Rect-to-2-rects operator
- // instead of using Rect::Subtract which gives you the bounding box of the
- // subtraction.
- invalid_rect_outside_interest_rect_tiles.Subtract(interest_rect_over_tiles);
- invalidation_expanded_to_full_tiles.Union(tiling_.ExpandRectToTileBounds(
- invalid_rect_outside_interest_rect_tiles));
+ Region invalidation_expanded_to_full_tiles;
- // Split this inflated invalidation across tile boundaries and apply it
- // to all tiles that it touches.
- bool include_borders = true;
- for (TilingData::Iterator iter(&tiling_, invalid_rect, include_borders);
- iter;
- ++iter) {
- const PictureMapKey& key = iter.index();
+ for (Region::Iterator i(*invalidation); i.has_rect(); i.next()) {
+ gfx::Rect invalid_rect = i.rect();
- PictureMap::iterator picture_it = picture_map_.find(key);
- if (picture_it == picture_map_.end())
- continue;
+ gfx::Rect invalid_rect_outside_interest_rect_tiles = invalid_rect;
+ // TODO(danakj): We should have a Rect-subtract-Rect-to-2-rects operator
+ // instead of using Rect::Subtract which gives you the bounding box of the
+ // subtraction.
+ invalid_rect_outside_interest_rect_tiles.Subtract(
+ interest_rect_over_tiles);
+ invalidation_expanded_to_full_tiles.Union(tiling_.ExpandRectToTileBounds(
+ invalid_rect_outside_interest_rect_tiles));
- // Inform the grid cell that it has been invalidated in this frame.
- updated = picture_it->second.Invalidate(frame_number) || updated;
- // Invalidate drops the picture so the whole tile better be invalidated if
- // it won't be re-recorded below.
- DCHECK_IMPLIES(!tiling_.TileBounds(key.first, key.second)
- .Intersects(interest_rect_over_tiles),
- invalidation_expanded_to_full_tiles.Contains(
- tiling_.TileBounds(key.first, key.second)));
+ // Split this inflated invalidation across tile boundaries and apply it
+ // to all tiles that it touches.
+ bool include_borders = true;
+ for (TilingData::Iterator iter(&tiling_, invalid_rect, include_borders);
+ iter;
+ ++iter) {
+ const PictureMapKey& key = iter.index();
+
+ PictureMap::iterator picture_it = picture_map_.find(key);
+ if (picture_it == picture_map_.end())
+ continue;
+
+ // Inform the grid cell that it has been invalidated in this frame.
+ updated = picture_it->second.Invalidate(frame_number) || updated;
+ // Invalidate drops the picture so the whole tile better be invalidated
+ // if it won't be re-recorded below.
+ DCHECK_IMPLIES(!tiling_.TileBounds(key.first, key.second)
+ .Intersects(interest_rect_over_tiles),
+ invalidation_expanded_to_full_tiles.Contains(
+ tiling_.TileBounds(key.first, key.second)));
+ }
}
+ invalidation->Union(invalidation_expanded_to_full_tiles);
}
- invalidation->Union(invalidation_expanded_to_full_tiles);
invalidation->Union(resize_invalidation);
// Make a list of all invalid tiles; we will attempt to
diff --git a/cc/resources/picture_pile_unittest.cc b/cc/resources/picture_pile_unittest.cc
index 15cb3d31..8d798d97 100644
--- a/cc/resources/picture_pile_unittest.cc
+++ b/cc/resources/picture_pile_unittest.cc
@@ -193,6 +193,28 @@
}
}
+TEST_F(PicturePileTest, InvalidateOnFullLayer) {
+ UpdateWholePile();
+
+ // Everything was invalidated once so far.
+ for (auto& it : pile_->picture_map()) {
+ EXPECT_FLOAT_EQ(
+ 1.0f / TestPicturePile::PictureInfo::INVALIDATION_FRAMES_TRACKED,
+ it.second.GetInvalidationFrequencyForTesting());
+ }
+
+ // Invalidate everything,
+ Region invalidation = tiling_rect();
+ UpdateAndExpandInvalidation(&invalidation, tiling_size(), tiling_rect());
+
+ // Everything was invalidated again.
+ for (auto& it : pile_->picture_map()) {
+ EXPECT_FLOAT_EQ(
+ 2.0f / TestPicturePile::PictureInfo::INVALIDATION_FRAMES_TRACKED,
+ it.second.GetInvalidationFrequencyForTesting());
+ }
+}
+
TEST_F(PicturePileTest, StopRecordingOffscreenInvalidations) {
gfx::Size new_tiling_size =
gfx::ToCeiledSize(gfx::ScaleSize(pile_->tiling_size(), 4.f));
@@ -364,6 +386,66 @@
EXPECT_EQ(Region().ToString(), invalidation.ToString());
}
+TEST_F(PicturePileTest, BigFullLayerInvalidation) {
+ gfx::Size huge_layer_size(100000000, 100000000);
+ gfx::Rect viewport(300000, 400000, 5000, 6000);
+
+ // Resize the pile.
+ Region invalidation;
+ UpdateAndExpandInvalidation(&invalidation, huge_layer_size, viewport);
+
+ // Invalidating a huge layer should be fast.
+ base::TimeTicks start = base::TimeTicks::Now();
+ invalidation = gfx::Rect(huge_layer_size);
+ UpdateAndExpandInvalidation(&invalidation, huge_layer_size, viewport);
+ base::TimeTicks end = base::TimeTicks::Now();
+ base::TimeDelta length = end - start;
+ // This is verrrry generous to avoid flake.
+ EXPECT_LT(length.InSeconds(), 5);
+}
+
+TEST_F(PicturePileTest, BigFullLayerInvalidationWithResizeGrow) {
+ gfx::Size huge_layer_size(100000000, 100000000);
+ gfx::Rect viewport(300000, 400000, 5000, 6000);
+
+ // Resize the pile.
+ Region invalidation;
+ UpdateAndExpandInvalidation(&invalidation, huge_layer_size, viewport);
+
+ // Resize the pile even larger, while invalidating everything in the old size.
+ // Invalidating the whole thing should be fast.
+ base::TimeTicks start = base::TimeTicks::Now();
+ gfx::Size bigger_layer_size(huge_layer_size.width() * 2,
+ huge_layer_size.height() * 2);
+ invalidation = gfx::Rect(huge_layer_size);
+ UpdateAndExpandInvalidation(&invalidation, bigger_layer_size, viewport);
+ base::TimeTicks end = base::TimeTicks::Now();
+ base::TimeDelta length = end - start;
+ // This is verrrry generous to avoid flake.
+ EXPECT_LT(length.InSeconds(), 5);
+}
+
+TEST_F(PicturePileTest, BigFullLayerInvalidationWithResizeShrink) {
+ gfx::Size huge_layer_size(100000000, 100000000);
+ gfx::Rect viewport(300000, 400000, 5000, 6000);
+
+ // Resize the pile.
+ Region invalidation;
+ UpdateAndExpandInvalidation(&invalidation, huge_layer_size, viewport);
+
+ // Resize the pile smaller, while invalidating everything in the new size.
+ // Invalidating the whole thing should be fast.
+ base::TimeTicks start = base::TimeTicks::Now();
+ gfx::Size smaller_layer_size(huge_layer_size.width() - 1000,
+ huge_layer_size.height() - 1000);
+ invalidation = gfx::Rect(smaller_layer_size);
+ UpdateAndExpandInvalidation(&invalidation, smaller_layer_size, viewport);
+ base::TimeTicks end = base::TimeTicks::Now();
+ base::TimeDelta length = end - start;
+ // This is verrrry generous to avoid flake.
+ EXPECT_LT(length.InSeconds(), 5);
+}
+
TEST_F(PicturePileTest, InvalidationOutsideRecordingRect) {
gfx::Size huge_layer_size(10000000, 20000000);
gfx::Rect viewport(300000, 400000, 5000, 6000);