| // 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/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_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 {}; |
| |
| // 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->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), |
| root_layer_(ContentLayer::Create(&client_)) {} |
| |
| void BeginTest() override { |
| 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_RECT_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<ContentLayer> 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)); |
| |
| 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: |
| // 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<FakeContentLayer> 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)); |
| |
| 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), |
| root_layer_(ContentLayer::Create(&client_)) {} |
| |
| void BeginTest() override { |
| 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_RECT_EQ(gfx::Rect(bounds_), root_damage_rect); |
| break; |
| case 1: |
| case 2: |
| EXPECT_RECT_EQ(gfx::Rect(0, 0, 0, 0), root_damage_rect); |
| break; |
| case 3: |
| EXPECT_RECT_EQ(invalid_rect_, root_damage_rect); |
| break; |
| case 4: |
| EXPECT_RECT_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<ContentLayer> 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() |
| : root_layer_(ContentLayer::Create(&client_)) {} |
| |
| void SetupTree() override { |
| 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. |
| parent_layer_ = FakeContentLayer::Create(&client_); |
| parent_layer_->SetBounds(gfx::Size(15, 15)); |
| parent_layer_->SetOpacity(0.0f); |
| root_layer_->AddChild(parent_layer_); |
| |
| 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_RECT_EQ(gfx::Rect(root_layer_->bounds()), root_damage_rect); |
| break; |
| case 1: |
| case 2: |
| case 3: |
| EXPECT_RECT_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<ContentLayer> root_layer_; |
| scoped_refptr<FakeContentLayer> parent_layer_; |
| scoped_refptr<FakeContentLayer> 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_->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( |
| 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()->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()->page_scale_factor()); |
| break; |
| case 2: |
| EXPECT_EQ(1.25f, impl->active_tree()->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); |
| } |
| void DidChangeLayerCanUseLCDText() override {} |
| 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_(), |
| update_check_layer_(ContentLayerWithUpdateTracking::Create( |
| &test_opacity_change_delegate_)) { |
| test_opacity_change_delegate_.SetTestLayer(update_check_layer_.get()); |
| } |
| |
| void BeginTest() override { |
| layer_tree_host()->SetViewportSize(gfx::Size(10, 10)); |
| layer_tree_host()->root_layer()->AddChild(update_check_layer_); |
| |
| PostSetNeedsCommitToMainThread(); |
| } |
| |
| void CommitCompleteOnThread(LayerTreeHostImpl* impl) override { EndTest(); } |
| |
| void AfterTest() override { |
| // Update() should have been called once. |
| EXPECT_EQ(1, update_check_layer_->PaintContentsCount()); |
| } |
| |
| private: |
| TestOpacityChangeLayerDelegate test_opacity_change_delegate_; |
| scoped_refptr<ContentLayerWithUpdateTracking> update_check_layer_; |
| }; |
| |
| MULTI_THREAD_TEST_F(LayerTreeHostTestOpacityChange); |
| |
| class NoScaleContentLayer : public ContentLayer { |
| public: |
| static scoped_refptr<NoScaleContentLayer> Create(ContentLayerClient* client) { |
| return make_scoped_refptr(new NoScaleContentLayer(client)); |
| } |
| |
| void CalculateContentsScale(float ideal_contents_scale, |
| float* contents_scale_x, |
| float* contents_scale_y, |
| gfx::Size* contentBounds) override { |
| // Skip over the ContentLayer's method to the base Layer class. |
| Layer::CalculateContentsScale(ideal_contents_scale, |
| contents_scale_x, |
| contents_scale_y, |
| contentBounds); |
| } |
| |
| private: |
| explicit NoScaleContentLayer(ContentLayerClient* client) |
| : ContentLayer(client) {} |
| ~NoScaleContentLayer() override {} |
| }; |
| |
| class LayerTreeHostTestDeviceScaleFactorScalesViewportAndLayers |
| : public LayerTreeHostTest { |
| public: |
| LayerTreeHostTestDeviceScaleFactorScalesViewportAndLayers() |
| : root_layer_(NoScaleContentLayer::Create(&client_)), |
| child_layer_(ContentLayer::Create(&client_)) {} |
| |
| void BeginTest() override { |
| 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()); |
| |
| LayerImpl* root = impl->active_tree()->root_layer(); |
| LayerImpl* child = 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_RECT_EQ(gfx::Rect(0, 0, 60, 60), |
| root->render_surface()->content_rect()); |
| |
| // The content bounds of the child should be scaled. |
| gfx::Size child_bounds_scaled = |
| gfx::ToCeiledSize(gfx::ScaleSize(child->bounds(), 1.5)); |
| EXPECT_EQ(child_bounds_scaled, child->content_bounds()); |
| |
| 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_screen_space_transform; |
| child_screen_space_transform.Translate(3.f, 3.f); |
| gfx::Transform child_draw_transform = child_screen_space_transform; |
| |
| EXPECT_TRANSFORMATION_MATRIX_EQ(child_draw_transform, |
| child->draw_transform()); |
| EXPECT_TRANSFORMATION_MATRIX_EQ(child_screen_space_transform, |
| child->screen_space_transform()); |
| |
| EndTest(); |
| } |
| |
| void AfterTest() override {} |
| |
| private: |
| FakeContentLayerClient client_; |
| scoped_refptr<NoScaleContentLayer> root_layer_; |
| scoped_refptr<ContentLayer> child_layer_; |
| }; |
| |
| MULTI_THREAD_TEST_F(LayerTreeHostTestDeviceScaleFactorScalesViewportAndLayers); |
| |
| // Verify atomicity of commits and reuse of textures. |
| class LayerTreeHostTestDirectRendererAtomicCommit : public LayerTreeHostTest { |
| public: |
| void InitializeSettings(LayerTreeSettings* settings) override { |
| 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); |
| |
| 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); |
| } |
| |
| class LayerTreeHostTestAtomicCommitWithPartialUpdate |
| : public LayerTreeHostTest { |
| public: |
| void InitializeSettings(LayerTreeSettings* settings) override { |
| 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); |
| |
| 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)); |
| |
| content_layer_ = ContentLayer::Create(&client_); |
| content_layer_->SetBounds(gfx::Size(10, 10)); |
| content_layer_->SetPosition(gfx::PointF(0.f, 0.f)); |
| content_layer_->SetIsDrawable(true); |
| layer_tree_host()->root_layer()->AddChild(content_layer_); |
| |
| PostSetNeedsCommitToMainThread(); |
| } |
| |
| void DidCommitAndDrawFrame() override { |
| if (num_draw_layers_ == 2) |
| return; |
| content_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> content_layer_; |
| int num_commit_complete_; |
| int num_draw_layers_; |
| }; |
| |
| MULTI_THREAD_NOIMPL_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()); |
| host.OnCreateAndInitializeOutputSurfaceAttempted(true); |
| |
| 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()); |
| host.OnCreateAndInitializeOutputSurfaceAttempted(true); |
| |
| 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()); |
| host.OnCreateAndInitializeOutputSurfaceAttempted(true); |
| |
| 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()); |
| 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()); |
| 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()); |
| 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()); |
| client.SetLayerTreeHost(host.get()); |
| host->Composite(base::TimeTicks::Now()); |
| |
| EXPECT_EQ(0u, host->MaxPartialTextureUpdates()); |
| } |
| |
| 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 LayerTreeHostTestLCDNotification : public LayerTreeHostTest { |
| public: |
| class NotificationClient : public ContentLayerClient { |
| public: |
| NotificationClient() |
| : layer_(0), paint_count_(0), lcd_notification_count_(0) {} |
| |
| void set_layer(Layer* layer) { layer_ = layer; } |
| int paint_count() const { return paint_count_; } |
| int lcd_notification_count() const { return lcd_notification_count_; } |
| |
| void PaintContents( |
| SkCanvas* canvas, |
| const gfx::Rect& clip, |
| ContentLayerClient::GraphicsContextStatus gc_status) override { |
| ++paint_count_; |
| } |
| void DidChangeLayerCanUseLCDText() override { |
| ++lcd_notification_count_; |
| layer_->SetNeedsDisplay(); |
| } |
| bool FillsBoundsCompletely() const override { return false; } |
| |
| private: |
| Layer* layer_; |
| int paint_count_; |
| int lcd_notification_count_; |
| }; |
| |
| void SetupTree() override { |
| scoped_refptr<Layer> root_layer; |
| if (layer_tree_host()->settings().impl_side_painting) |
| root_layer = PictureLayer::Create(&client_); |
| else |
| root_layer = ContentLayer::Create(&client_); |
| root_layer->SetIsDrawable(true); |
| root_layer->SetBounds(gfx::Size(1, 1)); |
| |
| layer_tree_host()->SetRootLayer(root_layer); |
| client_.set_layer(root_layer.get()); |
| |
| // 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 AfterTest() override {} |
| |
| void DidCommit() override { |
| switch (layer_tree_host()->source_frame_number()) { |
| case 1: |
| // The first update consists of one LCD notification and one paint. |
| EXPECT_EQ(1, client_.lcd_notification_count()); |
| 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 notification |
| // or paint on the second update. |
| EXPECT_EQ(1, client_.lcd_notification_count()); |
| 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 notification. |
| layer_tree_host()->root_layer()->SetOpacity(.5f); |
| // No need to request a commit - setting opacity will do it. |
| break; |
| default: |
| // Verify that there is no extra commit due to layer invalidation. |
| EXPECT_EQ(3, layer_tree_host()->source_frame_number()); |
| // LCD notification count should have incremented due to |
| // change in layer opacity. |
| EXPECT_EQ(2, client_.lcd_notification_count()); |
| // Paint count should be incremented due to invalidation. |
| EXPECT_EQ(2, 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()); |
| EndTest(); |
| break; |
| } |
| } |
| |
| private: |
| NotificationClient client_; |
| }; |
| |
| SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestLCDNotification); |
| |
| // Verify that the BeginFrame notification is used to initiate rendering. |
| class LayerTreeHostTestBeginFrameNotification : public LayerTreeHostTest { |
| public: |
| void InitializeSettings(LayerTreeSettings* settings) override { |
| settings->begin_frame_scheduling_enabled = 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->begin_frame_scheduling_enabled = 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->begin_frame_scheduling_enabled = 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, |
| bool did_handle) 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)); |
| } |
| |
| void DidChangeLayerCanUseLCDText() override {} |
| |
| 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_SIZE_EQ(gfx::Size(1, 1), root_layer->bounds()); |
| } else { |
| LayerImpl* root_layer = host_impl->active_tree()->root_layer(); |
| EXPECT_SIZE_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; |
| } |
| |
| virtual 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( |
| bool fallback) 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_SIZE_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( |
| bool fallback) 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->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(); |
| 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(); |
| |
| // 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_->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(); |
| 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(); |
| 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_->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); |
| |
| 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( |
| bool fallback) 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( |
| bool fallback) 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 { |
| 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<FakeContentLayer> root_; |
| }; |
| |
| // No output to copy for delegated renderers. |
| 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 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, |
| bool did_handle) 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, |
| bool did_handle) override { |
| EndTest(); |
| // This lets the test finally commit and exit. |
| MainThreadTaskRunner()->PostTask( |
| FROM_HERE, |
| base::Bind(&LayerTreeHostTestBreakSwapPromiseForContextAbortedCommit:: |
| FindOutputSurface, |
| base::Unretained(this))); |
| } |
| |
| void FindOutputSurface() { |
| layer_tree_host()->OnCreateAndInitializeOutputSurfaceAttempted(true); |
| } |
| |
| 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)); |
| PicturePile* pile = layer->GetPicturePileForTesting(); |
| |
| // Verify default values. |
| EXPECT_TRUE(root->IsSuitableForGpuRasterization()); |
| EXPECT_TRUE(layer->IsSuitableForGpuRasterization()); |
| EXPECT_TRUE(pile->is_suitable_for_gpu_rasterization()); |
| 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)); |
| PicturePile* pile = layer->GetPicturePileForTesting(); |
| |
| // Verify default values. |
| EXPECT_TRUE(root->IsSuitableForGpuRasterization()); |
| EXPECT_TRUE(layer->IsSuitableForGpuRasterization()); |
| EXPECT_TRUE(pile->is_suitable_for_gpu_rasterization()); |
| 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. |
| pile->SetUnsuitableForGpuRasterizationForTesting(); |
| EXPECT_FALSE(pile->is_suitable_for_gpu_rasterization()); |
| 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 { |
| settings->impl_side_painting = true; |
| |
| EXPECT_FALSE(settings->gpu_rasterization_forced); |
| settings->gpu_rasterization_forced = 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)); |
| PicturePile* pile = layer->GetPicturePileForTesting(); |
| |
| // Verify default values. |
| EXPECT_TRUE(root->IsSuitableForGpuRasterization()); |
| EXPECT_TRUE(layer->IsSuitableForGpuRasterization()); |
| EXPECT_TRUE(pile->is_suitable_for_gpu_rasterization()); |
| 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. |
| pile->SetUnsuitableForGpuRasterizationForTesting(); |
| EXPECT_FALSE(pile->is_suitable_for_gpu_rasterization()); |
| 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_; |
| }; |
| |
| MULTI_THREAD_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_); |
| |
| 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().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 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, |
| 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); |
| } // namespace cc |