// Copyright 2012 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 "cc/layers/tiled_layer_impl.h" #include "cc/layers/append_quads_data.h" #include "cc/quads/tile_draw_quad.h" #include "cc/resources/layer_tiling_data.h" #include "cc/test/fake_impl_proxy.h" #include "cc/test/fake_layer_tree_host_impl.h" #include "cc/test/layer_test_common.h" #include "cc/test/test_task_graph_runner.h" #include "cc/trees/single_thread_proxy.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" namespace cc { namespace { class TiledLayerImplTest : public testing::Test { public: TiledLayerImplTest() : host_impl_(&proxy_, &shared_bitmap_manager_, &task_graph_runner_) {} scoped_ptr CreateLayerNoTiles( const gfx::Size& tile_size, const gfx::Size& layer_size, LayerTilingData::BorderTexelOption border_texels) { scoped_ptr layer = TiledLayerImpl::Create(host_impl_.active_tree(), 1); scoped_ptr tiler = LayerTilingData::Create(tile_size, border_texels); tiler->SetTilingSize(layer_size); layer->SetTilingData(*tiler); layer->set_skips_draw(false); layer->draw_properties().visible_content_rect = gfx::Rect(layer_size); layer->draw_properties().opacity = 1; layer->SetBounds(layer_size); layer->SetContentBounds(layer_size); layer->SetHasRenderSurface(true); layer->draw_properties().render_target = layer.get(); return layer.Pass(); } // Create a default tiled layer with textures for all tiles and a default // visibility of the entire layer size. scoped_ptr CreateLayer( const gfx::Size& tile_size, const gfx::Size& layer_size, LayerTilingData::BorderTexelOption border_texels) { scoped_ptr layer = CreateLayerNoTiles(tile_size, layer_size, border_texels); ResourceProvider::ResourceId resource_id = 1; for (int i = 0; i < layer->TilingForTesting()->num_tiles_x(); ++i) { for (int j = 0; j < layer->TilingForTesting()->num_tiles_y(); ++j) layer->PushTileProperties(i, j, resource_id++, false); } return layer.Pass(); } void GetQuads(RenderPass* render_pass, const gfx::Size& tile_size, const gfx::Size& layer_size, LayerTilingData::BorderTexelOption border_texel_option, const gfx::Rect& visible_content_rect) { scoped_ptr layer = CreateLayer(tile_size, layer_size, border_texel_option); layer->draw_properties().visible_content_rect = visible_content_rect; layer->SetBounds(layer_size); AppendQuadsData data; layer->AppendQuads(render_pass, &data); } protected: FakeImplProxy proxy_; TestSharedBitmapManager shared_bitmap_manager_; TestTaskGraphRunner task_graph_runner_; FakeLayerTreeHostImpl host_impl_; }; TEST_F(TiledLayerImplTest, EmptyQuadList) { gfx::Size tile_size(90, 90); int num_tiles_x = 8; int num_tiles_y = 4; gfx::Size layer_size(tile_size.width() * num_tiles_x, tile_size.height() * num_tiles_y); // Verify default layer does creates quads { scoped_ptr layer = CreateLayer(tile_size, layer_size, LayerTilingData::NO_BORDER_TEXELS); scoped_ptr render_pass = RenderPass::Create(); AppendQuadsData data; EXPECT_TRUE(layer->WillDraw(DRAW_MODE_HARDWARE, nullptr)); layer->AppendQuads(render_pass.get(), &data); layer->DidDraw(nullptr); unsigned num_tiles = num_tiles_x * num_tiles_y; EXPECT_EQ(render_pass->quad_list.size(), num_tiles); } // Layer with empty visible layer rect produces no quads { scoped_ptr layer = CreateLayer(tile_size, layer_size, LayerTilingData::NO_BORDER_TEXELS); layer->draw_properties().visible_content_rect = gfx::Rect(); scoped_ptr render_pass = RenderPass::Create(); EXPECT_FALSE(layer->WillDraw(DRAW_MODE_HARDWARE, nullptr)); } // Layer with non-intersecting visible layer rect produces no quads { scoped_ptr layer = CreateLayer(tile_size, layer_size, LayerTilingData::NO_BORDER_TEXELS); gfx::Rect outside_bounds(-100, -100, 50, 50); layer->draw_properties().visible_content_rect = outside_bounds; scoped_ptr render_pass = RenderPass::Create(); AppendQuadsData data; EXPECT_TRUE(layer->WillDraw(DRAW_MODE_HARDWARE, nullptr)); layer->AppendQuads(render_pass.get(), &data); layer->DidDraw(nullptr); EXPECT_EQ(render_pass->quad_list.size(), 0u); } // Layer with skips draw produces no quads { scoped_ptr layer = CreateLayer(tile_size, layer_size, LayerTilingData::NO_BORDER_TEXELS); layer->set_skips_draw(true); scoped_ptr render_pass = RenderPass::Create(); AppendQuadsData data; layer->AppendQuads(render_pass.get(), &data); EXPECT_EQ(render_pass->quad_list.size(), 0u); } } TEST_F(TiledLayerImplTest, Checkerboarding) { gfx::Size tile_size(10, 10); int num_tiles_x = 2; int num_tiles_y = 2; gfx::Size layer_size(tile_size.width() * num_tiles_x, tile_size.height() * num_tiles_y); scoped_ptr layer = CreateLayer(tile_size, layer_size, LayerTilingData::NO_BORDER_TEXELS); // No checkerboarding { scoped_ptr render_pass = RenderPass::Create(); AppendQuadsData data; layer->AppendQuads(render_pass.get(), &data); EXPECT_EQ(render_pass->quad_list.size(), 4u); EXPECT_EQ(0u, data.num_missing_tiles); for (const auto& quad : render_pass->quad_list) EXPECT_EQ(quad->material, DrawQuad::TILED_CONTENT); } for (int i = 0; i < num_tiles_x; ++i) for (int j = 0; j < num_tiles_y; ++j) layer->PushTileProperties(i, j, 0, false); // All checkerboarding { scoped_ptr render_pass = RenderPass::Create(); AppendQuadsData data; layer->AppendQuads(render_pass.get(), &data); EXPECT_LT(0u, data.num_missing_tiles); EXPECT_EQ(render_pass->quad_list.size(), 4u); for (const auto& quad : render_pass->quad_list) EXPECT_NE(quad->material, DrawQuad::TILED_CONTENT); } } // Test with both border texels and without. #define WITH_AND_WITHOUT_BORDER_TEST(text_fixture_name) \ TEST_F(TiledLayerImplBorderTest, text_fixture_name##NoBorders) { \ text_fixture_name(LayerTilingData::NO_BORDER_TEXELS); \ } \ TEST_F(TiledLayerImplBorderTest, text_fixture_name##HasBorders) { \ text_fixture_name(LayerTilingData::HAS_BORDER_TEXELS); \ } class TiledLayerImplBorderTest : public TiledLayerImplTest { public: void CoverageVisibleRectOnTileBoundaries( LayerTilingData::BorderTexelOption borders) { gfx::Size layer_size(1000, 1000); scoped_ptr render_pass = RenderPass::Create(); GetQuads(render_pass.get(), gfx::Size(100, 100), layer_size, borders, gfx::Rect(layer_size)); LayerTestCommon::VerifyQuadsExactlyCoverRect(render_pass->quad_list, gfx::Rect(layer_size)); } void CoverageVisibleRectIntersectsTiles( LayerTilingData::BorderTexelOption borders) { // This rect intersects the middle 3x3 of the 5x5 tiles. gfx::Point top_left(65, 73); gfx::Point bottom_right(182, 198); gfx::Rect visible_content_rect = gfx::BoundingRect(top_left, bottom_right); gfx::Size layer_size(250, 250); scoped_ptr render_pass = RenderPass::Create(); GetQuads(render_pass.get(), gfx::Size(50, 50), gfx::Size(250, 250), LayerTilingData::NO_BORDER_TEXELS, visible_content_rect); LayerTestCommon::VerifyQuadsExactlyCoverRect(render_pass->quad_list, visible_content_rect); } void CoverageVisibleRectIntersectsBounds( LayerTilingData::BorderTexelOption borders) { gfx::Size layer_size(220, 210); gfx::Rect visible_content_rect(layer_size); scoped_ptr render_pass = RenderPass::Create(); GetQuads(render_pass.get(), gfx::Size(100, 100), layer_size, LayerTilingData::NO_BORDER_TEXELS, visible_content_rect); LayerTestCommon::VerifyQuadsExactlyCoverRect(render_pass->quad_list, visible_content_rect); } }; WITH_AND_WITHOUT_BORDER_TEST(CoverageVisibleRectOnTileBoundaries); WITH_AND_WITHOUT_BORDER_TEST(CoverageVisibleRectIntersectsTiles); WITH_AND_WITHOUT_BORDER_TEST(CoverageVisibleRectIntersectsBounds); TEST_F(TiledLayerImplTest, TextureInfoForLayerNoBorders) { gfx::Size tile_size(50, 50); gfx::Size layer_size(250, 250); scoped_ptr render_pass = RenderPass::Create(); GetQuads(render_pass.get(), tile_size, layer_size, LayerTilingData::NO_BORDER_TEXELS, gfx::Rect(layer_size)); for (auto iter = render_pass->quad_list.cbegin(); iter != render_pass->quad_list.cend(); ++iter) { const TileDrawQuad* quad = TileDrawQuad::MaterialCast(*iter); EXPECT_NE(0u, quad->resource_id) << LayerTestCommon::quad_string << iter.index(); EXPECT_EQ(gfx::RectF(gfx::PointF(), tile_size), quad->tex_coord_rect) << LayerTestCommon::quad_string << iter.index(); EXPECT_EQ(tile_size, quad->texture_size) << LayerTestCommon::quad_string << iter.index(); } } TEST_F(TiledLayerImplTest, GPUMemoryUsage) { gfx::Size tile_size(20, 30); int num_tiles_x = 12; int num_tiles_y = 32; gfx::Size layer_size(tile_size.width() * num_tiles_x, tile_size.height() * num_tiles_y); scoped_ptr layer = CreateLayerNoTiles( tile_size, layer_size, LayerTilingData::NO_BORDER_TEXELS); EXPECT_EQ(layer->GPUMemoryUsageInBytes(), 0u); ResourceProvider::ResourceId resource_id = 1; layer->PushTileProperties(0, 1, resource_id++, false); layer->PushTileProperties(2, 3, resource_id++, false); layer->PushTileProperties(2, 0, resource_id++, false); EXPECT_EQ( layer->GPUMemoryUsageInBytes(), static_cast(3 * 4 * tile_size.width() * tile_size.height())); ResourceProvider::ResourceId empty_resource(0); layer->PushTileProperties(0, 1, empty_resource, false); layer->PushTileProperties(2, 3, empty_resource, false); layer->PushTileProperties(2, 0, empty_resource, false); EXPECT_EQ(layer->GPUMemoryUsageInBytes(), 0u); } TEST_F(TiledLayerImplTest, EmptyMask) { gfx::Size tile_size(20, 20); gfx::Size layer_size(0, 0); scoped_ptr layer = CreateLayer(tile_size, layer_size, LayerTilingData::NO_BORDER_TEXELS); ResourceProvider::ResourceId mask_resource_id; gfx::Size mask_texture_size; layer->GetContentsResourceId(&mask_resource_id, &mask_texture_size); EXPECT_EQ(0u, mask_resource_id); EXPECT_EQ(0, layer->TilingForTesting()->num_tiles_x()); EXPECT_EQ(0, layer->TilingForTesting()->num_tiles_y()); } TEST_F(TiledLayerImplTest, Occlusion) { gfx::Size tile_size(100, 100); gfx::Size layer_bounds(1000, 1000); gfx::Size viewport_size(1000, 1000); LayerTestCommon::LayerImplTest impl; TiledLayerImpl* tiled_layer = impl.AddChildToRoot(); tiled_layer->SetBounds(layer_bounds); tiled_layer->SetContentBounds(layer_bounds); tiled_layer->SetDrawsContent(true); tiled_layer->set_skips_draw(false); scoped_ptr tiler = LayerTilingData::Create(tile_size, LayerTilingData::NO_BORDER_TEXELS); tiler->SetTilingSize(layer_bounds); tiled_layer->SetTilingData(*tiler); ResourceProvider::ResourceId resource_id = 1; for (int i = 0; i < tiled_layer->TilingForTesting()->num_tiles_x(); ++i) { for (int j = 0; j < tiled_layer->TilingForTesting()->num_tiles_y(); ++j) tiled_layer->PushTileProperties(i, j, resource_id++, false); } impl.CalcDrawProps(viewport_size); { SCOPED_TRACE("No occlusion"); gfx::Rect occluded; impl.AppendQuadsWithOcclusion(tiled_layer, occluded); LayerTestCommon::VerifyQuadsExactlyCoverRect(impl.quad_list(), gfx::Rect(layer_bounds)); EXPECT_EQ(100u, impl.quad_list().size()); } { SCOPED_TRACE("Full occlusion"); gfx::Rect occluded(tiled_layer->visible_content_rect()); impl.AppendQuadsWithOcclusion(tiled_layer, occluded); LayerTestCommon::VerifyQuadsExactlyCoverRect(impl.quad_list(), gfx::Rect()); EXPECT_EQ(impl.quad_list().size(), 0u); } { SCOPED_TRACE("Partial occlusion"); gfx::Rect occluded(150, 0, 200, 1000); impl.AppendQuadsWithOcclusion(tiled_layer, occluded); size_t partially_occluded_count = 0; LayerTestCommon::VerifyQuadsAreOccluded( impl.quad_list(), occluded, &partially_occluded_count); // The layer outputs one quad, which is partially occluded. EXPECT_EQ(100u - 10u, impl.quad_list().size()); EXPECT_EQ(10u + 10u, partially_occluded_count); } } } // namespace } // namespace cc