summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjbates@chromium.org <jbates@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-01-03 21:01:59 +0000
committerjbates@chromium.org <jbates@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-01-03 21:01:59 +0000
commit1e42174b401dbf8105ec4e5d864bad1c906c5717 (patch)
tree97c17cdae120c77b4cb173a2c5f632d5fb4c8476
parent1bf0ec47259705707ea7f90182554d49ef50ac39 (diff)
downloadchromium_src-1e42174b401dbf8105ec4e5d864bad1c906c5717.zip
chromium_src-1e42174b401dbf8105ec4e5d864bad1c906c5717.tar.gz
chromium_src-1e42174b401dbf8105ec4e5d864bad1c906c5717.tar.bz2
Input latency performance test that uses tracing.
This CL adds a new gyp target called performance_browser_tests to run the new input latency test as a browser test. See the comments in latency_tests.cc for an overview of how latency is measured. Review URL: http://codereview.chromium.org/8883005 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@116191 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--build/all.gyp7
-rw-r--r--chrome/chrome_tests.gypi178
-rw-r--r--chrome/test/data/perf/latency_suite.html152
-rw-r--r--chrome/test/perf/browser_perf_test.cc34
-rw-r--r--chrome/test/perf/browser_perf_test.h29
-rw-r--r--chrome/test/perf/rendering/OWNERS3
-rw-r--r--chrome/test/perf/rendering/latency_tests.cc615
-rw-r--r--content/browser/renderer_host/render_widget_host.cc8
-rw-r--r--gpu/command_buffer/service/gles2_cmd_decoder.cc11
9 files changed, 1032 insertions, 5 deletions
diff --git a/build/all.gyp b/build/all.gyp
index 07fb11e..2a21a48 100644
--- a/build/all.gyp
+++ b/build/all.gyp
@@ -1,4 +1,4 @@
-# Copyright (c) 2011 The Chromium Authors. All rights reserved.
+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
@@ -239,6 +239,7 @@
'type': 'none',
'dependencies': [
'chromium_builder_qa', # needed for pyauto
+ '../chrome/chrome.gyp:performance_browser_tests',
'../chrome/chrome.gyp:performance_ui_tests',
'../chrome/chrome.gyp:plugin_tests',
'../chrome/chrome.gyp:sync_performance_tests',
@@ -250,6 +251,7 @@
'type': 'none',
'dependencies': [
'../chrome/chrome.gyp:gpu_tests',
+ '../chrome/chrome.gyp:performance_browser_tests',
'../chrome/chrome.gyp:performance_ui_tests',
'../third_party/WebKit/Source/WebKit/chromium/WebKit.gyp:DumpRenderTree',
],
@@ -315,6 +317,7 @@
'type': 'none',
'dependencies': [
'../chrome/chrome.gyp:browser_tests',
+ '../chrome/chrome.gyp:performance_browser_tests',
'../chrome/chrome.gyp:performance_ui_tests',
'../chrome/chrome.gyp:plugin_tests',
'../chrome/chrome.gyp:safe_browsing_tests',
@@ -392,6 +395,7 @@
'../chrome/chrome.gyp:installer_util_unittests',
'../chrome/chrome.gyp:interactive_ui_tests',
'../chrome/chrome.gyp:mini_installer_test',
+ '../chrome/chrome.gyp:performance_browser_tests',
'../chrome/chrome.gyp:performance_ui_tests',
'../chrome/chrome.gyp:plugin_tests',
'../chrome/chrome.gyp:safe_browsing_tests',
@@ -531,6 +535,7 @@
'../chrome/chrome.gyp:browser_tests',
'../chrome/chrome.gyp:chrome',
'../chrome/chrome.gyp:interactive_ui_tests',
+ '../chrome/chrome.gyp:performance_browser_tests',
'../chrome/chrome.gyp:performance_ui_tests',
'../chrome/chrome.gyp:safe_browsing_tests',
'../chrome/chrome.gyp:sync_unit_tests',
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi
index 04b5feb..188a834 100644
--- a/chrome/chrome_tests.gypi
+++ b/chrome/chrome_tests.gypi
@@ -3051,6 +3051,184 @@
], # conditions
}, # target browser_tests
{
+ # Executable that runs each perf browser test in a new process.
+ 'target_name': 'performance_browser_tests',
+ 'type': 'executable',
+ 'msvs_cygwin_shell': 0,
+ 'msvs_cygwin_dirs': ['<(DEPTH)/third_party/cygwin'],
+ 'dependencies': [
+ 'browser',
+ 'browser/sync/protocol/sync_proto.gyp:sync_proto',
+ 'chrome',
+ 'chrome_resources.gyp:chrome_resources',
+ 'chrome_resources.gyp:chrome_strings',
+ 'renderer',
+ 'test_support_common',
+ '../base/base.gyp:base',
+ '../base/base.gyp:base_i18n',
+ '../base/base.gyp:test_support_base',
+ '../net/net.gyp:net',
+ '../net/net.gyp:net_test_support',
+ '../skia/skia.gyp:skia',
+ '../testing/gmock.gyp:gmock',
+ '../testing/gtest.gyp:gtest',
+ '../third_party/cld/cld.gyp:cld',
+ '../third_party/icu/icu.gyp:icui18n',
+ '../third_party/icu/icu.gyp:icuuc',
+ '../third_party/leveldatabase/leveldatabase.gyp:leveldatabase',
+ '../v8/tools/gyp/v8.gyp:v8',
+ '../webkit/webkit.gyp:test_shell_test_support',
+ # Runtime dependencies
+ '../third_party/mesa/mesa.gyp:osmesa',
+ '../third_party/WebKit/Source/WebKit/chromium/WebKit.gyp:copy_TestNetscapePlugIn',
+ ],
+ 'include_dirs': [
+ '..',
+ ],
+ 'defines': [
+ 'HAS_OUT_OF_PROC_TEST_RUNNER',
+ 'BROWSER_TESTS_HEADER_OVERRIDE="chrome/test/base/in_process_browser_test.h"',
+ ],
+ 'sources': [
+ 'app/breakpad_mac_stubs.mm',
+ 'app/chrome_command_ids.h',
+ 'app/chrome_dll.rc',
+ 'app/chrome_dll_resource.h',
+ 'app/chrome_version.rc.version',
+ 'test/base/chrome_render_view_test.cc',
+ 'test/base/chrome_render_view_test.h',
+ 'test/base/chrome_test_launcher.cc',
+ 'test/perf/browser_perf_test.cc',
+ 'test/perf/browser_perf_test.h',
+ 'test/perf/rendering/latency_tests.cc',
+ '../content/test/test_launcher.cc',
+ '../content/test/test_launcher.h',
+ ],
+ 'rules': [
+ {
+ 'rule_name': 'js2webui',
+ 'extension': 'js',
+ 'msvs_external_rule': 1,
+ 'inputs': [
+ '<(gypv8sh)',
+ '<(PRODUCT_DIR)/v8_shell<(EXECUTABLE_SUFFIX)',
+ '<(mock_js)',
+ '<(test_api_js)',
+ '<(js2gtest)',
+ ],
+ 'outputs': [
+ '<(INTERMEDIATE_DIR)/chrome/<(RULE_INPUT_DIRNAME)/<(RULE_INPUT_ROOT)-gen.cc',
+ '<(PRODUCT_DIR)/test_data/chrome/<(RULE_INPUT_DIRNAME)/<(RULE_INPUT_ROOT).js',
+ ],
+ 'process_outputs_as_sources': 1,
+ 'action': [
+ 'python',
+ '<@(_inputs)',
+ 'webui',
+ '<(RULE_INPUT_PATH)',
+ 'chrome/<(RULE_INPUT_DIRNAME)/<(RULE_INPUT_ROOT).js',
+ '<@(_outputs)',
+ ],
+ },
+ ],
+ 'conditions': [
+ ['OS!="linux" or toolkit_views==1', {
+ 'sources!': [
+ 'browser/extensions/browser_action_test_util_gtk.cc',
+ ],
+ }],
+ ['OS=="win"', {
+ 'sources': [
+ '<(SHARED_INTERMEDIATE_DIR)/chrome/browser_resources.rc',
+ '<(SHARED_INTERMEDIATE_DIR)/chrome/common_resources.rc',
+ '<(SHARED_INTERMEDIATE_DIR)/chrome/renderer_resources.rc',
+ '<(SHARED_INTERMEDIATE_DIR)/chrome/theme_resources.rc',
+ '<(SHARED_INTERMEDIATE_DIR)/chrome/theme_resources_standard.rc',
+ '<(SHARED_INTERMEDIATE_DIR)/chrome_version/other_version.rc',
+ '<(SHARED_INTERMEDIATE_DIR)/net/net_resources.rc',
+ '<(SHARED_INTERMEDIATE_DIR)/ui/ui_resources/ui_resources.rc',
+ '<(SHARED_INTERMEDIATE_DIR)/ui/ui_resources_standard/ui_resources_standard.rc',
+ '<(SHARED_INTERMEDIATE_DIR)/webkit/webkit_chromium_resources.rc',
+ '<(SHARED_INTERMEDIATE_DIR)/webkit/webkit_resources.rc',
+ ],
+ 'include_dirs': [
+ '<(DEPTH)/third_party/wtl/include',
+ ],
+ 'dependencies': [
+ 'chrome_version_resources',
+ 'installer_util_strings',
+ '../sandbox/sandbox.gyp:sandbox',
+ ],
+ 'conditions': [
+ ['win_use_allocator_shim==1', {
+ 'dependencies': [
+ '<(allocator_target)',
+ ],
+ }],
+ ],
+ 'configurations': {
+ 'Debug_Base': {
+ 'msvs_settings': {
+ 'VCLinkerTool': {
+ 'LinkIncremental': '<(msvs_debug_link_nonincremental)',
+ },
+ },
+ },
+ }
+ }, { # else: OS != "win"
+ 'sources!': [
+ 'app/chrome_command_ids.h',
+ 'app/chrome_dll.rc',
+ 'app/chrome_dll_resource.h',
+ 'app/chrome_version.rc.version',
+ ],
+ }],
+ ['toolkit_uses_gtk == 1', {
+ 'dependencies': [
+ '../build/linux/system.gyp:gtk',
+ '../tools/xdisplaycheck/xdisplaycheck.gyp:xdisplaycheck',
+ ],
+ }],
+ ['toolkit_uses_gtk == 1 or chromeos==1 or (OS=="linux" and use_aura==1)', {
+ 'dependencies': [
+ '../build/linux/system.gyp:ssl',
+ ],
+ }],
+ ['OS=="mac"', {
+ 'include_dirs': [
+ '../third_party/GTM',
+ ],
+ # TODO(mark): We really want this for all non-static library
+ # targets, but when we tried to pull it up to the common.gypi
+ # level, it broke other things like the ui, startup, and
+ # page_cycler tests. *shrug*
+ 'xcode_settings': {
+ 'OTHER_LDFLAGS': [
+ '-Wl,-ObjC',
+ ],
+ },
+ # See the comment in this section of the unit_tests target for an
+ # explanation (crbug.com/43791 - libwebcore.a is too large to mmap).
+ 'dependencies+++': [
+ '../third_party/WebKit/Source/WebCore/WebCore.gyp/WebCore.gyp:webcore',
+ ],
+ }, { # else: OS != "mac"
+ 'sources!': [
+ 'browser/extensions/browser_action_test_util_mac.mm',
+ ],
+ }],
+ ['os_posix == 1 and OS != "mac"', {
+ 'conditions': [
+ ['linux_use_tcmalloc==1', {
+ 'dependencies': [
+ '../base/allocator/allocator.gyp:allocator',
+ ],
+ }],
+ ],
+ }],
+ ], # conditions
+ }, # target performance_browser_tests
+ {
# Executable that runs safebrowsing test in a new process.
'target_name': 'safe_browsing_tests',
'type': 'executable',
diff --git a/chrome/test/data/perf/latency_suite.html b/chrome/test/data/perf/latency_suite.html
new file mode 100644
index 0000000..a05ea82
--- /dev/null
+++ b/chrome/test/data/perf/latency_suite.html
@@ -0,0 +1,152 @@
+<html>
+<head>
+<script type="text/javascript">
+/**
+ * @fileoverview This page executes various animation behaviors based on URL
+ * arguments to test input latency. There are two main modes of
+ * operation: webgl and software. Both modes use
+ * requestAnimationFrame to drive the update rate. The basic task
+ * of the page is to collect mouse input coordinates in the input
+ * handler and render with the latest input coordinate in RAF. The
+ * latency test will look at the rendering trace data to detect
+ * the latest mouse coordinate that affected the frame. For
+ * software runs, the pixel at 0,0 on the page must contain the
+ * mouse coordinate encoded as a color.
+ */
+
+var frameCountWarmup = 5;
+var frameCount = 0;
+var gl = null;
+var mouseX = 0;
+var testParams = {};
+
+function parseParams() {
+ var query = window.location.search.substring(1);
+ if (!query)
+ return;
+ var params = query.split('&');
+ for (var i = 0, len = params.length; i < len; i++) {
+ var pair = params[i].split('=');
+ if (pair.length == 1)
+ testParams[pair[0]] = true;
+ else
+ testParams[pair[0]] = pair[1];
+ }
+}
+
+function setCoordinates(e) {
+ // Ignore mouse events with wrong Y coordinate.
+ if (e.clientY != parseInt(testParams.y))
+ return;
+
+ mouseX = e.clientX;
+ if (testParams.inputDirty) {
+ document.getElementById('text').firstChild.textContent =
+ mouseX.toString();
+ }
+ if (testParams.inputHeavy) {
+ sleep(parseInt(testParams.delayTimeMS));
+ }
+}
+
+function init() {
+ parseParams();
+
+ if (testParams.mode == 'webgl') {
+ var canvas = document.getElementById('canvas');
+ if (!canvas)
+ return false;
+ canvas.width = parseInt(testParams.canvasWidth);
+ canvas.height = parseInt(testParams.canvasWidth);
+ try {
+ // Specify antialiasing to ensure that we get a BlitFramebufferEXT in
+ // the trace when the compositor consumes a webgl frame.
+ gl = canvas.getContext('webgl', { antialias: true });
+ } catch (e) {}
+ if (!gl) {
+ try {
+ gl = canvas.getContext('experimental-webgl');
+ } catch (e) {
+ return false;
+ }
+ }
+ return true;
+ } else if (testParams.mode == 'software') {
+ var table = document.getElementById('table');
+ table.style.backgroundColor = '#ff00ff';
+ return true;
+ }
+}
+
+function onLoad() {
+ if (init())
+ window.webkitRequestAnimationFrame(draw);
+ else
+ endTest();
+}
+
+function sleep(milliseconds) {
+ var start = Date.now();
+ while(Date.now() - start <= milliseconds);
+}
+
+function draw() {
+ if (testParams.rafHeavy) {
+ sleep(parseInt(testParams.delayTimeMS));
+ }
+
+ if (testParams.mode == 'webgl') {
+ gl.viewport(0, 0, testParams.canvasWidth, testParams.canvasWidth);
+ if (testParams.paintHeavy) {
+ gl.clearColor(0, 0, 0.0, 1.0);
+ for (var i = 0; i < 1000; ++i)
+ gl.clear(gl.COLOR_BUFFER_BIT);
+ }
+ gl.clearColor(mouseX, testParams.clearColorGreen, 0.0, 1.0);
+ gl.clear(gl.COLOR_BUFFER_BIT);
+ } else if (testParams.mode == 'software') {
+ var table = document.getElementById('table');
+ // Encode mouse x value into color channels (support up to 64k x values).
+ var g = (mouseX & 0xff00) >> 8;
+ var b = (mouseX & 0xff);
+ table.style.backgroundColor = 'rgb(0, ' + g + ', ' + b + ')';
+ // When no inputs are coming in, the first table won't change. Since we
+ // still need to cause a paint, toggle the color of another element:
+ var table2 = document.getElementById('table2');
+ table2.style.backgroundColor = (frameCount & 1) ? 'gray' : 'silver';
+ if (testParams.paintHeavy) {
+ var body = document.getElementById('body');
+ body.style.backgroundColor = (frameCount & 1) ? 'silver' : 'gray';
+ }
+ }
+
+ frameCount++;
+ if (frameCount == parseInt(testParams.numFrames)) {
+ if (testParams.mode == 'webgl')
+ gl.finish();
+ endTest();
+ } else {
+ window.webkitRequestAnimationFrame(draw);
+ }
+}
+
+function endTest() {
+ domAutomationController.setAutomationId(1);
+ domAutomationController.send('FINISHED');
+}
+</script>
+</head>
+<style>
+#table {
+ height: 10px;
+ width: 10px;
+}
+</style>
+<body id="body" style="margin:0px" onload="onLoad()"
+ onmousemove="setCoordinates(event)">
+<table id="table"><tr/></table>
+<table id="table2"><tr/></table>
+<canvas id="canvas"></canvas>
+<p><b id="text">x</b></p>
+</body>
+</html>
diff --git a/chrome/test/perf/browser_perf_test.cc b/chrome/test/perf/browser_perf_test.cc
new file mode 100644
index 0000000..30d54a8
--- /dev/null
+++ b/chrome/test/perf/browser_perf_test.cc
@@ -0,0 +1,34 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/test/perf/browser_perf_test.h"
+
+#include "base/command_line.h"
+#include "base/process_util.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/test/base/chrome_process_util.h"
+#include "chrome/test/perf/perf_test.h"
+
+BrowserPerfTest::BrowserPerfTest() {
+}
+
+BrowserPerfTest::~BrowserPerfTest() {
+}
+
+void BrowserPerfTest::SetUpCommandLine(CommandLine* command_line) {
+ // Reduce performance test variance by disabling background networking.
+ command_line->AppendSwitch(switches::kDisableBackgroundNetworking);
+}
+
+void BrowserPerfTest::PrintIOPerfInfo(const std::string& test_name) {
+ base::ProcessId browser_pid = base::GetCurrentProcId();
+ ChromeProcessList chrome_processes(GetRunningChromeProcesses(browser_pid));
+ perf_test::PrintIOPerfInfo(test_name, chrome_processes, browser_pid);
+}
+
+void BrowserPerfTest::PrintMemoryUsageInfo(const std::string& test_name) {
+ base::ProcessId browser_pid = base::GetCurrentProcId();
+ ChromeProcessList chrome_processes(GetRunningChromeProcesses(browser_pid));
+ perf_test::PrintMemoryUsageInfo(test_name, chrome_processes, browser_pid);
+}
diff --git a/chrome/test/perf/browser_perf_test.h b/chrome/test/perf/browser_perf_test.h
new file mode 100644
index 0000000..3310345
--- /dev/null
+++ b/chrome/test/perf/browser_perf_test.h
@@ -0,0 +1,29 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_TEST_PERF_BROWSER_PERF_TEST_H_
+#define CHROME_TEST_PERF_BROWSER_PERF_TEST_H_
+#pragma once
+
+#include "chrome/test/base/in_process_browser_test.h"
+
+class CommandLine;
+
+class BrowserPerfTest : public InProcessBrowserTest {
+ public:
+ BrowserPerfTest();
+ virtual ~BrowserPerfTest();
+
+ // Set up common browser perf test flags. Typically call down to this if
+ // overridden.
+ virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE;
+
+ // Prints IO performance data for use by perf graphs.
+ void PrintIOPerfInfo(const std::string& test_name);
+
+ // Prints memory usage data for use by perf graphs.
+ void PrintMemoryUsageInfo(const std::string& test_name);
+};
+
+#endif // CHROME_TEST_PERF_BROWSER_PERF_TEST_H_
diff --git a/chrome/test/perf/rendering/OWNERS b/chrome/test/perf/rendering/OWNERS
new file mode 100644
index 0000000..209a23a
--- /dev/null
+++ b/chrome/test/perf/rendering/OWNERS
@@ -0,0 +1,3 @@
+nduca@chromium.org
+junov@chromium.org
+jbates@chromium.org
diff --git a/chrome/test/perf/rendering/latency_tests.cc b/chrome/test/perf/rendering/latency_tests.cc
new file mode 100644
index 0000000..576b2d8
--- /dev/null
+++ b/chrome/test/perf/rendering/latency_tests.cc
@@ -0,0 +1,615 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/command_line.h"
+#include "base/file_util.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/path_service.h"
+#include "base/string_number_conversions.h"
+#include "base/stringprintf.h"
+#include "base/test/test_switches.h"
+#include "base/test/trace_event_analyzer.h"
+#include "base/threading/platform_thread.h"
+#include "base/timer.h"
+#include "base/version.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
+#include "chrome/common/chrome_paths.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/test/base/tracing.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "chrome/test/perf/browser_perf_test.h"
+#include "chrome/test/perf/perf_test.h"
+#include "content/browser/renderer_host/render_view_host.h"
+#include "content/browser/tab_contents/tab_contents.h"
+#include "content/public/common/content_switches.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebInputEvent.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+// Run with --vmodule=latency_tests=1 to print verbose latency info.
+
+// How is latency measured?
+//
+// The test injects mouse moves many times per frame from the browser via
+// RenderWidgetHost. Each input has a unique x coordinate. When the javascript
+// handler receives the input, it stores the coordinate for later use in the
+// requestAnimationFrame callback. In RAF, the test paints using the x
+// coordinate as a color (in software, it sets the color of a table; in webgl,
+// it executes a glClearColor). Trace events emit the color when it is picked up
+// by either UpdateRect for software or gles2_cmd_decoder/glClear for webgl.
+//
+// Each UpdateRect (software) or SwapBuffers (webgl) is considered to be a frame
+// boundary that will be used to measure latency in number of frames. Starting
+// from a frame boundary Y, the test first determines what mouse x coordinate
+// was represented by the color at that frame boundary. Then, the test walks
+// backward through the trace events to find the input event matching that
+// x coordinate. Then, the test find the nearest frame boundary X to the input
+// event (may be before or after). The number of frame boundaries is then
+// counted between X and Y to determine the input latency.
+//
+// By injecting mouse moves many times per frame, we reduce flakiness in the
+// finding of the nearest frame boundary.
+//
+// This test only measures the latency introduced by chrome code -- it does not
+// measure latency introduced by mouse drivers or the GL driver or the OS window
+// manager. The actual latency seen by a user is more than what is reported by
+// this test.
+//
+// Current modes:
+// - Software RAF
+// - WebGL RAF
+
+namespace {
+
+using namespace trace_analyzer;
+
+enum LatencyTestMode {
+ kWebGL,
+ kSoftware
+};
+
+enum LatencyTestFlags {
+ kInputHeavy = 1 << 0,
+ kInputDirty = 1 << 1,
+ kRafHeavy = 1 << 2,
+ kPaintHeavy = 1 << 3
+};
+
+const int kWebGLCanvasWidth = 10;
+const int kNumFrames = 80;
+const int kInputsPerFrame = 16;
+// Magic number to identify certain glClear events.
+const int kClearColorGreen = 137;
+const int kMouseY = 5;
+
+// Don't analyze begin frames that may be inaccurate. Latencies can be as high
+// as 5 frames or so, so skip the first 6 frames to get more accurate results.
+const int kIgnoreBeginFrames = 6;
+// Don't analyze end frames that may be inaccurate.
+const int kIgnoreEndFrames = 4;
+// Minimum frames to produce an answer.
+const int kMinimumFramesForAnalysis = 5;
+
+class LatencyTest
+ : public BrowserPerfTest,
+ public ::testing::WithParamInterface<int> {
+ public:
+ LatencyTest() :
+ query_instant_(Query(EVENT_PHASE) ==
+ Query::Phase(TRACE_EVENT_PHASE_INSTANT)),
+ // These queries are initialized in RunTest.
+ query_swaps_(Query::Bool(false)),
+ query_inputs_(Query::Bool(false)),
+ query_blits_(Query::Bool(false)),
+ query_clears_(Query::Bool(false)),
+ mouse_x_(0),
+ tab_width_(0),
+ mode_(kWebGL),
+ delay_time_us_(0),
+ num_frames_(0),
+ verbose_(false),
+ test_flags_(0) {}
+
+ virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE;
+
+ std::vector<int> GetAllBehaviors();
+
+ // Run test with specified |mode| and |behaviors|.
+ // |mode| can be webgl or software.
+ // |behaviors| is a list of combinations of LatencyTestFlags.
+ void RunTest(LatencyTestMode mode, const std::vector<int>& behaviors);
+
+ private:
+ void RunTestInternal(const std::string& test_url,
+ bool send_inputs,
+ int input_delay_us);
+
+ double CalculateLatency();
+
+ std::string GetModeString() {
+ switch (mode_) {
+ case kWebGL:
+ return "webgl";
+ case kSoftware:
+ return "software";
+ default:
+ NOTREACHED() << "invalid mode";
+ return "";
+ }
+ }
+
+ std::string GetTraceName(int flags);
+
+ std::string GetUrlModeString(int flags);
+
+ std::string GetUrl(int flags);
+
+ void GetMeanFrameTimeMicros(int* frame_time) const;
+
+ void SendInput();
+
+ void PrintEvents(const TraceEventVector& events);
+
+ // Path to html file.
+ FilePath test_path_;
+
+ // Query INSTANT events.
+ Query query_instant_;
+
+ // Query "swaps" which is SwapBuffers for GL and UpdateRect for software.
+ Query query_swaps_;
+
+ // Query mouse input entry events in browser process (ForwardMouseEvent).
+ Query query_inputs_;
+
+ // Query GL blits for the WebGL canvas -- represents the compositor consuming
+ // the WebGL contents for display.
+ Query query_blits_;
+
+ // Query glClear calls with mouse coordinate as clear color.
+ Query query_clears_;
+
+ // For searching trace data.
+ scoped_ptr<TraceAnalyzer> analyzer_;
+
+ // Current mouse x coordinate for injecting events.
+ int mouse_x_;
+
+ // Width of window containing our tab.
+ int tab_width_;
+
+ // Timer for injecting mouse events periodically.
+ base::RepeatingTimer<LatencyTest> timer_;
+
+ // Mode: webgl or software.
+ LatencyTestMode mode_;
+
+ // Delay time for javascript test code. Typically 2 x frame duration. Used
+ // to spin-wait in the javascript input handler and requestAnimationFrame.
+ int delay_time_us_;
+
+ // Number of frames to render from the html test code.
+ int num_frames_;
+
+ // Map from test flags combination to the calculated mean latency.
+ std::map<int, double> latencies_;
+
+ // Whether to print more verbose output.
+ bool verbose_;
+
+ // Current test flags combination, determining the behavior of the test.
+ int test_flags_;
+};
+
+void LatencyTest::SetUpCommandLine(CommandLine* command_line) {
+ BrowserPerfTest::SetUpCommandLine(command_line);
+ // This enables DOM automation for tab contents.
+ EnableDOMAutomation();
+ if (CommandLine::ForCurrentProcess()->
+ HasSwitch(switches::kEnableThreadedCompositing)) {
+ command_line->AppendSwitch(switches::kEnableThreadedCompositing);
+ }
+ // Default behavior is to thumbnail the tab after 0.5 seconds, causing
+ // a nasty frame hitch and disturbing the latency test. Fix that:
+ command_line->AppendSwitch(switches::kEnableInBrowserThumbnailing);
+ command_line->AppendSwitch(switches::kDisableBackgroundNetworking);
+}
+
+std::vector<int> LatencyTest::GetAllBehaviors() {
+ std::vector<int> behaviors;
+ int max_behaviors = kInputHeavy | kInputDirty | kRafHeavy | kPaintHeavy;
+ for (int i = 0; i <= max_behaviors; ++i)
+ behaviors.push_back(i);
+ return behaviors;
+}
+
+void LatencyTest::RunTest(LatencyTestMode mode,
+ const std::vector<int>& behaviors) {
+ mode_ = mode;
+ verbose_ = (logging::GetVlogLevel("latency_tests") > 0);
+
+ // Construct queries for searching trace events via TraceAnalyzer.
+ if (mode_ == kWebGL) {
+ query_swaps_ = query_instant_ &&
+ Query(EVENT_NAME) == Query::String("SwapBuffers") &&
+ Query(EVENT_ARG, "width") != Query::Int(kWebGLCanvasWidth);
+ } else if (mode_ == kSoftware) {
+ // Software updates need to have x=0 and y=0 to contain the input color.
+ query_swaps_ = query_instant_ &&
+ Query(EVENT_NAME) == Query::String("UpdateRect") &&
+ Query(EVENT_ARG, "x+y") == Query::Int(0);
+ }
+ query_inputs_ = query_instant_ &&
+ Query(EVENT_NAME) == Query::String("MouseEventBegin");
+ query_blits_ = query_instant_ &&
+ Query(EVENT_NAME) == Query::String("DoBlit") &&
+ Query(EVENT_ARG, "width") == Query::Int(kWebGLCanvasWidth);
+ query_clears_ = query_instant_ &&
+ Query(EVENT_NAME) == Query::String("DoClear") &&
+ Query(EVENT_ARG, "green") == Query::Int(kClearColorGreen);
+ Query query_width_swaps = query_swaps_;
+ if (mode_ == kSoftware) {
+ query_width_swaps = query_instant_ &&
+ Query(EVENT_NAME) == Query::String("UpdateRectWidth") &&
+ Query(EVENT_ARG, "width") > Query::Int(kWebGLCanvasWidth);
+ }
+
+ // Set path to test html.
+ ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &test_path_));
+ test_path_ = test_path_.Append(FILE_PATH_LITERAL("perf"));
+ test_path_ = test_path_.Append(FILE_PATH_LITERAL("latency_suite.html"));
+ ASSERT_TRUE(file_util::PathExists(test_path_))
+ << "Missing test file: " << test_path_.value();
+
+ // Run once with defaults to measure the frame times.
+ delay_time_us_ = 0;
+ // kNumFrames may be very high, but we only need a few frames to measure
+ // average frame times.
+ num_frames_ = 30;
+ int initial_flags = 0;
+ if (mode_ == kSoftware) {
+ // For the first run, run software with kPaintHeavy (which toggles the
+ // background color every frame) to force an update each RAF. Otherwise it
+ // won't trigger an UpdateRect each frame and we won't be able to measure
+ // framerate, because there are no inputs during the first run.
+ initial_flags = static_cast<int>(kPaintHeavy);
+ }
+ RunTestInternal(GetUrl(initial_flags), false, 0);
+
+ // Get width of tab so that we know the limit of x coordinates for the
+ // injected mouse inputs.
+ const TraceEvent* swap_event = analyzer_->FindOneEvent(query_width_swaps);
+ ASSERT_TRUE(swap_event);
+ tab_width_ = swap_event->GetKnownArgAsInt("width");
+ // Keep printf output clean by limiting input coords to three digits:
+ tab_width_ = (tab_width_ < 1000) ? tab_width_ : 999;
+ // Sanity check the tab_width -- it should be more than 100 pixels.
+ EXPECT_GT(tab_width_, 100);
+
+ int mean_frame_time_us = 0;
+ GetMeanFrameTimeMicros(&mean_frame_time_us);
+ if (verbose_)
+ printf("Mean frame time micros = %d\n", mean_frame_time_us);
+ // Delay time is 2x the average frame time.
+ delay_time_us_ = 2 * mean_frame_time_us;
+ // Calculate delay time between inputs based on the measured frame time.
+ // This prevents flooding the browser with more events than we need if the
+ // test is running very slow (such as on a VM).
+ int delay_us = mean_frame_time_us / kInputsPerFrame;
+
+ // Reset num_frames_ for actual test runs.
+ num_frames_ = kNumFrames;
+
+ // Run input latency test with each requested behavior.
+ for (size_t i = 0; i < behaviors.size(); ++i) {
+ test_flags_ = behaviors[i];
+ std::string url = GetUrl(test_flags_);
+ printf("=============================================================\n");
+ if (verbose_)
+ printf("Mode: %s\n", GetUrlModeString(i).c_str());
+ printf("URL: %s\n", url.c_str());
+
+ // Do the actual test with input events.
+ RunTestInternal(url, true, delay_us);
+ latencies_[test_flags_] = CalculateLatency();
+ }
+
+ // Print summary if more than 1 behavior was tested in this run. This is only
+ // for manual test runs for human reabable results, not for perf bots.
+ if (behaviors.size() > 1) {
+ printf("#############################################################\n");
+ printf("## %s\n", GetModeString().c_str());
+ if (verbose_) {
+ printf("Latency, behavior:\n");
+ for (size_t i = 0; i < behaviors.size(); ++i) {
+ printf("%.1f, %s%s%s%s\n", latencies_[behaviors[i]],
+ (i & kInputHeavy) ? "InputHeavy " : "",
+ (i & kInputDirty) ? "InputDirty " : "",
+ (i & kRafHeavy) ? "RafHeavy " : "",
+ (i & kPaintHeavy) ? "PaintHeavy " : "");
+ }
+ }
+ printf("Latencies for tests: ");
+ for (size_t i = 0; i < behaviors.size(); ++i) {
+ printf("%.1f%s", latencies_[behaviors[i]],
+ (i < behaviors.size() - 1) ? ", " : "");
+ }
+ printf("\n");
+ printf("#############################################################\n");
+ }
+}
+
+void LatencyTest::RunTestInternal(const std::string& test_url,
+ bool send_inputs,
+ int input_delay_us) {
+ mouse_x_ = 0;
+
+ ASSERT_TRUE(tracing::BeginTracing("test_gpu,test_latency"));
+
+ ui_test_utils::NavigateToURLWithDisposition(
+ browser(), GURL(test_url), CURRENT_TAB,
+ ui_test_utils::BROWSER_TEST_NONE);
+
+ // Start sending mouse inputs.
+ if (send_inputs) {
+ // Round input_delay_us down to nearest milliseconds. The rounding in timer
+ // code rounds up from us to ms, so we need to do our own rounding here.
+ int input_delay_ms = input_delay_us / 1000;
+ input_delay_ms = (input_delay_ms <= 0) ? 1 : input_delay_ms;
+ timer_.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(input_delay_ms),
+ this, &LatencyTest::SendInput);
+ }
+
+ // Wait for message indicating the test has finished running.
+ ui_test_utils::DOMMessageQueue message_queue;
+ ASSERT_TRUE(message_queue.WaitForMessage(NULL));
+
+ timer_.Stop();
+
+ std::string json_events;
+ ASSERT_TRUE(tracing::EndTracing(&json_events));
+
+ analyzer_.reset(TraceAnalyzer::Create(json_events));
+ analyzer_->AssociateBeginEndEvents();
+ analyzer_->MergeAssociatedEventArgs();
+}
+
+double LatencyTest::CalculateLatency() {
+ TraceEventVector events;
+ if (mode_ == kWebGL) {
+ // Search for three types of events in WebGL mode:
+ // - onscreen swaps.
+ // - DoClear calls that contain the mouse x coordinate.
+ // - mouse events.
+ analyzer_->FindEvents(query_swaps_ || query_inputs_ ||
+ query_blits_ || query_clears_, &events);
+ } else if (mode_ == kSoftware) {
+ analyzer_->FindEvents(query_swaps_ || query_inputs_, &events);
+ } else {
+ NOTREACHED() << "invalid mode";
+ }
+
+ if (verbose_)
+ PrintEvents(events);
+
+ int swap_count = 0;
+ size_t previous_blit_pos = 0;
+ swap_count = 0;
+ std::vector<int> latencies;
+ printf("Measured latency (in number of frames) for each frame:\n");
+ for (size_t i = 0; i < events.size(); ++i) {
+ if (query_swaps_.Evaluate(*events[i])) {
+ // Compositor context swap buffers.
+ ++swap_count;
+ // Don't analyze first few swaps, because they are filling the rendering
+ // pipeline and may be unstable.
+ if (swap_count > kIgnoreBeginFrames) {
+ int mouse_x = 0;
+ if (mode_ == kWebGL) {
+ // Trace backwards through the events to find the input event that
+ // matches the glClear that was presented by this SwapBuffers.
+
+ // Step 1: Find the last blit (which will be the WebGL blit).
+ size_t blit_pos = 0;
+ EXPECT_TRUE(FindLastOf(events, query_blits_, i, &blit_pos));
+ // Skip this SwapBuffers if the blit has already been consumed by a
+ // previous SwapBuffers. This means the current frame did not receive
+ // an update from WebGL.
+ EXPECT_GT(blit_pos, previous_blit_pos);
+ if (blit_pos == previous_blit_pos) {
+ if (verbose_)
+ printf(" %03d: ERROR\n", swap_count);
+ else
+ printf(" ERROR");
+ continue;
+ }
+ previous_blit_pos = blit_pos;
+
+ // Step 2: find the last clear from the WebGL blit. This will be the
+ // value of the latest mouse input that has affected this swap.
+ size_t clear_pos = 0;
+ EXPECT_TRUE(FindLastOf(events, query_clears_, blit_pos, &clear_pos));
+ mouse_x = events[clear_pos]->GetKnownArgAsInt("red");
+ } else if (mode_ == kSoftware) {
+ // The software path gets the mouse_x directly from the DIB colors.
+ mouse_x = events[i]->GetKnownArgAsInt("color");
+ }
+
+ // Find the corresponding mouse input.
+ size_t input_pos = 0;
+ Query query_mouse_event = query_inputs_ &&
+ Query(EVENT_ARG, "x") == Query::Int(mouse_x);
+ EXPECT_TRUE(FindLastOf(events, query_mouse_event, i, &input_pos));
+
+ // Step 4: Find the nearest onscreen SwapBuffers to this input event.
+ size_t closest_swap = 0;
+ size_t second_closest_swap = 0;
+ EXPECT_TRUE(FindClosest(events, query_swaps_, input_pos,
+ &closest_swap, &second_closest_swap));
+ int latency = CountMatches(events, query_swaps_, closest_swap, i);
+ latencies.push_back(latency);
+ if (verbose_)
+ printf(" %03d: %d\n", swap_count, latency);
+ else
+ printf(" %d", latency);
+ }
+ }
+ }
+ printf("\n");
+
+ size_t ignoreEndFrames = static_cast<size_t>(kIgnoreEndFrames);
+ bool haveEnoughFrames = latencies.size() >
+ ignoreEndFrames + static_cast<size_t>(kMinimumFramesForAnalysis);
+ EXPECT_TRUE(haveEnoughFrames);
+ if (!haveEnoughFrames)
+ return 0.0;
+
+ double mean_latency = 0.0;
+ // Skip last few frames, because they may be unreliable.
+ size_t num_consider = latencies.size() - ignoreEndFrames;
+ for (size_t i = 0; i < num_consider; ++i)
+ mean_latency += static_cast<double>(latencies[i]);
+ mean_latency /= static_cast<double>(num_consider);
+ printf("Mean latency = %f\n", mean_latency);
+
+ double mean_error = 0.0;
+ for (size_t i = 0; i < num_consider; ++i) {
+ double offset = fabs(mean_latency - static_cast<double>(latencies[i]));
+ mean_error = (offset > mean_error) ? offset : mean_error;
+ }
+
+ std::string trace_name = GetTraceName(test_flags_);
+ std::string mean_and_error = base::StringPrintf("%f,%f", mean_latency,
+ mean_error);
+ perf_test::PrintResultMeanAndError(GetModeString(), "", trace_name,
+ mean_and_error, "frames", true);
+ return mean_latency;
+}
+
+std::string LatencyTest::GetTraceName(int flags) {
+ if (flags == 0)
+ return "simple";
+ std::string name;
+ if (flags & kInputHeavy)
+ name += "ih";
+ if (flags & kInputDirty)
+ name += std::string(name.empty()? "" : "_") + "id";
+ if (flags & kRafHeavy)
+ name += std::string(name.empty()? "" : "_") + "rh";
+ if (flags & kPaintHeavy)
+ name += std::string(name.empty()? "" : "_") + "ph";
+ return name;
+}
+
+std::string LatencyTest::GetUrlModeString(int flags) {
+ std::string mode = "&mode=" + GetModeString();
+ if (flags & kInputHeavy)
+ mode += "&inputHeavy";
+ if (flags & kInputDirty)
+ mode += "&inputDirty";
+ if (flags & kRafHeavy)
+ mode += "&rafHeavy";
+ if (flags & kPaintHeavy)
+ mode += "&paintHeavy";
+ return mode;
+}
+
+std::string LatencyTest::GetUrl(int flags) {
+ std::string test_url =
+ net::FilePathToFileURL(test_path_).possibly_invalid_spec();
+ test_url += "?numFrames=" + base::IntToString(num_frames_);
+ test_url += "&canvasWidth=" + base::IntToString(kWebGLCanvasWidth);
+ test_url += "&clearColorGreen=" + base::IntToString(kClearColorGreen);
+ test_url += "&delayTimeMS=" + base::IntToString(delay_time_us_ / 1000);
+ test_url += "&y=" + base::IntToString(kMouseY);
+ return test_url + GetUrlModeString(flags);
+}
+
+void LatencyTest::GetMeanFrameTimeMicros(int* frame_time) const {
+ TraceEventVector events;
+ // Search for compositor swaps (or UpdateRects in the software path).
+ analyzer_->FindEvents(query_swaps_, &events);
+ RateStats stats;
+ ASSERT_TRUE(GetRateStats(events, &stats));
+
+ // Check that the number of swaps is close to kNumFrames.
+ EXPECT_LT(num_frames_ - num_frames_ / 4, static_cast<int>(events.size()));
+ *frame_time = static_cast<int>(stats.mean_us);
+}
+
+void LatencyTest::SendInput() {
+ RenderViewHost* rvh = browser()->GetSelectedTabContentsWrapper()->
+ tab_contents()->GetRenderManagerForTesting()->current_host();
+ WebKit::WebMouseEvent mouse_event;
+ mouse_event.movementX = 1;
+ mouse_x_ += mouse_event.movementX;
+ // Wrap mouse_x_ when it's near the edge of the tab.
+ if (mouse_x_ > tab_width_ - 5)
+ mouse_x_ = 1;
+ mouse_event.x = mouse_event.windowX = mouse_x_;
+ // Set y coordinate to be a few pixels down from the top of the window,
+ // so that it is between the top and bottom of the canvas.
+ mouse_event.y = mouse_event.windowY = 5;
+ mouse_event.type = WebKit::WebInputEvent::MouseMove;
+ TRACE_EVENT_INSTANT1("test_latency", "MouseEventBegin", "x", mouse_x_);
+ rvh->ForwardMouseEvent(mouse_event);
+}
+
+void LatencyTest::PrintEvents(const TraceEventVector& events) {
+ bool is_software = (mode_ == kSoftware);
+ int swap_count = 0;
+ for (size_t i = 0; i < events.size(); ++i) {
+ if (events[i]->name == "MouseEventBegin") {
+ printf("%03d ", events[i]->GetKnownArgAsInt("x"));
+ } else if (events[i]->name == "DoClear") {
+ printf("Clr%03d ", events[i]->GetKnownArgAsInt("red"));
+ } else if (events[i]->name == "DoBlit") {
+ // WebGL context swap buffers.
+ printf("BLT ");
+ } else if (events[i]->name == "SwapBuffers") {
+ // Compositor context swap buffers.
+ ++swap_count;
+ printf("|\nframe %03d: ", swap_count + 1);
+ } else if (is_software && events[i]->name == "UpdateRect") {
+ ++swap_count;
+ printf("(%d)|\nframe %03d: ",
+ events[i]->GetKnownArgAsInt("color"), swap_count + 1);
+ }
+ }
+ printf("\n");
+}
+
+////////////////////////////////////////////////////////////////////////////////
+/// Tests
+
+using ::testing::Values;
+
+// For manual testing only, run all input latency tests and print summary.
+IN_PROC_BROWSER_TEST_F(LatencyTest, DISABLED_LatencyWebGLAll) {
+ RunTest(kWebGL, GetAllBehaviors());
+}
+
+// For manual testing only, run all input latency tests and print summary.
+IN_PROC_BROWSER_TEST_F(LatencyTest, DISABLED_LatencySoftwareAll) {
+ RunTest(kSoftware, GetAllBehaviors());
+}
+
+IN_PROC_BROWSER_TEST_P(LatencyTest, LatencySoftware) {
+ RunTest(kSoftware, std::vector<int>(1, GetParam()));
+}
+
+IN_PROC_BROWSER_TEST_P(LatencyTest, LatencyWebGL) {
+ RunTest(kWebGL, std::vector<int>(1, GetParam()));
+}
+
+INSTANTIATE_TEST_CASE_P(, LatencyTest, ::testing::Values(
+ 0,
+ kInputHeavy,
+ kInputHeavy | kInputDirty | kRafHeavy,
+ kInputHeavy | kInputDirty | kRafHeavy | kPaintHeavy,
+ kInputDirty | kPaintHeavy,
+ kInputDirty | kRafHeavy | kPaintHeavy
+ ));
+
+} // namespace
diff --git a/content/browser/renderer_host/render_widget_host.cc b/content/browser/renderer_host/render_widget_host.cc
index d48963e8..e0f3137 100644
--- a/content/browser/renderer_host/render_widget_host.cc
+++ b/content/browser/renderer_host/render_widget_host.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -1036,6 +1036,12 @@ void RenderWidgetHost::OnMsgUpdateRect(
UserMetricsAction("BadMessageTerminate_RWH1"));
process()->ReceivedBadMessage();
} else {
+ UNSHIPPED_TRACE_EVENT_INSTANT2("test_latency", "UpdateRect",
+ "x+y", params.bitmap_rect.x() + params.bitmap_rect.y(),
+ "color", 0xffffff & *static_cast<uint32*>(dib->memory()));
+ UNSHIPPED_TRACE_EVENT_INSTANT1("test_latency", "UpdateRectWidth",
+ "width", params.bitmap_rect.width());
+
// Scroll the backing store.
if (!params.scroll_rect.IsEmpty()) {
ScrollBackingStoreRect(params.dx, params.dy,
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc
index 10c1c5d0..814e454 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -3913,6 +3913,8 @@ error::Error GLES2DecoderImpl::HandleRegisterSharedIdsCHROMIUM(
void GLES2DecoderImpl::DoClear(GLbitfield mask) {
if (CheckBoundFramebuffersValid("glClear")) {
+ UNSHIPPED_TRACE_EVENT_INSTANT2("test_gpu", "DoClear", "red", clear_red_,
+ "green", clear_green_);
ApplyDirtyState();
glClear(mask);
}
@@ -4222,6 +4224,7 @@ void GLES2DecoderImpl::DoBlitFramebufferEXT(
glBlitFramebufferEXT(
srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter);
}
+ UNSHIPPED_TRACE_EVENT_INSTANT1("test_gpu", "DoBlit", "width", srcX1 - srcX0);
}
void GLES2DecoderImpl::DoRenderbufferStorageMultisample(
@@ -7338,8 +7341,10 @@ error::Error GLES2DecoderImpl::HandleSwapBuffers(
bool is_offscreen = !!offscreen_target_frame_buffer_.get();
int this_frame_number = frame_number_++;
// TRACE_EVENT for gpu tests:
- TRACE_EVENT_INSTANT1("test_gpu", "SwapBuffers",
- "GLImpl", static_cast<int>(gfx::GetGLImplementation()));
+ TRACE_EVENT_INSTANT2("test_gpu", "SwapBuffers",
+ "GLImpl", static_cast<int>(gfx::GetGLImplementation()),
+ "width", (is_offscreen ? offscreen_size_.width() :
+ surface_->GetSize().width()));
TRACE_EVENT2("gpu", "GLES2DecoderImpl::HandleSwapBuffers",
"offscreen", is_offscreen,
"frame", this_frame_number);