1 // Copyright (c) 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 "base/command_line.h" 6 #include "base/file_util.h" 7 #include "base/files/file_path.h" 8 #include "base/json/json_reader.h" 9 #include "base/memory/scoped_ptr.h" 10 #include "base/path_service.h" 11 #include "base/run_loop.h" 12 #include "base/strings/string_number_conversions.h" 13 #include "base/strings/stringprintf.h" 14 #include "base/test/trace_event_analyzer.h" 15 #include "base/values.h" 16 #include "chrome/browser/ui/browser.h" 17 #include "chrome/browser/ui/browser_window.h" 18 #include "chrome/browser/ui/tabs/tab_strip_model.h" 19 #include "chrome/browser/ui/window_snapshot/window_snapshot.h" 20 #include "chrome/common/chrome_paths.h" 21 #include "chrome/common/chrome_switches.h" 22 #include "chrome/common/net/url_fixer_upper.h" 23 #include "chrome/test/base/test_switches.h" 24 #include "chrome/test/base/tracing.h" 25 #include "chrome/test/base/ui_test_utils.h" 26 #include "chrome/test/perf/browser_perf_test.h" 27 #include "chrome/test/perf/perf_test.h" 28 #include "content/public/browser/web_contents.h" 29 #include "content/public/browser/web_contents_view.h" 30 #include "content/public/common/content_switches.h" 31 #include "content/public/test/browser_test_utils.h" 32 #include "content/public/test/test_utils.h" 33 #include "gpu/config/gpu_test_config.h" 34 #include "net/base/net_util.h" 35 #include "net/dns/mock_host_resolver.h" 36 #include "testing/gtest/include/gtest/gtest.h" 37 #include "third_party/skia/include/core/SkBitmap.h" 38 #include "third_party/skia/include/core/SkColor.h" 39 #include "ui/gfx/codec/png_codec.h" 40 #include "ui/gl/gl_switches.h" 41 #include "url/gurl.h" 42 43 #if defined(OS_WIN) 44 #include "base/win/windows_version.h" 45 #endif 46 47 namespace { 48 49 enum RunTestFlags { 50 kNone = 0, 51 kInternal = 1 << 0, // Test uses internal test data. 52 kAllowExternalDNS = 1 << 1, // Test needs external DNS lookup. 53 kIsGpuCanvasTest = 1 << 2 // Test uses GPU accelerated canvas features. 54 }; 55 56 enum ThroughputTestFlags { 57 kSW = 0, 58 kGPU = 1 << 0, 59 kCompositorThread = 1 << 1 60 }; 61 62 const int kSpinUpTimeMs = 4 * 1000; 63 const int kRunTimeMs = 10 * 1000; 64 const int kIgnoreSomeFrames = 3; 65 66 class ThroughputTest : public BrowserPerfTest { 67 public: 68 explicit ThroughputTest(int flags) : 69 use_gpu_(flags & kGPU), 70 use_compositor_thread_(flags & kCompositorThread), 71 spinup_time_ms_(kSpinUpTimeMs), 72 run_time_ms_(kRunTimeMs) {} 73 74 // This indicates running on GPU bots, not necessarily using the GPU. 75 bool IsGpuAvailable() const { 76 return CommandLine::ForCurrentProcess()->HasSwitch("enable-gpu"); 77 } 78 79 // Parse flags from JSON to control the test behavior. 80 bool ParseFlagsFromJSON(const base::FilePath& json_dir, 81 const std::string& json, 82 int index) { 83 scoped_ptr<base::Value> root; 84 root.reset(base::JSONReader::Read(json)); 85 86 ListValue* root_list = NULL; 87 if (!root.get() || !root->GetAsList(&root_list)) { 88 LOG(ERROR) << "JSON missing root list element"; 89 return false; 90 } 91 92 DictionaryValue* item = NULL; 93 if (!root_list->GetDictionary(index, &item)) { 94 LOG(ERROR) << "index " << index << " not found in JSON"; 95 return false; 96 } 97 98 std::string str; 99 if (item->GetStringASCII("url", &str)) { 100 gurl_ = GURL(str); 101 } else if (item->GetStringASCII("file", &str)) { 102 base::FilePath empty; 103 gurl_ = URLFixerUpper::FixupRelativeFile(empty, empty.AppendASCII(str)); 104 } else { 105 LOG(ERROR) << "missing url or file"; 106 return false; 107 } 108 109 if (!gurl_.is_valid()) { 110 LOG(ERROR) << "invalid url: " << gurl_.possibly_invalid_spec(); 111 return false; 112 } 113 114 base::FilePath::StringType cache_dir; 115 if (item->GetString("local_path", &cache_dir)) 116 local_cache_path_ = json_dir.Append(cache_dir); 117 118 int num; 119 if (item->GetInteger("spinup_time", &num)) 120 spinup_time_ms_ = num * 1000; 121 if (item->GetInteger("run_time", &num)) 122 run_time_ms_ = num * 1000; 123 124 DictionaryValue* pixel = NULL; 125 if (item->GetDictionary("wait_pixel", &pixel)) { 126 int x, y, r, g, b; 127 ListValue* color; 128 if (pixel->GetInteger("x", &x) && 129 pixel->GetInteger("y", &y) && 130 pixel->GetString("op", &str) && 131 pixel->GetList("color", &color) && 132 color->GetInteger(0, &r) && 133 color->GetInteger(1, &g) && 134 color->GetInteger(2, &b)) { 135 wait_for_pixel_.reset(new WaitPixel(x, y, r, g, b, str)); 136 } else { 137 LOG(ERROR) << "invalid wait_pixel args"; 138 return false; 139 } 140 } 141 return true; 142 } 143 144 // Parse extra-chrome-flags for extra command line flags. 145 void ParseFlagsFromCommandLine() { 146 if (!CommandLine::ForCurrentProcess()->HasSwitch( 147 switches::kExtraChromeFlags)) 148 return; 149 CommandLine::StringType flags = 150 CommandLine::ForCurrentProcess()->GetSwitchValueNative( 151 switches::kExtraChromeFlags); 152 if (MatchPattern(flags, FILE_PATH_LITERAL("*.json:*"))) { 153 CommandLine::StringType::size_type colon_pos = flags.find_last_of(':'); 154 CommandLine::StringType::size_type num_pos = colon_pos + 1; 155 int index; 156 ASSERT_TRUE(base::StringToInt( 157 flags.substr(num_pos, flags.size() - num_pos), &index)); 158 base::FilePath filepath(flags.substr(0, colon_pos)); 159 std::string json; 160 ASSERT_TRUE(file_util::ReadFileToString(filepath, &json)); 161 ASSERT_TRUE(ParseFlagsFromJSON(filepath.DirName(), json, index)); 162 } else { 163 gurl_ = GURL(flags); 164 } 165 } 166 167 void AllowExternalDNS() { 168 net::RuleBasedHostResolverProc* resolver = 169 new net::RuleBasedHostResolverProc(host_resolver()); 170 resolver->AllowDirectLookup("*"); 171 host_resolver_override_.reset( 172 new net::ScopedDefaultHostResolverProc(resolver)); 173 } 174 175 void ResetAllowExternalDNS() { 176 host_resolver_override_.reset(); 177 } 178 179 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE { 180 BrowserPerfTest::SetUpCommandLine(command_line); 181 ParseFlagsFromCommandLine(); 182 if (!local_cache_path_.value().empty()) { 183 // If --record-mode is already specified, don't set playback-mode. 184 if (!command_line->HasSwitch(switches::kRecordMode)) 185 command_line->AppendSwitch(switches::kPlaybackMode); 186 command_line->AppendSwitchNative(switches::kDiskCacheDir, 187 local_cache_path_.value()); 188 LOG(INFO) << local_cache_path_.value(); 189 } 190 // We are measuring throughput, so we don't want any FPS throttling. 191 command_line->AppendSwitch(switches::kDisableGpuVsync); 192 command_line->AppendSwitch(switches::kAllowFileAccessFromFiles); 193 // Enable or disable GPU acceleration. 194 if (use_gpu_) { 195 command_line->AppendSwitch(switches::kForceCompositingMode); 196 } else { 197 command_line->AppendSwitch(switches::kDisableAcceleratedCompositing); 198 command_line->AppendSwitch(switches::kDisableExperimentalWebGL); 199 command_line->AppendSwitch(switches::kDisableAccelerated2dCanvas); 200 } 201 if (use_compositor_thread_) { 202 ASSERT_TRUE(use_gpu_); 203 command_line->AppendSwitch(switches::kEnableThreadedCompositing); 204 } else { 205 command_line->AppendSwitch(switches::kDisableThreadedCompositing); 206 } 207 } 208 209 void Wait(int ms) { 210 base::RunLoop run_loop; 211 base::MessageLoop::current()->PostDelayedTask( 212 FROM_HERE, 213 run_loop.QuitClosure(), 214 base::TimeDelta::FromMilliseconds(ms)); 215 content::RunThisRunLoop(&run_loop); 216 } 217 218 // Take snapshot of the current tab, encode it as PNG, and save to a SkBitmap. 219 bool TabSnapShotToImage(SkBitmap* bitmap) { 220 CHECK(bitmap); 221 std::vector<unsigned char> png; 222 223 gfx::Rect root_bounds = browser()->window()->GetBounds(); 224 gfx::Rect tab_contents_bounds; 225 browser()->tab_strip_model()->GetActiveWebContents()->GetView()-> 226 GetContainerBounds(&tab_contents_bounds); 227 228 gfx::Rect snapshot_bounds(tab_contents_bounds.x() - root_bounds.x(), 229 tab_contents_bounds.y() - root_bounds.y(), 230 tab_contents_bounds.width(), 231 tab_contents_bounds.height()); 232 233 gfx::NativeWindow native_window = browser()->window()->GetNativeWindow(); 234 if (!chrome::GrabWindowSnapshotForUser(native_window, &png, 235 snapshot_bounds)) { 236 LOG(ERROR) << "browser::GrabWindowSnapShot() failed"; 237 return false; 238 } 239 240 if (!gfx::PNGCodec::Decode(reinterpret_cast<unsigned char*>(&*png.begin()), 241 png.size(), bitmap)) { 242 LOG(ERROR) << "Decode PNG to a SkBitmap failed"; 243 return false; 244 } 245 return true; 246 } 247 248 // Check a pixel color every second until it passes test. 249 void WaitForPixelColor() { 250 if (wait_for_pixel_.get()) { 251 bool success = false; 252 do { 253 SkBitmap bitmap; 254 ASSERT_TRUE(TabSnapShotToImage(&bitmap)); 255 success = wait_for_pixel_->IsDone(bitmap); 256 if (!success) 257 Wait(1000); 258 } while (!success); 259 } 260 } 261 262 // flags is one or more of RunTestFlags OR'd together. 263 void RunTest(const std::string& test_name, int flags) { 264 // Set path to test html. 265 base::FilePath test_path; 266 ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &test_path)); 267 test_path = test_path.Append(FILE_PATH_LITERAL("perf")); 268 if (flags & kInternal) 269 test_path = test_path.Append(FILE_PATH_LITERAL("private")); 270 test_path = test_path.Append(FILE_PATH_LITERAL("rendering")); 271 test_path = test_path.Append(FILE_PATH_LITERAL("throughput")); 272 test_path = test_path.AppendASCII(test_name); 273 test_path = test_path.Append(FILE_PATH_LITERAL("index.html")); 274 ASSERT_TRUE(base::PathExists(test_path)) 275 << "Missing test file: " << test_path.value(); 276 277 gurl_ = net::FilePathToFileURL(test_path); 278 RunTestWithURL(test_name, flags); 279 } 280 281 // flags is one or more of RunTestFlags OR'd together. 282 void RunCanvasBenchTest(const std::string& test_name, int flags) { 283 // Set path to test html. 284 base::FilePath test_path; 285 ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &test_path)); 286 test_path = test_path.Append(FILE_PATH_LITERAL("perf")); 287 test_path = test_path.Append(FILE_PATH_LITERAL("canvas_bench")); 288 test_path = test_path.AppendASCII(test_name + ".html"); 289 ASSERT_TRUE(base::PathExists(test_path)) 290 << "Missing test file: " << test_path.value(); 291 292 gurl_ = net::FilePathToFileURL(test_path); 293 RunTestWithURL(test_name, flags); 294 } 295 296 // flags is one or more of RunTestFlags OR'd together. 297 void RunTestWithURL(int flags) { 298 RunTestWithURL(gurl_.possibly_invalid_spec(), flags); 299 } 300 301 // flags is one or more of RunTestFlags OR'd together. 302 void RunTestWithURL(const std::string& test_name, int flags) { 303 using trace_analyzer::Query; 304 using trace_analyzer::TraceAnalyzer; 305 using trace_analyzer::TraceEventVector; 306 307 #if defined(OS_WIN) 308 if (use_gpu_ && (flags & kIsGpuCanvasTest) && 309 base::win::OSInfo::GetInstance()->version() == base::win::VERSION_XP) { 310 // crbug.com/128208 311 LOG(WARNING) << "Test skipped: GPU canvas tests do not run on XP."; 312 return; 313 } 314 #endif 315 316 if (use_gpu_ && !IsGpuAvailable()) { 317 LOG(WARNING) << "Test skipped: requires gpu. Pass --enable-gpu on the " 318 "command line if use of GPU is desired."; 319 return; 320 } 321 322 if (flags & kAllowExternalDNS) 323 AllowExternalDNS(); 324 325 std::string json_events; 326 TraceEventVector events_sw, events_gpu; 327 scoped_ptr<TraceAnalyzer> analyzer; 328 329 LOG(INFO) << gurl_.possibly_invalid_spec(); 330 ui_test_utils::NavigateToURLWithDisposition( 331 browser(), gurl_, CURRENT_TAB, ui_test_utils::BROWSER_TEST_NONE); 332 content::WaitForLoadStop( 333 browser()->tab_strip_model()->GetActiveWebContents()); 334 335 // Let the test spin up. 336 LOG(INFO) << "Spinning up test..."; 337 ASSERT_TRUE(tracing::BeginTracing("test_gpu")); 338 Wait(spinup_time_ms_); 339 ASSERT_TRUE(tracing::EndTracing(&json_events)); 340 341 // Wait for a pixel color to change (if requested). 342 WaitForPixelColor(); 343 344 // Check if GPU is rendering: 345 analyzer.reset(TraceAnalyzer::Create(json_events)); 346 bool ran_on_gpu = (analyzer->FindEvents( 347 Query::EventNameIs("SwapBuffers"), &events_gpu) > 0u); 348 LOG(INFO) << "Mode: " << (ran_on_gpu ? "GPU" : "Software"); 349 EXPECT_EQ(use_gpu_, ran_on_gpu); 350 351 // Let the test run for a while. 352 LOG(INFO) << "Running test..."; 353 ASSERT_TRUE(tracing::BeginTracing("test_fps")); 354 Wait(run_time_ms_); 355 ASSERT_TRUE(tracing::EndTracing(&json_events)); 356 357 // Search for frame ticks. We look for both SW and GPU frame ticks so that 358 // the test can verify that only one or the other are found. 359 analyzer.reset(TraceAnalyzer::Create(json_events)); 360 Query query_sw = Query::EventNameIs("TestFrameTickSW"); 361 Query query_gpu = Query::EventNameIs("TestFrameTickGPU"); 362 analyzer->FindEvents(query_sw, &events_sw); 363 analyzer->FindEvents(query_gpu, &events_gpu); 364 TraceEventVector* frames = NULL; 365 if (use_gpu_) { 366 frames = &events_gpu; 367 EXPECT_EQ(0u, events_sw.size()); 368 } else { 369 frames = &events_sw; 370 EXPECT_EQ(0u, events_gpu.size()); 371 } 372 ASSERT_GT(frames->size(), 20u); 373 // Cull a few leading and trailing events as they might be unreliable. 374 TraceEventVector rate_events(frames->begin() + kIgnoreSomeFrames, 375 frames->end() - kIgnoreSomeFrames); 376 trace_analyzer::RateStats stats; 377 ASSERT_TRUE(GetRateStats(rate_events, &stats, NULL)); 378 LOG(INFO) << "FPS = " << 1000000.0 / stats.mean_us; 379 380 // Print perf results. 381 double mean_ms = stats.mean_us / 1000.0; 382 double std_dev_ms = stats.standard_deviation_us / 1000.0 / 1000.0; 383 std::string trace_name = use_compositor_thread_? "gpu_thread" : 384 ran_on_gpu ? "gpu" : "software"; 385 std::string mean_and_error = base::StringPrintf("%f,%f", mean_ms, 386 std_dev_ms); 387 perf_test::PrintResultMeanAndError(test_name, 388 std::string(), 389 trace_name, 390 mean_and_error, 391 "frame_time", 392 true); 393 394 if (flags & kAllowExternalDNS) 395 ResetAllowExternalDNS(); 396 397 // Close the tab so that we can quit without timing out during the 398 // wait-for-idle stage in browser_test framework. 399 browser()->tab_strip_model()->GetActiveWebContents()->Close(); 400 } 401 402 private: 403 // WaitPixel checks a color against the color at the given pixel coordinates 404 // of an SkBitmap. 405 class WaitPixel { 406 enum Operator { 407 EQUAL, 408 NOT_EQUAL 409 }; 410 public: 411 WaitPixel(int x, int y, int r, int g, int b, const std::string& op) : 412 x_(x), y_(y), r_(r), g_(g), b_(b) { 413 if (op == "!=") 414 op_ = EQUAL; 415 else if (op == "==") 416 op_ = NOT_EQUAL; 417 else 418 CHECK(false) << "op value \"" << op << "\" is not supported"; 419 } 420 bool IsDone(const SkBitmap& bitmap) { 421 SkColor color = bitmap.getColor(x_, y_); 422 int r = SkColorGetR(color); 423 int g = SkColorGetG(color); 424 int b = SkColorGetB(color); 425 LOG(INFO) << "color(" << x_ << "," << y_ << "): " << 426 r << "," << g << "," << b; 427 switch (op_) { 428 case EQUAL: 429 return r != r_ || g != g_ || b != b_; 430 case NOT_EQUAL: 431 return r == r_ && g == g_ && b == b_; 432 default: 433 return false; 434 } 435 } 436 private: 437 int x_; 438 int y_; 439 int r_; 440 int g_; 441 int b_; 442 Operator op_; 443 }; 444 445 bool use_gpu_; 446 bool use_compositor_thread_; 447 int spinup_time_ms_; 448 int run_time_ms_; 449 base::FilePath local_cache_path_; 450 GURL gurl_; 451 scoped_ptr<net::ScopedDefaultHostResolverProc> host_resolver_override_; 452 scoped_ptr<WaitPixel> wait_for_pixel_; 453 }; 454 455 // For running tests on GPU: 456 class ThroughputTestGPU : public ThroughputTest { 457 public: 458 ThroughputTestGPU() : ThroughputTest(kGPU) {} 459 }; 460 461 // For running tests on GPU with the compositor thread: 462 class ThroughputTestThread : public ThroughputTest { 463 public: 464 ThroughputTestThread() : ThroughputTest(kGPU | kCompositorThread) {} 465 }; 466 467 // For running tests on Software: 468 class ThroughputTestSW : public ThroughputTest { 469 public: 470 ThroughputTestSW() : ThroughputTest(kSW) {} 471 }; 472 473 //////////////////////////////////////////////////////////////////////////////// 474 /// Tests 475 476 // Run this test with a URL on the command line: 477 // performance_browser_tests --gtest_also_run_disabled_tests --enable-gpu 478 // --gtest_filter=ThroughputTest*URL --extra-chrome-flags=http://... 479 // or, specify a json file with a list of tests, and the index of the test: 480 // --extra-chrome-flags=path/to/tests.json:0 481 // The json format is an array of tests, for example: 482 // [ 483 // {"url":"http://...", 484 // "spinup_time":5, 485 // "run_time":10, 486 // "local_path":"path/to/disk-cache-dir", 487 // "wait_pixel":{"x":10,"y":10,"op":"!=","color":[24,24,24]}}, 488 // {"url":"http://..."} 489 // ] 490 // The only required argument is url. If local_path is set, the test goes into 491 // playback-mode and only loads files from the specified cache. If wait_pixel is 492 // specified, then after spinup_time the test waits for the color at the 493 // specified pixel coordinate to match the given color with the given operator. 494 IN_PROC_BROWSER_TEST_F(ThroughputTestSW, DISABLED_TestURL) { 495 RunTestWithURL(kAllowExternalDNS); 496 } 497 498 IN_PROC_BROWSER_TEST_F(ThroughputTestGPU, DISABLED_TestURL) { 499 RunTestWithURL(kAllowExternalDNS); 500 } 501 502 IN_PROC_BROWSER_TEST_F(ThroughputTestGPU, Particles) { 503 RunTest("particles", kInternal); 504 } 505 506 IN_PROC_BROWSER_TEST_F(ThroughputTestThread, Particles) { 507 RunTest("particles", kInternal); 508 } 509 510 IN_PROC_BROWSER_TEST_F(ThroughputTestSW, CanvasDemoSW) { 511 RunTest("canvas-demo", kInternal); 512 } 513 514 IN_PROC_BROWSER_TEST_F(ThroughputTestGPU, CanvasDemoGPU) { 515 RunTest("canvas-demo", kInternal | kIsGpuCanvasTest); 516 } 517 518 IN_PROC_BROWSER_TEST_F(ThroughputTestThread, CanvasDemoGPU) { 519 RunTest("canvas-demo", kInternal | kIsGpuCanvasTest); 520 } 521 522 // CompositingHugeDivSW timed out on Mac Intel Release GPU bot 523 // See crbug.com/114781 524 // Stopped producing results in SW: crbug.com/127621 525 IN_PROC_BROWSER_TEST_F(ThroughputTestSW, DISABLED_CompositingHugeDivSW) { 526 RunTest("compositing_huge_div", kNone); 527 } 528 529 // Failing with insufficient frames: crbug.com/127595 530 IN_PROC_BROWSER_TEST_F(ThroughputTestGPU, DISABLED_CompositingHugeDivGPU) { 531 RunTest("compositing_huge_div", kNone); 532 } 533 534 IN_PROC_BROWSER_TEST_F(ThroughputTestSW, DrawImageShadowSW) { 535 RunTest("canvas2d_balls_with_shadow", kNone); 536 } 537 538 IN_PROC_BROWSER_TEST_F(ThroughputTestGPU, DrawImageShadowGPU) { 539 RunTest("canvas2d_balls_with_shadow", kNone | kIsGpuCanvasTest); 540 } 541 542 IN_PROC_BROWSER_TEST_F(ThroughputTestThread, DrawImageShadowGPU) { 543 RunTest("canvas2d_balls_with_shadow", kNone | kIsGpuCanvasTest); 544 } 545 546 IN_PROC_BROWSER_TEST_F(ThroughputTestSW, CanvasToCanvasDrawSW) { 547 if (IsGpuAvailable() && 548 gpu::GPUTestBotConfig::CurrentConfigMatches("MAC AMD")) 549 return; 550 RunTest("canvas2d_balls_draw_from_canvas", kNone); 551 } 552 553 IN_PROC_BROWSER_TEST_F(ThroughputTestGPU, CanvasToCanvasDrawGPU) { 554 if (IsGpuAvailable() && 555 gpu::GPUTestBotConfig::CurrentConfigMatches("MAC AMD")) 556 return; 557 RunTest("canvas2d_balls_draw_from_canvas", kNone | kIsGpuCanvasTest); 558 } 559 560 // Failing on windows GPU bots: crbug.com/255192 561 IN_PROC_BROWSER_TEST_F(ThroughputTestSW, DISABLED_CanvasTextSW) { 562 if (IsGpuAvailable() && 563 gpu::GPUTestBotConfig::CurrentConfigMatches("MAC AMD")) 564 return; 565 RunTest("canvas2d_balls_text", kNone); 566 } 567 568 IN_PROC_BROWSER_TEST_F(ThroughputTestGPU, CanvasTextGPU) { 569 RunTest("canvas2d_balls_text", kNone | kIsGpuCanvasTest); 570 } 571 572 IN_PROC_BROWSER_TEST_F(ThroughputTestSW, CanvasFillPathSW) { 573 RunTest("canvas2d_balls_fill_path", kNone); 574 } 575 576 IN_PROC_BROWSER_TEST_F(ThroughputTestGPU, CanvasFillPathGPU) { 577 RunTest("canvas2d_balls_fill_path", kNone | kIsGpuCanvasTest); 578 } 579 580 IN_PROC_BROWSER_TEST_F(ThroughputTestSW, CanvasSingleImageSW) { 581 RunCanvasBenchTest("single_image", kNone); 582 } 583 584 IN_PROC_BROWSER_TEST_F(ThroughputTestGPU, CanvasSingleImageGPU) { 585 if (IsGpuAvailable() && 586 gpu::GPUTestBotConfig::CurrentConfigMatches("MAC AMD")) 587 return; 588 RunCanvasBenchTest("single_image", kNone | kIsGpuCanvasTest); 589 } 590 591 IN_PROC_BROWSER_TEST_F(ThroughputTestSW, CanvasManyImagesSW) { 592 RunCanvasBenchTest("many_images", kNone); 593 } 594 595 IN_PROC_BROWSER_TEST_F(ThroughputTestGPU, CanvasManyImagesGPU) { 596 RunCanvasBenchTest("many_images", kNone | kIsGpuCanvasTest); 597 } 598 599 IN_PROC_BROWSER_TEST_F(ThroughputTestThread, CanvasManyImagesGPU) { 600 RunCanvasBenchTest("many_images", kNone | kIsGpuCanvasTest); 601 } 602 603 } // namespace 604