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.
5 #include "content/browser/gpu/gpu_internals_ui.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/grit/content_resources.h"
20 #include "content/public/browser/browser_thread.h"
21 #include "content/public/browser/gpu_data_manager_observer.h"
22 #include "content/public/browser/web_contents.h"
23 #include "content/public/browser/web_ui.h"
24 #include "content/public/browser/web_ui_data_source.h"
25 #include "content/public/browser/web_ui_message_handler.h"
26 #include "content/public/common/content_client.h"
27 #include "content/public/common/content_switches.h"
28 #include "content/public/common/url_constants.h"
29 #include "gpu/config/gpu_feature_type.h"
30 #include "gpu/config/gpu_info.h"
31 #include "third_party/angle/src/common/version.h"
34 #include "ui/base/win/shell.h"
40 WebUIDataSource
* CreateGpuHTMLSource() {
41 WebUIDataSource
* source
= WebUIDataSource::Create(kChromeUIGpuHost
);
43 source
->SetJsonPath("strings.js");
44 source
->AddResourcePath("gpu_internals.js", IDR_GPU_INTERNALS_JS
);
45 source
->SetDefaultResource(IDR_GPU_INTERNALS_HTML
);
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
);
57 base::DictionaryValue
* NewDescriptionValuePair(const std::string
& desc
,
59 base::DictionaryValue
* dict
= new base::DictionaryValue();
60 dict
->SetString("description", desc
);
61 dict
->Set("value", value
);
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
=
71 it
!= node
.values
.end();
73 list
->Append(NewDescriptionValuePair(it
->first
, it
->second
));
76 for (std::map
<std::string
, gpu::DxDiagNode
>::const_iterator it
=
77 node
.children
.begin();
78 it
!= node
.children
.end();
80 base::ListValue
* sublist
= DxDiagNodeToList(it
->second
);
81 list
->Append(NewDescriptionValuePair(it
->first
, sublist
));
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*" : "");
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
])));
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)));
121 if (gpu_info
.display_link_version
.IsValid()) {
122 basic_info
->Append(NewDescriptionValuePair(
123 "DisplayLink Version", gpu_info
.display_link_version
.GetString()));
126 std::string compositor
=
127 ui::win::IsAeroGlassEnabled() ? "Aero Glass" : "none";
129 NewDescriptionValuePair("Desktop compositing", compositor
));
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";
162 NewDescriptionValuePair("Direct rendering", direct_rendering
));
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
));
169 basic_info
->Append(NewDescriptionValuePair(
170 "GPU process crash count",
171 new base::FundamentalValue(gpu_info
.process_crash_count
)));
173 base::DictionaryValue
* info
= new base::DictionaryValue();
174 info
->Set("basic_info", basic_info
);
177 base::ListValue
* perf_info
= new base::ListValue();
178 perf_info
->Append(NewDescriptionValuePair(
180 base::StringPrintf("%.1f", gpu_info
.performance_stats
.graphics
)));
181 perf_info
->Append(NewDescriptionValuePair(
183 base::StringPrintf("%.1f", gpu_info
.performance_stats
.gaming
)));
184 perf_info
->Append(NewDescriptionValuePair(
186 base::StringPrintf("%.1f", gpu_info
.performance_stats
.overall
)));
187 info
->Set("performance_info", perf_info
);
189 base::Value
* dx_info
= gpu_info
.dx_diagnostics
.children
.size() ?
190 DxDiagNodeToList(gpu_info
.dx_diagnostics
) :
191 base::Value::CreateNullValue();
192 info
->Set("diagnostics", dx_info
);
198 // This class receives javascript messages from the renderer.
199 // Note that the WebUI infrastructure runs on the UI thread, therefore all of
200 // this class's methods are expected to run on the UI thread.
201 class GpuMessageHandler
202 : public WebUIMessageHandler
,
203 public base::SupportsWeakPtr
<GpuMessageHandler
>,
204 public GpuDataManagerObserver
{
207 virtual ~GpuMessageHandler();
209 // WebUIMessageHandler implementation.
210 virtual void RegisterMessages() OVERRIDE
;
212 // GpuDataManagerObserver implementation.
213 virtual void OnGpuInfoUpdate() OVERRIDE
;
214 virtual void OnGpuSwitching() OVERRIDE
;
217 void OnBrowserBridgeInitialized(const base::ListValue
* list
);
218 void OnCallAsync(const base::ListValue
* list
);
220 // Submessages dispatched from OnCallAsync
221 base::Value
* OnRequestClientInfo(const base::ListValue
* list
);
222 base::Value
* OnRequestLogMessages(const base::ListValue
* list
);
225 // True if observing the GpuDataManager (re-attaching as observer would
229 DISALLOW_COPY_AND_ASSIGN(GpuMessageHandler
);
232 ////////////////////////////////////////////////////////////////////////////////
236 ////////////////////////////////////////////////////////////////////////////////
238 GpuMessageHandler::GpuMessageHandler()
239 : observing_(false) {
242 GpuMessageHandler::~GpuMessageHandler() {
243 GpuDataManagerImpl::GetInstance()->RemoveObserver(this);
246 /* BrowserBridge.callAsync prepends a requestID to these messages. */
247 void GpuMessageHandler::RegisterMessages() {
248 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
250 web_ui()->RegisterMessageCallback("browserBridgeInitialized",
251 base::Bind(&GpuMessageHandler::OnBrowserBridgeInitialized
,
252 base::Unretained(this)));
253 web_ui()->RegisterMessageCallback("callAsync",
254 base::Bind(&GpuMessageHandler::OnCallAsync
,
255 base::Unretained(this)));
258 void GpuMessageHandler::OnCallAsync(const base::ListValue
* args
) {
259 DCHECK_GE(args
->GetSize(), static_cast<size_t>(2));
260 // unpack args into requestId, submessage and submessageArgs
262 const base::Value
* requestId
;
263 ok
= args
->Get(0, &requestId
);
266 std::string submessage
;
267 ok
= args
->GetString(1, &submessage
);
270 base::ListValue
* submessageArgs
= new base::ListValue();
271 for (size_t i
= 2; i
< args
->GetSize(); ++i
) {
272 const base::Value
* arg
;
273 ok
= args
->Get(i
, &arg
);
276 base::Value
* argCopy
= arg
->DeepCopy();
277 submessageArgs
->Append(argCopy
);
280 // call the submessage handler
281 base::Value
* ret
= NULL
;
282 if (submessage
== "requestClientInfo") {
283 ret
= OnRequestClientInfo(submessageArgs
);
284 } else if (submessage
== "requestLogMessages") {
285 ret
= OnRequestLogMessages(submessageArgs
);
286 } else { // unrecognized submessage
288 delete submessageArgs
;
291 delete submessageArgs
;
293 // call BrowserBridge.onCallAsyncReply with result
295 web_ui()->CallJavascriptFunction("browserBridge.onCallAsyncReply",
300 web_ui()->CallJavascriptFunction("browserBridge.onCallAsyncReply",
305 void GpuMessageHandler::OnBrowserBridgeInitialized(
306 const base::ListValue
* args
) {
307 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
309 // Watch for changes in GPUInfo
311 GpuDataManagerImpl::GetInstance()->AddObserver(this);
314 // Tell GpuDataManager it should have full GpuInfo. If the
315 // Gpu process has not run yet, this will trigger its launch.
316 GpuDataManagerImpl::GetInstance()->RequestCompleteGpuInfoIfNeeded();
318 // Run callback immediately in case the info is ready and no update in the
323 base::Value
* GpuMessageHandler::OnRequestClientInfo(
324 const base::ListValue
* list
) {
325 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
327 base::DictionaryValue
* dict
= new base::DictionaryValue();
329 dict
->SetString("version", GetContentClient()->GetProduct());
330 dict
->SetString("command_line",
331 base::CommandLine::ForCurrentProcess()->GetCommandLineString());
332 dict
->SetString("operating_system",
333 base::SysInfo::OperatingSystemName() + " " +
334 base::SysInfo::OperatingSystemVersion());
335 dict
->SetString("angle_commit_id", ANGLE_COMMIT_HASH
);
336 dict
->SetString("graphics_backend", "Skia");
337 dict
->SetString("blacklist_version",
338 GpuDataManagerImpl::GetInstance()->GetBlacklistVersion());
339 dict
->SetString("driver_bug_list_version",
340 GpuDataManagerImpl::GetInstance()->GetDriverBugListVersion());
345 base::Value
* GpuMessageHandler::OnRequestLogMessages(const base::ListValue
*) {
346 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
348 return GpuDataManagerImpl::GetInstance()->GetLogMessages();
351 void GpuMessageHandler::OnGpuInfoUpdate() {
353 scoped_ptr
<base::DictionaryValue
> gpu_info_val(GpuInfoAsDictionaryValue());
355 // Add in blacklisting features
356 base::DictionaryValue
* feature_status
= new base::DictionaryValue
;
357 feature_status
->Set("featureStatus", GetFeatureStatus());
358 feature_status
->Set("problems", GetProblems());
359 feature_status
->Set("workarounds", GetDriverBugWorkarounds());
361 gpu_info_val
->Set("featureStatus", feature_status
);
363 // Send GPU Info to javascript.
364 web_ui()->CallJavascriptFunction("browserBridge.onGpuInfoUpdate",
365 *(gpu_info_val
.get()));
368 void GpuMessageHandler::OnGpuSwitching() {
369 GpuDataManagerImpl::GetInstance()->RequestCompleteGpuInfoIfNeeded();
375 ////////////////////////////////////////////////////////////////////////////////
379 ////////////////////////////////////////////////////////////////////////////////
381 GpuInternalsUI::GpuInternalsUI(WebUI
* web_ui
)
382 : WebUIController(web_ui
) {
383 web_ui
->AddMessageHandler(new GpuMessageHandler());
385 // Set up the chrome://gpu/ source.
386 BrowserContext
* browser_context
=
387 web_ui
->GetWebContents()->GetBrowserContext();
388 WebUIDataSource::Add(browser_context
, CreateGpuHTMLSource());
391 } // namespace content