Chromium Code Reviews| 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 LatencyTestFlags { | |
| 68 kInputHeavy = 1 << 0, | |
| 69 kInputDirty = 1 << 1, | |
| 70 kRafHeavy = 1 << 2, | |
| 71 kPaintHeavy = 1 << 3 | |
| 72 }; | |
| 73 | |
| 74 int kWebGLCanvasWidth = 10; | |
|
nduca
2011/12/21 20:24:23
should these be const? you've got them named as if
jbates
2011/12/22 02:41:17
Done.
| |
| 75 int kNumFrames = 60; | |
| 76 int kInputsPerFrame = 16; | |
| 77 // Magic number to identify certain glClear events. | |
| 78 int kClearColorGreen = 137; | |
| 79 int kMouseY = 5; | |
| 80 | |
| 81 class LatencyTest : public BrowserPerfTest { | |
| 82 public: | |
| 83 LatencyTest() : | |
| 84 query_instant_(Query(EVENT_PHASE) == | |
| 85 Query::Phase(TRACE_EVENT_PHASE_INSTANT)), | |
|
nduca
2011/12/21 20:24:23
you might make a note that these will be initializ
jbates
2011/12/22 02:41:17
Done. (Sadly no default constructor on Query)
| |
| 86 query_swaps_(Query::Bool(false)), | |
| 87 query_inputs_(Query::Bool(false)), | |
| 88 query_blits_(Query::Bool(false)), | |
| 89 query_clears_(Query::Bool(false)), | |
| 90 mouse_x_(0), | |
| 91 tab_width_(0), | |
| 92 delay_time_ms_(0), | |
| 93 num_frames_(0), | |
| 94 verbose_(false), | |
| 95 test_flags_(0) {} | |
| 96 | |
| 97 virtual void SetUpCommandLine(CommandLine* command_line) { | |
|
Paweł Hajdan Jr.
2011/12/21 09:50:29
nit: OVERRIDE
jbates
2011/12/21 19:34:18
Done.
| |
| 98 BrowserPerfTest::SetUpCommandLine(command_line); | |
| 99 // This enables DOM automation for tab contents. | |
| 100 EnableDOMAutomation(); | |
| 101 #ifdef TEST_THREADED_COMPOSITING_LATENCY | |
|
Paweł Hajdan Jr.
2011/12/21 09:50:29
Why is this pre-processor controlled? I don't see
jbates
2011/12/21 19:34:18
Done.
| |
| 102 command_line->AppendSwitch(switches::kEnableThreadedCompositing); | |
| 103 #endif | |
| 104 // Default behavior is to thumbnail the tab after 0.5 seconds, causing | |
| 105 // a nasty frame hitch and disturbing the latency test. Fix that: | |
| 106 command_line->AppendSwitch(switches::kEnableInBrowserThumbnailing); | |
| 107 command_line->AppendSwitch(switches::kDisableBackgroundNetworking); | |
| 108 } | |
| 109 | |
| 110 // Run test with specified |mode| and |behaviors|. | |
| 111 // |mode| can be webgl or software. | |
| 112 // |behaviors| is a list of combinations of LatencyTestFlags. | |
| 113 void RunTest(const std::string& mode, const std::vector<int>& behaviors); | |
| 114 | |
| 115 std::vector<int> GetAllBehaviors() { | |
| 116 std::vector<int> behaviors; | |
| 117 int max_behaviors = kInputHeavy | kInputDirty | kRafHeavy | kPaintHeavy; | |
| 118 for (int i = 0; i <= max_behaviors; ++i) | |
| 119 behaviors.push_back(i); | |
| 120 return behaviors; | |
| 121 } | |
| 122 | |
| 123 private: | |
|
Paweł Hajdan Jr.
2011/12/21 09:50:29
nit: Indent +1.
jbates
2011/12/21 19:34:18
Done.
| |
| 124 void RunTestInternal(const std::string& test_url, | |
| 125 bool send_inputs, | |
| 126 int input_delay_ms); | |
| 127 | |
| 128 double CalculateLatency(); | |
| 129 | |
| 130 std::string GetTraceName(int flags) { | |
| 131 if (flags == 0) | |
| 132 return "simple"; | |
| 133 std::string name; | |
| 134 if (flags & kInputHeavy) | |
| 135 name += "ih"; | |
| 136 if (flags & kInputDirty) | |
| 137 name += std::string(name.empty()? "" : "_") + "id"; | |
| 138 if (flags & kRafHeavy) | |
| 139 name += std::string(name.empty()? "" : "_") + "rh"; | |
| 140 if (flags & kPaintHeavy) | |
| 141 name += std::string(name.empty()? "" : "_") + "ph"; | |
| 142 return name; | |
| 143 } | |
| 144 | |
| 145 std::string GetUrlModeString(int flags) { | |
| 146 std::string mode = "&mode=" + mode_; | |
| 147 if (flags & kInputHeavy) | |
| 148 mode += "&inputHeavy=true"; | |
| 149 if (flags & kInputDirty) | |
| 150 mode += "&inputDirty=true"; | |
| 151 if (flags & kRafHeavy) | |
| 152 mode += "&rafHeavy=true"; | |
| 153 if (flags & kPaintHeavy) | |
| 154 mode += "&paintHeavy=true"; | |
| 155 return mode; | |
| 156 } | |
| 157 | |
| 158 std::string GetUrl(int flags) { | |
| 159 std::string test_url = | |
| 160 net::FilePathToFileURL(test_path_).possibly_invalid_spec(); | |
| 161 test_url += "?numFrames=" + base::IntToString(num_frames_); | |
| 162 test_url += "&canvasWidth=" + base::IntToString(kWebGLCanvasWidth); | |
| 163 test_url += "&clearColorGreen=" + base::IntToString(kClearColorGreen); | |
| 164 test_url += "&delayTimeMS=" + base::IntToString(delay_time_ms_); | |
| 165 test_url += "&y=" + base::IntToString(kMouseY); | |
| 166 return test_url + GetUrlModeString(flags); | |
| 167 } | |
| 168 | |
| 169 int GetMeanFrameTime() const { | |
| 170 TraceEventVector events; | |
| 171 // Search for compositor swaps (or UpdateRects in the software path). | |
| 172 analyzer_->FindEvents(query_swaps_, &events); | |
| 173 RateStats stats; | |
| 174 bool success = GetRateStats(events, &stats); | |
| 175 EXPECT_TRUE(success); | |
| 176 if (!success) | |
| 177 return 16000; | |
|
nduca
2011/12/21 20:24:23
Comment to explain why this magic number is used?
jbates
2011/12/22 02:41:17
Done.
| |
| 178 | |
| 179 // Check that the number of swaps is close to kNumFrames. | |
| 180 EXPECT_LT(num_frames_ - num_frames_/4, static_cast<int>(events.size())); | |
|
Paweł Hajdan Jr.
2011/12/21 09:50:29
nit: Spaces around "/".
jbates
2011/12/21 19:34:18
Done.
| |
| 181 return static_cast<int>(stats.mean_us); | |
| 182 } | |
| 183 | |
| 184 void SendInput() { | |
| 185 RenderViewHost* rvh = browser()->GetSelectedTabContentsWrapper()-> | |
| 186 tab_contents()->render_view_host(); | |
| 187 WebKit::WebMouseEvent mouse_event; | |
| 188 mouse_event.movementX = 1; | |
| 189 mouse_x_ += mouse_event.movementX; | |
| 190 // Wrap mouse_x_ when it's near the edge of the tab. | |
| 191 if (mouse_x_ > tab_width_ - 5) | |
| 192 mouse_x_ = 1; | |
| 193 mouse_event.x = mouse_event.windowX = mouse_x_; | |
| 194 // Set y coordinate to be a few pixels down from the top of the window, | |
| 195 // so that it is between the top and bottom of the canvas. | |
| 196 mouse_event.y = mouse_event.windowY = 5; | |
| 197 mouse_event.type = WebKit::WebInputEvent::MouseMove; | |
| 198 TRACE_EVENT_INSTANT1("test_latency", "MouseEventBegin", "x", mouse_x_); | |
| 199 rvh->ForwardMouseEvent(mouse_event); | |
|
nduca
2011/12/21 20:24:23
/me wonders out loud whether this will cleanly ext
jbates
2011/12/22 02:41:17
Yeah, it seems like it would be a trivial update t
| |
| 200 } | |
| 201 | |
| 202 void PrintEvents(const TraceEventVector& events) { | |
| 203 bool is_software = (mode_ == "software"); | |
| 204 // Print trace data. | |
|
nduca
2011/12/21 20:24:23
comment doesn't add a lot of value methinks
jbates
2011/12/22 02:41:17
Done.
| |
| 205 int swap_count = 0; | |
| 206 for (size_t i = 0; i < events.size(); ++i) { | |
| 207 if (events[i]->name == "MouseEventBegin") { | |
| 208 printf("%03d ", events[i]->GetKnownArgAsInt("x")); | |
| 209 } | |
| 210 else if (events[i]->name == "DoClear") { | |
|
Paweł Hajdan Jr.
2011/12/21 09:50:29
nit: move this to the previous line, same below, i
jbates
2011/12/21 19:34:18
Done.
| |
| 211 printf("Clr%03d ", events[i]->GetKnownArgAsInt("red")); | |
| 212 } | |
| 213 else if (events[i]->name == "DoBlit") { | |
| 214 // WebGL context swap buffers. | |
| 215 printf("BLT "); | |
| 216 } | |
| 217 else if (events[i]->name == "SwapBuffers") { | |
| 218 // Compositor context swap buffers. | |
| 219 ++swap_count; | |
| 220 printf("|\nframe %03d: ", swap_count + 1); | |
| 221 } | |
| 222 else if (is_software && events[i]->name == "UpdateRect") { | |
| 223 ++swap_count; | |
| 224 printf("(%d)|\nframe %03d: ", | |
| 225 events[i]->GetKnownArgAsInt("color"), swap_count + 1); | |
| 226 } | |
| 227 } | |
| 228 printf("\n"); | |
| 229 } | |
| 230 | |
| 231 FilePath test_path_; | |
|
Paweł Hajdan Jr.
2011/12/21 09:50:29
nit: Please comment member variables.
jbates
2011/12/21 19:34:18
Done.
| |
| 232 Query query_instant_; | |
| 233 Query query_swaps_; | |
| 234 Query query_inputs_; | |
| 235 Query query_blits_; | |
| 236 Query query_clears_; | |
| 237 scoped_ptr<TraceAnalyzer> analyzer_; | |
| 238 int mouse_x_; | |
| 239 int tab_width_; | |
| 240 base::RepeatingTimer<LatencyTest> timer_; | |
| 241 std::string mode_; | |
| 242 int delay_time_ms_; | |
| 243 int num_frames_; | |
| 244 std::map<int, double> latencies_; | |
| 245 bool verbose_; | |
| 246 int test_flags_; | |
| 247 }; | |
| 248 | |
| 249 void LatencyTest::RunTest(const std::string& mode, | |
|
nduca
2011/12/21 20:24:23
odd that you're using a mode string but an enum fo
jbates
2011/12/22 02:41:17
Done.
| |
| 250 const std::vector<int>& behaviors) { | |
|
nduca
2011/12/21 20:24:23
Also, a bit confused that some functions are imple
jbates
2011/12/22 02:41:17
Moved them out (to make the changes easier to foll
| |
| 251 mode_ = mode; | |
| 252 verbose_ = (logging::GetVlogLevel("latency_tests") > 0); | |
| 253 | |
| 254 // Construct queries for searching trace events via TraceAnalyzer. | |
| 255 int initial_flags = 0; | |
| 256 if (mode_ == "webgl") { | |
| 257 query_swaps_ = query_instant_ && | |
| 258 Query(EVENT_NAME) == Query::String("SwapBuffers") && | |
| 259 Query(EVENT_ARG, "width") != Query::Int(kWebGLCanvasWidth); | |
| 260 } else if (mode_ == "software") { | |
| 261 // Run software with kPaintHeavy the first time when no inputs are | |
| 262 // triggering paints -- otherwise it won't update each frame. | |
|
nduca
2011/12/21 20:24:23
Not sure I follow this. Also, this feels out of pl
jbates
2011/12/22 02:41:17
Done.
| |
| 263 initial_flags = static_cast<int>(kPaintHeavy); | |
| 264 // Software updates need to have x=0 and y=0 to contain the input color. | |
| 265 query_swaps_ = query_instant_ && | |
| 266 Query(EVENT_NAME) == Query::String("UpdateRect") && | |
| 267 Query(EVENT_ARG, "x+y") == Query::Int(0); | |
| 268 } | |
| 269 query_inputs_ = query_instant_ && | |
| 270 Query(EVENT_NAME) == Query::String("MouseEventBegin"); | |
| 271 query_blits_ = query_instant_ && | |
| 272 Query(EVENT_NAME) == Query::String("DoBlit") && | |
| 273 Query(EVENT_ARG, "width") == Query::Int(kWebGLCanvasWidth); | |
| 274 query_clears_ = query_instant_ && | |
| 275 Query(EVENT_NAME) == Query::String("DoClear") && | |
| 276 Query(EVENT_ARG, "green") == Query::Int(kClearColorGreen); | |
| 277 Query query_width_swaps = query_swaps_; | |
| 278 if (mode_ == "software") { | |
| 279 query_width_swaps = query_instant_ && | |
| 280 Query(EVENT_NAME) == Query::String("UpdateRectWidth") && | |
| 281 Query(EVENT_ARG, "width") > Query::Int(kWebGLCanvasWidth); | |
| 282 } | |
| 283 | |
| 284 // Set path to test html. | |
| 285 PathService::Get(chrome::DIR_TEST_DATA, &test_path_); | |
|
Paweł Hajdan Jr.
2011/12/21 09:50:29
Check the return value.
jbates
2011/12/21 19:34:18
Done.
| |
| 286 test_path_ = test_path_.Append(FILE_PATH_LITERAL("perf")); | |
| 287 test_path_ = test_path_.Append(FILE_PATH_LITERAL("latency_suite.html")); | |
| 288 ASSERT_TRUE(file_util::PathExists(test_path_)) | |
| 289 << "Missing test file: " << test_path_.value(); | |
| 290 | |
| 291 // Run once with defaults to measure the frame times. | |
| 292 delay_time_ms_ = 0; | |
| 293 // kNumFrames may be very high, but we only need a few frames to measure | |
| 294 // average frame times. | |
| 295 num_frames_ = 30; | |
| 296 RunTestInternal(GetUrl(initial_flags), false, 0); | |
| 297 | |
| 298 // Get width of tab so that we know the limit of x coordinates for the | |
| 299 // injected mouse inputs. | |
| 300 const TraceEvent* swap_event = analyzer_->FindOneEvent(query_width_swaps); | |
| 301 ASSERT_TRUE(swap_event); | |
| 302 tab_width_ = swap_event->GetKnownArgAsInt("width"); | |
| 303 // Keep printf output clean by limiting input coords to three digits: | |
| 304 tab_width_ = (tab_width_ < 1000) ? tab_width_ : 999; | |
| 305 // Sanity check the tab_width -- it should be more than 100 pixels. | |
| 306 EXPECT_GT(tab_width_, 100); | |
| 307 | |
| 308 int mean_frame_time_us = GetMeanFrameTime(); | |
|
nduca
2011/12/21 20:24:23
why are we mixing micros and ms?
jbates
2011/12/22 02:41:17
Done.
| |
| 309 if (verbose_) | |
| 310 printf("Mean frame time micros = %d\n", mean_frame_time_us); | |
| 311 // Delay time is 2x the average frame time. | |
| 312 delay_time_ms_ = 2 * mean_frame_time_us / 1000; | |
| 313 // Calculate delay time between inputs based on the measured frame time. | |
| 314 // This prevents flooding the browser with more events than we need if the | |
| 315 // test is running very slow (such as on a VM). | |
| 316 int delay_us = mean_frame_time_us / kInputsPerFrame; | |
| 317 // Round delay_us down to nearest milliseconds. | |
| 318 int delay_ms = delay_us / 1000; | |
| 319 delay_ms = (delay_ms <= 0) ? 1 : delay_ms; | |
| 320 | |
| 321 // Reset num_frames_ for actual test runs. | |
| 322 num_frames_ = kNumFrames; | |
| 323 | |
| 324 // Run input latency test with each requested behavior. | |
|
nduca
2011/12/21 20:24:23
This behaviors vs TEST_ distinction reminds me of
jbates
2011/12/22 02:41:17
Done.
| |
| 325 for (size_t i = 0; i < behaviors.size(); ++i) { | |
| 326 // Sleep to allow the rendering pipeline to flush. | |
| 327 base::PlatformThread::Sleep(200); | |
|
Paweł Hajdan Jr.
2011/12/21 09:50:29
Shouldn't you rather wait for an event or somethin
jbates
2011/12/21 19:34:18
You're right, this was left-over from local testin
| |
| 328 | |
| 329 test_flags_ = behaviors[i]; | |
| 330 std::string url = GetUrl(test_flags_); | |
| 331 printf("=============================================================\n"); | |
| 332 if (verbose_) | |
| 333 printf("Mode: %s\n", GetUrlModeString(i).c_str()); | |
| 334 printf("URL: %s\n", url.c_str()); | |
| 335 | |
| 336 // Do the actual test with input events. | |
| 337 RunTestInternal(url, true, delay_ms); | |
| 338 latencies_[test_flags_] = CalculateLatency(); | |
| 339 } | |
| 340 | |
| 341 // Print summary if more than 1 behavior was tested in this run. | |
|
nduca
2011/12/21 20:24:23
Is this for human consumption, or for consumption
jbates
2011/12/22 02:41:17
For human consumption, added a comment.
| |
| 342 if (behaviors.size() > 1) { | |
| 343 printf("#############################################################\n"); | |
| 344 printf("## %s\n", mode_.c_str()); | |
| 345 if (verbose_) { | |
| 346 printf("Latency, behavior:\n"); | |
| 347 for (size_t i = 0; i < behaviors.size(); ++i) { | |
| 348 printf("%.1f, %s%s%s%s\n", latencies_[behaviors[i]], | |
| 349 (i & kInputHeavy) ? "InputHeavy " : "", | |
| 350 (i & kInputDirty) ? "InputDirty " : "", | |
| 351 (i & kRafHeavy) ? "RafHeavy " : "", | |
| 352 (i & kPaintHeavy) ? "PaintHeavy " : ""); | |
| 353 } | |
| 354 } | |
| 355 printf("Latencies for tests: "); | |
| 356 for (size_t i = 0; i < behaviors.size(); ++i) { | |
| 357 printf("%.1f%s", latencies_[behaviors[i]], | |
| 358 (i < behaviors.size() - 1) ? ", " : ""); | |
| 359 } | |
| 360 printf("\n"); | |
| 361 printf("#############################################################\n"); | |
| 362 } | |
| 363 } | |
| 364 | |
| 365 void LatencyTest::RunTestInternal(const std::string& test_url, | |
| 366 bool send_inputs, | |
| 367 int input_delay_ms) { | |
| 368 mouse_x_ = 0; | |
| 369 | |
| 370 ASSERT_TRUE(tracing::BeginTracing("test_gpu,test_latency")); | |
| 371 | |
| 372 ui_test_utils::NavigateToURLWithDisposition( | |
| 373 browser(), GURL(test_url), CURRENT_TAB, | |
| 374 ui_test_utils::BROWSER_TEST_NONE); | |
| 375 | |
| 376 // Start sending mouse inputs. | |
| 377 if (send_inputs) { | |
| 378 timer_.Start(FROM_HERE, base::TimeDelta::FromMicroseconds(input_delay_ms), | |
| 379 this, &LatencyTest::SendInput); | |
| 380 } | |
| 381 | |
| 382 // Wait for message indicating the test has finished running. | |
| 383 ui_test_utils::DOMMessageQueue message_queue; | |
| 384 ASSERT_TRUE(message_queue.WaitForMessage(NULL)); | |
|
nduca
2011/12/21 20:24:23
Do we need any error handling here? Or is the idea
jbates
2011/12/22 02:41:17
The trace analysis does lots of error checking. Le
| |
| 385 | |
| 386 timer_.Stop(); | |
| 387 | |
| 388 std::string json_events; | |
| 389 ASSERT_TRUE(tracing::EndTracing(&json_events)); | |
| 390 | |
| 391 analyzer_.reset(TraceAnalyzer::Create(json_events)); | |
| 392 analyzer_->AssociateBeginEndEvents(); | |
| 393 analyzer_->MergeAssociatedEventArgs(); | |
| 394 } | |
| 395 | |
| 396 double LatencyTest::CalculateLatency() { | |
| 397 TraceEventVector events; | |
| 398 if (mode_ == "webgl") { | |
| 399 // Search for three types of events in WebGL mode: | |
| 400 // - onscreen swaps. | |
| 401 // - DoClear calls that contain the mouse x coordinate. | |
| 402 // - mouse events. | |
| 403 analyzer_->FindEvents(query_swaps_ || query_inputs_ || | |
| 404 query_blits_ || query_clears_, &events); | |
| 405 } | |
| 406 else if (mode_ == "software") { | |
|
Paweł Hajdan Jr.
2011/12/21 09:50:29
nit: Move to the previous line.
jbates
2011/12/21 19:34:18
Done.
| |
| 407 analyzer_->FindEvents(query_swaps_ || query_inputs_, &events); | |
| 408 } else { | |
| 409 CHECK(0) << "invalid mode"; | |
|
Paweł Hajdan Jr.
2011/12/21 09:50:29
Oh, a CHECK? We usually do a NOTREACHED() in those
jbates
2011/12/21 19:34:18
Done.
| |
| 410 } | |
| 411 | |
| 412 if (verbose_) | |
| 413 PrintEvents(events); | |
| 414 | |
| 415 int swap_count = 0; | |
| 416 size_t previous_blit_pos = 0; | |
| 417 swap_count = 0; | |
| 418 std::vector<int> latencies; | |
| 419 printf("Measured latency (in number of frames) for each frame:\n"); | |
|
nduca
2011/12/21 20:24:23
/me is confused about the output of this test. You
jbates
2011/12/22 02:41:17
The only stuff that is parsed by the perf bots com
| |
| 420 for (size_t i = 0; i < events.size(); ++i) { | |
|
nduca
2011/12/21 20:24:23
I'm a little surprise to see an actual loop in use
jbates
2011/12/22 02:41:17
I think it's just the nature of this test. It need
| |
| 421 if (query_swaps_.Evaluate(*events[i])) { | |
| 422 // Compositor context swap buffers. | |
| 423 ++swap_count; | |
| 424 // Don't analyze first few swaps, because they are filling the rendering | |
| 425 // pipeline and may be unstable. Latencies can be as high as 5 frames or | |
| 426 // so, so skip the first 6 frames to get more accurate results. | |
| 427 if (swap_count > 6) { | |
| 428 int mouse_x = 0; | |
| 429 if (mode_ == "webgl") { | |
| 430 // Trace backwards through the events to find the input event that | |
| 431 // matches the glClear that was presented by this SwapBuffers. | |
| 432 | |
| 433 // Step 1: Find the last blit (which will be the WebGL blit). | |
| 434 size_t blit_pos = 0; | |
| 435 EXPECT_TRUE(FindLastOf(events, query_blits_, i, &blit_pos)); | |
| 436 // Skip this SwapBuffers if the blit has already been consumed by a | |
| 437 // previous SwapBuffers. This means the current frame did not receive | |
| 438 // an update from WebGL. | |
| 439 EXPECT_GT(blit_pos, previous_blit_pos); | |
| 440 if (blit_pos == previous_blit_pos) { | |
| 441 if (verbose_) | |
| 442 printf(" %03d: ERROR\n", swap_count); | |
| 443 else | |
| 444 printf(" ERROR"); | |
| 445 continue; | |
| 446 } | |
| 447 previous_blit_pos = blit_pos; | |
| 448 | |
| 449 // Step 2: find the last clear from the WebGL blit. This will be the | |
| 450 // value of the latest mouse input that has affected this swap. | |
| 451 size_t clear_pos = 0; | |
| 452 EXPECT_TRUE(FindLastOf(events, query_clears_, blit_pos, &clear_pos)); | |
| 453 mouse_x = events[clear_pos]->GetKnownArgAsInt("red"); | |
| 454 } else if (mode_ == "software") { | |
| 455 // The software path gets the mouse_x directly from the DIB colors. | |
| 456 mouse_x = events[i]->GetKnownArgAsInt("color"); | |
| 457 } | |
| 458 | |
| 459 // Find the corresponding mouse input. | |
| 460 size_t input_pos = 0; | |
| 461 Query query_mouse_event = query_inputs_ && | |
| 462 Query(EVENT_ARG, "x") == Query::Int(mouse_x); | |
| 463 EXPECT_TRUE(FindLastOf(events, query_mouse_event, i, &input_pos)); | |
| 464 | |
| 465 // Step 4: Find the nearest onscreen SwapBuffers to this input event. | |
| 466 size_t closest_swap = 0; | |
| 467 size_t second_closest_swap = 0; | |
| 468 EXPECT_TRUE(FindClosest(events, query_swaps_, input_pos, | |
| 469 &closest_swap, &second_closest_swap)); | |
| 470 int latency = CountMatches(events, query_swaps_, closest_swap, i); | |
| 471 latencies.push_back(latency); | |
| 472 if (verbose_) | |
| 473 printf(" %03d: %d\n", swap_count, latency); | |
| 474 else | |
| 475 printf(" %d", latency); | |
| 476 } | |
| 477 } | |
| 478 } | |
| 479 printf("\n"); | |
| 480 | |
| 481 EXPECT_GT(latencies.size(), size_t(2)); | |
| 482 if (latencies.size() <= size_t(2)) | |
| 483 return 0.0; | |
| 484 | |
| 485 double mean_latency = 0.0; | |
| 486 // Skip last couple latencies, because they could be unreliable. | |
| 487 size_t num_consider = latencies.size() - 2; | |
| 488 for (size_t i = 0; i < num_consider; ++i) | |
| 489 mean_latency += static_cast<double>(latencies[i]); | |
| 490 mean_latency /= static_cast<double>(num_consider); | |
| 491 printf("Mean latency = %f\n", mean_latency); | |
| 492 | |
| 493 double mean_error = 0.0; | |
| 494 for (size_t i = 0; i < num_consider; ++i) { | |
| 495 double offset = fabs(mean_latency - static_cast<double>(latencies[i])); | |
| 496 mean_error = (offset > mean_error) ? offset : mean_error; | |
| 497 } | |
| 498 | |
| 499 std::string trace_name = GetTraceName(test_flags_); | |
| 500 std::string mean_and_error = base::StringPrintf("%f,%f", mean_latency, | |
| 501 mean_error); | |
| 502 perf_test::PrintResultMeanAndError(mode_, "", trace_name, mean_and_error, | |
| 503 "frames", true); | |
| 504 return mean_latency; | |
| 505 } | |
| 506 | |
| 507 // For manual testing only, run all input latency tests and print summary. | |
| 508 IN_PROC_BROWSER_TEST_F(LatencyTest, DISABLED_WebGLLatencyAll) { | |
| 509 RunTest("webgl", GetAllBehaviors()); | |
| 510 } | |
| 511 | |
| 512 // For manual testing only, run all input latency tests and print summary. | |
| 513 IN_PROC_BROWSER_TEST_F(LatencyTest, DISABLED_SoftwareLatencyAll) { | |
| 514 RunTest("software", GetAllBehaviors()); | |
| 515 } | |
| 516 | |
| 517 IN_PROC_BROWSER_TEST_F(LatencyTest, WebGLLatencySelect) { | |
| 518 std::vector<int> behaviors; | |
| 519 behaviors.push_back(0); | |
| 520 behaviors.push_back(kInputHeavy); | |
| 521 behaviors.push_back(kInputHeavy | kInputDirty | kRafHeavy); | |
| 522 behaviors.push_back(kInputDirty | kPaintHeavy); | |
| 523 behaviors.push_back(kInputDirty | kRafHeavy | kPaintHeavy); | |
| 524 behaviors.push_back(kInputDirty | kInputHeavy | kRafHeavy | kPaintHeavy); | |
| 525 RunTest("webgl", behaviors); | |
| 526 } | |
| 527 | |
| 528 IN_PROC_BROWSER_TEST_F(LatencyTest, SoftwareLatencySelect) { | |
| 529 std::vector<int> behaviors; | |
| 530 behaviors.push_back(0); | |
| 531 behaviors.push_back(kInputHeavy); | |
| 532 behaviors.push_back(kInputHeavy | kInputDirty | kRafHeavy); | |
| 533 behaviors.push_back(kInputDirty | kPaintHeavy); | |
| 534 behaviors.push_back(kInputDirty | kRafHeavy | kPaintHeavy); | |
| 535 behaviors.push_back(kInputDirty | kInputHeavy | kRafHeavy | kPaintHeavy); | |
| 536 RunTest("software", behaviors); | |
| 537 } | |
| 538 | |
| 539 } // namespace anonymous | |
| OLD | NEW |