OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2011 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/string_number_conversions.h" |
| 10 #include "base/stringprintf.h" |
| 11 #include "base/test/test_switches.h" |
| 12 #include "base/test/trace_event_analyzer.h" |
| 13 #include "base/threading/platform_thread.h" |
| 14 #include "base/timer.h" |
| 15 #include "base/version.h" |
| 16 #include "chrome/browser/ui/browser.h" |
| 17 #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h" |
| 18 #include "chrome/common/chrome_paths.h" |
| 19 #include "chrome/common/chrome_switches.h" |
| 20 #include "chrome/test/base/tracing.h" |
| 21 #include "chrome/test/base/ui_test_utils.h" |
| 22 #include "chrome/test/perf/browser_perf_test.h" |
| 23 #include "chrome/test/perf/perf_test.h" |
| 24 #include "content/browser/renderer_host/render_view_host.h" |
| 25 #include "content/browser/tab_contents/tab_contents.h" |
| 26 #include "content/public/common/content_switches.h" |
| 27 #include "third_party/WebKit/Source/WebKit/chromium/public/WebInputEvent.h" |
| 28 #include "testing/gtest/include/gtest/gtest.h" |
| 29 |
| 30 // Run with --vmodule=latency_tests=1 to print verbose latency info. |
| 31 |
| 32 // How is latency measured? |
| 33 // |
| 34 // The test injects mouse moves many times per frame from the browser via |
| 35 // RenderWidgetHost. Each input has a unique x coordinate. When the javascript |
| 36 // handler receives the input, it stores the coordinate for later use in the |
| 37 // requestAnimationFrame callback. In RAF, the test paints using the x |
| 38 // coordinate as a color (in software, it sets the color of a table; in webgl, |
| 39 // it executes a glClearColor). Trace events emit the color when it is picked up |
| 40 // by either UpdateRect for software or gles2_cmd_decoder/glClear for webgl. |
| 41 // |
| 42 // Each UpdateRect (software) or SwapBuffers (webgl) is considered to be a frame |
| 43 // boundary that will be used to measure latency in number of frames. Starting |
| 44 // from a frame boundary Y, the test first determines what mouse x coordinate |
| 45 // was represented by the color at that frame boundary. Then, the test walks |
| 46 // backward through the trace events to find the input event matching that |
| 47 // x coordinate. Then, the test find the nearest frame boundary X to the input |
| 48 // event (may be before or after). The number of frame boundaries is then |
| 49 // counted between X and Y to determine the input latency. |
| 50 // |
| 51 // By injecting mouse moves many times per frame, we reduce flakiness in the |
| 52 // finding of the nearest frame boundary. |
| 53 // |
| 54 // This test only measures the latency introduced by chrome code -- it does not |
| 55 // measure latency introduced by mouse drivers or the GL driver or the OS window |
| 56 // manager. The actual latency seen by a user is more than what is reported by |
| 57 // this test. |
| 58 // |
| 59 // Current modes: |
| 60 // - Software RAF |
| 61 // - WebGL RAF |
| 62 |
| 63 namespace { |
| 64 |
| 65 using namespace trace_analyzer; |
| 66 |
| 67 enum LatencyTestMode { |
| 68 kWebGL, |
| 69 kSoftware |
| 70 }; |
| 71 |
| 72 enum LatencyTestFlags { |
| 73 kInputHeavy = 1 << 0, |
| 74 kInputDirty = 1 << 1, |
| 75 kRafHeavy = 1 << 2, |
| 76 kPaintHeavy = 1 << 3 |
| 77 }; |
| 78 |
| 79 const int kWebGLCanvasWidth = 10; |
| 80 const int kNumFrames = 80; |
| 81 const int kInputsPerFrame = 16; |
| 82 // Magic number to identify certain glClear events. |
| 83 const int kClearColorGreen = 137; |
| 84 const int kMouseY = 5; |
| 85 |
| 86 // Don't analyze begin frames that may be inaccurate. Latencies can be as high |
| 87 // as 5 frames or so, so skip the first 6 frames to get more accurate results. |
| 88 const int kIgnoreBeginFrames = 6; |
| 89 // Don't analyze end frames that may be inaccurate. |
| 90 const int kIgnoreEndFrames = 4; |
| 91 // Minimum frames to produce an answer. |
| 92 const int kMinimumFramesForAnalysis = 5; |
| 93 |
| 94 class LatencyTest |
| 95 : public BrowserPerfTest, |
| 96 public ::testing::WithParamInterface<int> { |
| 97 public: |
| 98 LatencyTest() : |
| 99 query_instant_(Query(EVENT_PHASE) == |
| 100 Query::Phase(TRACE_EVENT_PHASE_INSTANT)), |
| 101 // These queries are initialized in RunTest. |
| 102 query_swaps_(Query::Bool(false)), |
| 103 query_inputs_(Query::Bool(false)), |
| 104 query_blits_(Query::Bool(false)), |
| 105 query_clears_(Query::Bool(false)), |
| 106 mouse_x_(0), |
| 107 tab_width_(0), |
| 108 mode_(kWebGL), |
| 109 delay_time_us_(0), |
| 110 num_frames_(0), |
| 111 verbose_(false), |
| 112 test_flags_(0) {} |
| 113 |
| 114 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE; |
| 115 |
| 116 std::vector<int> GetAllBehaviors(); |
| 117 |
| 118 // Run test with specified |mode| and |behaviors|. |
| 119 // |mode| can be webgl or software. |
| 120 // |behaviors| is a list of combinations of LatencyTestFlags. |
| 121 void RunTest(LatencyTestMode mode, const std::vector<int>& behaviors); |
| 122 |
| 123 private: |
| 124 void RunTestInternal(const std::string& test_url, |
| 125 bool send_inputs, |
| 126 int input_delay_us); |
| 127 |
| 128 double CalculateLatency(); |
| 129 |
| 130 std::string GetModeString() { |
| 131 switch (mode_) { |
| 132 case kWebGL: |
| 133 return "webgl"; |
| 134 case kSoftware: |
| 135 return "software"; |
| 136 default: |
| 137 NOTREACHED() << "invalid mode"; |
| 138 return ""; |
| 139 } |
| 140 } |
| 141 |
| 142 std::string GetTraceName(int flags); |
| 143 |
| 144 std::string GetUrlModeString(int flags); |
| 145 |
| 146 std::string GetUrl(int flags); |
| 147 |
| 148 void GetMeanFrameTimeMicros(int* frame_time) const; |
| 149 |
| 150 void SendInput(); |
| 151 |
| 152 void PrintEvents(const TraceEventVector& events); |
| 153 |
| 154 // Path to html file. |
| 155 FilePath test_path_; |
| 156 |
| 157 // Query INSTANT events. |
| 158 Query query_instant_; |
| 159 |
| 160 // Query "swaps" which is SwapBuffers for GL and UpdateRect for software. |
| 161 Query query_swaps_; |
| 162 |
| 163 // Query mouse input entry events in browser process (ForwardMouseEvent). |
| 164 Query query_inputs_; |
| 165 |
| 166 // Query GL blits for the WebGL canvas -- represents the compositor consuming |
| 167 // the WebGL contents for display. |
| 168 Query query_blits_; |
| 169 |
| 170 // Query glClear calls with mouse coordinate as clear color. |
| 171 Query query_clears_; |
| 172 |
| 173 // For searching trace data. |
| 174 scoped_ptr<TraceAnalyzer> analyzer_; |
| 175 |
| 176 // Current mouse x coordinate for injecting events. |
| 177 int mouse_x_; |
| 178 |
| 179 // Width of window containing our tab. |
| 180 int tab_width_; |
| 181 |
| 182 // Timer for injecting mouse events periodically. |
| 183 base::RepeatingTimer<LatencyTest> timer_; |
| 184 |
| 185 // Mode: webgl or software. |
| 186 LatencyTestMode mode_; |
| 187 |
| 188 // Delay time for javascript test code. Typically 2 x frame duration. Used |
| 189 // to spin-wait in the javascript input handler and requestAnimationFrame. |
| 190 int delay_time_us_; |
| 191 |
| 192 // Number of frames to render from the html test code. |
| 193 int num_frames_; |
| 194 |
| 195 // Map from test flags combination to the calculated mean latency. |
| 196 std::map<int, double> latencies_; |
| 197 |
| 198 // Whether to print more verbose output. |
| 199 bool verbose_; |
| 200 |
| 201 // Current test flags combination, determining the behavior of the test. |
| 202 int test_flags_; |
| 203 }; |
| 204 |
| 205 void LatencyTest::SetUpCommandLine(CommandLine* command_line) { |
| 206 BrowserPerfTest::SetUpCommandLine(command_line); |
| 207 // This enables DOM automation for tab contents. |
| 208 EnableDOMAutomation(); |
| 209 if (CommandLine::ForCurrentProcess()-> |
| 210 HasSwitch(switches::kEnableThreadedCompositing)) { |
| 211 command_line->AppendSwitch(switches::kEnableThreadedCompositing); |
| 212 } |
| 213 // Default behavior is to thumbnail the tab after 0.5 seconds, causing |
| 214 // a nasty frame hitch and disturbing the latency test. Fix that: |
| 215 command_line->AppendSwitch(switches::kEnableInBrowserThumbnailing); |
| 216 command_line->AppendSwitch(switches::kDisableBackgroundNetworking); |
| 217 } |
| 218 |
| 219 std::vector<int> LatencyTest::GetAllBehaviors() { |
| 220 std::vector<int> behaviors; |
| 221 int max_behaviors = kInputHeavy | kInputDirty | kRafHeavy | kPaintHeavy; |
| 222 for (int i = 0; i <= max_behaviors; ++i) |
| 223 behaviors.push_back(i); |
| 224 return behaviors; |
| 225 } |
| 226 |
| 227 void LatencyTest::RunTest(LatencyTestMode mode, |
| 228 const std::vector<int>& behaviors) { |
| 229 mode_ = mode; |
| 230 verbose_ = (logging::GetVlogLevel("latency_tests") > 0); |
| 231 |
| 232 // Construct queries for searching trace events via TraceAnalyzer. |
| 233 if (mode_ == kWebGL) { |
| 234 query_swaps_ = query_instant_ && |
| 235 Query(EVENT_NAME) == Query::String("SwapBuffers") && |
| 236 Query(EVENT_ARG, "width") != Query::Int(kWebGLCanvasWidth); |
| 237 } else if (mode_ == kSoftware) { |
| 238 // Software updates need to have x=0 and y=0 to contain the input color. |
| 239 query_swaps_ = query_instant_ && |
| 240 Query(EVENT_NAME) == Query::String("UpdateRect") && |
| 241 Query(EVENT_ARG, "x+y") == Query::Int(0); |
| 242 } |
| 243 query_inputs_ = query_instant_ && |
| 244 Query(EVENT_NAME) == Query::String("MouseEventBegin"); |
| 245 query_blits_ = query_instant_ && |
| 246 Query(EVENT_NAME) == Query::String("DoBlit") && |
| 247 Query(EVENT_ARG, "width") == Query::Int(kWebGLCanvasWidth); |
| 248 query_clears_ = query_instant_ && |
| 249 Query(EVENT_NAME) == Query::String("DoClear") && |
| 250 Query(EVENT_ARG, "green") == Query::Int(kClearColorGreen); |
| 251 Query query_width_swaps = query_swaps_; |
| 252 if (mode_ == kSoftware) { |
| 253 query_width_swaps = query_instant_ && |
| 254 Query(EVENT_NAME) == Query::String("UpdateRectWidth") && |
| 255 Query(EVENT_ARG, "width") > Query::Int(kWebGLCanvasWidth); |
| 256 } |
| 257 |
| 258 // Set path to test html. |
| 259 ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &test_path_)); |
| 260 test_path_ = test_path_.Append(FILE_PATH_LITERAL("perf")); |
| 261 test_path_ = test_path_.Append(FILE_PATH_LITERAL("latency_suite.html")); |
| 262 ASSERT_TRUE(file_util::PathExists(test_path_)) |
| 263 << "Missing test file: " << test_path_.value(); |
| 264 |
| 265 // Run once with defaults to measure the frame times. |
| 266 delay_time_us_ = 0; |
| 267 // kNumFrames may be very high, but we only need a few frames to measure |
| 268 // average frame times. |
| 269 num_frames_ = 30; |
| 270 int initial_flags = 0; |
| 271 if (mode_ == kSoftware) { |
| 272 // For the first run, run software with kPaintHeavy (which toggles the |
| 273 // background color every frame) to force an update each RAF. Otherwise it |
| 274 // won't trigger an UpdateRect each frame and we won't be able to measure |
| 275 // framerate, because there are no inputs during the first run. |
| 276 initial_flags = static_cast<int>(kPaintHeavy); |
| 277 } |
| 278 RunTestInternal(GetUrl(initial_flags), false, 0); |
| 279 |
| 280 // Get width of tab so that we know the limit of x coordinates for the |
| 281 // injected mouse inputs. |
| 282 const TraceEvent* swap_event = analyzer_->FindOneEvent(query_width_swaps); |
| 283 ASSERT_TRUE(swap_event); |
| 284 tab_width_ = swap_event->GetKnownArgAsInt("width"); |
| 285 // Keep printf output clean by limiting input coords to three digits: |
| 286 tab_width_ = (tab_width_ < 1000) ? tab_width_ : 999; |
| 287 // Sanity check the tab_width -- it should be more than 100 pixels. |
| 288 EXPECT_GT(tab_width_, 100); |
| 289 |
| 290 int mean_frame_time_us = 0; |
| 291 GetMeanFrameTimeMicros(&mean_frame_time_us); |
| 292 if (verbose_) |
| 293 printf("Mean frame time micros = %d\n", mean_frame_time_us); |
| 294 // Delay time is 2x the average frame time. |
| 295 delay_time_us_ = 2 * mean_frame_time_us; |
| 296 // Calculate delay time between inputs based on the measured frame time. |
| 297 // This prevents flooding the browser with more events than we need if the |
| 298 // test is running very slow (such as on a VM). |
| 299 int delay_us = mean_frame_time_us / kInputsPerFrame; |
| 300 |
| 301 // Reset num_frames_ for actual test runs. |
| 302 num_frames_ = kNumFrames; |
| 303 |
| 304 // Run input latency test with each requested behavior. |
| 305 for (size_t i = 0; i < behaviors.size(); ++i) { |
| 306 test_flags_ = behaviors[i]; |
| 307 std::string url = GetUrl(test_flags_); |
| 308 printf("=============================================================\n"); |
| 309 if (verbose_) |
| 310 printf("Mode: %s\n", GetUrlModeString(i).c_str()); |
| 311 printf("URL: %s\n", url.c_str()); |
| 312 |
| 313 // Do the actual test with input events. |
| 314 RunTestInternal(url, true, delay_us); |
| 315 latencies_[test_flags_] = CalculateLatency(); |
| 316 } |
| 317 |
| 318 // Print summary if more than 1 behavior was tested in this run. This is only |
| 319 // for manual test runs for human reabable results, not for perf bots. |
| 320 if (behaviors.size() > 1) { |
| 321 printf("#############################################################\n"); |
| 322 printf("## %s\n", GetModeString().c_str()); |
| 323 if (verbose_) { |
| 324 printf("Latency, behavior:\n"); |
| 325 for (size_t i = 0; i < behaviors.size(); ++i) { |
| 326 printf("%.1f, %s%s%s%s\n", latencies_[behaviors[i]], |
| 327 (i & kInputHeavy) ? "InputHeavy " : "", |
| 328 (i & kInputDirty) ? "InputDirty " : "", |
| 329 (i & kRafHeavy) ? "RafHeavy " : "", |
| 330 (i & kPaintHeavy) ? "PaintHeavy " : ""); |
| 331 } |
| 332 } |
| 333 printf("Latencies for tests: "); |
| 334 for (size_t i = 0; i < behaviors.size(); ++i) { |
| 335 printf("%.1f%s", latencies_[behaviors[i]], |
| 336 (i < behaviors.size() - 1) ? ", " : ""); |
| 337 } |
| 338 printf("\n"); |
| 339 printf("#############################################################\n"); |
| 340 } |
| 341 } |
| 342 |
| 343 void LatencyTest::RunTestInternal(const std::string& test_url, |
| 344 bool send_inputs, |
| 345 int input_delay_us) { |
| 346 mouse_x_ = 0; |
| 347 |
| 348 ASSERT_TRUE(tracing::BeginTracing("test_gpu,test_latency")); |
| 349 |
| 350 ui_test_utils::NavigateToURLWithDisposition( |
| 351 browser(), GURL(test_url), CURRENT_TAB, |
| 352 ui_test_utils::BROWSER_TEST_NONE); |
| 353 |
| 354 // Start sending mouse inputs. |
| 355 if (send_inputs) { |
| 356 // Round input_delay_us down to nearest milliseconds. The rounding in timer |
| 357 // code rounds up from us to ms, so we need to do our own rounding here. |
| 358 int input_delay_ms = input_delay_us / 1000; |
| 359 input_delay_ms = (input_delay_ms <= 0) ? 1 : input_delay_ms; |
| 360 timer_.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(input_delay_ms), |
| 361 this, &LatencyTest::SendInput); |
| 362 } |
| 363 |
| 364 // Wait for message indicating the test has finished running. |
| 365 ui_test_utils::DOMMessageQueue message_queue; |
| 366 ASSERT_TRUE(message_queue.WaitForMessage(NULL)); |
| 367 |
| 368 timer_.Stop(); |
| 369 |
| 370 std::string json_events; |
| 371 ASSERT_TRUE(tracing::EndTracing(&json_events)); |
| 372 |
| 373 analyzer_.reset(TraceAnalyzer::Create(json_events)); |
| 374 analyzer_->AssociateBeginEndEvents(); |
| 375 analyzer_->MergeAssociatedEventArgs(); |
| 376 } |
| 377 |
| 378 double LatencyTest::CalculateLatency() { |
| 379 TraceEventVector events; |
| 380 if (mode_ == kWebGL) { |
| 381 // Search for three types of events in WebGL mode: |
| 382 // - onscreen swaps. |
| 383 // - DoClear calls that contain the mouse x coordinate. |
| 384 // - mouse events. |
| 385 analyzer_->FindEvents(query_swaps_ || query_inputs_ || |
| 386 query_blits_ || query_clears_, &events); |
| 387 } else if (mode_ == kSoftware) { |
| 388 analyzer_->FindEvents(query_swaps_ || query_inputs_, &events); |
| 389 } else { |
| 390 NOTREACHED() << "invalid mode"; |
| 391 } |
| 392 |
| 393 if (verbose_) |
| 394 PrintEvents(events); |
| 395 |
| 396 int swap_count = 0; |
| 397 size_t previous_blit_pos = 0; |
| 398 swap_count = 0; |
| 399 std::vector<int> latencies; |
| 400 printf("Measured latency (in number of frames) for each frame:\n"); |
| 401 for (size_t i = 0; i < events.size(); ++i) { |
| 402 if (query_swaps_.Evaluate(*events[i])) { |
| 403 // Compositor context swap buffers. |
| 404 ++swap_count; |
| 405 // Don't analyze first few swaps, because they are filling the rendering |
| 406 // pipeline and may be unstable. |
| 407 if (swap_count > kIgnoreBeginFrames) { |
| 408 int mouse_x = 0; |
| 409 if (mode_ == kWebGL) { |
| 410 // Trace backwards through the events to find the input event that |
| 411 // matches the glClear that was presented by this SwapBuffers. |
| 412 |
| 413 // Step 1: Find the last blit (which will be the WebGL blit). |
| 414 size_t blit_pos = 0; |
| 415 EXPECT_TRUE(FindLastOf(events, query_blits_, i, &blit_pos)); |
| 416 // Skip this SwapBuffers if the blit has already been consumed by a |
| 417 // previous SwapBuffers. This means the current frame did not receive |
| 418 // an update from WebGL. |
| 419 EXPECT_GT(blit_pos, previous_blit_pos); |
| 420 if (blit_pos == previous_blit_pos) { |
| 421 if (verbose_) |
| 422 printf(" %03d: ERROR\n", swap_count); |
| 423 else |
| 424 printf(" ERROR"); |
| 425 continue; |
| 426 } |
| 427 previous_blit_pos = blit_pos; |
| 428 |
| 429 // Step 2: find the last clear from the WebGL blit. This will be the |
| 430 // value of the latest mouse input that has affected this swap. |
| 431 size_t clear_pos = 0; |
| 432 EXPECT_TRUE(FindLastOf(events, query_clears_, blit_pos, &clear_pos)); |
| 433 mouse_x = events[clear_pos]->GetKnownArgAsInt("red"); |
| 434 } else if (mode_ == kSoftware) { |
| 435 // The software path gets the mouse_x directly from the DIB colors. |
| 436 mouse_x = events[i]->GetKnownArgAsInt("color"); |
| 437 } |
| 438 |
| 439 // Find the corresponding mouse input. |
| 440 size_t input_pos = 0; |
| 441 Query query_mouse_event = query_inputs_ && |
| 442 Query(EVENT_ARG, "x") == Query::Int(mouse_x); |
| 443 EXPECT_TRUE(FindLastOf(events, query_mouse_event, i, &input_pos)); |
| 444 |
| 445 // Step 4: Find the nearest onscreen SwapBuffers to this input event. |
| 446 size_t closest_swap = 0; |
| 447 size_t second_closest_swap = 0; |
| 448 EXPECT_TRUE(FindClosest(events, query_swaps_, input_pos, |
| 449 &closest_swap, &second_closest_swap)); |
| 450 int latency = CountMatches(events, query_swaps_, closest_swap, i); |
| 451 latencies.push_back(latency); |
| 452 if (verbose_) |
| 453 printf(" %03d: %d\n", swap_count, latency); |
| 454 else |
| 455 printf(" %d", latency); |
| 456 } |
| 457 } |
| 458 } |
| 459 printf("\n"); |
| 460 |
| 461 size_t ignoreEndFrames = static_cast<size_t>(kIgnoreEndFrames); |
| 462 bool haveEnoughFrames = latencies.size() > |
| 463 ignoreEndFrames + static_cast<size_t>(kMinimumFramesForAnalysis); |
| 464 EXPECT_TRUE(haveEnoughFrames); |
| 465 if (!haveEnoughFrames) |
| 466 return 0.0; |
| 467 |
| 468 double mean_latency = 0.0; |
| 469 // Skip last few frames, because they may be unreliable. |
| 470 size_t num_consider = latencies.size() - ignoreEndFrames; |
| 471 for (size_t i = 0; i < num_consider; ++i) |
| 472 mean_latency += static_cast<double>(latencies[i]); |
| 473 mean_latency /= static_cast<double>(num_consider); |
| 474 printf("Mean latency = %f\n", mean_latency); |
| 475 |
| 476 double mean_error = 0.0; |
| 477 for (size_t i = 0; i < num_consider; ++i) { |
| 478 double offset = fabs(mean_latency - static_cast<double>(latencies[i])); |
| 479 mean_error = (offset > mean_error) ? offset : mean_error; |
| 480 } |
| 481 |
| 482 std::string trace_name = GetTraceName(test_flags_); |
| 483 std::string mean_and_error = base::StringPrintf("%f,%f", mean_latency, |
| 484 mean_error); |
| 485 perf_test::PrintResultMeanAndError(GetModeString(), "", trace_name, |
| 486 mean_and_error, "frames", true); |
| 487 return mean_latency; |
| 488 } |
| 489 |
| 490 std::string LatencyTest::GetTraceName(int flags) { |
| 491 if (flags == 0) |
| 492 return "simple"; |
| 493 std::string name; |
| 494 if (flags & kInputHeavy) |
| 495 name += "ih"; |
| 496 if (flags & kInputDirty) |
| 497 name += std::string(name.empty()? "" : "_") + "id"; |
| 498 if (flags & kRafHeavy) |
| 499 name += std::string(name.empty()? "" : "_") + "rh"; |
| 500 if (flags & kPaintHeavy) |
| 501 name += std::string(name.empty()? "" : "_") + "ph"; |
| 502 return name; |
| 503 } |
| 504 |
| 505 std::string LatencyTest::GetUrlModeString(int flags) { |
| 506 std::string mode = "&mode=" + GetModeString(); |
| 507 if (flags & kInputHeavy) |
| 508 mode += "&inputHeavy"; |
| 509 if (flags & kInputDirty) |
| 510 mode += "&inputDirty"; |
| 511 if (flags & kRafHeavy) |
| 512 mode += "&rafHeavy"; |
| 513 if (flags & kPaintHeavy) |
| 514 mode += "&paintHeavy"; |
| 515 return mode; |
| 516 } |
| 517 |
| 518 std::string LatencyTest::GetUrl(int flags) { |
| 519 std::string test_url = |
| 520 net::FilePathToFileURL(test_path_).possibly_invalid_spec(); |
| 521 test_url += "?numFrames=" + base::IntToString(num_frames_); |
| 522 test_url += "&canvasWidth=" + base::IntToString(kWebGLCanvasWidth); |
| 523 test_url += "&clearColorGreen=" + base::IntToString(kClearColorGreen); |
| 524 test_url += "&delayTimeMS=" + base::IntToString(delay_time_us_ / 1000); |
| 525 test_url += "&y=" + base::IntToString(kMouseY); |
| 526 return test_url + GetUrlModeString(flags); |
| 527 } |
| 528 |
| 529 void LatencyTest::GetMeanFrameTimeMicros(int* frame_time) const { |
| 530 TraceEventVector events; |
| 531 // Search for compositor swaps (or UpdateRects in the software path). |
| 532 analyzer_->FindEvents(query_swaps_, &events); |
| 533 RateStats stats; |
| 534 ASSERT_TRUE(GetRateStats(events, &stats)); |
| 535 |
| 536 // Check that the number of swaps is close to kNumFrames. |
| 537 EXPECT_LT(num_frames_ - num_frames_ / 4, static_cast<int>(events.size())); |
| 538 *frame_time = static_cast<int>(stats.mean_us); |
| 539 } |
| 540 |
| 541 void LatencyTest::SendInput() { |
| 542 RenderViewHost* rvh = browser()->GetSelectedTabContentsWrapper()-> |
| 543 tab_contents()->GetRenderManagerForTesting()->current_host(); |
| 544 WebKit::WebMouseEvent mouse_event; |
| 545 mouse_event.movementX = 1; |
| 546 mouse_x_ += mouse_event.movementX; |
| 547 // Wrap mouse_x_ when it's near the edge of the tab. |
| 548 if (mouse_x_ > tab_width_ - 5) |
| 549 mouse_x_ = 1; |
| 550 mouse_event.x = mouse_event.windowX = mouse_x_; |
| 551 // Set y coordinate to be a few pixels down from the top of the window, |
| 552 // so that it is between the top and bottom of the canvas. |
| 553 mouse_event.y = mouse_event.windowY = 5; |
| 554 mouse_event.type = WebKit::WebInputEvent::MouseMove; |
| 555 TRACE_EVENT_INSTANT1("test_latency", "MouseEventBegin", "x", mouse_x_); |
| 556 rvh->ForwardMouseEvent(mouse_event); |
| 557 } |
| 558 |
| 559 void LatencyTest::PrintEvents(const TraceEventVector& events) { |
| 560 bool is_software = (mode_ == kSoftware); |
| 561 int swap_count = 0; |
| 562 for (size_t i = 0; i < events.size(); ++i) { |
| 563 if (events[i]->name == "MouseEventBegin") { |
| 564 printf("%03d ", events[i]->GetKnownArgAsInt("x")); |
| 565 } else if (events[i]->name == "DoClear") { |
| 566 printf("Clr%03d ", events[i]->GetKnownArgAsInt("red")); |
| 567 } else if (events[i]->name == "DoBlit") { |
| 568 // WebGL context swap buffers. |
| 569 printf("BLT "); |
| 570 } else if (events[i]->name == "SwapBuffers") { |
| 571 // Compositor context swap buffers. |
| 572 ++swap_count; |
| 573 printf("|\nframe %03d: ", swap_count + 1); |
| 574 } else if (is_software && events[i]->name == "UpdateRect") { |
| 575 ++swap_count; |
| 576 printf("(%d)|\nframe %03d: ", |
| 577 events[i]->GetKnownArgAsInt("color"), swap_count + 1); |
| 578 } |
| 579 } |
| 580 printf("\n"); |
| 581 } |
| 582 |
| 583 //////////////////////////////////////////////////////////////////////////////// |
| 584 /// Tests |
| 585 |
| 586 using ::testing::Values; |
| 587 |
| 588 // For manual testing only, run all input latency tests and print summary. |
| 589 IN_PROC_BROWSER_TEST_F(LatencyTest, DISABLED_LatencyWebGLAll) { |
| 590 RunTest(kWebGL, GetAllBehaviors()); |
| 591 } |
| 592 |
| 593 // For manual testing only, run all input latency tests and print summary. |
| 594 IN_PROC_BROWSER_TEST_F(LatencyTest, DISABLED_LatencySoftwareAll) { |
| 595 RunTest(kSoftware, GetAllBehaviors()); |
| 596 } |
| 597 |
| 598 IN_PROC_BROWSER_TEST_P(LatencyTest, LatencySoftware) { |
| 599 RunTest(kSoftware, std::vector<int>(1, GetParam())); |
| 600 } |
| 601 |
| 602 IN_PROC_BROWSER_TEST_P(LatencyTest, LatencyWebGL) { |
| 603 RunTest(kWebGL, std::vector<int>(1, GetParam())); |
| 604 } |
| 605 |
| 606 INSTANTIATE_TEST_CASE_P(, LatencyTest, ::testing::Values( |
| 607 0, |
| 608 kInputHeavy, |
| 609 kInputHeavy | kInputDirty | kRafHeavy, |
| 610 kInputHeavy | kInputDirty | kRafHeavy | kPaintHeavy, |
| 611 kInputDirty | kPaintHeavy, |
| 612 kInputDirty | kRafHeavy | kPaintHeavy |
| 613 )); |
| 614 |
| 615 } // namespace |
OLD | NEW |