blob: 2a1915c5259471196d55d61c90a3442863f2229e [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 <algorithm>
#include "base/auto_reset.h"
#include "base/synchronization/lock.h"
#include "cc/animation/timing_function.h"
#include "cc/base/swap_promise.h"
#include "cc/debug/frame_rate_counter.h"
#include "cc/layers/content_layer.h"
#include "cc/layers/content_layer_client.h"
#include "cc/layers/io_surface_layer.h"
#include "cc/layers/layer_impl.h"
#include "cc/layers/painted_scrollbar_layer.h"
#include "cc/layers/picture_layer.h"
#include "cc/layers/solid_color_layer.h"
#include "cc/layers/video_layer.h"
#include "cc/output/begin_frame_args.h"
#include "cc/output/compositor_frame_ack.h"
#include "cc/output/copy_output_request.h"
#include "cc/output/copy_output_result.h"
#include "cc/output/output_surface.h"
#include "cc/quads/draw_quad.h"
#include "cc/quads/io_surface_draw_quad.h"
#include "cc/quads/tile_draw_quad.h"
#include "cc/resources/prioritized_resource.h"
#include "cc/resources/prioritized_resource_manager.h"
#include "cc/resources/resource_update_queue.h"
#include "cc/test/fake_content_layer.h"
#include "cc/test/fake_content_layer_client.h"
#include "cc/test/fake_content_layer_impl.h"
#include "cc/test/fake_layer_tree_host_client.h"
#include "cc/test/fake_output_surface.h"
#include "cc/test/fake_painted_scrollbar_layer.h"
#include "cc/test/fake_picture_layer.h"
#include "cc/test/fake_picture_layer_impl.h"
#include "cc/test/fake_picture_pile.h"
#include "cc/test/fake_proxy.h"
#include "cc/test/fake_scoped_ui_resource.h"
#include "cc/test/fake_video_frame_provider.h"
#include "cc/test/geometry_test_utils.h"
#include "cc/test/layer_tree_test.h"
#include "cc/test/test_shared_bitmap_manager.h"
#include "cc/test/test_web_graphics_context_3d.h"
#include "cc/trees/layer_tree_host_impl.h"
#include "cc/trees/layer_tree_impl.h"
#include "cc/trees/single_thread_proxy.h"
#include "cc/trees/thread_proxy.h"
#include "gpu/GLES2/gl2extchromium.h"
#include "skia/ext/refptr.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "third_party/khronos/GLES2/gl2.h"
#include "third_party/khronos/GLES2/gl2ext.h"
#include "third_party/skia/include/core/SkPicture.h"
#include "ui/gfx/frame_time.h"
#include "ui/gfx/geometry/point_conversions.h"
#include "ui/gfx/geometry/size_conversions.h"
#include "ui/gfx/geometry/vector2d_conversions.h"
using testing::_;
using testing::AnyNumber;
using testing::AtLeast;
using testing::Mock;
namespace cc {
namespace {
class LayerTreeHostTest : public LayerTreeTest {};
// Test if the LTHI receives ReadyToActivate notifications from the TileManager
// when no raster tasks get scheduled.
class LayerTreeHostTestReadyToActivateEmpty : public LayerTreeHostTest {
public:
LayerTreeHostTestReadyToActivateEmpty()
: did_notify_ready_to_activate_(false),
all_tiles_required_for_activation_are_ready_to_draw_(false),
required_for_activation_count_(0) {}
void BeginTest() override { PostSetNeedsCommitToMainThread(); }
void CommitCompleteOnThread(LayerTreeHostImpl* impl) override {
const std::vector<PictureLayerImpl*>& layers = impl->GetPictureLayers();
required_for_activation_count_ = 0;
for (const auto& layer : layers) {
FakePictureLayerImpl* fake_layer =
static_cast<FakePictureLayerImpl*>(layer);
required_for_activation_count_ +=
fake_layer->CountTilesRequiredForActivation();
}
}
void NotifyReadyToActivateOnThread(LayerTreeHostImpl* impl) override {
did_notify_ready_to_activate_ = true;
const std::vector<PictureLayerImpl*>& layers = impl->GetPictureLayers();
all_tiles_required_for_activation_are_ready_to_draw_ = true;
for (const auto& layer : layers) {
if (!layer->AllTilesRequiredForActivationAreReadyToDraw())
all_tiles_required_for_activation_are_ready_to_draw_ = false;
}
EndTest();
}
void AfterTest() override {
EXPECT_TRUE(did_notify_ready_to_activate_);
EXPECT_TRUE(all_tiles_required_for_activation_are_ready_to_draw_);
EXPECT_EQ(size_t(0), required_for_activation_count_);
}
protected:
bool did_notify_ready_to_activate_;
bool all_tiles_required_for_activation_are_ready_to_draw_;
size_t required_for_activation_count_;
};
SINGLE_AND_MULTI_THREAD_IMPL_TEST_F(LayerTreeHostTestReadyToActivateEmpty);
// Test if the LTHI receives ReadyToActivate notifications from the TileManager
// when some raster tasks flagged as REQUIRED_FOR_ACTIVATION got scheduled.
class LayerTreeHostTestReadyToActivateNonEmpty
: public LayerTreeHostTestReadyToActivateEmpty {
public:
void SetupTree() override {
client_.set_fill_with_nonsolid_color(true);
scoped_refptr<FakePictureLayer> root_layer =
FakePictureLayer::Create(&client_);
root_layer->SetBounds(gfx::Size(1024, 1024));
root_layer->SetIsDrawable(true);
layer_tree_host()->SetRootLayer(root_layer);
LayerTreeHostTest::SetupTree();
}
void AfterTest() override {
EXPECT_TRUE(did_notify_ready_to_activate_);
EXPECT_TRUE(all_tiles_required_for_activation_are_ready_to_draw_);
EXPECT_LE(size_t(1), required_for_activation_count_);
}
private:
FakeContentLayerClient client_;
};
SINGLE_AND_MULTI_THREAD_IMPL_TEST_F(LayerTreeHostTestReadyToActivateNonEmpty);
// Test if the LTHI receives ReadyToDraw notifications from the TileManager when
// no raster tasks get scheduled.
class LayerTreeHostTestReadyToDrawEmpty : public LayerTreeHostTest {
public:
LayerTreeHostTestReadyToDrawEmpty()
: did_notify_ready_to_draw_(false),
all_tiles_required_for_draw_are_ready_to_draw_(false),
required_for_draw_count_(0) {}
void BeginTest() override { PostSetNeedsCommitToMainThread(); }
void NotifyReadyToDrawOnThread(LayerTreeHostImpl* impl) override {
did_notify_ready_to_draw_ = true;
const std::vector<PictureLayerImpl*>& layers = impl->GetPictureLayers();
all_tiles_required_for_draw_are_ready_to_draw_ = true;
for (const auto& layer : layers) {
if (!layer->AllTilesRequiredForDrawAreReadyToDraw())
all_tiles_required_for_draw_are_ready_to_draw_ = false;
FakePictureLayerImpl* fake_layer =
static_cast<FakePictureLayerImpl*>(layer);
required_for_draw_count_ += fake_layer->CountTilesRequiredForDraw();
}
EndTest();
}
void AfterTest() override {
EXPECT_TRUE(did_notify_ready_to_draw_);
EXPECT_TRUE(all_tiles_required_for_draw_are_ready_to_draw_);
EXPECT_EQ(size_t(0), required_for_draw_count_);
}
protected:
bool did_notify_ready_to_draw_;
bool all_tiles_required_for_draw_are_ready_to_draw_;
size_t required_for_draw_count_;
};
SINGLE_AND_MULTI_THREAD_IMPL_TEST_F(LayerTreeHostTestReadyToDrawEmpty);
// Test if the LTHI receives ReadyToDraw notifications from the TileManager when
// some raster tasks flagged as REQUIRED_FOR_DRAW got scheduled.
class LayerTreeHostTestReadyToDrawNonEmpty
: public LayerTreeHostTestReadyToDrawEmpty {
public:
void SetupTree() override {
client_.set_fill_with_nonsolid_color(true);
scoped_refptr<FakePictureLayer> root_layer =
FakePictureLayer::Create(&client_);
root_layer->SetBounds(gfx::Size(1024, 1024));
root_layer->SetIsDrawable(true);
layer_tree_host()->SetRootLayer(root_layer);
LayerTreeHostTest::SetupTree();
}
void AfterTest() override {
EXPECT_TRUE(did_notify_ready_to_draw_);
EXPECT_TRUE(all_tiles_required_for_draw_are_ready_to_draw_);
EXPECT_LE(size_t(1), required_for_draw_count_);
}
private:
FakeContentLayerClient client_;
};
// Note: With this test setup, we only get tiles flagged as REQUIRED_FOR_DRAW in
// single threaded mode.
SINGLE_THREAD_IMPL_TEST_F(LayerTreeHostTestReadyToDrawNonEmpty);
// Two setNeedsCommits in a row should lead to at least 1 commit and at least 1
// draw with frame 0.
class LayerTreeHostTestSetNeedsCommit1 : public LayerTreeHostTest {
public:
LayerTreeHostTestSetNeedsCommit1() : num_commits_(0), num_draws_(0) {}
void BeginTest() override {
PostSetNeedsCommitToMainThread();
PostSetNeedsCommitToMainThread();
}
void DrawLayersOnThread(LayerTreeHostImpl* impl) override {
num_draws_++;
if (!impl->active_tree()->source_frame_number())
EndTest();
}
void CommitCompleteOnThread(LayerTreeHostImpl* impl) override {
num_commits_++;
}
void AfterTest() override {
EXPECT_LE(1, num_commits_);
EXPECT_LE(1, num_draws_);
}
private:
int num_commits_;
int num_draws_;
};
SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestSetNeedsCommit1);
// A SetNeedsCommit should lead to 1 commit. Issuing a second commit after that
// first committed frame draws should lead to another commit.
class LayerTreeHostTestSetNeedsCommit2 : public LayerTreeHostTest {
public:
LayerTreeHostTestSetNeedsCommit2() : num_commits_(0), num_draws_(0) {}
void BeginTest() override { PostSetNeedsCommitToMainThread(); }
void DrawLayersOnThread(LayerTreeHostImpl* impl) override { ++num_draws_; }
void CommitCompleteOnThread(LayerTreeHostImpl* impl) override {
++num_commits_;
switch (num_commits_) {
case 1:
PostSetNeedsCommitToMainThread();
break;
case 2:
EndTest();
break;
default:
NOTREACHED();
}
}
void AfterTest() override {
EXPECT_EQ(2, num_commits_);
EXPECT_LE(1, num_draws_);
}
private:
int num_commits_;
int num_draws_;
};
MULTI_THREAD_TEST_F(LayerTreeHostTestSetNeedsCommit2);
// Verify that we pass property values in PushPropertiesTo.
class LayerTreeHostTestPushPropertiesTo : public LayerTreeHostTest {
protected:
void SetupTree() override {
scoped_refptr<Layer> root = Layer::Create();
root->SetBounds(gfx::Size(10, 10));
layer_tree_host()->SetRootLayer(root);
LayerTreeHostTest::SetupTree();
}
enum Properties {
STARTUP,
BOUNDS,
HIDE_LAYER_AND_SUBTREE,
DRAWS_CONTENT,
DONE,
};
void BeginTest() override {
index_ = STARTUP;
PostSetNeedsCommitToMainThread();
}
void DrawLayersOnThread(LayerTreeHostImpl* impl) override {
VerifyAfterValues(impl->active_tree()->root_layer());
}
void DidCommitAndDrawFrame() override {
SetBeforeValues(layer_tree_host()->root_layer());
VerifyBeforeValues(layer_tree_host()->root_layer());
++index_;
if (index_ == DONE) {
EndTest();
return;
}
SetAfterValues(layer_tree_host()->root_layer());
}
void AfterTest() override {}
void VerifyBeforeValues(Layer* layer) {
EXPECT_EQ(gfx::Size(10, 10).ToString(), layer->bounds().ToString());
EXPECT_FALSE(layer->hide_layer_and_subtree());
EXPECT_FALSE(layer->DrawsContent());
}
void SetBeforeValues(Layer* layer) {
layer->SetBounds(gfx::Size(10, 10));
layer->SetHideLayerAndSubtree(false);
layer->SetIsDrawable(false);
}
void VerifyAfterValues(LayerImpl* layer) {
switch (static_cast<Properties>(index_)) {
case STARTUP:
case DONE:
break;
case BOUNDS:
EXPECT_EQ(gfx::Size(20, 20).ToString(), layer->bounds().ToString());
break;
case HIDE_LAYER_AND_SUBTREE:
EXPECT_TRUE(layer->hide_layer_and_subtree());
break;
case DRAWS_CONTENT:
EXPECT_TRUE(layer->DrawsContent());
break;
}
}
void SetAfterValues(Layer* layer) {
switch (static_cast<Properties>(index_)) {
case STARTUP:
case DONE:
break;
case BOUNDS:
layer->SetBounds(gfx::Size(20, 20));
break;
case HIDE_LAYER_AND_SUBTREE:
layer->SetHideLayerAndSubtree(true);
break;
case DRAWS_CONTENT:
layer->SetIsDrawable(true);
break;
}
}
int index_;
};
SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestPushPropertiesTo);
// 1 setNeedsRedraw after the first commit has completed should lead to 1
// additional draw.
class LayerTreeHostTestSetNeedsRedraw : public LayerTreeHostTest {
public:
LayerTreeHostTestSetNeedsRedraw() : num_commits_(0), num_draws_(0) {}
void BeginTest() override { PostSetNeedsCommitToMainThread(); }
void DrawLayersOnThread(LayerTreeHostImpl* impl) override {
EXPECT_EQ(0, impl->active_tree()->source_frame_number());
if (!num_draws_) {
// Redraw again to verify that the second redraw doesn't commit.
PostSetNeedsRedrawToMainThread();
} else {
EndTest();
}
num_draws_++;
}
void CommitCompleteOnThread(LayerTreeHostImpl* impl) override {
EXPECT_EQ(0, num_draws_);
num_commits_++;
}
void AfterTest() override {
EXPECT_GE(2, num_draws_);
EXPECT_EQ(1, num_commits_);
}
private:
int num_commits_;
int num_draws_;
};
MULTI_THREAD_TEST_F(LayerTreeHostTestSetNeedsRedraw);
// After setNeedsRedrawRect(invalid_rect) the final damage_rect
// must contain invalid_rect.
class LayerTreeHostTestSetNeedsRedrawRect : public LayerTreeHostTest {
public:
LayerTreeHostTestSetNeedsRedrawRect()
: num_draws_(0), bounds_(50, 50), invalid_rect_(10, 10, 20, 20) {}
void BeginTest() override {
if (layer_tree_host()->settings().impl_side_painting)
root_layer_ = FakePictureLayer::Create(&client_);
else
root_layer_ = ContentLayer::Create(&client_);
root_layer_->SetIsDrawable(true);
root_layer_->SetBounds(bounds_);
layer_tree_host()->SetRootLayer(root_layer_);
layer_tree_host()->SetViewportSize(bounds_);
PostSetNeedsCommitToMainThread();
}
DrawResult PrepareToDrawOnThread(LayerTreeHostImpl* host_impl,
LayerTreeHostImpl::FrameData* frame_data,
DrawResult draw_result) override {
EXPECT_EQ(DRAW_SUCCESS, draw_result);
gfx::RectF root_damage_rect;
if (!frame_data->render_passes.empty())
root_damage_rect = frame_data->render_passes.back()->damage_rect;
if (!num_draws_) {
// If this is the first frame, expect full frame damage.
EXPECT_EQ(root_damage_rect, gfx::Rect(bounds_));
} else {
// Check that invalid_rect_ is indeed repainted.
EXPECT_TRUE(root_damage_rect.Contains(invalid_rect_));
}
return draw_result;
}
void DrawLayersOnThread(LayerTreeHostImpl* impl) override {
if (!num_draws_) {
PostSetNeedsRedrawRectToMainThread(invalid_rect_);
} else {
EndTest();
}
num_draws_++;
}
void AfterTest() override { EXPECT_EQ(2, num_draws_); }
private:
int num_draws_;
const gfx::Size bounds_;
const gfx::Rect invalid_rect_;
FakeContentLayerClient client_;
scoped_refptr<Layer> root_layer_;
};
SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestSetNeedsRedrawRect);
class LayerTreeHostTestNoExtraCommitFromInvalidate : public LayerTreeHostTest {
public:
void InitializeSettings(LayerTreeSettings* settings) override {
settings->layer_transforms_should_scale_layer_contents = true;
}
void SetupTree() override {
root_layer_ = Layer::Create();
root_layer_->SetBounds(gfx::Size(10, 20));
if (layer_tree_host()->settings().impl_side_painting)
scaled_layer_ = FakePictureLayer::Create(&client_);
else
scaled_layer_ = FakeContentLayer::Create(&client_);
scaled_layer_->SetBounds(gfx::Size(1, 1));
root_layer_->AddChild(scaled_layer_);
layer_tree_host()->SetRootLayer(root_layer_);
LayerTreeHostTest::SetupTree();
}
void BeginTest() override { PostSetNeedsCommitToMainThread(); }
void DrawLayersOnThread(LayerTreeHostImpl* host_impl) override {
if (host_impl->active_tree()->source_frame_number() == 1)
EndTest();
}
void DidCommit() override {
switch (layer_tree_host()->source_frame_number()) {
case 1:
// SetBounds grows the layer and exposes new content.
if (layer_tree_host()->settings().impl_side_painting) {
scaled_layer_->SetBounds(gfx::Size(4, 4));
} else {
// Changing the device scale factor causes a commit. It also changes
// the content bounds of |scaled_layer_|, which should not generate
// a second commit as a result.
layer_tree_host()->SetDeviceScaleFactor(4.f);
}
break;
default:
// No extra commits.
EXPECT_EQ(2, layer_tree_host()->source_frame_number());
}
}
void AfterTest() override {
EXPECT_EQ(gfx::Size(4, 4).ToString(),
scaled_layer_->content_bounds().ToString());
}
private:
FakeContentLayerClient client_;
scoped_refptr<Layer> root_layer_;
scoped_refptr<Layer> scaled_layer_;
};
SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestNoExtraCommitFromInvalidate);
class LayerTreeHostTestNoExtraCommitFromScrollbarInvalidate
: public LayerTreeHostTest {
public:
void InitializeSettings(LayerTreeSettings* settings) override {
settings->layer_transforms_should_scale_layer_contents = true;
}
void SetupTree() override {
root_layer_ = Layer::Create();
root_layer_->SetBounds(gfx::Size(10, 20));
bool paint_scrollbar = true;
bool has_thumb = false;
scrollbar_ = FakePaintedScrollbarLayer::Create(
paint_scrollbar, has_thumb, root_layer_->id());
scrollbar_->SetPosition(gfx::Point(0, 10));
scrollbar_->SetBounds(gfx::Size(10, 10));
root_layer_->AddChild(scrollbar_);
layer_tree_host()->SetRootLayer(root_layer_);
LayerTreeHostTest::SetupTree();
}
void BeginTest() override { PostSetNeedsCommitToMainThread(); }
void DrawLayersOnThread(LayerTreeHostImpl* host_impl) override {
if (host_impl->active_tree()->source_frame_number() == 1)
EndTest();
}
void DidCommit() override {
switch (layer_tree_host()->source_frame_number()) {
case 1:
// Changing the device scale factor causes a commit. It also changes
// the content bounds of |scrollbar_|, which should not generate
// a second commit as a result.
layer_tree_host()->SetDeviceScaleFactor(4.f);
break;
default:
// No extra commits.
EXPECT_EQ(2, layer_tree_host()->source_frame_number());
}
}
void AfterTest() override {
EXPECT_EQ(gfx::Size(40, 40).ToString(),
scrollbar_->content_bounds().ToString());
}
private:
FakeContentLayerClient client_;
scoped_refptr<Layer> root_layer_;
scoped_refptr<FakePaintedScrollbarLayer> scrollbar_;
};
SINGLE_AND_MULTI_THREAD_TEST_F(
LayerTreeHostTestNoExtraCommitFromScrollbarInvalidate);
class LayerTreeHostTestSetNextCommitForcesRedraw : public LayerTreeHostTest {
public:
LayerTreeHostTestSetNextCommitForcesRedraw()
: num_draws_(0), bounds_(50, 50), invalid_rect_(10, 10, 20, 20) {}
void BeginTest() override {
if (layer_tree_host()->settings().impl_side_painting)
root_layer_ = FakePictureLayer::Create(&client_);
else
root_layer_ = ContentLayer::Create(&client_);
root_layer_->SetIsDrawable(true);
root_layer_->SetBounds(bounds_);
layer_tree_host()->SetRootLayer(root_layer_);
layer_tree_host()->SetViewportSize(bounds_);
PostSetNeedsCommitToMainThread();
}
void CommitCompleteOnThread(LayerTreeHostImpl* host_impl) override {
if (num_draws_ == 3 && host_impl->settings().impl_side_painting)
host_impl->SetNeedsRedrawRect(invalid_rect_);
}
DrawResult PrepareToDrawOnThread(LayerTreeHostImpl* host_impl,
LayerTreeHostImpl::FrameData* frame_data,
DrawResult draw_result) override {
EXPECT_EQ(DRAW_SUCCESS, draw_result);
gfx::RectF root_damage_rect;
if (!frame_data->render_passes.empty())
root_damage_rect = frame_data->render_passes.back()->damage_rect;
switch (num_draws_) {
case 0:
EXPECT_EQ(gfx::Rect(bounds_), root_damage_rect);
break;
case 1:
case 2:
EXPECT_EQ(gfx::Rect(0, 0, 0, 0), root_damage_rect);
break;
case 3:
EXPECT_EQ(invalid_rect_, root_damage_rect);
break;
case 4:
EXPECT_EQ(gfx::Rect(bounds_), root_damage_rect);
break;
default:
NOTREACHED();
}
return draw_result;
}
void DrawLayersOnThread(LayerTreeHostImpl* host_impl) override {
switch (num_draws_) {
case 0:
case 1:
// Cycle through a couple of empty commits to ensure we're observing the
// right behavior
PostSetNeedsCommitToMainThread();
break;
case 2:
// Should force full frame damage on the next commit
PostSetNextCommitForcesRedrawToMainThread();
PostSetNeedsCommitToMainThread();
if (host_impl->settings().impl_side_painting)
host_impl->BlockNotifyReadyToActivateForTesting(true);
else
num_draws_++;
break;
case 3:
host_impl->BlockNotifyReadyToActivateForTesting(false);
break;
default:
EndTest();
break;
}
num_draws_++;
}
void AfterTest() override { EXPECT_EQ(5, num_draws_); }
private:
int num_draws_;
const gfx::Size bounds_;
const gfx::Rect invalid_rect_;
FakeContentLayerClient client_;
scoped_refptr<Layer> root_layer_;
};
SINGLE_AND_MULTI_THREAD_BLOCKNOTIFY_TEST_F(
LayerTreeHostTestSetNextCommitForcesRedraw);
// Tests that if a layer is not drawn because of some reason in the parent then
// its damage is preserved until the next time it is drawn.
class LayerTreeHostTestUndrawnLayersDamageLater : public LayerTreeHostTest {
public:
LayerTreeHostTestUndrawnLayersDamageLater() {}
void SetupTree() override {
if (layer_tree_host()->settings().impl_side_painting)
root_layer_ = FakePictureLayer::Create(&client_);
else
root_layer_ = ContentLayer::Create(&client_);
root_layer_->SetIsDrawable(true);
root_layer_->SetBounds(gfx::Size(50, 50));
layer_tree_host()->SetRootLayer(root_layer_);
// The initially transparent layer has a larger child layer, which is
// not initially drawn because of the this (parent) layer.
if (layer_tree_host()->settings().impl_side_painting)
parent_layer_ = FakePictureLayer::Create(&client_);
else
parent_layer_ = FakeContentLayer::Create(&client_);
parent_layer_->SetBounds(gfx::Size(15, 15));
parent_layer_->SetOpacity(0.0f);
root_layer_->AddChild(parent_layer_);
if (layer_tree_host()->settings().impl_side_painting)
child_layer_ = FakePictureLayer::Create(&client_);
else
child_layer_ = FakeContentLayer::Create(&client_);
child_layer_->SetBounds(gfx::Size(25, 25));
parent_layer_->AddChild(child_layer_);
LayerTreeHostTest::SetupTree();
}
void BeginTest() override { PostSetNeedsCommitToMainThread(); }
DrawResult PrepareToDrawOnThread(LayerTreeHostImpl* host_impl,
LayerTreeHostImpl::FrameData* frame_data,
DrawResult draw_result) override {
EXPECT_EQ(DRAW_SUCCESS, draw_result);
gfx::RectF root_damage_rect;
if (!frame_data->render_passes.empty())
root_damage_rect = frame_data->render_passes.back()->damage_rect;
// The first time, the whole view needs be drawn.
// Afterwards, just the opacity of surface_layer1 is changed a few times,
// and each damage should be the bounding box of it and its child. If this
// was working improperly, the damage might not include its childs bounding
// box.
switch (host_impl->active_tree()->source_frame_number()) {
case 0:
EXPECT_EQ(gfx::Rect(root_layer_->bounds()), root_damage_rect);
break;
case 1:
case 2:
case 3:
EXPECT_EQ(gfx::Rect(child_layer_->bounds()), root_damage_rect);
break;
default:
NOTREACHED();
}
return draw_result;
}
void DidCommitAndDrawFrame() override {
switch (layer_tree_host()->source_frame_number()) {
case 1:
// Test not owning the surface.
parent_layer_->SetOpacity(1.0f);
break;
case 2:
parent_layer_->SetOpacity(0.0f);
break;
case 3:
// Test owning the surface.
parent_layer_->SetOpacity(0.5f);
parent_layer_->SetForceRenderSurface(true);
break;
case 4:
EndTest();
break;
default:
NOTREACHED();
}
}
void AfterTest() override {}
private:
FakeContentLayerClient client_;
scoped_refptr<Layer> root_layer_;
scoped_refptr<Layer> parent_layer_;
scoped_refptr<Layer> child_layer_;
};
SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestUndrawnLayersDamageLater);
// Tests that if a layer is not drawn because of some reason in the parent,
// causing its content bounds to not be computed, then when it is later drawn,
// its content bounds get pushed.
class LayerTreeHostTestUndrawnLayersPushContentBoundsLater
: public LayerTreeHostTest {
public:
LayerTreeHostTestUndrawnLayersPushContentBoundsLater()
: root_layer_(Layer::Create()) {}
void SetupTree() override {
root_layer_->SetIsDrawable(true);
root_layer_->SetBounds(gfx::Size(20, 20));
layer_tree_host()->SetRootLayer(root_layer_);
parent_layer_ = Layer::Create();
parent_layer_->SetBounds(gfx::Size(20, 20));
parent_layer_->SetOpacity(0.0f);
root_layer_->AddChild(parent_layer_);
child_layer_ = Layer::Create();
child_layer_->SetBounds(gfx::Size(15, 15));
parent_layer_->AddChild(child_layer_);
LayerTreeHostTest::SetupTree();
}
void BeginTest() override { PostSetNeedsCommitToMainThread(); }
void DidActivateTreeOnThread(LayerTreeHostImpl* host_impl) override {
LayerImpl* root = host_impl->active_tree()->root_layer();
LayerImpl* parent = root->children()[0];
LayerImpl* child = parent->children()[0];
switch (host_impl->active_tree()->source_frame_number()) {
case 0:
EXPECT_EQ(0.f, parent->opacity());
EXPECT_EQ(gfx::SizeF(), child->content_bounds());
break;
case 1:
EXPECT_EQ(1.f, parent->opacity());
EXPECT_EQ(gfx::SizeF(15.f, 15.f), child->content_bounds());
EndTest();
break;
default:
NOTREACHED();
}
}
void DidCommit() override {
switch (layer_tree_host()->source_frame_number()) {
case 1:
parent_layer_->SetOpacity(1.0f);
break;
case 2:
break;
default:
NOTREACHED();
}
}
void AfterTest() override {}
private:
scoped_refptr<Layer> root_layer_;
scoped_refptr<Layer> parent_layer_;
scoped_refptr<Layer> child_layer_;
};
SINGLE_AND_MULTI_THREAD_TEST_F(
LayerTreeHostTestUndrawnLayersPushContentBoundsLater);
// This test verifies that properties on the layer tree host are commited
// to the impl side.
class LayerTreeHostTestCommit : public LayerTreeHostTest {
public:
LayerTreeHostTestCommit() {}
void BeginTest() override {
layer_tree_host()->SetViewportSize(gfx::Size(20, 20));
layer_tree_host()->set_background_color(SK_ColorGRAY);
PostSetNeedsCommitToMainThread();
}
void DidActivateTreeOnThread(LayerTreeHostImpl* impl) override {
EXPECT_EQ(gfx::Size(20, 20), impl->DrawViewportSize());
EXPECT_EQ(SK_ColorGRAY, impl->active_tree()->background_color());
EndTest();
}
void AfterTest() override {}
};
MULTI_THREAD_TEST_F(LayerTreeHostTestCommit);
// This test verifies that LayerTreeHostImpl's current frame time gets
// updated in consecutive frames when it doesn't draw due to tree
// activation failure.
class LayerTreeHostTestFrameTimeUpdatesAfterActivationFails
: public LayerTreeHostTest {
public:
LayerTreeHostTestFrameTimeUpdatesAfterActivationFails()
: frame_count_with_pending_tree_(0) {}
void BeginTest() override {
layer_tree_host()->SetViewportSize(gfx::Size(20, 20));
layer_tree_host()->set_background_color(SK_ColorGRAY);
PostSetNeedsCommitToMainThread();
}
void BeginCommitOnThread(LayerTreeHostImpl* impl) override {
EXPECT_EQ(frame_count_with_pending_tree_, 0);
if (impl->settings().impl_side_painting)
impl->BlockNotifyReadyToActivateForTesting(true);
}
void WillBeginImplFrameOnThread(LayerTreeHostImpl* impl,
const BeginFrameArgs& args) override {
if (impl->pending_tree())
frame_count_with_pending_tree_++;
if (frame_count_with_pending_tree_ == 1) {
EXPECT_EQ(first_frame_time_.ToInternalValue(), 0);
first_frame_time_ = impl->CurrentBeginFrameArgs().frame_time;
} else if (frame_count_with_pending_tree_ == 2 &&
impl->settings().impl_side_painting) {
impl->BlockNotifyReadyToActivateForTesting(false);
}
}
void DrawLayersOnThread(LayerTreeHostImpl* impl) override {
if (frame_count_with_pending_tree_ > 1) {
EXPECT_NE(first_frame_time_.ToInternalValue(), 0);
EXPECT_NE(first_frame_time_.ToInternalValue(),
impl->CurrentBeginFrameArgs().frame_time.ToInternalValue());
EndTest();
return;
}
EXPECT_FALSE(impl->settings().impl_side_painting);
EndTest();
}
void DidActivateTreeOnThread(LayerTreeHostImpl* impl) override {
if (impl->settings().impl_side_painting)
EXPECT_NE(frame_count_with_pending_tree_, 1);
}
void AfterTest() override {}
private:
int frame_count_with_pending_tree_;
base::TimeTicks first_frame_time_;
};
SINGLE_AND_MULTI_THREAD_BLOCKNOTIFY_TEST_F(
LayerTreeHostTestFrameTimeUpdatesAfterActivationFails);
// This test verifies that LayerTreeHostImpl's current frame time gets
// updated in consecutive frames when it draws in each frame.
class LayerTreeHostTestFrameTimeUpdatesAfterDraw : public LayerTreeHostTest {
public:
LayerTreeHostTestFrameTimeUpdatesAfterDraw() : frame_(0) {}
void BeginTest() override {
layer_tree_host()->SetViewportSize(gfx::Size(20, 20));
layer_tree_host()->set_background_color(SK_ColorGRAY);
PostSetNeedsCommitToMainThread();
}
void DrawLayersOnThread(LayerTreeHostImpl* impl) override {
frame_++;
if (frame_ == 1) {
first_frame_time_ = impl->CurrentBeginFrameArgs().frame_time;
impl->SetNeedsRedraw();
// Since we might use a low-resolution clock on Windows, we need to
// make sure that the clock has incremented past first_frame_time_.
while (first_frame_time_ == gfx::FrameTime::Now()) {
}
return;
}
EXPECT_NE(first_frame_time_, impl->CurrentBeginFrameArgs().frame_time);
EndTest();
}
void CommitCompleteOnThread(LayerTreeHostImpl* host_impl) override {
// Ensure there isn't a commit between the two draws, to ensure that a
// commit isn't required for updating the current frame time. We can
// only check for this in the multi-threaded case, since in the single-
// threaded case there will always be a commit between consecutive draws.
if (HasImplThread())
EXPECT_EQ(0, frame_);
}
void AfterTest() override {}
private:
int frame_;
base::TimeTicks first_frame_time_;
};
SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestFrameTimeUpdatesAfterDraw);
// Verifies that StartPageScaleAnimation events propagate correctly
// from LayerTreeHost to LayerTreeHostImpl in the MT compositor.
class LayerTreeHostTestStartPageScaleAnimation : public LayerTreeHostTest {
public:
LayerTreeHostTestStartPageScaleAnimation() {}
void SetupTree() override {
LayerTreeHostTest::SetupTree();
if (layer_tree_host()->settings().impl_side_painting) {
scoped_refptr<FakePictureLayer> layer =
FakePictureLayer::Create(&client_);
layer->set_always_update_resources(true);
scroll_layer_ = layer;
} else {
scroll_layer_ = FakeContentLayer::Create(&client_);
}
Layer* root_layer = layer_tree_host()->root_layer();
scroll_layer_->SetScrollClipLayerId(root_layer->id());
scroll_layer_->SetIsContainerForFixedPositionLayers(true);
scroll_layer_->SetBounds(gfx::Size(2 * root_layer->bounds().width(),
2 * root_layer->bounds().height()));
scroll_layer_->SetScrollOffset(gfx::ScrollOffset());
layer_tree_host()->root_layer()->AddChild(scroll_layer_);
// This test requires the page_scale and inner viewport layers to be
// identified.
layer_tree_host()->RegisterViewportLayers(NULL, root_layer,
scroll_layer_.get(), NULL);
layer_tree_host()->SetPageScaleFactorAndLimits(1.f, 0.5f, 2.f);
}
void BeginTest() override { PostSetNeedsCommitToMainThread(); }
void ApplyViewportDeltas(const gfx::Vector2d& scroll_delta,
float scale,
float) override {
gfx::ScrollOffset offset = scroll_layer_->scroll_offset();
scroll_layer_->SetScrollOffset(ScrollOffsetWithDelta(offset,
scroll_delta));
layer_tree_host()->SetPageScaleFactorAndLimits(scale, 0.5f, 2.f);
}
void DidActivateTreeOnThread(LayerTreeHostImpl* impl) override {
// We get one commit before the first draw, and the animation doesn't happen
// until the second draw.
switch (impl->active_tree()->source_frame_number()) {
case 0:
EXPECT_EQ(1.f, impl->active_tree()->current_page_scale_factor());
// We'll start an animation when we get back to the main thread.
break;
case 1:
EXPECT_EQ(1.f, impl->active_tree()->current_page_scale_factor());
break;
case 2:
EXPECT_EQ(1.25f, impl->active_tree()->current_page_scale_factor());
EndTest();
break;
default:
NOTREACHED();
}
}
void DidCommitAndDrawFrame() override {
switch (layer_tree_host()->source_frame_number()) {
case 1:
layer_tree_host()->StartPageScaleAnimation(
gfx::Vector2d(), false, 1.25f, base::TimeDelta());
break;
}
}
void AfterTest() override {}
FakeContentLayerClient client_;
scoped_refptr<Layer> scroll_layer_;
};
MULTI_THREAD_TEST_F(LayerTreeHostTestStartPageScaleAnimation);
class LayerTreeHostTestSetVisible : public LayerTreeHostTest {
public:
LayerTreeHostTestSetVisible() : num_draws_(0) {}
void BeginTest() override {
PostSetNeedsCommitToMainThread();
PostSetVisibleToMainThread(false);
// This is suppressed while we're invisible.
PostSetNeedsRedrawToMainThread();
// Triggers the redraw.
PostSetVisibleToMainThread(true);
}
void DrawLayersOnThread(LayerTreeHostImpl* impl) override {
EXPECT_TRUE(impl->visible());
++num_draws_;
EndTest();
}
void AfterTest() override { EXPECT_EQ(1, num_draws_); }
private:
int num_draws_;
};
MULTI_THREAD_TEST_F(LayerTreeHostTestSetVisible);
class TestOpacityChangeLayerDelegate : public ContentLayerClient {
public:
TestOpacityChangeLayerDelegate() : test_layer_(0) {}
void SetTestLayer(Layer* test_layer) { test_layer_ = test_layer; }
void PaintContents(
SkCanvas* canvas,
const gfx::Rect& clip,
ContentLayerClient::GraphicsContextStatus gc_status) override {
// Set layer opacity to 0.
if (test_layer_)
test_layer_->SetOpacity(0.f);
}
scoped_refptr<DisplayItemList> PaintContentsToDisplayList(
const gfx::Rect& clip,
GraphicsContextStatus gc_status) override {
NOTIMPLEMENTED();
return DisplayItemList::Create();
}
bool FillsBoundsCompletely() const override { return false; }
private:
Layer* test_layer_;
};
class ContentLayerWithUpdateTracking : public ContentLayer {
public:
static scoped_refptr<ContentLayerWithUpdateTracking> Create(
ContentLayerClient* client) {
return make_scoped_refptr(new ContentLayerWithUpdateTracking(client));
}
int PaintContentsCount() { return paint_contents_count_; }
void ResetPaintContentsCount() { paint_contents_count_ = 0; }
bool Update(ResourceUpdateQueue* queue,
const OcclusionTracker<Layer>* occlusion) override {
bool updated = ContentLayer::Update(queue, occlusion);
paint_contents_count_++;
return updated;
}
private:
explicit ContentLayerWithUpdateTracking(ContentLayerClient* client)
: ContentLayer(client), paint_contents_count_(0) {
SetBounds(gfx::Size(10, 10));
SetIsDrawable(true);
}
~ContentLayerWithUpdateTracking() override {}
int paint_contents_count_;
};
// Layer opacity change during paint should not prevent compositor resources
// from being updated during commit.
class LayerTreeHostTestOpacityChange : public LayerTreeHostTest {
public:
LayerTreeHostTestOpacityChange() : test_opacity_change_delegate_() {}
void BeginTest() override {
if (layer_tree_host()->settings().impl_side_painting) {
update_check_picture_layer_ =
FakePictureLayer::Create(&test_opacity_change_delegate_);
test_opacity_change_delegate_.SetTestLayer(
update_check_picture_layer_.get());
is_impl_paint_ = true;
} else {
update_check_content_layer_ = ContentLayerWithUpdateTracking::Create(
&test_opacity_change_delegate_);
test_opacity_change_delegate_.SetTestLayer(
update_check_content_layer_.get());
is_impl_paint_ = false;
}
layer_tree_host()->SetViewportSize(gfx::Size(10, 10));
if (layer_tree_host()->settings().impl_side_painting)
layer_tree_host()->root_layer()->AddChild(update_check_picture_layer_);
else
layer_tree_host()->root_layer()->AddChild(update_check_content_layer_);
PostSetNeedsCommitToMainThread();
}
void CommitCompleteOnThread(LayerTreeHostImpl* impl) override { EndTest(); }
void AfterTest() override {
// Update() should have been called once.
if (is_impl_paint_)
EXPECT_EQ(1u, update_check_picture_layer_->update_count());
else
EXPECT_EQ(1, update_check_content_layer_->PaintContentsCount());
}
private:
TestOpacityChangeLayerDelegate test_opacity_change_delegate_;
scoped_refptr<ContentLayerWithUpdateTracking> update_check_content_layer_;
scoped_refptr<FakePictureLayer> update_check_picture_layer_;
bool is_impl_paint_;
};
MULTI_THREAD_TEST_F(LayerTreeHostTestOpacityChange);
class LayerTreeHostTestDeviceScaleFactorScalesViewportAndLayers
: public LayerTreeHostTest {
public:
LayerTreeHostTestDeviceScaleFactorScalesViewportAndLayers() {}
void InitializeSettings(LayerTreeSettings* settings) override {
// PictureLayer can only be used with impl side painting enabled.
settings->impl_side_painting = true;
}
void BeginTest() override {
client_.set_fill_with_nonsolid_color(true);
root_layer_ = FakePictureLayer::Create(&client_);
child_layer_ = FakePictureLayer::Create(&client_);
layer_tree_host()->SetViewportSize(gfx::Size(60, 60));
layer_tree_host()->SetDeviceScaleFactor(1.5);
EXPECT_EQ(gfx::Size(60, 60), layer_tree_host()->device_viewport_size());
root_layer_->AddChild(child_layer_);
root_layer_->SetIsDrawable(true);
root_layer_->SetBounds(gfx::Size(30, 30));
child_layer_->SetIsDrawable(true);
child_layer_->SetPosition(gfx::Point(2, 2));
child_layer_->SetBounds(gfx::Size(10, 10));
layer_tree_host()->SetRootLayer(root_layer_);
PostSetNeedsCommitToMainThread();
}
void DidActivateTreeOnThread(LayerTreeHostImpl* impl) override {
// Should only do one commit.
EXPECT_EQ(0, impl->active_tree()->source_frame_number());
// Device scale factor should come over to impl.
EXPECT_NEAR(impl->device_scale_factor(), 1.5f, 0.00001f);
// Both layers are on impl.
ASSERT_EQ(1u, impl->active_tree()->root_layer()->children().size());
// Device viewport is scaled.
EXPECT_EQ(gfx::Size(60, 60), impl->DrawViewportSize());
FakePictureLayerImpl* root =
static_cast<FakePictureLayerImpl*>(impl->active_tree()->root_layer());
FakePictureLayerImpl* child = static_cast<FakePictureLayerImpl*>(
impl->active_tree()->root_layer()->children()[0]);
// Positions remain in layout pixels.
EXPECT_EQ(gfx::Point(0, 0), root->position());
EXPECT_EQ(gfx::Point(2, 2), child->position());
// Compute all the layer transforms for the frame.
LayerTreeHostImpl::FrameData frame_data;
impl->PrepareToDraw(&frame_data);
impl->DidDrawAllLayers(frame_data);
const LayerImplList& render_surface_layer_list =
*frame_data.render_surface_layer_list;
// Both layers should be drawing into the root render surface.
ASSERT_EQ(1u, render_surface_layer_list.size());
ASSERT_EQ(root->render_surface(),
render_surface_layer_list[0]->render_surface());
ASSERT_EQ(2u, root->render_surface()->layer_list().size());
// The root render surface is the size of the viewport.
EXPECT_EQ(gfx::Rect(0, 0, 60, 60), root->render_surface()->content_rect());
// The max tiling scale of the child should be scaled.
EXPECT_FLOAT_EQ(1.5f, child->MaximumTilingContentsScale());
gfx::Transform scale_transform;
scale_transform.Scale(impl->device_scale_factor(),
impl->device_scale_factor());
// The root layer is scaled by 2x.
gfx::Transform root_screen_space_transform = scale_transform;
gfx::Transform root_draw_transform = scale_transform;
EXPECT_EQ(root_draw_transform, root->draw_transform());
EXPECT_EQ(root_screen_space_transform, root->screen_space_transform());
// The child is at position 2,2, which is transformed to 3,3 after the scale
gfx::Transform child_transform;
child_transform.Translate(3.f, 3.f);
child_transform.Scale(child->MaximumTilingContentsScale(),
child->MaximumTilingContentsScale());
EXPECT_TRANSFORMATION_MATRIX_EQ(child_transform, child->draw_transform());
EXPECT_TRANSFORMATION_MATRIX_EQ(child_transform,
child->screen_space_transform());
EndTest();
}
void AfterTest() override {}
private:
FakeContentLayerClient client_;
scoped_refptr<FakePictureLayer> root_layer_;
scoped_refptr<FakePictureLayer> child_layer_;
};
MULTI_THREAD_TEST_F(LayerTreeHostTestDeviceScaleFactorScalesViewportAndLayers);
// TODO(sohanjg) : Remove it once impl-side painting ships everywhere.
// Verify atomicity of commits and reuse of textures.
class LayerTreeHostTestDirectRendererAtomicCommit : public LayerTreeHostTest {
public:
void InitializeSettings(LayerTreeSettings* settings) override {
settings->renderer_settings.texture_id_allocation_chunk_size = 1;
// Make sure partial texture updates are turned off.
settings->max_partial_texture_updates = 0;
// Linear fade animator prevents scrollbars from drawing immediately.
settings->scrollbar_animator = LayerTreeSettings::NoAnimator;
}
void SetupTree() override {
layer_ = FakeContentLayer::Create(&client_);
layer_->SetBounds(gfx::Size(10, 20));
bool paint_scrollbar = true;
bool has_thumb = false;
scrollbar_ = FakePaintedScrollbarLayer::Create(
paint_scrollbar, has_thumb, layer_->id());
scrollbar_->SetPosition(gfx::Point(0, 10));
scrollbar_->SetBounds(gfx::Size(10, 10));
layer_->AddChild(scrollbar_);
layer_tree_host()->SetRootLayer(layer_);
LayerTreeHostTest::SetupTree();
}
void BeginTest() override {
drew_frame_ = -1;
PostSetNeedsCommitToMainThread();
}
void DidActivateTreeOnThread(LayerTreeHostImpl* impl) override {
ASSERT_EQ(0u, layer_tree_host()->settings().max_partial_texture_updates);
TestWebGraphicsContext3D* context = TestContext();
switch (impl->active_tree()->source_frame_number()) {
case 0:
// Number of textures should be one for each layer
ASSERT_EQ(2u, context->NumTextures());
// Number of textures used for commit should be one for each layer.
EXPECT_EQ(2u, context->NumUsedTextures());
// Verify that used texture is correct.
EXPECT_TRUE(context->UsedTexture(context->TextureAt(0)));
EXPECT_TRUE(context->UsedTexture(context->TextureAt(1)));
context->ResetUsedTextures();
break;
case 1:
// Number of textures should be one for scrollbar layer since it was
// requested and deleted on the impl-thread, and double for the content
// layer since its first texture is used by impl thread and cannot by
// used for update.
ASSERT_EQ(3u, context->NumTextures());
// Number of textures used for commit should be one for each layer.
EXPECT_EQ(2u, context->NumUsedTextures());
// First textures should not have been used.
EXPECT_FALSE(context->UsedTexture(context->TextureAt(0)));
EXPECT_TRUE(context->UsedTexture(context->TextureAt(1)));
// New textures should have been used.
EXPECT_TRUE(context->UsedTexture(context->TextureAt(2)));
context->ResetUsedTextures();
break;
case 2:
EndTest();
break;
default:
NOTREACHED();
break;
}
}
void DrawLayersOnThread(LayerTreeHostImpl* impl) override {
TestWebGraphicsContext3D* context = TestContext();
if (drew_frame_ == impl->active_tree()->source_frame_number()) {
EXPECT_EQ(0u, context->NumUsedTextures()) << "For frame " << drew_frame_;
return;
}
drew_frame_ = impl->active_tree()->source_frame_number();
// We draw/ship one texture each frame for each layer.
EXPECT_EQ(2u, context->NumUsedTextures());
context->ResetUsedTextures();
if (!TestEnded())
PostSetNeedsCommitToMainThread();
}
void Layout() override {
layer_->SetNeedsDisplay();
scrollbar_->SetNeedsDisplay();
}
void AfterTest() override {}
protected:
FakeContentLayerClient client_;
scoped_refptr<FakeContentLayer> layer_;
scoped_refptr<FakePaintedScrollbarLayer> scrollbar_;
int drew_frame_;
};
MULTI_THREAD_DIRECT_RENDERER_NOIMPL_TEST_F(
LayerTreeHostTestDirectRendererAtomicCommit);
// TODO(sohanjg) : Remove it once impl-side painting ships everywhere.
class LayerTreeHostTestDelegatingRendererAtomicCommit
: public LayerTreeHostTestDirectRendererAtomicCommit {
public:
void DidActivateTreeOnThread(LayerTreeHostImpl* impl) override {
ASSERT_EQ(0u, layer_tree_host()->settings().max_partial_texture_updates);
TestWebGraphicsContext3D* context = TestContext();
switch (impl->active_tree()->source_frame_number()) {
case 0:
// Number of textures should be one for each layer
ASSERT_EQ(2u, context->NumTextures());
// Number of textures used for commit should be one for each layer.
EXPECT_EQ(2u, context->NumUsedTextures());
// Verify that used texture is correct.
EXPECT_TRUE(context->UsedTexture(context->TextureAt(0)));
EXPECT_TRUE(context->UsedTexture(context->TextureAt(1)));
context->ResetUsedTextures();
break;
case 1:
// Number of textures should be doubled as the first context layer
// texture is being used by the impl-thread and cannot be used for
// update. The scrollbar behavior is different direct renderer because
// UI resource deletion with delegating renderer occurs after tree
// activation.
ASSERT_EQ(4u, context->NumTextures());
// Number of textures used for commit should still be
// one for each layer.
EXPECT_EQ(2u, context->NumUsedTextures());
// First textures should not have been used.
EXPECT_FALSE(context->UsedTexture(context->TextureAt(0)));
EXPECT_FALSE(context->UsedTexture(context->TextureAt(1)));
// New textures should have been used.
EXPECT_TRUE(context->UsedTexture(context->TextureAt(2)));
EXPECT_TRUE(context->UsedTexture(context->TextureAt(3)));
context->ResetUsedTextures();
break;
case 2:
EndTest();
break;
default:
NOTREACHED();
break;
}
}
};
MULTI_THREAD_DELEGATING_RENDERER_NOIMPL_TEST_F(
LayerTreeHostTestDelegatingRendererAtomicCommit);
static void SetLayerPropertiesForTesting(Layer* layer,
Layer* parent,
const gfx::Transform& transform,
const gfx::Point3F& transform_origin,
const gfx::PointF& position,
const gfx::Size& bounds,
bool opaque) {
layer->RemoveAllChildren();
if (parent)
parent->AddChild(layer);
layer->SetTransform(transform);
layer->SetTransformOrigin(transform_origin);
layer->SetPosition(position);
layer->SetBounds(bounds);
layer->SetContentsOpaque(opaque);
}
// TODO(sohanjg) : Remove it once impl-side painting ships everywhere.
class LayerTreeHostTestAtomicCommitWithPartialUpdate
: public LayerTreeHostTest {
public:
void InitializeSettings(LayerTreeSettings* settings) override {
settings->renderer_settings.texture_id_allocation_chunk_size = 1;
// Allow one partial texture update.
settings->max_partial_texture_updates = 1;
// No partial updates when impl side painting is enabled.
settings->impl_side_painting = false;
}
void SetupTree() override {
parent_ = FakeContentLayer::Create(&client_);
parent_->SetBounds(gfx::Size(10, 20));
child_ = FakeContentLayer::Create(&client_);
child_->SetPosition(gfx::Point(0, 10));
child_->SetBounds(gfx::Size(3, 10));
parent_->AddChild(child_);
layer_tree_host()->SetRootLayer(parent_);
LayerTreeHostTest::SetupTree();
}
void BeginTest() override { PostSetNeedsCommitToMainThread(); }
void DidCommitAndDrawFrame() override {
switch (layer_tree_host()->source_frame_number()) {
case 1:
parent_->SetNeedsDisplay();
child_->SetNeedsDisplay();
break;
case 2:
// Damage part of layers.
parent_->SetNeedsDisplayRect(gfx::Rect(5, 5));
child_->SetNeedsDisplayRect(gfx::Rect(5, 5));
break;
case 3:
child_->SetNeedsDisplay();
layer_tree_host()->SetViewportSize(gfx::Size(10, 10));
break;
case 4:
layer_tree_host()->SetViewportSize(gfx::Size(10, 20));
break;
case 5:
EndTest();
break;
default:
NOTREACHED() << layer_tree_host()->source_frame_number();
break;
}
}
void CommitCompleteOnThread(LayerTreeHostImpl* impl) override {
ASSERT_EQ(1u, layer_tree_host()->settings().max_partial_texture_updates);
TestWebGraphicsContext3D* context = TestContext();
switch (impl->active_tree()->source_frame_number()) {
case 0:
// Number of textures should be one for each layer.
ASSERT_EQ(2u, context->NumTextures());
// Number of textures used for commit should be one for each layer.
EXPECT_EQ(2u, context->NumUsedTextures());
// Verify that used textures are correct.
EXPECT_TRUE(context->UsedTexture(context->TextureAt(0)));
EXPECT_TRUE(context->UsedTexture(context->TextureAt(1)));
context->ResetUsedTextures();
break;
case 1:
if (HasImplThread()) {
// Number of textures should be two for each content layer.
ASSERT_EQ(4u, context->NumTextures());
} else {
// In single thread we can always do partial updates, so the limit has
// no effect.
ASSERT_EQ(2u, context->NumTextures());
}
// Number of textures used for commit should be one for each content
// layer.
EXPECT_EQ(2u, context->NumUsedTextures());
if (HasImplThread()) {
// First content textures should not have been used.
EXPECT_FALSE(context->UsedTexture(context->TextureAt(0)));
EXPECT_FALSE(context->UsedTexture(context->TextureAt(1)));
// New textures should have been used.
EXPECT_TRUE(context->UsedTexture(context->TextureAt(2)));
EXPECT_TRUE(context->UsedTexture(context->TextureAt(3)));
} else {
// In single thread we can always do partial updates, so the limit has
// no effect.
EXPECT_TRUE(context->UsedTexture(context->TextureAt(0)));
EXPECT_TRUE(context->UsedTexture(context->TextureAt(1)));
}
context->ResetUsedTextures();
break;
case 2:
if (HasImplThread()) {
// Number of textures should be two for each content layer.
ASSERT_EQ(4u, context->NumTextures());
} else {
// In single thread we can always do partial updates, so the limit has
// no effect.
ASSERT_EQ(2u, context->NumTextures());
}
// Number of textures used for commit should be one for each content
// layer.
EXPECT_EQ(2u, context->NumUsedTextures());
if (HasImplThread()) {
// One content layer does a partial update also.
EXPECT_TRUE(context->UsedTexture(context->TextureAt(2)));
EXPECT_FALSE(context->UsedTexture(context->TextureAt(3)));
} else {
// In single thread we can always do partial updates, so the limit has
// no effect.
EXPECT_TRUE(context->UsedTexture(context->TextureAt(0)));
EXPECT_TRUE(context->UsedTexture(context->TextureAt(1)));
}
context->ResetUsedTextures();
break;
case 3:
// No textures should be used for commit.
EXPECT_EQ(0u, context->NumUsedTextures());
context->ResetUsedTextures();
break;
case 4:
// Number of textures used for commit should be one, for the
// content layer.
EXPECT_EQ(1u, context->NumUsedTextures());
context->ResetUsedTextures();
break;
default:
NOTREACHED();
break;
}
}
void DrawLayersOnThread(LayerTreeHostImpl* impl) override {
EXPECT_LT(impl->active_tree()->source_frame_number(), 5);
TestWebGraphicsContext3D* context = TestContext();
// Number of textures used for drawing should one per layer except for
// frame 3 where the viewport only contains one layer.
if (impl->active_tree()->source_frame_number() == 3) {
EXPECT_EQ(1u, context->NumUsedTextures());
} else {
EXPECT_EQ(2u, context->NumUsedTextures())
<< "For frame " << impl->active_tree()->source_frame_number();
}
context->ResetUsedTextures();
}
void AfterTest() override {}
private:
FakeContentLayerClient client_;
scoped_refptr<FakeContentLayer> parent_;
scoped_refptr<FakeContentLayer> child_;
};
// Partial updates are not possible with a delegating renderer.
SINGLE_AND_MULTI_THREAD_DIRECT_RENDERER_TEST_F(
LayerTreeHostTestAtomicCommitWithPartialUpdate);
// TODO(sohanjg) : Make it work with impl-side painting.
class LayerTreeHostTestSurfaceNotAllocatedForLayersOutsideMemoryLimit
: public LayerTreeHostTest {
protected:
void SetupTree() override {
root_layer_ = FakeContentLayer::Create(&client_);
root_layer_->SetBounds(gfx::Size(100, 100));
surface_layer1_ = FakeContentLayer::Create(&client_);
surface_layer1_->SetBounds(gfx::Size(100, 100));
surface_layer1_->SetForceRenderSurface(true);
surface_layer1_->SetOpacity(0.5f);
root_layer_->AddChild(surface_layer1_);
surface_layer2_ = FakeContentLayer::Create(&client_);
surface_layer2_->SetBounds(gfx::Size(100, 100));
surface_layer2_->SetForceRenderSurface(true);
surface_layer2_->SetOpacity(0.5f);
surface_layer1_->AddChild(surface_layer2_);
replica_layer1_ = FakeContentLayer::Create(&client_);
surface_layer1_->SetReplicaLayer(replica_layer1_.get());
replica_layer2_ = FakeContentLayer::Create(&client_);
surface_layer2_->SetReplicaLayer(replica_layer2_.get());
layer_tree_host()->SetRootLayer(root_layer_);
LayerTreeHostTest::SetupTree();
}
void BeginTest() override { PostSetNeedsCommitToMainThread(); }
void DrawLayersOnThread(LayerTreeHostImpl* host_impl) override {
Renderer* renderer = host_impl->renderer();
RenderPassId surface1_render_pass_id = host_impl->active_tree()
->root_layer()
->children()[0]
->render_surface()
->GetRenderPassId();
RenderPassId surface2_render_pass_id = host_impl->active_tree()
->root_layer()
->children()[0]
->children()[0]
->render_surface()
->GetRenderPassId();
switch (host_impl->active_tree()->source_frame_number()) {
case 0:
EXPECT_TRUE(
renderer->HasAllocatedResourcesForTesting(surface1_render_pass_id));
EXPECT_TRUE(
renderer->HasAllocatedResourcesForTesting(surface2_render_pass_id));
// Reduce the memory limit to only fit the root layer and one render
// surface. This prevents any contents drawing into surfaces
// from being allocated.
host_impl->SetMemoryPolicy(ManagedMemoryPolicy(100 * 100 * 4 * 2));
break;
case 1:
EXPECT_FALSE(
renderer->HasAllocatedResourcesForTesting(surface1_render_pass_id));
EXPECT_FALSE(
renderer->HasAllocatedResourcesForTesting(surface2_render_pass_id));
EndTest();
break;
}
}
void DidCommitAndDrawFrame() override {
if (layer_tree_host()->source_frame_number() < 2)
root_layer_->SetNeedsDisplay();
}
void AfterTest() override {
EXPECT_LE(2u, root_layer_->update_count());
EXPECT_LE(2u, surface_layer1_->update_count());
EXPECT_LE(2u, surface_layer2_->update_count());
}
FakeContentLayerClient client_;
scoped_refptr<FakeContentLayer> root_layer_;
scoped_refptr<FakeContentLayer> surface_layer1_;
scoped_refptr<FakeContentLayer> replica_layer1_;
scoped_refptr<FakeContentLayer> surface_layer2_;
scoped_refptr<FakeContentLayer> replica_layer2_;
};
// Surfaces don't exist with a delegated renderer.
SINGLE_AND_MULTI_THREAD_DIRECT_RENDERER_NOIMPL_TEST_F(
LayerTreeHostTestSurfaceNotAllocatedForLayersOutsideMemoryLimit);
class EvictionTestLayer : public Layer {
public:
static scoped_refptr<EvictionTestLayer> Create() {
return make_scoped_refptr(new EvictionTestLayer());
}
bool Update(ResourceUpdateQueue*, const OcclusionTracker<Layer>*) override;
bool DrawsContent() const override { return true; }
scoped_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl) override;
void PushPropertiesTo(LayerImpl* impl) override;
void SetTexturePriorities(const PriorityCalculator&) override;
bool HaveBackingTexture() const {
return texture_.get() ? texture_->have_backing_texture() : false;
}
private:
EvictionTestLayer() : Layer() {}
~EvictionTestLayer() override {}
void CreateTextureIfNeeded() {
if (texture_)
return;
texture_ = PrioritizedResource::Create(
layer_tree_host()->contents_texture_manager());
texture_->SetDimensions(gfx::Size(10, 10), RGBA_8888);
bitmap_.allocN32Pixels(10, 10);
}
scoped_ptr<PrioritizedResource> texture_;
SkBitmap bitmap_;
};
class EvictionTestLayerImpl : public LayerImpl {
public:
static scoped_ptr<EvictionTestLayerImpl> Create(LayerTreeImpl* tree_impl,
int id) {
return make_scoped_ptr(new EvictionTestLayerImpl(tree_impl, id));
}
~EvictionTestLayerImpl() override {}
void AppendQuads(RenderPass* render_pass,
const Occlusion& occlusion_in_content_space,
AppendQuadsData* append_quads_data) override {
ASSERT_TRUE(has_texture_);
ASSERT_NE(0u, layer_tree_impl()->resource_provider()->num_resources());
}
void SetHasTexture(bool has_texture) { has_texture_ = has_texture; }
private:
EvictionTestLayerImpl(LayerTreeImpl* tree_impl, int id)
: LayerImpl(tree_impl, id), has_texture_(false) {}
bool has_texture_;
};
void EvictionTestLayer::SetTexturePriorities(const PriorityCalculator&) {
CreateTextureIfNeeded();
if (!texture_)
return;
texture_->set_request_priority(PriorityCalculator::UIPriority(true));
}
bool EvictionTestLayer::Update(ResourceUpdateQueue* queue,
const OcclusionTracker<Layer>* occlusion) {
CreateTextureIfNeeded();
if (!texture_)
return false;
gfx::Rect full_rect(0, 0, 10, 10);
ResourceUpdate upload = ResourceUpdate::Create(
texture_.get(), &bitmap_, full_rect, full_rect, gfx::Vector2d());
queue->AppendFullUpload(upload);
return true;
}
scoped_ptr<LayerImpl> EvictionTestLayer::CreateLayerImpl(
LayerTreeImpl* tree_impl) {
return EvictionTestLayerImpl::Create(tree_impl, layer_id_);
}
void EvictionTestLayer::PushPropertiesTo(LayerImpl* layer_impl) {
Layer::PushPropertiesTo(layer_impl);
EvictionTestLayerImpl* test_layer_impl =
static_cast<EvictionTestLayerImpl*>(layer_impl);
test_layer_impl->SetHasTexture(texture_->have_backing_texture());
}
class LayerTreeHostTestEvictTextures : public LayerTreeHostTest {
public:
LayerTreeHostTestEvictTextures()
: layer_(EvictionTestLayer::Create()),
impl_for_evict_textures_(0),
num_commits_(0) {}
void BeginTest() override {
layer_tree_host()->SetRootLayer(layer_);
layer_tree_host()->SetViewportSize(gfx::Size(10, 20));
gfx::Transform identity_matrix;
SetLayerPropertiesForTesting(layer_.get(),
0,
identity_matrix,
gfx::Point3F(0.f, 0.f, 0.f),
gfx::PointF(0.f, 0.f),
gfx::Size(10, 20),
true);
PostSetNeedsCommitToMainThread();
}
void PostEvictTextures() {
ImplThreadTaskRunner()->PostTask(
FROM_HERE,
base::Bind(&LayerTreeHostTestEvictTextures::EvictTexturesOnImplThread,
base::Unretained(this)));
}
void EvictTexturesOnImplThread() {
DCHECK(impl_for_evict_textures_);
impl_for_evict_textures_->EvictTexturesForTesting();
}
// Commit 1: Just commit and draw normally, then post an eviction at the end
// that will trigger a commit.
// Commit 2: Triggered by the eviction, let it go through and then set
// needsCommit.
// Commit 3: Triggered by the setNeedsCommit. In Layout(), post an eviction
// task, which will be handled before the commit. Don't set needsCommit, it
// should have been posted. A frame should not be drawn (note,
// didCommitAndDrawFrame may be called anyway).
// Commit 4: Triggered by the eviction, let it go through and then set
// needsCommit.
// Commit 5: Triggered by the setNeedsCommit, post an eviction task in
// Layout(), a frame should not be drawn but a commit will be posted.
// Commit 6: Triggered by the eviction, post an eviction task in
// Layout(), which will be a noop, letting the commit (which recreates the
// textures) go through and draw a frame, then end the test.
//
// Commits 1+2 test the eviction recovery path where eviction happens outside
// of the beginFrame/commit pair.
// Commits 3+4 test the eviction recovery path where eviction happens inside
// the beginFrame/commit pair.
// Commits 5+6 test the path where an eviction happens during the eviction
// recovery path.
void DidCommit() override {
switch (num_commits_) {
case 1:
EXPECT_TRUE(layer_->HaveBackingTexture());
PostEvictTextures();
break;
case 2:
EXPECT_TRUE(layer_->HaveBackingTexture());
layer_tree_host()->SetNeedsCommit();
break;
case 3:
break;
case 4:
EXPECT_TRUE(layer_->HaveBackingTexture());
layer_tree_host()->SetNeedsCommit();
break;
case 5:
break;
case 6:
EXPECT_TRUE(layer_->HaveBackingTexture());
EndTest();
break;
default:
NOTREACHED();
break;
}
}
void CommitCompleteOnThread(LayerTreeHostImpl* impl) override {
impl_for_evict_textures_ = impl;
}
void Layout() override {
++num_commits_;
switch (num_commits_) {
case 1:
case 2:
break;
case 3:
PostEvictTextures();
break;
case 4:
// We couldn't check in didCommitAndDrawFrame on commit 3,
// so check here.
EXPECT_FALSE(layer_->HaveBackingTexture());
break;
case 5:
PostEvictTextures();
break;
case 6:
// We couldn't check in didCommitAndDrawFrame on commit 5,
// so check here.
EXPECT_FALSE(layer_->HaveBackingTexture());
PostEvictTextures();
break;
default:
NOTREACHED();
break;
}
}
void AfterTest() override {}
private:
FakeContentLayerClient client_;
scoped_refptr<EvictionTestLayer> layer_;
LayerTreeHostImpl* impl_for_evict_textures_;
int num_commits_;
};
MULTI_THREAD_NOIMPL_TEST_F(LayerTreeHostTestEvictTextures);
class LayerTreeHostTestContinuousInvalidate : public LayerTreeHostTest {
public:
LayerTreeHostTestContinuousInvalidate()
: num_commit_complete_(0), num_draw_layers_(0) {}
void BeginTest() override {
layer_tree_host()->SetViewportSize(gfx::Size(10, 10));
layer_tree_host()->root_layer()->SetBounds(gfx::Size(10, 10));
if (layer_tree_host()->settings().impl_side_painting)
layer_ = FakePictureLayer::Create(&client_);
else
layer_ = FakeContentLayer::Create(&client_);
layer_->SetBounds(gfx::Size(10, 10));
layer_->SetPosition(gfx::PointF(0.f, 0.f));
layer_->SetIsDrawable(true);
layer_tree_host()->root_layer()->AddChild(layer_);
PostSetNeedsCommitToMainThread();
}
void DidCommitAndDrawFrame() override {
if (num_draw_layers_ == 2)
return;
layer_->SetNeedsDisplay();
}
void CommitCompleteOnThread(LayerTreeHostImpl* impl) override {
if (num_draw_layers_ == 1)
num_commit_complete_++;
}
void DrawLayersOnThread(LayerTreeHostImpl* impl) override {
num_draw_layers_++;
if (num_draw_layers_ == 2)
EndTest();
}
void AfterTest() override {
// Check that we didn't commit twice between first and second draw.
EXPECT_EQ(1, num_commit_complete_);
}
private:
FakeContentLayerClient client_;
scoped_refptr<Layer> layer_;
int num_commit_complete_;
int num_draw_layers_;
};
SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestContinuousInvalidate);
class LayerTreeHostTestDeferCommits : public LayerTreeHostTest {
public:
LayerTreeHostTestDeferCommits()
: num_commits_deferred_(0), num_complete_commits_(0) {}
void BeginTest() override { PostSetNeedsCommitToMainThread(); }
void DidDeferCommit() override {
num_commits_deferred_++;
layer_tree_host()->SetDeferCommits(false);
}
void DidCommit() override {
num_complete_commits_++;
switch (num_complete_commits_) {
case 1:
EXPECT_EQ(0, num_commits_deferred_);
layer_tree_host()->SetDeferCommits(true);
PostSetNeedsCommitToMainThread();
break;
case 2:
EndTest();
break;
default:
NOTREACHED();
break;
}
}
void AfterTest() override {
EXPECT_EQ(1, num_commits_deferred_);
EXPECT_EQ(2, num_complete_commits_);
}
private:
int num_commits_deferred_;
int num_complete_commits_;
};
SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestDeferCommits);
class LayerTreeHostWithProxy : public LayerTreeHost {
public:
LayerTreeHostWithProxy(FakeLayerTreeHostClient* client,
const LayerTreeSettings& settings,
scoped_ptr<FakeProxy> proxy)
: LayerTreeHost(client, NULL, NULL, settings) {
proxy->SetLayerTreeHost(this);
client->SetLayerTreeHost(this);
InitializeForTesting(proxy.Pass());
}
};
TEST(LayerTreeHostTest, LimitPartialUpdates) {
// When partial updates are not allowed, max updates should be 0.
{
FakeLayerTreeHostClient client(FakeLayerTreeHostClient::DIRECT_3D);
scoped_ptr<FakeProxy> proxy(new FakeProxy);
proxy->GetRendererCapabilities().allow_partial_texture_updates = false;
proxy->SetMaxPartialTextureUpdates(5);
LayerTreeSettings settings;
settings.max_partial_texture_updates = 10;
LayerTreeHostWithProxy host(&client, settings, proxy.Pass());
host.OnCreateAndInitializeOutputSurfaceAttempted(true);
EXPECT_EQ(0u, host.MaxPartialTextureUpdates());
}
// When partial updates are allowed,
// max updates should be limited by the proxy.
{
FakeLayerTreeHostClient client(FakeLayerTreeHostClient::DIRECT_3D);
scoped_ptr<FakeProxy> proxy(new FakeProxy);
proxy->GetRendererCapabilities().allow_partial_texture_updates = true;
proxy->SetMaxPartialTextureUpdates(5);
LayerTreeSettings settings;
settings.max_partial_texture_updates = 10;
LayerTreeHostWithProxy host(&client, settings, proxy.Pass());
host.OnCreateAndInitializeOutputSurfaceAttempted(true);
EXPECT_EQ(5u, host.MaxPartialTextureUpdates());
}
// When partial updates are allowed,
// max updates should also be limited by the settings.
{
FakeLayerTreeHostClient client(FakeLayerTreeHostClient::DIRECT_3D);
scoped_ptr<FakeProxy> proxy(new FakeProxy);
proxy->GetRendererCapabilities().allow_partial_texture_updates = true;
proxy->SetMaxPartialTextureUpdates(20);
LayerTreeSettings settings;
settings.max_partial_texture_updates = 10;
LayerTreeHostWithProxy host(&client, settings, proxy.Pass());
host.OnCreateAndInitializeOutputSurfaceAttempted(true);
EXPECT_EQ(10u, host.MaxPartialTextureUpdates());
}
}
TEST(LayerTreeHostTest, PartialUpdatesWithGLRenderer) {
FakeLayerTreeHostClient client(FakeLayerTreeHostClient::DIRECT_3D);
LayerTreeSettings settings;
settings.max_partial_texture_updates = 4;
settings.single_thread_proxy_scheduler = false;
scoped_ptr<SharedBitmapManager> shared_bitmap_manager(
new TestSharedBitmapManager());
scoped_ptr<LayerTreeHost> host =
LayerTreeHost::CreateSingleThreaded(&client,
&client,
shared_bitmap_manager.get(),
NULL,
settings,
base::MessageLoopProxy::current(),
nullptr);
client.SetLayerTreeHost(host.get());
host->Composite(base::TimeTicks::Now());
EXPECT_EQ(4u, host->settings().max_partial_texture_updates);
}
TEST(LayerTreeHostTest, PartialUpdatesWithSoftwareRenderer) {
FakeLayerTreeHostClient client(FakeLayerTreeHostClient::DIRECT_SOFTWARE);
LayerTreeSettings settings;
settings.max_partial_texture_updates = 4;
settings.single_thread_proxy_scheduler = false;
scoped_ptr<SharedBitmapManager> shared_bitmap_manager(
new TestSharedBitmapManager());
scoped_ptr<LayerTreeHost> host =
LayerTreeHost::CreateSingleThreaded(&client,
&client,
shared_bitmap_manager.get(),
NULL,
settings,
base::MessageLoopProxy::current(),
nullptr);
client.SetLayerTreeHost(host.get());
host->Composite(base::TimeTicks::Now());
EXPECT_EQ(4u, host->settings().max_partial_texture_updates);
}
TEST(LayerTreeHostTest, PartialUpdatesWithDelegatingRendererAndGLContent) {
FakeLayerTreeHostClient client(FakeLayerTreeHostClient::DELEGATED_3D);
LayerTreeSettings settings;
settings.max_partial_texture_updates = 4;
settings.single_thread_proxy_scheduler = false;
scoped_ptr<SharedBitmapManager> shared_bitmap_manager(
new TestSharedBitmapManager());
scoped_ptr<LayerTreeHost> host =
LayerTreeHost::CreateSingleThreaded(&client,
&client,
shared_bitmap_manager.get(),
NULL,
settings,
base::MessageLoopProxy::current(),
nullptr);
client.SetLayerTreeHost(host.get());
host->Composite(base::TimeTicks::Now());
EXPECT_EQ(0u, host->MaxPartialTextureUpdates());
}
TEST(LayerTreeHostTest,
PartialUpdatesWithDelegatingRendererAndSoftwareContent) {
FakeLayerTreeHostClient client(FakeLayerTreeHostClient::DELEGATED_SOFTWARE);
LayerTreeSettings settings;
settings.max_partial_texture_updates = 4;
settings.single_thread_proxy_scheduler = false;
scoped_ptr<SharedBitmapManager> shared_bitmap_manager(
new TestSharedBitmapManager());
scoped_ptr<LayerTreeHost> host =
LayerTreeHost::CreateSingleThreaded(&client,
&client,
shared_bitmap_manager.get(),
NULL,
settings,
base::MessageLoopProxy::current(),
nullptr);
client.SetLayerTreeHost(host.get());
host->Composite(base::TimeTicks::Now());
EXPECT_EQ(0u, host->MaxPartialTextureUpdates());
}
// TODO(sohanjg) : Remove it once impl-side painting ships everywhere.
class LayerTreeHostTestShutdownWithOnlySomeResourcesEvicted
: public LayerTreeHostTest {
public:
LayerTreeHostTestShutdownWithOnlySomeResourcesEvicted()
: root_layer_(FakeContentLayer::Create(&client_)),
child_layer1_(FakeContentLayer::Create(&client_)),
child_layer2_(FakeContentLayer::Create(&client_)),
num_commits_(0) {}
void BeginTest() override {
layer_tree_host()->SetViewportSize(gfx::Size(100, 100));
root_layer_->SetBounds(gfx::Size(100, 100));
child_layer1_->SetBounds(gfx::Size(100, 100));
child_layer2_->SetBounds(gfx::Size(100, 100));
root_layer_->AddChild(child_layer1_);
root_layer_->AddChild(child_layer2_);
layer_tree_host()->SetRootLayer(root_layer_);
PostSetNeedsCommitToMainThread();
}
void DidSetVisibleOnImplTree(LayerTreeHostImpl* host_impl,
bool visible) override {
if (visible) {
// One backing should remain unevicted.
EXPECT_EQ(
100u * 100u * 4u * 1u,
layer_tree_host()->contents_texture_manager()->MemoryUseBytes());
} else {
EXPECT_EQ(
0u, layer_tree_host()->contents_texture_manager()->MemoryUseBytes());
}
// Make sure that contents textures are marked as having been
// purged.
EXPECT_TRUE(host_impl->active_tree()->ContentsTexturesPurged());
// End the test in this state.
EndTest();
}
void DidActivateTreeOnThread(LayerTreeHostImpl* host_impl) override {
++num_commits_;
switch (num_commits_) {
case 1:
// All three backings should have memory.
EXPECT_EQ(
100u * 100u * 4u * 3u,
layer_tree_host()->contents_texture_manager()->MemoryUseBytes());
// Set a new policy that will kick out 1 of the 3 resources.
// Because a resource was evicted, a commit will be kicked off.
host_impl->SetMemoryPolicy(
ManagedMemoryPolicy(100 * 100 * 4 * 2,
gpu::MemoryAllocation::CUTOFF_ALLOW_EVERYTHING,
1000));
break;
case 2:
// Only two backings should have memory.
EXPECT_EQ(
100u * 100u * 4u * 2u,
layer_tree_host()->contents_texture_manager()->MemoryUseBytes());
// Become backgrounded, which will cause 1 more resource to be
// evicted.
PostSetVisibleToMainThread(false);
break;
default:
// No further commits should happen because this is not visible
// anymore.
NOTREACHED();
break;
}
}
void AfterTest() override {}
private:
FakeContentLayerClient client_;
scoped_refptr<FakeContentLayer> root_layer_;
scoped_refptr<FakeContentLayer> child_layer1_;
scoped_refptr<FakeContentLayer> child_layer2_;
int num_commits_;
};
SINGLE_AND_MULTI_THREAD_NOIMPL_TEST_F(
LayerTreeHostTestShutdownWithOnlySomeResourcesEvicted);
class LayerTreeHostTestLCDChange : public LayerTreeHostTest {
public:
class PaintClient : public FakeContentLayerClient {
public:
PaintClient() : paint_count_(0) {}
int paint_count() const { return paint_count_; }
void PaintContents(
SkCanvas* canvas,
const gfx::Rect& clip,
ContentLayerClient::GraphicsContextStatus gc_status) override {
FakeContentLayerClient::PaintContents(canvas, clip, gc_status);
++paint_count_;
}
scoped_refptr<DisplayItemList> PaintContentsToDisplayList(
const gfx::Rect& clip,
GraphicsContextStatus gc_status) override {
NOTIMPLEMENTED();
return DisplayItemList::Create();
}
bool FillsBoundsCompletely() const override { return false; }
private:
int paint_count_;
};
void SetupTree() override {
num_tiles_rastered_ = 0;
scoped_refptr<Layer> root_layer;
if (layer_tree_host()->settings().impl_side_painting)
root_layer = PictureLayer::Create(&client_);
else
root_layer = ContentLayer::Create(&client_);
client_.set_fill_with_nonsolid_color(true);
root_layer->SetIsDrawable(true);
root_layer->SetBounds(gfx::Size(10, 10));
root_layer->SetContentsOpaque(true);
layer_tree_host()->SetRootLayer(root_layer);
// The expecations are based on the assumption that the default
// LCD settings are:
EXPECT_TRUE(layer_tree_host()->settings().can_use_lcd_text);
EXPECT_FALSE(root_layer->can_use_lcd_text());
LayerTreeHostTest::SetupTree();
}
void BeginTest() override { PostSetNeedsCommitToMainThread(); }
void DidCommitAndDrawFrame() override {
switch (layer_tree_host()->source_frame_number()) {
case 1:
// The first update consists of a paint of the whole layer.
EXPECT_EQ(1, client_.paint_count());
// LCD text must have been enabled on the layer.
EXPECT_TRUE(layer_tree_host()->root_layer()->can_use_lcd_text());
PostSetNeedsCommitToMainThread();
break;
case 2:
// Since nothing changed on layer, there should be no paint.
EXPECT_EQ(1, client_.paint_count());
// LCD text must not have changed.
EXPECT_TRUE(layer_tree_host()->root_layer()->can_use_lcd_text());
// Change layer opacity that should trigger lcd change.
layer_tree_host()->root_layer()->SetOpacity(.5f);
break;
case 3:
// LCD text doesn't require re-recording, so no painting should occur.
EXPECT_EQ(1, client_.paint_count());
// LCD text must have been disabled on the layer due to opacity.
EXPECT_FALSE(layer_tree_host()->root_layer()->can_use_lcd_text());
// Change layer opacity that should not trigger lcd change.
layer_tree_host()->root_layer()->SetOpacity(1.f);
break;
case 4:
// LCD text doesn't require re-recording, so no painting should occur.
EXPECT_EQ(1, client_.paint_count());
// Even though LCD text could be allowed.
EXPECT_TRUE(layer_tree_host()->root_layer()->can_use_lcd_text());
EndTest();
break;
}
}
void NotifyTileStateChangedOnThread(LayerTreeHostImpl* host_impl,
const Tile* tile) override {
++num_tiles_rastered_;
}
void DrawLayersOnThread(LayerTreeHostImpl* host_impl) override {
switch (host_impl->active_tree()->source_frame_number()) {
case 0:
// The first draw.
EXPECT_EQ(1, num_tiles_rastered_);
break;
case 1:
// Nothing changed on the layer.
EXPECT_EQ(1, num_tiles_rastered_);
break;
case 2:
// LCD text was disabled, it should be re-rastered with LCD text off.
EXPECT_EQ(2, num_tiles_rastered_);
break;
case 3:
// LCD text was enabled but it's sticky and stays off.
EXPECT_EQ(2, num_tiles_rastered_);
break;
}
}
void AfterTest() override {}
private:
PaintClient client_;
int num_tiles_rastered_;
};
SINGLE_AND_MULTI_THREAD_IMPL_TEST_F(LayerTreeHostTestLCDChange);
// Verify that the BeginFrame notification is used to initiate rendering.
class LayerTreeHostTestBeginFrameNotification : public LayerTreeHostTest {
public:
void InitializeSettings(LayerTreeSettings* settings) override {
settings->use_external_begin_frame_source = true;
}
void BeginTest() override {
// This will trigger a SetNeedsBeginFrame which will trigger a
// BeginFrame.
PostSetNeedsCommitToMainThread();
}
DrawResult PrepareToDrawOnThread(LayerTreeHostImpl* host_impl,
LayerTreeHostImpl::FrameData* frame,
DrawResult draw_result) override {
EndTest();
return DRAW_SUCCESS;
}
void AfterTest() override {}
private:
base::TimeTicks frame_time_;
};
MULTI_THREAD_TEST_F(LayerTreeHostTestBeginFrameNotification);
class LayerTreeHostTestBeginFrameNotificationShutdownWhileEnabled
: public LayerTreeHostTest {
public:
void InitializeSettings(LayerTreeSettings* settings) override {
settings->use_external_begin_frame_source = true;
settings->using_synchronous_renderer_compositor = true;
}
void BeginTest() override { PostSetNeedsCommitToMainThread(); }
void CommitCompleteOnThread(LayerTreeHostImpl* host_impl) override {
// The BeginFrame notification is turned off now but will get enabled
// once we return. End test while it's enabled.
ImplThreadTaskRunner()->PostTask(
FROM_HERE,
base::Bind(&LayerTreeHostTestBeginFrameNotification::EndTest,
base::Unretained(this)));
}
void AfterTest() override {}
};
MULTI_THREAD_TEST_F(
LayerTreeHostTestBeginFrameNotificationShutdownWhileEnabled);
class LayerTreeHostTestAbortedCommitDoesntStall : public LayerTreeHostTest {
protected:
LayerTreeHostTestAbortedCommitDoesntStall()
: commit_count_(0), commit_abort_count_(0), commit_complete_count_(0) {}
void InitializeSettings(LayerTreeSettings* settings) override {
settings->use_external_begin_frame_source = true;
}
void BeginTest() override { PostSetNeedsCommitToMainThread(); }
void DidCommit() override {
commit_count_++;
if (commit_count_ == 4) {
// After two aborted commits, request a real commit now to make sure a
// real commit following an aborted commit will still complete and
// end the test even when the Impl thread is idle.
layer_tree_host()->SetNeedsCommit();
}
}
void BeginMainFrameAbortedOnThread(LayerTreeHostImpl* host_impl,
bool did_handle) override {
commit_abort_count_++;
// Initiate another abortable commit.
host_impl->SetNeedsCommit();
}
void CommitCompleteOnThread(LayerTreeHostImpl* host_impl) override {
commit_complete_count_++;
if (commit_complete_count_ == 1) {
// Initiate an abortable commit after the first commit.
host_impl->SetNeedsCommit();
} else {
EndTest();
}
}
void AfterTest() override {
EXPECT_EQ(commit_count_, 5);
EXPECT_EQ(commit_abort_count_, 3);
EXPECT_EQ(commit_complete_count_, 2);
}
int commit_count_;
int commit_abort_count_;
int commit_complete_count_;
};
class LayerTreeHostTestAbortedCommitDoesntStallSynchronousCompositor
: public LayerTreeHostTestAbortedCommitDoesntStall {
void InitializeSettings(LayerTreeSettings* settings) override {
LayerTreeHostTestAbortedCommitDoesntStall::InitializeSettings(settings);
settings->using_synchronous_renderer_compositor = true;
}
};
MULTI_THREAD_TEST_F(
LayerTreeHostTestAbortedCommitDoesntStallSynchronousCompositor);
class LayerTreeHostTestAbortedCommitDoesntStallDisabledVsync
: public LayerTreeHostTestAbortedCommitDoesntStall {
void InitializeSettings(LayerTreeSettings* settings) override {
LayerTreeHostTestAbortedCommitDoesntStall::InitializeSettings(settings);
settings->throttle_frame_production = false;
}
};
MULTI_THREAD_TEST_F(LayerTreeHostTestAbortedCommitDoesntStallDisabledVsync);
class LayerTreeHostTestUninvertibleTransformDoesNotBlockActivation
: public LayerTreeHostTest {
protected:
void InitializeSettings(LayerTreeSettings* settings) override {
settings->impl_side_painting = true;
}
void SetupTree() override {
LayerTreeHostTest::SetupTree();
scoped_refptr<Layer> layer = PictureLayer::Create(&client_);
layer->SetTransform(gfx::Transform(0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
layer->SetBounds(gfx::Size(10, 10));
layer_tree_host()->root_layer()->AddChild(layer);
}
void BeginTest() override { PostSetNeedsCommitToMainThread(); }
void DidActivateTreeOnThread(LayerTreeHostImpl* host_impl) override {
EndTest();
}
void AfterTest() override {}
FakeContentLayerClient client_;
};
MULTI_THREAD_TEST_F(
LayerTreeHostTestUninvertibleTransformDoesNotBlockActivation);
class LayerTreeHostTestChangeLayerPropertiesInPaintContents
: public LayerTreeHostTest {
public:
class SetBoundsClient : public ContentLayerClient {
public:
SetBoundsClient() : layer_(0) {}
void set_layer(Layer* layer) { layer_ = layer; }
void PaintContents(
SkCanvas* canvas,
const gfx::Rect& clip,
ContentLayerClient::GraphicsContextStatus gc_status) override {
layer_->SetBounds(gfx::Size(2, 2));
}
scoped_refptr<DisplayItemList> PaintContentsToDisplayList(
const gfx::Rect& clip,
GraphicsContextStatus gc_status) override {
NOTIMPLEMENTED();
return DisplayItemList::Create();
}
bool FillsBoundsCompletely() const override { return false; }
private:
Layer* layer_;
};
LayerTreeHostTestChangeLayerPropertiesInPaintContents() : num_commits_(0) {}
void SetupTree() override {
if (layer_tree_host()->settings().impl_side_painting) {
scoped_refptr<PictureLayer> root_layer = PictureLayer::Create(&client_);
layer_tree_host()->SetRootLayer(root_layer);
} else {
scoped_refptr<ContentLayer> root_layer = ContentLayer::Create(&client_);
layer_tree_host()->SetRootLayer(root_layer);
}
Layer* root_layer = layer_tree_host()->root_layer();
root_layer->SetIsDrawable(true);
root_layer->SetBounds(gfx::Size(1, 1));
client_.set_layer(root_layer);
LayerTreeHostTest::SetupTree();
}
void BeginTest() override { PostSetNeedsCommitToMainThread(); }
void AfterTest() override {}
void DidActivateTreeOnThread(LayerTreeHostImpl* host_impl) override {
num_commits_++;
if (num_commits_ == 1) {
LayerImpl* root_layer = host_impl->active_tree()->root_layer();
EXPECT_EQ(gfx::Size(1, 1), root_layer->bounds());
} else {
LayerImpl* root_layer = host_impl->active_tree()->root_layer();
EXPECT_EQ(gfx::Size(2, 2), root_layer->bounds());
EndTest();
}
}
private:
SetBoundsClient client_;
int num_commits_;
};
SINGLE_AND_MULTI_THREAD_TEST_F(
LayerTreeHostTestChangeLayerPropertiesInPaintContents);
class MockIOSurfaceWebGraphicsContext3D : public TestWebGraphicsContext3D {
public:
MockIOSurfaceWebGraphicsContext3D() {
test_capabilities_.gpu.iosurface = true;
test_capabilities_.gpu.texture_rectangle = true;
}
GLuint createTexture() override { return 1; }
MOCK_METHOD1(activeTexture, void(GLenum texture));
MOCK_METHOD2(bindTexture, void(GLenum target,
GLuint texture_id));
MOCK_METHOD3(texParameteri, void(GLenum target,
GLenum pname,
GLint param));
MOCK_METHOD5(texImageIOSurface2DCHROMIUM, void(GLenum target,
GLint width,
GLint height,
GLuint ioSurfaceId,
GLuint plane));
MOCK_METHOD4(drawElements, void(GLenum mode,
GLsizei count,
GLenum type,
GLintptr offset));
MOCK_METHOD1(deleteTexture, void(GLenum texture));
MOCK_METHOD2(produceTextureCHROMIUM,
void(GLenum target, const GLbyte* mailbox));
};
class LayerTreeHostTestIOSurfaceDrawing : public LayerTreeHostTest {
protected:
scoped_ptr<FakeOutputSurface> CreateFakeOutputSurface(
bool fallback) override {
scoped_ptr<MockIOSurfaceWebGraphicsContext3D> mock_context_owned(
new MockIOSurfaceWebGraphicsContext3D);
mock_context_ = mock_context_owned.get();
if (delegating_renderer())
return FakeOutputSurface::CreateDelegating3d(mock_context_owned.Pass());
else
return FakeOutputSurface::Create3d(mock_context_owned.Pass());
}
void SetupTree() override {
LayerTreeHostTest::SetupTree();
layer_tree_host()->root_layer()->SetIsDrawable(false);
io_surface_id_ = 9;
io_surface_size_ = gfx::Size(6, 7);
scoped_refptr<IOSurfaceLayer> io_surface_layer = IOSurfaceLayer::Create();
io_surface_layer->SetBounds(gfx::Size(10, 10));
io_surface_layer->SetIsDrawable(true);
io_surface_layer->SetContentsOpaque(true);
io_surface_layer->SetIOSurfaceProperties(io_surface_id_, io_surface_size_);
layer_tree_host()->root_layer()->AddChild(io_surface_layer);
}
void BeginTest() override { PostSetNeedsCommitToMainThread(); }
void DidActivateTreeOnThread(LayerTreeHostImpl* host_impl) override {
EXPECT_EQ(0u, host_impl->resource_provider()->num_resources());
// In WillDraw, the IOSurfaceLayer sets up the io surface texture.
EXPECT_CALL(*mock_context_, activeTexture(_)).Times(0);
EXPECT_CALL(*mock_context_, bindTexture(GL_TEXTURE_RECTANGLE_ARB, 1))
.Times(AtLeast(1));
EXPECT_CALL(*mock_context_,
texParameteri(
GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_LINEAR))
.Times(1);
EXPECT_CALL(*mock_context_,
texParameteri(
GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_LINEAR))
.Times(1);
EXPECT_CALL(*mock_context_,
texParameteri(GL_TEXTURE_RECTANGLE_ARB,
GL_TEXTURE_POOL_CHROMIUM,
GL_TEXTURE_POOL_UNMANAGED_CHROMIUM)).Times(1);
EXPECT_CALL(*mock_context_,
texParameteri(GL_TEXTURE_RECTANGLE_ARB,
GL_TEXTURE_WRAP_S,
GL_CLAMP_TO_EDGE)).Times(1);
EXPECT_CALL(*mock_context_,
texParameteri(GL_TEXTURE_RECTANGLE_ARB,
GL_TEXTURE_WRAP_T,
GL_CLAMP_TO_EDGE)).Times(1);
EXPECT_CALL(*mock_context_,
texImageIOSurface2DCHROMIUM(GL_TEXTURE_RECTANGLE_ARB,
io_surface_size_.width(),
io_surface_size_.height(),
io_surface_id_,
0)).Times(1);
EXPECT_CALL(*mock_context_, bindTexture(_, 0)).Times(AnyNumber());
}
DrawResult PrepareToDrawOnThread(LayerTreeHostImpl* host_impl,
LayerTreeHostImpl::FrameData* frame,
DrawResult draw_result) override {
Mock::VerifyAndClearExpectations(&mock_context_);
ResourceProvider* resource_provider = host_impl->resource_provider();
EXPECT_EQ(1u, resource_provider->num_resources());
CHECK_EQ(1u, frame->render_passes.size());
CHECK_LE(1u, frame->render_passes[0]->quad_list.size());
const DrawQuad* quad = frame->render_passes[0]->quad_list.front();
CHECK_EQ(DrawQuad::IO_SURFACE_CONTENT, quad->material);
const IOSurfaceDrawQuad* io_surface_draw_quad =
IOSurfaceDrawQuad::MaterialCast(quad);
EXPECT_EQ(io_surface_size_, io_surface_draw_quad->io_surface_size);
EXPECT_NE(0u, io_surface_draw_quad->io_surface_resource_id);
EXPECT_EQ(static_cast<GLenum>(GL_TEXTURE_RECTANGLE_ARB),
resource_provider->TargetForTesting(
io_surface_draw_quad->io_surface_resource_id));
EXPECT_CALL(*mock_context_, bindTexture(GL_TEXTURE_RECTANGLE_ARB, 1))
.Times(1);
if (delegating_renderer()) {
// The io surface layer's resource should be sent to the parent.
EXPECT_CALL(*mock_context_,
produceTextureCHROMIUM(GL_TEXTURE_RECTANGLE_ARB, _)).Times(1);
} else {
// The io surface layer's texture is drawn.
EXPECT_CALL(*mock_context_, activeTexture(GL_TEXTURE0)).Times(AtLeast(1));
EXPECT_CALL(*mock_context_, drawElements(GL_TRIANGLES, 6, _, _))
.Times(AtLeast(1));
}
return draw_result;
}
void DrawLayersOnThread(LayerTreeHostImpl* host_impl) override {
Mock::VerifyAndClearExpectations(&mock_context_);
EXPECT_CALL(*mock_context_, deleteTexture(1)).Times(AtLeast(1));
EndTest();
}
void AfterTest() override {}
int io_surface_id_;
MockIOSurfaceWebGraphicsContext3D* mock_context_;
gfx::Size io_surface_size_;
};
SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestIOSurfaceDrawing);
class LayerTreeHostTestNumFramesPending : public LayerTreeHostTest {
public:
void BeginTest() override {
frame_ = 0;
PostSetNeedsCommitToMainThread();
}
// Round 1: commit + draw
// Round 2: commit only (no draw/swap)
// Round 3: draw only (no commit)
void DidCommit() override {
int commit = layer_tree_host()->source_frame_number();
switch (commit) {
case 2:
// Round 2 done.
EXPECT_EQ(1, frame_);
layer_tree_host()->SetNeedsRedraw();
break;
}
}
void DidCompleteSwapBuffers() override {
int commit = layer_tree_host()->source_frame_number();
++frame_;
switch (frame_) {
case 1:
// Round 1 done.
EXPECT_EQ(1, commit);
layer_tree_host()->SetNeedsCommit();
break;
case 2:
// Round 3 done.
EXPECT_EQ(2, commit);
EndTest();
break;
}
}
void AfterTest() override {}
protected:
int frame_;
};
// Flaky on all platforms: http://crbug.com/327498
TEST_F(LayerTreeHostTestNumFramesPending, DISABLED_DelegatingRenderer) {
RunTest(true, true, true);
}
TEST_F(LayerTreeHostTestNumFramesPending, DISABLED_GLRenderer) {
RunTest(true, false, true);
}
class LayerTreeHostTestDeferredInitialize : public LayerTreeHostTest {
public:
void InitializeSettings(LayerTreeSettings* settings) override {
// PictureLayer can only be used with impl side painting enabled.
settings->impl_side_painting = true;
}
void SetupTree() override {
layer_ = FakePictureLayer::Create(&client_);
// Force commits to not be aborted so new frames get drawn, otherwise
// the renderer gets deferred initialized but nothing new needs drawing.
layer_->set_always_update_resources(true);
layer_tree_host()->SetRootLayer(layer_);
LayerTreeHostTest::SetupTree();
}
void BeginTest() override {
did_initialize_gl_ = false;
did_release_gl_ = false;
last_source_frame_number_drawn_ = -1; // Never drawn.
PostSetNeedsCommitToMainThread();
}
scoped_ptr<FakeOutputSurface> CreateFakeOutputSurface(
bool fallback) override {
scoped_ptr<TestWebGraphicsContext3D> context3d(
TestWebGraphicsContext3D::Create());
return FakeOutputSurface::CreateDeferredGL(
scoped_ptr<SoftwareOutputDevice>(new SoftwareOutputDevice),
delegating_renderer());
}
void DrawLayersOnThread(LayerTreeHostImpl* host_impl) override {
ASSERT_TRUE(host_impl->RootLayer());
FakePictureLayerImpl* layer_impl =
static_cast<FakePictureLayerImpl*>(host_impl->RootLayer());
// The same frame can be draw multiple times if new visible tiles are
// rasterized. But we want to make sure we only post DeferredInitialize
// and ReleaseGL once, so early out if the same frame is drawn again.
if (last_source_frame_number_drawn_ ==
host_impl->active_tree()->source_frame_number())
return;
last_source_frame_number_drawn_ =
host_impl->active_tree()->source_frame_number();
if (!did_initialize_gl_) {
EXPECT_LE(1u, layer_impl->append_quads_count());
ImplThreadTaskRunner()->PostTask(
FROM_HERE,
base::Bind(
&LayerTreeHostTestDeferredInitialize::DeferredInitializeAndRedraw,
base::Unretained(this),
base::Unretained(host_impl)));
} else if (did_initialize_gl_ && !did_release_gl_) {
EXPECT_LE(2u, layer_impl->append_quads_count());
ImplThreadTaskRunner()->PostTask(
FROM_HERE,
base::Bind(&LayerTreeHostTestDeferredInitialize::ReleaseGLAndRedraw,
base::Unretained(this),
base::Unretained(host_impl)));
} else if (did_initialize_gl_ && did_release_gl_) {
EXPECT_LE(3u, layer_impl->append_quads_count());
EndTest();
}
}
void DeferredInitializeAndRedraw(LayerTreeHostImpl* host_impl) {
EXPECT_FALSE(did_initialize_gl_);
// SetAndInitializeContext3D calls SetNeedsCommit.
FakeOutputSurface* fake_output_surface =
static_cast<FakeOutputSurface*>(host_impl->output_surface());
scoped_refptr<TestContextProvider> context_provider =
TestContextProvider::Create(); // Not bound to thread.
EXPECT_TRUE(
fake_output_surface->InitializeAndSetContext3d(context_provider));
did_initialize_gl_ = true;
}
void ReleaseGLAndRedraw(LayerTreeHostImpl* host_impl) {
EXPECT_TRUE(did_initialize_gl_);
EXPECT_FALSE(did_release_gl_);
// ReleaseGL calls SetNeedsCommit.
static_cast<FakeOutputSurface*>(host_impl->output_surface())->ReleaseGL();
did_release_gl_ = true;
}
void SwapBuffersOnThread(LayerTreeHostImpl* host_impl, bool result) override {
ASSERT_TRUE(result);
DelegatedFrameData* delegated_frame_data =
output_surface()->last_sent_frame().delegated_frame_data.get();
if (!delegated_frame_data)
return;
// Return all resources immediately.
TransferableResourceArray resources_to_return =
output_surface()->resources_held_by_parent();
CompositorFrameAck ack;
for (size_t i = 0; i < resources_to_return.size(); ++i)
output_surface()->ReturnResource(resources_to_return[i].id, &ack);
host_impl->ReclaimResources(&ack);
}
void AfterTest() override {
EXPECT_TRUE(did_initialize_gl_);
EXPECT_TRUE(did_release_gl_);
}
private:
FakeContentLayerClient client_;
scoped_refptr<FakePictureLayer> layer_;
bool did_initialize_gl_;
bool did_release_gl_;
int last_source_frame_number_drawn_;
};
MULTI_THREAD_TEST_F(LayerTreeHostTestDeferredInitialize);
class LayerTreeHostTestDeferredInitializeWithGpuRasterization
: public LayerTreeHostTestDeferredInitialize {
void InitializeSettings(LayerTreeSettings* settings) override {
// PictureLayer can only be used with impl side painting enabled.
settings->impl_side_painting = true;
settings->gpu_rasterization_enabled = true;
settings->gpu_rasterization_forced = true;
}
};
MULTI_THREAD_TEST_F(LayerTreeHostTestDeferredInitializeWithGpuRasterization);
// Test for UI Resource management.
class LayerTreeHostTestUIResource : public LayerTreeHostTest {
public:
LayerTreeHostTestUIResource() : num_ui_resources_(0) {}
void InitializeSettings(LayerTreeSettings* settings) override {
settings->renderer_settings.texture_id_allocation_chunk_size = 1;
}
void BeginTest() override { PostSetNeedsCommitToMainThread(); }
void DidCommit() override {
int frame = layer_tree_host()->source_frame_number();
switch (frame) {
case 1:
CreateResource();
CreateResource();
PostSetNeedsCommitToMainThread();
break;
case 2:
// Usually ScopedUIResource are deleted from the manager in their
// destructor. Here we just want to test that a direct call to
// DeleteUIResource works.
layer_tree_host()->DeleteUIResource(ui_resources_[0]->id());
PostSetNeedsCommitToMainThread();
break;
case 3:
// DeleteUIResource can be called with an invalid id.
layer_tree_host()->DeleteUIResource(ui_resources_[0]->id());
PostSetNeedsCommitToMainThread();
break;
case 4:
CreateResource();
CreateResource();
PostSetNeedsCommitToMainThread();
break;
case 5:
ClearResources();
EndTest();
break;
}
}
void PerformTest(LayerTreeHostImpl* impl) {
TestWebGraphicsContext3D* context = TestContext();
int frame = impl->active_tree()->source_frame_number();
switch (frame) {
case 0:
ASSERT_EQ(0u, context->NumTextures());
break;
case 1:
// Created two textures.
ASSERT_EQ(2u, context->NumTextures());
break;
case 2:
// One texture left after one deletion.
ASSERT_EQ(1u, context->NumTextures());
break;
case 3:
// Resource manager state should not change when delete is called on an
// invalid id.
ASSERT_EQ(1u, context->NumTextures());
break;
case 4:
// Creation after deletion: two more creates should total up to
// three textures.
ASSERT_EQ(3u, context->NumTextures());
break;
}
}
void CommitCompleteOnThread(LayerTreeHostImpl* impl) override {
if (!layer_tree_host()->settings().impl_side_painting)
PerformTest(impl);
}
void DidActivateTreeOnThread(LayerTreeHostImpl* impl) override {
if (layer_tree_host()->settings().impl_side_painting)
PerformTest(impl);
}
void AfterTest() override {}
private: