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