| // 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 <map> |
| #include <utility> |
| |
| #include "cc/resources/picture_pile.h" |
| #include "cc/test/fake_content_layer_client.h" |
| #include "cc/test/fake_rendering_stats_instrumentation.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 TestPicturePile : public PicturePile { |
| public: |
| ~TestPicturePile() override {} |
| |
| using PicturePile::buffer_pixels; |
| using PicturePile::CanRasterSlowTileCheck; |
| using PicturePile::Clear; |
| |
| PictureMap& picture_map() { return picture_map_; } |
| const gfx::Rect& recorded_viewport() const { return recorded_viewport_; } |
| |
| bool CanRasterLayerRect(const gfx::Rect& layer_rect) { |
| return CanRaster(1.f, layer_rect); |
| } |
| |
| typedef PicturePile::PictureInfo PictureInfo; |
| typedef PicturePile::PictureMapKey PictureMapKey; |
| typedef PicturePile::PictureMap PictureMap; |
| }; |
| |
| class PicturePileTestBase { |
| public: |
| PicturePileTestBase() |
| : background_color_(SK_ColorBLUE), |
| min_scale_(0.125), |
| frame_number_(0), |
| contents_opaque_(false) {} |
| |
| 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_.tiling_size(); } |
| gfx::Rect tiling_rect() const { return gfx::Rect(pile_.tiling_size()); } |
| |
| bool UpdateAndExpandInvalidation(Region* invalidation, |
| const gfx::Size& layer_size, |
| const gfx::Rect& visible_layer_rect) { |
| frame_number_++; |
| return pile_.UpdateAndExpandInvalidation(&client_, |
| invalidation, |
| background_color_, |
| contents_opaque_, |
| false, |
| layer_size, |
| visible_layer_rect, |
| frame_number_, |
| Picture::RECORD_NORMALLY, |
| &stats_instrumentation_); |
| } |
| |
| 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_; |
| FakeRenderingStatsInstrumentation stats_instrumentation_; |
| TestPicturePile pile_; |
| SkColor background_color_; |
| float min_scale_; |
| int frame_number_; |
| bool contents_opaque_; |
| }; |
| |
| class PicturePileTest : public PicturePileTestBase, public testing::Test { |
| public: |
| virtual void SetUp() override { InitializeData(); } |
| }; |
| |
| TEST_F(PicturePileTest, InvalidationOnTileBorderOutsideInterestRect) { |
| // Don't expand the interest rect past what we invalidate. |
| pile_.SetPixelRecordDistanceForTesting(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()); |
| |
| TestPicturePile::PictureInfo& picture_info = |
| pile_.picture_map().find(TestPicturePile::PictureMapKey(0, 0))->second; |
| // We should have a picture. |
| EXPECT_TRUE(!!picture_info.GetPicture()); |
| gfx::Rect picture_rect = gfx::ScaleToEnclosedRect( |
| picture_info.GetPicture()->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()); |
| |
| TestPicturePile::PictureInfo& picture_info = |
| pile_.picture_map().find(TestPicturePile::PictureMapKey(0, 0))->second; |
| EXPECT_TRUE(!!picture_info.GetPicture()); |
| |
| int expected_inflation = pile_.buffer_pixels(); |
| |
| const Picture* base_picture = picture_info.GetPicture(); |
| gfx::Rect base_picture_rect(pile_.tiling_size()); |
| base_picture_rect.Inset(-expected_inflation, -expected_inflation); |
| EXPECT_EQ(base_picture_rect.ToString(), |
| base_picture->LayerRect().ToString()); |
| } |
| |
| TEST_F(PicturePileTest, InvalidateOnTileBoundaryInflated) { |
| gfx::Size new_tiling_size = |
| gfx::ToCeiledSize(gfx::ScaleSize(pile_.tiling_size(), 2.f)); |
| // This creates initial pictures. |
| SetTilingSize(new_tiling_size); |
| |
| // Due to border pixels, we should have 3 tiles. |
| EXPECT_EQ(3, pile_.tiling().num_tiles_x()); |
| EXPECT_EQ(3, pile_.tiling().num_tiles_y()); |
| |
| // We should have 1/.125 - 1 = 7 border pixels. |
| EXPECT_EQ(7, pile_.buffer_pixels()); |
| EXPECT_EQ(7, pile_.tiling().border_texels()); |
| |
| // Invalidate everything to have a non zero invalidation frequency. |
| UpdateWholePile(); |
| |
| // Invalidate something just over a tile boundary by a single pixel. |
| // This will invalidate the tile (1, 1), as well as 1 row of pixels in (1, 0). |
| Region invalidate_rect( |
| gfx::Rect(pile_.tiling().TileBoundsWithBorder(0, 0).right(), |
| pile_.tiling().TileBoundsWithBorder(0, 0).bottom() - 1, |
| 50, |
| 50)); |
| Region expected_invalidation = invalidate_rect; |
| UpdateAndExpandInvalidation(&invalidate_rect, tiling_size(), tiling_rect()); |
| EXPECT_EQ(expected_invalidation.ToString(), invalidate_rect.ToString()); |
| |
| for (int i = 0; i < pile_.tiling().num_tiles_x(); ++i) { |
| for (int j = 0; j < pile_.tiling().num_tiles_y(); ++j) { |
| TestPicturePile::PictureInfo& picture_info = |
| pile_.picture_map() |
| .find(TestPicturePile::PictureMapKey(i, j)) |
| ->second; |
| |
| // Expect (1, 1) and (1, 0) to be invalidated once more |
| // than the rest of the tiles. |
| if (i == 1 && (j == 0 || j == 1)) { |
| EXPECT_FLOAT_EQ( |
| 2.0f / TestPicturePile::PictureInfo::INVALIDATION_FRAMES_TRACKED, |
| picture_info.GetInvalidationFrequencyForTesting()); |
| } else { |
| EXPECT_FLOAT_EQ( |
| 1.0f / TestPicturePile::PictureInfo::INVALIDATION_FRAMES_TRACKED, |
| picture_info.GetInvalidationFrequencyForTesting()); |
| } |
| } |
| } |
| } |
| |
| 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)); |
| SetTilingSize(new_tiling_size); |
| |
| gfx::Rect viewport(tiling_size().width(), 1); |
| |
| // Update the whole pile until the invalidation frequency is high. |
| for (int frame = 0; frame < 33; ++frame) { |
| UpdateWholePile(); |
| } |
| |
| // Make sure we have a high invalidation frequency. |
| for (int i = 0; i < pile_.tiling().num_tiles_x(); ++i) { |
| for (int j = 0; j < pile_.tiling().num_tiles_y(); ++j) { |
| TestPicturePile::PictureInfo& picture_info = |
| pile_.picture_map() |
| .find(TestPicturePile::PictureMapKey(i, j)) |
| ->second; |
| EXPECT_FLOAT_EQ(1.0f, picture_info.GetInvalidationFrequencyForTesting()) |
| << "i " << i << " j " << j; |
| } |
| } |
| |
| // Update once more with a small viewport. |
| Region invalidation(tiling_rect()); |
| UpdateAndExpandInvalidation(&invalidation, tiling_size(), viewport); |
| EXPECT_EQ(tiling_rect().ToString(), invalidation.ToString()); |
| |
| for (int i = 0; i < pile_.tiling().num_tiles_x(); ++i) { |
| for (int j = 0; j < pile_.tiling().num_tiles_y(); ++j) { |
| TestPicturePile::PictureInfo& picture_info = |
| pile_.picture_map() |
| .find(TestPicturePile::PictureMapKey(i, j)) |
| ->second; |
| EXPECT_FLOAT_EQ(1.0f, picture_info.GetInvalidationFrequencyForTesting()); |
| |
| // If the y far enough away we expect to find no picture (no re-recording |
| // happened). For close y, the picture should change. |
| if (j >= 2) |
| EXPECT_FALSE(picture_info.GetPicture()) << "i " << i << " j " << j; |
| else |
| EXPECT_TRUE(picture_info.GetPicture()) << "i " << i << " j " << j; |
| } |
| } |
| |
| // Update a partial tile that doesn't get recorded. We should expand the |
| // invalidation to the entire tiles that overlap it. |
| Region small_invalidation = |
| gfx::Rect(pile_.tiling().TileBounds(3, 4).x(), |
| pile_.tiling().TileBounds(3, 4).y() + 10, |
| 1, |
| 1); |
| UpdateAndExpandInvalidation(&small_invalidation, tiling_size(), viewport); |
| EXPECT_TRUE(small_invalidation.Contains(gfx::UnionRects( |
| pile_.tiling().TileBounds(2, 4), pile_.tiling().TileBounds(3, 4)))) |
| << small_invalidation.ToString(); |
| |
| // Now update with no invalidation and full viewport |
| Region empty_invalidation; |
| UpdateAndExpandInvalidation( |
| &empty_invalidation, tiling_size(), tiling_rect()); |
| EXPECT_EQ(Region().ToString(), empty_invalidation.ToString()); |
| |
| for (int i = 0; i < pile_.tiling().num_tiles_x(); ++i) { |
| for (int j = 0; j < pile_.tiling().num_tiles_y(); ++j) { |
| TestPicturePile::PictureInfo& picture_info = |
| pile_.picture_map() |
| .find(TestPicturePile::PictureMapKey(i, j)) |
| ->second; |
| // Expect the invalidation frequency to be less than 1, since we just |
| // updated with no invalidations. |
| EXPECT_LT(picture_info.GetInvalidationFrequencyForTesting(), 1.f); |
| |
| // We expect that there are pictures everywhere now. |
| EXPECT_TRUE(picture_info.GetPicture()) << "i " << i << " j " << j; |
| } |
| } |
| } |
| |
| 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, FrequentInvalidationCanRaster) { |
| // This test makes sure that if part of the page is frequently invalidated |
| // and doesn't get re-recorded, then CanRaster is not true for any |
| // tiles touching it, but is true for adjacent tiles, even if it |
| // overlaps on borders (edge case). |
| gfx::Size new_tiling_size = |
| gfx::ToCeiledSize(gfx::ScaleSize(pile_.tiling_size(), 4.f)); |
| SetTilingSize(new_tiling_size); |
| |
| gfx::Rect tile01_borders = pile_.tiling().TileBoundsWithBorder(0, 1); |
| gfx::Rect tile02_borders = pile_.tiling().TileBoundsWithBorder(0, 2); |
| gfx::Rect tile01_noborders = pile_.tiling().TileBounds(0, 1); |
| gfx::Rect tile02_noborders = pile_.tiling().TileBounds(0, 2); |
| |
| // Sanity check these two tiles are overlapping with borders, since this is |
| // what the test is trying to repro. |
| EXPECT_TRUE(tile01_borders.Intersects(tile02_borders)); |
| EXPECT_FALSE(tile01_noborders.Intersects(tile02_noborders)); |
| UpdateWholePile(); |
| EXPECT_TRUE(pile_.CanRasterLayerRect(tile01_noborders)); |
| EXPECT_TRUE(pile_.CanRasterSlowTileCheck(tile01_noborders)); |
| EXPECT_TRUE(pile_.CanRasterLayerRect(tile02_noborders)); |
| EXPECT_TRUE(pile_.CanRasterSlowTileCheck(tile02_noborders)); |
| // Sanity check that an initial paint goes down the fast path of having |
| // a valid recorded viewport. |
| EXPECT_TRUE(!pile_.recorded_viewport().IsEmpty()); |
| |
| // Update the whole layer until the invalidation frequency is high. |
| for (int frame = 0; frame < 33; ++frame) { |
| UpdateWholePile(); |
| } |
| |
| // Update once more with a small viewport. |
| gfx::Rect viewport(tiling_size().width(), 1); |
| Region invalidation(tiling_rect()); |
| UpdateAndExpandInvalidation(&invalidation, tiling_size(), viewport); |
| EXPECT_EQ(tiling_rect().ToString(), invalidation.ToString()); |
| |
| // Sanity check some pictures exist and others don't. |
| EXPECT_TRUE(pile_.picture_map() |
| .find(TestPicturePile::PictureMapKey(0, 1)) |
| ->second.GetPicture()); |
| EXPECT_FALSE(pile_.picture_map() |
| .find(TestPicturePile::PictureMapKey(0, 2)) |
| ->second.GetPicture()); |
| |
| EXPECT_TRUE(pile_.CanRasterLayerRect(tile01_noborders)); |
| EXPECT_TRUE(pile_.CanRasterSlowTileCheck(tile01_noborders)); |
| EXPECT_FALSE(pile_.CanRasterLayerRect(tile02_noborders)); |
| EXPECT_FALSE(pile_.CanRasterSlowTileCheck(tile02_noborders)); |
| } |
| |
| 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<Corner> { |
| protected: |
| virtual 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) { |
| TestPicturePile::PictureMapKey key(i, j); |
| TestPicturePile::PictureMap& map = pile_.picture_map(); |
| TestPicturePile::PictureMap::iterator it = map.find(key); |
| EXPECT_TRUE(it != map.end() && it->second.GetPicture()); |
| } |
| } |
| |
| UpdateAndExpandInvalidation( |
| &invalidation, |
| grow_down_tiling_size, |
| CornerSinglePixelRect(corner, grow_down_tiling_size)); |
| |
| // We should have lost the recordings in the bottom row. |
| 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) { |
| TestPicturePile::PictureMapKey key(i, j); |
| TestPicturePile::PictureMap& map = pile_.picture_map(); |
| TestPicturePile::PictureMap::iterator it = map.find(key); |
| EXPECT_EQ(j < 5, it != map.end() && it->second.GetPicture()); |
| } |
| } |
| |
| // 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)); |
| |
| // 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) { |
| TestPicturePile::PictureMapKey key(i, j); |
| TestPicturePile::PictureMap& map = pile_.picture_map(); |
| TestPicturePile::PictureMap::iterator it = map.find(key); |
| EXPECT_EQ(j < 6, it != map.end() && it->second.GetPicture()); |
| } |
| } |
| |
| // No invalidation when shrinking. |
| expected_invalidation.Clear(); |
| 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 the recordings in the right column. |
| 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) { |
| TestPicturePile::PictureMapKey key(i, j); |
| TestPicturePile::PictureMap& map = pile_.picture_map(); |
| TestPicturePile::PictureMap::iterator it = map.find(key); |
| EXPECT_EQ(i < 5, it != map.end() && it->second.GetPicture()); |
| } |
| } |
| |
| // 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)); |
| |
| // 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) { |
| TestPicturePile::PictureMapKey key(i, j); |
| TestPicturePile::PictureMap& map = pile_.picture_map(); |
| TestPicturePile::PictureMap::iterator it = map.find(key); |
| EXPECT_EQ(i < 6, it != map.end() && it->second.GetPicture()); |
| } |
| } |
| |
| // No invalidation when shrinking. |
| expected_invalidation.Clear(); |
| 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) { |
| TestPicturePile::PictureMapKey key(i, j); |
| TestPicturePile::PictureMap& map = pile_.picture_map(); |
| TestPicturePile::PictureMap::iterator it = map.find(key); |
| EXPECT_EQ(i < 5 && j < 5, it != map.end() && it->second.GetPicture()); |
| } |
| } |
| |
| // 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. |
| 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, 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) { |
| TestPicturePile::PictureMapKey key(i, j); |
| TestPicturePile::PictureMap& map = pile_.picture_map(); |
| TestPicturePile::PictureMap::iterator it = map.find(key); |
| EXPECT_EQ(i < 6 && j < 6, it != map.end() && it->second.GetPicture()); |
| } |
| } |
| |
| // No invalidation when shrinking. |
| expected_invalidation.Clear(); |
| 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) { |
| TestPicturePile::PictureMapKey key(i, j); |
| TestPicturePile::PictureMap& map = pile_.picture_map(); |
| TestPicturePile::PictureMap::iterator it = map.find(key); |
| EXPECT_TRUE(it != map.end() && it->second.GetPicture()); |
| } |
| } |
| |
| UpdateAndExpandInvalidation( |
| &invalidation, |
| grow_down_tiling_size, |
| CornerSinglePixelRect(corner, grow_down_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) { |
| TestPicturePile::PictureMapKey key(i, j); |
| TestPicturePile::PictureMap& map = pile_.picture_map(); |
| TestPicturePile::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.GetPicture()); |
| } |
| } |
| |
| // We invalidated the bottom row outside the new interest rect. The tile that |
| // insects the interest rect in invalidated only on its new pixels. |
| 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(); |
| |
| UpdateWholePile(); |
| UpdateAndExpandInvalidation(&invalidation, |
| base_tiling_size, |
| CornerSinglePixelRect(corner, base_tiling_size)); |
| |
| // We should have lost nothing. |
| 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) { |
| TestPicturePile::PictureMapKey key(i, j); |
| TestPicturePile::PictureMap& map = pile_.picture_map(); |
| TestPicturePile::PictureMap::iterator it = map.find(key); |
| EXPECT_TRUE(it != map.end() && it->second.GetPicture()); |
| } |
| } |
| |
| // We invalidated nothing. |
| expected_invalidation.Clear(); |
| 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 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) { |
| TestPicturePile::PictureMapKey key(i, j); |
| TestPicturePile::PictureMap& map = pile_.picture_map(); |
| TestPicturePile::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.GetPicture()); |
| } |
| } |
| |
| // We invalidated the right column outside the new interest rect. The tile |
| // that insects the interest rect in invalidated only on its new pixels. |
| 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(); |
| |
| UpdateWholePile(); |
| UpdateAndExpandInvalidation(&invalidation, |
| base_tiling_size, |
| CornerSinglePixelRect(corner, base_tiling_size)); |
| |
| // We should have lost nothing. |
| 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) { |
| TestPicturePile::PictureMapKey key(i, j); |
| TestPicturePile::PictureMap& map = pile_.picture_map(); |
| TestPicturePile::PictureMap::iterator it = map.find(key); |
| EXPECT_TRUE(it != map.end() && it->second.GetPicture()); |
| } |
| } |
| |
| // We invalidated nothing. |
| expected_invalidation.Clear(); |
| 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. The |
| // tile that insects the interest rect in invalidated only on its new 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) { |
| TestPicturePile::PictureMapKey key(i, j); |
| TestPicturePile::PictureMap& map = pile_.picture_map(); |
| TestPicturePile::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.GetPicture()) |
| << 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 pixels. |
| 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(); |
| |
| UpdateWholePile(); |
| UpdateAndExpandInvalidation(&invalidation, |
| base_tiling_size, |
| CornerSinglePixelRect(corner, base_tiling_size)); |
| |
| // We should have lost nothing. |
| 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) { |
| TestPicturePile::PictureMapKey key(i, j); |
| TestPicturePile::PictureMap& map = pile_.picture_map(); |
| TestPicturePile::PictureMap::iterator it = map.find(key); |
| EXPECT_TRUE(it != map.end() && it->second.GetPicture()); |
| } |
| } |
| |
| // We invalidated nothing. |
| expected_invalidation.Clear(); |
| 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) { |
| TestPicturePile::PictureMapKey key(i, j); |
| TestPicturePile::PictureMap& map = pile_.picture_map(); |
| TestPicturePile::PictureMap::iterator it = map.find(key); |
| EXPECT_TRUE(it != map.end() && it->second.GetPicture()); |
| } |
| } |
| |
| 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) { |
| TestPicturePile::PictureMapKey key(i, j); |
| TestPicturePile::PictureMap& map = pile_.picture_map(); |
| TestPicturePile::PictureMap::iterator it = map.find(key); |
| EXPECT_TRUE(it != map.end() && it->second.GetPicture()); |
| } |
| } |
| |
| // 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) { |
| TestPicturePile::PictureMapKey key(i, j); |
| TestPicturePile::PictureMap& map = pile_.picture_map(); |
| TestPicturePile::PictureMap::iterator it = map.find(key); |
| EXPECT_TRUE(it != map.end() && it->second.GetPicture()); |
| } |
| } |
| |
| // No invalidation when shrinking. |
| EXPECT_EQ(Region().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) { |
| TestPicturePile::PictureMapKey key(i, j); |
| TestPicturePile::PictureMap& map = pile_.picture_map(); |
| TestPicturePile::PictureMap::iterator it = map.find(key); |
| EXPECT_TRUE(it != map.end() && it->second.GetPicture()); |
| } |
| } |
| |
| // 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) { |
| TestPicturePile::PictureMapKey key(i, j); |
| TestPicturePile::PictureMap& map = pile_.picture_map(); |
| TestPicturePile::PictureMap::iterator it = map.find(key); |
| EXPECT_TRUE(it != map.end() && it->second.GetPicture()); |
| } |
| } |
| |
| // No invalidation when shrinking. |
| EXPECT_EQ(Region().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) { |
| TestPicturePile::PictureMapKey key(i, j); |
| TestPicturePile::PictureMap& map = pile_.picture_map(); |
| TestPicturePile::PictureMap::iterator it = map.find(key); |
| EXPECT_TRUE(it != map.end() && it->second.GetPicture()); |
| } |
| } |
| |
| // 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) { |
| TestPicturePile::PictureMapKey key(i, j); |
| TestPicturePile::PictureMap& map = pile_.picture_map(); |
| TestPicturePile::PictureMap::iterator it = map.find(key); |
| EXPECT_TRUE(it != map.end() && it->second.GetPicture()); |
| } |
| } |
| |
| // No invalidation when shrinking. |
| EXPECT_EQ(Region().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) { |
| TestPicturePile::PictureMapKey key(i, j); |
| TestPicturePile::PictureMap& map = pile_.picture_map(); |
| TestPicturePile::PictureMap::iterator it = map.find(key); |
| EXPECT_TRUE(it != map.end() && it->second.GetPicture()); |
| } |
| } |
| |
| 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) { |
| TestPicturePile::PictureMapKey key(i, j); |
| TestPicturePile::PictureMap& map = pile_.picture_map(); |
| TestPicturePile::PictureMap::iterator it = map.find(key); |
| EXPECT_TRUE(it != map.end() && it->second.GetPicture()); |
| } |
| } |
| |
| // 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) { |
| TestPicturePile::PictureMapKey key(i, j); |
| TestPicturePile::PictureMap& map = pile_.picture_map(); |
| TestPicturePile::PictureMap::iterator it = map.find(key); |
| EXPECT_TRUE(it != map.end() && it->second.GetPicture()); |
| } |
| } |
| |
| // No invalidation when shrinking. |
| EXPECT_EQ(Region().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) { |
| TestPicturePile::PictureMapKey key(i, j); |
| TestPicturePile::PictureMap& map = pile_.picture_map(); |
| TestPicturePile::PictureMap::iterator it = map.find(key); |
| EXPECT_TRUE(it != map.end() && it->second.GetPicture()); |
| } |
| } |
| |
| // 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) { |
| TestPicturePile::PictureMapKey key(i, j); |
| TestPicturePile::PictureMap& map = pile_.picture_map(); |
| TestPicturePile::PictureMap::iterator it = map.find(key); |
| EXPECT_TRUE(it != map.end() && it->second.GetPicture()); |
| } |
| } |
| |
| // No invalidation when shrinking. |
| EXPECT_EQ(Region().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) { |
| TestPicturePile::PictureMapKey key(i, j); |
| TestPicturePile::PictureMap& map = pile_.picture_map(); |
| TestPicturePile::PictureMap::iterator it = map.find(key); |
| EXPECT_TRUE(it != map.end() && it->second.GetPicture()); |
| } |
| } |
| |
| // 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) { |
| TestPicturePile::PictureMapKey key(i, j); |
| TestPicturePile::PictureMap& map = pile_.picture_map(); |
| TestPicturePile::PictureMap::iterator it = map.find(key); |
| EXPECT_TRUE(it != map.end() && it->second.GetPicture()); |
| } |
| } |
| |
| // No invalidation when shrinking. |
| EXPECT_EQ(Region().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<SkColor>(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_.tiling_size().IsEmpty()); |
| EXPECT_FALSE(pile_.picture_map().empty()); |
| EXPECT_TRUE(pile_.HasRecordings()); |
| pile_.SetEmptyBounds(); |
| EXPECT_FALSE(pile_.is_solid_color()); |
| EXPECT_TRUE(pile_.tiling_size().IsEmpty()); |
| EXPECT_TRUE(pile_.picture_map().empty()); |
| EXPECT_FALSE(pile_.HasRecordings()); |
| } |
| |
| } // namespace |
| } // namespace cc |