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