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/memory/scoped_ptr.h" 8 #include "base/path_service.h" 9 #include "base/strings/stringprintf.h" 10 #include "base/test/trace_event_analyzer.h" 11 #include "base/version.h" 12 #include "chrome/browser/ui/browser.h" 13 #include "chrome/browser/ui/browser_window.h" 14 #include "chrome/common/chrome_paths.h" 15 #include "chrome/common/chrome_switches.h" 16 #include "chrome/test/base/in_process_browser_test.h" 17 #include "chrome/test/base/tracing.h" 18 #include "chrome/test/base/ui_test_utils.h" 19 #include "content/public/browser/gpu_data_manager.h" 20 #include "content/public/common/content_client.h" 21 #include "content/public/common/content_switches.h" 22 #include "content/public/test/browser_test_utils.h" 23 #include "gpu/config/gpu_feature_type.h" 24 #include "gpu/config/gpu_info.h" 25 #include "gpu/config/gpu_test_config.h" 26 #include "net/base/filename_util.h" 27 #include "ui/gl/gl_implementation.h" 28 29 #if defined(OS_WIN) 30 #include "base/win/windows_version.h" 31 #endif 32 33 using content::GpuDataManager; 34 using gpu::GpuFeatureType; 35 using trace_analyzer::Query; 36 using trace_analyzer::TraceAnalyzer; 37 using trace_analyzer::TraceEventVector; 38 39 namespace { 40 41 const char kAcceleratedCanvasCreationEvent[] = "Canvas2DLayerBridgeCreation"; 42 const char kWebGLCreationEvent[] = "DrawingBufferCreation"; 43 44 class FakeContentClient : public content::ContentClient { 45 }; 46 47 class GpuFeatureTest : public InProcessBrowserTest { 48 public: 49 GpuFeatureTest() : category_patterns_("test_gpu") {} 50 51 virtual void SetUp() OVERRIDE { 52 content::SetContentClient(&content_client_); 53 } 54 55 virtual void TearDown() OVERRIDE { 56 content::SetContentClient(NULL); 57 } 58 59 virtual void SetUpInProcessBrowserTestFixture() OVERRIDE { 60 base::FilePath test_dir; 61 ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &test_dir)); 62 gpu_test_dir_ = test_dir.AppendASCII("gpu"); 63 } 64 65 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE { 66 command_line->AppendSwitch(switches::kDisablePopupBlocking); 67 command_line->AppendSwitchASCII(switches::kWindowSize, "400,300"); 68 } 69 70 void SetupBlacklist(const std::string& json_blacklist) { 71 gpu::GPUInfo gpu_info; 72 GpuDataManager::GetInstance()->InitializeForTesting( 73 json_blacklist, gpu_info); 74 } 75 76 // If expected_reply is NULL, we don't check the reply content. 77 void RunTest(const base::FilePath& url, 78 const char* expected_reply, 79 bool new_tab) { 80 #if defined(OS_LINUX) && !defined(NDEBUG) 81 // Bypass tests on GPU Linux Debug bots. 82 if (gfx::GetGLImplementation() != gfx::kGLImplementationOSMesaGL) 83 return; 84 #endif 85 86 base::FilePath test_path; 87 test_path = gpu_test_dir_.Append(url); 88 ASSERT_TRUE(base::PathExists(test_path)) 89 << "Missing test file: " << test_path.value(); 90 91 content::DOMMessageQueue message_queue; 92 if (new_tab) { 93 ui_test_utils::NavigateToURLWithDisposition( 94 browser(), net::FilePathToFileURL(test_path), 95 NEW_FOREGROUND_TAB, ui_test_utils::BROWSER_TEST_NONE); 96 } else { 97 ui_test_utils::NavigateToURL( 98 browser(), net::FilePathToFileURL(test_path)); 99 } 100 101 std::string result; 102 // Wait for message indicating the test has finished running. 103 ASSERT_TRUE(message_queue.WaitForMessage(&result)); 104 if (expected_reply) 105 EXPECT_STREQ(expected_reply, result.c_str()); 106 } 107 108 // Open the URL and check the trace stream for the given event. 109 void RunEventTest(const base::FilePath& url, 110 const char* event_name = NULL, 111 bool event_expected = false) { 112 #if defined(OS_LINUX) && !defined(NDEBUG) 113 // Bypass tests on GPU Linux Debug bots. 114 if (gfx::GetGLImplementation() != gfx::kGLImplementationOSMesaGL) 115 return; 116 #endif 117 118 ASSERT_TRUE(tracing::BeginTracing(category_patterns_)); 119 120 // Have to use a new tab for the blacklist to work. 121 RunTest(url, NULL, true); 122 123 ASSERT_TRUE(tracing::EndTracing(&trace_events_json_)); 124 125 analyzer_.reset(TraceAnalyzer::Create(trace_events_json_)); 126 analyzer_->AssociateBeginEndEvents(); 127 TraceEventVector events; 128 129 if (!event_name) 130 return; 131 132 size_t event_count = 133 analyzer_->FindEvents(Query::EventNameIs(event_name), &events); 134 135 if (event_expected) 136 EXPECT_GT(event_count, 0U); 137 else 138 EXPECT_EQ(event_count, 0U); 139 } 140 141 // Trigger a resize of the chrome window, and use tracing to wait for the 142 // given |wait_event|. 143 bool ResizeAndWait(const gfx::Rect& new_bounds, 144 const char* category_patterns, 145 const char* wait_category, 146 const char* wait_event) { 147 if (!tracing::BeginTracingWithWatch(category_patterns, wait_category, 148 wait_event, 1)) 149 return false; 150 browser()->window()->SetBounds(new_bounds); 151 if (!tracing::WaitForWatchEvent(base::TimeDelta())) 152 return false; 153 if (!tracing::EndTracing(&trace_events_json_)) 154 return false; 155 analyzer_.reset(TraceAnalyzer::Create(trace_events_json_)); 156 analyzer_->AssociateBeginEndEvents(); 157 return true; 158 } 159 160 protected: 161 base::FilePath gpu_test_dir_; 162 scoped_ptr<TraceAnalyzer> analyzer_; 163 std::string category_patterns_; 164 std::string trace_events_json_; 165 FakeContentClient content_client_; 166 }; 167 168 class GpuFeaturePixelTest : public GpuFeatureTest { 169 protected: 170 virtual void SetUp() OVERRIDE { 171 EnablePixelOutput(); 172 GpuFeatureTest::SetUp(); 173 } 174 }; 175 176 class GpuCompositingBlockedTest : public GpuFeatureTest { 177 public: 178 virtual void SetUpInProcessBrowserTestFixture() OVERRIDE { 179 GpuFeatureTest::SetUpInProcessBrowserTestFixture(); 180 const std::string json_blacklist = 181 "{\n" 182 " \"name\": \"gpu blacklist\",\n" 183 " \"version\": \"1.0\",\n" 184 " \"entries\": [\n" 185 " {\n" 186 " \"id\": 1,\n" 187 " \"features\": [\n" 188 " \"gpu_compositing\"\n" 189 " ]\n" 190 " }\n" 191 " ]\n" 192 "}"; 193 SetupBlacklist(json_blacklist); 194 } 195 }; 196 197 IN_PROC_BROWSER_TEST_F(GpuCompositingBlockedTest, GpuCompositingBlocked) { 198 EXPECT_TRUE(GpuDataManager::GetInstance()->IsFeatureBlacklisted( 199 gpu::GPU_FEATURE_TYPE_GPU_COMPOSITING)); 200 } 201 202 IN_PROC_BROWSER_TEST_F(GpuFeatureTest, WebGLAllowed) { 203 EXPECT_FALSE(GpuDataManager::GetInstance()->IsFeatureBlacklisted( 204 gpu::GPU_FEATURE_TYPE_WEBGL)); 205 206 // The below times out: http://crbug.com/166060 207 #if 0 208 const base::FilePath url(FILE_PATH_LITERAL("feature_webgl.html")); 209 RunEventTest(url, kWebGLCreationEvent, true); 210 #endif 211 } 212 213 IN_PROC_BROWSER_TEST_F(GpuFeatureTest, WebGLBlocked) { 214 const std::string json_blacklist = 215 "{\n" 216 " \"name\": \"gpu blacklist\",\n" 217 " \"version\": \"1.0\",\n" 218 " \"entries\": [\n" 219 " {\n" 220 " \"id\": 1,\n" 221 " \"features\": [\n" 222 " \"webgl\"\n" 223 " ]\n" 224 " }\n" 225 " ]\n" 226 "}"; 227 SetupBlacklist(json_blacklist); 228 EXPECT_TRUE(GpuDataManager::GetInstance()->IsFeatureBlacklisted( 229 gpu::GPU_FEATURE_TYPE_WEBGL)); 230 231 const base::FilePath url(FILE_PATH_LITERAL("feature_webgl.html")); 232 RunEventTest(url, kWebGLCreationEvent, false); 233 } 234 235 class WebGLTest : public GpuFeatureTest { 236 public: 237 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE { 238 GpuFeatureTest::SetUpCommandLine(command_line); 239 #if !defined(OS_ANDROID) 240 // On Android, WebGL is disabled by default 241 command_line->AppendSwitch(switches::kDisableExperimentalWebGL); 242 #endif 243 } 244 }; 245 246 IN_PROC_BROWSER_TEST_F(WebGLTest, WebGLDisabled) { 247 const base::FilePath url(FILE_PATH_LITERAL("feature_webgl.html")); 248 RunEventTest(url, kWebGLCreationEvent, false); 249 } 250 251 // This test is oblivious to the fact that multisample could be blacklisted on 252 // some configurations. Previously disabled on GOOGLE_CHROME_BUILD and 253 // on OS_MACOSX: http://crbug.com/314745 254 IN_PROC_BROWSER_TEST_F(GpuFeatureTest, DISABLED_MultisamplingAllowed) { 255 if (gpu::GPUTestBotConfig::GpuBlacklistedOnBot()) 256 return; 257 // Multisampling is not supported if running on top of osmesa. 258 if (gfx::GetGLImplementation() == gfx::kGLImplementationOSMesaGL) 259 return; 260 // Linux Intel uses mesa driver, where multisampling is not supported. 261 // Multisampling is also not supported on virtualized mac os. 262 std::vector<std::string> configs; 263 configs.push_back("LINUX INTEL"); 264 configs.push_back("MAC VMWARE"); 265 if (gpu::GPUTestBotConfig::CurrentConfigMatches(configs)) 266 return; 267 268 const base::FilePath url(FILE_PATH_LITERAL("feature_multisampling.html")); 269 RunTest(url, "\"TRUE\"", true); 270 } 271 272 class WebGLMultisamplingTest : public GpuFeatureTest { 273 public: 274 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE { 275 GpuFeatureTest::SetUpCommandLine(command_line); 276 command_line->AppendSwitch("disable_multisampling"); 277 } 278 }; 279 280 IN_PROC_BROWSER_TEST_F(WebGLMultisamplingTest, MultisamplingDisabled) { 281 // Multisampling fails on virtualized mac os. 282 if (gpu::GPUTestBotConfig::CurrentConfigMatches("MAC VMWARE")) 283 return; 284 285 const base::FilePath url(FILE_PATH_LITERAL("feature_multisampling.html")); 286 RunTest(url, "\"FALSE\"", true); 287 } 288 289 IN_PROC_BROWSER_TEST_F(GpuFeatureTest, Canvas2DAllowed) { 290 // Accelerated canvas 2D is not supported on XP. 291 if (gpu::GPUTestBotConfig::CurrentConfigMatches("XP")) 292 return; 293 294 enum Canvas2DState { 295 ENABLED, 296 BLACKLISTED, // Disabled via the blacklist. 297 DISABLED, // Not disabled via the blacklist, but expected to be disabled 298 // by configuration. 299 } expected_state = ENABLED; 300 #if defined(OS_LINUX) && !defined(OS_CHROMEOS) 301 // Blacklist rule #24 disables accelerated_2d_canvas on Linux. 302 expected_state = BLACKLISTED; 303 #elif defined(OS_WIN) 304 // Blacklist rule #67 disables accelerated_2d_canvas on XP. 305 if (base::win::GetVersion() < base::win::VERSION_VISTA) 306 expected_state = BLACKLISTED; 307 #endif 308 309 if (gpu::GPUTestBotConfig::GpuBlacklistedOnBot()) 310 expected_state = BLACKLISTED; 311 312 #if defined(USE_AURA) 313 // Canvas 2D is always disabled in software compositing mode, make sure it is 314 // marked as such if it wasn't blacklisted already. 315 if (expected_state == ENABLED && 316 !content::GpuDataManager::GetInstance()->CanUseGpuBrowserCompositor()) { 317 expected_state = DISABLED; 318 } 319 #endif 320 321 EXPECT_EQ(expected_state == BLACKLISTED, 322 GpuDataManager::GetInstance()->IsFeatureBlacklisted( 323 gpu::GPU_FEATURE_TYPE_ACCELERATED_2D_CANVAS)); 324 325 const base::FilePath url(FILE_PATH_LITERAL("feature_canvas2d.html")); 326 RunEventTest(url, kAcceleratedCanvasCreationEvent, expected_state == ENABLED); 327 } 328 329 IN_PROC_BROWSER_TEST_F(GpuFeatureTest, Canvas2DBlocked) { 330 const std::string json_blacklist = 331 "{\n" 332 " \"name\": \"gpu blacklist\",\n" 333 " \"version\": \"1.0\",\n" 334 " \"entries\": [\n" 335 " {\n" 336 " \"id\": 1,\n" 337 " \"features\": [\n" 338 " \"accelerated_2d_canvas\"\n" 339 " ]\n" 340 " }\n" 341 " ]\n" 342 "}"; 343 SetupBlacklist(json_blacklist); 344 EXPECT_TRUE(GpuDataManager::GetInstance()->IsFeatureBlacklisted( 345 gpu::GPU_FEATURE_TYPE_ACCELERATED_2D_CANVAS)); 346 347 const base::FilePath url(FILE_PATH_LITERAL("feature_canvas2d.html")); 348 RunEventTest(url, kAcceleratedCanvasCreationEvent, false); 349 } 350 351 class Canvas2DDisabledTest : public GpuFeatureTest { 352 public: 353 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE { 354 GpuFeatureTest::SetUpCommandLine(command_line); 355 command_line->AppendSwitch(switches::kDisableAccelerated2dCanvas); 356 } 357 }; 358 359 IN_PROC_BROWSER_TEST_F(Canvas2DDisabledTest, Canvas2DDisabled) { 360 const base::FilePath url(FILE_PATH_LITERAL("feature_canvas2d.html")); 361 RunEventTest(url, kAcceleratedCanvasCreationEvent, false); 362 } 363 364 IN_PROC_BROWSER_TEST_F(GpuFeaturePixelTest, 365 CanOpenPopupAndRenderWithWebGLCanvas) { 366 if (gpu::GPUTestBotConfig::GpuBlacklistedOnBot()) 367 return; 368 369 const base::FilePath url(FILE_PATH_LITERAL("webgl_popup.html")); 370 RunTest(url, "\"SUCCESS\"", false); 371 } 372 373 // crbug.com/176466 374 IN_PROC_BROWSER_TEST_F(GpuFeatureTest, 375 DISABLED_CanOpenPopupAndRenderWith2DCanvas) { 376 const base::FilePath url(FILE_PATH_LITERAL("canvas_popup.html")); 377 RunTest(url, "\"SUCCESS\"", false); 378 } 379 380 #if defined(OS_WIN) || defined(OS_CHROMEOS) || defined(OS_MACOSX) 381 // http://crbug.com/162343: flaky on Windows and Mac, failing on ChromiumOS. 382 #define MAYBE_RafNoDamage DISABLED_RafNoDamage 383 #else 384 #define MAYBE_RafNoDamage RafNoDamage 385 #endif 386 IN_PROC_BROWSER_TEST_F(GpuFeatureTest, MAYBE_RafNoDamage) { 387 category_patterns_ = "-test_*"; 388 const base::FilePath url(FILE_PATH_LITERAL("feature_raf_no_damage.html")); 389 RunEventTest(url); 390 391 if (!analyzer_.get()) 392 return; 393 394 // Search for matching name on begin event or async_begin event (any begin). 395 Query query_raf = 396 (Query::EventPhaseIs(TRACE_EVENT_PHASE_BEGIN) || 397 Query::EventPhaseIs(TRACE_EVENT_PHASE_ASYNC_BEGIN)) && 398 Query::EventNameIs("___RafWithNoDamage___"); 399 TraceEventVector events; 400 size_t num_events = analyzer_->FindEvents(query_raf, &events); 401 402 trace_analyzer::RateStats stats; 403 trace_analyzer::RateStatsOptions stats_options; 404 stats_options.trim_min = stats_options.trim_max = num_events / 10; 405 EXPECT_TRUE(trace_analyzer::GetRateStats(events, &stats, &stats_options)); 406 407 LOG(INFO) << "Number of RAFs: " << num_events << 408 " Mean: " << stats.mean_us << 409 " Min: " << stats.min_us << 410 " Max: " << stats.max_us << 411 " StdDev: " << stats.standard_deviation_us; 412 413 // Expect that the average time between RAFs is more than 15ms. That will 414 // indicate that the renderer is not simply spinning on RAF. 415 EXPECT_GT(stats.mean_us, 15000.0); 416 417 // Print out the trace events upon error to debug failures. 418 if (stats.mean_us <= 15000.0) { 419 fprintf(stderr, "\n\nTRACE JSON:\n\n%s\n\n", trace_events_json_.c_str()); 420 } 421 } 422 423 #if defined(OS_MACOSX) 424 IN_PROC_BROWSER_TEST_F(GpuFeatureTest, IOSurfaceReuse) { 425 if (gpu::GPUTestBotConfig::GpuBlacklistedOnBot()) 426 return; 427 428 const base::FilePath url( 429 FILE_PATH_LITERAL("feature_compositing_static.html")); 430 base::FilePath test_path = gpu_test_dir_.Append(url); 431 ASSERT_TRUE(base::PathExists(test_path)) 432 << "Missing test file: " << test_path.value(); 433 434 ui_test_utils::NavigateToURL(browser(), net::FilePathToFileURL(test_path)); 435 436 LOG(INFO) << "did navigate"; 437 gfx::Rect bounds = browser()->window()->GetBounds(); 438 gfx::Rect new_bounds = bounds; 439 440 const char* create_event = "IOSurfaceImageTransportSurface::CreateIOSurface"; 441 const char* resize_event = "IOSurfaceImageTransportSurface::OnResize"; 442 const char* draw_event = "CompositingIOSurfaceMac::DrawIOSurface"; 443 Query find_creates = Query::MatchBeginName(create_event); 444 Query find_resizes = Query::MatchBeginName(resize_event) && 445 Query::EventHasNumberArg("old_width") && 446 Query::EventHasNumberArg("new_width"); 447 Query find_draws = Query::MatchBeginName(draw_event) && 448 Query::EventHasNumberArg("scale"); 449 450 const int roundup = 64; 451 // A few resize values assuming a roundup of 64 pixels. The test will resize 452 // by these values one at a time and verify that CreateIOSurface only happens 453 // when the rounded width changes. 454 int offsets[] = { 1, roundup - 1, roundup, roundup + 1, 2*roundup}; 455 int num_offsets = static_cast<int>(arraysize(offsets)); 456 int w_start = bounds.width(); 457 458 for (int offset_i = 0; offset_i < num_offsets; ++offset_i) { 459 new_bounds.set_width(w_start + offsets[offset_i]); 460 LOG(INFO) << "before wait"; 461 ASSERT_TRUE(ResizeAndWait(new_bounds, "gpu", "gpu", resize_event)); 462 LOG(INFO) << "after wait"; 463 464 TraceEventVector resize_events; 465 analyzer_->FindEvents(find_resizes, &resize_events); 466 LOG(INFO) << "num rezize events = " << resize_events.size(); 467 for (size_t resize_i = 0; resize_i < resize_events.size(); ++resize_i) { 468 const trace_analyzer::TraceEvent* resize = resize_events[resize_i]; 469 // Was a create allowed: 470 int old_width = resize->GetKnownArgAsInt("old_width"); 471 int new_width = resize->GetKnownArgAsInt("new_width"); 472 bool expect_create = (old_width/roundup != new_width/roundup || 473 old_width == 0); 474 int expected_creates = expect_create ? 1 : 0; 475 476 // Find the create event inside this resize event (if any). This will 477 // determine if the resize triggered a reallocation of the IOSurface. 478 double begin_time = resize->timestamp; 479 double end_time = begin_time + resize->GetAbsTimeToOtherEvent(); 480 Query find_this_create = find_creates && 481 Query::EventTime() >= Query::Double(begin_time) && 482 Query::EventTime() <= Query::Double(end_time); 483 TraceEventVector create_events; 484 int num_creates = static_cast<int>(analyzer_->FindEvents(find_this_create, 485 &create_events)); 486 EXPECT_EQ(expected_creates, num_creates); 487 488 // For debugging failures, print out the width and height of each resize: 489 LOG(INFO) << 490 base::StringPrintf( 491 "%d (resize offset %d): IOSurface width %d -> %d; Creates %d " 492 "Expected %d", offset_i, offsets[offset_i], 493 old_width, new_width, num_creates, expected_creates); 494 } 495 } 496 LOG(INFO) << "finished test"; 497 } 498 #endif 499 500 } // namespace 501