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
37namespace content {
38namespace {
39
40WebUIDataSource* 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
49base::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
57base::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
67base::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
87std::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
98base::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.
197class 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
234GpuMessageHandler::GpuMessageHandler()
235    : observing_(false) {
236}
237
238GpuMessageHandler::~GpuMessageHandler() {
239  GpuDataManagerImpl::GetInstance()->RemoveObserver(this);
240}
241
242/* BrowserBridge.callAsync prepends a requestID to these messages. */
243void 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
254void 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
301void 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
319base::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
341base::Value* GpuMessageHandler::OnRequestLogMessages(const base::ListValue*) {
342  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
343
344  return GpuDataManagerImpl::GetInstance()->GetLogMessages();
345}
346
347void 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
364void GpuMessageHandler::OnGpuSwitching() {
365  GpuDataManagerImpl::GetInstance()->RequestCompleteGpuInfoIfNeeded();
366}
367
368}  // namespace
369
370
371////////////////////////////////////////////////////////////////////////////////
372//
373// GpuInternalsUI
374//
375////////////////////////////////////////////////////////////////////////////////
376
377GpuInternalsUI::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