// Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include #include #include "cc/playback/picture_pile.h" #include "cc/test/fake_content_layer_client.h" #include "cc/test/fake_picture_pile.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/gfx/geometry/rect_conversions.h" #include "ui/gfx/geometry/size_conversions.h" namespace cc { namespace { class PicturePileTestBase { public: PicturePileTestBase() : min_scale_(0.125), pile_(min_scale_, gfx::Size(1000, 1000)), frame_number_(0) {} void InitializeData() { pile_.SetTileGridSize(gfx::Size(1000, 1000)); pile_.SetMinContentsScale(min_scale_); client_ = FakeContentLayerClient(); SetTilingSize(pile_.tiling().max_texture_size()); } void SetTilingSize(const gfx::Size& tiling_size) { Region invalidation; gfx::Rect viewport_rect(tiling_size); UpdateAndExpandInvalidation(&invalidation, tiling_size, viewport_rect); } gfx::Size tiling_size() const { return pile_.GetSize(); } gfx::Rect tiling_rect() const { return gfx::Rect(pile_.GetSize()); } bool UpdateAndExpandInvalidation(Region* invalidation, const gfx::Size& layer_size, const gfx::Rect& visible_layer_rect) { frame_number_++; return pile_.UpdateAndExpandInvalidation(&client_, invalidation, layer_size, visible_layer_rect, frame_number_, RecordingSource::RECORD_NORMALLY); } bool UpdateWholePile() { Region invalidation = tiling_rect(); bool result = UpdateAndExpandInvalidation(&invalidation, tiling_size(), tiling_rect()); EXPECT_EQ(tiling_rect().ToString(), invalidation.ToString()); return result; } FakeContentLayerClient client_; float min_scale_; FakePicturePile pile_; int frame_number_; }; class PicturePileTest : public PicturePileTestBase, public testing::Test { public: void SetUp() override { InitializeData(); } }; TEST_F(PicturePileTest, InvalidationOnTileBorderOutsideInterestRect) { // Don't expand the interest rect past what we invalidate. pile_.SetPixelRecordDistance(0); gfx::Size tile_size(100, 100); pile_.tiling().SetMaxTextureSize(tile_size); gfx::Size pile_size(400, 400); SetTilingSize(pile_size); // We have multiple tiles. EXPECT_GT(pile_.tiling().num_tiles_x(), 2); EXPECT_GT(pile_.tiling().num_tiles_y(), 2); // Record everything. Region invalidation(tiling_rect()); UpdateAndExpandInvalidation(&invalidation, tiling_size(), tiling_rect()); // +----------+-----------------+-----------+ // | | VVVV 1,0| | // | | VVVV | | // | | VVVV | | // | ...|.................|... | // | ...|.................|... | // +----------+-----------------+-----------+ // | ...| |... | // | ...| |... | // | ...| |... | // | ...| |... | // | ...| 1,1|... | // +----------+-----------------+-----------+ // | ...|.................|... | // | ...|.................|... | // +----------+-----------------+-----------+ // // .. = border pixels for tile 1,1 // VV = interest rect (what we will record) // // The first invalidation is inside VV, so it does not touch border pixels of // tile 1,1. // // The second invalidation goes below VV into the .. border pixels of 1,1. // This is the VV interest rect which will be entirely inside 1,0 and not // touch the border of 1,1. gfx::Rect interest_rect( pile_.tiling().TilePositionX(1) + pile_.tiling().border_texels(), 0, 10, pile_.tiling().TileSizeY(0) - pile_.tiling().border_texels()); // Invalidate tile 1,0 only. This is a rect that avoids the borders of any // other tiles. gfx::Rect invalidate_tile = interest_rect; // This should cause the tile 1,0 to be invalidated and re-recorded. The // invalidation did not need to be expanded. invalidation = invalidate_tile; UpdateAndExpandInvalidation(&invalidation, tiling_size(), interest_rect); EXPECT_EQ(invalidate_tile, invalidation); // Invalidate tile 1,0 and 1,1 by invalidating something that only touches the // border of 1,1 (and is inside the tile bounds of 1,0). This is a 10px wide // strip from the top of the tiling onto the border pixels of tile 1,1 that // avoids border pixels of any other tiles. gfx::Rect invalidate_border = interest_rect; invalidate_border.Inset(0, 0, 0, -1); // This should cause the tile 1,0 and 1,1 to be invalidated. The 1,1 tile will // not be re-recorded since it does not touch the interest rect, so the // invalidation should be expanded to cover all of 1,1. invalidation = invalidate_border; UpdateAndExpandInvalidation(&invalidation, tiling_size(), interest_rect); Region expected_invalidation = invalidate_border; expected_invalidation.Union(pile_.tiling().TileBounds(1, 1)); EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString()); } TEST_F(PicturePileTest, SmallInvalidateInflated) { // Invalidate something inside a tile. Region invalidate_rect(gfx::Rect(50, 50, 1, 1)); UpdateAndExpandInvalidation(&invalidate_rect, tiling_size(), tiling_rect()); EXPECT_EQ(gfx::Rect(50, 50, 1, 1).ToString(), invalidate_rect.ToString()); EXPECT_EQ(1, pile_.tiling().num_tiles_x()); EXPECT_EQ(1, pile_.tiling().num_tiles_y()); PicturePile::PictureMapKey key = FakePicturePile::PictureMapKey(0, 0); PicturePile::PictureMap::iterator it = pile_.picture_map().find(key); EXPECT_TRUE(it != pile_.picture_map().end()); const Picture* picture = it->second.get(); EXPECT_TRUE(picture); gfx::Rect picture_rect = gfx::ScaleToEnclosedRect(picture->LayerRect(), min_scale_); // The the picture should be large enough that scaling it never makes a rect // smaller than 1 px wide or tall. EXPECT_FALSE(picture_rect.IsEmpty()) << "Picture rect " << picture_rect.ToString(); } TEST_F(PicturePileTest, LargeInvalidateInflated) { // Invalidate something inside a tile. Region invalidate_rect(gfx::Rect(50, 50, 100, 100)); UpdateAndExpandInvalidation(&invalidate_rect, tiling_size(), tiling_rect()); EXPECT_EQ(gfx::Rect(50, 50, 100, 100).ToString(), invalidate_rect.ToString()); EXPECT_EQ(1, pile_.tiling().num_tiles_x()); EXPECT_EQ(1, pile_.tiling().num_tiles_y()); PicturePile::PictureMapKey key = FakePicturePile::PictureMapKey(0, 0); PicturePile::PictureMap::iterator it = pile_.picture_map().find(key); EXPECT_TRUE(it != pile_.picture_map().end()); const Picture* picture = it->second.get(); EXPECT_TRUE(picture); int expected_inflation = pile_.buffer_pixels(); gfx::Rect base_picture_rect(tiling_size()); base_picture_rect.Inset(-expected_inflation, -expected_inflation); EXPECT_EQ(base_picture_rect.ToString(), picture->LayerRect().ToString()); } TEST_F(PicturePileTest, ClearingInvalidatesRecordedRect) { gfx::Rect rect(0, 0, 5, 5); EXPECT_TRUE(pile_.CanRasterLayerRect(rect)); EXPECT_TRUE(pile_.CanRasterSlowTileCheck(rect)); pile_.Clear(); // Make sure both the cache-aware check (using recorded region) and the normal // check are both false after clearing. EXPECT_FALSE(pile_.CanRasterLayerRect(rect)); EXPECT_FALSE(pile_.CanRasterSlowTileCheck(rect)); } TEST_F(PicturePileTest, NoInvalidationValidViewport) { // This test validates that the recorded_viewport cache of full tiles // is still valid for some use cases. If it's not, it's a performance // issue because CanRaster checks will go down the slow path. EXPECT_TRUE(!pile_.recorded_viewport().IsEmpty()); // No invalidation, same viewport. Region invalidation; UpdateAndExpandInvalidation(&invalidation, tiling_size(), tiling_rect()); EXPECT_TRUE(!pile_.recorded_viewport().IsEmpty()); EXPECT_EQ(Region().ToString(), invalidation.ToString()); // Partial invalidation, same viewport. invalidation = gfx::Rect(0, 0, 1, 1); UpdateAndExpandInvalidation(&invalidation, tiling_size(), tiling_rect()); EXPECT_TRUE(!pile_.recorded_viewport().IsEmpty()); EXPECT_EQ(gfx::Rect(0, 0, 1, 1).ToString(), invalidation.ToString()); // No invalidation, changing viewport. invalidation = Region(); UpdateAndExpandInvalidation(&invalidation, tiling_size(), gfx::Rect(5, 5, 5, 5)); EXPECT_TRUE(!pile_.recorded_viewport().IsEmpty()); 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); // Resize the pile and set up the interest rect. Region invalidation; UpdateAndExpandInvalidation(&invalidation, huge_layer_size, viewport); // Invalidation inside the recording rect does not need to be expanded. invalidation = viewport; UpdateAndExpandInvalidation(&invalidation, huge_layer_size, viewport); EXPECT_EQ(viewport.ToString(), invalidation.ToString()); // Invalidation outside the recording rect should expand to the tiles it // covers. gfx::Rect recorded_over_tiles = pile_.tiling().ExpandRectToTileBounds(pile_.recorded_viewport()); gfx::Rect invalidation_outside( recorded_over_tiles.right(), recorded_over_tiles.y(), 30, 30); invalidation = invalidation_outside; UpdateAndExpandInvalidation(&invalidation, huge_layer_size, viewport); gfx::Rect expanded_recorded_viewport = pile_.tiling().ExpandRectToTileBounds(pile_.recorded_viewport()); Region expected_invalidation = pile_.tiling().ExpandRectToTileBounds(invalidation_outside); EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString()); } enum Corner { TOP_LEFT, TOP_RIGHT, BOTTOM_LEFT, BOTTOM_RIGHT, }; class PicturePileResizeCornerTest : public PicturePileTestBase, public testing::TestWithParam { protected: void SetUp() override { InitializeData(); } static gfx::Rect CornerSinglePixelRect(Corner corner, const gfx::Size& s) { switch (corner) { case TOP_LEFT: return gfx::Rect(0, 0, 1, 1); case TOP_RIGHT: return gfx::Rect(s.width() - 1, 0, 1, 1); case BOTTOM_LEFT: return gfx::Rect(0, s.height() - 1, 1, 1); case BOTTOM_RIGHT: return gfx::Rect(s.width() - 1, s.height() - 1, 1, 1); } NOTREACHED(); return gfx::Rect(); } }; TEST_P(PicturePileResizeCornerTest, ResizePileOutsideInterestRect) { Corner corner = GetParam(); // This size chosen to be larger than the interest rect size, which is // at least kPixelDistanceToRecord * 2 in each dimension. int tile_size = 100000; // The small number subtracted keeps the last tile in each axis larger than // the interest rect also. int offset = -100; gfx::Size base_tiling_size(6 * tile_size + offset, 6 * tile_size + offset); gfx::Size grow_down_tiling_size(6 * tile_size + offset, 8 * tile_size + offset); gfx::Size grow_right_tiling_size(8 * tile_size + offset, 6 * tile_size + offset); gfx::Size grow_both_tiling_size(8 * tile_size + offset, 8 * tile_size + offset); Region invalidation; Region expected_invalidation; pile_.tiling().SetMaxTextureSize(gfx::Size(tile_size, tile_size)); SetTilingSize(base_tiling_size); // We should have a recording for every tile. EXPECT_EQ(6, pile_.tiling().num_tiles_x()); EXPECT_EQ(6, pile_.tiling().num_tiles_y()); for (int i = 0; i < pile_.tiling().num_tiles_x(); ++i) { for (int j = 0; j < pile_.tiling().num_tiles_y(); ++j) { FakePicturePile::PictureMapKey key(i, j); FakePicturePile::PictureMap& map = pile_.picture_map(); FakePicturePile::PictureMap::iterator it = map.find(key); EXPECT_TRUE(it != map.end() && it->second.get()); } } UpdateAndExpandInvalidation( &invalidation, grow_down_tiling_size, CornerSinglePixelRect(corner, grow_down_tiling_size)); // We should have lost all of the recordings in the bottom row as none of them // are in the current interest rect (which is either the above or below it). EXPECT_EQ(6, pile_.tiling().num_tiles_x()); EXPECT_EQ(8, pile_.tiling().num_tiles_y()); for (int i = 0; i < 6; ++i) { for (int j = 0; j < 6; ++j) { FakePicturePile::PictureMapKey key(i, j); FakePicturePile::PictureMap& map = pile_.picture_map(); FakePicturePile::PictureMap::iterator it = map.find(key); EXPECT_EQ(j < 5, it != map.end() && it->second.get()); } } // We invalidated all new pixels in the recording. expected_invalidation = SubtractRegions(gfx::Rect(grow_down_tiling_size), gfx::Rect(base_tiling_size)); // But the new pixels don't cover the whole bottom row. gfx::Rect bottom_row = gfx::UnionRects(pile_.tiling().TileBounds(0, 5), pile_.tiling().TileBounds(5, 5)); EXPECT_FALSE(expected_invalidation.Contains(bottom_row)); // We invalidated the entire old bottom row. expected_invalidation.Union(bottom_row); EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString()); invalidation.Clear(); UpdateWholePile(); UpdateAndExpandInvalidation(&invalidation, base_tiling_size, CornerSinglePixelRect(corner, base_tiling_size)); // When shrinking, we should have lost all the recordings in the bottom row // not touching the interest rect. EXPECT_EQ(6, pile_.tiling().num_tiles_x()); EXPECT_EQ(6, pile_.tiling().num_tiles_y()); for (int i = 0; i < pile_.tiling().num_tiles_x(); ++i) { for (int j = 0; j < pile_.tiling().num_tiles_y(); ++j) { FakePicturePile::PictureMapKey key(i, j); FakePicturePile::PictureMap& map = pile_.picture_map(); FakePicturePile::PictureMap::iterator it = map.find(key); bool expect_tile; switch (corner) { case TOP_LEFT: case TOP_RIGHT: expect_tile = j < 5; break; case BOTTOM_LEFT: // The interest rect in the bottom left tile means we'll record it. expect_tile = j < 5 || (j == 5 && i == 0); break; case BOTTOM_RIGHT: // The interest rect in the bottom right tile means we'll record it. expect_tile = j < 5 || (j == 5 && i == 5); break; } EXPECT_EQ(expect_tile, it != map.end() && it->second.get()); } } // When shrinking, the previously exposed region is invalidated. expected_invalidation = SubtractRegions(gfx::Rect(grow_down_tiling_size), gfx::Rect(base_tiling_size)); // The whole bottom row of tiles (except any with the interest rect) are // dropped. gfx::Rect bottom_row_minus_existing_corner = gfx::UnionRects( pile_.tiling().TileBounds(0, 5), pile_.tiling().TileBounds(5, 5)); switch (corner) { case TOP_LEFT: case TOP_RIGHT: // No tiles are kept in the changed region because it doesn't // intersect with the interest rect. break; case BOTTOM_LEFT: bottom_row_minus_existing_corner.Subtract( pile_.tiling().TileBounds(0, 5)); break; case BOTTOM_RIGHT: bottom_row_minus_existing_corner.Subtract( pile_.tiling().TileBounds(5, 5)); break; } expected_invalidation.Union(bottom_row_minus_existing_corner); EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString()); invalidation.Clear(); UpdateWholePile(); UpdateAndExpandInvalidation( &invalidation, grow_right_tiling_size, CornerSinglePixelRect(corner, grow_right_tiling_size)); // We should have lost all of the recordings in the right column as none of // them are in the current interest rect (which is either entirely left or // right of it). EXPECT_EQ(8, pile_.tiling().num_tiles_x()); EXPECT_EQ(6, pile_.tiling().num_tiles_y()); for (int i = 0; i < 6; ++i) { for (int j = 0; j < 6; ++j) { FakePicturePile::PictureMapKey key(i, j); FakePicturePile::PictureMap& map = pile_.picture_map(); FakePicturePile::PictureMap::iterator it = map.find(key); EXPECT_EQ(i < 5, it != map.end() && it->second.get()); } } // We invalidated all new pixels in the recording. expected_invalidation = SubtractRegions(gfx::Rect(grow_right_tiling_size), gfx::Rect(base_tiling_size)); // But the new pixels don't cover the whole right_column. gfx::Rect right_column = gfx::UnionRects(pile_.tiling().TileBounds(5, 0), pile_.tiling().TileBounds(5, 5)); EXPECT_FALSE(expected_invalidation.Contains(right_column)); // We invalidated the entire old right column. expected_invalidation.Union(right_column); EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString()); invalidation.Clear(); UpdateWholePile(); UpdateAndExpandInvalidation(&invalidation, base_tiling_size, CornerSinglePixelRect(corner, base_tiling_size)); // When shrinking, we should have lost all the recordings in the right column // not touching the interest rect. EXPECT_EQ(6, pile_.tiling().num_tiles_x()); EXPECT_EQ(6, pile_.tiling().num_tiles_y()); for (int i = 0; i < pile_.tiling().num_tiles_x(); ++i) { for (int j = 0; j < pile_.tiling().num_tiles_y(); ++j) { FakePicturePile::PictureMapKey key(i, j); FakePicturePile::PictureMap& map = pile_.picture_map(); FakePicturePile::PictureMap::iterator it = map.find(key); bool expect_tile; switch (corner) { case TOP_LEFT: case BOTTOM_LEFT: // No tiles are kept in the changed region because it doesn't // intersect with the interest rect. expect_tile = i < 5; break; case TOP_RIGHT: // The interest rect in the top right tile means we'll record it. expect_tile = i < 5 || (j == 0 && i == 5); break; case BOTTOM_RIGHT: // The interest rect in the bottom right tile means we'll record it. expect_tile = i < 5 || (j == 5 && i == 5); break; } EXPECT_EQ(expect_tile, it != map.end() && it->second.get()); } } // When shrinking, the previously exposed region is invalidated. expected_invalidation = SubtractRegions(gfx::Rect(grow_right_tiling_size), gfx::Rect(base_tiling_size)); // The whole right column of tiles (except for ones with the interest rect) // are dropped. gfx::Rect right_column_minus_existing_corner = gfx::UnionRects( pile_.tiling().TileBounds(5, 0), pile_.tiling().TileBounds(5, 5)); switch (corner) { case TOP_LEFT: case BOTTOM_LEFT: break; case TOP_RIGHT: right_column_minus_existing_corner.Subtract( pile_.tiling().TileBounds(5, 0)); break; case BOTTOM_RIGHT: right_column_minus_existing_corner.Subtract( pile_.tiling().TileBounds(5, 5)); break; } expected_invalidation.Union(right_column_minus_existing_corner); EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString()); invalidation.Clear(); UpdateWholePile(); UpdateAndExpandInvalidation( &invalidation, grow_both_tiling_size, CornerSinglePixelRect(corner, grow_both_tiling_size)); // We should have lost the recordings in the right column and bottom row. EXPECT_EQ(8, pile_.tiling().num_tiles_x()); EXPECT_EQ(8, pile_.tiling().num_tiles_y()); for (int i = 0; i < 6; ++i) { for (int j = 0; j < 6; ++j) { FakePicturePile::PictureMapKey key(i, j); FakePicturePile::PictureMap& map = pile_.picture_map(); FakePicturePile::PictureMap::iterator it = map.find(key); EXPECT_EQ(i < 5 && j < 5, it != map.end() && it->second.get()); } } // We invalidated all new pixels in the recording. expected_invalidation = SubtractRegions(gfx::Rect(grow_both_tiling_size), gfx::Rect(base_tiling_size)); // But the new pixels don't cover the whole right column or bottom row. Region right_column_and_bottom_row = UnionRegions(gfx::UnionRects(pile_.tiling().TileBounds(5, 0), pile_.tiling().TileBounds(5, 5)), gfx::UnionRects(pile_.tiling().TileBounds(0, 5), pile_.tiling().TileBounds(5, 5))); EXPECT_FALSE(expected_invalidation.Contains(right_column_and_bottom_row)); // We invalidated the entire old right column and the old bottom row. expected_invalidation.Union(right_column_and_bottom_row); EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString()); invalidation.Clear(); UpdateWholePile(); UpdateAndExpandInvalidation(&invalidation, base_tiling_size, CornerSinglePixelRect(corner, base_tiling_size)); // We should have lost the recordings in the right column and bottom row, // except where it intersects the interest rect. EXPECT_EQ(6, pile_.tiling().num_tiles_x()); EXPECT_EQ(6, pile_.tiling().num_tiles_y()); for (int i = 0; i < pile_.tiling().num_tiles_x(); ++i) { for (int j = 0; j < pile_.tiling().num_tiles_y(); ++j) { FakePicturePile::PictureMapKey key(i, j); FakePicturePile::PictureMap& map = pile_.picture_map(); FakePicturePile::PictureMap::iterator it = map.find(key); bool expect_tile; switch (corner) { case TOP_LEFT: expect_tile = i < 5 && j < 5; break; case TOP_RIGHT: // The interest rect in the top right tile means we'll record it. expect_tile = (i < 5 && j < 5) || (j == 0 && i == 5); break; case BOTTOM_LEFT: // The interest rect in the bottom left tile means we'll record it. expect_tile = (i < 5 && j < 5) || (j == 5 && i == 0); break; case BOTTOM_RIGHT: // The interest rect in the bottom right tile means we'll record it. expect_tile = (i < 5 && j < 5) || (j == 5 && i == 5); break; } EXPECT_EQ(expect_tile, it != map.end() && it->second.get()) << i << "," << j; } } // We invalidated all previous pixels in the recording. expected_invalidation = SubtractRegions(gfx::Rect(grow_both_tiling_size), gfx::Rect(base_tiling_size)); // The whole right column and bottom row of tiles (except for ones with the // interest rect) are dropped. Region right_column_and_bottom_row_minus_existing_corner = right_column_and_bottom_row; switch (corner) { case TOP_LEFT: break; case BOTTOM_LEFT: right_column_and_bottom_row_minus_existing_corner.Subtract( pile_.tiling().TileBounds(0, 5)); break; case TOP_RIGHT: right_column_and_bottom_row_minus_existing_corner.Subtract( pile_.tiling().TileBounds(5, 0)); break; case BOTTOM_RIGHT: right_column_and_bottom_row_minus_existing_corner.Subtract( pile_.tiling().TileBounds(5, 5)); break; } expected_invalidation.Union( right_column_and_bottom_row_minus_existing_corner); EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString()); invalidation.Clear(); } TEST_P(PicturePileResizeCornerTest, SmallResizePileOutsideInterestRect) { Corner corner = GetParam(); // This size chosen to be larger than the interest rect size, which is // at least kPixelDistanceToRecord * 2 in each dimension. int tile_size = 100000; // The small number subtracted keeps the last tile in each axis larger than // the interest rect also. int offset = -100; gfx::Size base_tiling_size(6 * tile_size + offset, 6 * tile_size + offset); gfx::Size grow_down_tiling_size(6 * tile_size + offset, 6 * tile_size + offset + 5); gfx::Size grow_right_tiling_size(6 * tile_size + offset + 5, 6 * tile_size + offset); gfx::Size grow_both_tiling_size(6 * tile_size + offset + 5, 6 * tile_size + offset + 5); Region invalidation; Region expected_invalidation; pile_.tiling().SetMaxTextureSize(gfx::Size(tile_size, tile_size)); SetTilingSize(base_tiling_size); // We should have a recording for every tile. EXPECT_EQ(6, pile_.tiling().num_tiles_x()); EXPECT_EQ(6, pile_.tiling().num_tiles_y()); for (int i = 0; i < pile_.tiling().num_tiles_x(); ++i) { for (int j = 0; j < pile_.tiling().num_tiles_y(); ++j) { FakePicturePile::PictureMapKey key(i, j); FakePicturePile::PictureMap& map = pile_.picture_map(); FakePicturePile::PictureMap::iterator it = map.find(key); EXPECT_TRUE(it != map.end() && it->second.get()); } } // In this test (unlike the large resize test), as all growing and shrinking // happens within tiles, the resulting invalidation is symmetrical, so use // this enum to repeat the test both ways. enum ChangeDirection { GROW, SHRINK, LAST_DIRECTION = SHRINK }; // Grow downward. for (int dir = 0; dir <= LAST_DIRECTION; ++dir) { gfx::Size new_tiling_size = dir == GROW ? grow_down_tiling_size : base_tiling_size; UpdateWholePile(); UpdateAndExpandInvalidation(&invalidation, new_tiling_size, CornerSinglePixelRect(corner, new_tiling_size)); // We should have lost the recordings in the bottom row that do not // intersect the interest rect. EXPECT_EQ(6, pile_.tiling().num_tiles_x()); EXPECT_EQ(6, pile_.tiling().num_tiles_y()); for (int i = 0; i < pile_.tiling().num_tiles_x(); ++i) { for (int j = 0; j < pile_.tiling().num_tiles_y(); ++j) { FakePicturePile::PictureMapKey key(i, j); FakePicturePile::PictureMap& map = pile_.picture_map(); FakePicturePile::PictureMap::iterator it = map.find(key); bool expect_tile; switch (corner) { case TOP_LEFT: case TOP_RIGHT: expect_tile = j < 5; break; case BOTTOM_LEFT: // The interest rect in the bottom left tile means we'll record it. expect_tile = j < 5 || (j == 5 && i == 0); break; case BOTTOM_RIGHT: // The interest rect in the bottom right tile means we'll record it. expect_tile = j < 5 || (j == 5 && i == 5); break; } EXPECT_EQ(expect_tile, it != map.end() && it->second.get()); } } // We invalidated the bottom row outside the new interest rect. The tile // that insects the interest rect in invalidated only on its newly // exposed or previously exposed pixels. if (dir == GROW) { // Only calculate the expected invalidation while growing, as the tile // bounds post-growing is the newly exposed / previously exposed sizes. // Post-shrinking, the tile bounds are smaller, so can't be used. switch (corner) { case TOP_LEFT: case TOP_RIGHT: expected_invalidation = gfx::UnionRects( pile_.tiling().TileBounds(0, 5), pile_.tiling().TileBounds(5, 5)); break; case BOTTOM_LEFT: expected_invalidation = gfx::UnionRects( pile_.tiling().TileBounds(1, 5), pile_.tiling().TileBounds(5, 5)); expected_invalidation.Union(SubtractRects( pile_.tiling().TileBounds(0, 5), gfx::Rect(base_tiling_size))); break; case BOTTOM_RIGHT: expected_invalidation = gfx::UnionRects( pile_.tiling().TileBounds(0, 5), pile_.tiling().TileBounds(4, 5)); expected_invalidation.Union(SubtractRects( pile_.tiling().TileBounds(5, 5), gfx::Rect(base_tiling_size))); break; } } EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString()); invalidation.Clear(); } // Grow right. for (int dir = 0; dir <= LAST_DIRECTION; ++dir) { gfx::Size new_tiling_size = dir == GROW ? grow_right_tiling_size : base_tiling_size; UpdateWholePile(); UpdateAndExpandInvalidation(&invalidation, new_tiling_size, CornerSinglePixelRect(corner, new_tiling_size)); // We should have lost the recordings in the right column. EXPECT_EQ(6, pile_.tiling().num_tiles_x()); EXPECT_EQ(6, pile_.tiling().num_tiles_y()); for (int i = 0; i < pile_.tiling().num_tiles_x(); ++i) { for (int j = 0; j < pile_.tiling().num_tiles_y(); ++j) { FakePicturePile::PictureMapKey key(i, j); FakePicturePile::PictureMap& map = pile_.picture_map(); FakePicturePile::PictureMap::iterator it = map.find(key); bool expect_tile; switch (corner) { case TOP_LEFT: case BOTTOM_LEFT: expect_tile = i < 5; break; case TOP_RIGHT: // The interest rect in the top right tile means we'll record it. expect_tile = i < 5 || (j == 0 && i == 5); break; case BOTTOM_RIGHT: // The interest rect in the bottom right tile means we'll record it. expect_tile = i < 5 || (j == 5 && i == 5); break; } EXPECT_EQ(expect_tile, it != map.end() && it->second.get()); } } // We invalidated the right column outside the new interest rect. The tile // that insects the interest rect in invalidated only on its new or // previously exposed pixels. if (dir == GROW) { // Calculate the expected invalidation the first time through the loop. switch (corner) { case TOP_LEFT: case BOTTOM_LEFT: expected_invalidation = gfx::UnionRects( pile_.tiling().TileBounds(5, 0), pile_.tiling().TileBounds(5, 5)); break; case TOP_RIGHT: expected_invalidation = gfx::UnionRects( pile_.tiling().TileBounds(5, 1), pile_.tiling().TileBounds(5, 5)); expected_invalidation.Union(SubtractRects( pile_.tiling().TileBounds(5, 0), gfx::Rect(base_tiling_size))); break; case BOTTOM_RIGHT: expected_invalidation = gfx::UnionRects( pile_.tiling().TileBounds(5, 0), pile_.tiling().TileBounds(5, 4)); expected_invalidation.Union(SubtractRects( pile_.tiling().TileBounds(5, 5), gfx::Rect(base_tiling_size))); break; } } EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString()); invalidation.Clear(); } // Grow both. for (int dir = 0; dir <= LAST_DIRECTION; ++dir) { gfx::Size new_tiling_size = dir == GROW ? grow_both_tiling_size : base_tiling_size; UpdateWholePile(); UpdateAndExpandInvalidation(&invalidation, new_tiling_size, CornerSinglePixelRect(corner, new_tiling_size)); // We should have lost the recordings in the right column and bottom row. // The tile that insects the interest rect in invalidated only on its new // or previously exposed pixels. EXPECT_EQ(6, pile_.tiling().num_tiles_x()); EXPECT_EQ(6, pile_.tiling().num_tiles_y()); for (int i = 0; i < pile_.tiling().num_tiles_x(); ++i) { for (int j = 0; j < pile_.tiling().num_tiles_y(); ++j) { FakePicturePile::PictureMapKey key(i, j); FakePicturePile::PictureMap& map = pile_.picture_map(); FakePicturePile::PictureMap::iterator it = map.find(key); bool expect_tile; switch (corner) { case TOP_LEFT: expect_tile = i < 5 && j < 5; break; case TOP_RIGHT: // The interest rect in the top right tile means we'll record it. expect_tile = (i < 5 && j < 5) || (j == 0 && i == 5); break; case BOTTOM_LEFT: // The interest rect in the bottom left tile means we'll record it. expect_tile = (i < 5 && j < 5) || (j == 5 && i == 0); break; case BOTTOM_RIGHT: // The interest rect in the bottom right tile means we'll record it. expect_tile = (i < 5 && j < 5) || (j == 5 && i == 5); break; } EXPECT_EQ(expect_tile, it != map.end() && it->second.get()) << i << "," << j; } } // We invalidated the right column and the bottom row outside the new // interest rect. The tile that insects the interest rect in invalidated // only on its new or previous exposed pixels. if (dir == GROW) { // Calculate the expected invalidation the first time through the loop. switch (corner) { case TOP_LEFT: expected_invalidation = gfx::UnionRects( pile_.tiling().TileBounds(5, 0), pile_.tiling().TileBounds(5, 5)); expected_invalidation.Union( gfx::UnionRects(pile_.tiling().TileBounds(0, 5), pile_.tiling().TileBounds(5, 5))); break; case TOP_RIGHT: expected_invalidation = gfx::UnionRects( pile_.tiling().TileBounds(5, 1), pile_.tiling().TileBounds(5, 5)); expected_invalidation.Union( gfx::UnionRects(pile_.tiling().TileBounds(0, 5), pile_.tiling().TileBounds(5, 5))); expected_invalidation.Union(SubtractRects( pile_.tiling().TileBounds(5, 0), gfx::Rect(base_tiling_size))); break; case BOTTOM_LEFT: expected_invalidation = gfx::UnionRects( pile_.tiling().TileBounds(5, 0), pile_.tiling().TileBounds(5, 5)); expected_invalidation.Union( gfx::UnionRects(pile_.tiling().TileBounds(1, 5), pile_.tiling().TileBounds(5, 5))); expected_invalidation.Union(SubtractRects( pile_.tiling().TileBounds(0, 5), gfx::Rect(base_tiling_size))); break; case BOTTOM_RIGHT: expected_invalidation = gfx::UnionRects( pile_.tiling().TileBounds(5, 0), pile_.tiling().TileBounds(5, 4)); expected_invalidation.Union( gfx::UnionRects(pile_.tiling().TileBounds(0, 5), pile_.tiling().TileBounds(4, 5))); expected_invalidation.Union(SubtractRegions( pile_.tiling().TileBounds(5, 5), gfx::Rect(base_tiling_size))); break; } } EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString()); invalidation.Clear(); } } INSTANTIATE_TEST_CASE_P( PicturePileResizeCornerTests, PicturePileResizeCornerTest, ::testing::Values(TOP_LEFT, TOP_RIGHT, BOTTOM_LEFT, BOTTOM_RIGHT)); TEST_F(PicturePileTest, ResizePileInsideInterestRect) { // This size chosen to be small enough that all the rects below fit inside the // the interest rect, so they are smaller than kPixelDistanceToRecord in each // dimension. int tile_size = 100; gfx::Size base_tiling_size(5 * tile_size, 5 * tile_size); gfx::Size grow_down_tiling_size(5 * tile_size, 7 * tile_size); gfx::Size grow_right_tiling_size(7 * tile_size, 5 * tile_size); gfx::Size grow_both_tiling_size(7 * tile_size, 7 * tile_size); Region invalidation; Region expected_invalidation; pile_.tiling().SetMaxTextureSize(gfx::Size(tile_size, tile_size)); SetTilingSize(base_tiling_size); // We should have a recording for every tile. EXPECT_EQ(6, pile_.tiling().num_tiles_x()); EXPECT_EQ(6, pile_.tiling().num_tiles_y()); for (int i = 0; i < pile_.tiling().num_tiles_x(); ++i) { for (int j = 0; j < pile_.tiling().num_tiles_y(); ++j) { FakePicturePile::PictureMapKey key(i, j); FakePicturePile::PictureMap& map = pile_.picture_map(); FakePicturePile::PictureMap::iterator it = map.find(key); EXPECT_TRUE(it != map.end() && it->second.get()); } } UpdateAndExpandInvalidation( &invalidation, grow_down_tiling_size, gfx::Rect(1, 1)); // We should have a recording for every tile. EXPECT_EQ(6, pile_.tiling().num_tiles_x()); EXPECT_EQ(8, pile_.tiling().num_tiles_y()); for (int i = 0; i < pile_.tiling().num_tiles_x(); ++i) { for (int j = 0; j < pile_.tiling().num_tiles_y(); ++j) { FakePicturePile::PictureMapKey key(i, j); FakePicturePile::PictureMap& map = pile_.picture_map(); FakePicturePile::PictureMap::iterator it = map.find(key); EXPECT_TRUE(it != map.end() && it->second.get()); } } // We invalidated the newly exposed pixels on the bottom row of tiles. expected_invalidation = SubtractRegions(gfx::Rect(grow_down_tiling_size), gfx::Rect(base_tiling_size)); Region bottom_row_new_pixels = SubtractRegions(gfx::UnionRects(pile_.tiling().TileBounds(0, 5), pile_.tiling().TileBounds(5, 5)), gfx::Rect(base_tiling_size)); EXPECT_TRUE(expected_invalidation.Contains(bottom_row_new_pixels)); EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString()); invalidation.Clear(); UpdateWholePile(); UpdateAndExpandInvalidation(&invalidation, base_tiling_size, gfx::Rect(1, 1)); // We should have a recording for every tile. EXPECT_EQ(6, pile_.tiling().num_tiles_x()); EXPECT_EQ(6, pile_.tiling().num_tiles_y()); for (int i = 0; i < pile_.tiling().num_tiles_x(); ++i) { for (int j = 0; j < pile_.tiling().num_tiles_y(); ++j) { FakePicturePile::PictureMapKey key(i, j); FakePicturePile::PictureMap& map = pile_.picture_map(); FakePicturePile::PictureMap::iterator it = map.find(key); EXPECT_TRUE(it != map.end() && it->second.get()); } } // We invalidated the previously exposed pixels on the bottom row of tiles. expected_invalidation = SubtractRegions(gfx::Rect(grow_down_tiling_size), gfx::Rect(base_tiling_size)); EXPECT_TRUE(expected_invalidation.Contains(bottom_row_new_pixels)); EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString()); invalidation.Clear(); UpdateWholePile(); UpdateAndExpandInvalidation( &invalidation, grow_right_tiling_size, gfx::Rect(1, 1)); // We should have a recording for every tile. EXPECT_EQ(8, pile_.tiling().num_tiles_x()); EXPECT_EQ(6, pile_.tiling().num_tiles_y()); for (int i = 0; i < pile_.tiling().num_tiles_x(); ++i) { for (int j = 0; j < pile_.tiling().num_tiles_y(); ++j) { FakePicturePile::PictureMapKey key(i, j); FakePicturePile::PictureMap& map = pile_.picture_map(); FakePicturePile::PictureMap::iterator it = map.find(key); EXPECT_TRUE(it != map.end() && it->second.get()); } } // We invalidated the newly exposed pixels on the right column of tiles. expected_invalidation = SubtractRegions(gfx::Rect(grow_right_tiling_size), gfx::Rect(base_tiling_size)); Region right_column_new_pixels = SubtractRegions(gfx::UnionRects(pile_.tiling().TileBounds(5, 0), pile_.tiling().TileBounds(5, 5)), gfx::Rect(base_tiling_size)); EXPECT_TRUE(expected_invalidation.Contains(right_column_new_pixels)); EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString()); invalidation.Clear(); UpdateWholePile(); UpdateAndExpandInvalidation(&invalidation, base_tiling_size, gfx::Rect(1, 1)); // We should have lost the recordings that are now outside the tiling only. EXPECT_EQ(6, pile_.tiling().num_tiles_x()); EXPECT_EQ(6, pile_.tiling().num_tiles_y()); for (int i = 0; i < pile_.tiling().num_tiles_x(); ++i) { for (int j = 0; j < pile_.tiling().num_tiles_y(); ++j) { FakePicturePile::PictureMapKey key(i, j); FakePicturePile::PictureMap& map = pile_.picture_map(); FakePicturePile::PictureMap::iterator it = map.find(key); EXPECT_TRUE(it != map.end() && it->second.get()); } } // We invalidated the previously exposed pixels on the right column of tiles. expected_invalidation = SubtractRegions(gfx::Rect(grow_right_tiling_size), gfx::Rect(base_tiling_size)); EXPECT_TRUE(expected_invalidation.Contains(right_column_new_pixels)); EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString()); invalidation.Clear(); UpdateWholePile(); UpdateAndExpandInvalidation( &invalidation, grow_both_tiling_size, gfx::Rect(1, 1)); // We should have a recording for every tile. EXPECT_EQ(8, pile_.tiling().num_tiles_x()); EXPECT_EQ(8, pile_.tiling().num_tiles_y()); for (int i = 0; i < pile_.tiling().num_tiles_x(); ++i) { for (int j = 0; j < pile_.tiling().num_tiles_y(); ++j) { FakePicturePile::PictureMapKey key(i, j); FakePicturePile::PictureMap& map = pile_.picture_map(); FakePicturePile::PictureMap::iterator it = map.find(key); EXPECT_TRUE(it != map.end() && it->second.get()); } } // We invalidated the newly exposed pixels on the bottom row and right column // of tiles. expected_invalidation = SubtractRegions(gfx::Rect(grow_both_tiling_size), gfx::Rect(base_tiling_size)); Region bottom_row_and_right_column_new_pixels = SubtractRegions( UnionRegions(gfx::UnionRects(pile_.tiling().TileBounds(0, 5), pile_.tiling().TileBounds(5, 5)), gfx::UnionRects(pile_.tiling().TileBounds(5, 0), pile_.tiling().TileBounds(5, 5))), gfx::Rect(base_tiling_size)); EXPECT_TRUE( expected_invalidation.Contains(bottom_row_and_right_column_new_pixels)); EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString()); invalidation.Clear(); UpdateWholePile(); UpdateAndExpandInvalidation(&invalidation, base_tiling_size, gfx::Rect()); // We should have lost the recordings that are now outside the tiling only. EXPECT_EQ(6, pile_.tiling().num_tiles_x()); EXPECT_EQ(6, pile_.tiling().num_tiles_y()); for (int i = 0; i < pile_.tiling().num_tiles_x(); ++i) { for (int j = 0; j < pile_.tiling().num_tiles_y(); ++j) { FakePicturePile::PictureMapKey key(i, j); FakePicturePile::PictureMap& map = pile_.picture_map(); FakePicturePile::PictureMap::iterator it = map.find(key); EXPECT_TRUE(it != map.end() && it->second.get()); } } // We invalidated the previously exposed pixels on the bottom row and right // column of tiles. expected_invalidation = SubtractRegions(gfx::Rect(grow_both_tiling_size), gfx::Rect(base_tiling_size)); EXPECT_TRUE( expected_invalidation.Contains(bottom_row_and_right_column_new_pixels)); EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString()); invalidation.Clear(); } TEST_F(PicturePileTest, SmallResizePileInsideInterestRect) { // This size chosen to be small enough that all the rects below fit inside the // the interest rect, so they are smaller than kPixelDistanceToRecord in each // dimension. int tile_size = 100; gfx::Size base_tiling_size(5 * tile_size, 5 * tile_size); gfx::Size grow_down_tiling_size(5 * tile_size, 5 * tile_size + 5); gfx::Size grow_right_tiling_size(5 * tile_size + 5, 5 * tile_size); gfx::Size grow_both_tiling_size(5 * tile_size + 5, 5 * tile_size + 5); Region invalidation; Region expected_invalidation; pile_.tiling().SetMaxTextureSize(gfx::Size(tile_size, tile_size)); SetTilingSize(base_tiling_size); // We should have a recording for every tile. EXPECT_EQ(6, pile_.tiling().num_tiles_x()); EXPECT_EQ(6, pile_.tiling().num_tiles_y()); for (int i = 0; i < pile_.tiling().num_tiles_x(); ++i) { for (int j = 0; j < pile_.tiling().num_tiles_y(); ++j) { FakePicturePile::PictureMapKey key(i, j); FakePicturePile::PictureMap& map = pile_.picture_map(); FakePicturePile::PictureMap::iterator it = map.find(key); EXPECT_TRUE(it != map.end() && it->second.get()); } } UpdateAndExpandInvalidation( &invalidation, grow_down_tiling_size, gfx::Rect(1, 1)); // We should have a recording for every tile. EXPECT_EQ(6, pile_.tiling().num_tiles_x()); EXPECT_EQ(6, pile_.tiling().num_tiles_y()); for (int i = 0; i < pile_.tiling().num_tiles_x(); ++i) { for (int j = 0; j < pile_.tiling().num_tiles_y(); ++j) { FakePicturePile::PictureMapKey key(i, j); FakePicturePile::PictureMap& map = pile_.picture_map(); FakePicturePile::PictureMap::iterator it = map.find(key); EXPECT_TRUE(it != map.end() && it->second.get()); } } // We invalidated the newly exposed pixels. expected_invalidation = SubtractRegions(gfx::Rect(grow_down_tiling_size), gfx::Rect(base_tiling_size)); EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString()); invalidation.Clear(); UpdateWholePile(); UpdateAndExpandInvalidation(&invalidation, base_tiling_size, gfx::Rect(1, 1)); // We should have a recording for every tile. EXPECT_EQ(6, pile_.tiling().num_tiles_x()); EXPECT_EQ(6, pile_.tiling().num_tiles_y()); for (int i = 0; i < pile_.tiling().num_tiles_x(); ++i) { for (int j = 0; j < pile_.tiling().num_tiles_y(); ++j) { FakePicturePile::PictureMapKey key(i, j); FakePicturePile::PictureMap& map = pile_.picture_map(); FakePicturePile::PictureMap::iterator it = map.find(key); EXPECT_TRUE(it != map.end() && it->second.get()); } } // We invalidated the previously exposed pixels. expected_invalidation = SubtractRegions(gfx::Rect(grow_down_tiling_size), gfx::Rect(base_tiling_size)); EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString()); invalidation.Clear(); UpdateWholePile(); UpdateAndExpandInvalidation( &invalidation, grow_right_tiling_size, gfx::Rect(1, 1)); // We should have a recording for every tile. EXPECT_EQ(6, pile_.tiling().num_tiles_x()); EXPECT_EQ(6, pile_.tiling().num_tiles_y()); for (int i = 0; i < pile_.tiling().num_tiles_x(); ++i) { for (int j = 0; j < pile_.tiling().num_tiles_y(); ++j) { FakePicturePile::PictureMapKey key(i, j); FakePicturePile::PictureMap& map = pile_.picture_map(); FakePicturePile::PictureMap::iterator it = map.find(key); EXPECT_TRUE(it != map.end() && it->second.get()); } } // We invalidated the newly exposed pixels. expected_invalidation = SubtractRegions(gfx::Rect(grow_right_tiling_size), gfx::Rect(base_tiling_size)); EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString()); invalidation.Clear(); UpdateWholePile(); UpdateAndExpandInvalidation(&invalidation, base_tiling_size, gfx::Rect(1, 1)); // We should have lost the recordings that are now outside the tiling only. EXPECT_EQ(6, pile_.tiling().num_tiles_x()); EXPECT_EQ(6, pile_.tiling().num_tiles_y()); for (int i = 0; i < pile_.tiling().num_tiles_x(); ++i) { for (int j = 0; j < pile_.tiling().num_tiles_y(); ++j) { FakePicturePile::PictureMapKey key(i, j); FakePicturePile::PictureMap& map = pile_.picture_map(); FakePicturePile::PictureMap::iterator it = map.find(key); EXPECT_TRUE(it != map.end() && it->second.get()); } } // We invalidated the previously exposed pixels. expected_invalidation = SubtractRegions(gfx::Rect(grow_right_tiling_size), gfx::Rect(base_tiling_size)); EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString()); invalidation.Clear(); UpdateWholePile(); UpdateAndExpandInvalidation( &invalidation, grow_both_tiling_size, gfx::Rect(1, 1)); // We should have a recording for every tile. EXPECT_EQ(6, pile_.tiling().num_tiles_x()); EXPECT_EQ(6, pile_.tiling().num_tiles_y()); for (int i = 0; i < pile_.tiling().num_tiles_x(); ++i) { for (int j = 0; j < pile_.tiling().num_tiles_y(); ++j) { FakePicturePile::PictureMapKey key(i, j); FakePicturePile::PictureMap& map = pile_.picture_map(); FakePicturePile::PictureMap::iterator it = map.find(key); EXPECT_TRUE(it != map.end() && it->second.get()); } } // We invalidated the newly exposed pixels. expected_invalidation = SubtractRegions(gfx::Rect(grow_both_tiling_size), gfx::Rect(base_tiling_size)); EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString()); invalidation.Clear(); UpdateWholePile(); UpdateAndExpandInvalidation(&invalidation, base_tiling_size, gfx::Rect()); // We should have lost the recordings that are now outside the tiling only. EXPECT_EQ(6, pile_.tiling().num_tiles_x()); EXPECT_EQ(6, pile_.tiling().num_tiles_y()); for (int i = 0; i < pile_.tiling().num_tiles_x(); ++i) { for (int j = 0; j < pile_.tiling().num_tiles_y(); ++j) { FakePicturePile::PictureMapKey key(i, j); FakePicturePile::PictureMap& map = pile_.picture_map(); FakePicturePile::PictureMap::iterator it = map.find(key); EXPECT_TRUE(it != map.end() && it->second.get()); } } // We invalidated the previously exposed pixels. expected_invalidation = SubtractRegions(gfx::Rect(grow_both_tiling_size), gfx::Rect(base_tiling_size)); EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString()); invalidation.Clear(); } TEST_F(PicturePileTest, SolidRectangleIsSolid) { // If the client has no contents, the solid state will be true. Region invalidation1(tiling_rect()); UpdateAndExpandInvalidation(&invalidation1, tiling_size(), tiling_rect()); EXPECT_TRUE(pile_.is_solid_color()); EXPECT_EQ(static_cast(SK_ColorTRANSPARENT), pile_.solid_color()); // If there is a single rect that covers the view, the solid // state will be true. SkPaint paint; paint.setColor(SK_ColorCYAN); client_.add_draw_rect(tiling_rect(), paint); Region invalidation2(tiling_rect()); UpdateAndExpandInvalidation(&invalidation2, tiling_size(), tiling_rect()); EXPECT_TRUE(pile_.is_solid_color()); EXPECT_EQ(SK_ColorCYAN, pile_.solid_color()); // If a second smaller rect is draw that doesn't cover the viewport // completely, the solid state will be false. gfx::Rect smallRect = tiling_rect(); smallRect.Inset(10, 10, 10, 10); client_.add_draw_rect(smallRect, paint); Region invalidation3(tiling_rect()); UpdateAndExpandInvalidation(&invalidation3, tiling_size(), tiling_rect()); EXPECT_FALSE(pile_.is_solid_color()); // If a third rect is drawn over everything, we should be solid again. paint.setColor(SK_ColorRED); client_.add_draw_rect(tiling_rect(), paint); Region invalidation4(tiling_rect()); UpdateAndExpandInvalidation(&invalidation4, tiling_size(), tiling_rect()); EXPECT_TRUE(pile_.is_solid_color()); EXPECT_EQ(SK_ColorRED, pile_.solid_color()); // If we draw too many, we don't bother doing the analysis and we should no // longer be in a solid state. There are 8 rects, two clips and a translate. client_.add_draw_rect(tiling_rect(), paint); client_.add_draw_rect(tiling_rect(), paint); client_.add_draw_rect(tiling_rect(), paint); client_.add_draw_rect(tiling_rect(), paint); client_.add_draw_rect(tiling_rect(), paint); Region invalidation5(tiling_rect()); UpdateAndExpandInvalidation(&invalidation5, tiling_size(), tiling_rect()); EXPECT_FALSE(pile_.is_solid_color()); } TEST_F(PicturePileTest, NonSolidRectangleOnOffsettedLayerIsNonSolid) { gfx::Rect visible_rect(tiling_rect()); visible_rect.Offset(gfx::Vector2d(1000, 1000)); // The picture pile requires that the tiling completely encompass the viewport // to make this test work correctly since the recorded viewport is an // intersection of the tile size and viewport rect. This is possibly a flaw // in |PicturePile|. gfx::Size tiling_size(visible_rect.right(), visible_rect.bottom()); // |Setup()| will create pictures here that mess with the test, clear it! pile_.Clear(); SkPaint paint; paint.setColor(SK_ColorCYAN); // Add a rect that doesn't cover the viewport completely, the solid state // will be false. gfx::Rect smallRect = visible_rect; smallRect.Inset(10, 10, 10, 10); client_.add_draw_rect(smallRect, paint); Region invalidation(visible_rect); UpdateAndExpandInvalidation(&invalidation, tiling_size, visible_rect); EXPECT_FALSE(pile_.is_solid_color()); } TEST_F(PicturePileTest, SetEmptyBounds) { EXPECT_TRUE(pile_.is_solid_color()); EXPECT_FALSE(pile_.GetSize().IsEmpty()); EXPECT_FALSE(pile_.picture_map().empty()); EXPECT_TRUE(pile_.HasRecordings()); pile_.SetEmptyBounds(); EXPECT_FALSE(pile_.is_solid_color()); EXPECT_TRUE(pile_.GetSize().IsEmpty()); EXPECT_TRUE(pile_.picture_map().empty()); EXPECT_FALSE(pile_.HasRecordings()); } } // namespace } // namespace cc