blob: 54f50295e57f678755dba45019a1f350da18e3af [file] [log] [blame]
// 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 {