|  | // Copyright 2011 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/trees/layer_tree_host.h" | 
|  |  | 
|  | #include <algorithm> | 
|  |  | 
|  | #include "base/auto_reset.h" | 
|  | #include "base/synchronization/lock.h" | 
|  | #include "cc/animation/timing_function.h" | 
|  | #include "cc/base/swap_promise.h" | 
|  | #include "cc/debug/frame_rate_counter.h" | 
|  | #include "cc/layers/content_layer.h" | 
|  | #include "cc/layers/content_layer_client.h" | 
|  | #include "cc/layers/io_surface_layer.h" | 
|  | #include "cc/layers/layer_impl.h" | 
|  | #include "cc/layers/painted_scrollbar_layer.h" | 
|  | #include "cc/layers/picture_layer.h" | 
|  | #include "cc/layers/solid_color_layer.h" | 
|  | #include "cc/output/begin_frame_args.h" | 
|  | #include "cc/output/compositor_frame_ack.h" | 
|  | #include "cc/output/copy_output_request.h" | 
|  | #include "cc/output/copy_output_result.h" | 
|  | #include "cc/output/output_surface.h" | 
|  | #include "cc/quads/draw_quad.h" | 
|  | #include "cc/quads/io_surface_draw_quad.h" | 
|  | #include "cc/quads/tile_draw_quad.h" | 
|  | #include "cc/resources/prioritized_resource.h" | 
|  | #include "cc/resources/prioritized_resource_manager.h" | 
|  | #include "cc/resources/resource_update_queue.h" | 
|  | #include "cc/test/fake_content_layer.h" | 
|  | #include "cc/test/fake_content_layer_client.h" | 
|  | #include "cc/test/fake_content_layer_impl.h" | 
|  | #include "cc/test/fake_layer_tree_host_client.h" | 
|  | #include "cc/test/fake_output_surface.h" | 
|  | #include "cc/test/fake_painted_scrollbar_layer.h" | 
|  | #include "cc/test/fake_picture_layer.h" | 
|  | #include "cc/test/fake_picture_layer_impl.h" | 
|  | #include "cc/test/fake_picture_pile.h" | 
|  | #include "cc/test/fake_proxy.h" | 
|  | #include "cc/test/fake_scoped_ui_resource.h" | 
|  | #include "cc/test/geometry_test_utils.h" | 
|  | #include "cc/test/layer_tree_test.h" | 
|  | #include "cc/test/test_shared_bitmap_manager.h" | 
|  | #include "cc/test/test_web_graphics_context_3d.h" | 
|  | #include "cc/trees/layer_tree_host_impl.h" | 
|  | #include "cc/trees/layer_tree_impl.h" | 
|  | #include "cc/trees/single_thread_proxy.h" | 
|  | #include "cc/trees/thread_proxy.h" | 
|  | #include "gpu/GLES2/gl2extchromium.h" | 
|  | #include "skia/ext/refptr.h" | 
|  | #include "testing/gmock/include/gmock/gmock.h" | 
|  | #include "third_party/khronos/GLES2/gl2.h" | 
|  | #include "third_party/khronos/GLES2/gl2ext.h" | 
|  | #include "third_party/skia/include/core/SkPicture.h" | 
|  | #include "ui/gfx/frame_time.h" | 
|  | #include "ui/gfx/geometry/point_conversions.h" | 
|  | #include "ui/gfx/geometry/size_conversions.h" | 
|  | #include "ui/gfx/geometry/vector2d_conversions.h" | 
|  |  | 
|  | using testing::_; | 
|  | using testing::AnyNumber; | 
|  | using testing::AtLeast; | 
|  | using testing::Mock; | 
|  |  | 
|  | namespace cc { | 
|  | namespace { | 
|  |  | 
|  | class LayerTreeHostTest : public LayerTreeTest {}; | 
|  |  | 
|  | // Test if the LTHI receives ReadyToActivate notifications from the TileManager | 
|  | // when no raster tasks get scheduled. | 
|  | class LayerTreeHostTestReadyToActivateEmpty : public LayerTreeHostTest { | 
|  | public: | 
|  | LayerTreeHostTestReadyToActivateEmpty() | 
|  | : did_notify_ready_to_activate_(false), | 
|  | all_tiles_required_for_activation_are_ready_to_draw_(false), | 
|  | required_for_activation_count_(0) {} | 
|  |  | 
|  | void BeginTest() override { PostSetNeedsCommitToMainThread(); } | 
|  |  | 
|  | void CommitCompleteOnThread(LayerTreeHostImpl* impl) override { | 
|  | const std::vector<PictureLayerImpl*>& layers = impl->GetPictureLayers(); | 
|  | required_for_activation_count_ = 0; | 
|  | for (const auto& layer : layers) { | 
|  | FakePictureLayerImpl* fake_layer = | 
|  | static_cast<FakePictureLayerImpl*>(layer); | 
|  | required_for_activation_count_ += | 
|  | fake_layer->CountTilesRequiredForActivation(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void NotifyReadyToActivateOnThread(LayerTreeHostImpl* impl) override { | 
|  | did_notify_ready_to_activate_ = true; | 
|  | const std::vector<PictureLayerImpl*>& layers = impl->GetPictureLayers(); | 
|  | all_tiles_required_for_activation_are_ready_to_draw_ = true; | 
|  | for (const auto& layer : layers) { | 
|  | if (!layer->AllTilesRequiredForActivationAreReadyToDraw()) | 
|  | all_tiles_required_for_activation_are_ready_to_draw_ = false; | 
|  | } | 
|  | EndTest(); | 
|  | } | 
|  |  | 
|  | void AfterTest() override { | 
|  | EXPECT_TRUE(did_notify_ready_to_activate_); | 
|  | EXPECT_TRUE(all_tiles_required_for_activation_are_ready_to_draw_); | 
|  | EXPECT_EQ(size_t(0), required_for_activation_count_); | 
|  | } | 
|  |  | 
|  | protected: | 
|  | bool did_notify_ready_to_activate_; | 
|  | bool all_tiles_required_for_activation_are_ready_to_draw_; | 
|  | size_t required_for_activation_count_; | 
|  | }; | 
|  |  | 
|  | SINGLE_AND_MULTI_THREAD_IMPL_TEST_F(LayerTreeHostTestReadyToActivateEmpty); | 
|  |  | 
|  | // Test if the LTHI receives ReadyToActivate notifications from the TileManager | 
|  | // when some raster tasks flagged as REQUIRED_FOR_ACTIVATION got scheduled. | 
|  | class LayerTreeHostTestReadyToActivateNonEmpty | 
|  | : public LayerTreeHostTestReadyToActivateEmpty { | 
|  | public: | 
|  | void SetupTree() override { | 
|  | client_.set_fill_with_nonsolid_color(true); | 
|  | scoped_refptr<FakePictureLayer> root_layer = | 
|  | FakePictureLayer::Create(&client_); | 
|  | root_layer->SetBounds(gfx::Size(1024, 1024)); | 
|  | root_layer->SetIsDrawable(true); | 
|  |  | 
|  | layer_tree_host()->SetRootLayer(root_layer); | 
|  | LayerTreeHostTest::SetupTree(); | 
|  | } | 
|  |  | 
|  | void AfterTest() override { | 
|  | EXPECT_TRUE(did_notify_ready_to_activate_); | 
|  | EXPECT_TRUE(all_tiles_required_for_activation_are_ready_to_draw_); | 
|  | EXPECT_LE(size_t(1), required_for_activation_count_); | 
|  | } | 
|  |  | 
|  | private: | 
|  | FakeContentLayerClient client_; | 
|  | }; | 
|  |  | 
|  | SINGLE_AND_MULTI_THREAD_IMPL_TEST_F(LayerTreeHostTestReadyToActivateNonEmpty); | 
|  |  | 
|  | // Test if the LTHI receives ReadyToDraw notifications from the TileManager when | 
|  | // no raster tasks get scheduled. | 
|  | class LayerTreeHostTestReadyToDrawEmpty : public LayerTreeHostTest { | 
|  | public: | 
|  | LayerTreeHostTestReadyToDrawEmpty() | 
|  | : did_notify_ready_to_draw_(false), | 
|  | all_tiles_required_for_draw_are_ready_to_draw_(false), | 
|  | required_for_draw_count_(0) {} | 
|  |  | 
|  | void BeginTest() override { PostSetNeedsCommitToMainThread(); } | 
|  |  | 
|  | void NotifyReadyToDrawOnThread(LayerTreeHostImpl* impl) override { | 
|  | did_notify_ready_to_draw_ = true; | 
|  | const std::vector<PictureLayerImpl*>& layers = impl->GetPictureLayers(); | 
|  | all_tiles_required_for_draw_are_ready_to_draw_ = true; | 
|  | for (const auto& layer : layers) { | 
|  | if (!layer->AllTilesRequiredForDrawAreReadyToDraw()) | 
|  | all_tiles_required_for_draw_are_ready_to_draw_ = false; | 
|  | FakePictureLayerImpl* fake_layer = | 
|  | static_cast<FakePictureLayerImpl*>(layer); | 
|  | required_for_draw_count_ += fake_layer->CountTilesRequiredForDraw(); | 
|  | } | 
|  |  | 
|  | EndTest(); | 
|  | } | 
|  |  | 
|  | void AfterTest() override { | 
|  | EXPECT_TRUE(did_notify_ready_to_draw_); | 
|  | EXPECT_TRUE(all_tiles_required_for_draw_are_ready_to_draw_); | 
|  | EXPECT_EQ(size_t(0), required_for_draw_count_); | 
|  | } | 
|  |  | 
|  | protected: | 
|  | bool did_notify_ready_to_draw_; | 
|  | bool all_tiles_required_for_draw_are_ready_to_draw_; | 
|  | size_t required_for_draw_count_; | 
|  | }; | 
|  |  | 
|  | SINGLE_AND_MULTI_THREAD_IMPL_TEST_F(LayerTreeHostTestReadyToDrawEmpty); | 
|  |  | 
|  | // Test if the LTHI receives ReadyToDraw notifications from the TileManager when | 
|  | // some raster tasks flagged as REQUIRED_FOR_DRAW got scheduled. | 
|  | class LayerTreeHostTestReadyToDrawNonEmpty | 
|  | : public LayerTreeHostTestReadyToDrawEmpty { | 
|  | public: | 
|  | void SetupTree() override { | 
|  | client_.set_fill_with_nonsolid_color(true); | 
|  | scoped_refptr<FakePictureLayer> root_layer = | 
|  | FakePictureLayer::Create(&client_); | 
|  | root_layer->SetBounds(gfx::Size(1024, 1024)); | 
|  | root_layer->SetIsDrawable(true); | 
|  |  | 
|  | layer_tree_host()->SetRootLayer(root_layer); | 
|  | LayerTreeHostTest::SetupTree(); | 
|  | } | 
|  |  | 
|  | void AfterTest() override { | 
|  | EXPECT_TRUE(did_notify_ready_to_draw_); | 
|  | EXPECT_TRUE(all_tiles_required_for_draw_are_ready_to_draw_); | 
|  | EXPECT_LE(size_t(1), required_for_draw_count_); | 
|  | } | 
|  |  | 
|  | private: | 
|  | FakeContentLayerClient client_; | 
|  | }; | 
|  |  | 
|  | // Note: With this test setup, we only get tiles flagged as REQUIRED_FOR_DRAW in | 
|  | // single threaded mode. | 
|  | SINGLE_THREAD_IMPL_TEST_F(LayerTreeHostTestReadyToDrawNonEmpty); | 
|  |  | 
|  | // Two setNeedsCommits in a row should lead to at least 1 commit and at least 1 | 
|  | // draw with frame 0. | 
|  | class LayerTreeHostTestSetNeedsCommit1 : public LayerTreeHostTest { | 
|  | public: | 
|  | LayerTreeHostTestSetNeedsCommit1() : num_commits_(0), num_draws_(0) {} | 
|  |  | 
|  | void BeginTest() override { | 
|  | PostSetNeedsCommitToMainThread(); | 
|  | PostSetNeedsCommitToMainThread(); | 
|  | } | 
|  |  | 
|  | void DrawLayersOnThread(LayerTreeHostImpl* impl) override { | 
|  | num_draws_++; | 
|  | if (!impl->active_tree()->source_frame_number()) | 
|  | EndTest(); | 
|  | } | 
|  |  | 
|  | void CommitCompleteOnThread(LayerTreeHostImpl* impl) override { | 
|  | num_commits_++; | 
|  | } | 
|  |  | 
|  | void AfterTest() override { | 
|  | EXPECT_LE(1, num_commits_); | 
|  | EXPECT_LE(1, num_draws_); | 
|  | } | 
|  |  | 
|  | private: | 
|  | int num_commits_; | 
|  | int num_draws_; | 
|  | }; | 
|  |  | 
|  | SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestSetNeedsCommit1); | 
|  |  | 
|  | // A SetNeedsCommit should lead to 1 commit. Issuing a second commit after that | 
|  | // first committed frame draws should lead to another commit. | 
|  | class LayerTreeHostTestSetNeedsCommit2 : public LayerTreeHostTest { | 
|  | public: | 
|  | LayerTreeHostTestSetNeedsCommit2() : num_commits_(0), num_draws_(0) {} | 
|  |  | 
|  | void BeginTest() override { PostSetNeedsCommitToMainThread(); } | 
|  |  | 
|  | void DrawLayersOnThread(LayerTreeHostImpl* impl) override { ++num_draws_; } | 
|  |  | 
|  | void CommitCompleteOnThread(LayerTreeHostImpl* impl) override { | 
|  | ++num_commits_; | 
|  | switch (num_commits_) { | 
|  | case 1: | 
|  | PostSetNeedsCommitToMainThread(); | 
|  | break; | 
|  | case 2: | 
|  | EndTest(); | 
|  | break; | 
|  | default: | 
|  | NOTREACHED(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void AfterTest() override { | 
|  | EXPECT_EQ(2, num_commits_); | 
|  | EXPECT_LE(1, num_draws_); | 
|  | } | 
|  |  | 
|  | private: | 
|  | int num_commits_; | 
|  | int num_draws_; | 
|  | }; | 
|  |  | 
|  | MULTI_THREAD_TEST_F(LayerTreeHostTestSetNeedsCommit2); | 
|  |  | 
|  | // Verify that we pass property values in PushPropertiesTo. | 
|  | class LayerTreeHostTestPushPropertiesTo : public LayerTreeHostTest { | 
|  | protected: | 
|  | void SetupTree() override { | 
|  | scoped_refptr<Layer> root = Layer::Create(); | 
|  | root->CreateRenderSurface(); | 
|  | root->SetBounds(gfx::Size(10, 10)); | 
|  | layer_tree_host()->SetRootLayer(root); | 
|  | LayerTreeHostTest::SetupTree(); | 
|  | } | 
|  |  | 
|  | enum Properties { | 
|  | STARTUP, | 
|  | BOUNDS, | 
|  | HIDE_LAYER_AND_SUBTREE, | 
|  | DRAWS_CONTENT, | 
|  | DONE, | 
|  | }; | 
|  |  | 
|  | void BeginTest() override { | 
|  | index_ = STARTUP; | 
|  | PostSetNeedsCommitToMainThread(); | 
|  | } | 
|  |  | 
|  | void DrawLayersOnThread(LayerTreeHostImpl* impl) override { | 
|  | VerifyAfterValues(impl->active_tree()->root_layer()); | 
|  | } | 
|  |  | 
|  | void DidCommitAndDrawFrame() override { | 
|  | SetBeforeValues(layer_tree_host()->root_layer()); | 
|  | VerifyBeforeValues(layer_tree_host()->root_layer()); | 
|  |  | 
|  | ++index_; | 
|  | if (index_ == DONE) { | 
|  | EndTest(); | 
|  | return; | 
|  | } | 
|  |  | 
|  | SetAfterValues(layer_tree_host()->root_layer()); | 
|  | } | 
|  |  | 
|  | void AfterTest() override {} | 
|  |  | 
|  | void VerifyBeforeValues(Layer* layer) { | 
|  | EXPECT_EQ(gfx::Size(10, 10).ToString(), layer->bounds().ToString()); | 
|  | EXPECT_FALSE(layer->hide_layer_and_subtree()); | 
|  | EXPECT_FALSE(layer->DrawsContent()); | 
|  | } | 
|  |  | 
|  | void SetBeforeValues(Layer* layer) { | 
|  | layer->SetBounds(gfx::Size(10, 10)); | 
|  | layer->SetHideLayerAndSubtree(false); | 
|  | layer->SetIsDrawable(false); | 
|  | } | 
|  |  | 
|  | void VerifyAfterValues(LayerImpl* layer) { | 
|  | switch (static_cast<Properties>(index_)) { | 
|  | case STARTUP: | 
|  | case DONE: | 
|  | break; | 
|  | case BOUNDS: | 
|  | EXPECT_EQ(gfx::Size(20, 20).ToString(), layer->bounds().ToString()); | 
|  | break; | 
|  | case HIDE_LAYER_AND_SUBTREE: | 
|  | EXPECT_TRUE(layer->hide_layer_and_subtree()); | 
|  | break; | 
|  | case DRAWS_CONTENT: | 
|  | EXPECT_TRUE(layer->DrawsContent()); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | void SetAfterValues(Layer* layer) { | 
|  | switch (static_cast<Properties>(index_)) { | 
|  | case STARTUP: | 
|  | case DONE: | 
|  | break; | 
|  | case BOUNDS: | 
|  | layer->SetBounds(gfx::Size(20, 20)); | 
|  | break; | 
|  | case HIDE_LAYER_AND_SUBTREE: | 
|  | layer->SetHideLayerAndSubtree(true); | 
|  | break; | 
|  | case DRAWS_CONTENT: | 
|  | layer->SetIsDrawable(true); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | int index_; | 
|  | }; | 
|  |  | 
|  | SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestPushPropertiesTo); | 
|  |  | 
|  | // 1 setNeedsRedraw after the first commit has completed should lead to 1 | 
|  | // additional draw. | 
|  | class LayerTreeHostTestSetNeedsRedraw : public LayerTreeHostTest { | 
|  | public: | 
|  | LayerTreeHostTestSetNeedsRedraw() : num_commits_(0), num_draws_(0) {} | 
|  |  | 
|  | void BeginTest() override { PostSetNeedsCommitToMainThread(); } | 
|  |  | 
|  | void DrawLayersOnThread(LayerTreeHostImpl* impl) override { | 
|  | EXPECT_EQ(0, impl->active_tree()->source_frame_number()); | 
|  | if (!num_draws_) { | 
|  | // Redraw again to verify that the second redraw doesn't commit. | 
|  | PostSetNeedsRedrawToMainThread(); | 
|  | } else { | 
|  | EndTest(); | 
|  | } | 
|  | num_draws_++; | 
|  | } | 
|  |  | 
|  | void CommitCompleteOnThread(LayerTreeHostImpl* impl) override { | 
|  | EXPECT_EQ(0, num_draws_); | 
|  | num_commits_++; | 
|  | } | 
|  |  | 
|  | void AfterTest() override { | 
|  | EXPECT_GE(2, num_draws_); | 
|  | EXPECT_EQ(1, num_commits_); | 
|  | } | 
|  |  | 
|  | private: | 
|  | int num_commits_; | 
|  | int num_draws_; | 
|  | }; | 
|  |  | 
|  | MULTI_THREAD_TEST_F(LayerTreeHostTestSetNeedsRedraw); | 
|  |  | 
|  | // After setNeedsRedrawRect(invalid_rect) the final damage_rect | 
|  | // must contain invalid_rect. | 
|  | class LayerTreeHostTestSetNeedsRedrawRect : public LayerTreeHostTest { | 
|  | public: | 
|  | LayerTreeHostTestSetNeedsRedrawRect() | 
|  | : num_draws_(0), bounds_(50, 50), invalid_rect_(10, 10, 20, 20) {} | 
|  |  | 
|  | void BeginTest() override { | 
|  | if (layer_tree_host()->settings().impl_side_painting) | 
|  | root_layer_ = FakePictureLayer::Create(&client_); | 
|  | else | 
|  | root_layer_ = ContentLayer::Create(&client_); | 
|  | root_layer_->SetIsDrawable(true); | 
|  | root_layer_->SetBounds(bounds_); | 
|  | layer_tree_host()->SetRootLayer(root_layer_); | 
|  | layer_tree_host()->SetViewportSize(bounds_); | 
|  | PostSetNeedsCommitToMainThread(); | 
|  | } | 
|  |  | 
|  | DrawResult PrepareToDrawOnThread(LayerTreeHostImpl* host_impl, | 
|  | LayerTreeHostImpl::FrameData* frame_data, | 
|  | DrawResult draw_result) override { | 
|  | EXPECT_EQ(DRAW_SUCCESS, draw_result); | 
|  |  | 
|  | gfx::RectF root_damage_rect; | 
|  | if (!frame_data->render_passes.empty()) | 
|  | root_damage_rect = frame_data->render_passes.back()->damage_rect; | 
|  |  | 
|  | if (!num_draws_) { | 
|  | // If this is the first frame, expect full frame damage. | 
|  | EXPECT_EQ(root_damage_rect, gfx::Rect(bounds_)); | 
|  | } else { | 
|  | // Check that invalid_rect_ is indeed repainted. | 
|  | EXPECT_TRUE(root_damage_rect.Contains(invalid_rect_)); | 
|  | } | 
|  |  | 
|  | return draw_result; | 
|  | } | 
|  |  | 
|  | void DrawLayersOnThread(LayerTreeHostImpl* impl) override { | 
|  | if (!num_draws_) { | 
|  | PostSetNeedsRedrawRectToMainThread(invalid_rect_); | 
|  | } else { | 
|  | EndTest(); | 
|  | } | 
|  | num_draws_++; | 
|  | } | 
|  |  | 
|  | void AfterTest() override { EXPECT_EQ(2, num_draws_); } | 
|  |  | 
|  | private: | 
|  | int num_draws_; | 
|  | const gfx::Size bounds_; | 
|  | const gfx::Rect invalid_rect_; | 
|  | FakeContentLayerClient client_; | 
|  | scoped_refptr<Layer> root_layer_; | 
|  | }; | 
|  |  | 
|  | SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestSetNeedsRedrawRect); | 
|  |  | 
|  | class LayerTreeHostTestNoExtraCommitFromInvalidate : public LayerTreeHostTest { | 
|  | public: | 
|  | void InitializeSettings(LayerTreeSettings* settings) override { | 
|  | settings->layer_transforms_should_scale_layer_contents = true; | 
|  | } | 
|  |  | 
|  | void SetupTree() override { | 
|  | root_layer_ = Layer::Create(); | 
|  | root_layer_->SetBounds(gfx::Size(10, 20)); | 
|  | root_layer_->CreateRenderSurface(); | 
|  |  | 
|  | if (layer_tree_host()->settings().impl_side_painting) | 
|  | scaled_layer_ = FakePictureLayer::Create(&client_); | 
|  | else | 
|  | scaled_layer_ = FakeContentLayer::Create(&client_); | 
|  | scaled_layer_->SetBounds(gfx::Size(1, 1)); | 
|  | root_layer_->AddChild(scaled_layer_); | 
|  |  | 
|  | layer_tree_host()->SetRootLayer(root_layer_); | 
|  | LayerTreeHostTest::SetupTree(); | 
|  | } | 
|  |  | 
|  | void BeginTest() override { PostSetNeedsCommitToMainThread(); } | 
|  |  | 
|  | void DrawLayersOnThread(LayerTreeHostImpl* host_impl) override { | 
|  | if (host_impl->active_tree()->source_frame_number() == 1) | 
|  | EndTest(); | 
|  | } | 
|  |  | 
|  | void DidCommit() override { | 
|  | switch (layer_tree_host()->source_frame_number()) { | 
|  | case 1: | 
|  | // SetBounds grows the layer and exposes new content. | 
|  | if (layer_tree_host()->settings().impl_side_painting) { | 
|  | scaled_layer_->SetBounds(gfx::Size(4, 4)); | 
|  | } else { | 
|  | // Changing the device scale factor causes a commit. It also changes | 
|  | // the content bounds of |scaled_layer_|, which should not generate | 
|  | // a second commit as a result. | 
|  | layer_tree_host()->SetDeviceScaleFactor(4.f); | 
|  | } | 
|  | break; | 
|  | default: | 
|  | // No extra commits. | 
|  | EXPECT_EQ(2, layer_tree_host()->source_frame_number()); | 
|  | } | 
|  | } | 
|  |  | 
|  | void AfterTest() override { | 
|  | EXPECT_EQ(gfx::Size(4, 4).ToString(), | 
|  | scaled_layer_->content_bounds().ToString()); | 
|  | } | 
|  |  | 
|  | private: | 
|  | FakeContentLayerClient client_; | 
|  | scoped_refptr<Layer> root_layer_; | 
|  | scoped_refptr<Layer> scaled_layer_; | 
|  | }; | 
|  |  | 
|  | SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestNoExtraCommitFromInvalidate); | 
|  |  | 
|  | class LayerTreeHostTestNoExtraCommitFromScrollbarInvalidate | 
|  | : public LayerTreeHostTest { | 
|  | public: | 
|  | void InitializeSettings(LayerTreeSettings* settings) override { | 
|  | settings->layer_transforms_should_scale_layer_contents = true; | 
|  | } | 
|  |  | 
|  | void SetupTree() override { | 
|  | root_layer_ = Layer::Create(); | 
|  | root_layer_->SetBounds(gfx::Size(10, 20)); | 
|  | root_layer_->CreateRenderSurface(); | 
|  |  | 
|  | bool paint_scrollbar = true; | 
|  | bool has_thumb = false; | 
|  | scrollbar_ = FakePaintedScrollbarLayer::Create( | 
|  | paint_scrollbar, has_thumb, root_layer_->id()); | 
|  | scrollbar_->SetPosition(gfx::Point(0, 10)); | 
|  | scrollbar_->SetBounds(gfx::Size(10, 10)); | 
|  |  | 
|  | root_layer_->AddChild(scrollbar_); | 
|  |  | 
|  | layer_tree_host()->SetRootLayer(root_layer_); | 
|  | LayerTreeHostTest::SetupTree(); | 
|  | } | 
|  |  | 
|  | void BeginTest() override { PostSetNeedsCommitToMainThread(); } | 
|  |  | 
|  | void DrawLayersOnThread(LayerTreeHostImpl* host_impl) override { | 
|  | if (host_impl->active_tree()->source_frame_number() == 1) | 
|  | EndTest(); | 
|  | } | 
|  |  | 
|  | void DidCommit() override { | 
|  | switch (layer_tree_host()->source_frame_number()) { | 
|  | case 1: | 
|  | // Changing the device scale factor causes a commit. It also changes | 
|  | // the content bounds of |scrollbar_|, which should not generate | 
|  | // a second commit as a result. | 
|  | layer_tree_host()->SetDeviceScaleFactor(4.f); | 
|  | break; | 
|  | default: | 
|  | // No extra commits. | 
|  | EXPECT_EQ(2, layer_tree_host()->source_frame_number()); | 
|  | } | 
|  | } | 
|  |  | 
|  | void AfterTest() override { | 
|  | EXPECT_EQ(gfx::Size(40, 40).ToString(), | 
|  | scrollbar_->content_bounds().ToString()); | 
|  | } | 
|  |  | 
|  | private: | 
|  | FakeContentLayerClient client_; | 
|  | scoped_refptr<Layer> root_layer_; | 
|  | scoped_refptr<FakePaintedScrollbarLayer> scrollbar_; | 
|  | }; | 
|  |  | 
|  | SINGLE_AND_MULTI_THREAD_TEST_F( | 
|  | LayerTreeHostTestNoExtraCommitFromScrollbarInvalidate); | 
|  |  | 
|  | class LayerTreeHostTestSetNextCommitForcesRedraw : public LayerTreeHostTest { | 
|  | public: | 
|  | LayerTreeHostTestSetNextCommitForcesRedraw() | 
|  | : num_draws_(0), bounds_(50, 50), invalid_rect_(10, 10, 20, 20) {} | 
|  |  | 
|  | void BeginTest() override { | 
|  | if (layer_tree_host()->settings().impl_side_painting) | 
|  | root_layer_ = FakePictureLayer::Create(&client_); | 
|  | else | 
|  | root_layer_ = ContentLayer::Create(&client_); | 
|  | root_layer_->SetIsDrawable(true); | 
|  | root_layer_->SetBounds(bounds_); | 
|  | layer_tree_host()->SetRootLayer(root_layer_); | 
|  | layer_tree_host()->SetViewportSize(bounds_); | 
|  | PostSetNeedsCommitToMainThread(); | 
|  | } | 
|  |  | 
|  | void CommitCompleteOnThread(LayerTreeHostImpl* host_impl) override { | 
|  | if (num_draws_ == 3 && host_impl->settings().impl_side_painting) | 
|  | host_impl->SetNeedsRedrawRect(invalid_rect_); | 
|  | } | 
|  |  | 
|  | DrawResult PrepareToDrawOnThread(LayerTreeHostImpl* host_impl, | 
|  | LayerTreeHostImpl::FrameData* frame_data, | 
|  | DrawResult draw_result) override { | 
|  | EXPECT_EQ(DRAW_SUCCESS, draw_result); | 
|  |  | 
|  | gfx::RectF root_damage_rect; | 
|  | if (!frame_data->render_passes.empty()) | 
|  | root_damage_rect = frame_data->render_passes.back()->damage_rect; | 
|  |  | 
|  | switch (num_draws_) { | 
|  | case 0: | 
|  | EXPECT_EQ(gfx::Rect(bounds_), root_damage_rect); | 
|  | break; | 
|  | case 1: | 
|  | case 2: | 
|  | EXPECT_EQ(gfx::Rect(0, 0, 0, 0), root_damage_rect); | 
|  | break; | 
|  | case 3: | 
|  | EXPECT_EQ(invalid_rect_, root_damage_rect); | 
|  | break; | 
|  | case 4: | 
|  | EXPECT_EQ(gfx::Rect(bounds_), root_damage_rect); | 
|  | break; | 
|  | default: | 
|  | NOTREACHED(); | 
|  | } | 
|  |  | 
|  | return draw_result; | 
|  | } | 
|  |  | 
|  | void DrawLayersOnThread(LayerTreeHostImpl* host_impl) override { | 
|  | switch (num_draws_) { | 
|  | case 0: | 
|  | case 1: | 
|  | // Cycle through a couple of empty commits to ensure we're observing the | 
|  | // right behavior | 
|  | PostSetNeedsCommitToMainThread(); | 
|  | break; | 
|  | case 2: | 
|  | // Should force full frame damage on the next commit | 
|  | PostSetNextCommitForcesRedrawToMainThread(); | 
|  | PostSetNeedsCommitToMainThread(); | 
|  | if (host_impl->settings().impl_side_painting) | 
|  | host_impl->BlockNotifyReadyToActivateForTesting(true); | 
|  | else | 
|  | num_draws_++; | 
|  | break; | 
|  | case 3: | 
|  | host_impl->BlockNotifyReadyToActivateForTesting(false); | 
|  | break; | 
|  | default: | 
|  | EndTest(); | 
|  | break; | 
|  | } | 
|  | num_draws_++; | 
|  | } | 
|  |  | 
|  | void AfterTest() override { EXPECT_EQ(5, num_draws_); } | 
|  |  | 
|  | private: | 
|  | int num_draws_; | 
|  | const gfx::Size bounds_; | 
|  | const gfx::Rect invalid_rect_; | 
|  | FakeContentLayerClient client_; | 
|  | scoped_refptr<Layer> root_layer_; | 
|  | }; | 
|  |  | 
|  | SINGLE_AND_MULTI_THREAD_BLOCKNOTIFY_TEST_F( | 
|  | LayerTreeHostTestSetNextCommitForcesRedraw); | 
|  |  | 
|  | // Tests that if a layer is not drawn because of some reason in the parent then | 
|  | // its damage is preserved until the next time it is drawn. | 
|  | class LayerTreeHostTestUndrawnLayersDamageLater : public LayerTreeHostTest { | 
|  | public: | 
|  | LayerTreeHostTestUndrawnLayersDamageLater() {} | 
|  |  | 
|  | void SetupTree() override { | 
|  | if (layer_tree_host()->settings().impl_side_painting) | 
|  | root_layer_ = FakePictureLayer::Create(&client_); | 
|  | else | 
|  | root_layer_ = ContentLayer::Create(&client_); | 
|  | root_layer_->SetIsDrawable(true); | 
|  | root_layer_->SetBounds(gfx::Size(50, 50)); | 
|  | layer_tree_host()->SetRootLayer(root_layer_); | 
|  |  | 
|  | // The initially transparent layer has a larger child layer, which is | 
|  | // not initially drawn because of the this (parent) layer. | 
|  | if (layer_tree_host()->settings().impl_side_painting) | 
|  | parent_layer_ = FakePictureLayer::Create(&client_); | 
|  | else | 
|  | parent_layer_ = FakeContentLayer::Create(&client_); | 
|  | parent_layer_->SetBounds(gfx::Size(15, 15)); | 
|  | parent_layer_->SetOpacity(0.0f); | 
|  | root_layer_->AddChild(parent_layer_); | 
|  |  | 
|  | if (layer_tree_host()->settings().impl_side_painting) | 
|  | child_layer_ = FakePictureLayer::Create(&client_); | 
|  | else | 
|  | child_layer_ = FakeContentLayer::Create(&client_); | 
|  | child_layer_->SetBounds(gfx::Size(25, 25)); | 
|  | parent_layer_->AddChild(child_layer_); | 
|  |  | 
|  | LayerTreeHostTest::SetupTree(); | 
|  | } | 
|  |  | 
|  | void BeginTest() override { PostSetNeedsCommitToMainThread(); } | 
|  |  | 
|  | DrawResult PrepareToDrawOnThread(LayerTreeHostImpl* host_impl, | 
|  | LayerTreeHostImpl::FrameData* frame_data, | 
|  | DrawResult draw_result) override { | 
|  | EXPECT_EQ(DRAW_SUCCESS, draw_result); | 
|  |  | 
|  | gfx::RectF root_damage_rect; | 
|  | if (!frame_data->render_passes.empty()) | 
|  | root_damage_rect = frame_data->render_passes.back()->damage_rect; | 
|  |  | 
|  | // The first time, the whole view needs be drawn. | 
|  | // Afterwards, just the opacity of surface_layer1 is changed a few times, | 
|  | // and each damage should be the bounding box of it and its child. If this | 
|  | // was working improperly, the damage might not include its childs bounding | 
|  | // box. | 
|  | switch (host_impl->active_tree()->source_frame_number()) { | 
|  | case 0: | 
|  | EXPECT_EQ(gfx::Rect(root_layer_->bounds()), root_damage_rect); | 
|  | break; | 
|  | case 1: | 
|  | case 2: | 
|  | case 3: | 
|  | EXPECT_EQ(gfx::Rect(child_layer_->bounds()), root_damage_rect); | 
|  | break; | 
|  | default: | 
|  | NOTREACHED(); | 
|  | } | 
|  |  | 
|  | return draw_result; | 
|  | } | 
|  |  | 
|  | void DidCommitAndDrawFrame() override { | 
|  | switch (layer_tree_host()->source_frame_number()) { | 
|  | case 1: | 
|  | // Test not owning the surface. | 
|  | parent_layer_->SetOpacity(1.0f); | 
|  | break; | 
|  | case 2: | 
|  | parent_layer_->SetOpacity(0.0f); | 
|  | break; | 
|  | case 3: | 
|  | // Test owning the surface. | 
|  | parent_layer_->SetOpacity(0.5f); | 
|  | parent_layer_->SetForceRenderSurface(true); | 
|  | break; | 
|  | case 4: | 
|  | EndTest(); | 
|  | break; | 
|  | default: | 
|  | NOTREACHED(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void AfterTest() override {} | 
|  |  | 
|  | private: | 
|  | FakeContentLayerClient client_; | 
|  | scoped_refptr<Layer> root_layer_; | 
|  | scoped_refptr<Layer> parent_layer_; | 
|  | scoped_refptr<Layer> child_layer_; | 
|  | }; | 
|  |  | 
|  | SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestUndrawnLayersDamageLater); | 
|  |  | 
|  | // Tests that if a layer is not drawn because of some reason in the parent, | 
|  | // causing its content bounds to not be computed, then when it is later drawn, | 
|  | // its content bounds get pushed. | 
|  | class LayerTreeHostTestUndrawnLayersPushContentBoundsLater | 
|  | : public LayerTreeHostTest { | 
|  | public: | 
|  | LayerTreeHostTestUndrawnLayersPushContentBoundsLater() | 
|  | : root_layer_(Layer::Create()) {} | 
|  |  | 
|  | void SetupTree() override { | 
|  | root_layer_->CreateRenderSurface(); | 
|  | root_layer_->SetIsDrawable(true); | 
|  | root_layer_->SetBounds(gfx::Size(20, 20)); | 
|  | layer_tree_host()->SetRootLayer(root_layer_); | 
|  |  | 
|  | parent_layer_ = Layer::Create(); | 
|  | parent_layer_->SetBounds(gfx::Size(20, 20)); | 
|  | parent_layer_->SetOpacity(0.0f); | 
|  | root_layer_->AddChild(parent_layer_); | 
|  |  | 
|  | child_layer_ = Layer::Create(); | 
|  | child_layer_->SetBounds(gfx::Size(15, 15)); | 
|  | parent_layer_->AddChild(child_layer_); | 
|  |  | 
|  | LayerTreeHostTest::SetupTree(); | 
|  | } | 
|  |  | 
|  | void BeginTest() override { PostSetNeedsCommitToMainThread(); } | 
|  |  | 
|  | void DidActivateTreeOnThread(LayerTreeHostImpl* host_impl) override { | 
|  | LayerImpl* root = host_impl->active_tree()->root_layer(); | 
|  | LayerImpl* parent = root->children()[0]; | 
|  | LayerImpl* child = parent->children()[0]; | 
|  |  | 
|  | switch (host_impl->active_tree()->source_frame_number()) { | 
|  | case 0: | 
|  | EXPECT_EQ(0.f, parent->opacity()); | 
|  | EXPECT_EQ(gfx::SizeF(), child->content_bounds()); | 
|  | break; | 
|  | case 1: | 
|  | EXPECT_EQ(1.f, parent->opacity()); | 
|  | EXPECT_EQ(gfx::SizeF(15.f, 15.f), child->content_bounds()); | 
|  | EndTest(); | 
|  | break; | 
|  | default: | 
|  | NOTREACHED(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void DidCommit() override { | 
|  | switch (layer_tree_host()->source_frame_number()) { | 
|  | case 1: | 
|  | parent_layer_->SetOpacity(1.0f); | 
|  | break; | 
|  | case 2: | 
|  | break; | 
|  | default: | 
|  | NOTREACHED(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void AfterTest() override {} | 
|  |  | 
|  | private: | 
|  | scoped_refptr<Layer> root_layer_; | 
|  | scoped_refptr<Layer> parent_layer_; | 
|  | scoped_refptr<Layer> child_layer_; | 
|  | }; | 
|  |  | 
|  | SINGLE_AND_MULTI_THREAD_TEST_F( | 
|  | LayerTreeHostTestUndrawnLayersPushContentBoundsLater); | 
|  |  | 
|  | // This test verifies that properties on the layer tree host are commited | 
|  | // to the impl side. | 
|  | class LayerTreeHostTestCommit : public LayerTreeHostTest { | 
|  | public: | 
|  | LayerTreeHostTestCommit() {} | 
|  |  | 
|  | void BeginTest() override { | 
|  | layer_tree_host()->SetViewportSize(gfx::Size(20, 20)); | 
|  | layer_tree_host()->set_background_color(SK_ColorGRAY); | 
|  |  | 
|  | PostSetNeedsCommitToMainThread(); | 
|  | } | 
|  |  | 
|  | void DidActivateTreeOnThread(LayerTreeHostImpl* impl) override { | 
|  | EXPECT_EQ(gfx::Size(20, 20), impl->DrawViewportSize()); | 
|  | EXPECT_EQ(SK_ColorGRAY, impl->active_tree()->background_color()); | 
|  |  | 
|  | EndTest(); | 
|  | } | 
|  |  | 
|  | void AfterTest() override {} | 
|  | }; | 
|  |  | 
|  | MULTI_THREAD_TEST_F(LayerTreeHostTestCommit); | 
|  |  | 
|  | // This test verifies that LayerTreeHostImpl's current frame time gets | 
|  | // updated in consecutive frames when it doesn't draw due to tree | 
|  | // activation failure. | 
|  | class LayerTreeHostTestFrameTimeUpdatesAfterActivationFails | 
|  | : public LayerTreeHostTest { | 
|  | public: | 
|  | LayerTreeHostTestFrameTimeUpdatesAfterActivationFails() | 
|  | : frame_count_with_pending_tree_(0) {} | 
|  |  | 
|  | void BeginTest() override { | 
|  | layer_tree_host()->SetViewportSize(gfx::Size(20, 20)); | 
|  | layer_tree_host()->set_background_color(SK_ColorGRAY); | 
|  |  | 
|  | PostSetNeedsCommitToMainThread(); | 
|  | } | 
|  |  | 
|  | void BeginCommitOnThread(LayerTreeHostImpl* impl) override { | 
|  | EXPECT_EQ(frame_count_with_pending_tree_, 0); | 
|  | if (impl->settings().impl_side_painting) | 
|  | impl->BlockNotifyReadyToActivateForTesting(true); | 
|  | } | 
|  |  | 
|  | void WillBeginImplFrameOnThread(LayerTreeHostImpl* impl, | 
|  | const BeginFrameArgs& args) override { | 
|  | if (impl->pending_tree()) | 
|  | frame_count_with_pending_tree_++; | 
|  |  | 
|  | if (frame_count_with_pending_tree_ == 1) { | 
|  | EXPECT_EQ(first_frame_time_.ToInternalValue(), 0); | 
|  | first_frame_time_ = impl->CurrentBeginFrameArgs().frame_time; | 
|  | } else if (frame_count_with_pending_tree_ == 2 && | 
|  | impl->settings().impl_side_painting) { | 
|  | impl->BlockNotifyReadyToActivateForTesting(false); | 
|  | } | 
|  | } | 
|  |  | 
|  | void DrawLayersOnThread(LayerTreeHostImpl* impl) override { | 
|  | if (frame_count_with_pending_tree_ > 1) { | 
|  | EXPECT_NE(first_frame_time_.ToInternalValue(), 0); | 
|  | EXPECT_NE(first_frame_time_.ToInternalValue(), | 
|  | impl->CurrentBeginFrameArgs().frame_time.ToInternalValue()); | 
|  | EndTest(); | 
|  | return; | 
|  | } | 
|  |  | 
|  | EXPECT_FALSE(impl->settings().impl_side_painting); | 
|  | EndTest(); | 
|  | } | 
|  | void DidActivateTreeOnThread(LayerTreeHostImpl* impl) override { | 
|  | if (impl->settings().impl_side_painting) | 
|  | EXPECT_NE(frame_count_with_pending_tree_, 1); | 
|  | } | 
|  |  | 
|  | void AfterTest() override {} | 
|  |  | 
|  | private: | 
|  | int frame_count_with_pending_tree_; | 
|  | base::TimeTicks first_frame_time_; | 
|  | }; | 
|  |  | 
|  | SINGLE_AND_MULTI_THREAD_BLOCKNOTIFY_TEST_F( | 
|  | LayerTreeHostTestFrameTimeUpdatesAfterActivationFails); | 
|  |  | 
|  | // This test verifies that LayerTreeHostImpl's current frame time gets | 
|  | // updated in consecutive frames when it draws in each frame. | 
|  | class LayerTreeHostTestFrameTimeUpdatesAfterDraw : public LayerTreeHostTest { | 
|  | public: | 
|  | LayerTreeHostTestFrameTimeUpdatesAfterDraw() : frame_(0) {} | 
|  |  | 
|  | void BeginTest() override { | 
|  | layer_tree_host()->SetViewportSize(gfx::Size(20, 20)); | 
|  | layer_tree_host()->set_background_color(SK_ColorGRAY); | 
|  |  | 
|  | PostSetNeedsCommitToMainThread(); | 
|  | } | 
|  |  | 
|  | void DrawLayersOnThread(LayerTreeHostImpl* impl) override { | 
|  | frame_++; | 
|  | if (frame_ == 1) { | 
|  | first_frame_time_ = impl->CurrentBeginFrameArgs().frame_time; | 
|  | impl->SetNeedsRedraw(); | 
|  |  | 
|  | // Since we might use a low-resolution clock on Windows, we need to | 
|  | // make sure that the clock has incremented past first_frame_time_. | 
|  | while (first_frame_time_ == gfx::FrameTime::Now()) { | 
|  | } | 
|  |  | 
|  | return; | 
|  | } | 
|  |  | 
|  | EXPECT_NE(first_frame_time_, impl->CurrentBeginFrameArgs().frame_time); | 
|  | EndTest(); | 
|  | } | 
|  |  | 
|  | void CommitCompleteOnThread(LayerTreeHostImpl* host_impl) override { | 
|  | // Ensure there isn't a commit between the two draws, to ensure that a | 
|  | // commit isn't required for updating the current frame time. We can | 
|  | // only check for this in the multi-threaded case, since in the single- | 
|  | // threaded case there will always be a commit between consecutive draws. | 
|  | if (HasImplThread()) | 
|  | EXPECT_EQ(0, frame_); | 
|  | } | 
|  |  | 
|  | void AfterTest() override {} | 
|  |  | 
|  | private: | 
|  | int frame_; | 
|  | base::TimeTicks first_frame_time_; | 
|  | }; | 
|  |  | 
|  | SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestFrameTimeUpdatesAfterDraw); | 
|  |  | 
|  | // Verifies that StartPageScaleAnimation events propagate correctly | 
|  | // from LayerTreeHost to LayerTreeHostImpl in the MT compositor. | 
|  | class LayerTreeHostTestStartPageScaleAnimation : public LayerTreeHostTest { | 
|  | public: | 
|  | LayerTreeHostTestStartPageScaleAnimation() {} | 
|  |  | 
|  | void SetupTree() override { | 
|  | LayerTreeHostTest::SetupTree(); | 
|  |  | 
|  | if (layer_tree_host()->settings().impl_side_painting) { | 
|  | scoped_refptr<FakePictureLayer> layer = | 
|  | FakePictureLayer::Create(&client_); | 
|  | layer->set_always_update_resources(true); | 
|  | scroll_layer_ = layer; | 
|  | } else { | 
|  | scroll_layer_ = FakeContentLayer::Create(&client_); | 
|  | } | 
|  |  | 
|  | Layer* root_layer = layer_tree_host()->root_layer(); | 
|  | scroll_layer_->SetScrollClipLayerId(root_layer->id()); | 
|  | scroll_layer_->SetIsContainerForFixedPositionLayers(true); | 
|  | scroll_layer_->SetBounds(gfx::Size(2 * root_layer->bounds().width(), | 
|  | 2 * root_layer->bounds().height())); | 
|  | scroll_layer_->SetScrollOffset(gfx::ScrollOffset()); | 
|  | layer_tree_host()->root_layer()->AddChild(scroll_layer_); | 
|  | // This test requires the page_scale and inner viewport layers to be | 
|  | // identified. | 
|  | layer_tree_host()->RegisterViewportLayers(NULL, root_layer, | 
|  | scroll_layer_.get(), NULL); | 
|  | layer_tree_host()->SetPageScaleFactorAndLimits(1.f, 0.5f, 2.f); | 
|  | } | 
|  |  | 
|  | void BeginTest() override { PostSetNeedsCommitToMainThread(); } | 
|  |  | 
|  | void ApplyViewportDeltas(const gfx::Vector2d& scroll_delta, | 
|  | float scale, | 
|  | float) override { | 
|  | gfx::ScrollOffset offset = scroll_layer_->scroll_offset(); | 
|  | scroll_layer_->SetScrollOffset(ScrollOffsetWithDelta(offset, | 
|  | scroll_delta)); | 
|  | layer_tree_host()->SetPageScaleFactorAndLimits(scale, 0.5f, 2.f); | 
|  | } | 
|  |  | 
|  | void DidActivateTreeOnThread(LayerTreeHostImpl* impl) override { | 
|  | // We get one commit before the first draw, and the animation doesn't happen | 
|  | // until the second draw. | 
|  | switch (impl->active_tree()->source_frame_number()) { | 
|  | case 0: | 
|  | EXPECT_EQ(1.f, impl->active_tree()->current_page_scale_factor()); | 
|  | // We'll start an animation when we get back to the main thread. | 
|  | break; | 
|  | case 1: | 
|  | EXPECT_EQ(1.f, impl->active_tree()->current_page_scale_factor()); | 
|  | break; | 
|  | case 2: | 
|  | EXPECT_EQ(1.25f, impl->active_tree()->current_page_scale_factor()); | 
|  | EndTest(); | 
|  | break; | 
|  | default: | 
|  | NOTREACHED(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void DidCommitAndDrawFrame() override { | 
|  | switch (layer_tree_host()->source_frame_number()) { | 
|  | case 1: | 
|  | layer_tree_host()->StartPageScaleAnimation( | 
|  | gfx::Vector2d(), false, 1.25f, base::TimeDelta()); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | void AfterTest() override {} | 
|  |  | 
|  | FakeContentLayerClient client_; | 
|  | scoped_refptr<Layer> scroll_layer_; | 
|  | }; | 
|  |  | 
|  | MULTI_THREAD_TEST_F(LayerTreeHostTestStartPageScaleAnimation); | 
|  |  | 
|  | class LayerTreeHostTestSetVisible : public LayerTreeHostTest { | 
|  | public: | 
|  | LayerTreeHostTestSetVisible() : num_draws_(0) {} | 
|  |  | 
|  | void BeginTest() override { | 
|  | PostSetNeedsCommitToMainThread(); | 
|  | PostSetVisibleToMainThread(false); | 
|  | // This is suppressed while we're invisible. | 
|  | PostSetNeedsRedrawToMainThread(); | 
|  | // Triggers the redraw. | 
|  | PostSetVisibleToMainThread(true); | 
|  | } | 
|  |  | 
|  | void DrawLayersOnThread(LayerTreeHostImpl* impl) override { | 
|  | EXPECT_TRUE(impl->visible()); | 
|  | ++num_draws_; | 
|  | EndTest(); | 
|  | } | 
|  |  | 
|  | void AfterTest() override { EXPECT_EQ(1, num_draws_); } | 
|  |  | 
|  | private: | 
|  | int num_draws_; | 
|  | }; | 
|  |  | 
|  | MULTI_THREAD_TEST_F(LayerTreeHostTestSetVisible); | 
|  |  | 
|  | class TestOpacityChangeLayerDelegate : public ContentLayerClient { | 
|  | public: | 
|  | TestOpacityChangeLayerDelegate() : test_layer_(0) {} | 
|  |  | 
|  | void SetTestLayer(Layer* test_layer) { test_layer_ = test_layer; } | 
|  |  | 
|  | void PaintContents( | 
|  | SkCanvas* canvas, | 
|  | const gfx::Rect& clip, | 
|  | ContentLayerClient::GraphicsContextStatus gc_status) override { | 
|  | // Set layer opacity to 0. | 
|  | if (test_layer_) | 
|  | test_layer_->SetOpacity(0.f); | 
|  | } | 
|  | scoped_refptr<DisplayItemList> PaintContentsToDisplayList( | 
|  | const gfx::Rect& clip, | 
|  | GraphicsContextStatus gc_status) override { | 
|  | NOTIMPLEMENTED(); | 
|  | return DisplayItemList::Create(); | 
|  | } | 
|  | bool FillsBoundsCompletely() const override { return false; } | 
|  |  | 
|  | private: | 
|  | Layer* test_layer_; | 
|  | }; | 
|  |  | 
|  | class ContentLayerWithUpdateTracking : public ContentLayer { | 
|  | public: | 
|  | static scoped_refptr<ContentLayerWithUpdateTracking> Create( | 
|  | ContentLayerClient* client) { | 
|  | return make_scoped_refptr(new ContentLayerWithUpdateTracking(client)); | 
|  | } | 
|  |  | 
|  | int PaintContentsCount() { return paint_contents_count_; } | 
|  | void ResetPaintContentsCount() { paint_contents_count_ = 0; } | 
|  |  | 
|  | bool Update(ResourceUpdateQueue* queue, | 
|  | const OcclusionTracker<Layer>* occlusion) override { | 
|  | bool updated = ContentLayer::Update(queue, occlusion); | 
|  | paint_contents_count_++; | 
|  | return updated; | 
|  | } | 
|  |  | 
|  | private: | 
|  | explicit ContentLayerWithUpdateTracking(ContentLayerClient* client) | 
|  | : ContentLayer(client), paint_contents_count_(0) { | 
|  | SetBounds(gfx::Size(10, 10)); | 
|  | SetIsDrawable(true); | 
|  | } | 
|  | ~ContentLayerWithUpdateTracking() override {} | 
|  |  | 
|  | int paint_contents_count_; | 
|  | }; | 
|  |  | 
|  | // Layer opacity change during paint should not prevent compositor resources | 
|  | // from being updated during commit. | 
|  | class LayerTreeHostTestOpacityChange : public LayerTreeHostTest { | 
|  | public: | 
|  | LayerTreeHostTestOpacityChange() : test_opacity_change_delegate_() {} | 
|  |  | 
|  | void BeginTest() override { | 
|  | if (layer_tree_host()->settings().impl_side_painting) { | 
|  | update_check_picture_layer_ = | 
|  | FakePictureLayer::Create(&test_opacity_change_delegate_); | 
|  | test_opacity_change_delegate_.SetTestLayer( | 
|  | update_check_picture_layer_.get()); | 
|  | is_impl_paint_ = true; | 
|  | } else { | 
|  | update_check_content_layer_ = ContentLayerWithUpdateTracking::Create( | 
|  | &test_opacity_change_delegate_); | 
|  | test_opacity_change_delegate_.SetTestLayer( | 
|  | update_check_content_layer_.get()); | 
|  | is_impl_paint_ = false; | 
|  | } | 
|  | layer_tree_host()->SetViewportSize(gfx::Size(10, 10)); | 
|  | if (layer_tree_host()->settings().impl_side_painting) | 
|  | layer_tree_host()->root_layer()->AddChild(update_check_picture_layer_); | 
|  | else | 
|  | layer_tree_host()->root_layer()->AddChild(update_check_content_layer_); | 
|  |  | 
|  | PostSetNeedsCommitToMainThread(); | 
|  | } | 
|  |  | 
|  | void CommitCompleteOnThread(LayerTreeHostImpl* impl) override { EndTest(); } | 
|  |  | 
|  | void AfterTest() override { | 
|  | // Update() should have been called once. | 
|  | if (is_impl_paint_) | 
|  | EXPECT_EQ(1u, update_check_picture_layer_->update_count()); | 
|  | else | 
|  | EXPECT_EQ(1, update_check_content_layer_->PaintContentsCount()); | 
|  | } | 
|  |  | 
|  | private: | 
|  | TestOpacityChangeLayerDelegate test_opacity_change_delegate_; | 
|  | scoped_refptr<ContentLayerWithUpdateTracking> update_check_content_layer_; | 
|  | scoped_refptr<FakePictureLayer> update_check_picture_layer_; | 
|  | bool is_impl_paint_; | 
|  | }; | 
|  |  | 
|  | MULTI_THREAD_TEST_F(LayerTreeHostTestOpacityChange); | 
|  |  | 
|  | class LayerTreeHostTestDeviceScaleFactorScalesViewportAndLayers | 
|  | : public LayerTreeHostTest { | 
|  | public: | 
|  | LayerTreeHostTestDeviceScaleFactorScalesViewportAndLayers() {} | 
|  |  | 
|  | void InitializeSettings(LayerTreeSettings* settings) override { | 
|  | // PictureLayer can only be used with impl side painting enabled. | 
|  | settings->impl_side_painting = true; | 
|  | } | 
|  |  | 
|  | void BeginTest() override { | 
|  | client_.set_fill_with_nonsolid_color(true); | 
|  | root_layer_ = FakePictureLayer::Create(&client_); | 
|  | child_layer_ = FakePictureLayer::Create(&client_); | 
|  |  | 
|  | layer_tree_host()->SetViewportSize(gfx::Size(60, 60)); | 
|  | layer_tree_host()->SetDeviceScaleFactor(1.5); | 
|  | EXPECT_EQ(gfx::Size(60, 60), layer_tree_host()->device_viewport_size()); | 
|  |  | 
|  | root_layer_->AddChild(child_layer_); | 
|  |  | 
|  | root_layer_->SetIsDrawable(true); | 
|  | root_layer_->SetBounds(gfx::Size(30, 30)); | 
|  |  | 
|  | child_layer_->SetIsDrawable(true); | 
|  | child_layer_->SetPosition(gfx::Point(2, 2)); | 
|  | child_layer_->SetBounds(gfx::Size(10, 10)); | 
|  |  | 
|  | layer_tree_host()->SetRootLayer(root_layer_); | 
|  |  | 
|  | PostSetNeedsCommitToMainThread(); | 
|  | } | 
|  |  | 
|  | void DidActivateTreeOnThread(LayerTreeHostImpl* impl) override { | 
|  | // Should only do one commit. | 
|  | EXPECT_EQ(0, impl->active_tree()->source_frame_number()); | 
|  | // Device scale factor should come over to impl. | 
|  | EXPECT_NEAR(impl->device_scale_factor(), 1.5f, 0.00001f); | 
|  |  | 
|  | // Both layers are on impl. | 
|  | ASSERT_EQ(1u, impl->active_tree()->root_layer()->children().size()); | 
|  |  | 
|  | // Device viewport is scaled. | 
|  | EXPECT_EQ(gfx::Size(60, 60), impl->DrawViewportSize()); | 
|  |  | 
|  | FakePictureLayerImpl* root = | 
|  | static_cast<FakePictureLayerImpl*>(impl->active_tree()->root_layer()); | 
|  | FakePictureLayerImpl* child = static_cast<FakePictureLayerImpl*>( | 
|  | impl->active_tree()->root_layer()->children()[0]); | 
|  |  | 
|  | // Positions remain in layout pixels. | 
|  | EXPECT_EQ(gfx::Point(0, 0), root->position()); | 
|  | EXPECT_EQ(gfx::Point(2, 2), child->position()); | 
|  |  | 
|  | // Compute all the layer transforms for the frame. | 
|  | LayerTreeHostImpl::FrameData frame_data; | 
|  | impl->PrepareToDraw(&frame_data); | 
|  | impl->DidDrawAllLayers(frame_data); | 
|  |  | 
|  | const LayerImplList& render_surface_layer_list = | 
|  | *frame_data.render_surface_layer_list; | 
|  |  | 
|  | // Both layers should be drawing into the root render surface. | 
|  | ASSERT_EQ(1u, render_surface_layer_list.size()); | 
|  | ASSERT_EQ(root->render_surface(), | 
|  | render_surface_layer_list[0]->render_surface()); | 
|  | ASSERT_EQ(2u, root->render_surface()->layer_list().size()); | 
|  |  | 
|  | // The root render surface is the size of the viewport. | 
|  | EXPECT_EQ(gfx::Rect(0, 0, 60, 60), root->render_surface()->content_rect()); | 
|  |  | 
|  | // The max tiling scale of the child should be scaled. | 
|  | EXPECT_FLOAT_EQ(1.5f, child->MaximumTilingContentsScale()); | 
|  |  | 
|  | gfx::Transform scale_transform; | 
|  | scale_transform.Scale(impl->device_scale_factor(), | 
|  | impl->device_scale_factor()); | 
|  |  | 
|  | // The root layer is scaled by 2x. | 
|  | gfx::Transform root_screen_space_transform = scale_transform; | 
|  | gfx::Transform root_draw_transform = scale_transform; | 
|  |  | 
|  | EXPECT_EQ(root_draw_transform, root->draw_transform()); | 
|  | EXPECT_EQ(root_screen_space_transform, root->screen_space_transform()); | 
|  |  | 
|  | // The child is at position 2,2, which is transformed to 3,3 after the scale | 
|  | gfx::Transform child_transform; | 
|  | child_transform.Translate(3.f, 3.f); | 
|  | child_transform.Scale(child->MaximumTilingContentsScale(), | 
|  | child->MaximumTilingContentsScale()); | 
|  |  | 
|  | EXPECT_TRANSFORMATION_MATRIX_EQ(child_transform, child->draw_transform()); | 
|  | EXPECT_TRANSFORMATION_MATRIX_EQ(child_transform, | 
|  | child->screen_space_transform()); | 
|  |  | 
|  | EndTest(); | 
|  | } | 
|  |  | 
|  | void AfterTest() override {} | 
|  |  | 
|  | private: | 
|  | FakeContentLayerClient client_; | 
|  | scoped_refptr<FakePictureLayer> root_layer_; | 
|  | scoped_refptr<FakePictureLayer> child_layer_; | 
|  | }; | 
|  |  | 
|  | MULTI_THREAD_TEST_F(LayerTreeHostTestDeviceScaleFactorScalesViewportAndLayers); | 
|  |  | 
|  | // TODO(sohanjg) : Remove it once impl-side painting ships everywhere. | 
|  | // Verify atomicity of commits and reuse of textures. | 
|  | class LayerTreeHostTestDirectRendererAtomicCommit : public LayerTreeHostTest { | 
|  | public: | 
|  | void InitializeSettings(LayerTreeSettings* settings) override { | 
|  | settings->renderer_settings.texture_id_allocation_chunk_size = 1; | 
|  | // Make sure partial texture updates are turned off. | 
|  | settings->max_partial_texture_updates = 0; | 
|  | // Linear fade animator prevents scrollbars from drawing immediately. | 
|  | settings->scrollbar_animator = LayerTreeSettings::NoAnimator; | 
|  | } | 
|  |  | 
|  | void SetupTree() override { | 
|  | layer_ = FakeContentLayer::Create(&client_); | 
|  | layer_->SetBounds(gfx::Size(10, 20)); | 
|  |  | 
|  | bool paint_scrollbar = true; | 
|  | bool has_thumb = false; | 
|  | scrollbar_ = FakePaintedScrollbarLayer::Create( | 
|  | paint_scrollbar, has_thumb, layer_->id()); | 
|  | scrollbar_->SetPosition(gfx::Point(0, 10)); | 
|  | scrollbar_->SetBounds(gfx::Size(10, 10)); | 
|  |  | 
|  | layer_->AddChild(scrollbar_); | 
|  |  | 
|  | layer_tree_host()->SetRootLayer(layer_); | 
|  | LayerTreeHostTest::SetupTree(); | 
|  | } | 
|  |  | 
|  | void BeginTest() override { | 
|  | drew_frame_ = -1; | 
|  | PostSetNeedsCommitToMainThread(); | 
|  | } | 
|  |  | 
|  | void DidActivateTreeOnThread(LayerTreeHostImpl* impl) override { | 
|  | ASSERT_EQ(0u, layer_tree_host()->settings().max_partial_texture_updates); | 
|  |  | 
|  | TestWebGraphicsContext3D* context = TestContext(); | 
|  |  | 
|  | switch (impl->active_tree()->source_frame_number()) { | 
|  | case 0: | 
|  | // Number of textures should be one for each layer | 
|  | ASSERT_EQ(2u, context->NumTextures()); | 
|  | // Number of textures used for commit should be one for each layer. | 
|  | EXPECT_EQ(2u, context->NumUsedTextures()); | 
|  | // Verify that used texture is correct. | 
|  | EXPECT_TRUE(context->UsedTexture(context->TextureAt(0))); | 
|  | EXPECT_TRUE(context->UsedTexture(context->TextureAt(1))); | 
|  |  | 
|  | context->ResetUsedTextures(); | 
|  | break; | 
|  | case 1: | 
|  | // Number of textures should be one for scrollbar layer since it was | 
|  | // requested and deleted on the impl-thread, and double for the content | 
|  | // layer since its first texture is used by impl thread and cannot by | 
|  | // used for update. | 
|  | ASSERT_EQ(3u, context->NumTextures()); | 
|  | // Number of textures used for commit should be one for each layer. | 
|  | EXPECT_EQ(2u, context->NumUsedTextures()); | 
|  | // First textures should not have been used. | 
|  | EXPECT_FALSE(context->UsedTexture(context->TextureAt(0))); | 
|  | EXPECT_TRUE(context->UsedTexture(context->TextureAt(1))); | 
|  | // New textures should have been used. | 
|  | EXPECT_TRUE(context->UsedTexture(context->TextureAt(2))); | 
|  | context->ResetUsedTextures(); | 
|  | break; | 
|  | case 2: | 
|  | EndTest(); | 
|  | break; | 
|  | default: | 
|  | NOTREACHED(); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | void DrawLayersOnThread(LayerTreeHostImpl* impl) override { | 
|  | TestWebGraphicsContext3D* context = TestContext(); | 
|  |  | 
|  | if (drew_frame_ == impl->active_tree()->source_frame_number()) { | 
|  | EXPECT_EQ(0u, context->NumUsedTextures()) << "For frame " << drew_frame_; | 
|  | return; | 
|  | } | 
|  | drew_frame_ = impl->active_tree()->source_frame_number(); | 
|  |  | 
|  | // We draw/ship one texture each frame for each layer. | 
|  | EXPECT_EQ(2u, context->NumUsedTextures()); | 
|  | context->ResetUsedTextures(); | 
|  |  | 
|  | if (!TestEnded()) | 
|  | PostSetNeedsCommitToMainThread(); | 
|  | } | 
|  |  | 
|  | void Layout() override { | 
|  | layer_->SetNeedsDisplay(); | 
|  | scrollbar_->SetNeedsDisplay(); | 
|  | } | 
|  |  | 
|  | void AfterTest() override {} | 
|  |  | 
|  | protected: | 
|  | FakeContentLayerClient client_; | 
|  | scoped_refptr<FakeContentLayer> layer_; | 
|  | scoped_refptr<FakePaintedScrollbarLayer> scrollbar_; | 
|  | int drew_frame_; | 
|  | }; | 
|  |  | 
|  | MULTI_THREAD_DIRECT_RENDERER_NOIMPL_TEST_F( | 
|  | LayerTreeHostTestDirectRendererAtomicCommit); | 
|  |  | 
|  | // TODO(sohanjg) : Remove it once impl-side painting ships everywhere. | 
|  | class LayerTreeHostTestDelegatingRendererAtomicCommit | 
|  | : public LayerTreeHostTestDirectRendererAtomicCommit { | 
|  | public: | 
|  | void DidActivateTreeOnThread(LayerTreeHostImpl* impl) override { | 
|  | ASSERT_EQ(0u, layer_tree_host()->settings().max_partial_texture_updates); | 
|  |  | 
|  | TestWebGraphicsContext3D* context = TestContext(); | 
|  |  | 
|  | switch (impl->active_tree()->source_frame_number()) { | 
|  | case 0: | 
|  | // Number of textures should be one for each layer | 
|  | ASSERT_EQ(2u, context->NumTextures()); | 
|  | // Number of textures used for commit should be one for each layer. | 
|  | EXPECT_EQ(2u, context->NumUsedTextures()); | 
|  | // Verify that used texture is correct. | 
|  | EXPECT_TRUE(context->UsedTexture(context->TextureAt(0))); | 
|  | EXPECT_TRUE(context->UsedTexture(context->TextureAt(1))); | 
|  | context->ResetUsedTextures(); | 
|  | break; | 
|  | case 1: | 
|  | // Number of textures should be doubled as the first context layer | 
|  | // texture is being used by the impl-thread and cannot be used for | 
|  | // update.  The scrollbar behavior is different direct renderer because | 
|  | // UI resource deletion with delegating renderer occurs after tree | 
|  | // activation. | 
|  | ASSERT_EQ(4u, context->NumTextures()); | 
|  | // Number of textures used for commit should still be | 
|  | // one for each layer. | 
|  | EXPECT_EQ(2u, context->NumUsedTextures()); | 
|  | // First textures should not have been used. | 
|  | EXPECT_FALSE(context->UsedTexture(context->TextureAt(0))); | 
|  | EXPECT_FALSE(context->UsedTexture(context->TextureAt(1))); | 
|  | // New textures should have been used. | 
|  | EXPECT_TRUE(context->UsedTexture(context->TextureAt(2))); | 
|  | EXPECT_TRUE(context->UsedTexture(context->TextureAt(3))); | 
|  | context->ResetUsedTextures(); | 
|  | break; | 
|  | case 2: | 
|  | EndTest(); | 
|  | break; | 
|  | default: | 
|  | NOTREACHED(); | 
|  | break; | 
|  | } | 
|  | } | 
|  | }; | 
|  |  | 
|  | MULTI_THREAD_DELEGATING_RENDERER_NOIMPL_TEST_F( | 
|  | LayerTreeHostTestDelegatingRendererAtomicCommit); | 
|  |  | 
|  | static void SetLayerPropertiesForTesting(Layer* layer, | 
|  | Layer* parent, | 
|  | const gfx::Transform& transform, | 
|  | const gfx::Point3F& transform_origin, | 
|  | const gfx::PointF& position, | 
|  | const gfx::Size& bounds, | 
|  | bool opaque) { | 
|  | layer->RemoveAllChildren(); | 
|  | if (parent) | 
|  | parent->AddChild(layer); | 
|  | layer->SetTransform(transform); | 
|  | layer->SetTransformOrigin(transform_origin); | 
|  | layer->SetPosition(position); | 
|  | layer->SetBounds(bounds); | 
|  | layer->SetContentsOpaque(opaque); | 
|  | } | 
|  |  | 
|  | // TODO(sohanjg) : Remove it once impl-side painting ships everywhere. | 
|  | class LayerTreeHostTestAtomicCommitWithPartialUpdate | 
|  | : public LayerTreeHostTest { | 
|  | public: | 
|  | void InitializeSettings(LayerTreeSettings* settings) override { | 
|  | settings->renderer_settings.texture_id_allocation_chunk_size = 1; | 
|  | // Allow one partial texture update. | 
|  | settings->max_partial_texture_updates = 1; | 
|  | // No partial updates when impl side painting is enabled. | 
|  | settings->impl_side_painting = false; | 
|  | } | 
|  |  | 
|  | void SetupTree() override { | 
|  | parent_ = FakeContentLayer::Create(&client_); | 
|  | parent_->SetBounds(gfx::Size(10, 20)); | 
|  |  | 
|  | child_ = FakeContentLayer::Create(&client_); | 
|  | child_->SetPosition(gfx::Point(0, 10)); | 
|  | child_->SetBounds(gfx::Size(3, 10)); | 
|  |  | 
|  | parent_->AddChild(child_); | 
|  |  | 
|  | layer_tree_host()->SetRootLayer(parent_); | 
|  | LayerTreeHostTest::SetupTree(); | 
|  | } | 
|  |  | 
|  | void BeginTest() override { PostSetNeedsCommitToMainThread(); } | 
|  |  | 
|  | void DidCommitAndDrawFrame() override { | 
|  | switch (layer_tree_host()->source_frame_number()) { | 
|  | case 1: | 
|  | parent_->SetNeedsDisplay(); | 
|  | child_->SetNeedsDisplay(); | 
|  | break; | 
|  | case 2: | 
|  | // Damage part of layers. | 
|  | parent_->SetNeedsDisplayRect(gfx::Rect(5, 5)); | 
|  | child_->SetNeedsDisplayRect(gfx::Rect(5, 5)); | 
|  | break; | 
|  | case 3: | 
|  | child_->SetNeedsDisplay(); | 
|  | layer_tree_host()->SetViewportSize(gfx::Size(10, 10)); | 
|  | break; | 
|  | case 4: | 
|  | layer_tree_host()->SetViewportSize(gfx::Size(10, 20)); | 
|  | break; | 
|  | case 5: | 
|  | EndTest(); | 
|  | break; | 
|  | default: | 
|  | NOTREACHED() << layer_tree_host()->source_frame_number(); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | void CommitCompleteOnThread(LayerTreeHostImpl* impl) override { | 
|  | ASSERT_EQ(1u, layer_tree_host()->settings().max_partial_texture_updates); | 
|  |  | 
|  | TestWebGraphicsContext3D* context = TestContext(); | 
|  |  | 
|  | switch (impl->active_tree()->source_frame_number()) { | 
|  | case 0: | 
|  | // Number of textures should be one for each layer. | 
|  | ASSERT_EQ(2u, context->NumTextures()); | 
|  | // Number of textures used for commit should be one for each layer. | 
|  | EXPECT_EQ(2u, context->NumUsedTextures()); | 
|  | // Verify that used textures are correct. | 
|  | EXPECT_TRUE(context->UsedTexture(context->TextureAt(0))); | 
|  | EXPECT_TRUE(context->UsedTexture(context->TextureAt(1))); | 
|  | context->ResetUsedTextures(); | 
|  | break; | 
|  | case 1: | 
|  | if (HasImplThread()) { | 
|  | // Number of textures should be two for each content layer. | 
|  | ASSERT_EQ(4u, context->NumTextures()); | 
|  | } else { | 
|  | // In single thread we can always do partial updates, so the limit has | 
|  | // no effect. | 
|  | ASSERT_EQ(2u, context->NumTextures()); | 
|  | } | 
|  | // Number of textures used for commit should be one for each content | 
|  | // layer. | 
|  | EXPECT_EQ(2u, context->NumUsedTextures()); | 
|  |  | 
|  | if (HasImplThread()) { | 
|  | // First content textures should not have been used. | 
|  | EXPECT_FALSE(context->UsedTexture(context->TextureAt(0))); | 
|  | EXPECT_FALSE(context->UsedTexture(context->TextureAt(1))); | 
|  | // New textures should have been used. | 
|  | EXPECT_TRUE(context->UsedTexture(context->TextureAt(2))); | 
|  | EXPECT_TRUE(context->UsedTexture(context->TextureAt(3))); | 
|  | } else { | 
|  | // In single thread we can always do partial updates, so the limit has | 
|  | // no effect. | 
|  | EXPECT_TRUE(context->UsedTexture(context->TextureAt(0))); | 
|  | EXPECT_TRUE(context->UsedTexture(context->TextureAt(1))); | 
|  | } | 
|  |  | 
|  | context->ResetUsedTextures(); | 
|  | break; | 
|  | case 2: | 
|  | if (HasImplThread()) { | 
|  | // Number of textures should be two for each content layer. | 
|  | ASSERT_EQ(4u, context->NumTextures()); | 
|  | } else { | 
|  | // In single thread we can always do partial updates, so the limit has | 
|  | // no effect. | 
|  | ASSERT_EQ(2u, context->NumTextures()); | 
|  | } | 
|  | // Number of textures used for commit should be one for each content | 
|  | // layer. | 
|  | EXPECT_EQ(2u, context->NumUsedTextures()); | 
|  |  | 
|  | if (HasImplThread()) { | 
|  | // One content layer does a partial update also. | 
|  | EXPECT_TRUE(context->UsedTexture(context->TextureAt(2))); | 
|  | EXPECT_FALSE(context->UsedTexture(context->TextureAt(3))); | 
|  | } else { | 
|  | // In single thread we can always do partial updates, so the limit has | 
|  | // no effect. | 
|  | EXPECT_TRUE(context->UsedTexture(context->TextureAt(0))); | 
|  | EXPECT_TRUE(context->UsedTexture(context->TextureAt(1))); | 
|  | } | 
|  |  | 
|  | context->ResetUsedTextures(); | 
|  | break; | 
|  | case 3: | 
|  | // No textures should be used for commit. | 
|  | EXPECT_EQ(0u, context->NumUsedTextures()); | 
|  |  | 
|  | context->ResetUsedTextures(); | 
|  | break; | 
|  | case 4: | 
|  | // Number of textures used for commit should be one, for the | 
|  | // content layer. | 
|  | EXPECT_EQ(1u, context->NumUsedTextures()); | 
|  |  | 
|  | context->ResetUsedTextures(); | 
|  | break; | 
|  | default: | 
|  | NOTREACHED(); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | void DrawLayersOnThread(LayerTreeHostImpl* impl) override { | 
|  | EXPECT_LT(impl->active_tree()->source_frame_number(), 5); | 
|  |  | 
|  | TestWebGraphicsContext3D* context = TestContext(); | 
|  |  | 
|  | // Number of textures used for drawing should one per layer except for | 
|  | // frame 3 where the viewport only contains one layer. | 
|  | if (impl->active_tree()->source_frame_number() == 3) { | 
|  | EXPECT_EQ(1u, context->NumUsedTextures()); | 
|  | } else { | 
|  | EXPECT_EQ(2u, context->NumUsedTextures()) | 
|  | << "For frame " << impl->active_tree()->source_frame_number(); | 
|  | } | 
|  |  | 
|  | context->ResetUsedTextures(); | 
|  | } | 
|  |  | 
|  | void AfterTest() override {} | 
|  |  | 
|  | private: | 
|  | FakeContentLayerClient client_; | 
|  | scoped_refptr<FakeContentLayer> parent_; | 
|  | scoped_refptr<FakeContentLayer> child_; | 
|  | }; | 
|  |  | 
|  | // Partial updates are not possible with a delegating renderer. | 
|  | SINGLE_AND_MULTI_THREAD_DIRECT_RENDERER_TEST_F( | 
|  | LayerTreeHostTestAtomicCommitWithPartialUpdate); | 
|  |  | 
|  | // TODO(sohanjg) : Make it work with impl-side painting. | 
|  | class LayerTreeHostTestSurfaceNotAllocatedForLayersOutsideMemoryLimit | 
|  | : public LayerTreeHostTest { | 
|  | protected: | 
|  | void SetupTree() override { | 
|  | root_layer_ = FakeContentLayer::Create(&client_); | 
|  | root_layer_->SetBounds(gfx::Size(100, 100)); | 
|  |  | 
|  | surface_layer1_ = FakeContentLayer::Create(&client_); | 
|  | surface_layer1_->SetBounds(gfx::Size(100, 100)); | 
|  | surface_layer1_->SetForceRenderSurface(true); | 
|  | surface_layer1_->SetOpacity(0.5f); | 
|  | root_layer_->AddChild(surface_layer1_); | 
|  |  | 
|  | surface_layer2_ = FakeContentLayer::Create(&client_); | 
|  | surface_layer2_->SetBounds(gfx::Size(100, 100)); | 
|  | surface_layer2_->SetForceRenderSurface(true); | 
|  | surface_layer2_->SetOpacity(0.5f); | 
|  | surface_layer1_->AddChild(surface_layer2_); | 
|  |  | 
|  | replica_layer1_ = FakeContentLayer::Create(&client_); | 
|  | surface_layer1_->SetReplicaLayer(replica_layer1_.get()); | 
|  |  | 
|  | replica_layer2_ = FakeContentLayer::Create(&client_); | 
|  | surface_layer2_->SetReplicaLayer(replica_layer2_.get()); | 
|  |  | 
|  | layer_tree_host()->SetRootLayer(root_layer_); | 
|  | LayerTreeHostTest::SetupTree(); | 
|  | } | 
|  |  | 
|  | void BeginTest() override { PostSetNeedsCommitToMainThread(); } | 
|  |  | 
|  | void DrawLayersOnThread(LayerTreeHostImpl* host_impl) override { | 
|  | Renderer* renderer = host_impl->renderer(); | 
|  | RenderPassId surface1_render_pass_id = host_impl->active_tree() | 
|  | ->root_layer() | 
|  | ->children()[0] | 
|  | ->render_surface() | 
|  | ->GetRenderPassId(); | 
|  | RenderPassId surface2_render_pass_id = host_impl->active_tree() | 
|  | ->root_layer() | 
|  | ->children()[0] | 
|  | ->children()[0] | 
|  | ->render_surface() | 
|  | ->GetRenderPassId(); | 
|  |  | 
|  | switch (host_impl->active_tree()->source_frame_number()) { | 
|  | case 0: | 
|  | EXPECT_TRUE( | 
|  | renderer->HasAllocatedResourcesForTesting(surface1_render_pass_id)); | 
|  | EXPECT_TRUE( | 
|  | renderer->HasAllocatedResourcesForTesting(surface2_render_pass_id)); | 
|  |  | 
|  | // Reduce the memory limit to only fit the root layer and one render | 
|  | // surface. This prevents any contents drawing into surfaces | 
|  | // from being allocated. | 
|  | host_impl->SetMemoryPolicy(ManagedMemoryPolicy(100 * 100 * 4 * 2)); | 
|  | break; | 
|  | case 1: | 
|  | EXPECT_FALSE( | 
|  | renderer->HasAllocatedResourcesForTesting(surface1_render_pass_id)); | 
|  | EXPECT_FALSE( | 
|  | renderer->HasAllocatedResourcesForTesting(surface2_render_pass_id)); | 
|  |  | 
|  | EndTest(); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | void DidCommitAndDrawFrame() override { | 
|  | if (layer_tree_host()->source_frame_number() < 2) | 
|  | root_layer_->SetNeedsDisplay(); | 
|  | } | 
|  |  | 
|  | void AfterTest() override { | 
|  | EXPECT_LE(2u, root_layer_->update_count()); | 
|  | EXPECT_LE(2u, surface_layer1_->update_count()); | 
|  | EXPECT_LE(2u, surface_layer2_->update_count()); | 
|  | } | 
|  |  | 
|  | FakeContentLayerClient client_; | 
|  | scoped_refptr<FakeContentLayer> root_layer_; | 
|  | scoped_refptr<FakeContentLayer> surface_layer1_; | 
|  | scoped_refptr<FakeContentLayer> replica_layer1_; | 
|  | scoped_refptr<FakeContentLayer> surface_layer2_; | 
|  | scoped_refptr<FakeContentLayer> replica_layer2_; | 
|  | }; | 
|  |  | 
|  | // Surfaces don't exist with a delegated renderer. | 
|  | SINGLE_AND_MULTI_THREAD_DIRECT_RENDERER_NOIMPL_TEST_F( | 
|  | LayerTreeHostTestSurfaceNotAllocatedForLayersOutsideMemoryLimit); | 
|  |  | 
|  | class EvictionTestLayer : public Layer { | 
|  | public: | 
|  | static scoped_refptr<EvictionTestLayer> Create() { | 
|  | return make_scoped_refptr(new EvictionTestLayer()); | 
|  | } | 
|  |  | 
|  | bool Update(ResourceUpdateQueue*, const OcclusionTracker<Layer>*) override; | 
|  | bool DrawsContent() const override { return true; } | 
|  |  | 
|  | scoped_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl) override; | 
|  | void PushPropertiesTo(LayerImpl* impl) override; | 
|  | void SetTexturePriorities(const PriorityCalculator&) override; | 
|  |  | 
|  | bool HaveBackingTexture() const { | 
|  | return texture_.get() ? texture_->have_backing_texture() : false; | 
|  | } | 
|  |  | 
|  | private: | 
|  | EvictionTestLayer() : Layer() {} | 
|  | ~EvictionTestLayer() override {} | 
|  |  | 
|  | void CreateTextureIfNeeded() { | 
|  | if (texture_) | 
|  | return; | 
|  | texture_ = PrioritizedResource::Create( | 
|  | layer_tree_host()->contents_texture_manager()); | 
|  | texture_->SetDimensions(gfx::Size(10, 10), RGBA_8888); | 
|  | bitmap_.allocN32Pixels(10, 10); | 
|  | } | 
|  |  | 
|  | scoped_ptr<PrioritizedResource> texture_; | 
|  | SkBitmap bitmap_; | 
|  | }; | 
|  |  | 
|  | class EvictionTestLayerImpl : public LayerImpl { | 
|  | public: | 
|  | static scoped_ptr<EvictionTestLayerImpl> Create(LayerTreeImpl* tree_impl, | 
|  | int id) { | 
|  | return make_scoped_ptr(new EvictionTestLayerImpl(tree_impl, id)); | 
|  | } | 
|  | ~EvictionTestLayerImpl() override {} | 
|  |  | 
|  | void AppendQuads(RenderPass* render_pass, | 
|  | const Occlusion& occlusion_in_content_space, | 
|  | AppendQuadsData* append_quads_data) override { | 
|  | ASSERT_TRUE(has_texture_); | 
|  | ASSERT_NE(0u, layer_tree_impl()->resource_provider()->num_resources()); | 
|  | } | 
|  |  | 
|  | void SetHasTexture(bool has_texture) { has_texture_ = has_texture; } | 
|  |  | 
|  | private: | 
|  | EvictionTestLayerImpl(LayerTreeImpl* tree_impl, int id) | 
|  | : LayerImpl(tree_impl, id), has_texture_(false) {} | 
|  |  | 
|  | bool has_texture_; | 
|  | }; | 
|  |  | 
|  | void EvictionTestLayer::SetTexturePriorities(const PriorityCalculator&) { | 
|  | CreateTextureIfNeeded(); | 
|  | if (!texture_) | 
|  | return; | 
|  | texture_->set_request_priority(PriorityCalculator::UIPriority(true)); | 
|  | } | 
|  |  | 
|  | bool EvictionTestLayer::Update(ResourceUpdateQueue* queue, | 
|  | const OcclusionTracker<Layer>* occlusion) { | 
|  | CreateTextureIfNeeded(); | 
|  | if (!texture_) | 
|  | return false; | 
|  |  | 
|  | gfx::Rect full_rect(0, 0, 10, 10); | 
|  | ResourceUpdate upload = ResourceUpdate::Create( | 
|  | texture_.get(), &bitmap_, full_rect, full_rect, gfx::Vector2d()); | 
|  | queue->AppendFullUpload(upload); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | scoped_ptr<LayerImpl> EvictionTestLayer::CreateLayerImpl( | 
|  | LayerTreeImpl* tree_impl) { | 
|  | return EvictionTestLayerImpl::Create(tree_impl, layer_id_); | 
|  | } | 
|  |  | 
|  | void EvictionTestLayer::PushPropertiesTo(LayerImpl* layer_impl) { | 
|  | Layer::PushPropertiesTo(layer_impl); | 
|  |  | 
|  | EvictionTestLayerImpl* test_layer_impl = | 
|  | static_cast<EvictionTestLayerImpl*>(layer_impl); | 
|  | test_layer_impl->SetHasTexture(texture_->have_backing_texture()); | 
|  | } | 
|  |  | 
|  | class LayerTreeHostTestEvictTextures : public LayerTreeHostTest { | 
|  | public: | 
|  | LayerTreeHostTestEvictTextures() | 
|  | : layer_(EvictionTestLayer::Create()), | 
|  | impl_for_evict_textures_(0), | 
|  | num_commits_(0) {} | 
|  |  | 
|  | void BeginTest() override { | 
|  | layer_tree_host()->SetRootLayer(layer_); | 
|  | layer_tree_host()->SetViewportSize(gfx::Size(10, 20)); | 
|  |  | 
|  | gfx::Transform identity_matrix; | 
|  | SetLayerPropertiesForTesting(layer_.get(), | 
|  | 0, | 
|  | identity_matrix, | 
|  | gfx::Point3F(0.f, 0.f, 0.f), | 
|  | gfx::PointF(0.f, 0.f), | 
|  | gfx::Size(10, 20), | 
|  | true); | 
|  |  | 
|  | PostSetNeedsCommitToMainThread(); | 
|  | } | 
|  |  | 
|  | void PostEvictTextures() { | 
|  | ImplThreadTaskRunner()->PostTask( | 
|  | FROM_HERE, | 
|  | base::Bind(&LayerTreeHostTestEvictTextures::EvictTexturesOnImplThread, | 
|  | base::Unretained(this))); | 
|  | } | 
|  |  | 
|  | void EvictTexturesOnImplThread() { | 
|  | DCHECK(impl_for_evict_textures_); | 
|  | impl_for_evict_textures_->EvictTexturesForTesting(); | 
|  | } | 
|  |  | 
|  | // Commit 1: Just commit and draw normally, then post an eviction at the end | 
|  | // that will trigger a commit. | 
|  | // Commit 2: Triggered by the eviction, let it go through and then set | 
|  | // needsCommit. | 
|  | // Commit 3: Triggered by the setNeedsCommit. In Layout(), post an eviction | 
|  | // task, which will be handled before the commit. Don't set needsCommit, it | 
|  | // should have been posted. A frame should not be drawn (note, | 
|  | // didCommitAndDrawFrame may be called anyway). | 
|  | // Commit 4: Triggered by the eviction, let it go through and then set | 
|  | // needsCommit. | 
|  | // Commit 5: Triggered by the setNeedsCommit, post an eviction task in | 
|  | // Layout(), a frame should not be drawn but a commit will be posted. | 
|  | // Commit 6: Triggered by the eviction, post an eviction task in | 
|  | // Layout(), which will be a noop, letting the commit (which recreates the | 
|  | // textures) go through and draw a frame, then end the test. | 
|  | // | 
|  | // Commits 1+2 test the eviction recovery path where eviction happens outside | 
|  | // of the beginFrame/commit pair. | 
|  | // Commits 3+4 test the eviction recovery path where eviction happens inside | 
|  | // the beginFrame/commit pair. | 
|  | // Commits 5+6 test the path where an eviction happens during the eviction | 
|  | // recovery path. | 
|  | void DidCommit() override { | 
|  | switch (num_commits_) { | 
|  | case 1: | 
|  | EXPECT_TRUE(layer_->HaveBackingTexture()); | 
|  | PostEvictTextures(); | 
|  | break; | 
|  | case 2: | 
|  | EXPECT_TRUE(layer_->HaveBackingTexture()); | 
|  | layer_tree_host()->SetNeedsCommit(); | 
|  | break; | 
|  | case 3: | 
|  | break; | 
|  | case 4: | 
|  | EXPECT_TRUE(layer_->HaveBackingTexture()); | 
|  | layer_tree_host()->SetNeedsCommit(); | 
|  | break; | 
|  | case 5: | 
|  | break; | 
|  | case 6: | 
|  | EXPECT_TRUE(layer_->HaveBackingTexture()); | 
|  | EndTest(); | 
|  | break; | 
|  | default: | 
|  | NOTREACHED(); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | void CommitCompleteOnThread(LayerTreeHostImpl* impl) override { | 
|  | impl_for_evict_textures_ = impl; | 
|  | } | 
|  |  | 
|  | void Layout() override { | 
|  | ++num_commits_; | 
|  | switch (num_commits_) { | 
|  | case 1: | 
|  | case 2: | 
|  | break; | 
|  | case 3: | 
|  | PostEvictTextures(); | 
|  | break; | 
|  | case 4: | 
|  | // We couldn't check in didCommitAndDrawFrame on commit 3, | 
|  | // so check here. | 
|  | EXPECT_FALSE(layer_->HaveBackingTexture()); | 
|  | break; | 
|  | case 5: | 
|  | PostEvictTextures(); | 
|  | break; | 
|  | case 6: | 
|  | // We couldn't check in didCommitAndDrawFrame on commit 5, | 
|  | // so check here. | 
|  | EXPECT_FALSE(layer_->HaveBackingTexture()); | 
|  | PostEvictTextures(); | 
|  | break; | 
|  | default: | 
|  | NOTREACHED(); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | void AfterTest() override {} | 
|  |  | 
|  | private: | 
|  | FakeContentLayerClient client_; | 
|  | scoped_refptr<EvictionTestLayer> layer_; | 
|  | LayerTreeHostImpl* impl_for_evict_textures_; | 
|  | int num_commits_; | 
|  | }; | 
|  |  | 
|  | MULTI_THREAD_NOIMPL_TEST_F(LayerTreeHostTestEvictTextures); | 
|  |  | 
|  | class LayerTreeHostTestContinuousInvalidate : public LayerTreeHostTest { | 
|  | public: | 
|  | LayerTreeHostTestContinuousInvalidate() | 
|  | : num_commit_complete_(0), num_draw_layers_(0) {} | 
|  |  | 
|  | void BeginTest() override { | 
|  | layer_tree_host()->SetViewportSize(gfx::Size(10, 10)); | 
|  | layer_tree_host()->root_layer()->SetBounds(gfx::Size(10, 10)); | 
|  |  | 
|  | if (layer_tree_host()->settings().impl_side_painting) | 
|  | layer_ = FakePictureLayer::Create(&client_); | 
|  | else | 
|  | layer_ = FakeContentLayer::Create(&client_); | 
|  |  | 
|  | layer_->SetBounds(gfx::Size(10, 10)); | 
|  | layer_->SetPosition(gfx::PointF(0.f, 0.f)); | 
|  | layer_->SetIsDrawable(true); | 
|  | layer_tree_host()->root_layer()->AddChild(layer_); | 
|  |  | 
|  | PostSetNeedsCommitToMainThread(); | 
|  | } | 
|  |  | 
|  | void DidCommitAndDrawFrame() override { | 
|  | if (num_draw_layers_ == 2) | 
|  | return; | 
|  | layer_->SetNeedsDisplay(); | 
|  | } | 
|  |  | 
|  | void CommitCompleteOnThread(LayerTreeHostImpl* impl) override { | 
|  | if (num_draw_layers_ == 1) | 
|  | num_commit_complete_++; | 
|  | } | 
|  |  | 
|  | void DrawLayersOnThread(LayerTreeHostImpl* impl) override { | 
|  | num_draw_layers_++; | 
|  | if (num_draw_layers_ == 2) | 
|  | EndTest(); | 
|  | } | 
|  |  | 
|  | void AfterTest() override { | 
|  | // Check that we didn't commit twice between first and second draw. | 
|  | EXPECT_EQ(1, num_commit_complete_); | 
|  | } | 
|  |  | 
|  | private: | 
|  | FakeContentLayerClient client_; | 
|  | scoped_refptr<Layer> layer_; | 
|  | int num_commit_complete_; | 
|  | int num_draw_layers_; | 
|  | }; | 
|  |  | 
|  | SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestContinuousInvalidate); | 
|  |  | 
|  | class LayerTreeHostTestDeferCommits : public LayerTreeHostTest { | 
|  | public: | 
|  | LayerTreeHostTestDeferCommits() | 
|  | : num_commits_deferred_(0), num_complete_commits_(0) {} | 
|  |  | 
|  | void BeginTest() override { PostSetNeedsCommitToMainThread(); } | 
|  |  | 
|  | void DidDeferCommit() override { | 
|  | num_commits_deferred_++; | 
|  | layer_tree_host()->SetDeferCommits(false); | 
|  | } | 
|  |  | 
|  | void DidCommit() override { | 
|  | num_complete_commits_++; | 
|  | switch (num_complete_commits_) { | 
|  | case 1: | 
|  | EXPECT_EQ(0, num_commits_deferred_); | 
|  | layer_tree_host()->SetDeferCommits(true); | 
|  | PostSetNeedsCommitToMainThread(); | 
|  | break; | 
|  | case 2: | 
|  | EndTest(); | 
|  | break; | 
|  | default: | 
|  | NOTREACHED(); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | void AfterTest() override { | 
|  | EXPECT_EQ(1, num_commits_deferred_); | 
|  | EXPECT_EQ(2, num_complete_commits_); | 
|  | } | 
|  |  | 
|  | private: | 
|  | int num_commits_deferred_; | 
|  | int num_complete_commits_; | 
|  | }; | 
|  |  | 
|  | SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestDeferCommits); | 
|  |  | 
|  | class LayerTreeHostWithProxy : public LayerTreeHost { | 
|  | public: | 
|  | LayerTreeHostWithProxy(FakeLayerTreeHostClient* client, | 
|  | const LayerTreeSettings& settings, | 
|  | scoped_ptr<FakeProxy> proxy) | 
|  | : LayerTreeHost(client, NULL, NULL, settings) { | 
|  | proxy->SetLayerTreeHost(this); | 
|  | client->SetLayerTreeHost(this); | 
|  | InitializeForTesting(proxy.Pass()); | 
|  | } | 
|  | }; | 
|  |  | 
|  | TEST(LayerTreeHostTest, LimitPartialUpdates) { | 
|  | // When partial updates are not allowed, max updates should be 0. | 
|  | { | 
|  | FakeLayerTreeHostClient client(FakeLayerTreeHostClient::DIRECT_3D); | 
|  |  | 
|  | scoped_ptr<FakeProxy> proxy(new FakeProxy); | 
|  | proxy->GetRendererCapabilities().allow_partial_texture_updates = false; | 
|  | proxy->SetMaxPartialTextureUpdates(5); | 
|  |  | 
|  | LayerTreeSettings settings; | 
|  | settings.max_partial_texture_updates = 10; | 
|  |  | 
|  | LayerTreeHostWithProxy host(&client, settings, proxy.Pass()); | 
|  |  | 
|  | EXPECT_EQ(0u, host.MaxPartialTextureUpdates()); | 
|  | } | 
|  |  | 
|  | // When partial updates are allowed, | 
|  | // max updates should be limited by the proxy. | 
|  | { | 
|  | FakeLayerTreeHostClient client(FakeLayerTreeHostClient::DIRECT_3D); | 
|  |  | 
|  | scoped_ptr<FakeProxy> proxy(new FakeProxy); | 
|  | proxy->GetRendererCapabilities().allow_partial_texture_updates = true; | 
|  | proxy->SetMaxPartialTextureUpdates(5); | 
|  |  | 
|  | LayerTreeSettings settings; | 
|  | settings.max_partial_texture_updates = 10; | 
|  |  | 
|  | LayerTreeHostWithProxy host(&client, settings, proxy.Pass()); | 
|  |  | 
|  | EXPECT_EQ(5u, host.MaxPartialTextureUpdates()); | 
|  | } | 
|  |  | 
|  | // When partial updates are allowed, | 
|  | // max updates should also be limited by the settings. | 
|  | { | 
|  | FakeLayerTreeHostClient client(FakeLayerTreeHostClient::DIRECT_3D); | 
|  |  | 
|  | scoped_ptr<FakeProxy> proxy(new FakeProxy); | 
|  | proxy->GetRendererCapabilities().allow_partial_texture_updates = true; | 
|  | proxy->SetMaxPartialTextureUpdates(20); | 
|  |  | 
|  | LayerTreeSettings settings; | 
|  | settings.max_partial_texture_updates = 10; | 
|  |  | 
|  | LayerTreeHostWithProxy host(&client, settings, proxy.Pass()); | 
|  |  | 
|  | EXPECT_EQ(10u, host.MaxPartialTextureUpdates()); | 
|  | } | 
|  | } | 
|  |  | 
|  | TEST(LayerTreeHostTest, PartialUpdatesWithGLRenderer) { | 
|  | FakeLayerTreeHostClient client(FakeLayerTreeHostClient::DIRECT_3D); | 
|  |  | 
|  | LayerTreeSettings settings; | 
|  | settings.max_partial_texture_updates = 4; | 
|  | settings.single_thread_proxy_scheduler = false; | 
|  |  | 
|  | scoped_ptr<SharedBitmapManager> shared_bitmap_manager( | 
|  | new TestSharedBitmapManager()); | 
|  | scoped_ptr<LayerTreeHost> host = | 
|  | LayerTreeHost::CreateSingleThreaded(&client, | 
|  | &client, | 
|  | shared_bitmap_manager.get(), | 
|  | NULL, | 
|  | settings, | 
|  | base::MessageLoopProxy::current(), | 
|  | nullptr); | 
|  | client.SetLayerTreeHost(host.get()); | 
|  | host->Composite(base::TimeTicks::Now()); | 
|  |  | 
|  | EXPECT_EQ(4u, host->settings().max_partial_texture_updates); | 
|  | } | 
|  |  | 
|  | TEST(LayerTreeHostTest, PartialUpdatesWithSoftwareRenderer) { | 
|  | FakeLayerTreeHostClient client(FakeLayerTreeHostClient::DIRECT_SOFTWARE); | 
|  |  | 
|  | LayerTreeSettings settings; | 
|  | settings.max_partial_texture_updates = 4; | 
|  | settings.single_thread_proxy_scheduler = false; | 
|  |  | 
|  | scoped_ptr<SharedBitmapManager> shared_bitmap_manager( | 
|  | new TestSharedBitmapManager()); | 
|  | scoped_ptr<LayerTreeHost> host = | 
|  | LayerTreeHost::CreateSingleThreaded(&client, | 
|  | &client, | 
|  | shared_bitmap_manager.get(), | 
|  | NULL, | 
|  | settings, | 
|  | base::MessageLoopProxy::current(), | 
|  | nullptr); | 
|  | client.SetLayerTreeHost(host.get()); | 
|  | host->Composite(base::TimeTicks::Now()); | 
|  |  | 
|  | EXPECT_EQ(4u, host->settings().max_partial_texture_updates); | 
|  | } | 
|  |  | 
|  | TEST(LayerTreeHostTest, PartialUpdatesWithDelegatingRendererAndGLContent) { | 
|  | FakeLayerTreeHostClient client(FakeLayerTreeHostClient::DELEGATED_3D); | 
|  |  | 
|  | LayerTreeSettings settings; | 
|  | settings.max_partial_texture_updates = 4; | 
|  | settings.single_thread_proxy_scheduler = false; | 
|  |  | 
|  | scoped_ptr<SharedBitmapManager> shared_bitmap_manager( | 
|  | new TestSharedBitmapManager()); | 
|  | scoped_ptr<LayerTreeHost> host = | 
|  | LayerTreeHost::CreateSingleThreaded(&client, | 
|  | &client, | 
|  | shared_bitmap_manager.get(), | 
|  | NULL, | 
|  | settings, | 
|  | base::MessageLoopProxy::current(), | 
|  | nullptr); | 
|  | client.SetLayerTreeHost(host.get()); | 
|  | host->Composite(base::TimeTicks::Now()); | 
|  |  | 
|  | EXPECT_EQ(0u, host->MaxPartialTextureUpdates()); | 
|  | } | 
|  |  | 
|  | TEST(LayerTreeHostTest, | 
|  | PartialUpdatesWithDelegatingRendererAndSoftwareContent) { | 
|  | FakeLayerTreeHostClient client(FakeLayerTreeHostClient::DELEGATED_SOFTWARE); | 
|  |  | 
|  | LayerTreeSettings settings; | 
|  | settings.max_partial_texture_updates = 4; | 
|  | settings.single_thread_proxy_scheduler = false; | 
|  |  | 
|  | scoped_ptr<SharedBitmapManager> shared_bitmap_manager( | 
|  | new TestSharedBitmapManager()); | 
|  | scoped_ptr<LayerTreeHost> host = | 
|  | LayerTreeHost::CreateSingleThreaded(&client, | 
|  | &client, | 
|  | shared_bitmap_manager.get(), | 
|  | NULL, | 
|  | settings, | 
|  | base::MessageLoopProxy::current(), | 
|  | nullptr); | 
|  | client.SetLayerTreeHost(host.get()); | 
|  | host->Composite(base::TimeTicks::Now()); | 
|  |  | 
|  | EXPECT_EQ(0u, host->MaxPartialTextureUpdates()); | 
|  | } | 
|  |  | 
|  | // TODO(sohanjg) : Remove it once impl-side painting ships everywhere. | 
|  | class LayerTreeHostTestShutdownWithOnlySomeResourcesEvicted | 
|  | : public LayerTreeHostTest { | 
|  | public: | 
|  | LayerTreeHostTestShutdownWithOnlySomeResourcesEvicted() | 
|  | : root_layer_(FakeContentLayer::Create(&client_)), | 
|  | child_layer1_(FakeContentLayer::Create(&client_)), | 
|  | child_layer2_(FakeContentLayer::Create(&client_)), | 
|  | num_commits_(0) {} | 
|  |  | 
|  | void BeginTest() override { | 
|  | layer_tree_host()->SetViewportSize(gfx::Size(100, 100)); | 
|  | root_layer_->SetBounds(gfx::Size(100, 100)); | 
|  | child_layer1_->SetBounds(gfx::Size(100, 100)); | 
|  | child_layer2_->SetBounds(gfx::Size(100, 100)); | 
|  | root_layer_->AddChild(child_layer1_); | 
|  | root_layer_->AddChild(child_layer2_); | 
|  | layer_tree_host()->SetRootLayer(root_layer_); | 
|  | PostSetNeedsCommitToMainThread(); | 
|  | } | 
|  |  | 
|  | void DidSetVisibleOnImplTree(LayerTreeHostImpl* host_impl, | 
|  | bool visible) override { | 
|  | if (visible) { | 
|  | // One backing should remain unevicted. | 
|  | EXPECT_EQ( | 
|  | 100u * 100u * 4u * 1u, | 
|  | layer_tree_host()->contents_texture_manager()->MemoryUseBytes()); | 
|  | } else { | 
|  | EXPECT_EQ( | 
|  | 0u, layer_tree_host()->contents_texture_manager()->MemoryUseBytes()); | 
|  | } | 
|  |  | 
|  | // Make sure that contents textures are marked as having been | 
|  | // purged. | 
|  | EXPECT_TRUE(host_impl->active_tree()->ContentsTexturesPurged()); | 
|  | // End the test in this state. | 
|  | EndTest(); | 
|  | } | 
|  |  | 
|  | void DidActivateTreeOnThread(LayerTreeHostImpl* host_impl) override { | 
|  | ++num_commits_; | 
|  | switch (num_commits_) { | 
|  | case 1: | 
|  | // All three backings should have memory. | 
|  | EXPECT_EQ( | 
|  | 100u * 100u * 4u * 3u, | 
|  | layer_tree_host()->contents_texture_manager()->MemoryUseBytes()); | 
|  | // Set a new policy that will kick out 1 of the 3 resources. | 
|  | // Because a resource was evicted, a commit will be kicked off. | 
|  | host_impl->SetMemoryPolicy( | 
|  | ManagedMemoryPolicy(100 * 100 * 4 * 2, | 
|  | gpu::MemoryAllocation::CUTOFF_ALLOW_EVERYTHING, | 
|  | 1000)); | 
|  | break; | 
|  | case 2: | 
|  | // Only two backings should have memory. | 
|  | EXPECT_EQ( | 
|  | 100u * 100u * 4u * 2u, | 
|  | layer_tree_host()->contents_texture_manager()->MemoryUseBytes()); | 
|  | // Become backgrounded, which will cause 1 more resource to be | 
|  | // evicted. | 
|  | PostSetVisibleToMainThread(false); | 
|  | break; | 
|  | default: | 
|  | // No further commits should happen because this is not visible | 
|  | // anymore. | 
|  | NOTREACHED(); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | void AfterTest() override {} | 
|  |  | 
|  | private: | 
|  | FakeContentLayerClient client_; | 
|  | scoped_refptr<FakeContentLayer> root_layer_; | 
|  | scoped_refptr<FakeContentLayer> child_layer1_; | 
|  | scoped_refptr<FakeContentLayer> child_layer2_; | 
|  | int num_commits_; | 
|  | }; | 
|  |  | 
|  | SINGLE_AND_MULTI_THREAD_NOIMPL_TEST_F( | 
|  | LayerTreeHostTestShutdownWithOnlySomeResourcesEvicted); | 
|  |  | 
|  | class LayerTreeHostTestLCDChange : public LayerTreeHostTest { | 
|  | public: | 
|  | class PaintClient : public FakeContentLayerClient { | 
|  | public: | 
|  | PaintClient() : paint_count_(0) {} | 
|  |  | 
|  | int paint_count() const { return paint_count_; } | 
|  |  | 
|  | void PaintContents( | 
|  | SkCanvas* canvas, | 
|  | const gfx::Rect& clip, | 
|  | ContentLayerClient::GraphicsContextStatus gc_status) override { | 
|  | FakeContentLayerClient::PaintContents(canvas, clip, gc_status); | 
|  | ++paint_count_; | 
|  | } | 
|  |  | 
|  | scoped_refptr<DisplayItemList> PaintContentsToDisplayList( | 
|  | const gfx::Rect& clip, | 
|  | GraphicsContextStatus gc_status) override { | 
|  | NOTIMPLEMENTED(); | 
|  | return DisplayItemList::Create(); | 
|  | } | 
|  |  | 
|  | bool FillsBoundsCompletely() const override { return false; } | 
|  |  | 
|  | private: | 
|  | int paint_count_; | 
|  | }; | 
|  |  | 
|  | void SetupTree() override { | 
|  | num_tiles_rastered_ = 0; | 
|  |  | 
|  | scoped_refptr<Layer> root_layer; | 
|  | if (layer_tree_host()->settings().impl_side_painting) | 
|  | root_layer = PictureLayer::Create(&client_); | 
|  | else | 
|  | root_layer = ContentLayer::Create(&client_); | 
|  | client_.set_fill_with_nonsolid_color(true); | 
|  | root_layer->SetIsDrawable(true); | 
|  | root_layer->SetBounds(gfx::Size(10, 10)); | 
|  | root_layer->SetContentsOpaque(true); | 
|  |  | 
|  | layer_tree_host()->SetRootLayer(root_layer); | 
|  |  | 
|  | // The expecations are based on the assumption that the default | 
|  | // LCD settings are: | 
|  | EXPECT_TRUE(layer_tree_host()->settings().can_use_lcd_text); | 
|  | EXPECT_FALSE(root_layer->can_use_lcd_text()); | 
|  |  | 
|  | LayerTreeHostTest::SetupTree(); | 
|  | } | 
|  |  | 
|  | void BeginTest() override { PostSetNeedsCommitToMainThread(); } | 
|  |  | 
|  | void DidCommitAndDrawFrame() override { | 
|  | switch (layer_tree_host()->source_frame_number()) { | 
|  | case 1: | 
|  | // The first update consists of a paint of the whole layer. | 
|  | EXPECT_EQ(1, client_.paint_count()); | 
|  | // LCD text must have been enabled on the layer. | 
|  | EXPECT_TRUE(layer_tree_host()->root_layer()->can_use_lcd_text()); | 
|  | PostSetNeedsCommitToMainThread(); | 
|  | break; | 
|  | case 2: | 
|  | // Since nothing changed on layer, there should be no paint. | 
|  | EXPECT_EQ(1, client_.paint_count()); | 
|  | // LCD text must not have changed. | 
|  | EXPECT_TRUE(layer_tree_host()->root_layer()->can_use_lcd_text()); | 
|  | // Change layer opacity that should trigger lcd change. | 
|  | layer_tree_host()->root_layer()->SetOpacity(.5f); | 
|  | break; | 
|  | case 3: | 
|  | // LCD text doesn't require re-recording, so no painting should occur. | 
|  | EXPECT_EQ(1, client_.paint_count()); | 
|  | // LCD text must have been disabled on the layer due to opacity. | 
|  | EXPECT_FALSE(layer_tree_host()->root_layer()->can_use_lcd_text()); | 
|  | // Change layer opacity that should not trigger lcd change. | 
|  | layer_tree_host()->root_layer()->SetOpacity(1.f); | 
|  | break; | 
|  | case 4: | 
|  | // LCD text doesn't require re-recording, so no painting should occur. | 
|  | EXPECT_EQ(1, client_.paint_count()); | 
|  | // Even though LCD text could be allowed. | 
|  | EXPECT_TRUE(layer_tree_host()->root_layer()->can_use_lcd_text()); | 
|  | EndTest(); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | void NotifyTileStateChangedOnThread(LayerTreeHostImpl* host_impl, | 
|  | const Tile* tile) override { | 
|  | ++num_tiles_rastered_; | 
|  | } | 
|  |  | 
|  | void DrawLayersOnThread(LayerTreeHostImpl* host_impl) override { | 
|  | switch (host_impl->active_tree()->source_frame_number()) { | 
|  | case 0: | 
|  | // The first draw. | 
|  | EXPECT_EQ(1, num_tiles_rastered_); | 
|  | break; | 
|  | case 1: | 
|  | // Nothing changed on the layer. | 
|  | EXPECT_EQ(1, num_tiles_rastered_); | 
|  | break; | 
|  | case 2: | 
|  | // LCD text was disabled, it should be re-rastered with LCD text off. | 
|  | EXPECT_EQ(2, num_tiles_rastered_); | 
|  | break; | 
|  | case 3: | 
|  | // LCD text was enabled but it's sticky and stays off. | 
|  | EXPECT_EQ(2, num_tiles_rastered_); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | void AfterTest() override {} | 
|  |  | 
|  | private: | 
|  | PaintClient client_; | 
|  | int num_tiles_rastered_; | 
|  | }; | 
|  |  | 
|  | SINGLE_AND_MULTI_THREAD_IMPL_TEST_F(LayerTreeHostTestLCDChange); | 
|  |  | 
|  | // Verify that the BeginFrame notification is used to initiate rendering. | 
|  | class LayerTreeHostTestBeginFrameNotification : public LayerTreeHostTest { | 
|  | public: | 
|  | void InitializeSettings(LayerTreeSettings* settings) override { | 
|  | settings->use_external_begin_frame_source = true; | 
|  | } | 
|  |  | 
|  | void BeginTest() override { | 
|  | // This will trigger a SetNeedsBeginFrame which will trigger a | 
|  | // BeginFrame. | 
|  | PostSetNeedsCommitToMainThread(); | 
|  | } | 
|  |  | 
|  | DrawResult PrepareToDrawOnThread(LayerTreeHostImpl* host_impl, | 
|  | LayerTreeHostImpl::FrameData* frame, | 
|  | DrawResult draw_result) override { | 
|  | EndTest(); | 
|  | return DRAW_SUCCESS; | 
|  | } | 
|  |  | 
|  | void AfterTest() override {} | 
|  |  | 
|  | private: | 
|  | base::TimeTicks frame_time_; | 
|  | }; | 
|  |  | 
|  | MULTI_THREAD_TEST_F(LayerTreeHostTestBeginFrameNotification); | 
|  |  | 
|  | class LayerTreeHostTestBeginFrameNotificationShutdownWhileEnabled | 
|  | : public LayerTreeHostTest { | 
|  | public: | 
|  | void InitializeSettings(LayerTreeSettings* settings) override { | 
|  | settings->use_external_begin_frame_source = true; | 
|  | settings->using_synchronous_renderer_compositor = true; | 
|  | } | 
|  |  | 
|  | void BeginTest() override { PostSetNeedsCommitToMainThread(); } | 
|  |  | 
|  | void CommitCompleteOnThread(LayerTreeHostImpl* host_impl) override { | 
|  | // The BeginFrame notification is turned off now but will get enabled | 
|  | // once we return. End test while it's enabled. | 
|  | ImplThreadTaskRunner()->PostTask( | 
|  | FROM_HERE, | 
|  | base::Bind(&LayerTreeHostTestBeginFrameNotification::EndTest, | 
|  | base::Unretained(this))); | 
|  | } | 
|  |  | 
|  | void AfterTest() override {} | 
|  | }; | 
|  |  | 
|  | MULTI_THREAD_TEST_F( | 
|  | LayerTreeHostTestBeginFrameNotificationShutdownWhileEnabled); | 
|  |  | 
|  | class LayerTreeHostTestAbortedCommitDoesntStall : public LayerTreeHostTest { | 
|  | protected: | 
|  | LayerTreeHostTestAbortedCommitDoesntStall() | 
|  | : commit_count_(0), commit_abort_count_(0), commit_complete_count_(0) {} | 
|  |  | 
|  | void InitializeSettings(LayerTreeSettings* settings) override { | 
|  | settings->use_external_begin_frame_source = true; | 
|  | } | 
|  |  | 
|  | void BeginTest() override { PostSetNeedsCommitToMainThread(); } | 
|  |  | 
|  | void DidCommit() override { | 
|  | commit_count_++; | 
|  | if (commit_count_ == 4) { | 
|  | // After two aborted commits, request a real commit now to make sure a | 
|  | // real commit following an aborted commit will still complete and | 
|  | // end the test even when the Impl thread is idle. | 
|  | layer_tree_host()->SetNeedsCommit(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void BeginMainFrameAbortedOnThread(LayerTreeHostImpl* host_impl, | 
|  | CommitEarlyOutReason reason) override { | 
|  | commit_abort_count_++; | 
|  | // Initiate another abortable commit. | 
|  | host_impl->SetNeedsCommit(); | 
|  | } | 
|  |  | 
|  | void CommitCompleteOnThread(LayerTreeHostImpl* host_impl) override { | 
|  | commit_complete_count_++; | 
|  | if (commit_complete_count_ == 1) { | 
|  | // Initiate an abortable commit after the first commit. | 
|  | host_impl->SetNeedsCommit(); | 
|  | } else { | 
|  | EndTest(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void AfterTest() override { | 
|  | EXPECT_EQ(commit_count_, 5); | 
|  | EXPECT_EQ(commit_abort_count_, 3); | 
|  | EXPECT_EQ(commit_complete_count_, 2); | 
|  | } | 
|  |  | 
|  | int commit_count_; | 
|  | int commit_abort_count_; | 
|  | int commit_complete_count_; | 
|  | }; | 
|  |  | 
|  | class LayerTreeHostTestAbortedCommitDoesntStallSynchronousCompositor | 
|  | : public LayerTreeHostTestAbortedCommitDoesntStall { | 
|  | void InitializeSettings(LayerTreeSettings* settings) override { | 
|  | LayerTreeHostTestAbortedCommitDoesntStall::InitializeSettings(settings); | 
|  | settings->using_synchronous_renderer_compositor = true; | 
|  | } | 
|  | }; | 
|  |  | 
|  | MULTI_THREAD_TEST_F( | 
|  | LayerTreeHostTestAbortedCommitDoesntStallSynchronousCompositor); | 
|  |  | 
|  | class LayerTreeHostTestAbortedCommitDoesntStallDisabledVsync | 
|  | : public LayerTreeHostTestAbortedCommitDoesntStall { | 
|  | void InitializeSettings(LayerTreeSettings* settings) override { | 
|  | LayerTreeHostTestAbortedCommitDoesntStall::InitializeSettings(settings); | 
|  | settings->throttle_frame_production = false; | 
|  | } | 
|  | }; | 
|  |  | 
|  | MULTI_THREAD_TEST_F(LayerTreeHostTestAbortedCommitDoesntStallDisabledVsync); | 
|  |  | 
|  | class LayerTreeHostTestUninvertibleTransformDoesNotBlockActivation | 
|  | : public LayerTreeHostTest { | 
|  | protected: | 
|  | void InitializeSettings(LayerTreeSettings* settings) override { | 
|  | settings->impl_side_painting = true; | 
|  | } | 
|  |  | 
|  | void SetupTree() override { | 
|  | LayerTreeHostTest::SetupTree(); | 
|  |  | 
|  | scoped_refptr<Layer> layer = PictureLayer::Create(&client_); | 
|  | layer->SetTransform(gfx::Transform(0.0, 0.0, 0.0, 0.0, 0.0, 0.0)); | 
|  | layer->SetBounds(gfx::Size(10, 10)); | 
|  | layer_tree_host()->root_layer()->AddChild(layer); | 
|  | } | 
|  |  | 
|  | void BeginTest() override { PostSetNeedsCommitToMainThread(); } | 
|  |  | 
|  | void DidActivateTreeOnThread(LayerTreeHostImpl* host_impl) override { | 
|  | EndTest(); | 
|  | } | 
|  |  | 
|  | void AfterTest() override {} | 
|  |  | 
|  | FakeContentLayerClient client_; | 
|  | }; | 
|  |  | 
|  | MULTI_THREAD_TEST_F( | 
|  | LayerTreeHostTestUninvertibleTransformDoesNotBlockActivation); | 
|  |  | 
|  | class LayerTreeHostTestChangeLayerPropertiesInPaintContents | 
|  | : public LayerTreeHostTest { | 
|  | public: | 
|  | class SetBoundsClient : public ContentLayerClient { | 
|  | public: | 
|  | SetBoundsClient() : layer_(0) {} | 
|  |  | 
|  | void set_layer(Layer* layer) { layer_ = layer; } | 
|  |  | 
|  | void PaintContents( | 
|  | SkCanvas* canvas, | 
|  | const gfx::Rect& clip, | 
|  | ContentLayerClient::GraphicsContextStatus gc_status) override { | 
|  | layer_->SetBounds(gfx::Size(2, 2)); | 
|  | } | 
|  |  | 
|  | scoped_refptr<DisplayItemList> PaintContentsToDisplayList( | 
|  | const gfx::Rect& clip, | 
|  | GraphicsContextStatus gc_status) override { | 
|  | NOTIMPLEMENTED(); | 
|  | return DisplayItemList::Create(); | 
|  | } | 
|  |  | 
|  | bool FillsBoundsCompletely() const override { return false; } | 
|  |  | 
|  | private: | 
|  | Layer* layer_; | 
|  | }; | 
|  |  | 
|  | LayerTreeHostTestChangeLayerPropertiesInPaintContents() : num_commits_(0) {} | 
|  |  | 
|  | void SetupTree() override { | 
|  | if (layer_tree_host()->settings().impl_side_painting) { | 
|  | scoped_refptr<PictureLayer> root_layer = PictureLayer::Create(&client_); | 
|  | layer_tree_host()->SetRootLayer(root_layer); | 
|  | } else { | 
|  | scoped_refptr<ContentLayer> root_layer = ContentLayer::Create(&client_); | 
|  | layer_tree_host()->SetRootLayer(root_layer); | 
|  | } | 
|  | Layer* root_layer = layer_tree_host()->root_layer(); | 
|  | root_layer->SetIsDrawable(true); | 
|  | root_layer->SetBounds(gfx::Size(1, 1)); | 
|  |  | 
|  | client_.set_layer(root_layer); | 
|  |  | 
|  | LayerTreeHostTest::SetupTree(); | 
|  | } | 
|  |  | 
|  | void BeginTest() override { PostSetNeedsCommitToMainThread(); } | 
|  | void AfterTest() override {} | 
|  |  | 
|  | void DidActivateTreeOnThread(LayerTreeHostImpl* host_impl) override { | 
|  | num_commits_++; | 
|  | if (num_commits_ == 1) { | 
|  | LayerImpl* root_layer = host_impl->active_tree()->root_layer(); | 
|  | EXPECT_EQ(gfx::Size(1, 1), root_layer->bounds()); | 
|  | } else { | 
|  | LayerImpl* root_layer = host_impl->active_tree()->root_layer(); | 
|  | EXPECT_EQ(gfx::Size(2, 2), root_layer->bounds()); | 
|  | EndTest(); | 
|  | } | 
|  | } | 
|  |  | 
|  | private: | 
|  | SetBoundsClient client_; | 
|  | int num_commits_; | 
|  | }; | 
|  |  | 
|  | SINGLE_AND_MULTI_THREAD_TEST_F( | 
|  | LayerTreeHostTestChangeLayerPropertiesInPaintContents); | 
|  |  | 
|  | class MockIOSurfaceWebGraphicsContext3D : public TestWebGraphicsContext3D { | 
|  | public: | 
|  | MockIOSurfaceWebGraphicsContext3D() { | 
|  | test_capabilities_.gpu.iosurface = true; | 
|  | test_capabilities_.gpu.texture_rectangle = true; | 
|  | } | 
|  |  | 
|  | GLuint createTexture() override { return 1; } | 
|  | MOCK_METHOD1(activeTexture, void(GLenum texture)); | 
|  | MOCK_METHOD2(bindTexture, void(GLenum target, | 
|  | GLuint texture_id)); | 
|  | MOCK_METHOD3(texParameteri, void(GLenum target, | 
|  | GLenum pname, | 
|  | GLint param)); | 
|  | MOCK_METHOD5(texImageIOSurface2DCHROMIUM, void(GLenum target, | 
|  | GLint width, | 
|  | GLint height, | 
|  | GLuint ioSurfaceId, | 
|  | GLuint plane)); | 
|  | MOCK_METHOD4(drawElements, void(GLenum mode, | 
|  | GLsizei count, | 
|  | GLenum type, | 
|  | GLintptr offset)); | 
|  | MOCK_METHOD1(deleteTexture, void(GLenum texture)); | 
|  | MOCK_METHOD2(produceTextureCHROMIUM, | 
|  | void(GLenum target, const GLbyte* mailbox)); | 
|  | }; | 
|  |  | 
|  | class LayerTreeHostTestIOSurfaceDrawing : public LayerTreeHostTest { | 
|  | protected: | 
|  | scoped_ptr<FakeOutputSurface> CreateFakeOutputSurface() override { | 
|  | scoped_ptr<MockIOSurfaceWebGraphicsContext3D> mock_context_owned( | 
|  | new MockIOSurfaceWebGraphicsContext3D); | 
|  | mock_context_ = mock_context_owned.get(); | 
|  |  | 
|  | if (delegating_renderer()) | 
|  | return FakeOutputSurface::CreateDelegating3d(mock_context_owned.Pass()); | 
|  | else | 
|  | return FakeOutputSurface::Create3d(mock_context_owned.Pass()); | 
|  | } | 
|  |  | 
|  | void SetupTree() override { | 
|  | LayerTreeHostTest::SetupTree(); | 
|  |  | 
|  | layer_tree_host()->root_layer()->SetIsDrawable(false); | 
|  |  | 
|  | io_surface_id_ = 9; | 
|  | io_surface_size_ = gfx::Size(6, 7); | 
|  |  | 
|  | scoped_refptr<IOSurfaceLayer> io_surface_layer = IOSurfaceLayer::Create(); | 
|  | io_surface_layer->SetBounds(gfx::Size(10, 10)); | 
|  | io_surface_layer->SetIsDrawable(true); | 
|  | io_surface_layer->SetContentsOpaque(true); | 
|  | io_surface_layer->SetIOSurfaceProperties(io_surface_id_, io_surface_size_); | 
|  | layer_tree_host()->root_layer()->AddChild(io_surface_layer); | 
|  | } | 
|  |  | 
|  | void BeginTest() override { PostSetNeedsCommitToMainThread(); } | 
|  |  | 
|  | void DidActivateTreeOnThread(LayerTreeHostImpl* host_impl) override { | 
|  | EXPECT_EQ(0u, host_impl->resource_provider()->num_resources()); | 
|  | // In WillDraw, the IOSurfaceLayer sets up the io surface texture. | 
|  |  | 
|  | EXPECT_CALL(*mock_context_, activeTexture(_)).Times(0); | 
|  | EXPECT_CALL(*mock_context_, bindTexture(GL_TEXTURE_RECTANGLE_ARB, 1)) | 
|  | .Times(AtLeast(1)); | 
|  | EXPECT_CALL(*mock_context_, | 
|  | texParameteri( | 
|  | GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_LINEAR)) | 
|  | .Times(1); | 
|  | EXPECT_CALL(*mock_context_, | 
|  | texParameteri( | 
|  | GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_LINEAR)) | 
|  | .Times(1); | 
|  | EXPECT_CALL(*mock_context_, | 
|  | texParameteri(GL_TEXTURE_RECTANGLE_ARB, | 
|  | GL_TEXTURE_POOL_CHROMIUM, | 
|  | GL_TEXTURE_POOL_UNMANAGED_CHROMIUM)).Times(1); | 
|  | EXPECT_CALL(*mock_context_, | 
|  | texParameteri(GL_TEXTURE_RECTANGLE_ARB, | 
|  | GL_TEXTURE_WRAP_S, | 
|  | GL_CLAMP_TO_EDGE)).Times(1); | 
|  | EXPECT_CALL(*mock_context_, | 
|  | texParameteri(GL_TEXTURE_RECTANGLE_ARB, | 
|  | GL_TEXTURE_WRAP_T, | 
|  | GL_CLAMP_TO_EDGE)).Times(1); | 
|  |  | 
|  | EXPECT_CALL(*mock_context_, | 
|  | texImageIOSurface2DCHROMIUM(GL_TEXTURE_RECTANGLE_ARB, | 
|  | io_surface_size_.width(), | 
|  | io_surface_size_.height(), | 
|  | io_surface_id_, | 
|  | 0)).Times(1); | 
|  |  | 
|  | EXPECT_CALL(*mock_context_, bindTexture(_, 0)).Times(AnyNumber()); | 
|  | } | 
|  |  | 
|  | DrawResult PrepareToDrawOnThread(LayerTreeHostImpl* host_impl, | 
|  | LayerTreeHostImpl::FrameData* frame, | 
|  | DrawResult draw_result) override { | 
|  | Mock::VerifyAndClearExpectations(&mock_context_); | 
|  | ResourceProvider* resource_provider = host_impl->resource_provider(); | 
|  | EXPECT_EQ(1u, resource_provider->num_resources()); | 
|  | CHECK_EQ(1u, frame->render_passes.size()); | 
|  | CHECK_LE(1u, frame->render_passes[0]->quad_list.size()); | 
|  | const DrawQuad* quad = frame->render_passes[0]->quad_list.front(); | 
|  | CHECK_EQ(DrawQuad::IO_SURFACE_CONTENT, quad->material); | 
|  | const IOSurfaceDrawQuad* io_surface_draw_quad = | 
|  | IOSurfaceDrawQuad::MaterialCast(quad); | 
|  | EXPECT_EQ(io_surface_size_, io_surface_draw_quad->io_surface_size); | 
|  | EXPECT_NE(0u, io_surface_draw_quad->io_surface_resource_id); | 
|  | EXPECT_EQ(static_cast<GLenum>(GL_TEXTURE_RECTANGLE_ARB), | 
|  | resource_provider->TargetForTesting( | 
|  | io_surface_draw_quad->io_surface_resource_id)); | 
|  |  | 
|  | EXPECT_CALL(*mock_context_, bindTexture(GL_TEXTURE_RECTANGLE_ARB, 1)) | 
|  | .Times(1); | 
|  | if (delegating_renderer()) { | 
|  | // The io surface layer's resource should be sent to the parent. | 
|  | EXPECT_CALL(*mock_context_, | 
|  | produceTextureCHROMIUM(GL_TEXTURE_RECTANGLE_ARB, _)).Times(1); | 
|  | } else { | 
|  | // The io surface layer's texture is drawn. | 
|  | EXPECT_CALL(*mock_context_, activeTexture(GL_TEXTURE0)).Times(AtLeast(1)); | 
|  | EXPECT_CALL(*mock_context_, drawElements(GL_TRIANGLES, 6, _, _)) | 
|  | .Times(AtLeast(1)); | 
|  | } | 
|  |  | 
|  | return draw_result; | 
|  | } | 
|  |  | 
|  | void DrawLayersOnThread(LayerTreeHostImpl* host_impl) override { | 
|  | Mock::VerifyAndClearExpectations(&mock_context_); | 
|  |  | 
|  | EXPECT_CALL(*mock_context_, deleteTexture(1)).Times(AtLeast(1)); | 
|  | EndTest(); | 
|  | } | 
|  |  | 
|  | void AfterTest() override {} | 
|  |  | 
|  | int io_surface_id_; | 
|  | MockIOSurfaceWebGraphicsContext3D* mock_context_; | 
|  | gfx::Size io_surface_size_; | 
|  | }; | 
|  |  | 
|  | SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestIOSurfaceDrawing); | 
|  |  | 
|  | class LayerTreeHostTestNumFramesPending : public LayerTreeHostTest { | 
|  | public: | 
|  | void BeginTest() override { | 
|  | frame_ = 0; | 
|  | PostSetNeedsCommitToMainThread(); | 
|  | } | 
|  |  | 
|  | // Round 1: commit + draw | 
|  | // Round 2: commit only (no draw/swap) | 
|  | // Round 3: draw only (no commit) | 
|  |  | 
|  | void DidCommit() override { | 
|  | int commit = layer_tree_host()->source_frame_number(); | 
|  | switch (commit) { | 
|  | case 2: | 
|  | // Round 2 done. | 
|  | EXPECT_EQ(1, frame_); | 
|  | layer_tree_host()->SetNeedsRedraw(); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | void DidCompleteSwapBuffers() override { | 
|  | int commit = layer_tree_host()->source_frame_number(); | 
|  | ++frame_; | 
|  | switch (frame_) { | 
|  | case 1: | 
|  | // Round 1 done. | 
|  | EXPECT_EQ(1, commit); | 
|  | layer_tree_host()->SetNeedsCommit(); | 
|  | break; | 
|  | case 2: | 
|  | // Round 3 done. | 
|  | EXPECT_EQ(2, commit); | 
|  | EndTest(); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | void AfterTest() override {} | 
|  |  | 
|  | protected: | 
|  | int frame_; | 
|  | }; | 
|  |  | 
|  | // Flaky on all platforms: http://crbug.com/327498 | 
|  | TEST_F(LayerTreeHostTestNumFramesPending, DISABLED_DelegatingRenderer) { | 
|  | RunTest(true, true, true); | 
|  | } | 
|  |  | 
|  | TEST_F(LayerTreeHostTestNumFramesPending, DISABLED_GLRenderer) { | 
|  | RunTest(true, false, true); | 
|  | } | 
|  |  | 
|  | class LayerTreeHostTestDeferredInitialize : public LayerTreeHostTest { | 
|  | public: | 
|  | void InitializeSettings(LayerTreeSettings* settings) override { | 
|  | // PictureLayer can only be used with impl side painting enabled. | 
|  | settings->impl_side_painting = true; | 
|  | } | 
|  |  | 
|  | void SetupTree() override { | 
|  | layer_ = FakePictureLayer::Create(&client_); | 
|  | // Force commits to not be aborted so new frames get drawn, otherwise | 
|  | // the renderer gets deferred initialized but nothing new needs drawing. | 
|  | layer_->set_always_update_resources(true); | 
|  | layer_tree_host()->SetRootLayer(layer_); | 
|  | LayerTreeHostTest::SetupTree(); | 
|  | } | 
|  |  | 
|  | void BeginTest() override { | 
|  | did_initialize_gl_ = false; | 
|  | did_release_gl_ = false; | 
|  | last_source_frame_number_drawn_ = -1;  // Never drawn. | 
|  | PostSetNeedsCommitToMainThread(); | 
|  | } | 
|  |  | 
|  | scoped_ptr<FakeOutputSurface> CreateFakeOutputSurface() override { | 
|  | scoped_ptr<TestWebGraphicsContext3D> context3d( | 
|  | TestWebGraphicsContext3D::Create()); | 
|  |  | 
|  | return FakeOutputSurface::CreateDeferredGL( | 
|  | scoped_ptr<SoftwareOutputDevice>(new SoftwareOutputDevice), | 
|  | delegating_renderer()); | 
|  | } | 
|  |  | 
|  | void DrawLayersOnThread(LayerTreeHostImpl* host_impl) override { | 
|  | ASSERT_TRUE(host_impl->RootLayer()); | 
|  | FakePictureLayerImpl* layer_impl = | 
|  | static_cast<FakePictureLayerImpl*>(host_impl->RootLayer()); | 
|  |  | 
|  | // The same frame can be draw multiple times if new visible tiles are | 
|  | // rasterized. But we want to make sure we only post DeferredInitialize | 
|  | // and ReleaseGL once, so early out if the same frame is drawn again. | 
|  | if (last_source_frame_number_drawn_ == | 
|  | host_impl->active_tree()->source_frame_number()) | 
|  | return; | 
|  |  | 
|  | last_source_frame_number_drawn_ = | 
|  | host_impl->active_tree()->source_frame_number(); | 
|  |  | 
|  | if (!did_initialize_gl_) { | 
|  | EXPECT_LE(1u, layer_impl->append_quads_count()); | 
|  | ImplThreadTaskRunner()->PostTask( | 
|  | FROM_HERE, | 
|  | base::Bind( | 
|  | &LayerTreeHostTestDeferredInitialize::DeferredInitializeAndRedraw, | 
|  | base::Unretained(this), | 
|  | base::Unretained(host_impl))); | 
|  | } else if (did_initialize_gl_ && !did_release_gl_) { | 
|  | EXPECT_LE(2u, layer_impl->append_quads_count()); | 
|  | ImplThreadTaskRunner()->PostTask( | 
|  | FROM_HERE, | 
|  | base::Bind(&LayerTreeHostTestDeferredInitialize::ReleaseGLAndRedraw, | 
|  | base::Unretained(this), | 
|  | base::Unretained(host_impl))); | 
|  | } else if (did_initialize_gl_ && did_release_gl_) { | 
|  | EXPECT_LE(3u, layer_impl->append_quads_count()); | 
|  | EndTest(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void DeferredInitializeAndRedraw(LayerTreeHostImpl* host_impl) { | 
|  | EXPECT_FALSE(did_initialize_gl_); | 
|  | // SetAndInitializeContext3D calls SetNeedsCommit. | 
|  | FakeOutputSurface* fake_output_surface = | 
|  | static_cast<FakeOutputSurface*>(host_impl->output_surface()); | 
|  | scoped_refptr<TestContextProvider> context_provider = | 
|  | TestContextProvider::Create();  // Not bound to thread. | 
|  | EXPECT_TRUE( | 
|  | fake_output_surface->InitializeAndSetContext3d(context_provider)); | 
|  | did_initialize_gl_ = true; | 
|  | } | 
|  |  | 
|  | void ReleaseGLAndRedraw(LayerTreeHostImpl* host_impl) { | 
|  | EXPECT_TRUE(did_initialize_gl_); | 
|  | EXPECT_FALSE(did_release_gl_); | 
|  | // ReleaseGL calls SetNeedsCommit. | 
|  | static_cast<FakeOutputSurface*>(host_impl->output_surface())->ReleaseGL(); | 
|  | did_release_gl_ = true; | 
|  | } | 
|  |  | 
|  | void SwapBuffersOnThread(LayerTreeHostImpl* host_impl, bool result) override { | 
|  | ASSERT_TRUE(result); | 
|  | DelegatedFrameData* delegated_frame_data = | 
|  | output_surface()->last_sent_frame().delegated_frame_data.get(); | 
|  | if (!delegated_frame_data) | 
|  | return; | 
|  |  | 
|  | // Return all resources immediately. | 
|  | TransferableResourceArray resources_to_return = | 
|  | output_surface()->resources_held_by_parent(); | 
|  |  | 
|  | CompositorFrameAck ack; | 
|  | for (size_t i = 0; i < resources_to_return.size(); ++i) | 
|  | output_surface()->ReturnResource(resources_to_return[i].id, &ack); | 
|  | host_impl->ReclaimResources(&ack); | 
|  | } | 
|  |  | 
|  | void AfterTest() override { | 
|  | EXPECT_TRUE(did_initialize_gl_); | 
|  | EXPECT_TRUE(did_release_gl_); | 
|  | } | 
|  |  | 
|  | private: | 
|  | FakeContentLayerClient client_; | 
|  | scoped_refptr<FakePictureLayer> layer_; | 
|  | bool did_initialize_gl_; | 
|  | bool did_release_gl_; | 
|  | int last_source_frame_number_drawn_; | 
|  | }; | 
|  |  | 
|  | MULTI_THREAD_TEST_F(LayerTreeHostTestDeferredInitialize); | 
|  |  | 
|  | class LayerTreeHostTestDeferredInitializeWithGpuRasterization | 
|  | : public LayerTreeHostTestDeferredInitialize { | 
|  | void InitializeSettings(LayerTreeSettings* settings) override { | 
|  | // PictureLayer can only be used with impl side painting enabled. | 
|  | settings->impl_side_painting = true; | 
|  | settings->gpu_rasterization_enabled = true; | 
|  | settings->gpu_rasterization_forced = true; | 
|  | } | 
|  | }; | 
|  |  | 
|  | MULTI_THREAD_TEST_F(LayerTreeHostTestDeferredInitializeWithGpuRasterization); | 
|  |  | 
|  | // Test for UI Resource management. | 
|  | class LayerTreeHostTestUIResource : public LayerTreeHostTest { | 
|  | public: | 
|  | LayerTreeHostTestUIResource() : num_ui_resources_(0) {} | 
|  |  | 
|  | void InitializeSettings(LayerTreeSettings* settings) override { | 
|  | settings->renderer_settings.texture_id_allocation_chunk_size = 1; | 
|  | } | 
|  |  | 
|  | void BeginTest() override { PostSetNeedsCommitToMainThread(); } | 
|  |  | 
|  | void DidCommit() override { | 
|  | int frame = layer_tree_host()->source_frame_number(); | 
|  | switch (frame) { | 
|  | case 1: | 
|  | CreateResource(); | 
|  | CreateResource(); | 
|  | PostSetNeedsCommitToMainThread(); | 
|  | break; | 
|  | case 2: | 
|  | // Usually ScopedUIResource are deleted from the manager in their | 
|  | // destructor.  Here we just want to test that a direct call to | 
|  | // DeleteUIResource works. | 
|  | layer_tree_host()->DeleteUIResource(ui_resources_[0]->id()); | 
|  | PostSetNeedsCommitToMainThread(); | 
|  | break; | 
|  | case 3: | 
|  | // DeleteUIResource can be called with an invalid id. | 
|  | layer_tree_host()->DeleteUIResource(ui_resources_[0]->id()); | 
|  | PostSetNeedsCommitToMainThread(); | 
|  | break; | 
|  | case 4: | 
|  | CreateResource(); | 
|  | CreateResource(); | 
|  | PostSetNeedsCommitToMainThread(); | 
|  | break; | 
|  | case 5: | 
|  | ClearResources(); | 
|  | EndTest(); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | void PerformTest(LayerTreeHostImpl* impl) { | 
|  | TestWebGraphicsContext3D* context = TestContext(); | 
|  |  | 
|  | int frame = impl->active_tree()->source_frame_number(); | 
|  | switch (frame) { | 
|  | case 0: | 
|  | ASSERT_EQ(0u, context->NumTextures()); | 
|  | break; | 
|  | case 1: | 
|  | // Created two textures. | 
|  | ASSERT_EQ(2u, context->NumTextures()); | 
|  | break; | 
|  | case 2: | 
|  | // One texture left after one deletion. | 
|  | ASSERT_EQ(1u, context->NumTextures()); | 
|  | break; | 
|  | case 3: | 
|  | // Resource manager state should not change when delete is called on an | 
|  | // invalid id. | 
|  | ASSERT_EQ(1u, context->NumTextures()); | 
|  | break; | 
|  | case 4: | 
|  | // Creation after deletion: two more creates should total up to | 
|  | // three textures. | 
|  | ASSERT_EQ(3u, context->NumTextures()); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | void CommitCompleteOnThread(LayerTreeHostImpl* impl) override { | 
|  | if (!layer_tree_host()->settings().impl_side_painting) | 
|  | PerformTest(impl); | 
|  | } | 
|  |  | 
|  | void DidActivateTreeOnThread(LayerTreeHostImpl* impl) override { | 
|  | if (layer_tree_host()->settings().impl_side_painting) | 
|  | PerformTest(impl); | 
|  | } | 
|  |  | 
|  | void AfterTest() override {} | 
|  |  | 
|  | private: | 
|  | // Must clear all resources before exiting. | 
|  | void ClearResources() { | 
|  | for (int i = 0; i < num_ui_resources_; i++) | 
|  | ui_resources_[i] = nullptr; | 
|  | } | 
|  |  | 
|  | void CreateResource() { | 
|  | ui_resources_[num_ui_resources_++] = | 
|  | FakeScopedUIResource::Create(layer_tree_host()); | 
|  | } | 
|  |  | 
|  | scoped_ptr<FakeScopedUIResource> ui_resources_[5]; | 
|  | int num_ui_resources_; | 
|  | }; | 
|  |  | 
|  | MULTI_THREAD_TEST_F(LayerTreeHostTestUIResource); | 
|  |  | 
|  | class PushPropertiesCountingLayerImpl : public LayerImpl { | 
|  | public: | 
|  | static scoped_ptr<PushPropertiesCountingLayerImpl> Create( | 
|  | LayerTreeImpl* tree_impl, int id) { | 
|  | return make_scoped_ptr(new PushPropertiesCountingLayerImpl(tree_impl, id)); | 
|  | } | 
|  |  | 
|  | ~PushPropertiesCountingLayerImpl() override {} | 
|  |  | 
|  | void PushPropertiesTo(LayerImpl* layer) override { | 
|  | LayerImpl::PushPropertiesTo(layer); | 
|  | push_properties_count_++; | 
|  | // Push state to the active tree because we can only access it from there. | 
|  | static_cast<PushPropertiesCountingLayerImpl*>( | 
|  | layer)->push_properties_count_ = push_properties_count_; | 
|  | } | 
|  |  | 
|  | scoped_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl) override { | 
|  | return PushPropertiesCountingLayerImpl::Create(tree_impl, id()); | 
|  | } | 
|  |  | 
|  | size_t push_properties_count() const { return push_properties_count_; } | 
|  | void reset_push_properties_count() { push_properties_count_ = 0; } | 
|  |  | 
|  | private: | 
|  | size_t push_properties_count_; | 
|  |  | 
|  | PushPropertiesCountingLayerImpl(LayerTreeImpl* tree_impl, int id) | 
|  | : LayerImpl(tree_impl, id), | 
|  | push_properties_count_(0) { | 
|  | SetBounds(gfx::Size(1, 1)); | 
|  | } | 
|  | }; | 
|  |  | 
|  | class PushPropertiesCountingLayer : public Layer { | 
|  | public: | 
|  | static scoped_refptr<PushPropertiesCountingLayer> Create() { | 
|  | return new PushPropertiesCountingLayer(); | 
|  | } | 
|  |  | 
|  | void PushPropertiesTo(LayerImpl* layer) override { | 
|  | Layer::PushPropertiesTo(layer); | 
|  | push_properties_count_++; | 
|  | if (persist_needs_push_properties_) | 
|  | needs_push_properties_ = true; | 
|  | } | 
|  |  | 
|  | scoped_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl) override { | 
|  | return PushPropertiesCountingLayerImpl::Create(tree_impl, id()); | 
|  | } | 
|  |  | 
|  | void SetDrawsContent(bool draws_content) { SetIsDrawable(draws_content); } | 
|  |  | 
|  | size_t push_properties_count() const { return push_properties_count_; } | 
|  | void reset_push_properties_count() { push_properties_count_ = 0; } | 
|  |  | 
|  | void set_persist_needs_push_properties(bool persist) { | 
|  | persist_needs_push_properties_ = persist; | 
|  | } | 
|  |  | 
|  | private: | 
|  | PushPropertiesCountingLayer() | 
|  | : push_properties_count_(0), persist_needs_push_properties_(false) { | 
|  | SetBounds(gfx::Size(1, 1)); | 
|  | } | 
|  | ~PushPropertiesCountingLayer() override {} | 
|  |  | 
|  | size_t push_properties_count_; | 
|  | bool persist_needs_push_properties_; | 
|  | }; | 
|  |  | 
|  | class LayerTreeHostTestLayersPushProperties : public LayerTreeHostTest { | 
|  | protected: | 
|  | void BeginTest() override { | 
|  | num_commits_ = 0; | 
|  | expected_push_properties_root_ = 0; | 
|  | expected_push_properties_child_ = 0; | 
|  | expected_push_properties_grandchild_ = 0; | 
|  | expected_push_properties_child2_ = 0; | 
|  | expected_push_properties_other_root_ = 0; | 
|  | expected_push_properties_leaf_layer_ = 0; | 
|  | PostSetNeedsCommitToMainThread(); | 
|  | } | 
|  |  | 
|  | void SetupTree() override { | 
|  | root_ = PushPropertiesCountingLayer::Create(); | 
|  | root_->CreateRenderSurface(); | 
|  | child_ = PushPropertiesCountingLayer::Create(); | 
|  | child2_ = PushPropertiesCountingLayer::Create(); | 
|  | grandchild_ = PushPropertiesCountingLayer::Create(); | 
|  | leaf_always_pushing_layer_ = PushPropertiesCountingLayer::Create(); | 
|  | leaf_always_pushing_layer_->set_persist_needs_push_properties(true); | 
|  |  | 
|  | root_->AddChild(child_); | 
|  | root_->AddChild(child2_); | 
|  | child_->AddChild(grandchild_); | 
|  | child2_->AddChild(leaf_always_pushing_layer_); | 
|  |  | 
|  | other_root_ = PushPropertiesCountingLayer::Create(); | 
|  | other_root_->CreateRenderSurface(); | 
|  |  | 
|  | // Don't set the root layer here. | 
|  | LayerTreeHostTest::SetupTree(); | 
|  | } | 
|  |  | 
|  | void DidCommitAndDrawFrame() override { | 
|  | ++num_commits_; | 
|  |  | 
|  | EXPECT_EQ(expected_push_properties_root_, root_->push_properties_count()); | 
|  | EXPECT_EQ(expected_push_properties_child_, child_->push_properties_count()); | 
|  | EXPECT_EQ(expected_push_properties_grandchild_, | 
|  | grandchild_->push_properties_count()); | 
|  | EXPECT_EQ(expected_push_properties_child2_, | 
|  | child2_->push_properties_count()); | 
|  | EXPECT_EQ(expected_push_properties_other_root_, | 
|  | other_root_->push_properties_count()); | 
|  | EXPECT_EQ(expected_push_properties_leaf_layer_, | 
|  | leaf_always_pushing_layer_->push_properties_count()); | 
|  |  | 
|  | // The scrollbar layer always needs to be pushed. | 
|  | if (root_->layer_tree_host()) { | 
|  | EXPECT_TRUE(root_->descendant_needs_push_properties()); | 
|  | EXPECT_FALSE(root_->needs_push_properties()); | 
|  | } | 
|  | if (child2_->layer_tree_host()) { | 
|  | EXPECT_TRUE(child2_->descendant_needs_push_properties()); | 
|  | EXPECT_FALSE(child2_->needs_push_properties()); | 
|  | } | 
|  | if (leaf_always_pushing_layer_->layer_tree_host()) { | 
|  | EXPECT_FALSE( | 
|  | leaf_always_pushing_layer_->descendant_needs_push_properties()); | 
|  | EXPECT_TRUE(leaf_always_pushing_layer_->needs_push_properties()); | 
|  | } | 
|  |  | 
|  | // child_ and grandchild_ don't persist their need to push properties. | 
|  | if (child_->layer_tree_host()) { | 
|  | EXPECT_FALSE(child_->descendant_needs_push_properties()); | 
|  | EXPECT_FALSE(child_->needs_push_properties()); | 
|  | } | 
|  | if (grandchild_->layer_tree_host()) { | 
|  | EXPECT_FALSE(grandchild_->descendant_needs_push_properties()); | 
|  | EXPECT_FALSE(grandchild_->needs_push_properties()); | 
|  | } | 
|  |  | 
|  | if (other_root_->layer_tree_host()) { | 
|  | EXPECT_FALSE(other_root_->descendant_needs_push_properties()); | 
|  | EXPECT_FALSE(other_root_->needs_push_properties()); | 
|  | } | 
|  |  | 
|  | switch (num_commits_) { | 
|  | case 1: | 
|  | layer_tree_host()->SetRootLayer(root_); | 
|  | // Layers added to the tree get committed. | 
|  | ++expected_push_properties_root_; | 
|  | ++expected_push_properties_child_; | 
|  | ++expected_push_properties_grandchild_; | 
|  | ++expected_push_properties_child2_; | 
|  | break; | 
|  | case 2: | 
|  | layer_tree_host()->SetNeedsCommit(); | 
|  | // No layers need commit. | 
|  | break; | 
|  | case 3: | 
|  | layer_tree_host()->SetRootLayer(other_root_); | 
|  | // Layers added to the tree get committed. | 
|  | ++expected_push_properties_other_root_; | 
|  | break; | 
|  | case 4: | 
|  | layer_tree_host()->SetRootLayer(root_); | 
|  | // Layers added to the tree get committed. | 
|  | ++expected_push_properties_root_; | 
|  | ++expected_push_properties_child_; | 
|  | ++expected_push_properties_grandchild_; | 
|  | ++expected_push_properties_child2_; | 
|  | break; | 
|  | case 5: | 
|  | layer_tree_host()->SetNeedsCommit(); | 
|  | // No layers need commit. | 
|  | break; | 
|  | case 6: | 
|  | child_->RemoveFromParent(); | 
|  | // No layers need commit. | 
|  | break; | 
|  | case 7: | 
|  | root_->AddChild(child_); | 
|  | // Layers added to the tree get committed. | 
|  | ++expected_push_properties_child_; | 
|  | ++expected_push_properties_grandchild_; | 
|  | break; | 
|  | case 8: | 
|  | grandchild_->RemoveFromParent(); | 
|  | // No layers need commit. | 
|  | break; | 
|  | case 9: | 
|  | child_->AddChild(grandchild_); | 
|  | // Layers added to the tree get committed. | 
|  | ++expected_push_properties_grandchild_; | 
|  | break; | 
|  | case 10: | 
|  | layer_tree_host()->SetViewportSize(gfx::Size(20, 20)); | 
|  | // No layers need commit. | 
|  | break; | 
|  | case 11: | 
|  | layer_tree_host()->SetPageScaleFactorAndLimits(1.f, 0.8f, 1.1f); | 
|  | // No layers need commit. | 
|  | break; | 
|  | case 12: | 
|  | child_->SetPosition(gfx::Point(1, 1)); | 
|  | // The modified layer needs commit | 
|  | ++expected_push_properties_child_; | 
|  | break; | 
|  | case 13: | 
|  | child2_->SetPosition(gfx::Point(1, 1)); | 
|  | // The modified layer needs commit | 
|  | ++expected_push_properties_child2_; | 
|  | break; | 
|  | case 14: | 
|  | child_->RemoveFromParent(); | 
|  | root_->AddChild(child_); | 
|  | // Layers added to the tree get committed. | 
|  | ++expected_push_properties_child_; | 
|  | ++expected_push_properties_grandchild_; | 
|  | break; | 
|  | case 15: | 
|  | grandchild_->SetPosition(gfx::Point(1, 1)); | 
|  | // The modified layer needs commit | 
|  | ++expected_push_properties_grandchild_; | 
|  | break; | 
|  | case 16: | 
|  | // SetNeedsDisplay does not always set needs commit (so call it | 
|  | // explicitly), but is a property change. | 
|  | child_->SetNeedsDisplay(); | 
|  | ++expected_push_properties_child_; | 
|  | layer_tree_host()->SetNeedsCommit(); | 
|  | break; | 
|  | case 17: | 
|  | EndTest(); | 
|  | break; | 
|  | } | 
|  |  | 
|  | // The leaf layer always pushes. | 
|  | if (leaf_always_pushing_layer_->layer_tree_host()) | 
|  | ++expected_push_properties_leaf_layer_; | 
|  | } | 
|  |  | 
|  | void AfterTest() override {} | 
|  |  | 
|  | int num_commits_; | 
|  | FakeContentLayerClient client_; | 
|  | scoped_refptr<PushPropertiesCountingLayer> root_; | 
|  | scoped_refptr<PushPropertiesCountingLayer> child_; | 
|  | scoped_refptr<PushPropertiesCountingLayer> child2_; | 
|  | scoped_refptr<PushPropertiesCountingLayer> grandchild_; | 
|  | scoped_refptr<PushPropertiesCountingLayer> other_root_; | 
|  | scoped_refptr<PushPropertiesCountingLayer> leaf_always_pushing_layer_; | 
|  | size_t expected_push_properties_root_; | 
|  | size_t expected_push_properties_child_; | 
|  | size_t expected_push_properties_child2_; | 
|  | size_t expected_push_properties_grandchild_; | 
|  | size_t expected_push_properties_other_root_; | 
|  | size_t expected_push_properties_leaf_layer_; | 
|  | }; | 
|  |  | 
|  | MULTI_THREAD_TEST_F(LayerTreeHostTestLayersPushProperties); | 
|  |  | 
|  | class LayerTreeHostTestImplLayersPushProperties | 
|  | : public LayerTreeHostTestLayersPushProperties { | 
|  | protected: | 
|  | void BeginTest() override { | 
|  | expected_push_properties_root_impl_ = 0; | 
|  | expected_push_properties_child_impl_ = 0; | 
|  | expected_push_properties_grandchild_impl_ = 0; | 
|  | expected_push_properties_child2_impl_ = 0; | 
|  | expected_push_properties_grandchild2_impl_ = 0; | 
|  | LayerTreeHostTestLayersPushProperties::BeginTest(); | 
|  | } | 
|  |  | 
|  | void DidActivateTreeOnThread(LayerTreeHostImpl* host_impl) override { | 
|  | // These commits are in response to the changes made in | 
|  | // LayerTreeHostTestLayersPushProperties::DidCommitAndDrawFrame() | 
|  | switch (num_commits_) { | 
|  | case 0: | 
|  | // Tree hasn't been setup yet don't bother to check anything. | 
|  | return; | 
|  | case 1: | 
|  | // Root gets set up, Everyone is initialized. | 
|  | ++expected_push_properties_root_impl_; | 
|  | ++expected_push_properties_child_impl_; | 
|  | ++expected_push_properties_grandchild_impl_; | 
|  | ++expected_push_properties_child2_impl_; | 
|  | ++expected_push_properties_grandchild2_impl_; | 
|  | break; | 
|  | case 2: | 
|  | // Tree doesn't change but the one leaf that always pushes is pushed. | 
|  | ++expected_push_properties_grandchild2_impl_; | 
|  | break; | 
|  | case 3: | 
|  | // Root is swapped here. | 
|  | // Clear the expected push properties the tree will be rebuilt. | 
|  | expected_push_properties_root_impl_ = 0; | 
|  | expected_push_properties_child_impl_ = 0; | 
|  | expected_push_properties_grandchild_impl_ = 0; | 
|  | expected_push_properties_child2_impl_ = 0; | 
|  | expected_push_properties_grandchild2_impl_ = 0; | 
|  |  | 
|  | // Make sure the new root is pushed. | 
|  | EXPECT_EQ(1u, static_cast<PushPropertiesCountingLayerImpl*>( | 
|  | host_impl->RootLayer())->push_properties_count()); | 
|  | return; | 
|  | case 4: | 
|  | // Root is swapped back all of the layers in the tree get pushed. | 
|  | ++expected_push_properties_root_impl_; | 
|  | ++expected_push_properties_child_impl_; | 
|  | ++expected_push_properties_grandchild_impl_; | 
|  | ++expected_push_properties_child2_impl_; | 
|  | ++expected_push_properties_grandchild2_impl_; | 
|  | break; | 
|  | case 5: | 
|  | // Tree doesn't change but the one leaf that always pushes is pushed. | 
|  | ++expected_push_properties_grandchild2_impl_; | 
|  | break; | 
|  | case 6: | 
|  | // First child is removed. Structure of the tree changes here so swap | 
|  | // some of the values. child_impl becomes child2_impl. | 
|  | expected_push_properties_child_impl_ = | 
|  | expected_push_properties_child2_impl_; | 
|  | expected_push_properties_child2_impl_ = 0; | 
|  | // grandchild_impl becomes grandchild2_impl. | 
|  | expected_push_properties_grandchild_impl_ = | 
|  | expected_push_properties_grandchild2_impl_; | 
|  | expected_push_properties_grandchild2_impl_ = 0; | 
|  |  | 
|  | // grandchild_impl is now the leaf that always pushes. It is pushed. | 
|  | ++expected_push_properties_grandchild_impl_; | 
|  | break; | 
|  | case 7: | 
|  | // The leaf that always pushes is pushed. | 
|  | ++expected_push_properties_grandchild_impl_; | 
|  |  | 
|  | // Child is added back. New layers are initialized. | 
|  | ++expected_push_properties_grandchild2_impl_; | 
|  | ++expected_push_properties_child2_impl_; | 
|  | break; | 
|  | case 8: | 
|  | // Leaf is removed. | 
|  | expected_push_properties_grandchild2_impl_ = 0; | 
|  |  | 
|  | // Always pushing. | 
|  | ++expected_push_properties_grandchild_impl_; | 
|  | break; | 
|  | case 9: | 
|  | // Leaf is added back | 
|  | ++expected_push_properties_grandchild2_impl_; | 
|  |  | 
|  | // The leaf that always pushes is pushed. | 
|  | ++expected_push_properties_grandchild_impl_; | 
|  | break; | 
|  | case 10: | 
|  | // The leaf that always pushes is pushed. | 
|  | ++expected_push_properties_grandchild_impl_; | 
|  | break; | 
|  | case 11: | 
|  | // The leaf that always pushes is pushed. | 
|  | ++expected_push_properties_grandchild_impl_; | 
|  | break; | 
|  | case 12: | 
|  | // The leaf that always pushes is pushed. | 
|  | ++expected_push_properties_grandchild_impl_; | 
|  |  | 
|  | // This child position was changed. | 
|  | ++expected_push_properties_child2_impl_; | 
|  | break; | 
|  | case 13: | 
|  | // The position of this child was changed. | 
|  | ++expected_push_properties_child_impl_; | 
|  |  | 
|  | // The leaf that always pushes is pushed. | 
|  | ++expected_push_properties_grandchild_impl_; | 
|  | break; | 
|  | case 14: | 
|  | // Second child is removed from tree. Don't discard counts because | 
|  | // they are added back before commit. | 
|  |  | 
|  | // The leaf that always pushes is pushed. | 
|  | ++expected_push_properties_grandchild_impl_; | 
|  |  | 
|  | // Second child added back. | 
|  | ++expected_push_properties_child2_impl_; | 
|  | ++expected_push_properties_grandchild2_impl_; | 
|  |  | 
|  | break; | 
|  | case 15: | 
|  | // The position of this child was changed. | 
|  | ++expected_push_properties_grandchild2_impl_; | 
|  |  | 
|  | // The leaf that always pushes is pushed. | 
|  | ++expected_push_properties_grandchild_impl_; | 
|  | break; | 
|  | case 16: | 
|  | // Second child is invalidated with SetNeedsDisplay | 
|  | ++expected_push_properties_child2_impl_; | 
|  |  | 
|  | // The leaf that always pushed is pushed. | 
|  | ++expected_push_properties_grandchild_impl_; | 
|  | break; | 
|  | } | 
|  |  | 
|  | PushPropertiesCountingLayerImpl* root_impl_ = NULL; | 
|  | PushPropertiesCountingLayerImpl* child_impl_ = NULL; | 
|  | PushPropertiesCountingLayerImpl* child2_impl_ = NULL; | 
|  | PushPropertiesCountingLayerImpl* grandchild_impl_ = NULL; | 
|  | PushPropertiesCountingLayerImpl* leaf_always_pushing_layer_impl_ = NULL; | 
|  |  | 
|  | // Pull the layers that we need from the tree assuming the same structure | 
|  | // as LayerTreeHostTestLayersPushProperties | 
|  | root_impl_ = static_cast<PushPropertiesCountingLayerImpl*>( | 
|  | host_impl->RootLayer()); | 
|  |  | 
|  | if (root_impl_ && root_impl_->children().size() > 0) { | 
|  | child_impl_ = static_cast<PushPropertiesCountingLayerImpl*>( | 
|  | root_impl_->children()[0]); | 
|  |  | 
|  | if (child_impl_ && child_impl_->children().size() > 0) | 
|  | grandchild_impl_ = static_cast<PushPropertiesCountingLayerImpl*>( | 
|  | child_impl_->children()[0]); | 
|  | } | 
|  |  | 
|  | if (root_impl_ && root_impl_->children().size() > 1) { | 
|  | child2_impl_ = static_cast<PushPropertiesCountingLayerImpl*>( | 
|  | root_impl_->children()[1]); | 
|  |  | 
|  | if (child2_impl_ && child2_impl_->children().size() > 0) | 
|  | leaf_always_pushing_layer_impl_ = | 
|  | static_cast<PushPropertiesCountingLayerImpl*>( | 
|  | child2_impl_->children()[0]); | 
|  | } | 
|  |  | 
|  | if (root_impl_) | 
|  | EXPECT_EQ(expected_push_properties_root_impl_, | 
|  | root_impl_->push_properties_count()); | 
|  | if (child_impl_) | 
|  | EXPECT_EQ(expected_push_properties_child_impl_, | 
|  | child_impl_->push_properties_count()); | 
|  | if (grandchild_impl_) | 
|  | EXPECT_EQ(expected_push_properties_grandchild_impl_, | 
|  | grandchild_impl_->push_properties_count()); | 
|  | if (child2_impl_) | 
|  | EXPECT_EQ(expected_push_properties_child2_impl_, | 
|  | child2_impl_->push_properties_count()); | 
|  | if (leaf_always_pushing_layer_impl_) | 
|  | EXPECT_EQ(expected_push_properties_grandchild2_impl_, | 
|  | leaf_always_pushing_layer_impl_->push_properties_count()); | 
|  | } | 
|  |  | 
|  | size_t expected_push_properties_root_impl_; | 
|  | size_t expected_push_properties_child_impl_; | 
|  | size_t expected_push_properties_child2_impl_; | 
|  | size_t expected_push_properties_grandchild_impl_; | 
|  | size_t expected_push_properties_grandchild2_impl_; | 
|  | }; | 
|  |  | 
|  | TEST_F(LayerTreeHostTestImplLayersPushProperties, DelegatingRenderer) { | 
|  | RunTestWithImplSidePainting(); | 
|  | } | 
|  |  | 
|  | class LayerTreeHostTestPropertyChangesDuringUpdateArePushed | 
|  | : public LayerTreeHostTest { | 
|  | protected: | 
|  | void BeginTest() override { PostSetNeedsCommitToMainThread(); } | 
|  |  | 
|  | void SetupTree() override { | 
|  | root_ = Layer::Create(); | 
|  | root_->CreateRenderSurface(); | 
|  | root_->SetBounds(gfx::Size(1, 1)); | 
|  |  | 
|  | bool paint_scrollbar = true; | 
|  | bool has_thumb = false; | 
|  | scrollbar_layer_ = FakePaintedScrollbarLayer::Create( | 
|  | paint_scrollbar, has_thumb, root_->id()); | 
|  |  | 
|  | root_->AddChild(scrollbar_layer_); | 
|  |  | 
|  | layer_tree_host()->SetRootLayer(root_); | 
|  | LayerTreeHostTest::SetupTree(); | 
|  | } | 
|  |  | 
|  | void DidCommitAndDrawFrame() override { | 
|  | switch (layer_tree_host()->source_frame_number()) { | 
|  | case 0: | 
|  | break; | 
|  | case 1: { | 
|  | // During update, the ignore_set_needs_commit_ bit is set to true to | 
|  | // avoid causing a second commit to be scheduled. If a property change | 
|  | // is made during this, however, it needs to be pushed in the upcoming | 
|  | // commit. | 
|  | scoped_ptr<base::AutoReset<bool>> ignore = | 
|  | scrollbar_layer_->IgnoreSetNeedsCommit(); | 
|  |  | 
|  | scrollbar_layer_->SetBounds(gfx::Size(30, 30)); | 
|  |  | 
|  | EXPECT_TRUE(scrollbar_layer_->needs_push_properties()); | 
|  | EXPECT_TRUE(root_->descendant_needs_push_properties()); | 
|  | layer_tree_host()->SetNeedsCommit(); | 
|  |  | 
|  | scrollbar_layer_->reset_push_properties_count(); | 
|  | EXPECT_EQ(0u, scrollbar_layer_->push_properties_count()); | 
|  | break; | 
|  | } | 
|  | case 2: | 
|  | EXPECT_EQ(1u, scrollbar_layer_->push_properties_count()); | 
|  | EndTest(); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | void AfterTest() override {} | 
|  |  | 
|  | scoped_refptr<Layer> root_; | 
|  | scoped_refptr<FakePaintedScrollbarLayer> scrollbar_layer_; | 
|  | }; | 
|  |  | 
|  | MULTI_THREAD_TEST_F(LayerTreeHostTestPropertyChangesDuringUpdateArePushed); | 
|  |  | 
|  | class LayerTreeHostTestSetDrawableCausesCommit : public LayerTreeHostTest { | 
|  | protected: | 
|  | void BeginTest() override { PostSetNeedsCommitToMainThread(); } | 
|  |  | 
|  | void SetupTree() override { | 
|  | root_ = PushPropertiesCountingLayer::Create(); | 
|  | root_->CreateRenderSurface(); | 
|  | child_ = PushPropertiesCountingLayer::Create(); | 
|  | root_->AddChild(child_); | 
|  |  | 
|  | layer_tree_host()->SetRootLayer(root_); | 
|  | LayerTreeHostTest::SetupTree(); | 
|  | } | 
|  |  | 
|  | void DidCommitAndDrawFrame() override { | 
|  | switch (layer_tree_host()->source_frame_number()) { | 
|  | case 0: | 
|  | break; | 
|  | case 1: { | 
|  | // During update, the ignore_set_needs_commit_ bit is set to true to | 
|  | // avoid causing a second commit to be scheduled. If a property change | 
|  | // is made during this, however, it needs to be pushed in the upcoming | 
|  | // commit. | 
|  | EXPECT_FALSE(root_->needs_push_properties()); | 
|  | EXPECT_FALSE(child_->needs_push_properties()); | 
|  | EXPECT_EQ(0, root_->NumDescendantsThatDrawContent()); | 
|  | root_->reset_push_properties_count(); | 
|  | child_->reset_push_properties_count(); | 
|  | child_->SetDrawsContent(true); | 
|  | EXPECT_EQ(1, root_->NumDescendantsThatDrawContent()); | 
|  | EXPECT_EQ(0u, root_->push_properties_count()); | 
|  | EXPECT_EQ(0u, child_->push_properties_count()); | 
|  | EXPECT_TRUE(root_->needs_push_properties()); | 
|  | EXPECT_TRUE(child_->needs_push_properties()); | 
|  | break; | 
|  | } | 
|  | case 2: | 
|  | EXPECT_EQ(1u, root_->push_properties_count()); | 
|  | EXPECT_EQ(1u, child_->push_properties_count()); | 
|  | EXPECT_FALSE(root_->needs_push_properties()); | 
|  | EXPECT_FALSE(child_->needs_push_properties()); | 
|  | EndTest(); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | void AfterTest() override {} | 
|  |  | 
|  | scoped_refptr<PushPropertiesCountingLayer> root_; | 
|  | scoped_refptr<PushPropertiesCountingLayer> child_; | 
|  | }; | 
|  |  | 
|  | MULTI_THREAD_TEST_F(LayerTreeHostTestSetDrawableCausesCommit); | 
|  |  | 
|  | class LayerTreeHostTestCasePushPropertiesThreeGrandChildren | 
|  | : public LayerTreeHostTest { | 
|  | protected: | 
|  | void BeginTest() override { | 
|  | expected_push_properties_root_ = 0; | 
|  | expected_push_properties_child_ = 0; | 
|  | expected_push_properties_grandchild1_ = 0; | 
|  | expected_push_properties_grandchild2_ = 0; | 
|  | expected_push_properties_grandchild3_ = 0; | 
|  | PostSetNeedsCommitToMainThread(); | 
|  | } | 
|  |  | 
|  | void SetupTree() override { | 
|  | root_ = PushPropertiesCountingLayer::Create(); | 
|  | root_->CreateRenderSurface(); | 
|  | child_ = PushPropertiesCountingLayer::Create(); | 
|  | grandchild1_ = PushPropertiesCountingLayer::Create(); | 
|  | grandchild2_ = PushPropertiesCountingLayer::Create(); | 
|  | grandchild3_ = PushPropertiesCountingLayer::Create(); | 
|  |  | 
|  | root_->AddChild(child_); | 
|  | child_->AddChild(grandchild1_); | 
|  | child_->AddChild(grandchild2_); | 
|  | child_->AddChild(grandchild3_); | 
|  |  | 
|  | // Don't set the root layer here. | 
|  | LayerTreeHostTest::SetupTree(); | 
|  | } | 
|  |  | 
|  | void AfterTest() override {} | 
|  |  | 
|  | FakeContentLayerClient client_; | 
|  | scoped_refptr<PushPropertiesCountingLayer> root_; | 
|  | scoped_refptr<PushPropertiesCountingLayer> child_; | 
|  | scoped_refptr<PushPropertiesCountingLayer> grandchild1_; | 
|  | scoped_refptr<PushPropertiesCountingLayer> grandchild2_; | 
|  | scoped_refptr<PushPropertiesCountingLayer> grandchild3_; | 
|  | size_t expected_push_properties_root_; | 
|  | size_t expected_push_properties_child_; | 
|  | size_t expected_push_properties_grandchild1_; | 
|  | size_t expected_push_properties_grandchild2_; | 
|  | size_t expected_push_properties_grandchild3_; | 
|  | }; | 
|  |  | 
|  | class LayerTreeHostTestPushPropertiesAddingToTreeRequiresPush | 
|  | : public LayerTreeHostTestCasePushPropertiesThreeGrandChildren { | 
|  | protected: | 
|  | void DidCommitAndDrawFrame() override { | 
|  | int last_source_frame_number = layer_tree_host()->source_frame_number() - 1; | 
|  | switch (last_source_frame_number) { | 
|  | case 0: | 
|  | EXPECT_FALSE(root_->needs_push_properties()); | 
|  | EXPECT_FALSE(root_->descendant_needs_push_properties()); | 
|  | EXPECT_FALSE(child_->needs_push_properties()); | 
|  | EXPECT_FALSE(child_->descendant_needs_push_properties()); | 
|  | EXPECT_FALSE(grandchild1_->needs_push_properties()); | 
|  | EXPECT_FALSE(grandchild1_->descendant_needs_push_properties()); | 
|  | EXPECT_FALSE(grandchild2_->needs_push_properties()); | 
|  | EXPECT_FALSE(grandchild2_->descendant_needs_push_properties()); | 
|  | EXPECT_FALSE(grandchild3_->needs_push_properties()); | 
|  | EXPECT_FALSE(grandchild3_->descendant_needs_push_properties()); | 
|  |  | 
|  | layer_tree_host()->SetRootLayer(root_); | 
|  |  | 
|  | EXPECT_TRUE(root_->needs_push_properties()); | 
|  | EXPECT_TRUE(root_->descendant_needs_push_properties()); | 
|  | EXPECT_TRUE(child_->needs_push_properties()); | 
|  | EXPECT_TRUE(child_->descendant_needs_push_properties()); | 
|  | EXPECT_TRUE(grandchild1_->needs_push_properties()); | 
|  | EXPECT_FALSE(grandchild1_->descendant_needs_push_properties()); | 
|  | EXPECT_TRUE(grandchild2_->needs_push_properties()); | 
|  | EXPECT_FALSE(grandchild2_->descendant_needs_push_properties()); | 
|  | EXPECT_TRUE(grandchild3_->needs_push_properties()); | 
|  | EXPECT_FALSE(grandchild3_->descendant_needs_push_properties()); | 
|  | break; | 
|  | case 1: | 
|  | EndTest(); | 
|  | break; | 
|  | } | 
|  | } | 
|  | }; | 
|  |  | 
|  | MULTI_THREAD_TEST_F(LayerTreeHostTestPushPropertiesAddingToTreeRequiresPush); | 
|  |  | 
|  | class LayerTreeHostTestPushPropertiesRemovingChildStopsRecursion | 
|  | : public LayerTreeHostTestCasePushPropertiesThreeGrandChildren { | 
|  | protected: | 
|  | void DidCommitAndDrawFrame() override { | 
|  | int last_source_frame_number = layer_tree_host()->source_frame_number() - 1; | 
|  | switch (last_source_frame_number) { | 
|  | case 0: | 
|  | layer_tree_host()->SetRootLayer(root_); | 
|  | break; | 
|  | case 1: | 
|  | EXPECT_FALSE(root_->needs_push_properties()); | 
|  | EXPECT_FALSE(root_->descendant_needs_push_properties()); | 
|  | EXPECT_FALSE(child_->needs_push_properties()); | 
|  | EXPECT_FALSE(child_->descendant_needs_push_properties()); | 
|  | EXPECT_FALSE(grandchild1_->needs_push_properties()); | 
|  | EXPECT_FALSE(grandchild1_->descendant_needs_push_properties()); | 
|  | EXPECT_FALSE(grandchild2_->needs_push_properties()); | 
|  | EXPECT_FALSE(grandchild2_->descendant_needs_push_properties()); | 
|  | EXPECT_FALSE(grandchild3_->needs_push_properties()); | 
|  | EXPECT_FALSE(grandchild3_->descendant_needs_push_properties()); | 
|  |  | 
|  | grandchild1_->RemoveFromParent(); | 
|  | grandchild1_->SetPosition(gfx::Point(1, 1)); | 
|  |  | 
|  | EXPECT_FALSE(root_->needs_push_properties()); | 
|  | EXPECT_FALSE(root_->descendant_needs_push_properties()); | 
|  | EXPECT_FALSE(child_->needs_push_properties()); | 
|  | EXPECT_FALSE(child_->descendant_needs_push_properties()); | 
|  | EXPECT_FALSE(grandchild2_->needs_push_properties()); | 
|  | EXPECT_FALSE(grandchild2_->descendant_needs_push_properties()); | 
|  | EXPECT_FALSE(grandchild3_->needs_push_properties()); | 
|  | EXPECT_FALSE(grandchild3_->descendant_needs_push_properties()); | 
|  |  | 
|  | child_->AddChild(grandchild1_); | 
|  |  | 
|  | EXPECT_FALSE(root_->needs_push_properties()); | 
|  | EXPECT_TRUE(root_->descendant_needs_push_properties()); | 
|  | EXPECT_FALSE(child_->needs_push_properties()); | 
|  | EXPECT_TRUE(child_->descendant_needs_push_properties()); | 
|  | EXPECT_TRUE(grandchild1_->needs_push_properties()); | 
|  | EXPECT_FALSE(grandchild1_->descendant_needs_push_properties()); | 
|  | EXPECT_FALSE(grandchild2_->needs_push_properties()); | 
|  | EXPECT_FALSE(grandchild2_->descendant_needs_push_properties()); | 
|  | EXPECT_FALSE(grandchild3_->needs_push_properties()); | 
|  | EXPECT_FALSE(grandchild3_->descendant_needs_push_properties()); | 
|  |  | 
|  | grandchild2_->SetPosition(gfx::Point(1, 1)); | 
|  |  | 
|  | EXPECT_FALSE(root_->needs_push_properties()); | 
|  | EXPECT_TRUE(root_->descendant_needs_push_properties()); | 
|  | EXPECT_FALSE(child_->needs_push_properties()); | 
|  | EXPECT_TRUE(child_->descendant_needs_push_properties()); | 
|  | EXPECT_TRUE(grandchild1_->needs_push_properties()); | 
|  | EXPECT_FALSE(grandchild1_->descendant_needs_push_properties()); | 
|  | EXPECT_TRUE(grandchild2_->needs_push_properties()); | 
|  | EXPECT_FALSE(grandchild2_->descendant_needs_push_properties()); | 
|  | EXPECT_FALSE(grandchild3_->needs_push_properties()); | 
|  | EXPECT_FALSE(grandchild3_->descendant_needs_push_properties()); | 
|  |  | 
|  | // grandchild2_ will still need a push properties. | 
|  | grandchild1_->RemoveFromParent(); | 
|  |  | 
|  | EXPECT_FALSE(root_->needs_push_properties()); | 
|  | EXPECT_TRUE(root_->descendant_needs_push_properties()); | 
|  | EXPECT_FALSE(child_->needs_push_properties()); | 
|  | EXPECT_TRUE(child_->descendant_needs_push_properties()); | 
|  |  | 
|  | // grandchild3_ does not need a push properties, so recursing should | 
|  | // no longer be needed. | 
|  | grandchild2_->RemoveFromParent(); | 
|  |  | 
|  | EXPECT_FALSE(root_->needs_push_properties()); | 
|  | EXPECT_FALSE(root_->descendant_needs_push_properties()); | 
|  | EXPECT_FALSE(child_->needs_push_properties()); | 
|  | EXPECT_FALSE(child_->descendant_needs_push_properties()); | 
|  | EndTest(); | 
|  | break; | 
|  | } | 
|  | } | 
|  | }; | 
|  |  | 
|  | MULTI_THREAD_TEST_F(LayerTreeHostTestPushPropertiesRemovingChildStopsRecursion); | 
|  |  | 
|  | class LayerTreeHostTestPushPropertiesRemovingChildStopsRecursionWithPersistence | 
|  | : public LayerTreeHostTestCasePushPropertiesThreeGrandChildren { | 
|  | protected: | 
|  | void DidCommitAndDrawFrame() override { | 
|  | int last_source_frame_number = layer_tree_host()->source_frame_number() - 1; | 
|  | switch (last_source_frame_number) { | 
|  | case 0: | 
|  | layer_tree_host()->SetRootLayer(root_); | 
|  | grandchild1_->set_persist_needs_push_properties(true); | 
|  | grandchild2_->set_persist_needs_push_properties(true); | 
|  | break; | 
|  | case 1: | 
|  | EXPECT_FALSE(root_->needs_push_properties()); | 
|  | EXPECT_TRUE(root_->descendant_needs_push_properties()); | 
|  | EXPECT_FALSE(child_->needs_push_properties()); | 
|  | EXPECT_TRUE(child_->descendant_needs_push_properties()); | 
|  | EXPECT_TRUE(grandchild1_->needs_push_properties()); | 
|  | EXPECT_FALSE(grandchild1_->descendant_needs_push_properties()); | 
|  | EXPECT_TRUE(grandchild2_->needs_push_properties()); | 
|  | EXPECT_FALSE(grandchild2_->descendant_needs_push_properties()); | 
|  | EXPECT_FALSE(grandchild3_->needs_push_properties()); | 
|  | EXPECT_FALSE(grandchild3_->descendant_needs_push_properties()); | 
|  |  | 
|  | // grandchild2_ will still need a push properties. | 
|  | grandchild1_->RemoveFromParent(); | 
|  |  | 
|  | EXPECT_FALSE(root_->needs_push_properties()); | 
|  | EXPECT_TRUE(root_->descendant_needs_push_properties()); | 
|  | EXPECT_FALSE(child_->needs_push_properties()); | 
|  | EXPECT_TRUE(child_->descendant_needs_push_properties()); | 
|  |  | 
|  | // grandchild3_ does not need a push properties, so recursing should | 
|  | // no longer be needed. | 
|  | grandchild2_->RemoveFromParent(); | 
|  |  | 
|  | EXPECT_FALSE(root_->needs_push_properties()); | 
|  | EXPECT_FALSE(root_->descendant_needs_push_properties()); | 
|  | EXPECT_FALSE(child_->needs_push_properties()); | 
|  | EXPECT_FALSE(child_->descendant_needs_push_properties()); | 
|  | EndTest(); | 
|  | break; | 
|  | } | 
|  | } | 
|  | }; | 
|  |  | 
|  | MULTI_THREAD_TEST_F( | 
|  | LayerTreeHostTestPushPropertiesRemovingChildStopsRecursionWithPersistence); | 
|  |  | 
|  | class LayerTreeHostTestPushPropertiesSetPropertiesWhileOutsideTree | 
|  | : public LayerTreeHostTestCasePushPropertiesThreeGrandChildren { | 
|  | protected: | 
|  | void DidCommitAndDrawFrame() override { | 
|  | int last_source_frame_number = layer_tree_host()->source_frame_number() - 1; | 
|  | switch (last_source_frame_number) { | 
|  | case 0: | 
|  | layer_tree_host()->SetRootLayer(root_); | 
|  | break; | 
|  | case 1: | 
|  | EXPECT_FALSE(root_->needs_push_properties()); | 
|  | EXPECT_FALSE(root_->descendant_needs_push_properties()); | 
|  | EXPECT_FALSE(child_->needs_push_properties()); | 
|  | EXPECT_FALSE(child_->descendant_needs_push_properties()); | 
|  | EXPECT_FALSE(grandchild1_->needs_push_properties()); | 
|  | EXPECT_FALSE(grandchild1_->descendant_needs_push_properties()); | 
|  | EXPECT_FALSE(grandchild2_->needs_push_properties()); | 
|  | EXPECT_FALSE(grandchild2_->descendant_needs_push_properties()); | 
|  | EXPECT_FALSE(grandchild3_->needs_push_properties()); | 
|  | EXPECT_FALSE(grandchild3_->descendant_needs_push_properties()); | 
|  |  | 
|  | // Change grandchildren while their parent is not in the tree. | 
|  | child_->RemoveFromParent(); | 
|  | grandchild1_->SetPosition(gfx::Point(1, 1)); | 
|  | grandchild2_->SetPosition(gfx::Point(1, 1)); | 
|  | root_->AddChild(child_); | 
|  |  | 
|  | EXPECT_FALSE(root_->needs_push_properties()); | 
|  | EXPECT_TRUE(root_->descendant_needs_push_properties()); | 
|  | EXPECT_TRUE(child_->needs_push_properties()); | 
|  | EXPECT_TRUE(child_->descendant_needs_push_properties()); | 
|  | EXPECT_TRUE(grandchild1_->needs_push_properties()); | 
|  | EXPECT_FALSE(grandchild1_->descendant_needs_push_properties()); | 
|  | EXPECT_TRUE(grandchild2_->needs_push_properties()); | 
|  | EXPECT_FALSE(grandchild2_->descendant_needs_push_properties()); | 
|  | EXPECT_TRUE(grandchild3_->needs_push_properties()); | 
|  | EXPECT_FALSE(grandchild3_->descendant_needs_push_properties()); | 
|  |  | 
|  | grandchild1_->RemoveFromParent(); | 
|  |  | 
|  | EXPECT_FALSE(root_->needs_push_properties()); | 
|  | EXPECT_TRUE(root_->descendant_needs_push_properties()); | 
|  | EXPECT_TRUE(child_->needs_push_properties()); | 
|  | EXPECT_TRUE(child_->descendant_needs_push_properties()); | 
|  |  | 
|  | grandchild2_->RemoveFromParent(); | 
|  |  | 
|  | EXPECT_FALSE(root_->needs_push_properties()); | 
|  | EXPECT_TRUE(root_->descendant_needs_push_properties()); | 
|  | EXPECT_TRUE(child_->needs_push_properties()); | 
|  | EXPECT_TRUE(child_->descendant_needs_push_properties()); | 
|  |  | 
|  | grandchild3_->RemoveFromParent(); | 
|  |  | 
|  | EXPECT_FALSE(root_->needs_push_properties()); | 
|  | EXPECT_TRUE(root_->descendant_needs_push_properties()); | 
|  | EXPECT_TRUE(child_->needs_push_properties()); | 
|  | EXPECT_FALSE(child_->descendant_needs_push_properties()); | 
|  |  | 
|  | EndTest(); | 
|  | break; | 
|  | } | 
|  | } | 
|  | }; | 
|  |  | 
|  | MULTI_THREAD_TEST_F( | 
|  | LayerTreeHostTestPushPropertiesSetPropertiesWhileOutsideTree); | 
|  |  | 
|  | class LayerTreeHostTestPushPropertiesSetPropertyInParentThenChild | 
|  | : public LayerTreeHostTestCasePushPropertiesThreeGrandChildren { | 
|  | protected: | 
|  | void DidCommitAndDrawFrame() override { | 
|  | int last_source_frame_number = layer_tree_host()->source_frame_number() - 1; | 
|  | switch (last_source_frame_number) { | 
|  | case 0: | 
|  | layer_tree_host()->SetRootLayer(root_); | 
|  | break; | 
|  | case 1: | 
|  | EXPECT_FALSE(root_->needs_push_properties()); | 
|  | EXPECT_FALSE(root_->descendant_needs_push_properties()); | 
|  | EXPECT_FALSE(child_->needs_push_properties()); | 
|  | EXPECT_FALSE(child_->descendant_needs_push_properties()); | 
|  | EXPECT_FALSE(grandchild1_->needs_push_properties()); | 
|  | EXPECT_FALSE(grandchild1_->descendant_needs_push_properties()); | 
|  | EXPECT_FALSE(grandchild2_->needs_push_properties()); | 
|  | EXPECT_FALSE(grandchild2_->descendant_needs_push_properties()); | 
|  | EXPECT_FALSE(grandchild3_->needs_push_properties()); | 
|  | EXPECT_FALSE(grandchild3_->descendant_needs_push_properties()); | 
|  |  | 
|  | child_->SetPosition(gfx::Point(1, 1)); | 
|  | grandchild1_->SetPosition(gfx::Point(1, 1)); | 
|  | grandchild2_->SetPosition(gfx::Point(1, 1)); | 
|  |  | 
|  | EXPECT_FALSE(root_->needs_push_properties()); | 
|  | EXPECT_TRUE(root_->descendant_needs_push_properties()); | 
|  | EXPECT_TRUE(child_->needs_push_properties()); | 
|  | EXPECT_TRUE(child_->descendant_needs_push_properties()); | 
|  | EXPECT_TRUE(grandchild1_->needs_push_properties()); | 
|  | EXPECT_FALSE(grandchild1_->descendant_needs_push_properties()); | 
|  | EXPECT_TRUE(grandchild2_->needs_push_properties()); | 
|  | EXPECT_FALSE(grandchild2_->descendant_needs_push_properties()); | 
|  | EXPECT_FALSE(grandchild3_->needs_push_properties()); | 
|  | EXPECT_FALSE(grandchild3_->descendant_needs_push_properties()); | 
|  |  | 
|  | grandchild1_->RemoveFromParent(); | 
|  |  | 
|  | EXPECT_FALSE(root_->needs_push_properties()); | 
|  | EXPECT_TRUE(root_->descendant_needs_push_properties()); | 
|  | EXPECT_TRUE(child_->needs_push_properties()); | 
|  | EXPECT_TRUE(child_->descendant_needs_push_properties()); | 
|  |  | 
|  | grandchild2_->RemoveFromParent(); | 
|  |  | 
|  | EXPECT_FALSE(root_->needs_push_properties()); | 
|  | EXPECT_TRUE(root_->descendant_needs_push_properties()); | 
|  | EXPECT_TRUE(child_->needs_push_properties()); | 
|  | EXPECT_FALSE(child_->descendant_needs_push_properties()); | 
|  |  | 
|  | child_->RemoveFromParent(); | 
|  |  | 
|  | EXPECT_FALSE(root_->needs_push_properties()); | 
|  | EXPECT_FALSE(root_->descendant_needs_push_properties()); | 
|  |  | 
|  | EndTest(); | 
|  | break; | 
|  | } | 
|  | } | 
|  | }; | 
|  |  | 
|  | MULTI_THREAD_TEST_F( | 
|  | LayerTreeHostTestPushPropertiesSetPropertyInParentThenChild); | 
|  |  | 
|  | class LayerTreeHostTestPushPropertiesSetPropertyInChildThenParent | 
|  | : public LayerTreeHostTestCasePushPropertiesThreeGrandChildren { | 
|  | protected: | 
|  | void DidCommitAndDrawFrame() override { | 
|  | int last_source_frame_number = layer_tree_host()->source_frame_number() - 1; | 
|  | switch (last_source_frame_number) { | 
|  | case 0: | 
|  | layer_tree_host()->SetRootLayer(root_); | 
|  | break; | 
|  | case 1: | 
|  | EXPECT_FALSE(root_->needs_push_properties()); | 
|  | EXPECT_FALSE(root_->descendant_needs_push_properties()); | 
|  | EXPECT_FALSE(child_->needs_push_properties()); | 
|  | EXPECT_FALSE(child_->descendant_needs_push_properties()); | 
|  | EXPECT_FALSE(grandchild1_->needs_push_properties()); | 
|  | EXPECT_FALSE(grandchild1_->descendant_needs_push_properties()); | 
|  | EXPECT_FALSE(grandchild2_->needs_push_properties()); | 
|  | EXPECT_FALSE(grandchild2_->descendant_needs_push_properties()); | 
|  | EXPECT_FALSE(grandchild3_->needs_push_properties()); | 
|  | EXPECT_FALSE(grandchild3_->descendant_needs_push_properties()); | 
|  |  | 
|  | grandchild1_->SetPosition(gfx::Point(1, 1)); | 
|  | grandchild2_->SetPosition(gfx::Point(1, 1)); | 
|  | child_->SetPosition(gfx::Point(1, 1)); | 
|  |  | 
|  | EXPECT_FALSE(root_->needs_push_properties()); | 
|  | EXPECT_TRUE(root_->descendant_needs_push_properties()); | 
|  | EXPECT_TRUE(child_->needs_push_properties()); | 
|  | EXPECT_TRUE(child_->descendant_needs_push_properties()); | 
|  | EXPECT_TRUE(grandchild1_->needs_push_properties()); | 
|  | EXPECT_FALSE(grandchild1_->descendant_needs_push_properties()); | 
|  | EXPECT_TRUE(grandchild2_->needs_push_properties()); | 
|  | EXPECT_FALSE(grandchild2_->descendant_needs_push_properties()); | 
|  | EXPECT_FALSE(grandchild3_->needs_push_properties()); | 
|  | EXPECT_FALSE(grandchild3_->descendant_needs_push_properties()); | 
|  |  | 
|  | grandchild1_->RemoveFromParent(); | 
|  |  | 
|  | EXPECT_FALSE(root_->needs_push_properties()); | 
|  | EXPECT_TRUE(root_->descendant_needs_push_properties()); | 
|  | EXPECT_TRUE(child_->needs_push_properties()); | 
|  | EXPECT_TRUE(child_->descendant_needs_push_properties()); | 
|  |  | 
|  | grandchild2_->RemoveFromParent(); | 
|  |  | 
|  | EXPECT_FALSE(root_->needs_push_properties()); | 
|  | EXPECT_TRUE(root_->descendant_needs_push_properties()); | 
|  | EXPECT_TRUE(child_->needs_push_properties()); | 
|  | EXPECT_FALSE(child_->descendant_needs_push_properties()); | 
|  |  | 
|  | child_->RemoveFromParent(); | 
|  |  | 
|  | EXPECT_FALSE(root_->needs_push_properties()); | 
|  | EXPECT_FALSE(root_->descendant_needs_push_properties()); | 
|  |  | 
|  | EndTest(); | 
|  | break; | 
|  | } | 
|  | } | 
|  | }; | 
|  |  | 
|  | MULTI_THREAD_TEST_F( | 
|  | LayerTreeHostTestPushPropertiesSetPropertyInChildThenParent); | 
|  |  | 
|  | // This test verifies that the tree activation callback is invoked correctly. | 
|  | class LayerTreeHostTestTreeActivationCallback : public LayerTreeHostTest { | 
|  | public: | 
|  | LayerTreeHostTestTreeActivationCallback() | 
|  | : num_commits_(0), callback_count_(0) {} | 
|  |  | 
|  | void BeginTest() override { | 
|  | EXPECT_TRUE(HasImplThread()); | 
|  | PostSetNeedsCommitToMainThread(); | 
|  | } | 
|  |  | 
|  | DrawResult PrepareToDrawOnThread(LayerTreeHostImpl* host_impl, | 
|  | LayerTreeHostImpl::FrameData* frame_data, | 
|  | DrawResult draw_result) override { | 
|  | ++num_commits_; | 
|  | switch (num_commits_) { | 
|  | case 1: | 
|  | EXPECT_EQ(0, callback_count_); | 
|  | callback_count_ = 0; | 
|  | SetCallback(true); | 
|  | PostSetNeedsCommitToMainThread(); | 
|  | break; | 
|  | case 2: | 
|  | EXPECT_EQ(1, callback_count_); | 
|  | callback_count_ = 0; | 
|  | SetCallback(false); | 
|  | PostSetNeedsCommitToMainThread(); | 
|  | break; | 
|  | case 3: | 
|  | EXPECT_EQ(0, callback_count_); | 
|  | callback_count_ = 0; | 
|  | EndTest(); | 
|  | break; | 
|  | default: | 
|  | ADD_FAILURE() << num_commits_; | 
|  | EndTest(); | 
|  | break; | 
|  | } | 
|  | return LayerTreeHostTest::PrepareToDrawOnThread( | 
|  | host_impl, frame_data, draw_result); | 
|  | } | 
|  |  | 
|  | void AfterTest() override { EXPECT_EQ(3, num_commits_); } | 
|  |  | 
|  | void SetCallback(bool enable) { | 
|  | output_surface()->SetTreeActivationCallback( | 
|  | enable | 
|  | ? base::Bind( | 
|  | &LayerTreeHostTestTreeActivationCallback::ActivationCallback, | 
|  | base::Unretained(this)) | 
|  | : base::Closure()); | 
|  | } | 
|  |  | 
|  | void ActivationCallback() { ++callback_count_; } | 
|  |  | 
|  | int num_commits_; | 
|  | int callback_count_; | 
|  | }; | 
|  |  | 
|  | TEST_F(LayerTreeHostTestTreeActivationCallback, DirectRenderer) { | 
|  | RunTest(true, false, true); | 
|  | } | 
|  |  | 
|  | TEST_F(LayerTreeHostTestTreeActivationCallback, DelegatingRenderer) { | 
|  | RunTest(true, true, true); | 
|  | } | 
|  |  | 
|  | class LayerInvalidateCausesDraw : public LayerTreeHostTest { | 
|  | public: | 
|  | LayerInvalidateCausesDraw() : num_commits_(0), num_draws_(0) {} | 
|  |  | 
|  | void BeginTest() override { | 
|  | ASSERT_TRUE(!!invalidate_layer_.get()) | 
|  | << "Derived tests must set this in SetupTree"; | 
|  |  | 
|  | // One initial commit. | 
|  | PostSetNeedsCommitToMainThread(); | 
|  | } | 
|  |  | 
|  | void DidCommitAndDrawFrame() override { | 
|  | // After commit, invalidate the layer.  This should cause a commit. | 
|  | if (layer_tree_host()->source_frame_number() == 1) | 
|  | invalidate_layer_->SetNeedsDisplay(); | 
|  | } | 
|  |  | 
|  | void DrawLayersOnThread(LayerTreeHostImpl* impl) override { | 
|  | num_draws_++; | 
|  | if (impl->active_tree()->source_frame_number() == 1) | 
|  | EndTest(); | 
|  | } | 
|  |  | 
|  | void CommitCompleteOnThread(LayerTreeHostImpl* impl) override { | 
|  | num_commits_++; | 
|  | } | 
|  |  | 
|  | void AfterTest() override { | 
|  | EXPECT_GE(2, num_commits_); | 
|  | EXPECT_GE(2, num_draws_); | 
|  | } | 
|  |  | 
|  | protected: | 
|  | scoped_refptr<Layer> invalidate_layer_; | 
|  |  | 
|  | private: | 
|  | int num_commits_; | 
|  | int num_draws_; | 
|  | }; | 
|  |  | 
|  | // IOSurfaceLayer must support being invalidated and then passing that along | 
|  | // to the compositor thread, even though no resources are updated in | 
|  | // response to that invalidation. | 
|  | class LayerTreeHostTestIOSurfaceLayerInvalidate | 
|  | : public LayerInvalidateCausesDraw { | 
|  | public: | 
|  | void SetupTree() override { | 
|  | LayerTreeHostTest::SetupTree(); | 
|  | scoped_refptr<IOSurfaceLayer> layer = IOSurfaceLayer::Create(); | 
|  | layer->SetBounds(gfx::Size(10, 10)); | 
|  | uint32_t fake_io_surface_id = 7; | 
|  | layer->SetIOSurfaceProperties(fake_io_surface_id, layer->bounds()); | 
|  | layer->SetIsDrawable(true); | 
|  | layer_tree_host()->root_layer()->AddChild(layer); | 
|  |  | 
|  | invalidate_layer_ = layer; | 
|  | } | 
|  | }; | 
|  |  | 
|  | // TODO(danakj): IOSurface layer can not be transported. crbug.com/239335 | 
|  | SINGLE_AND_MULTI_THREAD_DIRECT_RENDERER_TEST_F( | 
|  | LayerTreeHostTestIOSurfaceLayerInvalidate); | 
|  |  | 
|  | class LayerTreeHostTestPushHiddenLayer : public LayerTreeHostTest { | 
|  | protected: | 
|  | void SetupTree() override { | 
|  | root_layer_ = Layer::Create(); | 
|  | root_layer_->CreateRenderSurface(); | 
|  | root_layer_->SetPosition(gfx::Point()); | 
|  | root_layer_->SetBounds(gfx::Size(10, 10)); | 
|  |  | 
|  | parent_layer_ = SolidColorLayer::Create(); | 
|  | parent_layer_->SetPosition(gfx::Point()); | 
|  | parent_layer_->SetBounds(gfx::Size(10, 10)); | 
|  | parent_layer_->SetIsDrawable(true); | 
|  | root_layer_->AddChild(parent_layer_); | 
|  |  | 
|  | child_layer_ = SolidColorLayer::Create(); | 
|  | child_layer_->SetPosition(gfx::Point()); | 
|  | child_layer_->SetBounds(gfx::Size(10, 10)); | 
|  | child_layer_->SetIsDrawable(true); | 
|  | parent_layer_->AddChild(child_layer_); | 
|  |  | 
|  | layer_tree_host()->SetRootLayer(root_layer_); | 
|  | LayerTreeHostTest::SetupTree(); | 
|  | } | 
|  |  | 
|  | void BeginTest() override { PostSetNeedsCommitToMainThread(); } | 
|  |  | 
|  | void DidCommitAndDrawFrame() override { | 
|  | switch (layer_tree_host()->source_frame_number()) { | 
|  | case 1: | 
|  | // The layer type used does not need to push properties every frame. | 
|  | EXPECT_FALSE(child_layer_->needs_push_properties()); | 
|  |  | 
|  | // Change the bounds of the child layer, but make it skipped | 
|  | // by CalculateDrawProperties. | 
|  | parent_layer_->SetOpacity(0.f); | 
|  | child_layer_->SetBounds(gfx::Size(5, 5)); | 
|  | break; | 
|  | case 2: | 
|  | // The bounds of the child layer were pushed to the impl side. | 
|  | EXPECT_FALSE(child_layer_->needs_push_properties()); | 
|  |  | 
|  | EndTest(); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | void DidActivateTreeOnThread(LayerTreeHostImpl* impl) override { | 
|  | LayerImpl* root = impl->active_tree()->root_layer(); | 
|  | LayerImpl* parent = root->children()[0]; | 
|  | LayerImpl* child = parent->children()[0]; | 
|  |  | 
|  | switch (impl->active_tree()->source_frame_number()) { | 
|  | case 1: | 
|  | EXPECT_EQ(gfx::Size(5, 5).ToString(), child->bounds().ToString()); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | void AfterTest() override {} | 
|  |  | 
|  | scoped_refptr<Layer> root_layer_; | 
|  | scoped_refptr<SolidColorLayer> parent_layer_; | 
|  | scoped_refptr<SolidColorLayer> child_layer_; | 
|  | }; | 
|  |  | 
|  | SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestPushHiddenLayer); | 
|  |  | 
|  | class LayerTreeHostTestUpdateLayerInEmptyViewport : public LayerTreeHostTest { | 
|  | protected: | 
|  | void InitializeSettings(LayerTreeSettings* settings) override { | 
|  | settings->impl_side_painting = true; | 
|  | } | 
|  |  | 
|  | void SetupTree() override { | 
|  | root_layer_ = FakePictureLayer::Create(&client_); | 
|  | root_layer_->SetBounds(gfx::Size(10, 10)); | 
|  |  | 
|  | layer_tree_host()->SetRootLayer(root_layer_); | 
|  | LayerTreeHostTest::SetupTree(); | 
|  | } | 
|  |  | 
|  | void BeginTest() override { | 
|  | // The viewport is empty, but we still need to update layers on the main | 
|  | // thread. | 
|  | layer_tree_host()->SetViewportSize(gfx::Size(0, 0)); | 
|  | PostSetNeedsCommitToMainThread(); | 
|  | } | 
|  |  | 
|  | void DidCommit() override { | 
|  | // The layer should be updated even though the viewport is empty, so we | 
|  | // are capable of drawing it on the impl tree. | 
|  | EXPECT_GT(root_layer_->update_count(), 0u); | 
|  | EndTest(); | 
|  | } | 
|  |  | 
|  | void AfterTest() override {} | 
|  |  | 
|  | FakeContentLayerClient client_; | 
|  | scoped_refptr<FakePictureLayer> root_layer_; | 
|  | }; | 
|  |  | 
|  | MULTI_THREAD_TEST_F(LayerTreeHostTestUpdateLayerInEmptyViewport); | 
|  |  | 
|  | class LayerTreeHostTestAbortEvictedTextures : public LayerTreeHostTest { | 
|  | public: | 
|  | LayerTreeHostTestAbortEvictedTextures() | 
|  | : num_will_begin_main_frames_(0), num_impl_commits_(0) {} | 
|  |  | 
|  | protected: | 
|  | void SetupTree() override { | 
|  | scoped_refptr<SolidColorLayer> root_layer = SolidColorLayer::Create(); | 
|  | root_layer->SetBounds(gfx::Size(200, 200)); | 
|  | root_layer->SetIsDrawable(true); | 
|  | root_layer->CreateRenderSurface(); | 
|  |  | 
|  | layer_tree_host()->SetRootLayer(root_layer); | 
|  | LayerTreeHostTest::SetupTree(); | 
|  | } | 
|  |  | 
|  | void BeginTest() override { PostSetNeedsCommitToMainThread(); } | 
|  |  | 
|  | void WillBeginMainFrame() override { | 
|  | num_will_begin_main_frames_++; | 
|  | switch (num_will_begin_main_frames_) { | 
|  | case 2: | 
|  | // Send a redraw to the compositor thread.  This will (wrongly) be | 
|  | // ignored unless aborting resets the texture state. | 
|  | layer_tree_host()->SetNeedsRedraw(); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | void BeginCommitOnThread(LayerTreeHostImpl* impl) override { | 
|  | num_impl_commits_++; | 
|  | } | 
|  |  | 
|  | void DrawLayersOnThread(LayerTreeHostImpl* impl) override { | 
|  | switch (impl->SourceAnimationFrameNumber()) { | 
|  | case 1: | 
|  | // Prevent draws until commit. | 
|  | impl->active_tree()->SetContentsTexturesPurged(); | 
|  | EXPECT_FALSE(impl->CanDraw()); | 
|  | // Trigger an abortable commit. | 
|  | impl->SetNeedsCommit(); | 
|  | break; | 
|  | case 2: | 
|  | EndTest(); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | void AfterTest() override { | 
|  | // Ensure that the commit was truly aborted. | 
|  | EXPECT_EQ(2, num_will_begin_main_frames_); | 
|  | EXPECT_EQ(1, num_impl_commits_); | 
|  | } | 
|  |  | 
|  | private: | 
|  | int num_will_begin_main_frames_; | 
|  | int num_impl_commits_; | 
|  | }; | 
|  |  | 
|  | // Commits can only be aborted when using the thread proxy. | 
|  | MULTI_THREAD_TEST_F(LayerTreeHostTestAbortEvictedTextures); | 
|  |  | 
|  | class LayerTreeHostTestMaxTransferBufferUsageBytes : public LayerTreeHostTest { | 
|  | protected: | 
|  | void InitializeSettings(LayerTreeSettings* settings) override { | 
|  | settings->impl_side_painting = true; | 
|  | settings->use_zero_copy = false; | 
|  | settings->use_one_copy = false; | 
|  | } | 
|  |  | 
|  | scoped_ptr<FakeOutputSurface> CreateFakeOutputSurface() override { | 
|  | scoped_refptr<TestContextProvider> context_provider = | 
|  | TestContextProvider::Create(); | 
|  | context_provider->SetMaxTransferBufferUsageBytes(512 * 512); | 
|  | if (delegating_renderer()) | 
|  | return FakeOutputSurface::CreateDelegating3d(context_provider); | 
|  | else | 
|  | return FakeOutputSurface::Create3d(context_provider); | 
|  | } | 
|  |  | 
|  | void SetupTree() override { | 
|  | client_.set_fill_with_nonsolid_color(true); | 
|  | scoped_refptr<FakePictureLayer> root_layer = | 
|  | FakePictureLayer::Create(&client_); | 
|  | root_layer->SetBounds(gfx::Size(1024, 1024)); | 
|  | root_layer->SetIsDrawable(true); | 
|  |  | 
|  | layer_tree_host()->SetRootLayer(root_layer); | 
|  | LayerTreeHostTest::SetupTree(); | 
|  | } | 
|  |  | 
|  | void BeginTest() override { PostSetNeedsCommitToMainThread(); } | 
|  |  | 
|  | void DidActivateTreeOnThread(LayerTreeHostImpl* impl) override { | 
|  | TestWebGraphicsContext3D* context = TestContext(); | 
|  |  | 
|  | // Expect that the transfer buffer memory used is equal to the | 
|  | // MaxTransferBufferUsageBytes value set in CreateOutputSurface. | 
|  | EXPECT_EQ(512 * 512u, context->max_used_transfer_buffer_usage_bytes()); | 
|  | EndTest(); | 
|  | } | 
|  |  | 
|  | void AfterTest() override {} | 
|  |  | 
|  | private: | 
|  | FakeContentLayerClient client_; | 
|  | }; | 
|  |  | 
|  | // Impl-side painting is a multi-threaded compositor feature. | 
|  | MULTI_THREAD_TEST_F(LayerTreeHostTestMaxTransferBufferUsageBytes); | 
|  |  | 
|  | // Test ensuring that memory limits are sent to the prioritized resource | 
|  | // manager. | 
|  | class LayerTreeHostTestMemoryLimits : public LayerTreeHostTest { | 
|  | public: | 
|  | LayerTreeHostTestMemoryLimits() : num_commits_(0) {} | 
|  |  | 
|  | void BeginTest() override { PostSetNeedsCommitToMainThread(); } | 
|  |  | 
|  | void WillCommit() override { | 
|  | // Some commits are aborted, so increment number of attempted commits here. | 
|  | num_commits_++; | 
|  | } | 
|  |  | 
|  | void DidCommit() override { | 
|  | switch (num_commits_) { | 
|  | case 1: | 
|  | // Verify default values. | 
|  | EXPECT_EQ(PrioritizedResourceManager::DefaultMemoryAllocationLimit(), | 
|  | layer_tree_host() | 
|  | ->contents_texture_manager() | 
|  | ->MaxMemoryLimitBytes()); | 
|  | EXPECT_EQ(PriorityCalculator::AllowEverythingCutoff(), | 
|  | layer_tree_host() | 
|  | ->contents_texture_manager() | 
|  | ->ExternalPriorityCutoff()); | 
|  | PostSetNeedsCommitToMainThread(); | 
|  | break; | 
|  | case 2: | 
|  | // The values should remain the same until the commit after the policy | 
|  | // is changed. | 
|  | EXPECT_EQ(PrioritizedResourceManager::DefaultMemoryAllocationLimit(), | 
|  | layer_tree_host() | 
|  | ->contents_texture_manager() | 
|  | ->MaxMemoryLimitBytes()); | 
|  | EXPECT_EQ(PriorityCalculator::AllowEverythingCutoff(), | 
|  | layer_tree_host() | 
|  | ->contents_texture_manager() | 
|  | ->ExternalPriorityCutoff()); | 
|  | break; | 
|  | case 3: | 
|  | // Verify values were correctly passed. | 
|  | EXPECT_EQ(16u * 1024u * 1024u, | 
|  | layer_tree_host() | 
|  | ->contents_texture_manager() | 
|  | ->MaxMemoryLimitBytes()); | 
|  | EXPECT_EQ(PriorityCalculator::AllowVisibleAndNearbyCutoff(), | 
|  | layer_tree_host() | 
|  | ->contents_texture_manager() | 
|  | ->ExternalPriorityCutoff()); | 
|  | EndTest(); | 
|  | break; | 
|  | case 4: | 
|  | // Make sure no extra commits happen. | 
|  | NOTREACHED(); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | void CommitCompleteOnThread(LayerTreeHostImpl* impl) override { | 
|  | switch (num_commits_) { | 
|  | case 1: | 
|  | break; | 
|  | case 2: | 
|  | // This will trigger a commit because the priority cutoff has changed. | 
|  | impl->SetMemoryPolicy(ManagedMemoryPolicy( | 
|  | 16u * 1024u * 1024u, | 
|  | gpu::MemoryAllocation::CUTOFF_ALLOW_NICE_TO_HAVE, | 
|  | 1000)); | 
|  | break; | 
|  | case 3: | 
|  | // This will not trigger a commit because the priority cutoff has not | 
|  | // changed, and there is already enough memory for all allocations. | 
|  | impl->SetMemoryPolicy(ManagedMemoryPolicy( | 
|  | 32u * 1024u * 1024u, | 
|  | gpu::MemoryAllocation::CUTOFF_ALLOW_NICE_TO_HAVE, | 
|  | 1000)); | 
|  | break; | 
|  | case 4: | 
|  | NOTREACHED(); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | void AfterTest() override {} | 
|  |  | 
|  | private: | 
|  | int num_commits_; | 
|  | }; | 
|  |  | 
|  | SINGLE_AND_MULTI_THREAD_NOIMPL_TEST_F(LayerTreeHostTestMemoryLimits); | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | class LayerTreeHostTestSetMemoryPolicyOnLostOutputSurface | 
|  | : public LayerTreeHostTest { | 
|  | protected: | 
|  | LayerTreeHostTestSetMemoryPolicyOnLostOutputSurface() | 
|  | : first_output_surface_memory_limit_(4321234), | 
|  | second_output_surface_memory_limit_(1234321) {} | 
|  |  | 
|  | scoped_ptr<FakeOutputSurface> CreateFakeOutputSurface() override { | 
|  | if (!first_context_provider_.get()) { | 
|  | first_context_provider_ = TestContextProvider::Create(); | 
|  | } else { | 
|  | EXPECT_FALSE(second_context_provider_.get()); | 
|  | second_context_provider_ = TestContextProvider::Create(); | 
|  | } | 
|  |  | 
|  | scoped_refptr<TestContextProvider> provider(second_context_provider_.get() | 
|  | ? second_context_provider_ | 
|  | : first_context_provider_); | 
|  | scoped_ptr<FakeOutputSurface> output_surface; | 
|  | if (delegating_renderer()) | 
|  | output_surface = FakeOutputSurface::CreateDelegating3d(provider); | 
|  | else | 
|  | output_surface = FakeOutputSurface::Create3d(provider); | 
|  | output_surface->SetMemoryPolicyToSetAtBind( | 
|  | make_scoped_ptr(new ManagedMemoryPolicy( | 
|  | second_context_provider_.get() ? second_output_surface_memory_limit_ | 
|  | : first_output_surface_memory_limit_, | 
|  | gpu::MemoryAllocation::CUTOFF_ALLOW_NICE_TO_HAVE, | 
|  | ManagedMemoryPolicy::kDefaultNumResourcesLimit))); | 
|  | return output_surface.Pass(); | 
|  | } | 
|  |  | 
|  | void SetupTree() override { | 
|  | if (layer_tree_host()->settings().impl_side_painting) | 
|  | root_ = FakePictureLayer::Create(&client_); | 
|  | else | 
|  | root_ = FakeContentLayer::Create(&client_); | 
|  | root_->SetBounds(gfx::Size(20, 20)); | 
|  | layer_tree_host()->SetRootLayer(root_); | 
|  | LayerTreeHostTest::SetupTree(); | 
|  | } | 
|  |  | 
|  | void BeginTest() override { PostSetNeedsCommitToMainThread(); } | 
|  |  | 
|  | void DidCommitAndDrawFrame() override { | 
|  | // Lost context sometimes takes two frames to recreate. The third frame | 
|  | // is sometimes aborted, so wait until the fourth frame to verify that | 
|  | // the memory has been set, and the fifth frame to end the test. | 
|  | if (layer_tree_host()->source_frame_number() < 5) { | 
|  | layer_tree_host()->SetNeedsCommit(); | 
|  | } else if (layer_tree_host()->source_frame_number() == 5) { | 
|  | EndTest(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void SwapBuffersOnThread(LayerTreeHostImpl* impl, bool result) override { | 
|  | switch (impl->active_tree()->source_frame_number()) { | 
|  | case 1: | 
|  | EXPECT_EQ(first_output_surface_memory_limit_, | 
|  | impl->memory_allocation_limit_bytes()); | 
|  | // Lose the output surface. | 
|  | first_context_provider_->TestContext3d()->loseContextCHROMIUM( | 
|  | GL_GUILTY_CONTEXT_RESET_ARB, GL_INNOCENT_CONTEXT_RESET_ARB); | 
|  | break; | 
|  | case 4: | 
|  | EXPECT_EQ(second_output_surface_memory_limit_, | 
|  | impl->memory_allocation_limit_bytes()); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | void AfterTest() override {} | 
|  |  | 
|  | scoped_refptr<TestContextProvider> first_context_provider_; | 
|  | scoped_refptr<TestContextProvider> second_context_provider_; | 
|  | size_t first_output_surface_memory_limit_; | 
|  | size_t second_output_surface_memory_limit_; | 
|  | FakeContentLayerClient client_; | 
|  | scoped_refptr<Layer> root_; | 
|  | }; | 
|  |  | 
|  | SINGLE_AND_MULTI_THREAD_TEST_F( | 
|  | LayerTreeHostTestSetMemoryPolicyOnLostOutputSurface); | 
|  |  | 
|  | struct TestSwapPromiseResult { | 
|  | TestSwapPromiseResult() | 
|  | : did_swap_called(false), | 
|  | did_not_swap_called(false), | 
|  | dtor_called(false), | 
|  | reason(SwapPromise::DID_NOT_SWAP_UNKNOWN) {} | 
|  |  | 
|  | bool did_swap_called; | 
|  | bool did_not_swap_called; | 
|  | bool dtor_called; | 
|  | SwapPromise::DidNotSwapReason reason; | 
|  | base::Lock lock; | 
|  | }; | 
|  |  | 
|  | class TestSwapPromise : public SwapPromise { | 
|  | public: | 
|  | explicit TestSwapPromise(TestSwapPromiseResult* result) : result_(result) {} | 
|  |  | 
|  | ~TestSwapPromise() override { | 
|  | base::AutoLock lock(result_->lock); | 
|  | result_->dtor_called = true; | 
|  | } | 
|  |  | 
|  | void DidSwap(CompositorFrameMetadata* metadata) override { | 
|  | base::AutoLock lock(result_->lock); | 
|  | EXPECT_FALSE(result_->did_swap_called); | 
|  | EXPECT_FALSE(result_->did_not_swap_called); | 
|  | result_->did_swap_called = true; | 
|  | } | 
|  |  | 
|  | void DidNotSwap(DidNotSwapReason reason) override { | 
|  | base::AutoLock lock(result_->lock); | 
|  | EXPECT_FALSE(result_->did_swap_called); | 
|  | EXPECT_FALSE(result_->did_not_swap_called); | 
|  | result_->did_not_swap_called = true; | 
|  | result_->reason = reason; | 
|  | } | 
|  |  | 
|  | int64 TraceId() const override { return 0; } | 
|  |  | 
|  | private: | 
|  | // Not owned. | 
|  | TestSwapPromiseResult* result_; | 
|  | }; | 
|  |  | 
|  | class LayerTreeHostTestBreakSwapPromise : public LayerTreeHostTest { | 
|  | protected: | 
|  | LayerTreeHostTestBreakSwapPromise() | 
|  | : commit_count_(0), commit_complete_count_(0) {} | 
|  |  | 
|  | void WillBeginMainFrame() override { | 
|  | ASSERT_LE(commit_count_, 2); | 
|  | scoped_ptr<SwapPromise> swap_promise( | 
|  | new TestSwapPromise(&swap_promise_result_[commit_count_])); | 
|  | layer_tree_host()->QueueSwapPromise(swap_promise.Pass()); | 
|  | } | 
|  |  | 
|  | void BeginTest() override { PostSetNeedsCommitToMainThread(); } | 
|  |  | 
|  | void DidCommit() override { | 
|  | commit_count_++; | 
|  | if (commit_count_ == 2) { | 
|  | // This commit will finish. | 
|  | layer_tree_host()->SetNeedsCommit(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void CommitCompleteOnThread(LayerTreeHostImpl* host_impl) override { | 
|  | commit_complete_count_++; | 
|  | if (commit_complete_count_ == 1) { | 
|  | // This commit will be aborted because no actual update. | 
|  | PostSetNeedsUpdateLayersToMainThread(); | 
|  | } else { | 
|  | EndTest(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void AfterTest() override { | 
|  | // 3 commits are scheduled. 2 completes. 1 is aborted. | 
|  | EXPECT_EQ(commit_count_, 3); | 
|  | EXPECT_EQ(commit_complete_count_, 2); | 
|  |  | 
|  | { | 
|  | // The first commit completes and causes swap buffer which finishes | 
|  | // the promise. | 
|  | base::AutoLock lock(swap_promise_result_[0].lock); | 
|  | EXPECT_TRUE(swap_promise_result_[0].did_swap_called); | 
|  | EXPECT_FALSE(swap_promise_result_[0].did_not_swap_called); | 
|  | EXPECT_TRUE(swap_promise_result_[0].dtor_called); | 
|  | } | 
|  |  | 
|  | { | 
|  | // The second commit is aborted since it contains no updates. | 
|  | base::AutoLock lock(swap_promise_result_[1].lock); | 
|  | EXPECT_FALSE(swap_promise_result_[1].did_swap_called); | 
|  | EXPECT_TRUE(swap_promise_result_[1].did_not_swap_called); | 
|  | EXPECT_EQ(SwapPromise::COMMIT_NO_UPDATE, swap_promise_result_[1].reason); | 
|  | EXPECT_TRUE(swap_promise_result_[1].dtor_called); | 
|  | } | 
|  |  | 
|  | { | 
|  | // The last commit completes but it does not cause swap buffer because | 
|  | // there is no damage in the frame data. | 
|  | base::AutoLock lock(swap_promise_result_[2].lock); | 
|  | EXPECT_FALSE(swap_promise_result_[2].did_swap_called); | 
|  | EXPECT_TRUE(swap_promise_result_[2].did_not_swap_called); | 
|  | EXPECT_EQ(SwapPromise::SWAP_FAILS, swap_promise_result_[2].reason); | 
|  | EXPECT_TRUE(swap_promise_result_[2].dtor_called); | 
|  | } | 
|  | } | 
|  |  | 
|  | int commit_count_; | 
|  | int commit_complete_count_; | 
|  | TestSwapPromiseResult swap_promise_result_[3]; | 
|  | }; | 
|  |  | 
|  | MULTI_THREAD_TEST_F(LayerTreeHostTestBreakSwapPromise); | 
|  |  | 
|  | class LayerTreeHostTestKeepSwapPromise : public LayerTreeTest { | 
|  | public: | 
|  | LayerTreeHostTestKeepSwapPromise() {} | 
|  |  | 
|  | void BeginTest() override { | 
|  | layer_ = SolidColorLayer::Create(); | 
|  | layer_->SetIsDrawable(true); | 
|  | layer_->SetBounds(gfx::Size(10, 10)); | 
|  | layer_tree_host()->SetRootLayer(layer_); | 
|  | gfx::Size bounds(100, 100); | 
|  | layer_tree_host()->SetViewportSize(bounds); | 
|  | PostSetNeedsCommitToMainThread(); | 
|  | } | 
|  |  | 
|  | void DidCommit() override { | 
|  | MainThreadTaskRunner()->PostTask( | 
|  | FROM_HERE, base::Bind(&LayerTreeHostTestKeepSwapPromise::ChangeFrame, | 
|  | base::Unretained(this))); | 
|  | } | 
|  |  | 
|  | void ChangeFrame() { | 
|  | switch (layer_tree_host()->source_frame_number()) { | 
|  | case 1: | 
|  | layer_->SetBounds(gfx::Size(10, 11)); | 
|  | layer_tree_host()->QueueSwapPromise( | 
|  | make_scoped_ptr(new TestSwapPromise(&swap_promise_result_))); | 
|  | break; | 
|  | case 2: | 
|  | break; | 
|  | default: | 
|  | NOTREACHED(); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | void SwapBuffersOnThread(LayerTreeHostImpl* host_impl, bool result) override { | 
|  | EXPECT_TRUE(result); | 
|  | if (host_impl->active_tree()->source_frame_number() >= 1) { | 
|  | // The commit changes layers so it should cause a swap. | 
|  | base::AutoLock lock(swap_promise_result_.lock); | 
|  | EXPECT_TRUE(swap_promise_result_.did_swap_called); | 
|  | EXPECT_FALSE(swap_promise_result_.did_not_swap_called); | 
|  | EXPECT_TRUE(swap_promise_result_.dtor_called); | 
|  | EndTest(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void AfterTest() override {} | 
|  |  | 
|  | private: | 
|  | scoped_refptr<Layer> layer_; | 
|  | TestSwapPromiseResult swap_promise_result_; | 
|  | }; | 
|  |  | 
|  | SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestKeepSwapPromise); | 
|  |  | 
|  | class LayerTreeHostTestBreakSwapPromiseForVisibilityAbortedCommit | 
|  | : public LayerTreeHostTest { | 
|  | protected: | 
|  | void BeginTest() override { PostSetNeedsCommitToMainThread(); } | 
|  |  | 
|  | void DidCommit() override { | 
|  | layer_tree_host()->SetDeferCommits(true); | 
|  | layer_tree_host()->SetNeedsCommit(); | 
|  | } | 
|  |  | 
|  | void DidDeferCommit() override { | 
|  | layer_tree_host()->SetVisible(false); | 
|  | scoped_ptr<SwapPromise> swap_promise( | 
|  | new TestSwapPromise(&swap_promise_result_)); | 
|  | layer_tree_host()->QueueSwapPromise(swap_promise.Pass()); | 
|  | layer_tree_host()->SetDeferCommits(false); | 
|  | } | 
|  |  | 
|  | void BeginMainFrameAbortedOnThread(LayerTreeHostImpl* host_impl, | 
|  | CommitEarlyOutReason reason) override { | 
|  | EndTest(); | 
|  | } | 
|  |  | 
|  | void AfterTest() override { | 
|  | { | 
|  | base::AutoLock lock(swap_promise_result_.lock); | 
|  | EXPECT_FALSE(swap_promise_result_.did_swap_called); | 
|  | EXPECT_TRUE(swap_promise_result_.did_not_swap_called); | 
|  | EXPECT_EQ(SwapPromise::COMMIT_FAILS, swap_promise_result_.reason); | 
|  | EXPECT_TRUE(swap_promise_result_.dtor_called); | 
|  | } | 
|  | } | 
|  |  | 
|  | TestSwapPromiseResult swap_promise_result_; | 
|  | }; | 
|  |  | 
|  | SINGLE_AND_MULTI_THREAD_TEST_F( | 
|  | LayerTreeHostTestBreakSwapPromiseForVisibilityAbortedCommit); | 
|  |  | 
|  | class LayerTreeHostTestBreakSwapPromiseForContextAbortedCommit | 
|  | : public LayerTreeHostTest { | 
|  | protected: | 
|  | void BeginTest() override { PostSetNeedsCommitToMainThread(); } | 
|  |  | 
|  | void DidCommit() override { | 
|  | if (TestEnded()) | 
|  | return; | 
|  | layer_tree_host()->SetDeferCommits(true); | 
|  | layer_tree_host()->SetNeedsCommit(); | 
|  | } | 
|  |  | 
|  | void DidDeferCommit() override { | 
|  | layer_tree_host()->DidLoseOutputSurface(); | 
|  | scoped_ptr<SwapPromise> swap_promise( | 
|  | new TestSwapPromise(&swap_promise_result_)); | 
|  | layer_tree_host()->QueueSwapPromise(swap_promise.Pass()); | 
|  | layer_tree_host()->SetDeferCommits(false); | 
|  | } | 
|  |  | 
|  | void BeginMainFrameAbortedOnThread(LayerTreeHostImpl* host_impl, | 
|  | CommitEarlyOutReason reason) override { | 
|  | // This is needed so that the impl-thread state matches main-thread state. | 
|  | host_impl->DidLoseOutputSurface(); | 
|  | EndTest(); | 
|  | } | 
|  |  | 
|  | void AfterTest() override { | 
|  | { | 
|  | base::AutoLock lock(swap_promise_result_.lock); | 
|  | EXPECT_FALSE(swap_promise_result_.did_swap_called); | 
|  | EXPECT_TRUE(swap_promise_result_.did_not_swap_called); | 
|  | EXPECT_EQ(SwapPromise::COMMIT_FAILS, swap_promise_result_.reason); | 
|  | EXPECT_TRUE(swap_promise_result_.dtor_called); | 
|  | } | 
|  | } | 
|  |  | 
|  | TestSwapPromiseResult swap_promise_result_; | 
|  | }; | 
|  |  | 
|  | SINGLE_AND_MULTI_THREAD_TEST_F( | 
|  | LayerTreeHostTestBreakSwapPromiseForContextAbortedCommit); | 
|  |  | 
|  | class SimpleSwapPromiseMonitor : public SwapPromiseMonitor { | 
|  | public: | 
|  | SimpleSwapPromiseMonitor(LayerTreeHost* layer_tree_host, | 
|  | LayerTreeHostImpl* layer_tree_host_impl, | 
|  | int* set_needs_commit_count, | 
|  | int* set_needs_redraw_count) | 
|  | : SwapPromiseMonitor(layer_tree_host, layer_tree_host_impl), | 
|  | set_needs_commit_count_(set_needs_commit_count) {} | 
|  |  | 
|  | ~SimpleSwapPromiseMonitor() override {} | 
|  |  | 
|  | void OnSetNeedsCommitOnMain() override { (*set_needs_commit_count_)++; } | 
|  |  | 
|  | void OnSetNeedsRedrawOnImpl() override { | 
|  | ADD_FAILURE() << "Should not get called on main thread."; | 
|  | } | 
|  |  | 
|  | void OnForwardScrollUpdateToMainThreadOnImpl() override { | 
|  | ADD_FAILURE() << "Should not get called on main thread."; | 
|  | } | 
|  |  | 
|  | private: | 
|  | int* set_needs_commit_count_; | 
|  | }; | 
|  |  | 
|  | class LayerTreeHostTestSimpleSwapPromiseMonitor : public LayerTreeHostTest { | 
|  | public: | 
|  | void BeginTest() override { PostSetNeedsCommitToMainThread(); } | 
|  |  | 
|  | void WillBeginMainFrame() override { | 
|  | if (TestEnded()) | 
|  | return; | 
|  |  | 
|  | int set_needs_commit_count = 0; | 
|  | int set_needs_redraw_count = 0; | 
|  |  | 
|  | { | 
|  | scoped_ptr<SimpleSwapPromiseMonitor> swap_promise_monitor( | 
|  | new SimpleSwapPromiseMonitor(layer_tree_host(), | 
|  | NULL, | 
|  | &set_needs_commit_count, | 
|  | &set_needs_redraw_count)); | 
|  | layer_tree_host()->SetNeedsCommit(); | 
|  | EXPECT_EQ(1, set_needs_commit_count); | 
|  | EXPECT_EQ(0, set_needs_redraw_count); | 
|  | } | 
|  |  | 
|  | // Now the monitor is destroyed, SetNeedsCommit() is no longer being | 
|  | // monitored. | 
|  | layer_tree_host()->SetNeedsCommit(); | 
|  | EXPECT_EQ(1, set_needs_commit_count); | 
|  | EXPECT_EQ(0, set_needs_redraw_count); | 
|  |  | 
|  | { | 
|  | scoped_ptr<SimpleSwapPromiseMonitor> swap_promise_monitor( | 
|  | new SimpleSwapPromiseMonitor(layer_tree_host(), | 
|  | NULL, | 
|  | &set_needs_commit_count, | 
|  | &set_needs_redraw_count)); | 
|  | layer_tree_host()->SetNeedsUpdateLayers(); | 
|  | EXPECT_EQ(2, set_needs_commit_count); | 
|  | EXPECT_EQ(0, set_needs_redraw_count); | 
|  | } | 
|  |  | 
|  | { | 
|  | scoped_ptr<SimpleSwapPromiseMonitor> swap_promise_monitor( | 
|  | new SimpleSwapPromiseMonitor(layer_tree_host(), | 
|  | NULL, | 
|  | &set_needs_commit_count, | 
|  | &set_needs_redraw_count)); | 
|  | layer_tree_host()->SetNeedsAnimate(); | 
|  | EXPECT_EQ(3, set_needs_commit_count); | 
|  | EXPECT_EQ(0, set_needs_redraw_count); | 
|  | } | 
|  |  | 
|  | EndTest(); | 
|  | } | 
|  |  | 
|  | void AfterTest() override {} | 
|  | }; | 
|  |  | 
|  | SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestSimpleSwapPromiseMonitor); | 
|  |  | 
|  | class LayerTreeHostTestHighResRequiredAfterEvictingUIResources | 
|  | : public LayerTreeHostTest { | 
|  | protected: | 
|  | void InitializeSettings(LayerTreeSettings* settings) override { | 
|  | settings->impl_side_painting = true; | 
|  | } | 
|  |  | 
|  | void SetupTree() override { | 
|  | LayerTreeHostTest::SetupTree(); | 
|  | ui_resource_ = FakeScopedUIResource::Create(layer_tree_host()); | 
|  | } | 
|  |  | 
|  | void BeginTest() override { PostSetNeedsCommitToMainThread(); } | 
|  |  | 
|  | void DidActivateTreeOnThread(LayerTreeHostImpl* host_impl) override { | 
|  | host_impl->EvictAllUIResources(); | 
|  | // Existence of evicted UI resources will trigger NEW_CONTENT_TAKES_PRIORITY | 
|  | // mode. Active tree should require high-res to draw after entering this | 
|  | // mode to ensure that high-res tiles are also required for a pending tree | 
|  | // to be activated. | 
|  | EXPECT_TRUE(host_impl->RequiresHighResToDraw()); | 
|  | } | 
|  |  | 
|  | void DidCommit() override { | 
|  | int frame = layer_tree_host()->source_frame_number(); | 
|  | switch (frame) { | 
|  | case 1: | 
|  | PostSetNeedsCommitToMainThread(); | 
|  | break; | 
|  | case 2: | 
|  | ui_resource_ = nullptr; | 
|  | EndTest(); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | void AfterTest() override {} | 
|  |  | 
|  | FakeContentLayerClient client_; | 
|  | scoped_ptr<FakeScopedUIResource> ui_resource_; | 
|  | }; | 
|  |  | 
|  | // This test is flaky, see http://crbug.com/386199 | 
|  | // MULTI_THREAD_TEST_F(LayerTreeHostTestHighResRequiredAfterEvictingUIResources) | 
|  |  | 
|  | class LayerTreeHostTestGpuRasterizationDefault : public LayerTreeHostTest { | 
|  | protected: | 
|  | void InitializeSettings(LayerTreeSettings* settings) override { | 
|  | settings->impl_side_painting = true; | 
|  |  | 
|  | EXPECT_FALSE(settings->gpu_rasterization_enabled); | 
|  | EXPECT_FALSE(settings->gpu_rasterization_forced); | 
|  | } | 
|  |  | 
|  | void SetupTree() override { | 
|  | LayerTreeHostTest::SetupTree(); | 
|  |  | 
|  | scoped_refptr<PictureLayer> layer = PictureLayer::Create(&layer_client_); | 
|  | layer->SetBounds(gfx::Size(10, 10)); | 
|  | layer->SetIsDrawable(true); | 
|  | layer_tree_host()->root_layer()->AddChild(layer); | 
|  | } | 
|  |  | 
|  | void BeginTest() override { | 
|  | Layer* root = layer_tree_host()->root_layer(); | 
|  | PictureLayer* layer = static_cast<PictureLayer*>(root->child_at(0)); | 
|  | RecordingSource* recording_source = layer->GetRecordingSourceForTesting(); | 
|  |  | 
|  | // Verify default values. | 
|  | EXPECT_TRUE(root->IsSuitableForGpuRasterization()); | 
|  | EXPECT_TRUE(layer->IsSuitableForGpuRasterization()); | 
|  | EXPECT_TRUE(recording_source->IsSuitableForGpuRasterization()); | 
|  | EXPECT_FALSE(layer_tree_host()->has_gpu_rasterization_trigger()); | 
|  | EXPECT_FALSE(layer_tree_host()->UseGpuRasterization()); | 
|  |  | 
|  | // Setting gpu rasterization trigger does not enable gpu rasterization. | 
|  | layer_tree_host()->SetHasGpuRasterizationTrigger(true); | 
|  | EXPECT_TRUE(layer_tree_host()->has_gpu_rasterization_trigger()); | 
|  | EXPECT_FALSE(layer_tree_host()->UseGpuRasterization()); | 
|  |  | 
|  | PostSetNeedsCommitToMainThread(); | 
|  | } | 
|  |  | 
|  | void CommitCompleteOnThread(LayerTreeHostImpl* host_impl) override { | 
|  | EXPECT_FALSE(host_impl->pending_tree()->use_gpu_rasterization()); | 
|  | EXPECT_FALSE(host_impl->use_gpu_rasterization()); | 
|  | } | 
|  |  | 
|  | void DidActivateTreeOnThread(LayerTreeHostImpl* host_impl) override { | 
|  | EXPECT_FALSE(host_impl->active_tree()->use_gpu_rasterization()); | 
|  | EXPECT_FALSE(host_impl->use_gpu_rasterization()); | 
|  | EndTest(); | 
|  | } | 
|  |  | 
|  | void AfterTest() override {} | 
|  |  | 
|  | FakeContentLayerClient layer_client_; | 
|  | }; | 
|  |  | 
|  | MULTI_THREAD_TEST_F(LayerTreeHostTestGpuRasterizationDefault); | 
|  |  | 
|  | class LayerTreeHostTestGpuRasterizationEnabled : public LayerTreeHostTest { | 
|  | protected: | 
|  | void InitializeSettings(LayerTreeSettings* settings) override { | 
|  | settings->impl_side_painting = true; | 
|  |  | 
|  | EXPECT_FALSE(settings->gpu_rasterization_enabled); | 
|  | settings->gpu_rasterization_enabled = true; | 
|  | } | 
|  |  | 
|  | void SetupTree() override { | 
|  | LayerTreeHostTest::SetupTree(); | 
|  |  | 
|  | scoped_refptr<PictureLayer> layer = PictureLayer::Create(&layer_client_); | 
|  | layer->SetBounds(gfx::Size(10, 10)); | 
|  | layer->SetIsDrawable(true); | 
|  | layer_tree_host()->root_layer()->AddChild(layer); | 
|  | } | 
|  |  | 
|  | void BeginTest() override { | 
|  | Layer* root = layer_tree_host()->root_layer(); | 
|  | PictureLayer* layer = static_cast<PictureLayer*>(root->child_at(0)); | 
|  | RecordingSource* recording_source = layer->GetRecordingSourceForTesting(); | 
|  |  | 
|  | // Verify default values. | 
|  | EXPECT_TRUE(root->IsSuitableForGpuRasterization()); | 
|  | EXPECT_TRUE(layer->IsSuitableForGpuRasterization()); | 
|  | EXPECT_TRUE(recording_source->IsSuitableForGpuRasterization()); | 
|  | EXPECT_FALSE(layer_tree_host()->has_gpu_rasterization_trigger()); | 
|  | EXPECT_FALSE(layer_tree_host()->UseGpuRasterization()); | 
|  |  | 
|  | // Gpu rasterization trigger is relevant. | 
|  | layer_tree_host()->SetHasGpuRasterizationTrigger(true); | 
|  | EXPECT_TRUE(layer_tree_host()->has_gpu_rasterization_trigger()); | 
|  | EXPECT_TRUE(layer_tree_host()->UseGpuRasterization()); | 
|  |  | 
|  | // Content-based veto is relevant as well. | 
|  | recording_source->SetUnsuitableForGpuRasterizationForTesting(); | 
|  | EXPECT_FALSE(recording_source->IsSuitableForGpuRasterization()); | 
|  | EXPECT_FALSE(layer->IsSuitableForGpuRasterization()); | 
|  | // Veto will take effect when layers are updated. | 
|  | // The results will be verified after commit is completed below. | 
|  | // Since we are manually marking picture pile as unsuitable, | 
|  | // make sure that the layer gets a chance to update. | 
|  | layer->SetNeedsDisplay(); | 
|  | PostSetNeedsCommitToMainThread(); | 
|  | } | 
|  |  | 
|  | void CommitCompleteOnThread(LayerTreeHostImpl* host_impl) override { | 
|  | EXPECT_FALSE(host_impl->pending_tree()->use_gpu_rasterization()); | 
|  | EXPECT_FALSE(host_impl->use_gpu_rasterization()); | 
|  | } | 
|  |  | 
|  | void DidActivateTreeOnThread(LayerTreeHostImpl* host_impl) override { | 
|  | EXPECT_FALSE(host_impl->active_tree()->use_gpu_rasterization()); | 
|  | EXPECT_FALSE(host_impl->use_gpu_rasterization()); | 
|  | EndTest(); | 
|  | } | 
|  |  | 
|  | void AfterTest() override {} | 
|  |  | 
|  | FakeContentLayerClient layer_client_; | 
|  | }; | 
|  |  | 
|  | MULTI_THREAD_TEST_F(LayerTreeHostTestGpuRasterizationEnabled); | 
|  |  | 
|  | class LayerTreeHostTestGpuRasterizationForced : public LayerTreeHostTest { | 
|  | protected: | 
|  | void InitializeSettings(LayerTreeSettings* settings) override { | 
|  | ASSERT_TRUE(settings->impl_side_painting); | 
|  |  | 
|  | EXPECT_FALSE(settings->gpu_rasterization_forced); | 
|  | settings->gpu_rasterization_forced = true; | 
|  | } | 
|  |  | 
|  | void SetupTree() override { | 
|  | LayerTreeHostTest::SetupTree(); | 
|  |  | 
|  | scoped_refptr<FakePictureLayer> layer = | 
|  | FakePictureLayer::Create(&layer_client_); | 
|  | layer->SetBounds(gfx::Size(10, 10)); | 
|  | layer->SetIsDrawable(true); | 
|  | layer_tree_host()->root_layer()->AddChild(layer); | 
|  | } | 
|  |  | 
|  | void BeginTest() override { | 
|  | Layer* root = layer_tree_host()->root_layer(); | 
|  | PictureLayer* layer = static_cast<PictureLayer*>(root->child_at(0)); | 
|  | RecordingSource* recording_source = layer->GetRecordingSourceForTesting(); | 
|  |  | 
|  | // Verify default values. | 
|  | EXPECT_TRUE(root->IsSuitableForGpuRasterization()); | 
|  | EXPECT_TRUE(layer->IsSuitableForGpuRasterization()); | 
|  | EXPECT_TRUE(recording_source->IsSuitableForGpuRasterization()); | 
|  | EXPECT_FALSE(layer_tree_host()->has_gpu_rasterization_trigger()); | 
|  |  | 
|  | // With gpu rasterization forced, gpu rasterization trigger is irrelevant. | 
|  | EXPECT_TRUE(layer_tree_host()->UseGpuRasterization()); | 
|  | layer_tree_host()->SetHasGpuRasterizationTrigger(true); | 
|  | EXPECT_TRUE(layer_tree_host()->has_gpu_rasterization_trigger()); | 
|  | EXPECT_TRUE(layer_tree_host()->UseGpuRasterization()); | 
|  |  | 
|  | // Content-based veto is irrelevant as well. | 
|  | recording_source->SetUnsuitableForGpuRasterizationForTesting(); | 
|  | EXPECT_FALSE(recording_source->IsSuitableForGpuRasterization()); | 
|  | EXPECT_FALSE(layer->IsSuitableForGpuRasterization()); | 
|  | // Veto will take effect when layers are updated. | 
|  | // The results will be verified after commit is completed below. | 
|  | // Since we are manually marking picture pile as unsuitable, | 
|  | // make sure that the layer gets a chance to update. | 
|  | layer->SetNeedsDisplay(); | 
|  | PostSetNeedsCommitToMainThread(); | 
|  | } | 
|  |  | 
|  | void CommitCompleteOnThread(LayerTreeHostImpl* host_impl) override { | 
|  | EXPECT_TRUE(host_impl->pending_tree()->use_gpu_rasterization()); | 
|  | EXPECT_TRUE(host_impl->use_gpu_rasterization()); | 
|  | } | 
|  |  | 
|  | void DidActivateTreeOnThread(LayerTreeHostImpl* host_impl) override { | 
|  | EXPECT_TRUE(host_impl->active_tree()->use_gpu_rasterization()); | 
|  | EXPECT_TRUE(host_impl->use_gpu_rasterization()); | 
|  | EndTest(); | 
|  | } | 
|  |  | 
|  | void AfterTest() override {} | 
|  |  | 
|  | FakeContentLayerClient layer_client_; | 
|  | }; | 
|  |  | 
|  | SINGLE_AND_MULTI_THREAD_IMPL_TEST_F(LayerTreeHostTestGpuRasterizationForced); | 
|  |  | 
|  | class LayerTreeHostTestContinuousPainting : public LayerTreeHostTest { | 
|  | public: | 
|  | LayerTreeHostTestContinuousPainting() | 
|  | : num_commits_(0), num_draws_(0), bounds_(20, 20), child_layer_(NULL) {} | 
|  |  | 
|  | protected: | 
|  | enum { kExpectedNumCommits = 10 }; | 
|  |  | 
|  | void SetupTree() override { | 
|  | scoped_refptr<Layer> root_layer = Layer::Create(); | 
|  | root_layer->SetBounds(bounds_); | 
|  | root_layer->CreateRenderSurface(); | 
|  |  | 
|  | if (layer_tree_host()->settings().impl_side_painting) { | 
|  | picture_layer_ = FakePictureLayer::Create(&client_); | 
|  | child_layer_ = picture_layer_.get(); | 
|  | } else { | 
|  | content_layer_ = ContentLayerWithUpdateTracking::Create(&client_); | 
|  | child_layer_ = content_layer_.get(); | 
|  | } | 
|  | child_layer_->SetBounds(bounds_); | 
|  | child_layer_->SetIsDrawable(true); | 
|  | root_layer->AddChild(child_layer_); | 
|  |  | 
|  | layer_tree_host()->SetRootLayer(root_layer); | 
|  | layer_tree_host()->SetViewportSize(bounds_); | 
|  | LayerTreeHostTest::SetupTree(); | 
|  | } | 
|  |  | 
|  | void BeginTest() override { | 
|  | MainThreadTaskRunner()->PostTask( | 
|  | FROM_HERE, | 
|  | base::Bind( | 
|  | &LayerTreeHostTestContinuousPainting::EnableContinuousPainting, | 
|  | base::Unretained(this))); | 
|  | // Wait 50x longer than expected. | 
|  | double milliseconds_per_frame = | 
|  | 1000.0 / layer_tree_host()->settings().renderer_settings.refresh_rate; | 
|  | MainThreadTaskRunner()->PostDelayedTask( | 
|  | FROM_HERE, | 
|  | base::Bind( | 
|  | &LayerTreeHostTestContinuousPainting::DisableContinuousPainting, | 
|  | base::Unretained(this)), | 
|  | base::TimeDelta::FromMilliseconds(50 * kExpectedNumCommits * | 
|  | milliseconds_per_frame)); | 
|  | } | 
|  |  | 
|  | void BeginMainFrame(const BeginFrameArgs& args) override { | 
|  | child_layer_->SetNeedsDisplay(); | 
|  | } | 
|  |  | 
|  | void AfterTest() override { | 
|  | EXPECT_LE(kExpectedNumCommits, num_commits_); | 
|  | EXPECT_LE(kExpectedNumCommits, num_draws_); | 
|  | int update_count = content_layer_.get() | 
|  | ? content_layer_->PaintContentsCount() | 
|  | : picture_layer_->update_count(); | 
|  | EXPECT_LE(kExpectedNumCommits, update_count); | 
|  | } | 
|  |  | 
|  | void DrawLayersOnThread(LayerTreeHostImpl* impl) override { | 
|  | if (++num_draws_ == kExpectedNumCommits) | 
|  | EndTest(); | 
|  | } | 
|  |  | 
|  | void CommitCompleteOnThread(LayerTreeHostImpl* impl) override { | 
|  | ++num_commits_; | 
|  | } | 
|  |  | 
|  | private: | 
|  | void EnableContinuousPainting() { | 
|  | LayerTreeDebugState debug_state = layer_tree_host()->debug_state(); | 
|  | debug_state.continuous_painting = true; | 
|  | layer_tree_host()->SetDebugState(debug_state); | 
|  | } | 
|  |  | 
|  | void DisableContinuousPainting() { | 
|  | LayerTreeDebugState debug_state = layer_tree_host()->debug_state(); | 
|  | debug_state.continuous_painting = false; | 
|  | layer_tree_host()->SetDebugState(debug_state); | 
|  | EndTest(); | 
|  | } | 
|  |  | 
|  | int num_commits_; | 
|  | int num_draws_; | 
|  | const gfx::Size bounds_; | 
|  | FakeContentLayerClient client_; | 
|  | scoped_refptr<ContentLayerWithUpdateTracking> content_layer_; | 
|  | scoped_refptr<FakePictureLayer> picture_layer_; | 
|  | Layer* child_layer_; | 
|  | }; | 
|  |  | 
|  | MULTI_THREAD_TEST_F(LayerTreeHostTestContinuousPainting); | 
|  |  | 
|  | class LayerTreeHostTestSendBeginFramesToChildren : public LayerTreeHostTest { | 
|  | public: | 
|  | LayerTreeHostTestSendBeginFramesToChildren() | 
|  | : begin_frame_sent_to_children_(false) { | 
|  | } | 
|  |  | 
|  | void InitializeSettings(LayerTreeSettings* settings) override { | 
|  | settings->forward_begin_frames_to_children = true; | 
|  | } | 
|  |  | 
|  | void BeginTest() override { | 
|  | // Kick off the test with a commit. | 
|  | PostSetNeedsCommitToMainThread(); | 
|  | } | 
|  |  | 
|  | void SendBeginFramesToChildren(const BeginFrameArgs& args) override { | 
|  | begin_frame_sent_to_children_ = true; | 
|  | EndTest(); | 
|  | } | 
|  |  | 
|  | void DidBeginMainFrame() override { | 
|  | // Children requested BeginFrames. | 
|  | layer_tree_host()->SetChildrenNeedBeginFrames(true); | 
|  | } | 
|  |  | 
|  | void AfterTest() override { | 
|  | // Ensure that BeginFrame message is sent to children during parent | 
|  | // scheduler handles its BeginFrame. | 
|  | EXPECT_TRUE(begin_frame_sent_to_children_); | 
|  | } | 
|  |  | 
|  | private: | 
|  | bool begin_frame_sent_to_children_; | 
|  | }; | 
|  |  | 
|  | SINGLE_THREAD_TEST_F(LayerTreeHostTestSendBeginFramesToChildren); | 
|  |  | 
|  | class LayerTreeHostTestSendBeginFramesToChildrenWithExternalBFS | 
|  | : public LayerTreeHostTest { | 
|  | public: | 
|  | LayerTreeHostTestSendBeginFramesToChildrenWithExternalBFS() | 
|  | : begin_frame_sent_to_children_(false) { | 
|  | } | 
|  |  | 
|  | void InitializeSettings(LayerTreeSettings* settings) override { | 
|  | settings->use_external_begin_frame_source = true; | 
|  | settings->forward_begin_frames_to_children = true; | 
|  | } | 
|  |  | 
|  | void BeginTest() override { | 
|  | // Kick off the test with a commit. | 
|  | PostSetNeedsCommitToMainThread(); | 
|  | } | 
|  |  | 
|  | void SendBeginFramesToChildren(const BeginFrameArgs& args) override { | 
|  | begin_frame_sent_to_children_ = true; | 
|  | EndTest(); | 
|  | } | 
|  |  | 
|  | void DidBeginMainFrame() override { | 
|  | // Children requested BeginFrames. | 
|  | layer_tree_host()->SetChildrenNeedBeginFrames(true); | 
|  | } | 
|  |  | 
|  | void AfterTest() override { | 
|  | // Ensure that BeginFrame message is sent to children during parent | 
|  | // scheduler handles its BeginFrame. | 
|  | EXPECT_TRUE(begin_frame_sent_to_children_); | 
|  | } | 
|  |  | 
|  | private: | 
|  | bool begin_frame_sent_to_children_; | 
|  | }; | 
|  |  | 
|  | SINGLE_THREAD_TEST_F(LayerTreeHostTestSendBeginFramesToChildrenWithExternalBFS); | 
|  |  | 
|  | class LayerTreeHostTestActivateOnInvisible : public LayerTreeHostTest { | 
|  | public: | 
|  | LayerTreeHostTestActivateOnInvisible() | 
|  | : activation_count_(0), visible_(true) {} | 
|  |  | 
|  | void InitializeSettings(LayerTreeSettings* settings) override { | 
|  | settings->impl_side_painting = true; | 
|  | } | 
|  |  | 
|  | void BeginTest() override { | 
|  | // Kick off the test with a commit. | 
|  | PostSetNeedsCommitToMainThread(); | 
|  | } | 
|  |  | 
|  | void BeginCommitOnThread(LayerTreeHostImpl* host_impl) override { | 
|  | // Make sure we don't activate using the notify signal from tile manager. | 
|  | host_impl->BlockNotifyReadyToActivateForTesting(true); | 
|  | } | 
|  |  | 
|  | void DidCommit() override { layer_tree_host()->SetVisible(false); } | 
|  |  | 
|  | void DidSetVisibleOnImplTree(LayerTreeHostImpl* host_impl, | 
|  | bool visible) override { | 
|  | visible_ = visible; | 
|  |  | 
|  | // Once invisible, we can go visible again. | 
|  | if (!visible) { | 
|  | PostSetVisibleToMainThread(true); | 
|  | } else { | 
|  | EXPECT_TRUE(host_impl->RequiresHighResToDraw()); | 
|  | EndTest(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void DidActivateTreeOnThread(LayerTreeHostImpl* host_impl) override { | 
|  | ++activation_count_; | 
|  | EXPECT_FALSE(visible_); | 
|  | } | 
|  |  | 
|  | void AfterTest() override { | 
|  | // Ensure we activated even though the signal was blocked. | 
|  | EXPECT_EQ(1, activation_count_); | 
|  | EXPECT_TRUE(visible_); | 
|  | } | 
|  |  | 
|  | private: | 
|  | int activation_count_; | 
|  | bool visible_; | 
|  |  | 
|  | FakeContentLayerClient client_; | 
|  | scoped_refptr<FakePictureLayer> picture_layer_; | 
|  | }; | 
|  |  | 
|  | // TODO(vmpstr): Enable with single thread impl-side painting. | 
|  | MULTI_THREAD_TEST_F(LayerTreeHostTestActivateOnInvisible); | 
|  |  | 
|  | // Do a synchronous composite and assert that the swap promise succeeds. | 
|  | class LayerTreeHostTestSynchronousCompositeSwapPromise | 
|  | : public LayerTreeHostTest { | 
|  | public: | 
|  | LayerTreeHostTestSynchronousCompositeSwapPromise() : commit_count_(0) {} | 
|  |  | 
|  | void InitializeSettings(LayerTreeSettings* settings) override { | 
|  | settings->single_thread_proxy_scheduler = false; | 
|  | } | 
|  |  | 
|  | void BeginTest() override { | 
|  | // Successful composite. | 
|  | scoped_ptr<SwapPromise> swap_promise0( | 
|  | new TestSwapPromise(&swap_promise_result_[0])); | 
|  | layer_tree_host()->QueueSwapPromise(swap_promise0.Pass()); | 
|  | layer_tree_host()->Composite(gfx::FrameTime::Now()); | 
|  |  | 
|  | // Fail to swap (no damage). | 
|  | scoped_ptr<SwapPromise> swap_promise1( | 
|  | new TestSwapPromise(&swap_promise_result_[1])); | 
|  | layer_tree_host()->QueueSwapPromise(swap_promise1.Pass()); | 
|  | layer_tree_host()->SetNeedsCommit(); | 
|  | layer_tree_host()->Composite(gfx::FrameTime::Now()); | 
|  |  | 
|  | // Fail to draw (not visible). | 
|  | scoped_ptr<SwapPromise> swap_promise2( | 
|  | new TestSwapPromise(&swap_promise_result_[2])); | 
|  | layer_tree_host()->QueueSwapPromise(swap_promise2.Pass()); | 
|  | layer_tree_host()->SetNeedsDisplayOnAllLayers(); | 
|  | layer_tree_host()->SetVisible(false); | 
|  | layer_tree_host()->Composite(gfx::FrameTime::Now()); | 
|  |  | 
|  | EndTest(); | 
|  | } | 
|  |  | 
|  | void DidCommit() override { | 
|  | commit_count_++; | 
|  | ASSERT_LE(commit_count_, 3); | 
|  | } | 
|  |  | 
|  | void AfterTest() override { | 
|  | EXPECT_EQ(3, commit_count_); | 
|  |  | 
|  | // Initial swap promise should have succeded. | 
|  | { | 
|  | base::AutoLock lock(swap_promise_result_[0].lock); | 
|  | EXPECT_TRUE(swap_promise_result_[0].did_swap_called); | 
|  | EXPECT_FALSE(swap_promise_result_[0].did_not_swap_called); | 
|  | EXPECT_TRUE(swap_promise_result_[0].dtor_called); | 
|  | } | 
|  |  | 
|  | // Second swap promise fails to swap. | 
|  | { | 
|  | base::AutoLock lock(swap_promise_result_[1].lock); | 
|  | EXPECT_FALSE(swap_promise_result_[1].did_swap_called); | 
|  | EXPECT_TRUE(swap_promise_result_[1].did_not_swap_called); | 
|  | EXPECT_EQ(SwapPromise::SWAP_FAILS, swap_promise_result_[1].reason); | 
|  | EXPECT_TRUE(swap_promise_result_[1].dtor_called); | 
|  | } | 
|  |  | 
|  | // Third swap promises also fails to swap (and draw). | 
|  | { | 
|  | base::AutoLock lock(swap_promise_result_[2].lock); | 
|  | EXPECT_FALSE(swap_promise_result_[2].did_swap_called); | 
|  | EXPECT_TRUE(swap_promise_result_[2].did_not_swap_called); | 
|  | EXPECT_EQ(SwapPromise::SWAP_FAILS, swap_promise_result_[2].reason); | 
|  | EXPECT_TRUE(swap_promise_result_[2].dtor_called); | 
|  | } | 
|  | } | 
|  |  | 
|  | int commit_count_; | 
|  | TestSwapPromiseResult swap_promise_result_[3]; | 
|  | }; | 
|  |  | 
|  | // Impl-side painting is not supported for synchronous compositing. | 
|  | SINGLE_THREAD_NOIMPL_TEST_F(LayerTreeHostTestSynchronousCompositeSwapPromise); | 
|  |  | 
|  | // Make sure page scale and top control deltas are applied to the client even | 
|  | // when the LayerTreeHost doesn't have a root layer. | 
|  | class LayerTreeHostAcceptsDeltasFromImplWithoutRootLayer | 
|  | : public LayerTreeHostTest { | 
|  | public: | 
|  | LayerTreeHostAcceptsDeltasFromImplWithoutRootLayer() | 
|  | : deltas_sent_to_client_(false) {} | 
|  |  | 
|  | void BeginTest() override { | 
|  | layer_tree_host()->SetRootLayer(nullptr); | 
|  | info_.page_scale_delta = 3.14f; | 
|  | info_.top_controls_delta = 2.73f; | 
|  |  | 
|  | PostSetNeedsCommitToMainThread(); | 
|  | } | 
|  |  | 
|  | void BeginMainFrame(const BeginFrameArgs& args) override { | 
|  | EXPECT_EQ(nullptr, layer_tree_host()->root_layer()); | 
|  |  | 
|  | layer_tree_host()->ApplyScrollAndScale(&info_); | 
|  | EndTest(); | 
|  | } | 
|  |  | 
|  | void ApplyViewportDeltas(const gfx::Vector2d& inner, | 
|  | const gfx::Vector2d& outer, | 
|  | const gfx::Vector2dF& elastic_overscroll_delta, | 
|  | float scale_delta, | 
|  | float top_controls_delta) override { | 
|  | EXPECT_EQ(info_.page_scale_delta, scale_delta); | 
|  | EXPECT_EQ(info_.top_controls_delta, top_controls_delta); | 
|  | deltas_sent_to_client_ = true; | 
|  | } | 
|  |  | 
|  | void ApplyViewportDeltas( | 
|  | const gfx::Vector2d& scroll, | 
|  | float scale_delta, | 
|  | float top_controls_delta) override { | 
|  | EXPECT_EQ(info_.page_scale_delta, scale_delta); | 
|  | EXPECT_EQ(info_.top_controls_delta, top_controls_delta); | 
|  | deltas_sent_to_client_ = true; | 
|  | } | 
|  |  | 
|  | void AfterTest() override { | 
|  | EXPECT_TRUE(deltas_sent_to_client_); | 
|  | } | 
|  |  | 
|  | ScrollAndScaleSet info_; | 
|  | bool deltas_sent_to_client_; | 
|  | }; | 
|  |  | 
|  | MULTI_THREAD_TEST_F(LayerTreeHostAcceptsDeltasFromImplWithoutRootLayer); | 
|  |  | 
|  | class LayerTreeHostTestCrispUpAfterPinchEnds : public LayerTreeHostTest { | 
|  | protected: | 
|  | LayerTreeHostTestCrispUpAfterPinchEnds() | 
|  | : playback_allowed_event_(true, true) {} | 
|  |  | 
|  | void SetupTree() override { | 
|  | frame_ = 1; | 
|  | posted_ = false; | 
|  | client_.set_fill_with_nonsolid_color(true); | 
|  |  | 
|  | scoped_refptr<Layer> root = Layer::Create(); | 
|  | root->SetBounds(gfx::Size(500, 500)); | 
|  |  | 
|  | scoped_refptr<Layer> pinch = Layer::Create(); | 
|  | pinch->SetBounds(gfx::Size(500, 500)); | 
|  | pinch->SetScrollClipLayerId(root->id()); | 
|  | pinch->SetIsContainerForFixedPositionLayers(true); | 
|  | root->AddChild(pinch); | 
|  |  | 
|  | scoped_ptr<FakePicturePile> pile(new FakePicturePile); | 
|  | pile->SetPlaybackAllowedEvent(&playback_allowed_event_); | 
|  | scoped_refptr<FakePictureLayer> layer = | 
|  | FakePictureLayer::CreateWithRecordingSource(&client_, pile.Pass()); | 
|  | layer->SetBounds(gfx::Size(500, 500)); | 
|  | layer->SetContentsOpaque(true); | 
|  | // Avoid LCD text on the layer so we don't cause extra commits when we | 
|  | // pinch. | 
|  | layer->disable_lcd_text(); | 
|  | pinch->AddChild(layer); | 
|  |  | 
|  | layer_tree_host()->RegisterViewportLayers(NULL, root, pinch, pinch); | 
|  | layer_tree_host()->SetPageScaleFactorAndLimits(1.f, 1.f, 4.f); | 
|  | layer_tree_host()->SetRootLayer(root); | 
|  | LayerTreeHostTest::SetupTree(); | 
|  | } | 
|  |  | 
|  | // Returns the delta scale of all quads in the frame's root pass from their | 
|  | // ideal, or 0 if they are not all the same. | 
|  | float FrameQuadScaleDeltaFromIdeal(LayerTreeHostImpl::FrameData* frame_data) { | 
|  | if (frame_data->has_no_damage) | 
|  | return 0.f; | 
|  | float frame_scale = 0.f; | 
|  | RenderPass* root_pass = frame_data->render_passes.back(); | 
|  | for (const auto& draw_quad : root_pass->quad_list) { | 
|  | // Checkerboards mean an incomplete frame. | 
|  | if (draw_quad->material != DrawQuad::TILED_CONTENT) | 
|  | return 0.f; | 
|  | const TileDrawQuad* quad = TileDrawQuad::MaterialCast(draw_quad); | 
|  | float quad_scale = | 
|  | quad->tex_coord_rect.width() / static_cast<float>(quad->rect.width()); | 
|  | float transform_scale = | 
|  | SkMScalarToFloat(quad->quadTransform().matrix().get(0, 0)); | 
|  | float scale = quad_scale / transform_scale; | 
|  | if (frame_scale != 0.f && frame_scale != scale) | 
|  | return 0.f; | 
|  | frame_scale = scale; | 
|  | } | 
|  | return frame_scale; | 
|  | } | 
|  |  | 
|  | void BeginTest() override { PostSetNeedsCommitToMainThread(); } | 
|  |  | 
|  | DrawResult PrepareToDrawOnThread(LayerTreeHostImpl* host_impl, | 
|  | LayerTreeHostImpl::FrameData* frame_data, | 
|  | DrawResult draw_result) override { | 
|  | float quad_scale_delta = FrameQuadScaleDeltaFromIdeal(frame_data); | 
|  | switch (frame_) { | 
|  | case 1: | 
|  | // Drew at page scale 1 before any pinching. | 
|  | EXPECT_EQ(1.f, host_impl->active_tree()->current_page_scale_factor()); | 
|  | EXPECT_EQ(1.f, quad_scale_delta); | 
|  | PostNextAfterDraw(host_impl); | 
|  | break; | 
|  | case 2: | 
|  | if (quad_scale_delta != 1.f) | 
|  | break; | 
|  | // Drew at page scale 1.5 after pinching in. | 
|  | EXPECT_EQ(1.5f, host_impl->active_tree()->current_page_scale_factor()); | 
|  | EXPECT_EQ(1.f, quad_scale_delta); | 
|  | PostNextAfterDraw(host_impl); | 
|  | break; | 
|  | case 3: | 
|  | // By pinching out, we will create a new tiling and raster it. This may | 
|  | // cause some additional draws, though we should still be drawing with | 
|  | // the old 1.5 tiling. | 
|  | if (frame_data->has_no_damage) | 
|  | break; | 
|  | // Drew at page scale 1 with the 1.5 tiling while pinching out. | 
|  | EXPECT_EQ(1.f, host_impl->active_tree()->current_page_scale_factor()); | 
|  | EXPECT_EQ(1.5f, quad_scale_delta); | 
|  | // We don't PostNextAfterDraw here, instead we wait for the new tiling | 
|  | // to finish rastering so we don't get any noise in further steps. | 
|  | break; | 
|  | case 4: | 
|  | // Drew at page scale 1 with the 1.5 tiling after pinching out completed | 
|  | // while waiting for texture uploads to complete. | 
|  | EXPECT_EQ(1.f, host_impl->active_tree()->current_page_scale_factor()); | 
|  | // This frame will not have any damage, since it's actually the same as | 
|  | // the last frame, and should contain no incomplete tiles. We just want | 
|  | // to make sure we drew here at least once after the pinch ended to be | 
|  | // sure that drawing after pinch doesn't leave us at the wrong scale | 
|  | EXPECT_TRUE(frame_data->has_no_damage); | 
|  | PostNextAfterDraw(host_impl); | 
|  | break; | 
|  | case 5: | 
|  | if (quad_scale_delta != 1.f) | 
|  | break; | 
|  | // Drew at scale 1 after texture uploads are done. | 
|  | EXPECT_EQ(1.f, host_impl->active_tree()->current_page_scale_factor()); | 
|  | EXPECT_EQ(1.f, quad_scale_delta); | 
|  | EndTest(); | 
|  | break; | 
|  | } | 
|  | return draw_result; | 
|  | } | 
|  |  | 
|  | void PostNextAfterDraw(LayerTreeHostImpl* host_impl) { | 
|  | if (posted_) | 
|  | return; | 
|  | posted_ = true; | 
|  | ImplThreadTaskRunner()->PostDelayedTask( | 
|  | FROM_HERE, base::Bind(&LayerTreeHostTestCrispUpAfterPinchEnds::Next, | 
|  | base::Unretained(this), host_impl), | 
|  | // Use a delay to allow raster/upload to happen in between frames. This | 
|  | // should cause flakiness if we fail to block raster/upload when | 
|  | // desired. | 
|  | base::TimeDelta::FromMilliseconds(16 * 4)); | 
|  | } | 
|  |  | 
|  | void Next(LayerTreeHostImpl* host_impl) { | 
|  | ++frame_; | 
|  | posted_ = false; | 
|  | switch (frame_) { | 
|  | case 2: | 
|  | // Pinch zoom in. | 
|  | host_impl->PinchGestureBegin(); | 
|  | host_impl->PinchGestureUpdate(1.5f, gfx::Point(100, 100)); | 
|  | host_impl->PinchGestureEnd(); | 
|  | break; | 
|  | case 3: | 
|  | // Pinch zoom back to 1.f but don't end it. | 
|  | host_impl->PinchGestureBegin(); | 
|  | host_impl->PinchGestureUpdate(1.f / 1.5f, gfx::Point(100, 100)); | 
|  | break; | 
|  | case 4: | 
|  | // End the pinch, but delay tile production. | 
|  | playback_allowed_event_.Reset(); | 
|  | host_impl->PinchGestureEnd(); | 
|  | break; | 
|  | case 5: | 
|  | // Let tiles complete. | 
|  | playback_allowed_event_.Signal(); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | void NotifyTileStateChangedOnThread(LayerTreeHostImpl* host_impl, | 
|  | const Tile* tile) override { | 
|  | if (frame_ == 3) { | 
|  | // On frame 3, we will have a lower res tile complete for the pinch-out | 
|  | // gesture even though it's not displayed. We wait for it here to prevent | 
|  | // flakiness. | 
|  | EXPECT_EQ(0.75f, tile->contents_scale()); | 
|  | PostNextAfterDraw(host_impl); | 
|  | } | 
|  | // On frame_ == 4, we are preventing texture uploads from completing, | 
|  | // so this verifies they are not completing before frame_ == 5. | 
|  | // Flaky failures here indicate we're failing to prevent uploads from | 
|  | // completing. | 
|  | EXPECT_NE(4, frame_) << tile->contents_scale(); | 
|  | } | 
|  |  | 
|  | void AfterTest() override {} | 
|  |  | 
|  | FakeContentLayerClient client_; | 
|  | int frame_; | 
|  | bool posted_; | 
|  | base::WaitableEvent playback_allowed_event_; | 
|  | }; | 
|  |  | 
|  | MULTI_THREAD_IMPL_TEST_F(LayerTreeHostTestCrispUpAfterPinchEnds); | 
|  |  | 
|  | class LayerTreeHostTestCrispUpAfterPinchEndsWithOneCopy | 
|  | : public LayerTreeHostTestCrispUpAfterPinchEnds { | 
|  | protected: | 
|  | void InitializeSettings(LayerTreeSettings* settings) override { | 
|  | settings->use_one_copy = true; | 
|  | } | 
|  |  | 
|  | scoped_ptr<FakeOutputSurface> CreateFakeOutputSurface() override { | 
|  | scoped_ptr<TestWebGraphicsContext3D> context3d = | 
|  | TestWebGraphicsContext3D::Create(); | 
|  | context3d->set_support_image(true); | 
|  | context3d->set_support_sync_query(true); | 
|  | #if defined(OS_MACOSX) | 
|  | context3d->set_support_texture_rectangle(true); | 
|  | #endif | 
|  |  | 
|  | if (delegating_renderer()) | 
|  | return FakeOutputSurface::CreateDelegating3d(context3d.Pass()); | 
|  | else | 
|  | return FakeOutputSurface::Create3d(context3d.Pass()); | 
|  | } | 
|  | }; | 
|  |  | 
|  | MULTI_THREAD_IMPL_TEST_F(LayerTreeHostTestCrispUpAfterPinchEndsWithOneCopy); | 
|  |  | 
|  | class RasterizeWithGpuRasterizationCreatesResources : public LayerTreeHostTest { | 
|  | protected: | 
|  | RasterizeWithGpuRasterizationCreatesResources() {} | 
|  |  | 
|  | void InitializeSettings(LayerTreeSettings* settings) override { | 
|  | settings->impl_side_painting = true; | 
|  | settings->gpu_rasterization_forced = true; | 
|  | } | 
|  |  | 
|  | void SetupTree() override { | 
|  | client_.set_fill_with_nonsolid_color(true); | 
|  |  | 
|  | scoped_refptr<Layer> root = Layer::Create(); | 
|  | root->SetBounds(gfx::Size(500, 500)); | 
|  |  | 
|  | scoped_ptr<FakePicturePile> pile(new FakePicturePile); | 
|  | scoped_refptr<FakePictureLayer> layer = | 
|  | FakePictureLayer::CreateWithRecordingSource(&client_, pile.Pass()); | 
|  | layer->SetBounds(gfx::Size(500, 500)); | 
|  | layer->SetContentsOpaque(true); | 
|  | root->AddChild(layer); | 
|  |  | 
|  | layer_tree_host()->SetRootLayer(root); | 
|  | LayerTreeHostTest::SetupTree(); | 
|  | } | 
|  |  | 
|  | void BeginTest() override { PostSetNeedsCommitToMainThread(); } | 
|  |  | 
|  | DrawResult PrepareToDrawOnThread(LayerTreeHostImpl* host_impl, | 
|  | LayerTreeHostImpl::FrameData* frame_data, | 
|  | DrawResult draw_result) override { | 
|  | EXPECT_NE(0u, host_impl->resource_provider()->num_resources()); | 
|  | EndTest(); | 
|  | return draw_result; | 
|  | } | 
|  | void AfterTest() override {} | 
|  |  | 
|  | FakeContentLayerClient client_; | 
|  | }; | 
|  |  | 
|  | MULTI_THREAD_IMPL_TEST_F(RasterizeWithGpuRasterizationCreatesResources); | 
|  |  | 
|  | class LayerTreeHostTestContinuousDrawWhenCreatingVisibleTiles | 
|  | : public LayerTreeHostTest { | 
|  | protected: | 
|  | LayerTreeHostTestContinuousDrawWhenCreatingVisibleTiles() | 
|  | : playback_allowed_event_(true, true) {} | 
|  |  | 
|  | void InitializeSettings(LayerTreeSettings* settings) override { | 
|  | settings->impl_side_painting = true; | 
|  | } | 
|  |  | 
|  | void SetupTree() override { | 
|  | step_ = 1; | 
|  | continuous_draws_ = 0; | 
|  | client_.set_fill_with_nonsolid_color(true); | 
|  |  | 
|  | scoped_refptr<Layer> root = Layer::Create(); | 
|  | root->SetBounds(gfx::Size(500, 500)); | 
|  |  | 
|  | scoped_refptr<Layer> pinch = Layer::Create(); | 
|  | pinch->SetBounds(gfx::Size(500, 500)); | 
|  | pinch->SetScrollClipLayerId(root->id()); | 
|  | pinch->SetIsContainerForFixedPositionLayers(true); | 
|  | root->AddChild(pinch); | 
|  |  | 
|  | scoped_ptr<FakePicturePile> pile(new FakePicturePile); | 
|  | pile->SetPlaybackAllowedEvent(&playback_allowed_event_); | 
|  | scoped_refptr<FakePictureLayer> layer = | 
|  | FakePictureLayer::CreateWithRecordingSource(&client_, pile.Pass()); | 
|  | layer->SetBounds(gfx::Size(500, 500)); | 
|  | layer->SetContentsOpaque(true); | 
|  | // Avoid LCD text on the layer so we don't cause extra commits when we | 
|  | // pinch. | 
|  | layer->disable_lcd_text(); | 
|  | pinch->AddChild(layer); | 
|  |  | 
|  | layer_tree_host()->RegisterViewportLayers(NULL, root, pinch, pinch); | 
|  | layer_tree_host()->SetPageScaleFactorAndLimits(1.f, 1.f, 4.f); | 
|  | layer_tree_host()->SetRootLayer(root); | 
|  | LayerTreeHostTest::SetupTree(); | 
|  | } | 
|  |  | 
|  | // Returns the delta scale of all quads in the frame's root pass from their | 
|  | // ideal, or 0 if they are not all the same. | 
|  | float FrameQuadScaleDeltaFromIdeal(LayerTreeHostImpl::FrameData* frame_data) { | 
|  | if (frame_data->has_no_damage) | 
|  | return 0.f; | 
|  | float frame_scale = 0.f; | 
|  | RenderPass* root_pass = frame_data->render_passes.back(); | 
|  | for (const auto& draw_quad : root_pass->quad_list) { | 
|  | const TileDrawQuad* quad = TileDrawQuad::MaterialCast(draw_quad); | 
|  | float quad_scale = | 
|  | quad->tex_coord_rect.width() / static_cast<float>(quad->rect.width()); | 
|  | float transform_scale = | 
|  | SkMScalarToFloat(quad->quadTransform().matrix().get(0, 0)); | 
|  | float scale = quad_scale / transform_scale; | 
|  | if (frame_scale != 0.f && frame_scale != scale) | 
|  | return 0.f; | 
|  | frame_scale = scale; | 
|  | } | 
|  | return frame_scale; | 
|  | } | 
|  |  | 
|  | void BeginTest() override { PostSetNeedsCommitToMainThread(); } | 
|  |  | 
|  | DrawResult PrepareToDrawOnThread(LayerTreeHostImpl* host_impl, | 
|  | LayerTreeHostImpl::FrameData* frame_data, | 
|  | DrawResult draw_result) override { | 
|  | float quad_scale_delta = FrameQuadScaleDeltaFromIdeal(frame_data); | 
|  | switch (step_) { | 
|  | case 1: | 
|  | // Drew at scale 1 before any pinching. | 
|  | EXPECT_EQ(1.f, host_impl->active_tree()->current_page_scale_factor()); | 
|  | EXPECT_EQ(1.f, quad_scale_delta); | 
|  | break; | 
|  | case 2: | 
|  | if (quad_scale_delta != 1.f / 1.5f) | 
|  | break; | 
|  | // Drew at scale 1 still though the ideal is 1.5. | 
|  | EXPECT_EQ(1.5f, host_impl->active_tree()->current_page_scale_factor()); | 
|  | EXPECT_EQ(1.f / 1.5f, quad_scale_delta); | 
|  | break; | 
|  | case 3: | 
|  | // Continuous draws are attempted. | 
|  | EXPECT_EQ(1.5f, host_impl->active_tree()->current_page_scale_factor()); | 
|  | if (!frame_data->has_no_damage) | 
|  | EXPECT_EQ(1.f / 1.5f, quad_scale_delta); | 
|  | break; | 
|  | case 4: | 
|  | if (quad_scale_delta != 1.f) | 
|  | break; | 
|  | // Drew at scale 1.5 when all the tiles completed. | 
|  | EXPECT_EQ(1.5f, host_impl->active_tree()->current_page_scale_factor()); | 
|  | EXPECT_EQ(1.f, quad_scale_delta); | 
|  | break; | 
|  | case 5: | 
|  | // TODO(danakj): We get more draws before the NotifyReadyToDraw | 
|  | // because it is asynchronous from the previous draw and happens late. | 
|  | break; | 
|  | case 6: | 
|  | // NotifyReadyToDraw happened. If we were already inside a frame, we may | 
|  | // try to draw once more. | 
|  | break; | 
|  | case 7: | 
|  | NOTREACHED() << "No draws should happen once we have a complete frame."; | 
|  | break; | 
|  | } | 
|  | return draw_result; | 
|  | } | 
|  |  | 
|  | void DrawLayersOnThread(LayerTreeHostImpl* host_impl) override { | 
|  | switch (step_) { | 
|  | case 1: | 
|  | // Delay tile production. | 
|  | playback_allowed_event_.Reset(); | 
|  | // Pinch zoom in to cause new tiles to be required. | 
|  | host_impl->PinchGestureBegin(); | 
|  | host_impl->PinchGestureUpdate(1.5f, gfx::Point(100, 100)); | 
|  | host_impl->PinchGestureEnd(); | 
|  | ++step_; | 
|  | break; | 
|  | case 2: | 
|  | ++step_; | 
|  | break; | 
|  | case 3: | 
|  | // We should continue to try draw while there are incomplete visible | 
|  | // tiles. | 
|  | if (++continuous_draws_ > 5) { | 
|  | // Allow the tiles to complete. | 
|  | playback_allowed_event_.Signal(); | 
|  | ++step_; | 
|  | } | 
|  | break; | 
|  | case 4: | 
|  | ++step_; | 
|  | break; | 
|  | case 5: | 
|  | // Waiting for NotifyReadyToDraw. | 
|  | break; | 
|  | case 6: | 
|  | // NotifyReadyToDraw happened. | 
|  | ++step_; | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | void NotifyReadyToDrawOnThread(LayerTreeHostImpl* host_impl) override { | 
|  | if (step_ == 5) { | 
|  | ++step_; | 
|  | // NotifyReadyToDraw has happened, we may draw once more, but should not | 
|  | // get any more draws after that. End the test after a timeout to watch | 
|  | // for any extraneous draws. | 
|  | // TODO(brianderson): We could remove this delay and instead wait until | 
|  | // the BeginFrameSource decides it doesn't need to send frames anymore, | 
|  | // or test that it already doesn't here. | 
|  | EndTestAfterDelayMs(16 * 4); | 
|  | } | 
|  | } | 
|  |  | 
|  | void NotifyTileStateChangedOnThread(LayerTreeHostImpl* host_impl, | 
|  | const Tile* tile) override { | 
|  | // On step_ == 2, we are preventing texture uploads from completing, | 
|  | // so this verifies they are not completing before step_ == 3. | 
|  | // Flaky failures here indicate we're failing to prevent uploads from | 
|  | // completing. | 
|  | EXPECT_NE(2, step_); | 
|  | } | 
|  |  | 
|  | void AfterTest() override { EXPECT_GT(continuous_draws_, 5); } | 
|  |  | 
|  | FakeContentLayerClient client_; | 
|  | int step_; | 
|  | int continuous_draws_; | 
|  | base::WaitableEvent playback_allowed_event_; | 
|  | }; | 
|  |  | 
|  | MULTI_THREAD_TEST_F(LayerTreeHostTestContinuousDrawWhenCreatingVisibleTiles); | 
|  |  | 
|  | class LayerTreeHostTestOneActivatePerPrepareTiles : public LayerTreeHostTest { | 
|  | public: | 
|  | LayerTreeHostTestOneActivatePerPrepareTiles() | 
|  | : notify_ready_to_activate_count_(0u), | 
|  | scheduled_prepare_tiles_count_(0) {} | 
|  |  | 
|  | void SetupTree() override { | 
|  | client_.set_fill_with_nonsolid_color(true); | 
|  | scoped_refptr<FakePictureLayer> root_layer = | 
|  | FakePictureLayer::Create(&client_); | 
|  | root_layer->SetBounds(gfx::Size(1500, 1500)); | 
|  | root_layer->SetIsDrawable(true); | 
|  |  | 
|  | layer_tree_host()->SetRootLayer(root_layer); | 
|  | LayerTreeHostTest::SetupTree(); | 
|  | } | 
|  |  | 
|  | void BeginTest() override { | 
|  | layer_tree_host()->SetViewportSize(gfx::Size(16, 16)); | 
|  | PostSetNeedsCommitToMainThread(); | 
|  | } | 
|  |  | 
|  | void InitializedRendererOnThread(LayerTreeHostImpl* host_impl, | 
|  | bool success) override { | 
|  | ASSERT_TRUE(success); | 
|  | host_impl->tile_manager()->SetScheduledRasterTaskLimitForTesting(1); | 
|  | } | 
|  |  | 
|  | void NotifyReadyToActivateOnThread(LayerTreeHostImpl* impl) override { | 
|  | ++notify_ready_to_activate_count_; | 
|  | EndTestAfterDelayMs(100); | 
|  | } | 
|  |  | 
|  | void ScheduledActionPrepareTiles() override { | 
|  | ++scheduled_prepare_tiles_count_; | 
|  | } | 
|  |  | 
|  | void AfterTest() override { | 
|  | // Expect at most a notification for each scheduled prepare tiles, plus one | 
|  | // for the initial commit (which doesn't go through scheduled actions). | 
|  | // The reason this is not an equality is because depending on timing, we | 
|  | // might get a prepare tiles but not yet get a notification that we're | 
|  | // ready to activate. The intent of a test is to ensure that we don't | 
|  | // get more than one notification per prepare tiles, so this is OK. | 
|  | EXPECT_LE(notify_ready_to_activate_count_, | 
|  | 1u + scheduled_prepare_tiles_count_); | 
|  | } | 
|  |  | 
|  | protected: | 
|  | FakeContentLayerClient client_; | 
|  | size_t notify_ready_to_activate_count_; | 
|  | size_t scheduled_prepare_tiles_count_; | 
|  | }; | 
|  |  | 
|  | MULTI_THREAD_IMPL_TEST_F(LayerTreeHostTestOneActivatePerPrepareTiles); | 
|  |  | 
|  | class LayerTreeHostTestActivationCausesPrepareTiles : public LayerTreeHostTest { | 
|  | public: | 
|  | LayerTreeHostTestActivationCausesPrepareTiles() | 
|  | : scheduled_prepare_tiles_count_(0) {} | 
|  |  | 
|  | void SetupTree() override { | 
|  | client_.set_fill_with_nonsolid_color(true); | 
|  | scoped_refptr<FakePictureLayer> root_layer = | 
|  | FakePictureLayer::Create(&client_); | 
|  | root_layer->SetBounds(gfx::Size(150, 150)); | 
|  | root_layer->SetIsDrawable(true); | 
|  |  | 
|  | layer_tree_host()->SetRootLayer(root_layer); | 
|  | LayerTreeHostTest::SetupTree(); | 
|  | } | 
|  |  | 
|  | void BeginTest() override { PostSetNeedsCommitToMainThread(); } | 
|  |  | 
|  | void NotifyReadyToActivateOnThread(LayerTreeHostImpl* impl) override { | 
|  | // Ensure we've already activated. | 
|  | EXPECT_FALSE(impl->pending_tree()); | 
|  |  | 
|  | // After activating, we either need to prepare tiles, or we've already | 
|  | // called a scheduled prepare tiles. This is done because activation might | 
|  | // cause us to have to memory available (old active tree is gone), so we | 
|  | // need to ensure we will get a PrepareTiles call. | 
|  | if (!impl->prepare_tiles_needed()) | 
|  | EXPECT_GE(scheduled_prepare_tiles_count_, 1); | 
|  | EndTest(); | 
|  | } | 
|  |  | 
|  | void ScheduledActionPrepareTiles() override { | 
|  | ++scheduled_prepare_tiles_count_; | 
|  | } | 
|  |  | 
|  | void AfterTest() override {} | 
|  |  | 
|  | protected: | 
|  | FakeContentLayerClient client_; | 
|  | int scheduled_prepare_tiles_count_; | 
|  | }; | 
|  |  | 
|  | MULTI_THREAD_IMPL_TEST_F(LayerTreeHostTestActivationCausesPrepareTiles); | 
|  |  | 
|  | // This tests an assertion that DidCommit and WillCommit happen in the same | 
|  | // stack frame with no tasks that run between them.  Various embedders of | 
|  | // cc depend on this logic.  ui::Compositor holds a compositor lock between | 
|  | // these events and the inspector timeline wants begin/end CompositeLayers | 
|  | // to be properly nested with other begin/end events. | 
|  | class LayerTreeHostTestNoTasksBetweenWillAndDidCommit | 
|  | : public LayerTreeHostTest { | 
|  | public: | 
|  | LayerTreeHostTestNoTasksBetweenWillAndDidCommit() : did_commit_(false) {} | 
|  |  | 
|  | void BeginTest() override { PostSetNeedsCommitToMainThread(); } | 
|  |  | 
|  | void WillCommit() override { | 
|  | MainThreadTaskRunner()->PostTask( | 
|  | FROM_HERE, base::Bind(&LayerTreeHostTestNoTasksBetweenWillAndDidCommit:: | 
|  | EndTestShouldRunAfterDidCommit, | 
|  | base::Unretained(this))); | 
|  | } | 
|  |  | 
|  | void EndTestShouldRunAfterDidCommit() { | 
|  | EXPECT_TRUE(did_commit_); | 
|  | EndTest(); | 
|  | } | 
|  |  | 
|  | void DidCommit() override { | 
|  | EXPECT_FALSE(did_commit_); | 
|  | did_commit_ = true; | 
|  | } | 
|  |  | 
|  | void AfterTest() override { EXPECT_TRUE(did_commit_); } | 
|  |  | 
|  | private: | 
|  | bool did_commit_; | 
|  | }; | 
|  |  | 
|  | SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestNoTasksBetweenWillAndDidCommit); | 
|  |  | 
|  | }  // namespace cc |