diff options
Diffstat (limited to 'chromium/cc/trees/layer_tree_host_unittest.cc')
-rw-r--r-- | chromium/cc/trees/layer_tree_host_unittest.cc | 4353 |
1 files changed, 4353 insertions, 0 deletions
diff --git a/chromium/cc/trees/layer_tree_host_unittest.cc b/chromium/cc/trees/layer_tree_host_unittest.cc new file mode 100644 index 00000000000..55841e52e70 --- /dev/null +++ b/chromium/cc/trees/layer_tree_host_unittest.cc @@ -0,0 +1,4353 @@ +// 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/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/picture_layer.h" +#include "cc/layers/scrollbar_layer.h" +#include "cc/layers/solid_color_layer.h" +#include "cc/layers/video_layer.h" +#include "cc/output/begin_frame_args.h" +#include "cc/output/copy_output_request.h" +#include "cc/output/copy_output_result.h" +#include "cc/output/output_surface.h" +#include "cc/resources/prioritized_resource.h" +#include "cc/resources/prioritized_resource_manager.h" +#include "cc/resources/resource_update_queue.h" +#include "cc/scheduler/frame_rate_controller.h" +#include "cc/test/fake_content_layer.h" +#include "cc/test/fake_content_layer_client.h" +#include "cc/test/fake_layer_tree_host_client.h" +#include "cc/test/fake_output_surface.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/fake_scrollbar_layer.h" +#include "cc/test/fake_video_frame_provider.h" +#include "cc/test/geometry_test_utils.h" +#include "cc/test/layer_tree_test.h" +#include "cc/test/occlusion_tracker_test_common.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/point_conversions.h" +#include "ui/gfx/size_conversions.h" +#include "ui/gfx/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) {} + + virtual void BeginTest() OVERRIDE { + PostSetNeedsCommitToMainThread(); + PostSetNeedsCommitToMainThread(); + } + + virtual void DrawLayersOnThread(LayerTreeHostImpl* impl) OVERRIDE { + num_draws_++; + if (!impl->active_tree()->source_frame_number()) + EndTest(); + } + + virtual void CommitCompleteOnThread(LayerTreeHostImpl* impl) OVERRIDE { + num_commits_++; + } + + virtual void AfterTest() OVERRIDE { + EXPECT_GE(1, num_commits_); + EXPECT_GE(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) {} + + virtual void BeginTest() OVERRIDE { PostSetNeedsCommitToMainThread(); } + + virtual void DrawLayersOnThread(LayerTreeHostImpl* impl) OVERRIDE { + ++num_draws_; + } + + virtual void CommitCompleteOnThread(LayerTreeHostImpl* impl) OVERRIDE { + ++num_commits_; + switch (num_commits_) { + case 1: + PostSetNeedsCommitToMainThread(); + break; + case 2: + EndTest(); + break; + default: + NOTREACHED(); + } + } + + virtual 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: + virtual 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, + }; + + virtual void BeginTest() OVERRIDE { + index_ = STARTUP; + PostSetNeedsCommitToMainThread(); + } + + virtual void DrawLayersOnThread(LayerTreeHostImpl* impl) OVERRIDE { + VerifyAfterValues(impl->active_tree()->root_layer()); + } + + virtual 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()); + } + + virtual 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) {} + + virtual void BeginTest() OVERRIDE { PostSetNeedsCommitToMainThread(); } + + virtual 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_++; + } + + virtual void CommitCompleteOnThread(LayerTreeHostImpl* impl) OVERRIDE { + EXPECT_EQ(0, num_draws_); + num_commits_++; + } + + virtual 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_)) { + } + + virtual void BeginTest() OVERRIDE { + root_layer_->SetIsDrawable(true); + root_layer_->SetBounds(bounds_); + layer_tree_host()->SetRootLayer(root_layer_); + layer_tree_host()->SetViewportSize(bounds_); + PostSetNeedsCommitToMainThread(); + } + + virtual bool PrepareToDrawOnThread(LayerTreeHostImpl* host_impl, + LayerTreeHostImpl::FrameData* frame_data, + bool result) OVERRIDE { + EXPECT_TRUE(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 result; + } + + virtual void DrawLayersOnThread(LayerTreeHostImpl* impl) OVERRIDE { + if (!num_draws_) { + PostSetNeedsRedrawRectToMainThread(invalid_rect_); + } else { + EndTest(); + } + num_draws_++; + } + + virtual 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: + virtual void InitializeSettings(LayerTreeSettings* settings) OVERRIDE { + settings->layer_transforms_should_scale_layer_contents = true; + } + + virtual 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(); + } + + virtual void BeginTest() OVERRIDE { + PostSetNeedsCommitToMainThread(); + } + + virtual void DrawLayersOnThread(LayerTreeHostImpl* host_impl) OVERRIDE { + if (host_impl->active_tree()->source_frame_number() == 1) + EndTest(); + } + + virtual 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()); + } + } + + virtual 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: + virtual void InitializeSettings(LayerTreeSettings* settings) OVERRIDE { + settings->layer_transforms_should_scale_layer_contents = true; + } + + virtual void SetupTree() OVERRIDE { + root_layer_ = Layer::Create(); + root_layer_->SetBounds(gfx::Size(10, 20)); + + bool paint_scrollbar = true; + bool has_thumb = false; + scrollbar_ = FakeScrollbarLayer::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(); + } + + virtual void BeginTest() OVERRIDE { + PostSetNeedsCommitToMainThread(); + } + + virtual void DrawLayersOnThread(LayerTreeHostImpl* host_impl) OVERRIDE { + if (host_impl->active_tree()->source_frame_number() == 1) + EndTest(); + } + + virtual 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()); + } + } + + virtual void AfterTest() OVERRIDE { + EXPECT_EQ(gfx::Size(40, 40).ToString(), + scrollbar_->content_bounds().ToString()); + } + + private: + FakeContentLayerClient client_; + scoped_refptr<Layer> root_layer_; + scoped_refptr<FakeScrollbarLayer> scrollbar_; +}; + +SINGLE_AND_MULTI_THREAD_TEST_F( + LayerTreeHostTestNoExtraCommitFromScrollbarInvalidate); + +class LayerTreeHostTestCompositeAndReadback : public LayerTreeHostTest { + public: + LayerTreeHostTestCompositeAndReadback() : num_commits_(0) {} + + virtual void BeginTest() OVERRIDE { PostSetNeedsCommitToMainThread(); } + + virtual void DidCommit() OVERRIDE { + num_commits_++; + if (num_commits_ == 1) { + char pixels[4]; + layer_tree_host()->CompositeAndReadback(&pixels, gfx::Rect(0, 0, 1, 1)); + } else if (num_commits_ == 2) { + // This is inside the readback. We should get another commit after it. + } else if (num_commits_ == 3) { + EndTest(); + } else { + NOTREACHED(); + } + } + + virtual void AfterTest() OVERRIDE {} + + private: + int num_commits_; +}; + +MULTI_THREAD_TEST_F(LayerTreeHostTestCompositeAndReadback); + +class LayerTreeHostTestCompositeAndReadbackBeforePreviousCommitDraws + : public LayerTreeHostTest { + public: + LayerTreeHostTestCompositeAndReadbackBeforePreviousCommitDraws() + : num_commits_(0) {} + + virtual void BeginTest() OVERRIDE { PostSetNeedsCommitToMainThread(); } + + virtual void DidCommit() OVERRIDE { + num_commits_++; + if (num_commits_ == 1) { + layer_tree_host()->SetNeedsCommit(); + } else if (num_commits_ == 2) { + char pixels[4]; + layer_tree_host()->CompositeAndReadback(&pixels, gfx::Rect(0, 0, 1, 1)); + } else if (num_commits_ == 3) { + // This is inside the readback. We should get another commit after it. + } else if (num_commits_ == 4) { + EndTest(); + } else { + NOTREACHED(); + } + } + + virtual void AfterTest() OVERRIDE {} + + private: + int num_commits_; +}; + +MULTI_THREAD_TEST_F( + LayerTreeHostTestCompositeAndReadbackBeforePreviousCommitDraws); + +// If the layerTreeHost says it can't draw, Then we should not try to draw. +class LayerTreeHostTestCanDrawBlocksDrawing : public LayerTreeHostTest { + public: + LayerTreeHostTestCanDrawBlocksDrawing() : num_commits_(0), done_(false) {} + + virtual void BeginTest() OVERRIDE { PostSetNeedsCommitToMainThread(); } + + virtual void DrawLayersOnThread(LayerTreeHostImpl* impl) OVERRIDE { + if (done_) + return; + // Only the initial draw should bring us here. + EXPECT_TRUE(impl->CanDraw()); + EXPECT_EQ(0, impl->active_tree()->source_frame_number()); + } + + virtual void CommitCompleteOnThread(LayerTreeHostImpl* impl) OVERRIDE { + if (done_) + return; + if (num_commits_ >= 1) { + // After the first commit, we should not be able to draw. + EXPECT_FALSE(impl->CanDraw()); + } + } + + virtual void DidCommit() OVERRIDE { + num_commits_++; + if (num_commits_ == 1) { + // Make the viewport empty so the host says it can't draw. + layer_tree_host()->SetViewportSize(gfx::Size(0, 0)); + } else if (num_commits_ == 2) { + char pixels[4]; + layer_tree_host()->CompositeAndReadback(&pixels, gfx::Rect(0, 0, 1, 1)); + } else if (num_commits_ == 3) { + // Let it draw so we go idle and end the test. + layer_tree_host()->SetViewportSize(gfx::Size(1, 1)); + done_ = true; + EndTest(); + } + } + + virtual void AfterTest() OVERRIDE {} + + private: + int num_commits_; + bool done_; +}; + +SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestCanDrawBlocksDrawing); + +// beginLayerWrite should prevent draws from executing until a commit occurs +class LayerTreeHostTestWriteLayersRedraw : public LayerTreeHostTest { + public: + LayerTreeHostTestWriteLayersRedraw() : num_commits_(0), num_draws_(0) {} + + virtual void BeginTest() OVERRIDE { + PostAcquireLayerTextures(); + PostSetNeedsRedrawToMainThread(); // should be inhibited without blocking + PostSetNeedsCommitToMainThread(); + } + + virtual void DrawLayersOnThread(LayerTreeHostImpl* impl) OVERRIDE { + num_draws_++; + EXPECT_EQ(num_draws_, num_commits_); + } + + virtual void CommitCompleteOnThread(LayerTreeHostImpl* impl) OVERRIDE { + num_commits_++; + EndTest(); + } + + virtual void AfterTest() OVERRIDE { EXPECT_EQ(1, num_commits_); } + + private: + int num_commits_; + int num_draws_; +}; + +MULTI_THREAD_TEST_F(LayerTreeHostTestWriteLayersRedraw); + +// Verify that when resuming visibility, Requesting layer write permission +// will not deadlock the main thread even though there are not yet any +// scheduled redraws. This behavior is critical for reliably surviving tab +// switching. There are no failure conditions to this test, it just passes +// by not timing out. +class LayerTreeHostTestWriteLayersAfterVisible : public LayerTreeHostTest { + public: + LayerTreeHostTestWriteLayersAfterVisible() : num_commits_(0) {} + + virtual void BeginTest() OVERRIDE { PostSetNeedsCommitToMainThread(); } + + virtual void CommitCompleteOnThread(LayerTreeHostImpl* impl) OVERRIDE { + num_commits_++; + if (num_commits_ == 2) + EndTest(); + else if (num_commits_ < 2) { + PostSetVisibleToMainThread(false); + PostSetVisibleToMainThread(true); + PostAcquireLayerTextures(); + PostSetNeedsCommitToMainThread(); + } + } + + virtual void AfterTest() OVERRIDE {} + + private: + int num_commits_; +}; + +MULTI_THREAD_TEST_F(LayerTreeHostTestWriteLayersAfterVisible); + +// A compositeAndReadback while invisible should force a normal commit without +// assertion. +class LayerTreeHostTestCompositeAndReadbackWhileInvisible + : public LayerTreeHostTest { + public: + LayerTreeHostTestCompositeAndReadbackWhileInvisible() : num_commits_(0) {} + + virtual void BeginTest() OVERRIDE { PostSetNeedsCommitToMainThread(); } + + virtual void DidCommitAndDrawFrame() OVERRIDE { + num_commits_++; + if (num_commits_ == 1) { + layer_tree_host()->SetVisible(false); + layer_tree_host()->SetNeedsCommit(); + layer_tree_host()->SetNeedsCommit(); + char pixels[4]; + layer_tree_host()->CompositeAndReadback(&pixels, gfx::Rect(0, 0, 1, 1)); + } else { + EndTest(); + } + } + + virtual void AfterTest() OVERRIDE {} + + private: + int num_commits_; +}; + +MULTI_THREAD_TEST_F(LayerTreeHostTestCompositeAndReadbackWhileInvisible); + +class LayerTreeHostTestAbortFrameWhenInvisible : public LayerTreeHostTest { + public: + LayerTreeHostTestAbortFrameWhenInvisible() {} + + virtual void BeginTest() OVERRIDE { + // Request a commit (from the main thread), Which will trigger the commit + // flow from the impl side. + layer_tree_host()->SetNeedsCommit(); + // Then mark ourselves as not visible before processing any more messages + // on the main thread. + layer_tree_host()->SetVisible(false); + // If we make it without kicking a frame, we pass! + EndTestAfterDelay(1); + } + + virtual void Layout() OVERRIDE { + ASSERT_FALSE(true); + EndTest(); + } + + virtual void AfterTest() OVERRIDE {} + + private: +}; + +MULTI_THREAD_TEST_F(LayerTreeHostTestAbortFrameWhenInvisible); + +// This test verifies that properties on the layer tree host are commited +// to the impl side. +class LayerTreeHostTestCommit : public LayerTreeHostTest { + public: + LayerTreeHostTestCommit() {} + + virtual void BeginTest() OVERRIDE { + layer_tree_host()->SetViewportSize(gfx::Size(20, 20)); + layer_tree_host()->set_background_color(SK_ColorGRAY); + + PostSetNeedsCommitToMainThread(); + } + + virtual void DidActivateTreeOnThread(LayerTreeHostImpl* impl) OVERRIDE { + EXPECT_EQ(gfx::Size(20, 20), impl->device_viewport_size()); + EXPECT_EQ(SK_ColorGRAY, impl->active_tree()->background_color()); + + EndTest(); + } + + virtual 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_(0) {} + + virtual void BeginTest() OVERRIDE { + layer_tree_host()->SetViewportSize(gfx::Size(20, 20)); + layer_tree_host()->set_background_color(SK_ColorGRAY); + + PostSetNeedsCommitToMainThread(); + } + + virtual void DrawLayersOnThread(LayerTreeHostImpl* impl) OVERRIDE { + if (frame_ >= 1) { + EXPECT_NE(first_frame_time_, impl->CurrentFrameTimeTicks()); + EndTest(); + return; + } + + EXPECT_FALSE(impl->settings().impl_side_painting); + EndTest(); + } + + virtual bool CanActivatePendingTree(LayerTreeHostImpl* impl) OVERRIDE { + if (frame_ >= 1) + return true; + + return false; + } + + virtual bool CanActivatePendingTreeIfNeeded(LayerTreeHostImpl* impl) + OVERRIDE { + frame_++; + if (frame_ == 1) { + first_frame_time_ = impl->CurrentFrameTimeTicks(); + + // Since base::TimeTicks::Now() uses a low-resolution clock on + // Windows, we need to make sure that the clock has incremented past + // first_frame_time_. + while (first_frame_time_ == base::TimeTicks::Now()) {} + + return false; + } + + return true; + } + + virtual void AfterTest() OVERRIDE {} + + private: + int frame_; + base::TimeTicks first_frame_time_; +}; + +SINGLE_AND_MULTI_THREAD_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) {} + + virtual void BeginTest() OVERRIDE { + layer_tree_host()->SetViewportSize(gfx::Size(20, 20)); + layer_tree_host()->set_background_color(SK_ColorGRAY); + + PostSetNeedsCommitToMainThread(); + } + + virtual void DrawLayersOnThread(LayerTreeHostImpl* impl) OVERRIDE { + frame_++; + if (frame_ == 1) { + first_frame_time_ = impl->CurrentFrameTimeTicks(); + impl->SetNeedsRedraw(); + + // Since base::TimeTicks::Now() uses a low-resolution clock on + // Windows, we need to make sure that the clock has incremented past + // first_frame_time_. + while (first_frame_time_ == base::TimeTicks::Now()) {} + + return; + } + + EXPECT_NE(first_frame_time_, impl->CurrentFrameTimeTicks()); + EndTest(); + } + + virtual 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_); + } + + virtual 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() {} + + virtual void SetupTree() OVERRIDE { + LayerTreeHostTest::SetupTree(); + + scroll_layer_ = FakeContentLayer::Create(&client_); + scroll_layer_->SetScrollable(true); + scroll_layer_->SetScrollOffset(gfx::Vector2d()); + layer_tree_host()->root_layer()->AddChild(scroll_layer_); + } + + virtual void BeginTest() OVERRIDE { + PostSetNeedsCommitToMainThread(); + } + + virtual void ApplyScrollAndScale(gfx::Vector2d scroll_delta, float scale) + OVERRIDE { + gfx::Vector2d offset = scroll_layer_->scroll_offset(); + scroll_layer_->SetScrollOffset(offset + scroll_delta); + layer_tree_host()->SetPageScaleFactorAndLimits(scale, 0.5f, 2.f); + } + + virtual void DidActivateTreeOnThread(LayerTreeHostImpl* impl) OVERRIDE { + impl->ProcessScrollDeltas(); + // 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()); + PostSetNeedsRedrawToMainThread(); + break; + case 2: + EXPECT_EQ(1.25f, impl->active_tree()->page_scale_factor()); + EndTest(); + break; + default: + NOTREACHED(); + } + } + + virtual void DidCommitAndDrawFrame() OVERRIDE { + switch (layer_tree_host()->source_frame_number()) { + case 1: + layer_tree_host()->SetPageScaleFactorAndLimits(1.f, 0.5f, 2.f); + layer_tree_host()->StartPageScaleAnimation( + gfx::Vector2d(), false, 1.25f, base::TimeDelta()); + break; + } + } + + virtual void AfterTest() OVERRIDE {} + + FakeContentLayerClient client_; + scoped_refptr<FakeContentLayer> scroll_layer_; +}; + +MULTI_THREAD_TEST_F(LayerTreeHostTestStartPageScaleAnimation); + +class LayerTreeHostTestSetVisible : public LayerTreeHostTest { + public: + LayerTreeHostTestSetVisible() : num_draws_(0) {} + + virtual void BeginTest() OVERRIDE { + PostSetNeedsCommitToMainThread(); + PostSetVisibleToMainThread(false); + // This is suppressed while we're invisible. + PostSetNeedsRedrawToMainThread(); + // Triggers the redraw. + PostSetVisibleToMainThread(true); + } + + virtual void DrawLayersOnThread(LayerTreeHostImpl* impl) OVERRIDE { + EXPECT_TRUE(impl->visible()); + ++num_draws_; + EndTest(); + } + + virtual 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; } + + virtual void PaintContents(SkCanvas*, gfx::Rect, gfx::RectF*) OVERRIDE { + // Set layer opacity to 0. + if (test_layer_) + test_layer_->SetOpacity(0.f); + } + virtual void DidChangeLayerCanUseLCDText() OVERRIDE {} + + 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; } + + virtual bool Update(ResourceUpdateQueue* queue, + const OcclusionTracker* occlusion) OVERRIDE { + bool updated = ContentLayer::Update(queue, occlusion); + paint_contents_count_++; + return updated; + } + + private: + explicit ContentLayerWithUpdateTracking(ContentLayerClient* client) + : ContentLayer(client), paint_contents_count_(0) { + SetAnchorPoint(gfx::PointF(0.f, 0.f)); + SetBounds(gfx::Size(10, 10)); + SetIsDrawable(true); + } + virtual ~ContentLayerWithUpdateTracking() {} + + 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()); + } + + virtual void BeginTest() OVERRIDE { + layer_tree_host()->SetViewportSize(gfx::Size(10, 10)); + layer_tree_host()->root_layer()->AddChild(update_check_layer_); + + PostSetNeedsCommitToMainThread(); + } + + virtual void CommitCompleteOnThread(LayerTreeHostImpl* impl) OVERRIDE { + EndTest(); + } + + virtual 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)); + } + + virtual void CalculateContentsScale(float ideal_contents_scale, + float device_scale_factor, + float page_scale_factor, + bool animating_transform_to_screen, + 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, + device_scale_factor, + page_scale_factor, + animating_transform_to_screen, + contents_scale_x, + contents_scale_y, + contentBounds); + } + + private: + explicit NoScaleContentLayer(ContentLayerClient* client) + : ContentLayer(client) {} + virtual ~NoScaleContentLayer() {} +}; + +class LayerTreeHostTestDeviceScaleFactorScalesViewportAndLayers + : public LayerTreeHostTest { + public: + LayerTreeHostTestDeviceScaleFactorScalesViewportAndLayers() + : root_layer_(NoScaleContentLayer::Create(&client_)), + child_layer_(ContentLayer::Create(&client_)) {} + + virtual 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)); + root_layer_->SetAnchorPoint(gfx::PointF(0.f, 0.f)); + + child_layer_->SetIsDrawable(true); + child_layer_->SetPosition(gfx::Point(2, 2)); + child_layer_->SetBounds(gfx::Size(10, 10)); + child_layer_->SetAnchorPoint(gfx::PointF(0.f, 0.f)); + + layer_tree_host()->SetRootLayer(root_layer_); + + PostSetNeedsCommitToMainThread(); + } + + virtual 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->device_viewport_size()); + + 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, gfx::Rect()); + 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(); + } + + virtual 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: + virtual void InitializeSettings(LayerTreeSettings* settings) OVERRIDE { + // Make sure partial texture updates are turned off. + settings->max_partial_texture_updates = 0; + // Linear fade animator prevents scrollbars from drawing immediately. + settings->use_linear_fade_scrollbar_animator = false; + } + + virtual void SetupTree() OVERRIDE { + layer_ = FakeContentLayer::Create(&client_); + layer_->SetBounds(gfx::Size(10, 20)); + + bool paint_scrollbar = true; + bool has_thumb = false; + scrollbar_ = + FakeScrollbarLayer::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(); + } + + virtual void BeginTest() OVERRIDE { + drew_frame_ = -1; + PostSetNeedsCommitToMainThread(); + } + + virtual void DidActivateTreeOnThread(LayerTreeHostImpl* impl) OVERRIDE { + ASSERT_EQ(0u, layer_tree_host()->settings().max_partial_texture_updates); + + TestWebGraphicsContext3D* context = static_cast<TestWebGraphicsContext3D*>( + impl->output_surface()->context3d()); + + 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(); + PostSetNeedsCommitToMainThread(); + 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(); + PostSetNeedsCommitToMainThread(); + break; + case 2: + EndTest(); + break; + default: + NOTREACHED(); + break; + } + } + + virtual void DrawLayersOnThread(LayerTreeHostImpl* impl) OVERRIDE { + TestWebGraphicsContext3D* context = static_cast<TestWebGraphicsContext3D*>( + impl->output_surface()->context3d()); + + 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(); + } + + virtual void Layout() OVERRIDE { + layer_->SetNeedsDisplay(); + scrollbar_->SetNeedsDisplay(); + } + + virtual void AfterTest() OVERRIDE {} + + protected: + FakeContentLayerClient client_; + scoped_refptr<FakeContentLayer> layer_; + scoped_refptr<FakeScrollbarLayer> scrollbar_; + int drew_frame_; +}; + +MULTI_THREAD_DIRECT_RENDERER_TEST_F( + LayerTreeHostTestDirectRendererAtomicCommit); + +class LayerTreeHostTestDelegatingRendererAtomicCommit + : public LayerTreeHostTestDirectRendererAtomicCommit { + public: + virtual void DidActivateTreeOnThread(LayerTreeHostImpl* impl) OVERRIDE { + ASSERT_EQ(0u, layer_tree_host()->settings().max_partial_texture_updates); + + TestWebGraphicsContext3D* context = static_cast<TestWebGraphicsContext3D*>( + impl->output_surface()->context3d()); + + 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(); + PostSetNeedsCommitToMainThread(); + 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(); + PostSetNeedsCommitToMainThread(); + break; + case 2: + EndTest(); + break; + default: + NOTREACHED(); + break; + } + } +}; + +MULTI_THREAD_DELEGATING_RENDERER_TEST_F( + LayerTreeHostTestDelegatingRendererAtomicCommit); + +static void SetLayerPropertiesForTesting(Layer* layer, + Layer* parent, + const gfx::Transform& transform, + gfx::PointF anchor, + gfx::PointF position, + gfx::Size bounds, + bool opaque) { + layer->RemoveAllChildren(); + if (parent) + parent->AddChild(layer); + layer->SetTransform(transform); + layer->SetAnchorPoint(anchor); + layer->SetPosition(position); + layer->SetBounds(bounds); + layer->SetContentsOpaque(opaque); +} + +class LayerTreeHostTestAtomicCommitWithPartialUpdate + : public LayerTreeHostTest { + public: + virtual void InitializeSettings(LayerTreeSettings* settings) OVERRIDE { + // 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; + } + + virtual 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(); + } + + virtual void BeginTest() OVERRIDE { + PostSetNeedsCommitToMainThread(); + } + + virtual 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::RectF(0.f, 0.f, 5.f, 5.f)); + child_->SetNeedsDisplayRect(gfx::RectF(0.f, 0.f, 5.f, 5.f)); + 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; + } + } + + virtual void CommitCompleteOnThread(LayerTreeHostImpl* impl) OVERRIDE { + ASSERT_EQ(1u, layer_tree_host()->settings().max_partial_texture_updates); + + TestWebGraphicsContext3D* context = static_cast<TestWebGraphicsContext3D*>( + impl->output_surface()->context3d()); + + 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: + // Number of textures should be two for each content layer. + ASSERT_EQ(4u, context->NumTextures()); + // Number of textures used for commit should be one for each content + // layer. + EXPECT_EQ(2u, context->NumUsedTextures()); + + // 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))); + + context->ResetUsedTextures(); + break; + case 2: + // Number of textures should be two for each content layer. + ASSERT_EQ(4u, context->NumTextures()); + // Number of textures used for commit should be one for each content + // layer. + EXPECT_EQ(2u, context->NumUsedTextures()); + + // One content layer does a partial update also. + EXPECT_TRUE(context->UsedTexture(context->TextureAt(2))); + EXPECT_FALSE(context->UsedTexture(context->TextureAt(3))); + + 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; + } + } + + virtual void DrawLayersOnThread(LayerTreeHostImpl* impl) OVERRIDE { + EXPECT_LT(impl->active_tree()->source_frame_number(), 5); + + TestWebGraphicsContext3D* context = static_cast<TestWebGraphicsContext3D*>( + impl->output_surface()->context3d()); + + // 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(); + } + + virtual 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 LayerTreeHostTestFinishAllRendering : public LayerTreeHostTest { + public: + LayerTreeHostTestFinishAllRendering() : once_(false), draw_count_(0) {} + + virtual void BeginTest() OVERRIDE { + layer_tree_host()->SetNeedsRedraw(); + PostSetNeedsCommitToMainThread(); + } + + virtual void DidCommitAndDrawFrame() OVERRIDE { + if (once_) + return; + once_ = true; + layer_tree_host()->SetNeedsRedraw(); + layer_tree_host()->AcquireLayerTextures(); + { + base::AutoLock lock(lock_); + draw_count_ = 0; + } + layer_tree_host()->FinishAllRendering(); + { + base::AutoLock lock(lock_); + EXPECT_EQ(0, draw_count_); + } + EndTest(); + } + + virtual void DrawLayersOnThread(LayerTreeHostImpl* impl) OVERRIDE { + base::AutoLock lock(lock_); + ++draw_count_; + } + + virtual void AfterTest() OVERRIDE {} + + private: + bool once_; + base::Lock lock_; + int draw_count_; +}; + +SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestFinishAllRendering); + +class LayerTreeHostTestCompositeAndReadbackCleanup : public LayerTreeHostTest { + public: + virtual void BeginTest() OVERRIDE { + Layer* root_layer = layer_tree_host()->root_layer(); + + char pixels[4]; + layer_tree_host()->CompositeAndReadback(static_cast<void*>(&pixels), + gfx::Rect(0, 0, 1, 1)); + EXPECT_FALSE(root_layer->render_surface()); + + EndTest(); + } + + virtual void AfterTest() OVERRIDE {} +}; + +SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestCompositeAndReadbackCleanup); + +class LayerTreeHostTestSurfaceNotAllocatedForLayersOutsideMemoryLimit + : public LayerTreeHostTest { + protected: + virtual void InitializeSettings(LayerTreeSettings* settings) OVERRIDE { + settings->cache_render_pass_contents = true; + } + + virtual 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(); + } + + virtual void BeginTest() OVERRIDE { + PostSetNeedsCommitToMainThread(); + } + + virtual void DrawLayersOnThread(LayerTreeHostImpl* host_impl) OVERRIDE { + Renderer* renderer = host_impl->renderer(); + RenderPass::Id surface1_render_pass_id = host_impl->active_tree() + ->root_layer()->children()[0]->render_surface()->RenderPassId(); + RenderPass::Id surface2_render_pass_id = + host_impl->active_tree()->root_layer()->children()[0]->children()[0] + ->render_surface()->RenderPassId(); + + switch (host_impl->active_tree()->source_frame_number()) { + case 0: + EXPECT_TRUE(renderer->HaveCachedResourcesForRenderPassId( + surface1_render_pass_id)); + EXPECT_TRUE(renderer->HaveCachedResourcesForRenderPassId( + 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)); + host_impl->SetDiscardBackBufferWhenNotVisible(true); + break; + case 1: + EXPECT_FALSE(renderer->HaveCachedResourcesForRenderPassId( + surface1_render_pass_id)); + EXPECT_FALSE(renderer->HaveCachedResourcesForRenderPassId( + surface2_render_pass_id)); + + EndTest(); + break; + } + } + + virtual void DidCommitAndDrawFrame() OVERRIDE { + if (layer_tree_host()->source_frame_number() < 2) + root_layer_->SetNeedsDisplay(); + } + + virtual 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_TEST_F( + LayerTreeHostTestSurfaceNotAllocatedForLayersOutsideMemoryLimit); + +class EvictionTestLayer : public Layer { + public: + static scoped_refptr<EvictionTestLayer> Create() { + return make_scoped_refptr(new EvictionTestLayer()); + } + + virtual bool Update(ResourceUpdateQueue*, + const OcclusionTracker*) OVERRIDE; + virtual bool DrawsContent() const OVERRIDE { return true; } + + virtual scoped_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl) + OVERRIDE; + virtual void PushPropertiesTo(LayerImpl* impl) OVERRIDE; + virtual void SetTexturePriorities(const PriorityCalculator&) OVERRIDE; + + bool HaveBackingTexture() const { + return texture_.get() ? texture_->have_backing_texture() : false; + } + + private: + EvictionTestLayer() : Layer() {} + virtual ~EvictionTestLayer() {} + + void CreateTextureIfNeeded() { + if (texture_) + return; + texture_ = PrioritizedResource::Create( + layer_tree_host()->contents_texture_manager()); + texture_->SetDimensions(gfx::Size(10, 10), GL_RGBA); + bitmap_.setConfig(SkBitmap::kARGB_8888_Config, 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)); + } + virtual ~EvictionTestLayerImpl() {} + + virtual void AppendQuads(QuadSink* quad_sink, + 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*) { + 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_) + .PassAs<LayerImpl>(); +} + +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) {} + + virtual 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::PointF(0.f, 0.f), + gfx::PointF(0.f, 0.f), + gfx::Size(10, 20), + true); + + PostSetNeedsCommitToMainThread(); + } + + void PostEvictTextures() { + DCHECK(HasImplThread()); + 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. + virtual 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; + } + } + + virtual void CommitCompleteOnThread(LayerTreeHostImpl* impl) OVERRIDE { + impl_for_evict_textures_ = impl; + } + + virtual 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; + } + } + + virtual void AfterTest() OVERRIDE {} + + private: + FakeContentLayerClient client_; + scoped_refptr<EvictionTestLayer> layer_; + LayerTreeHostImpl* impl_for_evict_textures_; + int num_commits_; +}; + +MULTI_THREAD_TEST_F(LayerTreeHostTestEvictTextures); + +class LayerTreeHostTestContinuousCommit : public LayerTreeHostTest { + public: + LayerTreeHostTestContinuousCommit() + : num_commit_complete_(0), num_draw_layers_(0) {} + + virtual void BeginTest() OVERRIDE { + layer_tree_host()->SetViewportSize(gfx::Size(10, 10)); + layer_tree_host()->root_layer()->SetBounds(gfx::Size(10, 10)); + + PostSetNeedsCommitToMainThread(); + } + + virtual void DidCommit() OVERRIDE { + if (num_draw_layers_ == 2) + return; + layer_tree_host()->SetNeedsCommit(); + } + + virtual void CommitCompleteOnThread(LayerTreeHostImpl* impl) OVERRIDE { + if (num_draw_layers_ == 1) + num_commit_complete_++; + } + + virtual void DrawLayersOnThread(LayerTreeHostImpl* impl) OVERRIDE { + num_draw_layers_++; + if (num_draw_layers_ == 2) + EndTest(); + } + + virtual void AfterTest() OVERRIDE { + // Check that we didn't commit twice between first and second draw. + EXPECT_EQ(1, num_commit_complete_); + } + + private: + int num_commit_complete_; + int num_draw_layers_; +}; + +MULTI_THREAD_TEST_F(LayerTreeHostTestContinuousCommit); + +class LayerTreeHostTestContinuousInvalidate : public LayerTreeHostTest { + public: + LayerTreeHostTestContinuousInvalidate() + : num_commit_complete_(0), num_draw_layers_(0) {} + + virtual 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_->SetAnchorPoint(gfx::PointF(0.f, 0.f)); + content_layer_->SetIsDrawable(true); + layer_tree_host()->root_layer()->AddChild(content_layer_); + + PostSetNeedsCommitToMainThread(); + } + + virtual void DidCommitAndDrawFrame() OVERRIDE { + if (num_draw_layers_ == 2) + return; + content_layer_->SetNeedsDisplay(); + } + + virtual void CommitCompleteOnThread(LayerTreeHostImpl* impl) OVERRIDE { + if (num_draw_layers_ == 1) + num_commit_complete_++; + } + + virtual void DrawLayersOnThread(LayerTreeHostImpl* impl) OVERRIDE { + num_draw_layers_++; + if (num_draw_layers_ == 2) + EndTest(); + } + + virtual 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_TEST_F(LayerTreeHostTestContinuousInvalidate); + +class LayerTreeHostTestDeferCommits : public LayerTreeHostTest { + public: + LayerTreeHostTestDeferCommits() + : num_commits_deferred_(0), num_complete_commits_(0) {} + + virtual void BeginTest() OVERRIDE { PostSetNeedsCommitToMainThread(); } + + virtual void DidDeferCommit() OVERRIDE { + num_commits_deferred_++; + layer_tree_host()->SetDeferCommits(false); + } + + virtual 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; + } + } + + virtual void AfterTest() OVERRIDE { + EXPECT_EQ(1, num_commits_deferred_); + EXPECT_EQ(2, num_complete_commits_); + } + + private: + int num_commits_deferred_; + int num_complete_commits_; +}; + +MULTI_THREAD_TEST_F(LayerTreeHostTestDeferCommits); + +class LayerTreeHostWithProxy : public LayerTreeHost { + public: + LayerTreeHostWithProxy(FakeLayerTreeHostClient* client, + const LayerTreeSettings& settings, + scoped_ptr<FakeProxy> proxy) + : LayerTreeHost(client, settings) { + proxy->SetLayerTreeHost(this); + EXPECT_TRUE(InitializeForTesting(proxy.PassAs<Proxy>())); + } +}; + +TEST(LayerTreeHostTest, LimitPartialUpdates) { + // When partial updates are not allowed, max updates should be 0. + { + FakeLayerTreeHostClient client(FakeLayerTreeHostClient::DIRECT_3D); + + scoped_ptr<FakeProxy> proxy(new FakeProxy); + proxy->GetRendererCapabilities().allow_partial_texture_updates = false; + proxy->SetMaxPartialTextureUpdates(5); + + LayerTreeSettings settings; + settings.max_partial_texture_updates = 10; + + LayerTreeHostWithProxy host(&client, settings, proxy.Pass()); + EXPECT_TRUE(host.InitializeOutputSurfaceIfNeeded()); + + EXPECT_EQ(0u, host.settings().max_partial_texture_updates); + } + + // When partial updates are allowed, + // max updates should be limited by the proxy. + { + FakeLayerTreeHostClient client(FakeLayerTreeHostClient::DIRECT_3D); + + scoped_ptr<FakeProxy> proxy(new FakeProxy); + proxy->GetRendererCapabilities().allow_partial_texture_updates = true; + proxy->SetMaxPartialTextureUpdates(5); + + LayerTreeSettings settings; + settings.max_partial_texture_updates = 10; + + LayerTreeHostWithProxy host(&client, settings, proxy.Pass()); + EXPECT_TRUE(host.InitializeOutputSurfaceIfNeeded()); + + EXPECT_EQ(5u, host.settings().max_partial_texture_updates); + } + + // When partial updates are allowed, + // max updates should also be limited by the settings. + { + FakeLayerTreeHostClient client(FakeLayerTreeHostClient::DIRECT_3D); + + scoped_ptr<FakeProxy> proxy(new FakeProxy); + proxy->GetRendererCapabilities().allow_partial_texture_updates = true; + proxy->SetMaxPartialTextureUpdates(20); + + LayerTreeSettings settings; + settings.max_partial_texture_updates = 10; + + LayerTreeHostWithProxy host(&client, settings, proxy.Pass()); + EXPECT_TRUE(host.InitializeOutputSurfaceIfNeeded()); + + EXPECT_EQ(10u, host.settings().max_partial_texture_updates); + } +} + +TEST(LayerTreeHostTest, PartialUpdatesWithGLRenderer) { + FakeLayerTreeHostClient client(FakeLayerTreeHostClient::DIRECT_3D); + + LayerTreeSettings settings; + settings.max_partial_texture_updates = 4; + + scoped_ptr<LayerTreeHost> host = + LayerTreeHost::Create(&client, settings, NULL); + EXPECT_TRUE(host->InitializeOutputSurfaceIfNeeded()); + 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; + + scoped_ptr<LayerTreeHost> host = + LayerTreeHost::Create(&client, settings, NULL); + EXPECT_TRUE(host->InitializeOutputSurfaceIfNeeded()); + 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; + + scoped_ptr<LayerTreeHost> host = + LayerTreeHost::Create(&client, settings, NULL); + EXPECT_TRUE(host->InitializeOutputSurfaceIfNeeded()); + EXPECT_EQ(0u, host->settings().max_partial_texture_updates); +} + +TEST(LayerTreeHostTest, + PartialUpdatesWithDelegatingRendererAndSoftwareContent) { + FakeLayerTreeHostClient client(FakeLayerTreeHostClient::DELEGATED_SOFTWARE); + + LayerTreeSettings settings; + settings.max_partial_texture_updates = 4; + + scoped_ptr<LayerTreeHost> host = + LayerTreeHost::Create(&client, settings, NULL); + EXPECT_TRUE(host->InitializeOutputSurfaceIfNeeded()); + EXPECT_EQ(0u, host->settings().max_partial_texture_updates); +} + +class LayerTreeHostTestShutdownWithOnlySomeResourcesEvicted + : public LayerTreeHostTest { + public: + LayerTreeHostTestShutdownWithOnlySomeResourcesEvicted() + : root_layer_(FakeContentLayer::Create(&client_)), + child_layer1_(FakeContentLayer::Create(&client_)), + child_layer2_(FakeContentLayer::Create(&client_)), + num_commits_(0) {} + + virtual 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(); + } + + virtual void DidSetVisibleOnImplTree(LayerTreeHostImpl* host_impl, + bool visible) OVERRIDE { + // One backing should remain unevicted. + EXPECT_EQ(100u * 100u * 4u * 1u, + 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(); + } + + virtual 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, + ManagedMemoryPolicy::CUTOFF_ALLOW_EVERYTHING, + 100 * 100 * 4 * 1, + ManagedMemoryPolicy::CUTOFF_ALLOW_EVERYTHING, + 1000)); + host_impl->SetDiscardBackBufferWhenNotVisible(true); + 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; + } + } + + virtual 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_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_; } + + virtual void PaintContents(SkCanvas* canvas, + gfx::Rect clip, + gfx::RectF* opaque) OVERRIDE { + ++paint_count_; + } + virtual void DidChangeLayerCanUseLCDText() OVERRIDE { + ++lcd_notification_count_; + layer_->SetNeedsDisplay(); + } + + private: + Layer* layer_; + int paint_count_; + int lcd_notification_count_; + }; + + virtual void SetupTree() OVERRIDE { + scoped_refptr<ContentLayer> 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(); + } + + virtual void BeginTest() OVERRIDE { PostSetNeedsCommitToMainThread(); } + virtual void AfterTest() OVERRIDE {} + + virtual void DidCommit() OVERRIDE { + switch (layer_tree_host()->source_frame_number()) { + case 1: + // The first update consists 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 not 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_THREAD_TEST_F(LayerTreeHostTestLCDNotification); + +// Verify that the BeginFrame notification is used to initiate rendering. +class LayerTreeHostTestBeginFrameNotification : public LayerTreeHostTest { + public: + virtual void InitializeSettings(LayerTreeSettings* settings) OVERRIDE { + settings->begin_frame_scheduling_enabled = true; + } + + virtual void BeginTest() OVERRIDE { + // This will trigger a SetNeedsBeginFrame which will trigger a BeginFrame. + PostSetNeedsCommitToMainThread(); + } + + virtual bool PrepareToDrawOnThread( + LayerTreeHostImpl* host_impl, + LayerTreeHostImpl::FrameData* frame, + bool result) OVERRIDE { + EndTest(); + return true; + } + + virtual void AfterTest() OVERRIDE {} + + private: + base::TimeTicks frame_time_; +}; + +MULTI_THREAD_TEST_F(LayerTreeHostTestBeginFrameNotification); + +class LayerTreeHostTestBeginFrameNotificationShutdownWhileEnabled + : public LayerTreeHostTest { + public: + virtual void InitializeSettings(LayerTreeSettings* settings) OVERRIDE { + settings->begin_frame_scheduling_enabled = true; + settings->using_synchronous_renderer_compositor = true; + } + + virtual void BeginTest() OVERRIDE { PostSetNeedsCommitToMainThread(); } + + virtual 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))); + } + + virtual void AfterTest() OVERRIDE {} +}; + +MULTI_THREAD_TEST_F( + LayerTreeHostTestBeginFrameNotificationShutdownWhileEnabled); + +class LayerTreeHostTestUninvertibleTransformDoesNotBlockActivation + : public LayerTreeHostTest { + protected: + virtual void InitializeSettings(LayerTreeSettings* settings) OVERRIDE { + settings->impl_side_painting = true; + } + + virtual 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); + } + + virtual void BeginTest() OVERRIDE { + PostSetNeedsCommitToMainThread(); + } + + virtual void DidActivateTreeOnThread(LayerTreeHostImpl* host_impl) OVERRIDE { + EndTest(); + } + + virtual 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; } + + virtual void PaintContents(SkCanvas* canvas, + gfx::Rect clip, + gfx::RectF* opaque) OVERRIDE { + layer_->SetBounds(gfx::Size(2, 2)); + } + + virtual void DidChangeLayerCanUseLCDText() OVERRIDE {} + + private: + Layer* layer_; + }; + + LayerTreeHostTestChangeLayerPropertiesInPaintContents() : num_commits_(0) {} + + virtual void SetupTree() OVERRIDE { + scoped_refptr<ContentLayer> 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()); + + LayerTreeHostTest::SetupTree(); + } + + virtual void BeginTest() OVERRIDE { PostSetNeedsCommitToMainThread(); } + virtual void AfterTest() OVERRIDE {} + + virtual void CommitCompleteOnThread(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_THREAD_TEST_F(LayerTreeHostTestChangeLayerPropertiesInPaintContents); + +class MockIOSurfaceWebGraphicsContext3D : public FakeWebGraphicsContext3D { + public: + MockIOSurfaceWebGraphicsContext3D() + : FakeWebGraphicsContext3D() {} + + virtual WebKit::WebGLId createTexture() OVERRIDE { + return 1; + } + + virtual WebKit::WebString getString(WebKit::WGC3Denum name) OVERRIDE { + if (name == GL_EXTENSIONS) { + return WebKit::WebString( + "GL_CHROMIUM_iosurface GL_ARB_texture_rectangle"); + } + return WebKit::WebString(); + } + + MOCK_METHOD1(activeTexture, void(WebKit::WGC3Denum texture)); + MOCK_METHOD2(bindTexture, void(WebKit::WGC3Denum target, + WebKit::WebGLId texture_id)); + MOCK_METHOD3(texParameteri, void(WebKit::WGC3Denum target, + WebKit::WGC3Denum pname, + WebKit::WGC3Dint param)); + MOCK_METHOD5(texImageIOSurface2DCHROMIUM, void(WebKit::WGC3Denum target, + WebKit::WGC3Dint width, + WebKit::WGC3Dint height, + WebKit::WGC3Duint ioSurfaceId, + WebKit::WGC3Duint plane)); + MOCK_METHOD4(drawElements, void(WebKit::WGC3Denum mode, + WebKit::WGC3Dsizei count, + WebKit::WGC3Denum type, + WebKit::WGC3Dintptr offset)); +}; + + +class LayerTreeHostTestIOSurfaceDrawing : public LayerTreeHostTest { + protected: + virtual scoped_ptr<OutputSurface> CreateOutputSurface(bool fallback) + OVERRIDE { + scoped_ptr<MockIOSurfaceWebGraphicsContext3D> context( + new MockIOSurfaceWebGraphicsContext3D); + mock_context_ = context.get(); + scoped_ptr<OutputSurface> output_surface = FakeOutputSurface::Create3d( + context.PassAs<WebKit::WebGraphicsContext3D>()).PassAs<OutputSurface>(); + return output_surface.Pass(); + } + + virtual 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->SetAnchorPoint(gfx::PointF()); + io_surface_layer->SetIsDrawable(true); + io_surface_layer->SetIOSurfaceProperties(io_surface_id_, io_surface_size_); + layer_tree_host()->root_layer()->AddChild(io_surface_layer); + } + + virtual void BeginTest() OVERRIDE { + PostSetNeedsCommitToMainThread(); + } + + virtual void DidActivateTreeOnThread(LayerTreeHostImpl* host_impl) OVERRIDE { + // 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_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()); + } + + virtual bool PrepareToDrawOnThread( + LayerTreeHostImpl* host_impl, + LayerTreeHostImpl::FrameData* frame, + bool result) OVERRIDE { + Mock::VerifyAndClearExpectations(&mock_context_); + + // The io surface layer's texture is drawn. + EXPECT_CALL(*mock_context_, activeTexture(GL_TEXTURE0)) + .Times(AtLeast(1)); + EXPECT_CALL(*mock_context_, bindTexture(GL_TEXTURE_RECTANGLE_ARB, 1)) + .Times(1); + EXPECT_CALL(*mock_context_, drawElements(GL_TRIANGLES, 6, _, _)) + .Times(AtLeast(1)); + + return result; + } + + virtual void DrawLayersOnThread(LayerTreeHostImpl* host_impl) OVERRIDE { + Mock::VerifyAndClearExpectations(&mock_context_); + EndTest(); + } + + virtual void AfterTest() OVERRIDE {} + + int io_surface_id_; + MockIOSurfaceWebGraphicsContext3D* mock_context_; + gfx::Size io_surface_size_; +}; + +// TODO(danakj): IOSurface layer can not be transported. crbug.com/239335 +SINGLE_AND_MULTI_THREAD_DIRECT_RENDERER_TEST_F( + LayerTreeHostTestIOSurfaceDrawing); + +class LayerTreeHostTestAsyncReadback : public LayerTreeHostTest { + protected: + virtual void SetupTree() OVERRIDE { + root = FakeContentLayer::Create(&client_); + root->SetBounds(gfx::Size(20, 20)); + + child = FakeContentLayer::Create(&client_); + child->SetBounds(gfx::Size(10, 10)); + root->AddChild(child); + + layer_tree_host()->SetRootLayer(root); + LayerTreeHostTest::SetupTree(); + } + + virtual void BeginTest() OVERRIDE { + PostSetNeedsCommitToMainThread(); + } + + virtual void DidCommitAndDrawFrame() OVERRIDE { + WaitForCallback(); + } + + void WaitForCallback() { + base::MessageLoop::current()->PostTask( + FROM_HERE, + base::Bind( + &LayerTreeHostTestAsyncReadback::NextStep, + base::Unretained(this))); + } + + void NextStep() { + int frame = layer_tree_host()->source_frame_number(); + switch (frame) { + case 1: + child->RequestCopyOfOutput(CopyOutputRequest::CreateBitmapRequest( + base::Bind(&LayerTreeHostTestAsyncReadback::CopyOutputCallback, + base::Unretained(this)))); + EXPECT_EQ(0u, callbacks_.size()); + break; + case 2: + if (callbacks_.size() < 1u) { + WaitForCallback(); + return; + } + EXPECT_EQ(1u, callbacks_.size()); + EXPECT_EQ(gfx::Size(10, 10).ToString(), callbacks_[0].ToString()); + + child->RequestCopyOfOutput(CopyOutputRequest::CreateBitmapRequest( + base::Bind(&LayerTreeHostTestAsyncReadback::CopyOutputCallback, + base::Unretained(this)))); + root->RequestCopyOfOutput(CopyOutputRequest::CreateBitmapRequest( + base::Bind(&LayerTreeHostTestAsyncReadback::CopyOutputCallback, + base::Unretained(this)))); + child->RequestCopyOfOutput(CopyOutputRequest::CreateBitmapRequest( + base::Bind(&LayerTreeHostTestAsyncReadback::CopyOutputCallback, + base::Unretained(this)))); + EXPECT_EQ(1u, callbacks_.size()); + break; + case 3: + if (callbacks_.size() < 4u) { + WaitForCallback(); + return; + } + EXPECT_EQ(4u, callbacks_.size()); + // The child was copied to a bitmap and passed back twice. + EXPECT_EQ(gfx::Size(10, 10).ToString(), callbacks_[1].ToString()); + EXPECT_EQ(gfx::Size(10, 10).ToString(), callbacks_[2].ToString()); + // The root was copied to a bitmap and passed back also. + EXPECT_EQ(gfx::Size(20, 20).ToString(), callbacks_[3].ToString()); + EndTest(); + break; + } + } + + void CopyOutputCallback(scoped_ptr<CopyOutputResult> result) { + EXPECT_TRUE(layer_tree_host()->proxy()->IsMainThread()); + EXPECT_TRUE(result->HasBitmap()); + scoped_ptr<SkBitmap> bitmap = result->TakeBitmap().Pass(); + EXPECT_EQ(result->size().ToString(), + gfx::Size(bitmap->width(), bitmap->height()).ToString()); + callbacks_.push_back(result->size()); + } + + virtual void AfterTest() OVERRIDE { + EXPECT_EQ(4u, callbacks_.size()); + } + + virtual scoped_ptr<OutputSurface> CreateOutputSurface(bool fallback) + OVERRIDE { + if (use_gl_renderer_) + return FakeOutputSurface::Create3d().PassAs<OutputSurface>(); + return FakeOutputSurface::CreateSoftware( + make_scoped_ptr(new SoftwareOutputDevice)).PassAs<OutputSurface>(); + } + + bool use_gl_renderer_; + std::vector<gfx::Size> callbacks_; + FakeContentLayerClient client_; + scoped_refptr<FakeContentLayer> root; + scoped_refptr<FakeContentLayer> child; +}; + +// Readback can't be done with a delegating renderer. +TEST_F(LayerTreeHostTestAsyncReadback, GLRenderer_RunSingleThread) { + use_gl_renderer_ = true; + RunTest(false, false, false); +} + +TEST_F(LayerTreeHostTestAsyncReadback, + GLRenderer_RunMultiThread_MainThreadPainting) { + use_gl_renderer_ = true; + RunTest(true, false, false); +} + +TEST_F(LayerTreeHostTestAsyncReadback, + GLRenderer_RunMultiThread_ImplSidePainting) { + use_gl_renderer_ = true; + RunTest(true, false, true); +} + +TEST_F(LayerTreeHostTestAsyncReadback, SoftwareRenderer_RunSingleThread) { + use_gl_renderer_ = false; + RunTest(false, false, false); +} + +TEST_F(LayerTreeHostTestAsyncReadback, + SoftwareRenderer_RunMultiThread_MainThreadPainting) { + use_gl_renderer_ = false; + RunTest(true, false, false); +} + +TEST_F(LayerTreeHostTestAsyncReadback, + SoftwareRenderer_RunMultiThread_ImplSidePainting) { + use_gl_renderer_ = false; + RunTest(true, false, true); +} + +class LayerTreeHostTestAsyncReadbackLayerDestroyed : public LayerTreeHostTest { + protected: + virtual void SetupTree() OVERRIDE { + root_ = FakeContentLayer::Create(&client_); + root_->SetBounds(gfx::Size(20, 20)); + + main_destroyed_ = FakeContentLayer::Create(&client_); + main_destroyed_->SetBounds(gfx::Size(15, 15)); + root_->AddChild(main_destroyed_); + + impl_destroyed_ = FakeContentLayer::Create(&client_); + impl_destroyed_->SetBounds(gfx::Size(10, 10)); + root_->AddChild(impl_destroyed_); + + layer_tree_host()->SetRootLayer(root_); + LayerTreeHostTest::SetupTree(); + } + + virtual void BeginTest() OVERRIDE { + callback_count_ = 0; + PostSetNeedsCommitToMainThread(); + } + + virtual void DidCommit() OVERRIDE { + int frame = layer_tree_host()->source_frame_number(); + switch (frame) { + case 1: + main_destroyed_->RequestCopyOfOutput( + CopyOutputRequest::CreateBitmapRequest(base::Bind( + &LayerTreeHostTestAsyncReadbackLayerDestroyed:: + CopyOutputCallback, + base::Unretained(this)))); + impl_destroyed_->RequestCopyOfOutput( + CopyOutputRequest::CreateBitmapRequest(base::Bind( + &LayerTreeHostTestAsyncReadbackLayerDestroyed:: + CopyOutputCallback, + base::Unretained(this)))); + EXPECT_EQ(0, callback_count_); + + // Destroy the main thread layer right away. + main_destroyed_->RemoveFromParent(); + main_destroyed_ = NULL; + + // Should callback with a NULL bitmap. + EXPECT_EQ(1, callback_count_); + + // Prevent drawing so we can't make a copy of the impl_destroyed layer. + layer_tree_host()->SetViewportSize(gfx::Size()); + break; + case 2: + // Flush the message loops and make sure the callbacks run. + layer_tree_host()->SetNeedsCommit(); + break; + case 3: + // No drawing means no readback yet. + EXPECT_EQ(1, callback_count_); + + // Destroy the impl thread layer. + impl_destroyed_->RemoveFromParent(); + impl_destroyed_ = NULL; + + // No callback yet because it's on the impl side. + EXPECT_EQ(1, callback_count_); + break; + case 4: + // Flush the message loops and make sure the callbacks run. + layer_tree_host()->SetNeedsCommit(); + break; + case 5: + // We should get another callback with a NULL bitmap. + EXPECT_EQ(2, callback_count_); + EndTest(); + break; + } + } + + void CopyOutputCallback(scoped_ptr<CopyOutputResult> result) { + EXPECT_TRUE(layer_tree_host()->proxy()->IsMainThread()); + EXPECT_TRUE(result->IsEmpty()); + ++callback_count_; + } + + virtual void AfterTest() OVERRIDE {} + + int callback_count_; + FakeContentLayerClient client_; + scoped_refptr<FakeContentLayer> root_; + scoped_refptr<FakeContentLayer> main_destroyed_; + scoped_refptr<FakeContentLayer> impl_destroyed_; +}; + +SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestAsyncReadbackLayerDestroyed); + +class LayerTreeHostTestAsyncReadbackInHiddenSubtree : public LayerTreeHostTest { + protected: + virtual void SetupTree() OVERRIDE { + root_ = FakeContentLayer::Create(&client_); + root_->SetBounds(gfx::Size(20, 20)); + + grand_parent_layer_ = FakeContentLayer::Create(&client_); + grand_parent_layer_->SetBounds(gfx::Size(15, 15)); + root_->AddChild(grand_parent_layer_); + + // parent_layer_ owns a render surface. + parent_layer_ = FakeContentLayer::Create(&client_); + parent_layer_->SetBounds(gfx::Size(15, 15)); + parent_layer_->SetForceRenderSurface(true); + grand_parent_layer_->AddChild(parent_layer_); + + copy_layer_ = FakeContentLayer::Create(&client_); + copy_layer_->SetBounds(gfx::Size(10, 10)); + parent_layer_->AddChild(copy_layer_); + + layer_tree_host()->SetRootLayer(root_); + LayerTreeHostTest::SetupTree(); + } + + void AddCopyRequest(Layer* layer) { + layer->RequestCopyOfOutput( + CopyOutputRequest::CreateBitmapRequest(base::Bind( + &LayerTreeHostTestAsyncReadbackInHiddenSubtree::CopyOutputCallback, + base::Unretained(this)))); + } + + virtual void BeginTest() OVERRIDE { + callback_count_ = 0; + PostSetNeedsCommitToMainThread(); + + AddCopyRequest(copy_layer_.get()); + } + + void CopyOutputCallback(scoped_ptr<CopyOutputResult> result) { + EXPECT_TRUE(layer_tree_host()->proxy()->IsMainThread()); + EXPECT_EQ(copy_layer_->bounds().ToString(), result->size().ToString()); + ++callback_count_; + + switch (callback_count_) { + case 1: + // Hide the copy request layer. + grand_parent_layer_->SetHideLayerAndSubtree(false); + parent_layer_->SetHideLayerAndSubtree(false); + copy_layer_->SetHideLayerAndSubtree(true); + AddCopyRequest(copy_layer_.get()); + break; + case 2: + // Hide the copy request layer's parent only. + grand_parent_layer_->SetHideLayerAndSubtree(false); + parent_layer_->SetHideLayerAndSubtree(true); + copy_layer_->SetHideLayerAndSubtree(false); + AddCopyRequest(copy_layer_.get()); + break; + case 3: + // Hide the copy request layer's grand parent only. + grand_parent_layer_->SetHideLayerAndSubtree(true); + parent_layer_->SetHideLayerAndSubtree(false); + copy_layer_->SetHideLayerAndSubtree(false); + AddCopyRequest(copy_layer_.get()); + break; + case 4: + // Hide the copy request layer's parent and grandparent. + grand_parent_layer_->SetHideLayerAndSubtree(true); + parent_layer_->SetHideLayerAndSubtree(true); + copy_layer_->SetHideLayerAndSubtree(false); + AddCopyRequest(copy_layer_.get()); + break; + case 5: + // Hide the copy request layer as well as its parent and grandparent. + grand_parent_layer_->SetHideLayerAndSubtree(true); + parent_layer_->SetHideLayerAndSubtree(true); + copy_layer_->SetHideLayerAndSubtree(true); + AddCopyRequest(copy_layer_.get()); + break; + case 6: + EndTest(); + break; + } + } + + virtual void AfterTest() OVERRIDE {} + + int callback_count_; + FakeContentLayerClient client_; + scoped_refptr<FakeContentLayer> root_; + scoped_refptr<FakeContentLayer> grand_parent_layer_; + scoped_refptr<FakeContentLayer> parent_layer_; + scoped_refptr<FakeContentLayer> copy_layer_; +}; + +// No output to copy for delegated renderers. +SINGLE_AND_MULTI_THREAD_DIRECT_RENDERER_TEST_F( + LayerTreeHostTestAsyncReadbackInHiddenSubtree); + +class LayerTreeHostTestHiddenSurfaceNotAllocatedForSubtreeCopyRequest + : public LayerTreeHostTest { + protected: + virtual void InitializeSettings(LayerTreeSettings* settings) OVERRIDE { + settings->cache_render_pass_contents = true; + } + + virtual void SetupTree() OVERRIDE { + root_ = FakeContentLayer::Create(&client_); + root_->SetBounds(gfx::Size(20, 20)); + + grand_parent_layer_ = FakeContentLayer::Create(&client_); + grand_parent_layer_->SetBounds(gfx::Size(15, 15)); + grand_parent_layer_->SetHideLayerAndSubtree(true); + root_->AddChild(grand_parent_layer_); + + // parent_layer_ owns a render surface. + parent_layer_ = FakeContentLayer::Create(&client_); + parent_layer_->SetBounds(gfx::Size(15, 15)); + parent_layer_->SetForceRenderSurface(true); + grand_parent_layer_->AddChild(parent_layer_); + + copy_layer_ = FakeContentLayer::Create(&client_); + copy_layer_->SetBounds(gfx::Size(10, 10)); + parent_layer_->AddChild(copy_layer_); + + layer_tree_host()->SetRootLayer(root_); + LayerTreeHostTest::SetupTree(); + } + + virtual void BeginTest() OVERRIDE { + did_draw_ = false; + PostSetNeedsCommitToMainThread(); + + copy_layer_->RequestCopyOfOutput( + CopyOutputRequest::CreateBitmapRequest(base::Bind( + &LayerTreeHostTestHiddenSurfaceNotAllocatedForSubtreeCopyRequest:: + CopyOutputCallback, + base::Unretained(this)))); + } + + void CopyOutputCallback(scoped_ptr<CopyOutputResult> result) { + EXPECT_TRUE(layer_tree_host()->proxy()->IsMainThread()); + EXPECT_EQ(copy_layer_->bounds().ToString(), result->size().ToString()); + EndTest(); + } + + virtual void DrawLayersOnThread(LayerTreeHostImpl* host_impl) OVERRIDE { + Renderer* renderer = host_impl->renderer(); + + LayerImpl* root = host_impl->active_tree()->root_layer(); + LayerImpl* grand_parent = root->children()[0]; + LayerImpl* parent = grand_parent->children()[0]; + LayerImpl* copy_layer = parent->children()[0]; + + // |parent| owns a surface, but it was hidden and not part of the copy + // request so it should not allocate any resource. + EXPECT_FALSE(renderer->HaveCachedResourcesForRenderPassId( + parent->render_surface()->RenderPassId())); + + // |copy_layer| should have been rendered to a texture since it was needed + // for a copy request. + EXPECT_TRUE(renderer->HaveCachedResourcesForRenderPassId( + copy_layer->render_surface()->RenderPassId())); + + did_draw_ = true; + } + + virtual void AfterTest() OVERRIDE { EXPECT_TRUE(did_draw_); } + + FakeContentLayerClient client_; + bool did_draw_; + scoped_refptr<FakeContentLayer> root_; + scoped_refptr<FakeContentLayer> grand_parent_layer_; + scoped_refptr<FakeContentLayer> parent_layer_; + scoped_refptr<FakeContentLayer> copy_layer_; +}; + +// No output to copy for delegated renderers. +SINGLE_AND_MULTI_THREAD_DIRECT_RENDERER_TEST_F( + LayerTreeHostTestHiddenSurfaceNotAllocatedForSubtreeCopyRequest); + +class LayerTreeHostTestAsyncReadbackClippedOut : public LayerTreeHostTest { + protected: + virtual void SetupTree() OVERRIDE { + root_ = FakeContentLayer::Create(&client_); + root_->SetBounds(gfx::Size(20, 20)); + + parent_layer_ = FakeContentLayer::Create(&client_); + parent_layer_->SetBounds(gfx::Size(15, 15)); + parent_layer_->SetMasksToBounds(true); + root_->AddChild(parent_layer_); + + copy_layer_ = FakeContentLayer::Create(&client_); + copy_layer_->SetPosition(gfx::Point(15, 15)); + copy_layer_->SetBounds(gfx::Size(10, 10)); + parent_layer_->AddChild(copy_layer_); + + layer_tree_host()->SetRootLayer(root_); + LayerTreeHostTest::SetupTree(); + } + + virtual void BeginTest() OVERRIDE { + PostSetNeedsCommitToMainThread(); + + copy_layer_->RequestCopyOfOutput( + CopyOutputRequest::CreateBitmapRequest(base::Bind( + &LayerTreeHostTestAsyncReadbackClippedOut::CopyOutputCallback, + base::Unretained(this)))); + } + + void CopyOutputCallback(scoped_ptr<CopyOutputResult> result) { + // We should still get a callback with no output if the copy requested layer + // was completely clipped away. + EXPECT_TRUE(layer_tree_host()->proxy()->IsMainThread()); + EXPECT_EQ(gfx::Size().ToString(), result->size().ToString()); + EndTest(); + } + + virtual void AfterTest() OVERRIDE {} + + FakeContentLayerClient client_; + scoped_refptr<FakeContentLayer> root_; + scoped_refptr<FakeContentLayer> parent_layer_; + scoped_refptr<FakeContentLayer> copy_layer_; +}; + +// No output to copy for delegated renderers. +SINGLE_AND_MULTI_THREAD_DIRECT_RENDERER_TEST_F( + LayerTreeHostTestAsyncReadbackClippedOut); + +class LayerTreeHostTestAsyncTwoReadbacksWithoutDraw : public LayerTreeHostTest { + protected: + virtual void SetupTree() OVERRIDE { + root_ = FakeContentLayer::Create(&client_); + root_->SetBounds(gfx::Size(20, 20)); + + copy_layer_ = FakeContentLayer::Create(&client_); + copy_layer_->SetBounds(gfx::Size(10, 10)); + root_->AddChild(copy_layer_); + + layer_tree_host()->SetRootLayer(root_); + LayerTreeHostTest::SetupTree(); + } + + void AddCopyRequest(Layer* layer) { + layer->RequestCopyOfOutput( + CopyOutputRequest::CreateBitmapRequest(base::Bind( + &LayerTreeHostTestAsyncTwoReadbacksWithoutDraw::CopyOutputCallback, + base::Unretained(this)))); + } + + virtual void BeginTest() OVERRIDE { + saw_copy_request_ = false; + callback_count_ = 0; + PostSetNeedsCommitToMainThread(); + + // Prevent drawing. + layer_tree_host()->SetViewportSize(gfx::Size(0, 0)); + + AddCopyRequest(copy_layer_.get()); + } + + virtual void DidActivateTreeOnThread(LayerTreeHostImpl* impl) OVERRIDE { + if (impl->active_tree()->source_frame_number() == 0) { + LayerImpl* root = impl->active_tree()->root_layer(); + EXPECT_TRUE(root->children()[0]->HasCopyRequest()); + saw_copy_request_ = true; + } + } + + virtual void DidCommit() OVERRIDE { + if (layer_tree_host()->source_frame_number() == 1) { + // Allow drawing. + layer_tree_host()->SetViewportSize(gfx::Size(root_->bounds())); + + AddCopyRequest(copy_layer_.get()); + } + } + + void CopyOutputCallback(scoped_ptr<CopyOutputResult> result) { + EXPECT_TRUE(layer_tree_host()->proxy()->IsMainThread()); + EXPECT_EQ(copy_layer_->bounds().ToString(), result->size().ToString()); + ++callback_count_; + + if (callback_count_ == 2) + EndTest(); + } + + virtual void AfterTest() OVERRIDE { EXPECT_TRUE(saw_copy_request_); } + + bool saw_copy_request_; + int callback_count_; + FakeContentLayerClient client_; + scoped_refptr<FakeContentLayer> root_; + scoped_refptr<FakeContentLayer> copy_layer_; +}; + +// No output to copy for delegated renderers. +SINGLE_AND_MULTI_THREAD_DIRECT_RENDERER_TEST_F( + LayerTreeHostTestAsyncTwoReadbacksWithoutDraw); + +class LayerTreeHostTestNumFramesPending : public LayerTreeHostTest { + public: + virtual void BeginTest() OVERRIDE { + frame_ = 0; + PostSetNeedsCommitToMainThread(); + } + + // Round 1: commit + draw + // Round 2: commit only (no draw/swap) + // Round 3: draw only (no commit) + // Round 4: composite & readback (2 commits, no draw/swap) + // Round 5: commit + draw + + virtual 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; + case 3: + // CompositeAndReadback in Round 4, first commit. + EXPECT_EQ(2, frame_); + break; + case 4: + // Round 4 done. + EXPECT_EQ(2, frame_); + layer_tree_host()->SetNeedsCommit(); + layer_tree_host()->SetNeedsRedraw(); + break; + } + } + + virtual void DidCompleteSwapBuffers() OVERRIDE { + int commit = layer_tree_host()->source_frame_number(); + ++frame_; + char pixels[4] = {0}; + 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); + layer_tree_host()->CompositeAndReadback(pixels, gfx::Rect(0, 0, 1, 1)); + break; + case 3: + // Round 5 done. + EXPECT_EQ(5, commit); + EndTest(); + break; + } + } + + virtual void AfterTest() OVERRIDE {} + + protected: + int frame_; +}; + +TEST_F(LayerTreeHostTestNumFramesPending, DelegatingRenderer) { + RunTest(true, true, true); +} + +TEST_F(LayerTreeHostTestNumFramesPending, GLRenderer) { + RunTest(true, false, true); +} + +class LayerTreeHostTestDeferredInitialize : public LayerTreeHostTest { + public: + virtual void InitializeSettings(LayerTreeSettings* settings) OVERRIDE { + // PictureLayer can only be used with impl side painting enabled. + settings->impl_side_painting = true; + settings->solid_color_scrollbars = true; + } + + virtual 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(); + } + + virtual void BeginTest() OVERRIDE { + did_initialize_gl_ = false; + did_release_gl_ = false; + last_source_frame_number_drawn_ = -1; // Never drawn. + PostSetNeedsCommitToMainThread(); + } + + virtual scoped_ptr<OutputSurface> CreateOutputSurface(bool fallback) + OVERRIDE { + scoped_ptr<TestWebGraphicsContext3D> context3d( + TestWebGraphicsContext3D::Create()); + context3d->set_support_swapbuffers_complete_callback(false); + + return FakeOutputSurface::CreateDeferredGL( + scoped_ptr<SoftwareOutputDevice>(new SoftwareOutputDevice)) + .PassAs<OutputSurface>(); + } + + virtual 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. + EXPECT_TRUE(static_cast<FakeOutputSurface*>(host_impl->output_surface()) + ->SetAndInitializeContext3D( + scoped_ptr<WebKit::WebGraphicsContext3D>( + TestWebGraphicsContext3D::Create()))); + 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; + } + + virtual 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); + +// Test for UI Resource management. +class LayerTreeHostTestUIResource : public LayerTreeHostTest { + public: + LayerTreeHostTestUIResource() : num_ui_resources_(0), num_commits_(0) {} + + virtual void BeginTest() OVERRIDE { PostSetNeedsCommitToMainThread(); } + + virtual void DidCommit() OVERRIDE { + int frame = num_commits_; + 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 = static_cast<TestWebGraphicsContext3D*>( + impl->output_surface()->context3d()); + + int frame = num_commits_; + switch (frame) { + case 1: + ASSERT_EQ(0u, context->NumTextures()); + break; + case 2: + // Created two textures. + ASSERT_EQ(2u, context->NumTextures()); + break; + case 3: + // One texture left after one deletion. + ASSERT_EQ(1u, context->NumTextures()); + break; + case 4: + // Resource manager state should not change when delete is called on an + // invalid id. + ASSERT_EQ(1u, context->NumTextures()); + break; + case 5: + // Creation after deletion: two more creates should total up to + // three textures. + ASSERT_EQ(3u, context->NumTextures()); + break; + } + } + + virtual void CommitCompleteOnThread(LayerTreeHostImpl* impl) OVERRIDE { + ++num_commits_; + if (!layer_tree_host()->settings().impl_side_painting) + PerformTest(impl); + } + + virtual void DidActivateTreeOnThread(LayerTreeHostImpl* impl) OVERRIDE { + if (layer_tree_host()->settings().impl_side_painting) + PerformTest(impl); + } + + virtual void AfterTest() OVERRIDE {} + + private: + // Must clear all resources before exiting. + void ClearResources() { + for (int i = 0; i < num_ui_resources_; i++) + ui_resources_[i].reset(); + } + + void CreateResource() { + ui_resources_[num_ui_resources_++] = + FakeScopedUIResource::Create(layer_tree_host()); + } + + scoped_ptr<FakeScopedUIResource> ui_resources_[5]; + int num_ui_resources_; + int num_commits_; +}; + +MULTI_THREAD_TEST_F(LayerTreeHostTestUIResource); + +class PushPropertiesCountingLayer : public Layer { + public: + static scoped_refptr<PushPropertiesCountingLayer> Create() { + return new PushPropertiesCountingLayer(); + } + + virtual void PushPropertiesTo(LayerImpl* layer) OVERRIDE { + Layer::PushPropertiesTo(layer); + push_properties_count_++; + if (persist_needs_push_properties_) + needs_push_properties_ = true; + } + + 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) { + SetAnchorPoint(gfx::PointF()); + SetBounds(gfx::Size(1, 1)); + SetIsDrawable(true); + } + virtual ~PushPropertiesCountingLayer() {} + + size_t push_properties_count_; + bool persist_needs_push_properties_; +}; + +class LayerTreeHostTestLayersPushProperties : public LayerTreeHostTest { + protected: + virtual 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(); + } + + virtual void SetupTree() OVERRIDE { + root_ = PushPropertiesCountingLayer::Create(); + child_ = PushPropertiesCountingLayer::Create(); + child2_ = PushPropertiesCountingLayer::Create(); + grandchild_ = PushPropertiesCountingLayer::Create(); + + if (layer_tree_host()->settings().impl_side_painting) + leaf_picture_layer_ = FakePictureLayer::Create(&client_); + else + leaf_content_layer_ = FakeContentLayer::Create(&client_); + + root_->AddChild(child_); + root_->AddChild(child2_); + child_->AddChild(grandchild_); + if (leaf_picture_layer_) + child2_->AddChild(leaf_picture_layer_); + if (leaf_content_layer_) + child2_->AddChild(leaf_content_layer_); + + other_root_ = PushPropertiesCountingLayer::Create(); + + // Don't set the root layer here. + LayerTreeHostTest::SetupTree(); + } + + virtual 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()); + if (leaf_content_layer_) { + EXPECT_EQ(expected_push_properties_leaf_layer_, + leaf_content_layer_->push_properties_count()); + } + if (leaf_picture_layer_) { + EXPECT_EQ(expected_push_properties_leaf_layer_, + leaf_picture_layer_->push_properties_count()); + } + + // The content/picture 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_content_layer_.get() && leaf_content_layer_->layer_tree_host()) { + EXPECT_FALSE(leaf_content_layer_->descendant_needs_push_properties()); + EXPECT_TRUE(leaf_content_layer_->needs_push_properties()); + } + if (leaf_picture_layer_.get() && leaf_picture_layer_->layer_tree_host()) { + EXPECT_FALSE(leaf_picture_layer_->descendant_needs_push_properties()); + EXPECT_TRUE(leaf_picture_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; + } + + // Content/Picture layers require PushProperties every commit that they are + // in the tree. + if ((leaf_content_layer_.get() && leaf_content_layer_->layer_tree_host()) || + (leaf_picture_layer_.get() && leaf_picture_layer_->layer_tree_host())) + ++expected_push_properties_leaf_layer_; + } + + virtual 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<FakeContentLayer> leaf_content_layer_; + scoped_refptr<FakePictureLayer> leaf_picture_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 LayerTreeHostTestPropertyChangesDuringUpdateArePushed + : public LayerTreeHostTest { + protected: + virtual void BeginTest() OVERRIDE { + PostSetNeedsCommitToMainThread(); + } + + virtual void SetupTree() OVERRIDE { + root_ = Layer::Create(); + root_->SetBounds(gfx::Size(1, 1)); + + bool paint_scrollbar = true; + bool has_thumb = false; + scrollbar_layer_ = + FakeScrollbarLayer::Create(paint_scrollbar, has_thumb, root_->id()); + + root_->AddChild(scrollbar_layer_); + + layer_tree_host()->SetRootLayer(root_); + LayerTreeHostTest::SetupTree(); + } + + virtual 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; + } + } + + virtual void AfterTest() OVERRIDE {} + + scoped_refptr<Layer> root_; + scoped_refptr<FakeScrollbarLayer> scrollbar_layer_; +}; + +MULTI_THREAD_TEST_F(LayerTreeHostTestPropertyChangesDuringUpdateArePushed); + +class LayerTreeHostTestCasePushPropertiesThreeGrandChildren + : public LayerTreeHostTest { + protected: + virtual 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(); + } + + virtual 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(); + } + + virtual 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: + virtual 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: + virtual 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: + virtual 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: + virtual 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: + virtual 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: + virtual 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) {} + + virtual void BeginTest() OVERRIDE { + EXPECT_TRUE(HasImplThread()); + PostSetNeedsCommitToMainThread(); + } + + virtual bool PrepareToDrawOnThread(LayerTreeHostImpl* host_impl, + LayerTreeHostImpl::FrameData* frame_data, + bool 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, + result); + } + + virtual 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) {} + + virtual void BeginTest() OVERRIDE { + ASSERT_TRUE(!!invalidate_layer_) + << "Derived tests must set this in SetupTree"; + + // One initial commit. + PostSetNeedsCommitToMainThread(); + } + + virtual void DidCommitAndDrawFrame() OVERRIDE { + // After commit, invalidate the layer. This should cause a commit. + if (layer_tree_host()->source_frame_number() == 1) + invalidate_layer_->SetNeedsDisplay(); + } + + virtual void DrawLayersOnThread(LayerTreeHostImpl* impl) OVERRIDE { + num_draws_++; + if (impl->active_tree()->source_frame_number() == 1) + EndTest(); + } + + virtual void CommitCompleteOnThread(LayerTreeHostImpl* impl) OVERRIDE { + num_commits_++; + } + + virtual 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_; +}; + +// VideoLayer 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 LayerTreeHostTestVideoLayerInvalidate : public LayerInvalidateCausesDraw { + public: + virtual void SetupTree() OVERRIDE { + LayerTreeHostTest::SetupTree(); + scoped_refptr<VideoLayer> video_layer = VideoLayer::Create(&provider_); + video_layer->SetBounds(gfx::Size(10, 10)); + video_layer->SetIsDrawable(true); + layer_tree_host()->root_layer()->AddChild(video_layer); + + invalidate_layer_ = video_layer; + } + + private: + FakeVideoFrameProvider provider_; +}; + +SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestVideoLayerInvalidate); + +// 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: + virtual 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: + virtual void SetupTree() OVERRIDE { + root_layer_ = Layer::Create(); + root_layer_->SetAnchorPoint(gfx::PointF()); + root_layer_->SetPosition(gfx::Point()); + root_layer_->SetBounds(gfx::Size(10, 10)); + + parent_layer_ = SolidColorLayer::Create(); + parent_layer_->SetAnchorPoint(gfx::PointF()); + 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_->SetAnchorPoint(gfx::PointF()); + 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(); + } + + virtual void BeginTest() OVERRIDE { PostSetNeedsCommitToMainThread(); } + + virtual 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; + } + } + + virtual 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; + } + } + + virtual 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: + virtual void InitializeSettings(LayerTreeSettings* settings) OVERRIDE { + settings->impl_side_painting = true; + } + + virtual void SetupTree() OVERRIDE { + root_layer_ = FakePictureLayer::Create(&client_); + root_layer_->SetAnchorPoint(gfx::PointF()); + root_layer_->SetBounds(gfx::Size(10, 10)); + + layer_tree_host()->SetRootLayer(root_layer_); + LayerTreeHostTest::SetupTree(); + } + + virtual 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(); + } + + virtual 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(); + } + + virtual void AfterTest() OVERRIDE {} + + FakeContentLayerClient client_; + scoped_refptr<FakePictureLayer> root_layer_; +}; + +MULTI_THREAD_TEST_F(LayerTreeHostTestUpdateLayerInEmptyViewport); + +class LayerTreeHostTestAbortEvictedTextures : public LayerTreeHostTest { + public: + LayerTreeHostTestAbortEvictedTextures() + : num_will_begin_frames_(0), num_impl_commits_(0) {} + + protected: + virtual 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(); + } + + virtual void BeginTest() OVERRIDE { PostSetNeedsCommitToMainThread(); } + + virtual void WillBeginFrame() OVERRIDE { + num_will_begin_frames_++; + switch (num_will_begin_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; + } + } + + virtual void BeginCommitOnThread(LayerTreeHostImpl* impl) OVERRIDE { + num_impl_commits_++; + } + + virtual 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; + } + } + + virtual void AfterTest() OVERRIDE { + // Ensure that the commit was truly aborted. + EXPECT_EQ(2, num_will_begin_frames_); + EXPECT_EQ(1, num_impl_commits_); + } + + private: + int num_will_begin_frames_; + int num_impl_commits_; +}; + +// Commits can only be aborted when using the thread proxy. +MULTI_THREAD_TEST_F(LayerTreeHostTestAbortEvictedTextures); + +} // namespace + +} // namespace cc |