tab_capture_performancetest.cc revision f2477e01787aa58f445919b809d89e252beef54f
1// Copyright 2013 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/basictypes.h" 6#include "base/command_line.h" 7#if defined(OS_MACOSX) 8#include "base/mac/mac_util.h" 9#endif 10#include "base/strings/stringprintf.h" 11#include "base/test/trace_event_analyzer.h" 12#include "base/win/windows_version.h" 13#include "chrome/browser/extensions/extension_apitest.h" 14#include "chrome/browser/extensions/extension_service.h" 15#include "chrome/browser/extensions/extension_test_message_listener.h" 16#include "chrome/browser/extensions/tab_helper.h" 17#include "chrome/browser/profiles/profile.h" 18#include "chrome/browser/ui/fullscreen/fullscreen_controller.h" 19#include "chrome/common/chrome_switches.h" 20#include "chrome/common/chrome_version_info.h" 21#include "chrome/common/extensions/features/base_feature_provider.h" 22#include "chrome/common/extensions/features/complex_feature.h" 23#include "chrome/common/extensions/features/simple_feature.h" 24#include "chrome/test/base/test_launcher_utils.h" 25#include "chrome/test/base/test_switches.h" 26#include "chrome/test/base/tracing.h" 27#include "content/public/browser/render_process_host.h" 28#include "content/public/browser/render_view_host.h" 29#include "content/public/common/content_switches.h" 30#include "extensions/common/feature_switch.h" 31#include "extensions/common/features/feature.h" 32#include "testing/gtest/include/gtest/gtest.h" 33#include "testing/perf/perf_test.h" 34#include "ui/compositor/compositor_switches.h" 35#include "ui/gl/gl_switches.h" 36 37namespace { 38 39const char kExtensionId[] = "ddchlicdkolnonkihahngkmmmjnjlkkf"; 40 41enum TestFlags { 42 kUseGpu = 1 << 0, // Only execute test if --enable-gpu was given 43 // on the command line. This is required for 44 // tests that run on GPU. 45 kForceGpuComposited = 1 << 1, // Force the test to use the compositor. 46 kDisableVsync = 1 << 2, // Do not limit framerate to vertical refresh. 47 // when on GPU, nor to 60hz when not on GPU. 48 kTestThroughWebRTC = 1 << 3, // Send captured frames through webrtc 49 kSmallWindow = 1 << 4, // 1 = 800x600, 0 = 2000x1000 50 51 kScaleQualityMask = 3 << 5, // two bits select which scaling quality 52 kScaleQualityDefault = 0 << 5, // to use on aura. 53 kScaleQualityFast = 1 << 5, 54 kScaleQualityGood = 2 << 5, 55 kScaleQualityBest = 3 << 5, 56}; 57 58class TabCapturePerformanceTest 59 : public ExtensionApiTest, 60 public testing::WithParamInterface<int> { 61 public: 62 TabCapturePerformanceTest() {} 63 64 bool HasFlag(TestFlags flag) const { 65 return (GetParam() & flag) == flag; 66 } 67 68 bool IsGpuAvailable() const { 69 return CommandLine::ForCurrentProcess()->HasSwitch("enable-gpu"); 70 } 71 72 std::string ScalingMethod() const { 73 switch (GetParam() & kScaleQualityMask) { 74 case kScaleQualityFast: 75 return "fast"; 76 case kScaleQualityGood: 77 return "good"; 78 case kScaleQualityBest: 79 return "best"; 80 default: 81 return ""; 82 } 83 } 84 85 std::string GetSuffixForTestFlags() { 86 std::string suffix; 87 if (HasFlag(kForceGpuComposited)) 88 suffix += "_comp"; 89 if (HasFlag(kUseGpu)) 90 suffix += "_gpu"; 91 if (HasFlag(kDisableVsync)) 92 suffix += "_novsync"; 93 if (HasFlag(kTestThroughWebRTC)) 94 suffix += "_webrtc"; 95 if (!ScalingMethod().empty()) 96 suffix += "_scale" + ScalingMethod(); 97 if (HasFlag(kSmallWindow)) 98 suffix += "_small"; 99 return suffix; 100 } 101 102 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE { 103 if (!ScalingMethod().empty()) { 104 command_line->AppendSwitchASCII(switches::kTabCaptureUpscaleQuality, 105 ScalingMethod()); 106 command_line->AppendSwitchASCII(switches::kTabCaptureDownscaleQuality, 107 ScalingMethod()); 108 } 109 110 // UI tests boot up render views starting from about:blank. This causes 111 // the renderer to start up thinking it cannot use the GPU. To work 112 // around that, and allow the frame rate test to use the GPU, we must 113 // pass kAllowWebUICompositing. 114 command_line->AppendSwitch(switches::kAllowWebUICompositing); 115 116 // Some of the tests may launch http requests through JSON or AJAX 117 // which causes a security error (cross domain request) when the page 118 // is loaded from the local file system ( file:// ). The following switch 119 // fixes that error. 120 command_line->AppendSwitch(switches::kAllowFileAccessFromFiles); 121 122 if (HasFlag(kSmallWindow)) { 123 command_line->AppendSwitchASCII(switches::kWindowSize, "800,600"); 124 } else { 125 command_line->AppendSwitchASCII(switches::kWindowSize, "2000,1500"); 126 } 127 128 UseRealGLContexts(); 129 130 if (!HasFlag(kUseGpu)) { 131 command_line->AppendSwitch(switches::kDisableGpu); 132 } else { 133 command_line->AppendSwitch(switches::kForceCompositingMode); 134 } 135 136 if (HasFlag(kDisableVsync)) 137 command_line->AppendSwitch(switches::kDisableGpuVsync); 138 139 command_line->AppendSwitchASCII(switches::kWhitelistedExtensionID, 140 kExtensionId); 141 ExtensionApiTest::SetUpCommandLine(command_line); 142 } 143 144 bool PrintResults(trace_analyzer::TraceAnalyzer *analyzer, 145 const std::string& test_name, 146 const std::string& event_name, 147 const std::string& unit) { 148 trace_analyzer::TraceEventVector events; 149 trace_analyzer::Query query = 150 trace_analyzer::Query::EventNameIs(event_name) && 151 (trace_analyzer::Query::EventPhaseIs(TRACE_EVENT_PHASE_BEGIN) || 152 trace_analyzer::Query::EventPhaseIs(TRACE_EVENT_PHASE_ASYNC_BEGIN) || 153 trace_analyzer::Query::EventPhaseIs(TRACE_EVENT_PHASE_FLOW_BEGIN) || 154 trace_analyzer::Query::EventPhaseIs(TRACE_EVENT_PHASE_INSTANT)); 155 analyzer->FindEvents(query, &events); 156 if (events.size() < 20) { 157 LOG(INFO) << "Not enough events of type " << event_name << " found."; 158 return false; 159 } 160 161 // Ignore some events for startup/setup/caching. 162 trace_analyzer::TraceEventVector rate_events(events.begin() + 3, 163 events.end() - 3); 164 trace_analyzer::RateStats stats; 165 if (!GetRateStats(rate_events, &stats, NULL)) { 166 LOG(INFO) << "GetRateStats failed"; 167 return false; 168 } 169 double mean_ms = stats.mean_us / 1000.0; 170 double std_dev_ms = stats.standard_deviation_us / 1000.0; 171 std::string mean_and_error = base::StringPrintf("%f,%f", mean_ms, 172 std_dev_ms); 173 perf_test::PrintResultMeanAndError(test_name, 174 GetSuffixForTestFlags(), 175 event_name, 176 mean_and_error, 177 unit, 178 true); 179 return true; 180 } 181 182 void RunTest(const std::string& test_name) { 183 if (HasFlag(kUseGpu) && !IsGpuAvailable()) { 184 LOG(WARNING) << 185 "Test skipped: requires gpu. Pass --enable-gpu on the command " 186 "line if use of GPU is desired."; 187 return; 188 } 189 190 std::string json_events; 191 ASSERT_TRUE(tracing::BeginTracing("test_fps,mirroring")); 192 std::string page = "performance.html"; 193 page += HasFlag(kTestThroughWebRTC) ? "?WebRTC=1" : "?WebRTC=0"; 194 // Ideally we'd like to run a higher capture rate when vsync is disabled, 195 // but libjingle currently doesn't allow that. 196 // page += HasFlag(kDisableVsync) ? "&fps=300" : "&fps=30"; 197 page += "&fps=30"; 198 ASSERT_TRUE(RunExtensionSubtest("tab_capture/experimental", page)) 199 << message_; 200 ASSERT_TRUE(tracing::EndTracing(&json_events)); 201 scoped_ptr<trace_analyzer::TraceAnalyzer> analyzer; 202 analyzer.reset(trace_analyzer::TraceAnalyzer::Create(json_events)); 203 204 // Only one of these PrintResults should actually print something. 205 // The printed result will be the average time between frames in the 206 // browser window. 207 bool sw_frames = PrintResults(analyzer.get(), 208 test_name, 209 "TestFrameTickSW", 210 "frame_time"); 211 bool gpu_frames = PrintResults(analyzer.get(), 212 test_name, 213 "TestFrameTickGPU", 214 "frame_time"); 215 EXPECT_TRUE(sw_frames || gpu_frames); 216 EXPECT_NE(sw_frames, gpu_frames); 217 218 // This prints out the average time between capture events. 219 // As the capture frame rate is capped at 30fps, this score 220 // cannot get any better than 33.33 ms. 221 EXPECT_TRUE(PrintResults(analyzer.get(), 222 test_name, 223 "Capture", 224 "capture_time")); 225 } 226}; 227 228} // namespace 229 230IN_PROC_BROWSER_TEST_P(TabCapturePerformanceTest, Performance) { 231 RunTest("TabCapturePerformance"); 232} 233 234// Note: First argument is optional and intentionally left blank. 235// (it's a prefix for the generated test cases) 236INSTANTIATE_TEST_CASE_P( 237 , 238 TabCapturePerformanceTest, 239 testing::Values( 240 0, 241 kUseGpu | kForceGpuComposited, 242 kDisableVsync, 243 kDisableVsync | kUseGpu | kForceGpuComposited, 244 kTestThroughWebRTC, 245 kTestThroughWebRTC | kUseGpu | kForceGpuComposited, 246 kTestThroughWebRTC | kDisableVsync, 247 kTestThroughWebRTC | kDisableVsync | kUseGpu | kForceGpuComposited)); 248 249#if defined(USE_AURA) 250// TODO(hubbe): 251// These are temporary tests for the purpose of determining what the 252// appropriate scaling quality is. Once that has been determined, 253// these tests will be removed. 254 255const int kScalingTestBase = 256 kTestThroughWebRTC | kDisableVsync | kUseGpu | kForceGpuComposited; 257 258INSTANTIATE_TEST_CASE_P( 259 ScalingTests, 260 TabCapturePerformanceTest, 261 testing::Values( 262 kScalingTestBase | kScaleQualityFast, 263 kScalingTestBase | kScaleQualityGood, 264 kScalingTestBase | kScaleQualityBest, 265 kScalingTestBase | kScaleQualityFast | kSmallWindow, 266 kScalingTestBase | kScaleQualityGood | kSmallWindow, 267 kScalingTestBase | kScaleQualityBest | kSmallWindow)); 268 269#endif // USE_AURA 270