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/layers/content_layer.h" 15 #include "cc/layers/nine_patch_layer.h" 16 #include "cc/layers/solid_color_layer.h" 17 #include "cc/layers/texture_layer.h" 18 #include "cc/resources/texture_mailbox.h" 19 #include "cc/test/fake_content_layer_client.h" 20 #include "cc/test/lap_timer.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 27 namespace cc { 28 namespace { 29 30 static const int kTimeLimitMillis = 2000; 31 static const int kWarmupRuns = 5; 32 static const int kTimeCheckInterval = 10; 33 34 class 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 } 60 61 virtual void BeginCommitOnThread(LayerTreeHostImpl* host_impl) OVERRIDE { 62 if (measure_commit_cost_) 63 commit_timer_.Start(); 64 } 65 66 virtual void CommitCompleteOnThread(LayerTreeHostImpl* host_impl) OVERRIDE { 67 if (measure_commit_cost_ && draw_timer_.IsWarmedUp()) { 68 commit_timer_.NextLap(); 69 } 70 } 71 72 virtual void DrawLayersOnThread(LayerTreeHostImpl* impl) OVERRIDE { 73 if (TestEnded() || CleanUpStarted()) 74 return; 75 draw_timer_.NextLap(); 76 if (draw_timer_.HasTimeLimitExpired()) { 77 CleanUpAndEndTest(impl); 78 return; 79 } 80 if (!animation_driven_drawing_) 81 impl->SetNeedsRedraw(); 82 if (full_damage_each_frame_) 83 impl->SetFullRootLayerDamage(); 84 } 85 86 virtual void CleanUpAndEndTest(LayerTreeHostImpl* host_impl) { EndTest(); } 87 88 virtual bool CleanUpStarted() { return false; } 89 90 virtual void BuildTree() {} 91 92 virtual void AfterTest() OVERRIDE { 93 CHECK(!test_name_.empty()) << "Must SetTestName() before AfterTest()."; 94 perf_test::PrintResult("layer_tree_host_frame_time", "", test_name_, 95 1000 * draw_timer_.MsPerLap(), "us", true); 96 if (measure_commit_cost_) { 97 perf_test::PrintResult("layer_tree_host_commit_time", "", test_name_, 98 1000 * commit_timer_.MsPerLap(), "us", true); 99 } 100 } 101 102 protected: 103 LapTimer draw_timer_; 104 LapTimer commit_timer_; 105 106 std::string test_name_; 107 FakeContentLayerClient fake_content_layer_client_; 108 bool full_damage_each_frame_; 109 bool animation_driven_drawing_; 110 111 bool measure_commit_cost_; 112 }; 113 114 115 class LayerTreeHostPerfTestJsonReader : public LayerTreeHostPerfTest { 116 public: 117 LayerTreeHostPerfTestJsonReader() 118 : LayerTreeHostPerfTest() { 119 } 120 121 void SetTestName(const std::string& name) { 122 test_name_ = name; 123 } 124 125 void ReadTestFile(const std::string& name) { 126 base::FilePath test_data_dir; 127 ASSERT_TRUE(PathService::Get(CCPaths::DIR_TEST_DATA, &test_data_dir)); 128 base::FilePath json_file = test_data_dir.AppendASCII(name + ".json"); 129 ASSERT_TRUE(base::ReadFileToString(json_file, &json_)); 130 } 131 132 virtual void BuildTree() OVERRIDE { 133 gfx::Size viewport = gfx::Size(720, 1038); 134 layer_tree_host()->SetViewportSize(viewport); 135 scoped_refptr<Layer> root = ParseTreeFromJson(json_, 136 &fake_content_layer_client_); 137 ASSERT_TRUE(root.get()); 138 layer_tree_host()->SetRootLayer(root); 139 } 140 141 private: 142 std::string json_; 143 }; 144 145 // Simulates a tab switcher scene with two stacks of 10 tabs each. 146 TEST_F(LayerTreeHostPerfTestJsonReader, TenTenSingleThread) { 147 SetTestName("10_10_single_thread"); 148 ReadTestFile("10_10_layer_tree"); 149 RunTest(false, false, false); 150 } 151 152 TEST_F(LayerTreeHostPerfTestJsonReader, TenTenThreadedImplSide) { 153 SetTestName("10_10_threaded_impl_side"); 154 ReadTestFile("10_10_layer_tree"); 155 RunTestWithImplSidePainting(); 156 } 157 158 // Simulates a tab switcher scene with two stacks of 10 tabs each. 159 TEST_F(LayerTreeHostPerfTestJsonReader, 160 TenTenSingleThread_FullDamageEachFrame) { 161 full_damage_each_frame_ = true; 162 SetTestName("10_10_single_thread_full_damage_each_frame"); 163 ReadTestFile("10_10_layer_tree"); 164 RunTest(false, false, false); 165 } 166 167 TEST_F(LayerTreeHostPerfTestJsonReader, 168 TenTenThreadedImplSide_FullDamageEachFrame) { 169 full_damage_each_frame_ = true; 170 SetTestName("10_10_threaded_impl_side_full_damage_each_frame"); 171 ReadTestFile("10_10_layer_tree"); 172 RunTestWithImplSidePainting(); 173 } 174 175 // Invalidates a leaf layer in the tree on the main thread after every commit. 176 class LayerTreeHostPerfTestLeafInvalidates 177 : public LayerTreeHostPerfTestJsonReader { 178 public: 179 virtual void BuildTree() OVERRIDE { 180 LayerTreeHostPerfTestJsonReader::BuildTree(); 181 182 // Find a leaf layer. 183 for (layer_to_invalidate_ = layer_tree_host()->root_layer(); 184 layer_to_invalidate_->children().size(); 185 layer_to_invalidate_ = layer_to_invalidate_->children()[0]) {} 186 } 187 188 virtual void DidCommitAndDrawFrame() OVERRIDE { 189 if (TestEnded()) 190 return; 191 192 static bool flip = true; 193 layer_to_invalidate_->SetOpacity(flip ? 1.f : 0.5f); 194 flip = !flip; 195 } 196 197 protected: 198 Layer* layer_to_invalidate_; 199 }; 200 201 // Simulates a tab switcher scene with two stacks of 10 tabs each. Invalidate a 202 // property on a leaf layer in the tree every commit. 203 TEST_F(LayerTreeHostPerfTestLeafInvalidates, TenTenSingleThread) { 204 SetTestName("10_10_single_thread_leaf_invalidates"); 205 ReadTestFile("10_10_layer_tree"); 206 RunTest(false, false, false); 207 } 208 209 TEST_F(LayerTreeHostPerfTestLeafInvalidates, TenTenThreadedImplSide) { 210 SetTestName("10_10_threaded_impl_side_leaf_invalidates"); 211 ReadTestFile("10_10_layer_tree"); 212 RunTestWithImplSidePainting(); 213 } 214 215 // Simulates main-thread scrolling on each frame. 216 class ScrollingLayerTreePerfTest : public LayerTreeHostPerfTestJsonReader { 217 public: 218 ScrollingLayerTreePerfTest() 219 : LayerTreeHostPerfTestJsonReader() { 220 } 221 222 virtual void BuildTree() OVERRIDE { 223 LayerTreeHostPerfTestJsonReader::BuildTree(); 224 scrollable_ = layer_tree_host()->root_layer()->children()[1]; 225 ASSERT_TRUE(scrollable_.get()); 226 } 227 228 virtual void Layout() OVERRIDE { 229 static const gfx::Vector2d delta = gfx::Vector2d(0, 10); 230 scrollable_->SetScrollOffset(scrollable_->scroll_offset() + delta); 231 } 232 233 private: 234 scoped_refptr<Layer> scrollable_; 235 }; 236 237 TEST_F(ScrollingLayerTreePerfTest, LongScrollablePageSingleThread) { 238 SetTestName("long_scrollable_page"); 239 ReadTestFile("long_scrollable_page"); 240 RunTest(false, false, false); 241 } 242 243 TEST_F(ScrollingLayerTreePerfTest, LongScrollablePageThreadedImplSide) { 244 SetTestName("long_scrollable_page_threaded_impl_side"); 245 ReadTestFile("long_scrollable_page"); 246 RunTestWithImplSidePainting(); 247 } 248 249 static void EmptyReleaseCallback(unsigned sync_point, bool lost_resource) {} 250 251 // Simulates main-thread scrolling on each frame. 252 class BrowserCompositorInvalidateLayerTreePerfTest 253 : public LayerTreeHostPerfTestJsonReader { 254 public: 255 BrowserCompositorInvalidateLayerTreePerfTest() 256 : next_sync_point_(1), clean_up_started_(false) {} 257 258 virtual void BuildTree() OVERRIDE { 259 LayerTreeHostPerfTestJsonReader::BuildTree(); 260 tab_contents_ = 261 static_cast<TextureLayer*>( 262 layer_tree_host()->root_layer()->children()[0]-> 263 children()[0]-> 264 children()[0]-> 265 children()[0].get()); 266 ASSERT_TRUE(tab_contents_.get()); 267 } 268 269 virtual void WillCommit() OVERRIDE { 270 gpu::Mailbox gpu_mailbox; 271 std::ostringstream name_stream; 272 name_stream << "name" << next_sync_point_; 273 gpu_mailbox.SetName( 274 reinterpret_cast<const int8*>(name_stream.str().c_str())); 275 scoped_ptr<SingleReleaseCallback> callback = SingleReleaseCallback::Create( 276 base::Bind(&EmptyReleaseCallback)); 277 TextureMailbox mailbox(gpu_mailbox, next_sync_point_); 278 next_sync_point_++; 279 280 tab_contents_->SetTextureMailbox(mailbox, callback.Pass()); 281 } 282 283 virtual void DidCommit() OVERRIDE { 284 if (CleanUpStarted()) 285 return; 286 layer_tree_host()->SetNeedsCommit(); 287 } 288 289 virtual void DidCommitAndDrawFrame() OVERRIDE { 290 if (CleanUpStarted()) 291 EndTest(); 292 } 293 294 virtual void CleanUpAndEndTest(LayerTreeHostImpl* host_impl) OVERRIDE { 295 clean_up_started_ = true; 296 MainThreadTaskRunner()->PostTask( 297 FROM_HERE, 298 base::Bind(&BrowserCompositorInvalidateLayerTreePerfTest:: 299 CleanUpAndEndTestOnMainThread, 300 base::Unretained(this))); 301 } 302 303 void CleanUpAndEndTestOnMainThread() { 304 tab_contents_->SetTextureMailbox(TextureMailbox(), 305 scoped_ptr<SingleReleaseCallback>()); 306 } 307 308 virtual bool CleanUpStarted() OVERRIDE { return clean_up_started_; } 309 310 private: 311 scoped_refptr<TextureLayer> tab_contents_; 312 unsigned next_sync_point_; 313 bool clean_up_started_; 314 }; 315 316 TEST_F(BrowserCompositorInvalidateLayerTreePerfTest, DenseBrowserUI) { 317 SetTestName("dense_layer_tree"); 318 ReadTestFile("dense_layer_tree"); 319 RunTestWithImplSidePainting(); 320 } 321 322 // Simulates a page with several large, transformed and animated layers. 323 TEST_F(LayerTreeHostPerfTestJsonReader, HeavyPageThreadedImplSide) { 324 animation_driven_drawing_ = true; 325 measure_commit_cost_ = true; 326 SetTestName("heavy_page"); 327 ReadTestFile("heavy_layer_tree"); 328 RunTestWithImplSidePainting(); 329 } 330 331 class PageScaleImplSidePaintingPerfTest 332 : public LayerTreeHostPerfTestJsonReader { 333 public: 334 PageScaleImplSidePaintingPerfTest() 335 : max_scale_(16.f), min_scale_(1.f / max_scale_) {} 336 337 virtual void SetupTree() OVERRIDE { 338 layer_tree_host()->SetPageScaleFactorAndLimits(1.f, min_scale_, max_scale_); 339 } 340 341 virtual void ApplyScrollAndScale(gfx::Vector2d scroll_delta, 342 float scale_delta) OVERRIDE { 343 float page_scale_factor = layer_tree_host()->page_scale_factor(); 344 page_scale_factor *= scale_delta; 345 layer_tree_host()->SetPageScaleFactorAndLimits( 346 page_scale_factor, min_scale_, max_scale_); 347 } 348 349 virtual void AnimateLayers(LayerTreeHostImpl* host_impl, 350 base::TimeTicks monotonic_time) OVERRIDE { 351 if (!host_impl->pinch_gesture_active()) { 352 host_impl->PinchGestureBegin(); 353 start_time_ = monotonic_time; 354 } 355 gfx::Point anchor(200, 200); 356 357 float seconds = (monotonic_time - start_time_).InSecondsF(); 358 359 // Every half second, zoom from min scale to max scale. 360 float interval = 0.5f; 361 362 // Start time in the middle of the interval when zoom = 1. 363 seconds += interval / 2.f; 364 365 // Stack two ranges together to go up from min to max and down from 366 // max to min in the next so as not to have a zoom discrepancy. 367 float time_in_two_intervals = fmod(seconds, 2.f * interval) / interval; 368 369 // Map everything to go from min to max between 0 and 1. 370 float time_in_one_interval = 371 time_in_two_intervals > 1.f ? 2.f - time_in_two_intervals 372 : time_in_two_intervals; 373 // Normalize time to -1..1. 374 float normalized = 2.f * time_in_one_interval - 1.f; 375 float scale_factor = std::abs(normalized) * (max_scale_ - 1.f) + 1.f; 376 float total_scale = normalized < 0.f ? 1.f / scale_factor : scale_factor; 377 378 float desired_delta = 379 total_scale / host_impl->active_tree()->total_page_scale_factor(); 380 host_impl->PinchGestureUpdate(desired_delta, anchor); 381 } 382 383 private: 384 float max_scale_; 385 float min_scale_; 386 base::TimeTicks start_time_; 387 }; 388 389 TEST_F(PageScaleImplSidePaintingPerfTest, HeavyPage) { 390 measure_commit_cost_ = true; 391 SetTestName("heavy_page_page_scale"); 392 ReadTestFile("heavy_layer_tree"); 393 RunTestWithImplSidePainting(); 394 } 395 396 } // namespace 397 } // namespace cc 398