1// Copyright 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "cc/trees/layer_tree_host.h"
6
7#include <sstream>
8
9#include "base/file_util.h"
10#include "base/files/file_path.h"
11#include "base/path_service.h"
12#include "base/strings/string_piece.h"
13#include "base/time/time.h"
14#include "cc/debug/lap_timer.h"
15#include "cc/layers/content_layer.h"
16#include "cc/layers/nine_patch_layer.h"
17#include "cc/layers/solid_color_layer.h"
18#include "cc/layers/texture_layer.h"
19#include "cc/resources/texture_mailbox.h"
20#include "cc/test/fake_content_layer_client.h"
21#include "cc/test/layer_tree_json_parser.h"
22#include "cc/test/layer_tree_test.h"
23#include "cc/test/paths.h"
24#include "cc/trees/layer_tree_impl.h"
25#include "testing/perf/perf_test.h"
26
27namespace cc {
28namespace {
29
30static const int kTimeLimitMillis = 2000;
31static const int kWarmupRuns = 5;
32static const int kTimeCheckInterval = 10;
33
34class LayerTreeHostPerfTest : public LayerTreeTest {
35 public:
36  LayerTreeHostPerfTest()
37      : draw_timer_(kWarmupRuns,
38                    base::TimeDelta::FromMilliseconds(kTimeLimitMillis),
39                    kTimeCheckInterval),
40        commit_timer_(0, base::TimeDelta(), 1),
41        full_damage_each_frame_(false),
42        animation_driven_drawing_(false),
43        measure_commit_cost_(false) {
44    fake_content_layer_client_.set_paint_all_opaque(true);
45  }
46
47  virtual void InitializeSettings(LayerTreeSettings* settings) OVERRIDE {
48    settings->throttle_frame_production = false;
49  }
50
51  virtual void BeginTest() OVERRIDE {
52    BuildTree();
53    PostSetNeedsCommitToMainThread();
54  }
55
56  virtual void Animate(base::TimeTicks monotonic_time) OVERRIDE {
57    if (animation_driven_drawing_ && !TestEnded()) {
58      layer_tree_host()->SetNeedsAnimate();
59      layer_tree_host()->SetNextCommitForcesRedraw();
60    }
61  }
62
63  virtual void BeginCommitOnThread(LayerTreeHostImpl* host_impl) OVERRIDE {
64    if (measure_commit_cost_)
65      commit_timer_.Start();
66  }
67
68  virtual void CommitCompleteOnThread(LayerTreeHostImpl* host_impl) OVERRIDE {
69    if (measure_commit_cost_ && draw_timer_.IsWarmedUp()) {
70      commit_timer_.NextLap();
71    }
72  }
73
74  virtual void DrawLayersOnThread(LayerTreeHostImpl* impl) OVERRIDE {
75    if (TestEnded() || CleanUpStarted())
76      return;
77    draw_timer_.NextLap();
78    if (draw_timer_.HasTimeLimitExpired()) {
79      CleanUpAndEndTest(impl);
80      return;
81    }
82    if (!animation_driven_drawing_)
83      impl->SetNeedsRedraw();
84    if (full_damage_each_frame_)
85      impl->SetFullRootLayerDamage();
86  }
87
88  virtual void CleanUpAndEndTest(LayerTreeHostImpl* host_impl) { EndTest(); }
89
90  virtual bool CleanUpStarted() { return false; }
91
92  virtual void BuildTree() {}
93
94  virtual void AfterTest() OVERRIDE {
95    CHECK(!test_name_.empty()) << "Must SetTestName() before AfterTest().";
96    perf_test::PrintResult("layer_tree_host_frame_time", "", test_name_,
97                           1000 * draw_timer_.MsPerLap(), "us", true);
98    if (measure_commit_cost_) {
99      perf_test::PrintResult("layer_tree_host_commit_time", "", test_name_,
100                             1000 * commit_timer_.MsPerLap(), "us", true);
101    }
102  }
103
104 protected:
105  LapTimer draw_timer_;
106  LapTimer commit_timer_;
107
108  std::string test_name_;
109  FakeContentLayerClient fake_content_layer_client_;
110  bool full_damage_each_frame_;
111  bool animation_driven_drawing_;
112
113  bool measure_commit_cost_;
114};
115
116
117class LayerTreeHostPerfTestJsonReader : public LayerTreeHostPerfTest {
118 public:
119  LayerTreeHostPerfTestJsonReader()
120      : LayerTreeHostPerfTest() {
121  }
122
123  void SetTestName(const std::string& name) {
124    test_name_ = name;
125  }
126
127  void ReadTestFile(const std::string& name) {
128    base::FilePath test_data_dir;
129    ASSERT_TRUE(PathService::Get(CCPaths::DIR_TEST_DATA, &test_data_dir));
130    base::FilePath json_file = test_data_dir.AppendASCII(name + ".json");
131    ASSERT_TRUE(base::ReadFileToString(json_file, &json_));
132  }
133
134  virtual void BuildTree() OVERRIDE {
135    gfx::Size viewport = gfx::Size(720, 1038);
136    layer_tree_host()->SetViewportSize(viewport);
137    scoped_refptr<Layer> root = ParseTreeFromJson(json_,
138                                                  &fake_content_layer_client_);
139    ASSERT_TRUE(root.get());
140    layer_tree_host()->SetRootLayer(root);
141  }
142
143 private:
144  std::string json_;
145};
146
147// Simulates a tab switcher scene with two stacks of 10 tabs each.
148TEST_F(LayerTreeHostPerfTestJsonReader, TenTenSingleThread) {
149  SetTestName("10_10_single_thread");
150  ReadTestFile("10_10_layer_tree");
151  RunTest(false, false, false);
152}
153
154TEST_F(LayerTreeHostPerfTestJsonReader, TenTenThreadedImplSide) {
155  SetTestName("10_10_threaded_impl_side");
156  ReadTestFile("10_10_layer_tree");
157  RunTestWithImplSidePainting();
158}
159
160// Simulates a tab switcher scene with two stacks of 10 tabs each.
161TEST_F(LayerTreeHostPerfTestJsonReader,
162       TenTenSingleThread_FullDamageEachFrame) {
163  full_damage_each_frame_ = true;
164  SetTestName("10_10_single_thread_full_damage_each_frame");
165  ReadTestFile("10_10_layer_tree");
166  RunTest(false, false, false);
167}
168
169TEST_F(LayerTreeHostPerfTestJsonReader,
170       TenTenThreadedImplSide_FullDamageEachFrame) {
171  full_damage_each_frame_ = true;
172  SetTestName("10_10_threaded_impl_side_full_damage_each_frame");
173  ReadTestFile("10_10_layer_tree");
174  RunTestWithImplSidePainting();
175}
176
177// Invalidates a leaf layer in the tree on the main thread after every commit.
178class LayerTreeHostPerfTestLeafInvalidates
179    : public LayerTreeHostPerfTestJsonReader {
180 public:
181  virtual void BuildTree() OVERRIDE {
182    LayerTreeHostPerfTestJsonReader::BuildTree();
183
184    // Find a leaf layer.
185    for (layer_to_invalidate_ = layer_tree_host()->root_layer();
186         layer_to_invalidate_->children().size();
187         layer_to_invalidate_ = layer_to_invalidate_->children()[0]) {}
188  }
189
190  virtual void DidCommitAndDrawFrame() OVERRIDE {
191    if (TestEnded())
192      return;
193
194    static bool flip = true;
195    layer_to_invalidate_->SetOpacity(flip ? 1.f : 0.5f);
196    flip = !flip;
197  }
198
199 protected:
200  Layer* layer_to_invalidate_;
201};
202
203// Simulates a tab switcher scene with two stacks of 10 tabs each. Invalidate a
204// property on a leaf layer in the tree every commit.
205TEST_F(LayerTreeHostPerfTestLeafInvalidates, TenTenSingleThread) {
206  SetTestName("10_10_single_thread_leaf_invalidates");
207  ReadTestFile("10_10_layer_tree");
208  RunTest(false, false, false);
209}
210
211TEST_F(LayerTreeHostPerfTestLeafInvalidates, TenTenThreadedImplSide) {
212  SetTestName("10_10_threaded_impl_side_leaf_invalidates");
213  ReadTestFile("10_10_layer_tree");
214  RunTestWithImplSidePainting();
215}
216
217// Simulates main-thread scrolling on each frame.
218class ScrollingLayerTreePerfTest : public LayerTreeHostPerfTestJsonReader {
219 public:
220  ScrollingLayerTreePerfTest()
221      : LayerTreeHostPerfTestJsonReader() {
222  }
223
224  virtual void BuildTree() OVERRIDE {
225    LayerTreeHostPerfTestJsonReader::BuildTree();
226    scrollable_ = layer_tree_host()->root_layer()->children()[1];
227    ASSERT_TRUE(scrollable_.get());
228  }
229
230  virtual void Layout() OVERRIDE {
231    static const gfx::Vector2d delta = gfx::Vector2d(0, 10);
232    scrollable_->SetScrollOffset(scrollable_->scroll_offset() + delta);
233  }
234
235 private:
236  scoped_refptr<Layer> scrollable_;
237};
238
239TEST_F(ScrollingLayerTreePerfTest, LongScrollablePageSingleThread) {
240  SetTestName("long_scrollable_page");
241  ReadTestFile("long_scrollable_page");
242  RunTest(false, false, false);
243}
244
245TEST_F(ScrollingLayerTreePerfTest, LongScrollablePageThreadedImplSide) {
246  SetTestName("long_scrollable_page_threaded_impl_side");
247  ReadTestFile("long_scrollable_page");
248  RunTestWithImplSidePainting();
249}
250
251static void EmptyReleaseCallback(uint32 sync_point, bool lost_resource) {}
252
253// Simulates main-thread scrolling on each frame.
254class BrowserCompositorInvalidateLayerTreePerfTest
255    : public LayerTreeHostPerfTestJsonReader {
256 public:
257  BrowserCompositorInvalidateLayerTreePerfTest()
258      : LayerTreeHostPerfTestJsonReader(),
259        next_sync_point_(1),
260        clean_up_started_(false) {}
261
262  virtual void BuildTree() OVERRIDE {
263    LayerTreeHostPerfTestJsonReader::BuildTree();
264    tab_contents_ =
265        static_cast<TextureLayer*>(
266            layer_tree_host()->root_layer()->children()[0]->
267                                             children()[0]->
268                                             children()[0]->
269                                             children()[0].get());
270    ASSERT_TRUE(tab_contents_.get());
271  }
272
273  virtual void WillCommit() OVERRIDE {
274    if (CleanUpStarted())
275      return;
276    gpu::Mailbox gpu_mailbox;
277    std::ostringstream name_stream;
278    name_stream << "name" << next_sync_point_;
279    gpu_mailbox.SetName(
280        reinterpret_cast<const int8*>(name_stream.str().c_str()));
281    scoped_ptr<SingleReleaseCallback> callback = SingleReleaseCallback::Create(
282        base::Bind(&EmptyReleaseCallback));
283    TextureMailbox mailbox(gpu_mailbox, GL_TEXTURE_2D, next_sync_point_);
284    next_sync_point_++;
285
286    tab_contents_->SetTextureMailbox(mailbox, callback.Pass());
287  }
288
289  virtual void DidCommit() OVERRIDE {
290    if (CleanUpStarted())
291      return;
292    layer_tree_host()->SetNeedsCommit();
293  }
294
295  virtual void CleanUpAndEndTest(LayerTreeHostImpl* host_impl) OVERRIDE {
296    clean_up_started_ = true;
297    MainThreadTaskRunner()->PostTask(
298        FROM_HERE,
299        base::Bind(&BrowserCompositorInvalidateLayerTreePerfTest::
300                        CleanUpAndEndTestOnMainThread,
301                   base::Unretained(this)));
302  }
303
304  void CleanUpAndEndTestOnMainThread() {
305    tab_contents_->SetTextureMailbox(TextureMailbox(),
306                                     scoped_ptr<SingleReleaseCallback>());
307    EndTest();
308  }
309
310  virtual bool CleanUpStarted() OVERRIDE { return clean_up_started_; }
311
312 private:
313  scoped_refptr<TextureLayer> tab_contents_;
314  unsigned next_sync_point_;
315  bool clean_up_started_;
316};
317
318TEST_F(BrowserCompositorInvalidateLayerTreePerfTest, DenseBrowserUI) {
319  measure_commit_cost_ = true;
320  SetTestName("dense_layer_tree");
321  ReadTestFile("dense_layer_tree");
322  RunTestWithImplSidePainting();
323}
324
325// Simulates a page with several large, transformed and animated layers.
326TEST_F(LayerTreeHostPerfTestJsonReader, HeavyPageThreadedImplSide) {
327  animation_driven_drawing_ = true;
328  measure_commit_cost_ = true;
329  SetTestName("heavy_page");
330  ReadTestFile("heavy_layer_tree");
331  RunTestWithImplSidePainting();
332}
333
334}  // namespace
335}  // namespace cc
336