1 // Copyright (c) 2012 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 "content/browser/gpu/gpu_internals_ui.h" 6 7 #include <string> 8 9 #include "base/bind.h" 10 #include "base/bind_helpers.h" 11 #include "base/command_line.h" 12 #include "base/i18n/time_formatting.h" 13 #include "base/strings/string_number_conversions.h" 14 #include "base/strings/stringprintf.h" 15 #include "base/sys_info.h" 16 #include "base/values.h" 17 #include "content/browser/gpu/compositor_util.h" 18 #include "content/browser/gpu/gpu_data_manager_impl.h" 19 #include "content/public/browser/browser_thread.h" 20 #include "content/public/browser/gpu_data_manager_observer.h" 21 #include "content/public/browser/web_contents.h" 22 #include "content/public/browser/web_ui.h" 23 #include "content/public/browser/web_ui_data_source.h" 24 #include "content/public/browser/web_ui_message_handler.h" 25 #include "content/public/common/content_client.h" 26 #include "content/public/common/content_switches.h" 27 #include "content/public/common/url_constants.h" 28 #include "gpu/config/gpu_feature_type.h" 29 #include "gpu/config/gpu_info.h" 30 #include "grit/content_resources.h" 31 #include "third_party/angle/src/common/version.h" 32 33 #if defined(OS_WIN) 34 #include "ui/base/win/shell.h" 35 #endif 36 37 namespace content { 38 namespace { 39 40 WebUIDataSource* CreateGpuHTMLSource() { 41 WebUIDataSource* source = WebUIDataSource::Create(kChromeUIGpuHost); 42 43 source->SetJsonPath("strings.js"); 44 source->AddResourcePath("gpu_internals.js", IDR_GPU_INTERNALS_JS); 45 source->SetDefaultResource(IDR_GPU_INTERNALS_HTML); 46 return source; 47 } 48 49 base::DictionaryValue* NewDescriptionValuePair(const std::string& desc, 50 const std::string& value) { 51 base::DictionaryValue* dict = new base::DictionaryValue(); 52 dict->SetString("description", desc); 53 dict->SetString("value", value); 54 return dict; 55 } 56 57 base::DictionaryValue* NewDescriptionValuePair(const std::string& desc, 58 base::Value* value) { 59 base::DictionaryValue* dict = new base::DictionaryValue(); 60 dict->SetString("description", desc); 61 dict->Set("value", value); 62 return dict; 63 } 64 65 #if defined(OS_WIN) 66 // Output DxDiagNode tree as nested array of {description,value} pairs 67 base::ListValue* DxDiagNodeToList(const gpu::DxDiagNode& node) { 68 base::ListValue* list = new base::ListValue(); 69 for (std::map<std::string, std::string>::const_iterator it = 70 node.values.begin(); 71 it != node.values.end(); 72 ++it) { 73 list->Append(NewDescriptionValuePair(it->first, it->second)); 74 } 75 76 for (std::map<std::string, gpu::DxDiagNode>::const_iterator it = 77 node.children.begin(); 78 it != node.children.end(); 79 ++it) { 80 base::ListValue* sublist = DxDiagNodeToList(it->second); 81 list->Append(NewDescriptionValuePair(it->first, sublist)); 82 } 83 return list; 84 } 85 #endif 86 87 std::string GPUDeviceToString(const gpu::GPUInfo::GPUDevice& gpu) { 88 std::string vendor = base::StringPrintf("0x%04x", gpu.vendor_id); 89 if (!gpu.vendor_string.empty()) 90 vendor += " [" + gpu.vendor_string + "]"; 91 std::string device = base::StringPrintf("0x%04x", gpu.device_id); 92 if (!gpu.device_string.empty()) 93 device += " [" + gpu.device_string + "]"; 94 return base::StringPrintf("VENDOR = %s, DEVICE= %s%s", 95 vendor.c_str(), device.c_str(), gpu.active ? " *ACTIVE*" : ""); 96 } 97 98 base::DictionaryValue* GpuInfoAsDictionaryValue() { 99 gpu::GPUInfo gpu_info = GpuDataManagerImpl::GetInstance()->GetGPUInfo(); 100 base::ListValue* basic_info = new base::ListValue(); 101 basic_info->Append(NewDescriptionValuePair( 102 "Initialization time", 103 base::Int64ToString(gpu_info.initialization_time.InMilliseconds()))); 104 basic_info->Append(NewDescriptionValuePair( 105 "Sandboxed", new base::FundamentalValue(gpu_info.sandboxed))); 106 basic_info->Append(NewDescriptionValuePair( 107 "GPU0", GPUDeviceToString(gpu_info.gpu))); 108 for (size_t i = 0; i < gpu_info.secondary_gpus.size(); ++i) { 109 basic_info->Append(NewDescriptionValuePair( 110 base::StringPrintf("GPU%d", static_cast<int>(i + 1)), 111 GPUDeviceToString(gpu_info.secondary_gpus[i]))); 112 } 113 basic_info->Append(NewDescriptionValuePair( 114 "Optimus", new base::FundamentalValue(gpu_info.optimus))); 115 basic_info->Append(NewDescriptionValuePair( 116 "AMD switchable", new base::FundamentalValue(gpu_info.amd_switchable))); 117 if (gpu_info.lenovo_dcute) { 118 basic_info->Append(NewDescriptionValuePair( 119 "Lenovo dCute", new base::FundamentalValue(true))); 120 } 121 if (gpu_info.display_link_version.IsValid()) { 122 basic_info->Append(NewDescriptionValuePair( 123 "DisplayLink Version", gpu_info.display_link_version.GetString())); 124 } 125 #if defined(OS_WIN) 126 std::string compositor = 127 ui::win::IsAeroGlassEnabled() ? "Aero Glass" : "none"; 128 basic_info->Append( 129 NewDescriptionValuePair("Desktop compositing", compositor)); 130 #endif 131 132 basic_info->Append( 133 NewDescriptionValuePair("Driver vendor", gpu_info.driver_vendor)); 134 basic_info->Append(NewDescriptionValuePair("Driver version", 135 gpu_info.driver_version)); 136 basic_info->Append(NewDescriptionValuePair("Driver date", 137 gpu_info.driver_date)); 138 basic_info->Append(NewDescriptionValuePair("Pixel shader version", 139 gpu_info.pixel_shader_version)); 140 basic_info->Append(NewDescriptionValuePair("Vertex shader version", 141 gpu_info.vertex_shader_version)); 142 basic_info->Append(NewDescriptionValuePair("Machine model name", 143 gpu_info.machine_model_name)); 144 basic_info->Append(NewDescriptionValuePair("Machine model version", 145 gpu_info.machine_model_version)); 146 basic_info->Append(NewDescriptionValuePair("GL_VENDOR", 147 gpu_info.gl_vendor)); 148 basic_info->Append(NewDescriptionValuePair("GL_RENDERER", 149 gpu_info.gl_renderer)); 150 basic_info->Append(NewDescriptionValuePair("GL_VERSION", 151 gpu_info.gl_version)); 152 basic_info->Append(NewDescriptionValuePair("GL_EXTENSIONS", 153 gpu_info.gl_extensions)); 154 basic_info->Append(NewDescriptionValuePair("Window system binding vendor", 155 gpu_info.gl_ws_vendor)); 156 basic_info->Append(NewDescriptionValuePair("Window system binding version", 157 gpu_info.gl_ws_version)); 158 basic_info->Append(NewDescriptionValuePair("Window system binding extensions", 159 gpu_info.gl_ws_extensions)); 160 std::string direct_rendering = gpu_info.direct_rendering ? "Yes" : "No"; 161 basic_info->Append( 162 NewDescriptionValuePair("Direct rendering", direct_rendering)); 163 164 std::string reset_strategy = 165 base::StringPrintf("0x%04x", gpu_info.gl_reset_notification_strategy); 166 basic_info->Append(NewDescriptionValuePair( 167 "Reset notification strategy", reset_strategy)); 168 169 base::DictionaryValue* info = new base::DictionaryValue(); 170 info->Set("basic_info", basic_info); 171 172 #if defined(OS_WIN) 173 base::ListValue* perf_info = new base::ListValue(); 174 perf_info->Append(NewDescriptionValuePair( 175 "Graphics", 176 base::StringPrintf("%.1f", gpu_info.performance_stats.graphics))); 177 perf_info->Append(NewDescriptionValuePair( 178 "Gaming", 179 base::StringPrintf("%.1f", gpu_info.performance_stats.gaming))); 180 perf_info->Append(NewDescriptionValuePair( 181 "Overall", 182 base::StringPrintf("%.1f", gpu_info.performance_stats.overall))); 183 info->Set("performance_info", perf_info); 184 185 base::Value* dx_info = gpu_info.dx_diagnostics.children.size() ? 186 DxDiagNodeToList(gpu_info.dx_diagnostics) : 187 base::Value::CreateNullValue(); 188 info->Set("diagnostics", dx_info); 189 #endif 190 191 return info; 192 } 193 194 // This class receives javascript messages from the renderer. 195 // Note that the WebUI infrastructure runs on the UI thread, therefore all of 196 // this class's methods are expected to run on the UI thread. 197 class GpuMessageHandler 198 : public WebUIMessageHandler, 199 public base::SupportsWeakPtr<GpuMessageHandler>, 200 public GpuDataManagerObserver { 201 public: 202 GpuMessageHandler(); 203 virtual ~GpuMessageHandler(); 204 205 // WebUIMessageHandler implementation. 206 virtual void RegisterMessages() OVERRIDE; 207 208 // GpuDataManagerObserver implementation. 209 virtual void OnGpuInfoUpdate() OVERRIDE; 210 virtual void OnGpuSwitching() OVERRIDE; 211 212 // Messages 213 void OnBrowserBridgeInitialized(const base::ListValue* list); 214 void OnCallAsync(const base::ListValue* list); 215 216 // Submessages dispatched from OnCallAsync 217 base::Value* OnRequestClientInfo(const base::ListValue* list); 218 base::Value* OnRequestLogMessages(const base::ListValue* list); 219 220 private: 221 // True if observing the GpuDataManager (re-attaching as observer would 222 // DCHECK). 223 bool observing_; 224 225 DISALLOW_COPY_AND_ASSIGN(GpuMessageHandler); 226 }; 227 228 //////////////////////////////////////////////////////////////////////////////// 229 // 230 // GpuMessageHandler 231 // 232 //////////////////////////////////////////////////////////////////////////////// 233 234 GpuMessageHandler::GpuMessageHandler() 235 : observing_(false) { 236 } 237 238 GpuMessageHandler::~GpuMessageHandler() { 239 GpuDataManagerImpl::GetInstance()->RemoveObserver(this); 240 } 241 242 /* BrowserBridge.callAsync prepends a requestID to these messages. */ 243 void GpuMessageHandler::RegisterMessages() { 244 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 245 246 web_ui()->RegisterMessageCallback("browserBridgeInitialized", 247 base::Bind(&GpuMessageHandler::OnBrowserBridgeInitialized, 248 base::Unretained(this))); 249 web_ui()->RegisterMessageCallback("callAsync", 250 base::Bind(&GpuMessageHandler::OnCallAsync, 251 base::Unretained(this))); 252 } 253 254 void GpuMessageHandler::OnCallAsync(const base::ListValue* args) { 255 DCHECK_GE(args->GetSize(), static_cast<size_t>(2)); 256 // unpack args into requestId, submessage and submessageArgs 257 bool ok; 258 const base::Value* requestId; 259 ok = args->Get(0, &requestId); 260 DCHECK(ok); 261 262 std::string submessage; 263 ok = args->GetString(1, &submessage); 264 DCHECK(ok); 265 266 base::ListValue* submessageArgs = new base::ListValue(); 267 for (size_t i = 2; i < args->GetSize(); ++i) { 268 const base::Value* arg; 269 ok = args->Get(i, &arg); 270 DCHECK(ok); 271 272 base::Value* argCopy = arg->DeepCopy(); 273 submessageArgs->Append(argCopy); 274 } 275 276 // call the submessage handler 277 base::Value* ret = NULL; 278 if (submessage == "requestClientInfo") { 279 ret = OnRequestClientInfo(submessageArgs); 280 } else if (submessage == "requestLogMessages") { 281 ret = OnRequestLogMessages(submessageArgs); 282 } else { // unrecognized submessage 283 NOTREACHED(); 284 delete submessageArgs; 285 return; 286 } 287 delete submessageArgs; 288 289 // call BrowserBridge.onCallAsyncReply with result 290 if (ret) { 291 web_ui()->CallJavascriptFunction("browserBridge.onCallAsyncReply", 292 *requestId, 293 *ret); 294 delete ret; 295 } else { 296 web_ui()->CallJavascriptFunction("browserBridge.onCallAsyncReply", 297 *requestId); 298 } 299 } 300 301 void GpuMessageHandler::OnBrowserBridgeInitialized( 302 const base::ListValue* args) { 303 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 304 305 // Watch for changes in GPUInfo 306 if (!observing_) 307 GpuDataManagerImpl::GetInstance()->AddObserver(this); 308 observing_ = true; 309 310 // Tell GpuDataManager it should have full GpuInfo. If the 311 // Gpu process has not run yet, this will trigger its launch. 312 GpuDataManagerImpl::GetInstance()->RequestCompleteGpuInfoIfNeeded(); 313 314 // Run callback immediately in case the info is ready and no update in the 315 // future. 316 OnGpuInfoUpdate(); 317 } 318 319 base::Value* GpuMessageHandler::OnRequestClientInfo( 320 const base::ListValue* list) { 321 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 322 323 base::DictionaryValue* dict = new base::DictionaryValue(); 324 325 dict->SetString("version", GetContentClient()->GetProduct()); 326 dict->SetString("command_line", 327 CommandLine::ForCurrentProcess()->GetCommandLineString()); 328 dict->SetString("operating_system", 329 base::SysInfo::OperatingSystemName() + " " + 330 base::SysInfo::OperatingSystemVersion()); 331 dict->SetString("angle_commit_id", ANGLE_COMMIT_HASH); 332 dict->SetString("graphics_backend", "Skia"); 333 dict->SetString("blacklist_version", 334 GpuDataManagerImpl::GetInstance()->GetBlacklistVersion()); 335 dict->SetString("driver_bug_list_version", 336 GpuDataManagerImpl::GetInstance()->GetDriverBugListVersion()); 337 338 return dict; 339 } 340 341 base::Value* GpuMessageHandler::OnRequestLogMessages(const base::ListValue*) { 342 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 343 344 return GpuDataManagerImpl::GetInstance()->GetLogMessages(); 345 } 346 347 void GpuMessageHandler::OnGpuInfoUpdate() { 348 // Get GPU Info. 349 scoped_ptr<base::DictionaryValue> gpu_info_val(GpuInfoAsDictionaryValue()); 350 351 // Add in blacklisting features 352 base::DictionaryValue* feature_status = new base::DictionaryValue; 353 feature_status->Set("featureStatus", GetFeatureStatus()); 354 feature_status->Set("problems", GetProblems()); 355 feature_status->Set("workarounds", GetDriverBugWorkarounds()); 356 if (feature_status) 357 gpu_info_val->Set("featureStatus", feature_status); 358 359 // Send GPU Info to javascript. 360 web_ui()->CallJavascriptFunction("browserBridge.onGpuInfoUpdate", 361 *(gpu_info_val.get())); 362 } 363 364 void GpuMessageHandler::OnGpuSwitching() { 365 GpuDataManagerImpl::GetInstance()->RequestCompleteGpuInfoIfNeeded(); 366 } 367 368 } // namespace 369 370 371 //////////////////////////////////////////////////////////////////////////////// 372 // 373 // GpuInternalsUI 374 // 375 //////////////////////////////////////////////////////////////////////////////// 376 377 GpuInternalsUI::GpuInternalsUI(WebUI* web_ui) 378 : WebUIController(web_ui) { 379 web_ui->AddMessageHandler(new GpuMessageHandler()); 380 381 // Set up the chrome://gpu/ source. 382 BrowserContext* browser_context = 383 web_ui->GetWebContents()->GetBrowserContext(); 384 WebUIDataSource::Add(browser_context, CreateGpuHTMLSource()); 385 } 386 387 } // namespace content 388