| // 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 <stddef.h> |
| #include <stdint.h> |
| |
| #include <algorithm> |
| |
| #include "base/auto_reset.h" |
| #include "base/bind.h" |
| #include "base/location.h" |
| #include "base/single_thread_task_runner.h" |
| #include "base/stl_util.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/synchronization/lock.h" |
| #include "base/threading/thread_task_runner_handle.h" |
| #include "base/time/time.h" |
| #include "build/build_config.h" |
| #include "cc/animation/timing_function.h" |
| #include "cc/input/scroll_elasticity_helper.h" |
| #include "cc/layers/content_layer_client.h" |
| #include "cc/layers/heads_up_display_layer.h" |
| #include "cc/layers/layer_impl.h" |
| #include "cc/layers/painted_scrollbar_layer.h" |
| #include "cc/layers/picture_layer.h" |
| #include "cc/layers/solid_color_layer.h" |
| #include "cc/layers/video_layer.h" |
| #include "cc/paint/image_animation_count.h" |
| #include "cc/resources/ui_resource_manager.h" |
| #include "cc/test/fake_content_layer_client.h" |
| #include "cc/test/fake_layer_tree_host_client.h" |
| #include "cc/test/fake_paint_image_generator.h" |
| #include "cc/test/fake_painted_scrollbar_layer.h" |
| #include "cc/test/fake_picture_layer.h" |
| #include "cc/test/fake_picture_layer_impl.h" |
| #include "cc/test/fake_proxy.h" |
| #include "cc/test/fake_recording_source.h" |
| #include "cc/test/fake_scoped_ui_resource.h" |
| #include "cc/test/fake_video_frame_provider.h" |
| #include "cc/test/geometry_test_utils.h" |
| #include "cc/test/layer_test_common.h" |
| #include "cc/test/layer_tree_test.h" |
| #include "cc/test/push_properties_counting_layer.h" |
| #include "cc/test/push_properties_counting_layer_impl.h" |
| #include "cc/test/render_pass_test_utils.h" |
| #include "cc/test/skia_common.h" |
| #include "cc/trees/clip_node.h" |
| #include "cc/trees/effect_node.h" |
| #include "cc/trees/frame_rate_counter.h" |
| #include "cc/trees/layer_tree_host_common.h" |
| #include "cc/trees/layer_tree_host_impl.h" |
| #include "cc/trees/layer_tree_impl.h" |
| #include "cc/trees/scroll_node.h" |
| #include "cc/trees/single_thread_proxy.h" |
| #include "cc/trees/swap_promise.h" |
| #include "cc/trees/swap_promise_manager.h" |
| #include "cc/trees/transform_node.h" |
| #include "components/ukm/test_ukm_recorder.h" |
| #include "components/viz/common/frame_sinks/begin_frame_args.h" |
| #include "components/viz/common/frame_sinks/copy_output_request.h" |
| #include "components/viz/common/frame_sinks/copy_output_result.h" |
| #include "components/viz/common/quads/draw_quad.h" |
| #include "components/viz/common/quads/render_pass_draw_quad.h" |
| #include "components/viz/common/quads/tile_draw_quad.h" |
| #include "components/viz/service/display/output_surface.h" |
| #include "components/viz/test/begin_frame_args_test.h" |
| #include "components/viz/test/fake_output_surface.h" |
| #include "components/viz/test/test_gles2_interface.h" |
| #include "components/viz/test/test_layer_tree_frame_sink.h" |
| #include "gpu/GLES2/gl2extchromium.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 "third_party/skia/include/gpu/GrContext.h" |
| #include "ui/gfx/geometry/point_conversions.h" |
| #include "ui/gfx/geometry/size_conversions.h" |
| #include "ui/gfx/geometry/vector2d_conversions.h" |
| |
| using testing::_; |
| using testing::AnyNumber; |
| using testing::AtLeast; |
| using testing::Mock; |
| |
| namespace cc { |
| namespace { |
| const char kUserInteraction[] = "Compositor.UserInteraction"; |
| const char kCheckerboardArea[] = "CheckerboardedContentArea"; |
| const char kCheckerboardAreaRatio[] = "CheckerboardedContentAreaRatio"; |
| const char kMissingTiles[] = "NumMissingTiles"; |
| |
| bool LayerSubtreeHasCopyRequest(Layer* layer) { |
| LayerTreeHost* host = layer->layer_tree_host(); |
| int index = layer->effect_tree_index(); |
| auto* node = host->property_trees()->effect_tree.Node(index); |
| return node->subtree_has_copy_request; |
| } |
| |
| class LayerTreeHostTest : public LayerTreeTest {}; |
| |
| class LayerTreeHostTestHasImplThreadTest : public LayerTreeHostTest { |
| public: |
| LayerTreeHostTestHasImplThreadTest() : single_threaded_(false) {} |
| |
| void RunTest(CompositorMode mode) override { |
| single_threaded_ = mode == CompositorMode::SINGLE_THREADED; |
| LayerTreeHostTest::RunTest(mode); |
| } |
| |
| void BeginTest() override { |
| EXPECT_EQ(single_threaded_, !HasImplThread()); |
| EndTest(); |
| } |
| |
| void AfterTest() override { EXPECT_EQ(single_threaded_, !HasImplThread()); } |
| |
| private: |
| bool single_threaded_; |
| }; |
| |
| SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestHasImplThreadTest); |
| |
| class LayerTreeHostTestSetNeedsCommitInsideLayout : public LayerTreeHostTest { |
| protected: |
| void BeginTest() override { PostSetNeedsCommitToMainThread(); } |
| |
| void UpdateLayerTreeHost() override { |
| // This shouldn't cause a second commit to happen. |
| layer_tree_host()->SetNeedsCommit(); |
| } |
| |
| void DidCommit() override { |
| EXPECT_EQ(1, layer_tree_host()->SourceFrameNumber()); |
| EndTest(); |
| } |
| |
| void AfterTest() override {} |
| }; |
| |
| SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestSetNeedsCommitInsideLayout); |
| |
| class LayerTreeHostTestFrameOrdering : public LayerTreeHostTest { |
| protected: |
| enum MainOrder : int { |
| MAIN_START = 1, |
| MAIN_LAYOUT, |
| MAIN_COMMIT_COMPLETE, |
| MAIN_DID_BEGIN_FRAME, |
| MAIN_END, |
| }; |
| |
| enum ImplOrder : int { |
| IMPL_START = 1, |
| IMPL_READY_TO_COMMIT, |
| IMPL_COMMIT, |
| IMPL_COMMIT_COMPLETE, |
| IMPL_ACTIVATE, |
| IMPL_DRAW, |
| IMPL_END, |
| }; |
| |
| template <typename T> |
| bool CheckStep(T next, T* var) { |
| int expected = next - 1; |
| EXPECT_EQ(expected, *var); |
| bool correct = expected == *var; |
| *var = next; |
| return correct; |
| } |
| |
| void BeginTest() override { PostSetNeedsCommitToMainThread(); } |
| |
| void UpdateLayerTreeHost() override { |
| EXPECT_TRUE(CheckStep(MAIN_LAYOUT, &main_)); |
| } |
| |
| void DidCommit() override { |
| EXPECT_TRUE(CheckStep(MAIN_COMMIT_COMPLETE, &main_)); |
| } |
| |
| void DidBeginMainFrame() override { |
| EXPECT_TRUE(CheckStep(MAIN_DID_BEGIN_FRAME, &main_)); |
| } |
| |
| void ReadyToCommitOnThread(LayerTreeHostImpl* impl) override { |
| EXPECT_TRUE(CheckStep(IMPL_READY_TO_COMMIT, &impl_)); |
| } |
| |
| void BeginCommitOnThread(LayerTreeHostImpl* impl) override { |
| EXPECT_TRUE(CheckStep(IMPL_COMMIT, &impl_)); |
| } |
| |
| void CommitCompleteOnThread(LayerTreeHostImpl* impl) override { |
| EXPECT_TRUE(CheckStep(IMPL_COMMIT_COMPLETE, &impl_)); |
| } |
| |
| void WillActivateTreeOnThread(LayerTreeHostImpl* impl) override { |
| EXPECT_TRUE(CheckStep(IMPL_ACTIVATE, &impl_)); |
| } |
| |
| void DrawLayersOnThread(LayerTreeHostImpl* impl) override { |
| EXPECT_TRUE(CheckStep(IMPL_DRAW, &impl_)); |
| EndTest(); |
| } |
| |
| void AfterTest() override { |
| EXPECT_TRUE(CheckStep(MAIN_END, &main_)); |
| EXPECT_TRUE(CheckStep(IMPL_END, &impl_)); |
| } |
| |
| MainOrder main_ = MAIN_START; |
| ImplOrder impl_ = IMPL_START; |
| }; |
| |
| SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestFrameOrdering); |
| |
| class LayerTreeHostTestRequestedMainFrame : public LayerTreeHostTest { |
| public: |
| void BeginTest() override { PostSetNeedsCommitToMainThread(); } |
| |
| void WillBeginMainFrame() override { |
| // Post NextStep() so it happens after the MainFrame completes. |
| MainThreadTaskRunner()->PostTask( |
| FROM_HERE, |
| base::BindOnce(&LayerTreeHostTestRequestedMainFrame::NextStep, |
| base::Unretained(this))); |
| } |
| |
| void NextStep() { |
| // The MainFrame request is cleared once a MainFrame happens. |
| EXPECT_FALSE(layer_tree_host()->RequestedMainFramePendingForTesting()); |
| switch (layer_tree_host()->SourceFrameNumber()) { |
| case 0: |
| ADD_FAILURE() |
| << "Case 0 is the initial commit used to send the test here"; |
| FALLTHROUGH; |
| case 1: |
| layer_tree_host()->SetNeedsAnimate(); |
| break; |
| case 2: |
| layer_tree_host()->SetNeedsUpdateLayers(); |
| break; |
| case 3: |
| layer_tree_host()->SetNeedsCommit(); |
| break; |
| case 4: |
| EndTest(); |
| return; |
| } |
| // SetNeeds{Animate,UpdateLayers,Commit}() will mean a MainFrame is pending. |
| EXPECT_TRUE(layer_tree_host()->RequestedMainFramePendingForTesting()); |
| } |
| |
| void AfterTest() override {} |
| }; |
| |
| SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestRequestedMainFrame); |
| |
| class LayerTreeHostTestSetNeedsUpdateInsideLayout : public LayerTreeHostTest { |
| protected: |
| void BeginTest() override { PostSetNeedsCommitToMainThread(); } |
| |
| void UpdateLayerTreeHost() override { |
| // This shouldn't cause a second commit to happen. |
| layer_tree_host()->SetNeedsUpdateLayers(); |
| } |
| |
| void DidCommit() override { |
| EXPECT_EQ(1, layer_tree_host()->SourceFrameNumber()); |
| EndTest(); |
| } |
| |
| void AfterTest() override {} |
| }; |
| |
| SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestSetNeedsUpdateInsideLayout); |
| |
| // Test if the LTHI receives ReadyToActivate notifications from the TileManager |
| // when no raster tasks get scheduled. |
| class LayerTreeHostTestReadyToActivateEmpty : public LayerTreeHostTest { |
| public: |
| LayerTreeHostTestReadyToActivateEmpty() |
| : did_notify_ready_to_activate_(false), |
| all_tiles_required_for_activation_are_ready_to_draw_(false), |
| required_for_activation_count_(0) {} |
| |
| void BeginTest() override { PostSetNeedsCommitToMainThread(); } |
| |
| void CommitCompleteOnThread(LayerTreeHostImpl* impl) override { |
| const std::vector<PictureLayerImpl*>& layers = |
| impl->sync_tree()->picture_layers(); |
| required_for_activation_count_ = 0; |
| for (auto* layer : layers) { |
| FakePictureLayerImpl* fake_layer = |
| static_cast<FakePictureLayerImpl*>(layer); |
| required_for_activation_count_ += |
| fake_layer->CountTilesRequiredForActivation(); |
| } |
| } |
| |
| void NotifyReadyToActivateOnThread(LayerTreeHostImpl* impl) override { |
| did_notify_ready_to_activate_ = true; |
| all_tiles_required_for_activation_are_ready_to_draw_ = |
| impl->tile_manager()->IsReadyToActivate(); |
| EndTest(); |
| } |
| |
| void AfterTest() override { |
| EXPECT_TRUE(did_notify_ready_to_activate_); |
| EXPECT_TRUE(all_tiles_required_for_activation_are_ready_to_draw_); |
| EXPECT_EQ(size_t(0), required_for_activation_count_); |
| } |
| |
| protected: |
| bool did_notify_ready_to_activate_; |
| bool all_tiles_required_for_activation_are_ready_to_draw_; |
| size_t required_for_activation_count_; |
| }; |
| |
| SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestReadyToActivateEmpty); |
| |
| // Test if the LTHI receives ReadyToActivate notifications from the TileManager |
| // when some raster tasks flagged as REQUIRED_FOR_ACTIVATION got scheduled. |
| class LayerTreeHostTestReadyToActivateNonEmpty |
| : public LayerTreeHostTestReadyToActivateEmpty { |
| public: |
| void SetupTree() override { |
| client_.set_fill_with_nonsolid_color(true); |
| scoped_refptr<FakePictureLayer> root_layer = |
| FakePictureLayer::Create(&client_); |
| root_layer->SetBounds(gfx::Size(1024, 1024)); |
| root_layer->SetIsDrawable(true); |
| |
| layer_tree_host()->SetRootLayer(root_layer); |
| LayerTreeHostTest::SetupTree(); |
| client_.set_bounds(root_layer->bounds()); |
| } |
| |
| void AfterTest() override { |
| EXPECT_TRUE(did_notify_ready_to_activate_); |
| EXPECT_TRUE(all_tiles_required_for_activation_are_ready_to_draw_); |
| EXPECT_LE(size_t(1), required_for_activation_count_); |
| } |
| |
| private: |
| FakeContentLayerClient client_; |
| }; |
| |
| // No single thread test because the commit goes directly to the active tree in |
| // single thread mode, so notify ready to activate is skipped. |
| // Flaky: https://crbug.com/947673 |
| // MULTI_THREAD_TEST_F(LayerTreeHostTestReadyToActivateNonEmpty); |
| |
| // Test if the LTHI receives ReadyToDraw notifications from the TileManager when |
| // no raster tasks get scheduled. |
| class LayerTreeHostTestReadyToDrawEmpty : public LayerTreeHostTest { |
| public: |
| LayerTreeHostTestReadyToDrawEmpty() |
| : did_notify_ready_to_draw_(false), |
| all_tiles_required_for_draw_are_ready_to_draw_(false), |
| required_for_draw_count_(0) {} |
| |
| void BeginTest() override { PostSetNeedsCommitToMainThread(); } |
| |
| void NotifyReadyToDrawOnThread(LayerTreeHostImpl* impl) override { |
| did_notify_ready_to_draw_ = true; |
| const std::vector<PictureLayerImpl*>& layers = |
| impl->active_tree()->picture_layers(); |
| all_tiles_required_for_draw_are_ready_to_draw_ = |
| impl->tile_manager()->IsReadyToDraw(); |
| for (auto* layer : layers) { |
| FakePictureLayerImpl* fake_layer = |
| static_cast<FakePictureLayerImpl*>(layer); |
| required_for_draw_count_ += fake_layer->CountTilesRequiredForDraw(); |
| } |
| |
| EndTest(); |
| } |
| |
| void AfterTest() override { |
| EXPECT_TRUE(did_notify_ready_to_draw_); |
| EXPECT_TRUE(all_tiles_required_for_draw_are_ready_to_draw_); |
| EXPECT_EQ(size_t(0), required_for_draw_count_); |
| } |
| |
| protected: |
| bool did_notify_ready_to_draw_; |
| bool all_tiles_required_for_draw_are_ready_to_draw_; |
| size_t required_for_draw_count_; |
| }; |
| |
| SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestReadyToDrawEmpty); |
| |
| // Test if the LTHI receives ReadyToDraw notifications from the TileManager when |
| // some raster tasks flagged as REQUIRED_FOR_DRAW got scheduled. |
| class LayerTreeHostTestReadyToDrawNonEmpty |
| : public LayerTreeHostTestReadyToDrawEmpty { |
| public: |
| void SetupTree() override { |
| client_.set_fill_with_nonsolid_color(true); |
| scoped_refptr<FakePictureLayer> root_layer = |
| FakePictureLayer::Create(&client_); |
| root_layer->SetBounds(gfx::Size(1024, 1024)); |
| root_layer->SetIsDrawable(true); |
| |
| layer_tree_host()->SetRootLayer(root_layer); |
| LayerTreeHostTest::SetupTree(); |
| client_.set_bounds(root_layer->bounds()); |
| } |
| |
| void AfterTest() override { |
| EXPECT_TRUE(did_notify_ready_to_draw_); |
| EXPECT_TRUE(all_tiles_required_for_draw_are_ready_to_draw_); |
| EXPECT_LE(size_t(1), required_for_draw_count_); |
| } |
| |
| private: |
| FakeContentLayerClient client_; |
| }; |
| |
| // Note: With this test setup, we only get tiles flagged as REQUIRED_FOR_DRAW in |
| // single threaded mode. |
| SINGLE_THREAD_TEST_F(LayerTreeHostTestReadyToDrawNonEmpty); |
| |
| // This tests if we get the READY_TO_DRAW signal and draw if we become invisible |
| // and then become visible again. |
| class LayerTreeHostTestReadyToDrawVisibility : public LayerTreeHostTest { |
| public: |
| LayerTreeHostTestReadyToDrawVisibility() |
| : LayerTreeHostTest(), |
| toggled_visibility_(false), |
| did_notify_ready_to_draw_(false), |
| did_draw_(false) {} |
| |
| void BeginTest() override { PostSetNeedsCommitToMainThread(); } |
| |
| void SetupTree() override { |
| client_.set_fill_with_nonsolid_color(true); |
| scoped_refptr<FakePictureLayer> root_layer = |
| FakePictureLayer::Create(&client_); |
| root_layer->SetBounds(gfx::Size(1024, 1024)); |
| client_.set_bounds(root_layer->bounds()); |
| root_layer->SetIsDrawable(true); |
| |
| layer_tree_host()->SetRootLayer(root_layer); |
| LayerTreeHostTest::SetupTree(); |
| } |
| |
| void CommitCompleteOnThread(LayerTreeHostImpl* host_impl) override { |
| if (!toggled_visibility_) { |
| { |
| DebugScopedSetMainThread main(task_runner_provider()); |
| layer_tree_host()->SetVisible(false); |
| } |
| toggled_visibility_ = true; |
| EXPECT_FALSE(host_impl->visible()); |
| } |
| } |
| |
| void NotifyReadyToDrawOnThread(LayerTreeHostImpl* host_impl) override { |
| // Sometimes the worker thread posts NotifyReadyToDraw in the extremely |
| // short duration of time between PrepareTiles and SetVisible(false) so we |
| // might get two NotifyReadyToDraw signals for this test. |
| did_notify_ready_to_draw_ = true; |
| } |
| |
| void DrawLayersOnThread(LayerTreeHostImpl* host_impl) override { |
| EXPECT_FALSE(did_draw_); |
| did_draw_ = true; |
| EndTest(); |
| } |
| |
| void DidFinishImplFrameOnThread(LayerTreeHostImpl* host_impl) override { |
| if (!host_impl->visible()) { |
| { |
| DebugScopedSetMainThread main(task_runner_provider()); |
| layer_tree_host()->SetVisible(true); |
| } |
| EXPECT_TRUE(host_impl->visible()); |
| } |
| } |
| |
| void AfterTest() override { |
| EXPECT_TRUE(did_notify_ready_to_draw_); |
| EXPECT_TRUE(did_draw_); |
| } |
| |
| private: |
| FakeContentLayerClient client_; |
| bool toggled_visibility_; |
| bool did_notify_ready_to_draw_; |
| bool did_draw_; |
| }; |
| |
| // Note: With this test setup, we only get tiles flagged as REQUIRED_FOR_DRAW in |
| // single threaded mode. |
| SINGLE_THREAD_TEST_F(LayerTreeHostTestReadyToDrawVisibility); |
| |
| class LayerTreeHostContextCacheTest : public LayerTreeHostTest { |
| public: |
| std::unique_ptr<viz::TestLayerTreeFrameSink> CreateLayerTreeFrameSink( |
| const viz::RendererSettings& renderer_settings, |
| double refresh_rate, |
| scoped_refptr<viz::ContextProvider> compositor_context_provider, |
| scoped_refptr<viz::RasterContextProvider> worker_context_provider) |
| override { |
| // Create the main viz::ContextProvider with a MockContextSupport. |
| auto main_support = std::make_unique<MockContextSupport>(); |
| mock_main_context_support_ = main_support.get(); |
| auto test_main_context_provider = |
| viz::TestContextProvider::Create(std::move(main_support)); |
| |
| // Create the main viz::ContextProvider with a MockContextSupport. |
| auto worker_support = std::make_unique<MockContextSupport>(); |
| mock_worker_context_support_ = worker_support.get(); |
| auto test_worker_context_provider = |
| viz::TestContextProvider::CreateWorker(std::move(worker_support)); |
| |
| // At init, visibility is set to true, so SetAggressivelyFreeResources will |
| // be disabled. |
| EXPECT_CALL(*mock_main_context_support_, |
| SetAggressivelyFreeResources(false)); |
| EXPECT_CALL(*mock_worker_context_support_, |
| SetAggressivelyFreeResources(false)); |
| |
| return LayerTreeHostTest::CreateLayerTreeFrameSink( |
| renderer_settings, refresh_rate, std::move(test_main_context_provider), |
| std::move(test_worker_context_provider)); |
| } |
| |
| void InitializeSettings(LayerTreeSettings* settings) override { |
| settings->gpu_rasterization_forced = true; |
| } |
| |
| void BeginTest() override {} |
| void AfterTest() override {} |
| |
| protected: |
| class MockContextSupport : public viz::TestContextSupport { |
| public: |
| MockContextSupport() = default; |
| MOCK_METHOD1(SetAggressivelyFreeResources, |
| void(bool aggressively_free_resources)); |
| }; |
| |
| MockContextSupport* mock_main_context_support_; |
| MockContextSupport* mock_worker_context_support_; |
| }; |
| |
| // Test if the LTH successfully frees resources on the main/worker |
| // ContextSupport when visibility is set to false. |
| class LayerTreeHostFreesContextResourcesOnInvisible |
| : public LayerTreeHostContextCacheTest { |
| public: |
| void InitializedRendererOnThread(LayerTreeHostImpl* host_impl, |
| bool success) override { |
| // Ensure that our initialization expectations have completed. |
| Mock::VerifyAndClearExpectations(mock_main_context_support_); |
| Mock::VerifyAndClearExpectations(mock_worker_context_support_); |
| |
| // Update visibility and make sure resources are freed. |
| EXPECT_CALL(*mock_main_context_support_, |
| SetAggressivelyFreeResources(true)); |
| EXPECT_CALL(*mock_worker_context_support_, |
| SetAggressivelyFreeResources(true)) |
| .WillOnce(testing::Invoke([this](bool is_visible) { EndTest(); })); |
| PostSetVisibleToMainThread(false); |
| } |
| }; |
| |
| SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostFreesContextResourcesOnInvisible); |
| |
| // Test if the LTH successfully frees worker context resources when the hard |
| // memory limit is set to zero. |
| class LayerTreeHostFreesWorkerContextResourcesOnZeroMemoryLimit |
| : public LayerTreeHostContextCacheTest { |
| public: |
| void InitializedRendererOnThread(LayerTreeHostImpl* host_impl, |
| bool success) override { |
| // Ensure that our initialization expectations have completed. |
| Mock::VerifyAndClearExpectations(mock_worker_context_support_); |
| |
| // Worker context support should start freeing resources when hard memory |
| // limit is zeroed. |
| EXPECT_CALL(*mock_worker_context_support_, |
| SetAggressivelyFreeResources(true)) |
| .WillOnce(testing::Invoke([this](bool is_visible) { |
| // Main context is unchanged. It will start freeing on destruction. |
| EXPECT_CALL(*mock_main_context_support_, |
| SetAggressivelyFreeResources(true)); |
| EndTest(); |
| })); |
| ManagedMemoryPolicy zero_policy( |
| 0, gpu::MemoryAllocation::CUTOFF_ALLOW_NOTHING, 0); |
| host_impl->SetMemoryPolicy(zero_policy); |
| } |
| }; |
| |
| SINGLE_AND_MULTI_THREAD_TEST_F( |
| LayerTreeHostFreesWorkerContextResourcesOnZeroMemoryLimit); |
| |
| // Test if the LTH successfully frees worker context resources when hard memory |
| // limit is set to zero while using a synchronous compositor (Android WebView). |
| class LayerTreeHostFreesWorkerContextResourcesOnZeroMemoryLimitSynchronous |
| : public LayerTreeHostFreesWorkerContextResourcesOnZeroMemoryLimit { |
| public: |
| void InitializeSettings(LayerTreeSettings* settings) override { |
| LayerTreeHostContextCacheTest::InitializeSettings(settings); |
| settings->using_synchronous_renderer_compositor = true; |
| } |
| }; |
| |
| // Android Webview only runs in multi-threaded compositing mode. |
| MULTI_THREAD_TEST_F( |
| LayerTreeHostFreesWorkerContextResourcesOnZeroMemoryLimitSynchronous); |
| |
| // Test if the LTH successfully frees main and worker resources when the |
| // OutputSurface is destroyed. |
| class LayerTreeHostFreeContextResourcesOnDestroy |
| : public LayerTreeHostContextCacheTest { |
| public: |
| void WillBeginImplFrameOnThread(LayerTreeHostImpl* host_impl, |
| const viz::BeginFrameArgs& args) override { |
| // Ensure that our initialization expectations have completed. |
| Mock::VerifyAndClearExpectations(mock_main_context_support_); |
| Mock::VerifyAndClearExpectations(mock_worker_context_support_); |
| |
| // We leave the LTHI visible, so it start freeing resources on destruction. |
| EXPECT_CALL(*mock_worker_context_support_, |
| SetAggressivelyFreeResources(true)); |
| EXPECT_CALL(*mock_main_context_support_, |
| SetAggressivelyFreeResources(true)); |
| EndTest(); |
| } |
| }; |
| |
| SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostFreeContextResourcesOnDestroy); |
| |
| // Test if the LTH successfully frees and stops freeing context resources |
| // when the LayerTreeFrameSink is lost and recreated. |
| class LayerTreeHostCacheBehaviorOnLayerTreeFrameSinkRecreated |
| : public LayerTreeHostContextCacheTest { |
| public: |
| void WillBeginImplFrameOnThread(LayerTreeHostImpl* host_impl, |
| const viz::BeginFrameArgs& args) override { |
| // This code is run once, to trigger recreation of our LayerTreeFrameSink. |
| if (test_state_ != TestState::INIT) |
| return; |
| |
| // Ensure that our initialization expectations have completed. |
| Mock::VerifyAndClearExpectations(mock_main_context_support_); |
| Mock::VerifyAndClearExpectations(mock_worker_context_support_); |
| |
| // LayerTreeFrameSink lost expectations. |
| EXPECT_CALL(*mock_worker_context_support_, |
| SetAggressivelyFreeResources(true)); |
| EXPECT_CALL(*mock_main_context_support_, |
| SetAggressivelyFreeResources(true)); |
| host_impl->DidLoseLayerTreeFrameSink(); |
| test_state_ = TestState::RECREATED; |
| } |
| |
| void InitializedRendererOnThread(LayerTreeHostImpl* host_impl, |
| bool success) override { |
| // This is run after we have recreated our LayerTreeFrameSink. |
| if (test_state_ != TestState::RECREATED) |
| return; |
| |
| // Ensure that our expectations have completed. |
| Mock::VerifyAndClearExpectations(mock_main_context_support_); |
| Mock::VerifyAndClearExpectations(mock_worker_context_support_); |
| |
| // Destruction exptectations. |
| EXPECT_CALL(*mock_worker_context_support_, |
| SetAggressivelyFreeResources(true)); |
| EXPECT_CALL(*mock_main_context_support_, |
| SetAggressivelyFreeResources(true)); |
| EndTest(); |
| test_state_ = TestState::DONE; |
| } |
| |
| private: |
| enum class TestState { INIT, RECREATED, DONE }; |
| TestState test_state_ = TestState::INIT; |
| }; |
| |
| SINGLE_AND_MULTI_THREAD_TEST_F( |
| LayerTreeHostCacheBehaviorOnLayerTreeFrameSinkRecreated); |
| |
| // Two setNeedsCommits in a row should lead to at least 1 commit and at least 1 |
| // draw with frame 0. |
| class LayerTreeHostTestSetNeedsCommit1 : public LayerTreeHostTest { |
| public: |
| LayerTreeHostTestSetNeedsCommit1() : num_commits_(0), num_draws_(0) {} |
| |
| void BeginTest() override { |
| PostSetNeedsCommitToMainThread(); |
| PostSetNeedsCommitToMainThread(); |
| } |
| |
| void DrawLayersOnThread(LayerTreeHostImpl* impl) override { |
| num_draws_++; |
| if (!impl->active_tree()->source_frame_number()) |
| EndTest(); |
| } |
| |
| void CommitCompleteOnThread(LayerTreeHostImpl* impl) override { |
| num_commits_++; |
| } |
| |
| void AfterTest() override { |
| EXPECT_LE(1, num_commits_); |
| EXPECT_LE(1, num_draws_); |
| } |
| |
| private: |
| int num_commits_; |
| int num_draws_; |
| }; |
| |
| SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestSetNeedsCommit1); |
| |
| // A SetNeedsCommit should lead to 1 commit. Issuing a second commit after that |
| // first committed frame draws should lead to another commit. |
| class LayerTreeHostTestSetNeedsCommit2 : public LayerTreeHostTest { |
| public: |
| LayerTreeHostTestSetNeedsCommit2() : num_commits_(0), num_draws_(0) {} |
| |
| void BeginTest() override { PostSetNeedsCommitToMainThread(); } |
| |
| void DrawLayersOnThread(LayerTreeHostImpl* impl) override { ++num_draws_; } |
| |
| void CommitCompleteOnThread(LayerTreeHostImpl* impl) override { |
| ++num_commits_; |
| switch (num_commits_) { |
| case 1: |
| PostSetNeedsCommitToMainThread(); |
| break; |
| case 2: |
| EndTest(); |
| break; |
| default: |
| NOTREACHED(); |
| } |
| } |
| |
| void AfterTest() override { |
| EXPECT_EQ(2, num_commits_); |
| EXPECT_LE(1, num_draws_); |
| } |
| |
| private: |
| int num_commits_; |
| int num_draws_; |
| }; |
| |
| MULTI_THREAD_TEST_F(LayerTreeHostTestSetNeedsCommit2); |
| |
| // Verify that we pass property values in PushPropertiesTo. |
| class LayerTreeHostTestPushPropertiesTo : public LayerTreeHostTest { |
| protected: |
| void SetupTree() override { |
| scoped_refptr<Layer> root = Layer::Create(); |
| root->SetBounds(gfx::Size(10, 10)); |
| layer_tree_host()->SetRootLayer(root); |
| LayerTreeHostTest::SetupTree(); |
| } |
| |
| enum Properties { |
| STARTUP, |
| BOUNDS, |
| HIDE_LAYER_AND_SUBTREE, |
| DRAWS_CONTENT, |
| DONE, |
| }; |
| |
| void BeginTest() override { |
| index_ = STARTUP; |
| PostSetNeedsCommitToMainThread(); |
| } |
| |
| void DrawLayersOnThread(LayerTreeHostImpl* impl) override { |
| VerifyAfterValues(impl->active_tree()->root_layer_for_testing()); |
| } |
| |
| void DidCommitAndDrawFrame() override { |
| SetBeforeValues(layer_tree_host()->root_layer()); |
| VerifyBeforeValues(layer_tree_host()->root_layer()); |
| |
| ++index_; |
| if (index_ == DONE) { |
| EndTest(); |
| return; |
| } |
| |
| SetAfterValues(layer_tree_host()->root_layer()); |
| } |
| |
| void AfterTest() override {} |
| |
| void VerifyBeforeValues(Layer* layer) { |
| EXPECT_EQ(gfx::Size(10, 10).ToString(), layer->bounds().ToString()); |
| EXPECT_FALSE(layer->hide_layer_and_subtree()); |
| EXPECT_FALSE(layer->DrawsContent()); |
| } |
| |
| void SetBeforeValues(Layer* layer) { |
| layer->SetBounds(gfx::Size(10, 10)); |
| layer->SetHideLayerAndSubtree(false); |
| layer->SetIsDrawable(false); |
| } |
| |
| void VerifyAfterValues(LayerImpl* layer) { |
| EffectTree& tree = layer->layer_tree_impl()->property_trees()->effect_tree; |
| EffectNode* node = tree.Node(layer->effect_tree_index()); |
| 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_EQ(tree.EffectiveOpacity(node), 0.f); |
| 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); |
| |
| class LayerTreeHostTestPushNodeOwnerToNodeIdMap : public LayerTreeHostTest { |
| protected: |
| void SetupTree() override { |
| root_ = Layer::Create(); |
| child_ = Layer::Create(); |
| root_->AddChild(child_); |
| layer_tree_host()->SetRootLayer(root_); |
| LayerTreeHostTest::SetupTree(); |
| } |
| |
| void BeginTest() override { PostSetNeedsCommitToMainThread(); } |
| |
| void DidCommit() override { |
| switch (layer_tree_host()->SourceFrameNumber()) { |
| case 1: |
| // child_ should create transform, effect node. |
| child_->SetForceRenderSurfaceForTesting(true); |
| break; |
| case 2: |
| // child_ should create a scroll node. |
| child_->SetScrollable(gfx::Size(1, 1)); |
| break; |
| case 3: |
| // child_ should create a clip node. |
| child_->SetMasksToBounds(true); |
| break; |
| case 4: |
| // child_ should only create the scroll-related nodes. |
| child_->SetMasksToBounds(false); |
| child_->SetForceRenderSurfaceForTesting(false); |
| // Should have no effect because empty bounds do not prevent scrolling. |
| child_->SetScrollable(gfx::Size(0, 0)); |
| } |
| } |
| |
| void CommitCompleteOnThread(LayerTreeHostImpl* impl) override { |
| PropertyTrees* property_trees = impl->sync_tree()->property_trees(); |
| const TransformNode* root_transform_node = |
| property_trees->transform_tree.Node(root_->transform_tree_index()); |
| const TransformNode* child_transform_node = |
| property_trees->transform_tree.Node(child_->transform_tree_index()); |
| const EffectNode* root_effect_node = |
| property_trees->effect_tree.Node(root_->effect_tree_index()); |
| const EffectNode* child_effect_node = |
| property_trees->effect_tree.Node(child_->effect_tree_index()); |
| const ClipNode* root_clip_node = |
| property_trees->clip_tree.Node(root_->clip_tree_index()); |
| const ClipNode* child_clip_node = |
| property_trees->clip_tree.Node(child_->clip_tree_index()); |
| const ScrollNode* root_scroll_node = |
| property_trees->scroll_tree.Node(root_->scroll_tree_index()); |
| const ScrollNode* child_scroll_node = |
| property_trees->scroll_tree.Node(child_->scroll_tree_index()); |
| switch (impl->sync_tree()->source_frame_number()) { |
| case 0: |
| // root_ should create transform, scroll and effect tree nodes but not |
| // a clip node. |
| EXPECT_NE(nullptr, root_transform_node); |
| EXPECT_NE(nullptr, root_effect_node); |
| EXPECT_NE(nullptr, root_scroll_node); |
| EXPECT_NE(nullptr, root_clip_node); |
| EXPECT_EQ(root_transform_node, child_transform_node); |
| EXPECT_EQ(child_effect_node, root_effect_node); |
| EXPECT_EQ(root_clip_node, child_clip_node); |
| EXPECT_EQ(root_scroll_node, child_scroll_node); |
| |
| break; |
| case 1: |
| // child_ should create a transfrom, effect nodes but not a scroll, clip |
| // node. |
| EXPECT_NE(root_transform_node, child_transform_node); |
| EXPECT_NE(child_effect_node, root_effect_node); |
| EXPECT_EQ(root_clip_node, child_clip_node); |
| EXPECT_EQ(root_scroll_node, child_scroll_node); |
| |
| break; |
| case 2: |
| // child_ should create a scroll node. |
| EXPECT_NE(root_scroll_node, child_scroll_node); |
| break; |
| case 3: |
| // child_ should create a clip node. |
| EXPECT_NE(root_clip_node, child_clip_node); |
| break; |
| case 4: |
| // child_ should only create the scroll-related nodes. |
| EXPECT_EQ(child_transform_node->id, child_scroll_node->transform_id); |
| EXPECT_EQ(child_effect_node, root_effect_node); |
| EXPECT_EQ(root_clip_node, child_clip_node); |
| EXPECT_NE(root_scroll_node, child_scroll_node); |
| EndTest(); |
| break; |
| } |
| } |
| |
| void AfterTest() override {} |
| |
| private: |
| scoped_refptr<Layer> root_; |
| scoped_refptr<Layer> child_; |
| }; |
| |
| SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestPushNodeOwnerToNodeIdMap); |
| |
| class LayerTreeHostTestPushElementIdToNodeIdMap : public LayerTreeHostTest { |
| protected: |
| void SetupTree() override { |
| root_ = Layer::Create(); |
| child_ = Layer::Create(); |
| root_->AddChild(child_); |
| layer_tree_host()->SetRootLayer(root_); |
| LayerTreeHostTest::SetupTree(); |
| } |
| |
| void BeginTest() override { PostSetNeedsCommitToMainThread(); } |
| |
| void DidCommit() override { |
| switch (layer_tree_host()->SourceFrameNumber()) { |
| case 1: |
| child_->SetForceRenderSurfaceForTesting(true); |
| child_->AddMainThreadScrollingReasons( |
| MainThreadScrollingReason::kHasBackgroundAttachmentFixedObjects); |
| break; |
| case 2: |
| child_->SetForceRenderSurfaceForTesting(false); |
| child_->ClearMainThreadScrollingReasons( |
| MainThreadScrollingReason::kHasBackgroundAttachmentFixedObjects); |
| break; |
| } |
| } |
| |
| void CommitCompleteOnThread(LayerTreeHostImpl* impl) override { |
| PropertyTrees* property_trees = impl->sync_tree()->property_trees(); |
| LayerImpl* child_impl_ = impl->sync_tree()->LayerById(child_->id()); |
| switch (impl->sync_tree()->source_frame_number()) { |
| case 0: |
| EXPECT_EQ(2U, child_impl_->layer_tree_impl() |
| ->property_trees() |
| ->transform_tree.size()); |
| EXPECT_EQ(2U, child_impl_->layer_tree_impl() |
| ->property_trees() |
| ->effect_tree.size()); |
| EXPECT_EQ(2U, child_impl_->layer_tree_impl() |
| ->property_trees() |
| ->scroll_tree.size()); |
| EXPECT_TRUE(property_trees->element_id_to_transform_node_index.find( |
| child_->element_id()) == |
| property_trees->element_id_to_transform_node_index.end()); |
| EXPECT_TRUE(property_trees->element_id_to_effect_node_index.find( |
| child_->element_id()) == |
| property_trees->element_id_to_effect_node_index.end()); |
| EXPECT_TRUE(property_trees->element_id_to_scroll_node_index.find( |
| child_->element_id()) == |
| property_trees->element_id_to_scroll_node_index.end()); |
| break; |
| case 1: |
| EXPECT_EQ(3U, child_impl_->layer_tree_impl() |
| ->property_trees() |
| ->transform_tree.size()); |
| EXPECT_EQ(3U, child_impl_->layer_tree_impl() |
| ->property_trees() |
| ->effect_tree.size()); |
| EXPECT_EQ(3U, child_impl_->layer_tree_impl() |
| ->property_trees() |
| ->scroll_tree.size()); |
| EXPECT_EQ( |
| 2, property_trees |
| ->element_id_to_transform_node_index[child_->element_id()]); |
| EXPECT_EQ(2, |
| property_trees |
| ->element_id_to_effect_node_index[child_->element_id()]); |
| EXPECT_EQ(2, |
| property_trees |
| ->element_id_to_scroll_node_index[child_->element_id()]); |
| break; |
| case 2: |
| EXPECT_EQ(2U, child_impl_->layer_tree_impl() |
| ->property_trees() |
| ->transform_tree.size()); |
| EXPECT_EQ(2U, child_impl_->layer_tree_impl() |
| ->property_trees() |
| ->effect_tree.size()); |
| EXPECT_TRUE(property_trees->element_id_to_transform_node_index.find( |
| child_->element_id()) == |
| property_trees->element_id_to_transform_node_index.end()); |
| EXPECT_TRUE(property_trees->element_id_to_effect_node_index.find( |
| child_->element_id()) == |
| property_trees->element_id_to_effect_node_index.end()); |
| EXPECT_TRUE(property_trees->element_id_to_scroll_node_index.find( |
| child_->element_id()) == |
| property_trees->element_id_to_scroll_node_index.end()); |
| break; |
| } |
| EndTest(); |
| } |
| |
| void AfterTest() override {} |
| |
| private: |
| scoped_refptr<Layer> root_; |
| scoped_refptr<Layer> child_; |
| }; |
| |
| // Validates that, for a layer with a compositor element id set on it, mappings |
| // from compositor element id to transform/effect node indexes are created as |
| // part of building a layer's property tree and are present on the impl thread. |
| SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestPushElementIdToNodeIdMap); |
| |
| class LayerTreeHostTestSurfaceDamage : public LayerTreeHostTest { |
| protected: |
| void SetupTree() override { |
| root_ = Layer::Create(); |
| child_ = Layer::Create(); |
| grand_child_ = Layer::Create(); |
| |
| layer_tree_host()->SetRootLayer(root_); |
| root_->AddChild(child_); |
| child_->AddChild(grand_child_); |
| |
| root_->SetBounds(gfx::Size(50, 50)); |
| root_->SetMasksToBounds(true); |
| child_->SetForceRenderSurfaceForTesting(true); |
| |
| LayerTreeHostTest::SetupTree(); |
| } |
| |
| void BeginTest() override { PostSetNeedsCommitToMainThread(); } |
| |
| void DidCommit() override { |
| switch (layer_tree_host()->SourceFrameNumber()) { |
| case 1: |
| grand_child_->SetOpacity(0.9f); |
| break; |
| case 2: |
| root_->SetBounds(gfx::Size(20, 20)); |
| break; |
| case 3: |
| child_->SetDoubleSided(false); |
| break; |
| } |
| } |
| |
| DrawResult PrepareToDrawOnThread(LayerTreeHostImpl* impl, |
| LayerTreeHostImpl::FrameData* frame_data, |
| DrawResult draw_result) override { |
| LayerImpl* root_impl = impl->active_tree()->LayerById(root_->id()); |
| LayerImpl* child_impl = impl->active_tree()->LayerById(child_->id()); |
| switch (impl->active_tree()->source_frame_number()) { |
| case 0: |
| EXPECT_TRUE(GetRenderSurface(root_impl)->AncestorPropertyChanged()); |
| EXPECT_TRUE(GetRenderSurface(child_impl)->AncestorPropertyChanged()); |
| PostSetNeedsCommitToMainThread(); |
| break; |
| case 1: |
| EXPECT_FALSE(GetRenderSurface(root_impl)->AncestorPropertyChanged()); |
| EXPECT_FALSE(GetRenderSurface(child_impl)->AncestorPropertyChanged()); |
| PostSetNeedsCommitToMainThread(); |
| break; |
| case 2: |
| EXPECT_TRUE(GetRenderSurface(root_impl)->AncestorPropertyChanged()); |
| EXPECT_TRUE(GetRenderSurface(child_impl)->AncestorPropertyChanged()); |
| PostSetNeedsCommitToMainThread(); |
| break; |
| case 3: |
| EXPECT_FALSE(GetRenderSurface(root_impl)->AncestorPropertyChanged()); |
| EXPECT_TRUE(GetRenderSurface(child_impl)->AncestorPropertyChanged()); |
| EndTest(); |
| PostSetNeedsCommitToMainThread(); |
| break; |
| case 4: |
| EXPECT_FALSE(GetRenderSurface(root_impl)->AncestorPropertyChanged()); |
| EXPECT_FALSE(GetRenderSurface(child_impl)->AncestorPropertyChanged()); |
| EndTest(); |
| break; |
| } |
| |
| return draw_result; |
| } |
| |
| void AfterTest() override {} |
| |
| private: |
| scoped_refptr<Layer> root_; |
| scoped_refptr<Layer> child_; |
| scoped_refptr<Layer> grand_child_; |
| }; |
| |
| SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestSurfaceDamage); |
| |
| class LayerTreeHostTestLayerListSurfaceDamage : public LayerTreeHostTest { |
| protected: |
| void InitializeSettings(LayerTreeSettings* settings) override { |
| settings->use_layer_lists = true; |
| } |
| |
| void SetupTree() override { |
| root_ = Layer::Create(); |
| child_a_ = Layer::Create(); |
| child_b_ = Layer::Create(); |
| child_c_ = Layer::Create(); |
| |
| layer_tree_host()->SetRootLayer(root_); |
| |
| root_->AddChild(child_a_); |
| root_->AddChild(child_b_); |
| root_->AddChild(child_c_); |
| |
| root_->SetBounds(gfx::Size(50, 50)); |
| |
| child_a_->SetBounds(gfx::Size(10, 20)); |
| child_a_->SetForceRenderSurfaceForTesting(true); |
| child_a_->SetIsDrawable(true); |
| |
| child_b_->SetBounds(gfx::Size(20, 10)); |
| child_b_->SetForceRenderSurfaceForTesting(true); |
| child_b_->SetIsDrawable(true); |
| |
| child_c_->SetBounds(gfx::Size(15, 15)); |
| child_c_->SetForceRenderSurfaceForTesting(true); |
| child_c_->SetIsDrawable(true); |
| |
| // TODO(pdr): Do not use the property tree builder for testing in layer list |
| // mode. This will require rewriting this test to manually build property |
| // trees. |
| layer_tree_host()->BuildPropertyTreesForTesting(); |
| |
| LayerTreeHostTest::SetupTree(); |
| } |
| |
| void BeginTest() override { PostSetNeedsCommitToMainThread(); } |
| |
| void DidCommit() override { |
| switch (layer_tree_host()->SourceFrameNumber()) { |
| case 1: |
| // Push an unchanged list. This should cause no damage. |
| { |
| LayerList same_list = root_->children(); |
| root_->SetChildLayerList(same_list); |
| } |
| break; |
| case 2: |
| // Reverse the last two layers so the order becomes: [a, c, b]. This |
| // should only damage the 'b' layer. |
| { |
| LayerList last_two_reversed; |
| last_two_reversed.push_back(child_a_); |
| last_two_reversed.push_back(child_c_); |
| last_two_reversed.push_back(child_b_); |
| root_->SetChildLayerList(last_two_reversed); |
| } |
| break; |
| case 3: |
| // Reverse the first two layers so the order becomes: [c, a, b]. This |
| // should damage the last two layers, 'a' and 'b'. |
| { |
| LayerList last_pair_reversed; |
| last_pair_reversed.push_back(child_c_); |
| last_pair_reversed.push_back(child_a_); |
| last_pair_reversed.push_back(child_b_); |
| root_->SetChildLayerList(last_pair_reversed); |
| } |
| break; |
| case 4: |
| // Remove the first layer, 'c', so the order becomes: ['a', 'b']. This |
| // should not damage 'a' or 'b'. |
| { |
| LayerList first_removed = root_->children(); |
| first_removed.erase(first_removed.begin()); |
| root_->SetChildLayerList(first_removed); |
| } |
| break; |
| case 5: |
| // Add a new layer, 'c', so the order becomes: ['a', 'b', 'c']. This |
| // should only damage 'c'. |
| { |
| LayerList existing_plus_new_child = root_->children(); |
| existing_plus_new_child.push_back(child_c_); |
| root_->SetChildLayerList(existing_plus_new_child); |
| } |
| break; |
| } |
| } |
| |
| DrawResult PrepareToDrawOnThread(LayerTreeHostImpl* impl, |
| LayerTreeHostImpl::FrameData* frame_data, |
| DrawResult draw_result) override { |
| LayerImpl* child_a_impl = impl->active_tree()->LayerById(child_a_->id()); |
| LayerImpl* child_b_impl = impl->active_tree()->LayerById(child_b_->id()); |
| LayerImpl* child_c_impl = impl->active_tree()->LayerById(child_c_->id()); |
| switch (impl->active_tree()->source_frame_number()) { |
| case 0: |
| // Full damage on first frame. |
| EXPECT_EQ(GetRenderSurface(child_a_impl)->GetDamageRect(), |
| gfx::Rect(0, 0, 10, 20)); |
| EXPECT_EQ(GetRenderSurface(child_b_impl)->GetDamageRect(), |
| gfx::Rect(0, 0, 20, 10)); |
| EXPECT_EQ(GetRenderSurface(child_c_impl)->GetDamageRect(), |
| gfx::Rect(0, 0, 15, 15)); |
| PostSetNeedsCommitToMainThread(); |
| break; |
| case 1: |
| // No damage after pushing the same list. |
| EXPECT_TRUE(GetRenderSurface(child_a_impl)->GetDamageRect().IsEmpty()); |
| EXPECT_TRUE(GetRenderSurface(child_b_impl)->GetDamageRect().IsEmpty()); |
| EXPECT_TRUE(GetRenderSurface(child_c_impl)->GetDamageRect().IsEmpty()); |
| PostSetNeedsCommitToMainThread(); |
| break; |
| case 2: |
| // Only 'b' damaged after reversing the last two layers. |
| EXPECT_TRUE(GetRenderSurface(child_a_impl)->GetDamageRect().IsEmpty()); |
| EXPECT_EQ(GetRenderSurface(child_b_impl)->GetDamageRect(), |
| gfx::Rect(0, 0, 20, 10)); |
| EXPECT_TRUE(GetRenderSurface(child_c_impl)->GetDamageRect().IsEmpty()); |
| PostSetNeedsCommitToMainThread(); |
| break; |
| case 3: |
| // 'a' and 'b' damaged after reversing the first two layers. |
| EXPECT_EQ(GetRenderSurface(child_a_impl)->GetDamageRect(), |
| gfx::Rect(0, 0, 10, 20)); |
| EXPECT_EQ(GetRenderSurface(child_b_impl)->GetDamageRect(), |
| gfx::Rect(0, 0, 20, 10)); |
| EXPECT_TRUE(GetRenderSurface(child_c_impl)->GetDamageRect().IsEmpty()); |
| PostSetNeedsCommitToMainThread(); |
| break; |
| case 4: |
| // When the first layer, 'c', is removed, 'a' and 'b' should not be |
| // damaged. |
| EXPECT_TRUE(GetRenderSurface(child_a_impl)->GetDamageRect().IsEmpty()); |
| EXPECT_TRUE(GetRenderSurface(child_b_impl)->GetDamageRect().IsEmpty()); |
| PostSetNeedsCommitToMainThread(); |
| break; |
| case 5: |
| // When 'c' is added, 'a' and 'b' should not be damaged. |
| EXPECT_TRUE(GetRenderSurface(child_a_impl)->GetDamageRect().IsEmpty()); |
| EXPECT_TRUE(GetRenderSurface(child_b_impl)->GetDamageRect().IsEmpty()); |
| EXPECT_EQ(GetRenderSurface(child_c_impl)->GetDamageRect(), |
| gfx::Rect(0, 0, 15, 15)); |
| EndTest(); |
| break; |
| } |
| |
| return draw_result; |
| } |
| |
| void AfterTest() override {} |
| |
| private: |
| scoped_refptr<Layer> root_; |
| scoped_refptr<Layer> child_a_; |
| scoped_refptr<Layer> child_b_; |
| scoped_refptr<Layer> child_c_; |
| }; |
| |
| SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestLayerListSurfaceDamage); |
| |
| // When settings->enable_early_damage_check is true, verify that invalidate is |
| // not called when changes to a layer don't cause visible damage. |
| class LayerTreeHostTestNoDamageCausesNoInvalidate : public LayerTreeHostTest { |
| void InitializeSettings(LayerTreeSettings* settings) override { |
| settings->using_synchronous_renderer_compositor = true; |
| settings->enable_early_damage_check = true; |
| } |
| |
| protected: |
| void SetupTree() override { |
| root_ = Layer::Create(); |
| |
| layer_tree_host()->SetViewportSizeAndScale(gfx::Size(10, 10), 1.f, |
| viz::LocalSurfaceIdAllocation()); |
| |
| layer_tree_host()->SetRootLayer(root_); |
| |
| // Translate the root layer past the viewport. |
| gfx::Transform translation; |
| translation.Translate(100, 100); |
| root_->SetTransform(translation); |
| |
| root_->SetBounds(gfx::Size(50, 50)); |
| |
| LayerTreeHostTest::SetupTree(); |
| } |
| |
| void BeginTest() override { PostSetNeedsCommitToMainThread(); } |
| |
| void DidCommit() override { |
| // This does not damage the frame because the root layer is outside the |
| // viewport. |
| if (layer_tree_host()->SourceFrameNumber() == 2) |
| root_->SetOpacity(0.9f); |
| } |
| |
| DrawResult PrepareToDrawOnThread(LayerTreeHostImpl* impl, |
| LayerTreeHostImpl::FrameData* frame_data, |
| DrawResult draw_result) override { |
| PostSetNeedsCommitToMainThread(); |
| return draw_result; |
| } |
| |
| void CommitCompleteOnThread(LayerTreeHostImpl* impl) override { |
| switch (impl->active_tree()->source_frame_number()) { |
| // This gives us some assurance that invalidates happen before this call, |
| // so invalidating on frame 2 will cause a failure before the test ends. |
| case 0: |
| EXPECT_TRUE(first_frame_invalidate_before_commit_); |
| break; |
| case 2: |
| EndTest(); |
| } |
| } |
| |
| void DidInvalidateLayerTreeFrameSink(LayerTreeHostImpl* impl) override { |
| switch (impl->active_tree()->source_frame_number()) { |
| // Be sure that invalidates happen before commits, so the below failure |
| // works. |
| case 0: |
| first_frame_invalidate_before_commit_ = true; |
| break; |
| // Frame 1 will invalidate, even though it has no damage. The early |
| // damage check that prevents frame 2 from invalidating only runs if |
| // a previous frame did not have damage. |
| |
| // This frame should not cause an invalidate, since there is no visible |
| // damage. |
| case 2: |
| ADD_FAILURE(); |
| } |
| } |
| |
| void AfterTest() override {} |
| |
| private: |
| scoped_refptr<Layer> root_; |
| bool first_frame_invalidate_before_commit_ = false; |
| }; |
| |
| // This behavior is specific to Android WebView, which only uses |
| // multi-threaded compositor. |
| MULTI_THREAD_TEST_F(LayerTreeHostTestNoDamageCausesNoInvalidate); |
| |
| // When settings->enable_early_damage_check is true, verify that the early |
| // damage check is turned off after |settings->damaged_frame_limit| frames |
| // have consecutive damage. |
| // The test verifies that frames come in as follows: |
| // 0: visible damage as the root appears; invalidated |
| // 1: no visible damage; invalidate because all previous frames had damage |
| // 2: no visible damage; check early since frame 1 had no damage; no invalidate |
| // 3: visible damage |
| // ... (visible damage) |
| // 3 + damaged_frame_limit - 1: visible damage |
| // 3 + damaged_frame_limit: no visible damage, but invalidate because all of |
| // the last |damaged_frame_limit| frames had damage. |
| class LayerTreeHostTestEarlyDamageCheckStops : public LayerTreeHostTest { |
| void InitializeSettings(LayerTreeSettings* settings) override { |
| settings->using_synchronous_renderer_compositor = true; |
| settings->enable_early_damage_check = true; |
| damaged_frame_limit_ = settings->damaged_frame_limit; |
| } |
| |
| protected: |
| void SetupTree() override { |
| root_ = Layer::Create(); |
| child_ = Layer::Create(); |
| root_->AddChild(child_); |
| |
| layer_tree_host()->SetViewportSizeAndScale(gfx::Size(100, 100), 1.f, |
| viz::LocalSurfaceIdAllocation()); |
| |
| layer_tree_host()->SetRootLayer(root_); |
| root_->SetBounds(gfx::Size(50, 50)); |
| child_->SetBounds(gfx::Size(50, 50)); |
| |
| // Translate the child layer past the viewport. |
| gfx::Transform translation; |
| translation.Translate(200, 200); |
| child_->SetTransform(translation); |
| |
| LayerTreeHostTest::SetupTree(); |
| } |
| |
| void BeginTest() override { PostSetNeedsCommitToMainThread(); } |
| |
| void WillBeginMainFrame() override { |
| int frame = layer_tree_host()->SourceFrameNumber(); |
| // Change the child layer each frame. Since the child layer is translated |
| // past the viewport, it should not cause damage, but webview will still |
| // invalidate if the frame doesn't check for damage early. |
| child_->SetOpacity(1.0 / (float)(frame + 1)); |
| |
| // For |damaged_frame_limit| consecutive frames, cause actual damage. |
| if (frame >= 3 && frame < (damaged_frame_limit_ + 3)) { |
| root_->SetOpacity(1.0 / (float)frame); |
| } |
| } |
| |
| void DidActivateTreeOnThread(LayerTreeHostImpl* impl) override { |
| PostSetNeedsCommitToMainThread(); |
| } |
| |
| void DidInvalidateLayerTreeFrameSink(LayerTreeHostImpl* impl) override { |
| int frame_number = impl->active_tree()->source_frame_number(); |
| // Frames 0 and 1 invalidate because the early damage check is not enabled |
| // during this setup. But frames 1 and 2 are not damaged, so the early |
| // check should prevent frame 2 from invalidating. |
| if (frame_number == 2) { |
| ADD_FAILURE(); |
| } else if (frame_number > 2) { |
| invalidate_count_++; |
| } |
| |
| // Frame number |damaged_frame_limit_ + 3| was not damaged, but it should |
| // invalidate since the previous |damaged_frame_limit_| frames had damage |
| // and should have turned off the early damage check. |
| if (frame_number == damaged_frame_limit_ + 3) { |
| EndTest(); |
| return; |
| } |
| } |
| |
| void AfterTest() override { |
| // We should invalidate |damaged_frame_limit_| frames that had actual damage |
| // and one additional frame after, since the early check is disabled. |
| EXPECT_EQ(invalidate_count_, damaged_frame_limit_ + 1); |
| } |
| |
| private: |
| scoped_refptr<Layer> root_; |
| scoped_refptr<Layer> child_; |
| int invalidate_count_ = 0; |
| int damaged_frame_limit_; |
| }; |
| |
| // This behavior is specific to Android WebView, which only uses |
| // multi-threaded compositor. |
| MULTI_THREAD_TEST_F(LayerTreeHostTestEarlyDamageCheckStops); |
| |
| // When settings->enable_early_damage_check is true, verifies that PrepareTiles |
| // need not cause a draw when there is no visible damage. Here, a child layer is |
| // translated outside of the viewport. After two draws, the early damage check |
| // should prevent further draws, but preventing further draws should not prevent |
| // PrepareTiles. |
| class LayerTreeHostTestPrepareTilesWithoutDraw : public LayerTreeHostTest { |
| void InitializeSettings(LayerTreeSettings* settings) override { |
| settings->using_synchronous_renderer_compositor = true; |
| settings->enable_early_damage_check = true; |
| } |
| |
| protected: |
| void SetupTree() override { |
| LayerTreeHostTest::SetupTree(); |
| child_layer_ = Layer::Create(); |
| layer_tree_host()->root_layer()->AddChild(child_layer_); |
| |
| layer_tree_host()->SetViewportSizeAndScale(gfx::Size(10, 10), 1.f, |
| viz::LocalSurfaceIdAllocation()); |
| |
| layer_tree_host()->root_layer()->SetBounds(gfx::Size(50, 50)); |
| child_layer_->SetBounds(gfx::Size(50, 50)); |
| |
| // Translate the child layer past the viewport. |
| gfx::Transform translation; |
| translation.Translate(100, 100); |
| child_layer_->SetTransform(translation); |
| child_layer_->SetBounds(gfx::Size(50, 50)); |
| } |
| |
| void BeginTest() override { PostSetNeedsCommitToMainThread(); } |
| |
| void DidCommit() override { |
| int frame_number = layer_tree_host()->SourceFrameNumber(); |
| if (frame_number > 3) { |
| EndTest(); |
| return; |
| } |
| |
| // Modify the child layer each frame. |
| float new_opacity = 0.9f / (frame_number + 1); |
| child_layer_->SetOpacity(new_opacity); |
| PostSetNeedsCommitToMainThread(); |
| } |
| |
| void WillPrepareTilesOnThread(LayerTreeHostImpl* impl) override { |
| if (impl->active_tree()->source_frame_number() >= 0) |
| prepare_tiles_count_++; |
| |
| switch (impl->active_tree()->source_frame_number()) { |
| case 0: |
| EXPECT_EQ(1, prepare_tiles_count_); |
| break; |
| case 1: |
| EXPECT_EQ(2, prepare_tiles_count_); |
| break; |
| case 2: |
| EXPECT_EQ(3, prepare_tiles_count_); |
| break; |
| } |
| } |
| |
| void DrawLayersOnThread(LayerTreeHostImpl* impl) override { |
| draw_count_++; |
| |
| switch (impl->active_tree()->source_frame_number()) { |
| case 0: |
| // There is actual damage as the layers are set up. |
| EXPECT_EQ(1, draw_count_); |
| break; |
| case 1: |
| // There is no damage, but draw because the early damage check |
| // didn't occur. |
| EXPECT_EQ(2, draw_count_); |
| break; |
| default: |
| // After the first two draws, the early damage check should kick |
| // in and prevent further draws. |
| ADD_FAILURE(); |
| } |
| } |
| |
| void AfterTest() override { EXPECT_EQ(2, draw_count_); } |
| |
| private: |
| scoped_refptr<Layer> child_layer_; |
| int prepare_tiles_count_ = 0; |
| int draw_count_ = 0; |
| }; |
| |
| // This behavior is specific to Android WebView, which only uses |
| // multi-threaded compositor. |
| // Flaky: https://crbug.com/947673 |
| // MULTI_THREAD_TEST_F(LayerTreeHostTestPrepareTilesWithoutDraw); |
| |
| // Verify CanDraw() is false until first commit. |
| class LayerTreeHostTestCantDrawBeforeCommit : public LayerTreeHostTest { |
| protected: |
| void BeginTest() override { PostSetNeedsCommitToMainThread(); } |
| |
| void ReadyToCommitOnThread(LayerTreeHostImpl* host_impl) override { |
| EXPECT_FALSE(host_impl->CanDraw()); |
| } |
| |
| void DidActivateTreeOnThread(LayerTreeHostImpl* host_impl) override { |
| EXPECT_TRUE(host_impl->CanDraw()); |
| EndTest(); |
| } |
| |
| void AfterTest() override {} |
| }; |
| |
| SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestCantDrawBeforeCommit); |
| |
| // Verify CanDraw() is false until first commit+activate. |
| class LayerTreeHostTestCantDrawBeforeCommitActivate : public LayerTreeHostTest { |
| protected: |
| void BeginTest() override { PostSetNeedsCommitToMainThread(); } |
| |
| void CommitCompleteOnThread(LayerTreeHostImpl* host_impl) override { |
| EXPECT_FALSE(host_impl->CanDraw()); |
| } |
| |
| void WillActivateTreeOnThread(LayerTreeHostImpl* host_impl) override { |
| EXPECT_FALSE(host_impl->CanDraw()); |
| } |
| |
| void DidActivateTreeOnThread(LayerTreeHostImpl* host_impl) override { |
| EXPECT_TRUE(host_impl->CanDraw()); |
| EndTest(); |
| } |
| |
| void AfterTest() override {} |
| }; |
| |
| // Single thread mode commits directly to the active tree, so CanDraw() |
| // is true by the time WillActivateTreeOnThread is called. |
| MULTI_THREAD_TEST_F(LayerTreeHostTestCantDrawBeforeCommitActivate); |
| |
| // Verify damage status of property trees is preserved after commit. |
| class LayerTreeHostTestPropertyTreesChangedSync : public LayerTreeHostTest { |
| protected: |
| void SetupTree() override { |
| root_ = Layer::Create(); |
| child_ = Layer::Create(); |
| // This is to force the child to create a transform and effect node. |
| child_->SetForceRenderSurfaceForTesting(true); |
| root_->AddChild(child_); |
| layer_tree_host()->SetRootLayer(root_); |
| LayerTreeHostTest::SetupTree(); |
| } |
| |
| enum Animations { |
| OPACITY, |
| TRANSFORM, |
| FILTER, |
| END, |
| }; |
| |
| void BeginTest() override { |
| index_ = OPACITY; |
| PostSetNeedsCommitToMainThread(); |
| } |
| |
| void DidCommit() override { |
| switch (layer_tree_host()->SourceFrameNumber()) { |
| case 2: |
| // We rebuild property trees for this case to test the code path of |
| // damage status synchronization when property trees are different. |
| layer_tree_host()->property_trees()->needs_rebuild = true; |
| break; |
| default: |
| EXPECT_FALSE(layer_tree_host()->property_trees()->needs_rebuild); |
| } |
| } |
| |
| void CommitCompleteOnThread(LayerTreeHostImpl* impl) override { |
| gfx::Transform transform; |
| FilterOperations filters; |
| LayerImpl* root = impl->active_tree()->root_layer_for_testing(); |
| switch (static_cast<Animations>(index_)) { |
| case OPACITY: |
| index_++; |
| impl->active_tree()->ResetAllChangeTracking(); |
| impl->active_tree()->SetOpacityMutated(root->element_id(), 0.5f); |
| PostSetNeedsCommitToMainThread(); |
| break; |
| case TRANSFORM: |
| index_++; |
| EXPECT_TRUE(impl->active_tree() |
| ->LayerById(root_->id()) |
| ->LayerPropertyChanged()); |
| impl->active_tree()->ResetAllChangeTracking(); |
| EXPECT_FALSE(impl->active_tree() |
| ->LayerById(root_->id()) |
| ->LayerPropertyChanged()); |
| EXPECT_FALSE(impl->active_tree() |
| ->LayerById(child_->id()) |
| ->LayerPropertyChanged()); |
| transform.Translate(10, 10); |
| impl->active_tree()->SetTransformMutated(root->element_id(), transform); |
| PostSetNeedsCommitToMainThread(); |
| break; |
| case FILTER: |
| index_++; |
| EXPECT_TRUE(root->LayerPropertyChanged()); |
| EXPECT_TRUE(impl->active_tree() |
| ->LayerById(child_->id()) |
| ->LayerPropertyChanged()); |
| impl->active_tree()->ResetAllChangeTracking(); |
| EXPECT_FALSE(root->LayerPropertyChanged()); |
| EXPECT_FALSE(impl->active_tree() |
| ->LayerById(child_->id()) |
| ->LayerPropertyChanged()); |
| filters.Append(FilterOperation::CreateOpacityFilter(0.5f)); |
| impl->active_tree()->SetFilterMutated(root->element_id(), filters); |
| PostSetNeedsCommitToMainThread(); |
| break; |
| case END: |
| EXPECT_TRUE(root->LayerPropertyChanged()); |
| EndTest(); |
| break; |
| } |
| } |
| |
| void AfterTest() override {} |
| |
| private: |
| int index_; |
| scoped_refptr<Layer> root_; |
| scoped_refptr<Layer> child_; |
| }; |
| |
| SINGLE_THREAD_TEST_F(LayerTreeHostTestPropertyTreesChangedSync); |
| |
| // Simple base class for tests that just need to mutate the layer tree |
| // host and observe results without any impl thread, multi-layer, or |
| // multi-frame antics. |
| class LayerTreeHostTestLayerListsTest : public LayerTreeHostTest { |
| public: |
| explicit LayerTreeHostTestLayerListsTest(bool use_layer_lists) |
| : use_layer_lists_(use_layer_lists) {} |
| |
| protected: |
| void InitializeSettings(LayerTreeSettings* settings) override { |
| settings->use_layer_lists = use_layer_lists_; |
| } |
| |
| void SetupTree() override { |
| root_ = Layer::Create(); |
| layer_tree_host()->SetRootLayer(root_); |
| LayerTreeHostTest::SetupTree(); |
| } |
| |
| void AfterTest() override {} |
| |
| scoped_refptr<Layer> root_; |
| |
| private: |
| bool use_layer_lists_; |
| }; |
| |
| class LayerTreeHostTestAnimationOpacityMutatedNotUsingLayerLists |
| : public LayerTreeHostTestLayerListsTest { |
| public: |
| LayerTreeHostTestAnimationOpacityMutatedNotUsingLayerLists() |
| : LayerTreeHostTestLayerListsTest(false) {} |
| |
| protected: |
| void BeginTest() override { |
| EXPECT_EQ(1.0f, root_->opacity()); |
| layer_tree_host()->SetElementOpacityMutated(root_->element_id(), |
| ElementListType::ACTIVE, 0.3f); |
| // When not using layer lists, opacity is stored on the layer. |
| EXPECT_EQ(0.3f, root_->opacity()); |
| EndTest(); |
| } |
| }; |
| |
| SINGLE_THREAD_TEST_F( |
| LayerTreeHostTestAnimationOpacityMutatedNotUsingLayerLists); |
| |
| class LayerTreeHostTestAnimationOpacityMutatedUsingLayerLists |
| : public LayerTreeHostTestLayerListsTest { |
| public: |
| LayerTreeHostTestAnimationOpacityMutatedUsingLayerLists() |
| : LayerTreeHostTestLayerListsTest(true) {} |
| |
| protected: |
| void BeginTest() override { |
| // Insert a dummy effect node to observe its mutation. This would |
| // normally have been created by PaintArtifactCompositor. |
| int effect_node_id = |
| layer_tree_host()->property_trees()->effect_tree.Insert( |
| EffectNode(), EffectTree::kInvalidNodeId); |
| layer_tree_host() |
| ->property_trees() |
| ->element_id_to_effect_node_index[root_->element_id()] = effect_node_id; |
| |
| EXPECT_EQ(1.0f, root_->opacity()); |
| EXPECT_EQ(1.0f, |
| layer_tree_host() |
| ->property_trees() |
| ->effect_tree.FindNodeFromElementId(root_->element_id()) |
| ->opacity); |
| |
| layer_tree_host()->SetElementOpacityMutated(root_->element_id(), |
| ElementListType::ACTIVE, 0.3f); |
| |
| // When using layer lists, we don't have to store the opacity on the layer. |
| EXPECT_EQ(1.0f, root_->opacity()); |
| // The opacity should have been set directly on the effect node instead. |
| EXPECT_EQ(0.3f, |
| layer_tree_host() |
| ->property_trees() |
| ->effect_tree.FindNodeFromElementId(root_->element_id()) |
| ->opacity); |
| EndTest(); |
| } |
| }; |
| |
| SINGLE_THREAD_TEST_F(LayerTreeHostTestAnimationOpacityMutatedUsingLayerLists); |
| |
| class LayerTreeHostTestAnimationTransformMutatedNotUsingLayerLists |
| : public LayerTreeHostTestLayerListsTest { |
| public: |
| LayerTreeHostTestAnimationTransformMutatedNotUsingLayerLists() |
| : LayerTreeHostTestLayerListsTest(false) {} |
| |
| protected: |
| void BeginTest() override { |
| EXPECT_EQ(gfx::Transform(), root_->transform()); |
| gfx::Transform expected_transform; |
| expected_transform.Translate(42, 42); |
| layer_tree_host()->SetElementTransformMutated( |
| root_->element_id(), ElementListType::ACTIVE, expected_transform); |
| // When not using layer lists, transform is stored on the layer. |
| EXPECT_EQ(expected_transform, root_->transform()); |
| EndTest(); |
| } |
| }; |
| |
| SINGLE_THREAD_TEST_F( |
| LayerTreeHostTestAnimationTransformMutatedNotUsingLayerLists); |
| |
| class LayerTreeHostTestAnimationTransformMutatedUsingLayerLists |
| : public LayerTreeHostTestLayerListsTest { |
| public: |
| LayerTreeHostTestAnimationTransformMutatedUsingLayerLists() |
| : LayerTreeHostTestLayerListsTest(true) {} |
| |
| protected: |
| void BeginTest() override { |
| // Insert a dummy transform node to observe its mutation. This would |
| // normally have been created by PaintArtifactCompositor. |
| int transform_node_id = |
| layer_tree_host()->property_trees()->transform_tree.Insert( |
| TransformNode(), TransformTree::kInvalidNodeId); |
| layer_tree_host() |
| ->property_trees() |
| ->element_id_to_transform_node_index[root_->element_id()] = |
| transform_node_id; |
| |
| EXPECT_EQ(gfx::Transform(), root_->transform()); |
| EXPECT_EQ(gfx::Transform(), |
| layer_tree_host() |
| ->property_trees() |
| ->transform_tree.FindNodeFromElementId(root_->element_id()) |
| ->local); |
| |
| gfx::Transform expected_transform; |
| expected_transform.Translate(42, 42); |
| layer_tree_host()->SetElementTransformMutated( |
| root_->element_id(), ElementListType::ACTIVE, expected_transform); |
| |
| // When using layer lists, we don't have to store the transform on the |
| // layer. |
| EXPECT_EQ(gfx::Transform(), root_->transform()); |
| // The transform should have been set directly on the transform node |
| // instead. |
| EXPECT_EQ(expected_transform, |
| layer_tree_host() |
| ->property_trees() |
| ->transform_tree.FindNodeFromElementId(root_->element_id()) |
| ->local); |
| EndTest(); |
| } |
| }; |
| |
| SINGLE_THREAD_TEST_F(LayerTreeHostTestAnimationTransformMutatedUsingLayerLists); |
| |
| class LayerTreeHostTestAnimationFilterMutatedNotUsingLayerLists |
| : public LayerTreeHostTestLayerListsTest { |
| public: |
| LayerTreeHostTestAnimationFilterMutatedNotUsingLayerLists() |
| : LayerTreeHostTestLayerListsTest(false) {} |
| |
| protected: |
| void BeginTest() override { |
| FilterOperations filters; |
| EXPECT_EQ(FilterOperations(), root_->filters()); |
| filters.Append(FilterOperation::CreateOpacityFilter(0.5f)); |
| layer_tree_host()->SetElementFilterMutated( |
| root_->element_id(), ElementListType::ACTIVE, filters); |
| // When not using layer lists, filters are just stored directly on the |
| // layer. |
| EXPECT_EQ(filters, root_->filters()); |
| EndTest(); |
| } |
| }; |
| |
| SINGLE_THREAD_TEST_F(LayerTreeHostTestAnimationFilterMutatedNotUsingLayerLists); |
| |
| class LayerTreeHostTestAnimationFilterMutatedUsingLayerLists |
| : public LayerTreeHostTestLayerListsTest { |
| public: |
| LayerTreeHostTestAnimationFilterMutatedUsingLayerLists() |
| : LayerTreeHostTestLayerListsTest(true) {} |
| |
| protected: |
| void BeginTest() override { |
| // Insert a dummy effect node to observe its mutation. This would |
| // normally have been created by PaintArtifactCompositor. |
| int effect_node_id = |
| layer_tree_host()->property_trees()->effect_tree.Insert( |
| EffectNode(), EffectTree::kInvalidNodeId); |
| layer_tree_host() |
| ->property_trees() |
| ->element_id_to_effect_node_index[root_->element_id()] = effect_node_id; |
| |
| EXPECT_EQ(FilterOperations(), root_->filters()); |
| EXPECT_EQ(FilterOperations(), |
| layer_tree_host() |
| ->property_trees() |
| ->effect_tree.FindNodeFromElementId(root_->element_id()) |
| ->filters); |
| |
| FilterOperations filters; |
| filters.Append(FilterOperation::CreateOpacityFilter(0.5f)); |
| layer_tree_host()->SetElementFilterMutated( |
| root_->element_id(), ElementListType::ACTIVE, filters); |
| |
| // When using layer lists, we don't have to store the filters on the layer. |
| EXPECT_EQ(FilterOperations(), root_->filters()); |
| // The filter should have been set directly on the effect node instead. |
| EXPECT_EQ(filters, |
| layer_tree_host() |
| ->property_trees() |
| ->effect_tree.FindNodeFromElementId(root_->element_id()) |
| ->filters); |
| EndTest(); |
| } |
| }; |
| |
| SINGLE_THREAD_TEST_F(LayerTreeHostTestAnimationFilterMutatedUsingLayerLists); |
| |
| class LayerTreeHostTestEffectTreeSync : public LayerTreeHostTest { |
| protected: |
| void SetupTree() override { |
| root_ = Layer::Create(); |
| layer_tree_host()->SetRootLayer(root_); |
| blur_filter_.Append(FilterOperation::CreateBlurFilter(0.5f)); |
| brightness_filter_.Append(FilterOperation::CreateBrightnessFilter(0.25f)); |
| sepia_filter_.Append(FilterOperation::CreateSepiaFilter(0.75f)); |
| LayerTreeHostTest::SetupTree(); |
| } |
| |
| void BeginTest() override { PostSetNeedsCommitToMainThread(); } |
| |
| void DidCommit() override { |
| EffectTree& effect_tree = layer_tree_host()->property_trees()->effect_tree; |
| EffectNode* node = effect_tree.Node(root_->effect_tree_index()); |
| switch (layer_tree_host()->SourceFrameNumber()) { |
| case 1: |
| node->opacity = 0.5f; |
| node->is_currently_animating_opacity = true; |
| break; |
| case 2: |
| node->is_currently_animating_opacity = true; |
| break; |
| case 3: |
| node->is_currently_animating_opacity = false; |
| break; |
| case 4: |
| node->opacity = 0.25f; |
| node->is_currently_animating_opacity = true; |
| break; |
| case 5: |
| node->filters = blur_filter_; |
| node->is_currently_animating_filter = true; |
| break; |
| case 6: |
| node->is_currently_animating_filter = true; |
| break; |
| case 7: |
| node->is_currently_animating_filter = false; |
| break; |
| case 8: |
| node->filters = sepia_filter_; |
| node->is_currently_animating_filter = true; |
| break; |
| } |
| } |
| |
| void CommitCompleteOnThread(LayerTreeHostImpl* impl) override { |
| EffectTree& effect_tree = impl->sync_tree()->property_trees()->effect_tree; |
| LayerImpl* root = impl->sync_tree()->root_layer_for_testing(); |
| EffectNode* node = effect_tree.Node(root->effect_tree_index()); |
| switch (impl->sync_tree()->source_frame_number()) { |
| case 0: |
| impl->sync_tree()->SetOpacityMutated(root->element_id(), 0.75f); |
| PostSetNeedsCommitToMainThread(); |
| break; |
| case 1: |
| EXPECT_EQ(node->opacity, 0.75f); |
| PostSetNeedsCommitToMainThread(); |
| break; |
| case 2: |
| EXPECT_EQ(node->opacity, 0.75f); |
| impl->sync_tree()->SetOpacityMutated(root->element_id(), 0.75f); |
| PostSetNeedsCommitToMainThread(); |
| break; |
| case 3: |
| EXPECT_EQ(node->opacity, 0.5f); |
| PostSetNeedsCommitToMainThread(); |
| break; |
| case 4: |
| EXPECT_EQ(node->opacity, 0.25f); |
| impl->sync_tree()->SetFilterMutated(root->element_id(), |
| brightness_filter_); |
| PostSetNeedsCommitToMainThread(); |
| break; |
| case 5: |
| EXPECT_EQ(node->filters, brightness_filter_); |
| PostSetNeedsCommitToMainThread(); |
| break; |
| case 6: |
| EXPECT_EQ(node->filters, brightness_filter_); |
| impl->sync_tree()->SetFilterMutated(root->element_id(), |
| brightness_filter_); |
| PostSetNeedsCommitToMainThread(); |
| break; |
| case 7: |
| EXPECT_EQ(node->filters, blur_filter_); |
| PostSetNeedsCommitToMainThread(); |
| break; |
| case 8: |
| EXPECT_EQ(node->filters, sepia_filter_); |
| EndTest(); |
| break; |
| } |
| } |
| |
| void AfterTest() override {} |
| |
| private: |
| scoped_refptr<Layer> root_; |
| FilterOperations blur_filter_; |
| FilterOperations brightness_filter_; |
| FilterOperations sepia_filter_; |
| }; |
| |
| SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestEffectTreeSync); |
| |
| class LayerTreeHostTestTransformTreeSync : public LayerTreeHostTest { |
| protected: |
| void SetupTree() override { |
| root_ = Layer::Create(); |
| layer_tree_host()->SetRootLayer(root_); |
| LayerTreeHostTest::SetupTree(); |
| } |
| |
| void BeginTest() override { PostSetNeedsCommitToMainThread(); } |
| |
| void DidCommit() override { |
| TransformTree& transform_tree = |
| layer_tree_host()->property_trees()->transform_tree; |
| TransformNode* node = transform_tree.Node(root_->transform_tree_index()); |
| gfx::Transform rotate10; |
| rotate10.Rotate(10.f); |
| switch (layer_tree_host()->SourceFrameNumber()) { |
| case 1: |
| node->local = rotate10; |
| node->is_currently_animating = true; |
| break; |
| case 2: |
| node->is_currently_animating = true; |
| break; |
| case 3: |
| node->is_currently_animating = false; |
| break; |
| case 4: |
| node->local = gfx::Transform(); |
| node->is_currently_animating = true; |
| break; |
| } |
| } |
| |
| void CommitCompleteOnThread(LayerTreeHostImpl* impl) override { |
| TransformTree& transform_tree = |
| impl->sync_tree()->property_trees()->transform_tree; |
| const LayerImpl* root_layer = impl->sync_tree()->root_layer_for_testing(); |
| const TransformNode* node = |
| transform_tree.Node(root_layer->transform_tree_index()); |
| gfx::Transform rotate10; |
| rotate10.Rotate(10.f); |
| gfx::Transform rotate20; |
| rotate20.Rotate(20.f); |
| switch (impl->sync_tree()->source_frame_number()) { |
| case 0: |
| impl->sync_tree()->SetTransformMutated(root_layer->element_id(), |
| rotate20); |
| PostSetNeedsCommitToMainThread(); |
| break; |
| case 1: |
| EXPECT_EQ(node->local, rotate20); |
| PostSetNeedsCommitToMainThread(); |
| break; |
| case 2: |
| EXPECT_EQ(node->local, rotate20); |
| impl->sync_tree()->SetTransformMutated(root_layer->element_id(), |
| rotate20); |
| PostSetNeedsCommitToMainThread(); |
| break; |
| case 3: |
| EXPECT_EQ(node->local, rotate10); |
| PostSetNeedsCommitToMainThread(); |
| break; |
| case 4: |
| EXPECT_EQ(node->local, gfx::Transform()); |
| EndTest(); |
| } |
| } |
| |
| void AfterTest() override {} |
| |
| private: |
| scoped_refptr<Layer> root_; |
| }; |
| |
| SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestTransformTreeSync); |
| |
| // Verify damage status is updated even when the transform tree doesn't need |
| // to be updated at draw time. |
| class LayerTreeHostTestTransformTreeDamageIsUpdated : public LayerTreeHostTest { |
| protected: |
| void SetupTree() override { |
| root_ = Layer::Create(); |
| child_ = Layer::Create(); |
| grand_child_ = Layer::Create(); |
| |
| root_->SetBounds(gfx::Size(50, 50)); |
| |
| // Make sure child is registered for animation. |
| child_->SetElementId(ElementId(2)); |
| |
| // Make sure child and grand_child have transform nodes. |
| gfx::Transform rotation; |
| rotation.RotateAboutZAxis(45.0); |
| child_->SetTransform(rotation); |
| grand_child_->SetTransform(rotation); |
| |
| root_->AddChild(child_); |
| child_->AddChild(grand_child_); |
| layer_tree_host()->SetRootLayer(root_); |
| LayerTreeHostTest::SetupTree(); |
| } |
| |
| void BeginTest() override { PostSetNeedsCommitToMainThread(); } |
| |
| void DidCommit() override { |
| if (layer_tree_host()->SourceFrameNumber() == 1) { |
| gfx::Transform scale; |
| scale.Scale(2.0, 2.0); |
| layer_tree_host()->SetElementTransformMutated( |
| child_->element_id(), ElementListType::ACTIVE, scale); |
| } |
| } |
| |
| void CommitCompleteOnThread(LayerTreeHostImpl* impl) override { |
| if (impl->sync_tree()->source_frame_number() == 0) |
| PostSetNeedsCommitToMainThread(); |
| } |
| |
| DrawResult PrepareToDrawOnThread(LayerTreeHostImpl* impl, |
| LayerTreeHostImpl::FrameData* frame_data, |
| DrawResult draw_result) override { |
| if (impl->active_tree()->source_frame_number() == 1) { |
| EXPECT_FALSE( |
| impl->active_tree()->LayerById(root_->id())->LayerPropertyChanged()); |
| EXPECT_TRUE( |
| impl->active_tree()->LayerById(child_->id())->LayerPropertyChanged()); |
| EXPECT_TRUE(impl->active_tree() |
| ->LayerById(grand_child_->id()) |
| ->LayerPropertyChanged()); |
| EndTest(); |
| } |
| |
| return draw_result; |
| } |
| |
| void DrawLayersOnThread(LayerTreeHostImpl* impl) override { |
| if (impl->active_tree()->source_frame_number() == 0) { |
| gfx::Transform scale; |
| scale.Scale(2.0, 2.0); |
| impl->active_tree()->SetTransformMutated(child_->element_id(), scale); |
| } |
| } |
| |
| void AfterTest() override {} |
| |
| private: |
| scoped_refptr<Layer> root_; |
| scoped_refptr<Layer> child_; |
| scoped_refptr<Layer> grand_child_; |
| }; |
| |
| SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestTransformTreeDamageIsUpdated); |
| |
| class UpdateCountingLayer : public PictureLayer { |
| public: |
| explicit UpdateCountingLayer(ContentLayerClient* client) |
| : PictureLayer(client) {} |
| |
| bool Update() override { |
| update_count_++; |
| return false; |
| } |
| |
| int update_count() const { return update_count_; } |
| |
| private: |
| ~UpdateCountingLayer() override = default; |
| |
| int update_count_ = 0; |
| }; |
| |
| // Test that when mask layers switches layers, this gets pushed onto impl. |
| // Also test that mask layer is in the layer update list even if its owning |
| // layer isn't. |
| class LayerTreeHostTestSwitchMaskLayer : public LayerTreeHostTest { |
| protected: |
| void SetupTree() override { |
| scoped_refptr<Layer> root = Layer::Create(); |
| root->SetBounds(gfx::Size(10, 10)); |
| child_layer_ = base::MakeRefCounted<UpdateCountingLayer>(&client_); |
| child_layer_->SetBounds(gfx::Size(10, 10)); |
| mask_layer_ = base::MakeRefCounted<UpdateCountingLayer>(&client_); |
| mask_layer_->SetBounds(gfx::Size(10, 10)); |
| child_layer_->SetMaskLayer(mask_layer_.get()); |
| root->AddChild(child_layer_); |
| layer_tree_host()->SetRootLayer(root); |
| LayerTreeHostTest::SetupTree(); |
| } |
| |
| void BeginTest() override { |
| index_ = 0; |
| PostSetNeedsCommitToMainThread(); |
| } |
| |
| void DidCommit() override { |
| switch (layer_tree_host()->SourceFrameNumber()) { |
| case 1: |
| // Root and mask layer should have the same source frame number as they |
| // will be in the layer update list but the child is not as it has empty |
| // bounds. |
| EXPECT_EQ(mask_layer_->update_count(), 1); |
| EXPECT_EQ(child_layer_->update_count(), 0); |
| |
| layer_tree_host()->root_layer()->RemoveAllChildren(); |
| layer_tree_host()->root_layer()->SetMaskLayer(mask_layer_.get()); |
| break; |
| } |
| } |
| |
| void CommitCompleteOnThread(LayerTreeHostImpl* impl) override { |
| switch (index_) { |
| case 0: |
| index_++; |
| EXPECT_FALSE( |
| GetRenderSurface(impl->sync_tree()->root_layer_for_testing()) |
| ->MaskLayer()); |
| break; |
| case 1: |
| EXPECT_TRUE( |
| GetRenderSurface(impl->sync_tree()->root_layer_for_testing()) |
| ->MaskLayer()); |
| EndTest(); |
| break; |
| } |
| } |
| |
| void AfterTest() override {} |
| |
| FakeContentLayerClient client_; |
| scoped_refptr<UpdateCountingLayer> mask_layer_; |
| scoped_refptr<UpdateCountingLayer> child_layer_; |
| int index_; |
| }; |
| |
| SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestSwitchMaskLayer); |
| |
| // 1 setNeedsRedraw after the first commit has completed should lead to 1 |
| // additional draw. |
| class LayerTreeHostTestSetNeedsRedraw : public LayerTreeHostTest { |
| public: |
| LayerTreeHostTestSetNeedsRedraw() : num_commits_(0), num_draws_(0) {} |
| |
| void BeginTest() override { PostSetNeedsCommitToMainThread(); } |
| |
| void DrawLayersOnThread(LayerTreeHostImpl* impl) override { |
| EXPECT_EQ(0, impl->active_tree()->source_frame_number()); |
| if (!num_draws_) { |
| // Redraw again to verify that the second redraw doesn't commit. |
| PostSetNeedsRedrawToMainThread(); |
| } else { |
| EndTest(); |
| } |
| num_draws_++; |
| } |
| |
| void CommitCompleteOnThread(LayerTreeHostImpl* impl) override { |
| EXPECT_EQ(0, num_draws_); |
| num_commits_++; |
| } |
| |
| void AfterTest() override { |
| EXPECT_GE(2, num_draws_); |
| EXPECT_EQ(1, num_commits_); |
| } |
| |
| private: |
| int num_commits_; |
| int num_draws_; |
| }; |
| |
| MULTI_THREAD_TEST_F(LayerTreeHostTestSetNeedsRedraw); |
| |
| // After setNeedsRedrawRect(invalid_rect) the final damage_rect |
| // must contain invalid_rect. |
| class LayerTreeHostTestSetNeedsRedrawRect : public LayerTreeHostTest { |
| public: |
| LayerTreeHostTestSetNeedsRedrawRect() |
| : num_draws_(0), bounds_(50, 50), invalid_rect_(10, 10, 20, 20) {} |
| |
| void BeginTest() override { |
| root_layer_ = FakePictureLayer::Create(&client_); |
| root_layer_->SetIsDrawable(true); |
| root_layer_->SetBounds(bounds_); |
| layer_tree_host()->SetRootLayer(root_layer_); |
| layer_tree_host()->SetViewportSizeAndScale(bounds_, 1.f, |
| viz::LocalSurfaceIdAllocation()); |
| PostSetNeedsCommitToMainThread(); |
| client_.set_bounds(root_layer_->bounds()); |
| } |
| |
| DrawResult PrepareToDrawOnThread(LayerTreeHostImpl* host_impl, |
| LayerTreeHostImpl::FrameData* frame_data, |
| DrawResult draw_result) override { |
| EXPECT_EQ(DRAW_SUCCESS, draw_result); |
| |
| gfx::Rect root_damage_rect; |
| if (!frame_data->render_passes.empty()) |
| root_damage_rect = frame_data->render_passes.back()->damage_rect; |
| |
| if (!num_draws_) { |
| // If this is the first frame, expect full frame damage. |
| EXPECT_EQ(root_damage_rect, gfx::Rect(bounds_)); |
| } else { |
| // Check that invalid_rect_ is indeed repainted. |
| EXPECT_TRUE(root_damage_rect.Contains(invalid_rect_)); |
| } |
| |
| return draw_result; |
| } |
| |
| void DrawLayersOnThread(LayerTreeHostImpl* impl) override { |
| if (!num_draws_) { |
| PostSetNeedsRedrawRectToMainThread(invalid_rect_); |
| } else { |
| EndTest(); |
| } |
| num_draws_++; |
| } |
| |
| void AfterTest() override { EXPECT_EQ(2, num_draws_); } |
| |
| private: |
| int num_draws_; |
| const gfx::Size bounds_; |
| const gfx::Rect invalid_rect_; |
| FakeContentLayerClient client_; |
| scoped_refptr<FakePictureLayer> root_layer_; |
| }; |
| |
| SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestSetNeedsRedrawRect); |
| |
| // Ensure the texture size of the pending and active trees are identical when a |
| // layer is not in the viewport and a resize happens on the viewport |
| class LayerTreeHostTestGpuRasterDeviceSizeChanged : public LayerTreeHostTest { |
| public: |
| LayerTreeHostTestGpuRasterDeviceSizeChanged() |
| : num_draws_(0), bounds_(500, 64), invalid_rect_(10, 10, 20, 20) {} |
| |
| void BeginTest() override { |
| client_.set_fill_with_nonsolid_color(true); |
| root_layer_ = FakePictureLayer::Create(&client_); |
| root_layer_->SetIsDrawable(true); |
| gfx::Transform transform; |
| // Translate the layer out of the viewport to force it to not update its |
| // tile size via PushProperties. |
| transform.Translate(10000.0, 10000.0); |
| root_layer_->SetTransform(transform); |
| root_layer_->SetBounds(bounds_); |
| layer_tree_host()->SetRootLayer(root_layer_); |
| layer_tree_host()->SetViewportSizeAndScale(bounds_, 1.f, |
| viz::LocalSurfaceIdAllocation()); |
| |
| PostSetNeedsCommitToMainThread(); |
| client_.set_bounds(root_layer_->bounds()); |
| } |
| |
| void InitializeSettings(LayerTreeSettings* settings) override { |
| settings->gpu_rasterization_forced = true; |
| } |
| |
| void CommitCompleteOnThread(LayerTreeHostImpl* host_impl) override { |
| if (num_draws_ == 2) { |
| auto* pending_tree = host_impl->pending_tree(); |
| auto* pending_layer_impl = static_cast<FakePictureLayerImpl*>( |
| pending_tree->root_layer_for_testing()); |
| EXPECT_NE(pending_layer_impl, nullptr); |
| |
| auto* active_tree = host_impl->pending_tree(); |
| auto* active_layer_impl = static_cast<FakePictureLayerImpl*>( |
| active_tree->root_layer_for_testing()); |
| EXPECT_NE(pending_layer_impl, nullptr); |
| |
| auto* active_tiling_set = active_layer_impl->picture_layer_tiling_set(); |
| auto* active_tiling = active_tiling_set->tiling_at(0); |
| auto* pending_tiling_set = pending_layer_impl->picture_layer_tiling_set(); |
| auto* pending_tiling = pending_tiling_set->tiling_at(0); |
| EXPECT_EQ( |
| pending_tiling->TilingDataForTesting().max_texture_size().width(), |
| active_tiling->TilingDataForTesting().max_texture_size().width()); |
| } |
| } |
| |
| void DidCommitAndDrawFrame() override { |
| // On the second commit, resize the viewport. |
| if (num_draws_ == 1) { |
| layer_tree_host()->SetViewportSizeAndScale( |
| gfx::Size(400, 64), 1.f, viz::LocalSurfaceIdAllocation()); |
| } |
| if (num_draws_ < 2) { |
| layer_tree_host()->SetNeedsRedrawRect(invalid_rect_); |
| layer_tree_host()->SetNeedsCommit(); |
| num_draws_++; |
| } else { |
| EndTest(); |
| } |
| } |
| |
| void AfterTest() override {} |
| |
| private: |
| int num_draws_; |
| const gfx::Size bounds_; |
| const gfx::Rect invalid_rect_; |
| FakeContentLayerClient client_; |
| scoped_refptr<FakePictureLayer> root_layer_; |
| }; |
| |
| // As there's no pending tree in single-threaded case, this test should run |
| // only for multi-threaded case. |
| MULTI_THREAD_TEST_F(LayerTreeHostTestGpuRasterDeviceSizeChanged); |
| |
| class LayerTreeHostTestNoExtraCommitFromInvalidate : public LayerTreeHostTest { |
| public: |
| void InitializeSettings(LayerTreeSettings* settings) override { |
| settings->layer_transforms_should_scale_layer_contents = true; |
| } |
| |
| void SetupTree() override { |
| root_layer_ = Layer::Create(); |
| root_layer_->SetBounds(gfx::Size(10, 20)); |
| |
| scaled_layer_ = FakePictureLayer::Create(&client_); |
| scaled_layer_->SetBounds(gfx::Size(1, 1)); |
| root_layer_->AddChild(scaled_layer_); |
| |
| layer_tree_host()->SetRootLayer(root_layer_); |
| LayerTreeHostTest::SetupTree(); |
| client_.set_bounds(root_layer_->bounds()); |
| } |
| |
| void BeginTest() override { PostSetNeedsCommitToMainThread(); } |
| |
| void DrawLayersOnThread(LayerTreeHostImpl* host_impl) override { |
| if (host_impl->active_tree()->source_frame_number() == 1) |
| EndTest(); |
| } |
| |
| void DidCommit() override { |
| switch (layer_tree_host()->SourceFrameNumber()) { |
| case 1: |
| // SetBounds grows the layer and exposes new content. |
| scaled_layer_->SetBounds(gfx::Size(4, 4)); |
| break; |
| default: |
| // No extra commits. |
| EXPECT_EQ(2, layer_tree_host()->SourceFrameNumber()); |
| } |
| } |
| |
| void AfterTest() override { |
| EXPECT_EQ(gfx::Size(4, 4).ToString(), scaled_layer_->bounds().ToString()); |
| } |
| |
| private: |
| FakeContentLayerClient client_; |
| scoped_refptr<Layer> root_layer_; |
| scoped_refptr<Layer> scaled_layer_; |
| }; |
| |
| SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestNoExtraCommitFromInvalidate); |
| |
| class LayerTreeHostTestNoExtraCommitFromScrollbarInvalidate |
| : public LayerTreeHostTest { |
| public: |
| void InitializeSettings(LayerTreeSettings* settings) override { |
| settings->layer_transforms_should_scale_layer_contents = true; |
| } |
| |
| void SetupTree() override { |
| root_layer_ = Layer::Create(); |
| root_layer_->SetBounds(gfx::Size(10, 20)); |
| |
| bool paint_scrollbar = true; |
| bool has_thumb = false; |
| scrollbar_ = FakePaintedScrollbarLayer::Create(paint_scrollbar, has_thumb, |
| root_layer_->element_id()); |
| scrollbar_->SetPosition(gfx::PointF(0.f, 10.f)); |
| scrollbar_->SetBounds(gfx::Size(10, 10)); |
| |
| root_layer_->AddChild(scrollbar_); |
| |
| layer_tree_host()->SetRootLayer(root_layer_); |
| LayerTreeHostTest::SetupTree(); |
| client_.set_bounds(root_layer_->bounds()); |
| } |
| |
| void BeginTest() override { PostSetNeedsCommitToMainThread(); } |
| |
| void DrawLayersOnThread(LayerTreeHostImpl* host_impl) override { |
| if (host_impl->active_tree()->source_frame_number() == 1) |
| EndTest(); |
| } |
| |
| void DidCommit() override { |
| switch (layer_tree_host()->SourceFrameNumber()) { |
| 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()->SetViewportSizeAndScale( |
| layer_tree_host()->device_viewport_size(), 4.f, |
| layer_tree_host()->local_surface_id_allocation_from_parent()); |
| break; |
| default: |
| // No extra commits. |
| EXPECT_EQ(2, layer_tree_host()->SourceFrameNumber()); |
| break; |
| } |
| } |
| |
| void AfterTest() override {} |
| |
| private: |
| FakeContentLayerClient client_; |
| scoped_refptr<Layer> root_layer_; |
| scoped_refptr<FakePaintedScrollbarLayer> scrollbar_; |
| }; |
| |
| SINGLE_AND_MULTI_THREAD_TEST_F( |
| LayerTreeHostTestNoExtraCommitFromScrollbarInvalidate); |
| |
| class LayerTreeHostTestDeviceScaleFactorChange : public LayerTreeHostTest { |
| public: |
| void InitializeSettings(LayerTreeSettings* settings) override { |
| settings->layer_transforms_should_scale_layer_contents = true; |
| } |
| |
| void SetupTree() override { |
| root_layer_ = Layer::Create(); |
| root_layer_->SetBounds(gfx::Size(10, 20)); |
| |
| child_layer_ = FakePictureLayer::Create(&client_); |
| child_layer_->SetBounds(gfx::Size(10, 10)); |
| root_layer_->AddChild(child_layer_); |
| |
| layer_tree_host()->SetRootLayer(root_layer_); |
| LayerTreeHostTest::SetupTree(); |
| client_.set_bounds(root_layer_->bounds()); |
| } |
| |
| void BeginTest() override { PostSetNeedsCommitToMainThread(); } |
| |
| void DidCommit() override { |
| if (layer_tree_host()->SourceFrameNumber() == 1) { |
| layer_tree_host()->SetViewportSizeAndScale( |
| layer_tree_host()->device_viewport_size(), 4.f, |
| layer_tree_host()->local_surface_id_allocation_from_parent()); |
| } |
| } |
| |
| void CommitCompleteOnThread(LayerTreeHostImpl* host_impl) override { |
| if (host_impl->sync_tree()->source_frame_number() == 1) { |
| EXPECT_EQ(4.f, host_impl->sync_tree()->device_scale_factor()); |
| if (host_impl->pending_tree()) { |
| // The active tree's device scale factor shouldn't change until |
| // activation. |
| EXPECT_EQ(1.f, host_impl->active_tree()->device_scale_factor()); |
| } |
| } |
| } |
| |
| DrawResult PrepareToDrawOnThread(LayerTreeHostImpl* host_impl, |
| LayerTreeHostImpl::FrameData* frame_data, |
| DrawResult draw_result) override { |
| if (host_impl->active_tree()->source_frame_number() == 0) { |
| EXPECT_EQ(1.f, host_impl->active_tree()->device_scale_factor()); |
| } else { |
| gfx::Rect root_damage_rect = |
| frame_data->render_passes.back()->damage_rect; |
| EXPECT_EQ( |
| gfx::Rect( |
| host_impl->active_tree()->root_layer_for_testing()->bounds()), |
| root_damage_rect); |
| EXPECT_EQ(4.f, host_impl->active_tree()->device_scale_factor()); |
| EndTest(); |
| } |
| |
| return draw_result; |
| } |
| |
| void AfterTest() override {} |
| |
| private: |
| FakeContentLayerClient client_; |
| scoped_refptr<Layer> root_layer_; |
| scoped_refptr<Layer> child_layer_; |
| }; |
| |
| SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestDeviceScaleFactorChange); |
| |
| class LayerTreeHostTestRasterColorSpaceChange : public LayerTreeHostTest { |
| public: |
| void SetupTree() override { |
| space1_ = gfx::ColorSpace::CreateXYZD50(); |
| space2_ = gfx::ColorSpace::CreateSRGB(); |
| |
| root_layer_ = Layer::Create(); |
| root_layer_->SetBounds(gfx::Size(10, 20)); |
| |
| child_layer_ = FakePictureLayer::Create(&client_); |
| child_layer_->SetBounds(gfx::Size(10, 10)); |
| root_layer_->AddChild(child_layer_); |
| |
| layer_tree_host()->SetRootLayer(root_layer_); |
| layer_tree_host()->SetRasterColorSpace(space1_); |
| LayerTreeHostTest::SetupTree(); |
| client_.set_bounds(root_layer_->bounds()); |
| } |
| |
| void BeginTest() override { PostSetNeedsCommitToMainThread(); } |
| |
| DrawResult PrepareToDrawOnThread(LayerTreeHostImpl* host_impl, |
| LayerTreeHostImpl::FrameData* frame_data, |
| DrawResult draw_result) override { |
| EXPECT_EQ(DRAW_SUCCESS, draw_result); |
| |
| int source_frame = host_impl->active_tree()->source_frame_number(); |
| switch (source_frame) { |
| case 0: |
| // The first frame will have full damage, and should be in the initial |
| // color space. |
| EXPECT_FALSE(frame_data->has_no_damage); |
| EXPECT_TRUE(space1_ == host_impl->active_tree()->raster_color_space()); |
| break; |
| case 1: |
| // Empty commit. |
| EXPECT_TRUE(frame_data->has_no_damage); |
| EXPECT_TRUE(space1_ == host_impl->active_tree()->raster_color_space()); |
| break; |
| case 2: |
| // The change from space1 to space2 should cause full damage. |
| EXPECT_FALSE(frame_data->has_no_damage); |
| EXPECT_TRUE(space2_ == host_impl->active_tree()->raster_color_space()); |
| break; |
| case 3: |
| // Empty commit with the color space set to space2 redundantly. |
| EXPECT_TRUE(frame_data->has_no_damage); |
| EXPECT_TRUE(space2_ == host_impl->active_tree()->raster_color_space()); |
| break; |
| case 4: |
| // The change from space2 to space1 should cause full damage. |
| EXPECT_FALSE(frame_data->has_no_damage); |
| EXPECT_TRUE(space1_ == host_impl->active_tree()->raster_color_space()); |
| break; |
| case 5: |
| // Empty commit. |
| EXPECT_TRUE(frame_data->has_no_damage); |
| EXPECT_TRUE(space1_ == host_impl->active_tree()->raster_color_space()); |
| EndTest(); |
| break; |
| default: |
| NOTREACHED(); |
| break; |
| } |
| |
| if (!frame_data->has_no_damage) { |
| gfx::Rect root_damage_rect = |
| frame_data->render_passes.back()->damage_rect; |
| EXPECT_EQ( |
| gfx::Rect( |
| host_impl->active_tree()->root_layer_for_testing()->bounds()), |
| root_damage_rect); |
| } |
| |
| return draw_result; |
| } |
| |
| void DidCommit() override { |
| switch (layer_tree_host()->SourceFrameNumber()) { |
| case 1: |
| PostSetNeedsCommitToMainThread(); |
| break; |
| case 2: |
| EXPECT_TRUE(child_layer_->update_rect().IsEmpty()); |
| layer_tree_host()->SetRasterColorSpace(space2_); |
| EXPECT_FALSE(child_layer_->update_rect().IsEmpty()); |
| break; |
| case 3: |
| // The redundant SetRasterColorSpace should cause no commit and no |
| // damage. Force a commit for the test to continue. |
| layer_tree_host()->SetRasterColorSpace(space2_); |
| PostSetNeedsCommitToMainThread(); |
| EXPECT_TRUE(child_layer_->update_rect().IsEmpty()); |
| break; |
| case 4: |
| EXPECT_TRUE(child_layer_->update_rect().IsEmpty()); |
| layer_tree_host()->SetRasterColorSpace(space1_); |
| EXPECT_FALSE(child_layer_->update_rect().IsEmpty()); |
| break; |
| case 5: |
| EXPECT_TRUE(child_layer_->update_rect().IsEmpty()); |
| PostSetNeedsCommitToMainThread(); |
| break; |
| case 6: |
| break; |
| default: |
| NOTREACHED(); |
| break; |
| } |
| } |
| |
| void AfterTest() override {} |
| |
| private: |
| gfx::ColorSpace space1_; |
| gfx::ColorSpace space2_; |
| FakeContentLayerClient client_; |
| scoped_refptr<Layer> root_layer_; |
| scoped_refptr<Layer> child_layer_; |
| }; |
| |
| SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestRasterColorSpaceChange); |
| |
| class LayerTreeHostTestSetNeedsCommitWithForcedRedraw |
| : public LayerTreeHostTest { |
|