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 <stddef.h>
8 
9 #include <memory>
10 #include <string>
11 #include <utility>
12 
13 #include "base/base64.h"
14 #include "base/bind.h"
15 #include "base/callback_helpers.h"
16 #include "base/command_line.h"
17 #include "base/environment.h"
18 #include "base/i18n/time_formatting.h"
19 #include "base/macros.h"
20 #include "base/stl_util.h"
21 #include "base/strings/string_number_conversions.h"
22 #include "base/strings/stringize_macros.h"
23 #include "base/strings/stringprintf.h"
24 #include "base/system/sys_info.h"
25 #include "base/values.h"
26 #include "build/build_config.h"
27 #include "content/browser/gpu/compositor_util.h"
28 #include "content/browser/gpu/gpu_data_manager_impl.h"
29 #include "content/browser/gpu/gpu_process_host.h"
30 #include "content/grit/content_resources.h"
31 #include "content/grit/dev_ui_content_resources.h"
32 #include "content/public/browser/browser_thread.h"
33 #include "content/public/browser/content_browser_client.h"
34 #include "content/public/browser/gpu_data_manager_observer.h"
35 #include "content/public/browser/web_contents.h"
36 #include "content/public/browser/web_ui.h"
37 #include "content/public/browser/web_ui_data_source.h"
38 #include "content/public/browser/web_ui_message_handler.h"
39 #include "content/public/common/content_client.h"
40 #include "content/public/common/content_switches.h"
41 #include "content/public/common/url_constants.h"
42 #include "gpu/config/device_perf_info.h"
43 #include "gpu/config/gpu_feature_type.h"
44 #include "gpu/config/gpu_info.h"
45 #include "gpu/config/gpu_lists_version.h"
46 #include "gpu/config/gpu_util.h"
47 #include "gpu/ipc/common/gpu_memory_buffer_support.h"
48 #include "gpu/ipc/host/gpu_memory_buffer_support.h"
49 #include "services/network/public/mojom/content_security_policy.mojom.h"
50 #include "skia/ext/skia_commit_hash.h"
51 #include "third_party/angle/src/common/version.h"
52 #include "third_party/skia/include/core/SkMilestone.h"
53 #include "ui/display/display.h"
54 #include "ui/display/screen.h"
55 #include "ui/gfx/buffer_format_util.h"
56 #include "ui/gfx/buffer_usage_util.h"
57 #include "ui/gfx/gpu_extra_info.h"
58 #include "ui/gl/gpu_switching_manager.h"
59 
60 #if defined(OS_WIN)
61 #include "ui/base/win/shell.h"
62 #include "ui/gfx/win/physical_size.h"
63 #endif
64 
65 #if defined(USE_X11)
66 #include "ui/base/x/x11_util.h"       // nogncheck
67 #include "ui/gfx/x/x11_atom_cache.h"  // nogncheck
68 #endif
69 
70 #if defined(USE_OZONE)
71 #include "ui/base/ui_base_features.h"
72 #endif
73 
74 namespace content {
75 namespace {
76 
CreateGpuHTMLSource()77 WebUIDataSource* CreateGpuHTMLSource() {
78   WebUIDataSource* source = WebUIDataSource::Create(kChromeUIGpuHost);
79   source->OverrideContentSecurityPolicy(
80       network::mojom::CSPDirectiveName::ScriptSrc,
81       "script-src chrome://resources 'self' 'unsafe-eval';");
82   source->OverrideContentSecurityPolicy(
83       network::mojom::CSPDirectiveName::TrustedTypes,
84       "trusted-types jstemplate;");
85 
86   source->UseStringsJs();
87   source->AddResourcePath("gpu_internals.js", IDR_GPU_INTERNALS_JS);
88   source->AddResourcePath("vulkan_info.mojom.js", IDR_VULKAN_INFO_MOJO_JS);
89   source->AddResourcePath("vulkan_types.mojom.js", IDR_VULKAN_TYPES_MOJO_JS);
90   source->SetDefaultResource(IDR_GPU_INTERNALS_HTML);
91   return source;
92 }
93 
94 // Must be in sync with the copy in //ui/base/x/x11_util.cc.
NewDescriptionValuePair(base::StringPiece desc,base::StringPiece value)95 std::unique_ptr<base::DictionaryValue> NewDescriptionValuePair(
96     base::StringPiece desc,
97     base::StringPiece value) {
98   std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
99   dict->SetString("description", desc);
100   dict->SetString("value", value);
101   return dict;
102 }
103 
NewDescriptionValuePair(base::StringPiece desc,std::unique_ptr<base::Value> value)104 std::unique_ptr<base::DictionaryValue> NewDescriptionValuePair(
105     base::StringPiece desc,
106     std::unique_ptr<base::Value> value) {
107   std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
108   dict->SetString("description", desc);
109   dict->Set("value", std::move(value));
110   return dict;
111 }
112 
113 #if defined(OS_WIN)
114 // Output DxDiagNode tree as nested array of {description,value} pairs
DxDiagNodeToList(const gpu::DxDiagNode & node)115 std::unique_ptr<base::ListValue> DxDiagNodeToList(const gpu::DxDiagNode& node) {
116   auto list = std::make_unique<base::ListValue>();
117   for (std::map<std::string, std::string>::const_iterator it =
118       node.values.begin();
119       it != node.values.end();
120       ++it) {
121     list->Append(NewDescriptionValuePair(it->first, it->second));
122   }
123 
124   for (std::map<std::string, gpu::DxDiagNode>::const_iterator it =
125       node.children.begin();
126       it != node.children.end();
127       ++it) {
128     std::unique_ptr<base::ListValue> sublist = DxDiagNodeToList(it->second);
129     list->Append(NewDescriptionValuePair(it->first, std::move(sublist)));
130   }
131   return list;
132 }
133 #endif  // OS_WIN
134 
GPUDeviceToString(const gpu::GPUInfo::GPUDevice & gpu)135 std::string GPUDeviceToString(const gpu::GPUInfo::GPUDevice& gpu) {
136   std::string vendor = base::StringPrintf("0x%04x", gpu.vendor_id);
137   if (!gpu.vendor_string.empty())
138     vendor += " [" + gpu.vendor_string + "]";
139   std::string device = base::StringPrintf("0x%04x", gpu.device_id);
140   if (!gpu.device_string.empty())
141     device += " [" + gpu.device_string + "]";
142   std::string rt = base::StringPrintf("VENDOR= %s, DEVICE=%s", vendor.c_str(),
143                                       device.c_str());
144 #if defined(OS_WIN)
145   if (gpu.sub_sys_id || gpu.revision) {
146     rt += base::StringPrintf(", SUBSYS=0x%08x, REV=%u", gpu.sub_sys_id,
147                              gpu.revision);
148   }
149 
150   rt += base::StringPrintf(", LUID={%ld,%lu}", gpu.luid.HighPart,
151                            gpu.luid.LowPart);
152 #endif
153   if (gpu.active)
154     rt += " *ACTIVE*";
155   return rt;
156 }
157 
GpuExtraInfoToListValue(const gfx::GpuExtraInfo & gpu_extra_info)158 base::Value GpuExtraInfoToListValue(const gfx::GpuExtraInfo& gpu_extra_info) {
159   base::Value gpu_info_lines(base::Value::Type::LIST);
160 #if defined(USE_OZONE)
161   if (features::IsUsingOzonePlatform()) {
162     return display::Screen::GetScreen()->GetGpuExtraInfoAsListValue(
163         gpu_extra_info);
164   }
165 #endif
166 #if defined(USE_X11)
167   gpu_info_lines = ui::GpuExtraInfoAsListValue(gpu_extra_info.system_visual,
168                                                gpu_extra_info.rgba_visual);
169 #endif
170   return gpu_info_lines;
171 }
172 
BasicGpuInfoAsListValue(const gpu::GPUInfo & gpu_info,const gpu::GpuFeatureInfo & gpu_feature_info,const gfx::GpuExtraInfo & gpu_extra_info)173 std::unique_ptr<base::ListValue> BasicGpuInfoAsListValue(
174     const gpu::GPUInfo& gpu_info,
175     const gpu::GpuFeatureInfo& gpu_feature_info,
176     const gfx::GpuExtraInfo& gpu_extra_info) {
177   const gpu::GPUInfo::GPUDevice& active_gpu = gpu_info.active_gpu();
178   auto basic_info = std::make_unique<base::ListValue>();
179   basic_info->Append(NewDescriptionValuePair(
180       "Initialization time",
181       base::NumberToString(gpu_info.initialization_time.InMilliseconds())));
182   basic_info->Append(NewDescriptionValuePair(
183       "In-process GPU",
184       std::make_unique<base::Value>(gpu_info.in_process_gpu)));
185   basic_info->Append(NewDescriptionValuePair(
186       "Passthrough Command Decoder",
187       std::make_unique<base::Value>(gpu_info.passthrough_cmd_decoder)));
188   basic_info->Append(NewDescriptionValuePair(
189       "Sandboxed", std::make_unique<base::Value>(gpu_info.sandboxed)));
190   basic_info->Append(
191       NewDescriptionValuePair("GPU0", GPUDeviceToString(gpu_info.gpu)));
192   for (size_t i = 0; i < gpu_info.secondary_gpus.size(); ++i) {
193     basic_info->Append(NewDescriptionValuePair(
194         base::StringPrintf("GPU%d", static_cast<int>(i + 1)),
195         GPUDeviceToString(gpu_info.secondary_gpus[i])));
196   }
197   basic_info->Append(NewDescriptionValuePair(
198       "Optimus", std::make_unique<base::Value>(gpu_info.optimus)));
199   basic_info->Append(NewDescriptionValuePair(
200       "AMD switchable",
201       std::make_unique<base::Value>(gpu_info.amd_switchable)));
202 #if defined(OS_WIN)
203   std::string compositor =
204       ui::win::IsAeroGlassEnabled() ? "Aero Glass" : "none";
205   basic_info->Append(
206       NewDescriptionValuePair("Desktop compositing", compositor));
207 
208   basic_info->Append(NewDescriptionValuePair(
209       "Direct composition",
210       std::make_unique<base::Value>(gpu_info.overlay_info.direct_composition)));
211   basic_info->Append(NewDescriptionValuePair(
212       "Supports overlays",
213       std::make_unique<base::Value>(gpu_info.overlay_info.supports_overlays)));
214   basic_info->Append(NewDescriptionValuePair(
215       "YUY2 overlay support",
216       gpu::OverlaySupportToString(gpu_info.overlay_info.yuy2_overlay_support)));
217   basic_info->Append(NewDescriptionValuePair(
218       "NV12 overlay support",
219       gpu::OverlaySupportToString(gpu_info.overlay_info.nv12_overlay_support)));
220   basic_info->Append(NewDescriptionValuePair(
221       "BGRA8 overlay support",
222       gpu::OverlaySupportToString(
223           gpu_info.overlay_info.bgra8_overlay_support)));
224   basic_info->Append(NewDescriptionValuePair(
225       "RGB10A2 overlay support",
226       gpu::OverlaySupportToString(
227           gpu_info.overlay_info.rgb10a2_overlay_support)));
228 
229   std::vector<gfx::PhysicalDisplaySize> display_sizes =
230       gfx::GetPhysicalSizeForDisplays();
231   for (const auto& display_size : display_sizes) {
232     const int w = display_size.width_mm;
233     const int h = display_size.height_mm;
234     const double size_mm = sqrt(w * w + h * h);
235     const double size_inches = 0.0393701 * size_mm;
236     const double rounded_size_inches = floor(10.0 * size_inches) / 10.0;
237     std::string size_string = base::StringPrintf("%.1f\"", rounded_size_inches);
238     std::string description_string = base::StringPrintf(
239         "Diagonal Monitor Size of %s", display_size.display_name.c_str());
240     basic_info->Append(
241         NewDescriptionValuePair(description_string, size_string));
242   }
243 
244   basic_info->Append(NewDescriptionValuePair(
245       "Driver D3D12 feature level",
246       gpu::D3DFeatureLevelToString(gpu_info.d3d12_feature_level)));
247 
248   basic_info->Append(NewDescriptionValuePair(
249       "Driver Vulkan API version",
250       gpu::VulkanVersionToString(gpu_info.vulkan_version)));
251 #endif
252 
253   basic_info->Append(
254       NewDescriptionValuePair("Driver vendor", active_gpu.driver_vendor));
255   basic_info->Append(
256       NewDescriptionValuePair("Driver version", active_gpu.driver_version));
257   basic_info->Append(NewDescriptionValuePair(
258       "GPU CUDA compute capability major version",
259       std::make_unique<base::Value>(active_gpu.cuda_compute_capability_major)));
260   basic_info->Append(NewDescriptionValuePair("Pixel shader version",
261                                              gpu_info.pixel_shader_version));
262   basic_info->Append(NewDescriptionValuePair("Vertex shader version",
263                                              gpu_info.vertex_shader_version));
264   basic_info->Append(
265       NewDescriptionValuePair("Max. MSAA samples", gpu_info.max_msaa_samples));
266   basic_info->Append(NewDescriptionValuePair("Machine model name",
267                                              gpu_info.machine_model_name));
268   basic_info->Append(NewDescriptionValuePair("Machine model version",
269                                              gpu_info.machine_model_version));
270   basic_info->Append(NewDescriptionValuePair("GL_VENDOR", gpu_info.gl_vendor));
271   basic_info->Append(
272       NewDescriptionValuePair("GL_RENDERER", gpu_info.gl_renderer));
273   basic_info->Append(
274       NewDescriptionValuePair("GL_VERSION", gpu_info.gl_version));
275   basic_info->Append(
276       NewDescriptionValuePair("GL_EXTENSIONS", gpu_info.gl_extensions));
277   basic_info->Append(NewDescriptionValuePair(
278       "Disabled Extensions", gpu_feature_info.disabled_extensions));
279   basic_info->Append(NewDescriptionValuePair(
280       "Disabled WebGL Extensions", gpu_feature_info.disabled_webgl_extensions));
281   basic_info->Append(NewDescriptionValuePair("Window system binding vendor",
282                                              gpu_info.gl_ws_vendor));
283   basic_info->Append(NewDescriptionValuePair("Window system binding version",
284                                              gpu_info.gl_ws_version));
285   basic_info->Append(NewDescriptionValuePair("Window system binding extensions",
286                                              gpu_info.gl_ws_extensions));
287 
288   {
289     base::Value gpu_extra_info_as_list_value =
290         GpuExtraInfoToListValue(gpu_extra_info);
291     DCHECK(gpu_extra_info_as_list_value.is_list());
292     {
293       auto pairs = gpu_extra_info_as_list_value.TakeList();
294       for (auto& pair : pairs) {
295         if (pair.FindStringKey("description") == nullptr ||
296             pair.FindKey("value") == nullptr) {
297           LOG(WARNING) << "Unexpected item format: should have a string "
298                           "description and a value.";
299         }
300         basic_info->Append(std::move(pair));
301       }
302     }
303   }
304 
305   std::string direct_rendering_version;
306   if (gpu_info.direct_rendering_version == "1") {
307     direct_rendering_version = "indirect";
308   } else if (gpu_info.direct_rendering_version == "2") {
309     direct_rendering_version = "direct but version unknown";
310   } else if (base::StartsWith(gpu_info.direct_rendering_version, "2.",
311                               base::CompareCase::INSENSITIVE_ASCII)) {
312     direct_rendering_version = gpu_info.direct_rendering_version;
313     base::ReplaceFirstSubstringAfterOffset(&direct_rendering_version, 0, "2.",
314                                            "DRI");
315   } else {
316     direct_rendering_version = "unknown";
317   }
318   basic_info->Append(NewDescriptionValuePair("Direct rendering version",
319                                              direct_rendering_version));
320 
321   std::string reset_strategy =
322       base::StringPrintf("0x%04x", gpu_info.gl_reset_notification_strategy);
323   basic_info->Append(
324       NewDescriptionValuePair("Reset notification strategy", reset_strategy));
325 
326   basic_info->Append(NewDescriptionValuePair(
327       "GPU process crash count",
328       std::make_unique<base::Value>(GpuProcessHost::GetGpuCrashCount())));
329 
330   std::string buffer_formats;
331   for (int i = 0; i <= static_cast<int>(gfx::BufferFormat::LAST); ++i) {
332     const gfx::BufferFormat buffer_format = static_cast<gfx::BufferFormat>(i);
333     if (i > 0)
334       buffer_formats += ",  ";
335     buffer_formats += gfx::BufferFormatToString(buffer_format);
336     const bool supported = base::Contains(
337         gpu_feature_info.supported_buffer_formats_for_allocation_and_texturing,
338         buffer_format);
339     buffer_formats += supported ? ": supported" : ": not supported";
340   }
341   basic_info->Append(NewDescriptionValuePair(
342       "gfx::BufferFormats supported for allocation and texturing",
343       buffer_formats));
344 
345   return basic_info;
346 }
347 
GpuInfoAsDictionaryValue()348 std::unique_ptr<base::DictionaryValue> GpuInfoAsDictionaryValue() {
349   auto info = std::make_unique<base::DictionaryValue>();
350 
351   const gpu::GPUInfo gpu_info = GpuDataManagerImpl::GetInstance()->GetGPUInfo();
352   const gpu::GpuFeatureInfo gpu_feature_info =
353       GpuDataManagerImpl::GetInstance()->GetGpuFeatureInfo();
354   const gfx::GpuExtraInfo gpu_extra_info =
355       GpuDataManagerImpl::GetInstance()->GetGpuExtraInfo();
356   auto basic_info =
357       BasicGpuInfoAsListValue(gpu_info, gpu_feature_info, gpu_extra_info);
358   info->Set("basicInfo", std::move(basic_info));
359 
360 #if defined(OS_WIN)
361   auto dx_info = std::make_unique<base::Value>();
362   if (gpu_info.dx_diagnostics.children.size())
363     dx_info = DxDiagNodeToList(gpu_info.dx_diagnostics);
364   info->Set("diagnostics", std::move(dx_info));
365 #endif
366 
367 #if BUILDFLAG(ENABLE_VULKAN)
368   if (gpu_info.vulkan_info) {
369     auto blob = gpu_info.vulkan_info->Serialize();
370     info->SetString("vulkanInfo", base::Base64Encode(blob));
371   }
372 #endif
373 
374   return info;
375 }
376 
CompositorInfo()377 std::unique_ptr<base::ListValue> CompositorInfo() {
378   auto compositor_info = std::make_unique<base::ListValue>();
379 
380   compositor_info->Append(NewDescriptionValuePair(
381       "Tile Update Mode",
382       IsZeroCopyUploadEnabled() ? "Zero-copy" : "One-copy"));
383 
384   compositor_info->Append(NewDescriptionValuePair(
385       "Partial Raster", IsPartialRasterEnabled() ? "Enabled" : "Disabled"));
386   return compositor_info;
387 }
388 
GpuMemoryBufferInfo(const gfx::GpuExtraInfo & gpu_extra_info)389 std::unique_ptr<base::ListValue> GpuMemoryBufferInfo(
390     const gfx::GpuExtraInfo& gpu_extra_info) {
391   auto gpu_memory_buffer_info = std::make_unique<base::ListValue>();
392 
393   gpu::GpuMemoryBufferSupport gpu_memory_buffer_support;
394 
395   gpu::GpuMemoryBufferConfigurationSet native_config;
396 #if defined(USE_X11)
397   if (!features::IsUsingOzonePlatform()) {
398     for (const auto& config : gpu_extra_info.gpu_memory_buffer_support_x11) {
399       native_config.emplace(config);
400     }
401   }
402 #endif
403   if (native_config.empty()) {
404     native_config =
405         gpu::GetNativeGpuMemoryBufferConfigurations(&gpu_memory_buffer_support);
406   }
407   for (size_t format = 0;
408        format < static_cast<size_t>(gfx::BufferFormat::LAST) + 1; format++) {
409     std::string native_usage_support;
410     for (size_t usage = 0;
411          usage < static_cast<size_t>(gfx::BufferUsage::LAST) + 1; usage++) {
412       gfx::BufferUsageAndFormat element{static_cast<gfx::BufferUsage>(usage),
413                                         static_cast<gfx::BufferFormat>(format)};
414       if (base::Contains(native_config, element)) {
415         native_usage_support = base::StringPrintf(
416             "%s%s %s", native_usage_support.c_str(),
417             native_usage_support.empty() ? "" : ",",
418             gfx::BufferUsageToString(static_cast<gfx::BufferUsage>(usage)));
419       }
420     }
421     if (native_usage_support.empty())
422       native_usage_support = base::StringPrintf("Software only");
423 
424     gpu_memory_buffer_info->Append(NewDescriptionValuePair(
425         gfx::BufferFormatToString(static_cast<gfx::BufferFormat>(format)),
426         native_usage_support));
427   }
428   return gpu_memory_buffer_info;
429 }
430 
GetDisplayInfo()431 std::unique_ptr<base::ListValue> GetDisplayInfo() {
432   auto display_info = std::make_unique<base::ListValue>();
433   const std::vector<display::Display> displays =
434       display::Screen::GetScreen()->GetAllDisplays();
435   for (const auto& display : displays) {
436     display_info->Append(NewDescriptionValuePair("Info ", display.ToString()));
437     {
438       std::vector<std::string> names;
439       std::vector<gfx::ColorSpace> color_spaces;
440       std::vector<gfx::BufferFormat> buffer_formats;
441       display.color_spaces().ToStrings(&names, &color_spaces, &buffer_formats);
442       for (size_t i = 0; i < names.size(); ++i) {
443         display_info->Append(NewDescriptionValuePair(
444             base::StringPrintf("Color space (%s)", names[i].c_str()),
445             color_spaces[i].ToString()));
446         display_info->Append(NewDescriptionValuePair(
447             base::StringPrintf("Buffer format (%s)", names[i].c_str()),
448             gfx::BufferFormatToString(buffer_formats[i])));
449       }
450     }
451     display_info->Append(NewDescriptionValuePair(
452         "SDR white level in nits",
453         base::NumberToString(display.color_spaces().GetSDRWhiteLevel())));
454     display_info->Append(NewDescriptionValuePair(
455         "Bits per color component",
456         base::NumberToString(display.depth_per_component())));
457     display_info->Append(NewDescriptionValuePair(
458         "Bits per pixel", base::NumberToString(display.color_depth())));
459     if (display.display_frequency()) {
460       display_info->Append(NewDescriptionValuePair(
461           "Refresh Rate in Hz",
462           base::NumberToString(display.display_frequency())));
463     }
464   }
465   return display_info;
466 }
467 
468 #if defined(OS_WIN)
D3dFeatureLevelToString(D3D_FEATURE_LEVEL level)469 const char* D3dFeatureLevelToString(D3D_FEATURE_LEVEL level) {
470   switch (level) {
471     case D3D_FEATURE_LEVEL_1_0_CORE:
472       return "Unknown";
473     case D3D_FEATURE_LEVEL_9_1:
474       return "9_1";
475     case D3D_FEATURE_LEVEL_9_2:
476       return "9_2";
477     case D3D_FEATURE_LEVEL_9_3:
478       return "9_3";
479     case D3D_FEATURE_LEVEL_10_0:
480       return "10_0";
481     case D3D_FEATURE_LEVEL_10_1:
482       return "10_1";
483     case D3D_FEATURE_LEVEL_11_0:
484       return "11_0";
485     case D3D_FEATURE_LEVEL_11_1:
486       return "11_1";
487     case D3D_FEATURE_LEVEL_12_0:
488       return "12_0";
489     case D3D_FEATURE_LEVEL_12_1:
490       return "12_1";
491     default:
492       NOTREACHED();
493       return "";
494   }
495 }
496 
HasDiscreteGpuToString(gpu::HasDiscreteGpu has_discrete_gpu)497 const char* HasDiscreteGpuToString(gpu::HasDiscreteGpu has_discrete_gpu) {
498   switch (has_discrete_gpu) {
499     case gpu::HasDiscreteGpu::kUnknown:
500       return "unknown";
501     case gpu::HasDiscreteGpu::kNo:
502       return "no";
503     case gpu::HasDiscreteGpu::kYes:
504       return "yes";
505   }
506   NOTREACHED();
507   return "";
508 }
509 #endif  // OS_WIN
510 
GetDevicePerfInfo()511 std::unique_ptr<base::ListValue> GetDevicePerfInfo() {
512   auto list = std::make_unique<base::ListValue>();
513   const base::Optional<gpu::DevicePerfInfo> device_perf_info =
514       gpu::GetDevicePerfInfo();
515   if (device_perf_info.has_value()) {
516     list->Append(NewDescriptionValuePair(
517         "Total Physical Memory (Gb)",
518         base::NumberToString(device_perf_info->total_physical_memory_mb /
519                              1024)));
520     list->Append(NewDescriptionValuePair(
521         "Total Disk Space (Gb)",
522         base::NumberToString(device_perf_info->total_disk_space_mb / 1024)));
523     list->Append(NewDescriptionValuePair(
524         "Hardware Concurrency",
525         base::NumberToString(device_perf_info->hardware_concurrency)));
526 
527 #if defined(OS_WIN)
528     list->Append(NewDescriptionValuePair(
529         "System Commit Limit (Gb)",
530         base::NumberToString(device_perf_info->system_commit_limit_mb / 1024)));
531     list->Append(NewDescriptionValuePair(
532         "D3D11 Feature Level",
533         D3dFeatureLevelToString(device_perf_info->d3d11_feature_level)));
534     list->Append(NewDescriptionValuePair(
535         "Has Discrete GPU",
536         HasDiscreteGpuToString(device_perf_info->has_discrete_gpu)));
537 #endif  // OS_WIN
538 
539     if (device_perf_info->intel_gpu_generation !=
540         gpu::IntelGpuGeneration::kNonIntel) {
541       std::string intel_gpu_gen;
542       if (device_perf_info->intel_gpu_generation ==
543           gpu::IntelGpuGeneration::kUnknownIntel) {
544         intel_gpu_gen = "unknown";
545       } else {
546         intel_gpu_gen = base::NumberToString(
547             static_cast<int>(device_perf_info->intel_gpu_generation));
548       }
549       list->Append(
550           NewDescriptionValuePair("Intel GPU Generation", intel_gpu_gen));
551     }
552     list->Append(NewDescriptionValuePair(
553         "Software Rendering",
554         device_perf_info->software_rendering ? "Yes" : "No"));
555   }
556   return list;
557 }
558 
GetProfileName(gpu::VideoCodecProfile profile)559 const char* GetProfileName(gpu::VideoCodecProfile profile) {
560   switch (profile) {
561     case gpu::VIDEO_CODEC_PROFILE_UNKNOWN:
562       return "unknown";
563     case gpu::H264PROFILE_BASELINE:
564       return "h264 baseline";
565     case gpu::H264PROFILE_MAIN:
566       return "h264 main";
567     case gpu::H264PROFILE_EXTENDED:
568       return "h264 extended";
569     case gpu::H264PROFILE_HIGH:
570       return "h264 high";
571     case gpu::H264PROFILE_HIGH10PROFILE:
572       return "h264 high 10";
573     case gpu::H264PROFILE_HIGH422PROFILE:
574       return "h264 high 4:2:2";
575     case gpu::H264PROFILE_HIGH444PREDICTIVEPROFILE:
576       return "h264 high 4:4:4 predictive";
577     case gpu::H264PROFILE_SCALABLEBASELINE:
578       return "h264 scalable baseline";
579     case gpu::H264PROFILE_SCALABLEHIGH:
580       return "h264 scalable high";
581     case gpu::H264PROFILE_STEREOHIGH:
582       return "h264 stereo high";
583     case gpu::H264PROFILE_MULTIVIEWHIGH:
584       return "h264 multiview high";
585     case gpu::HEVCPROFILE_MAIN:
586       return "hevc main";
587     case gpu::HEVCPROFILE_MAIN10:
588       return "hevc main 10";
589     case gpu::HEVCPROFILE_MAIN_STILL_PICTURE:
590       return "hevc main still-picture";
591     case gpu::VP8PROFILE_ANY:
592       return "vp8";
593     case gpu::VP9PROFILE_PROFILE0:
594       return "vp9 profile0";
595     case gpu::VP9PROFILE_PROFILE1:
596       return "vp9 profile1";
597     case gpu::VP9PROFILE_PROFILE2:
598       return "vp9 profile2";
599     case gpu::VP9PROFILE_PROFILE3:
600       return "vp9 profile3";
601     case gpu::DOLBYVISION_PROFILE0:
602       return "dolby vision profile 0";
603     case gpu::DOLBYVISION_PROFILE4:
604       return "dolby vision profile 4";
605     case gpu::DOLBYVISION_PROFILE5:
606       return "dolby vision profile 5";
607     case gpu::DOLBYVISION_PROFILE7:
608       return "dolby vision profile 7";
609     case gpu::DOLBYVISION_PROFILE8:
610       return "dolby vision profile 8";
611     case gpu::DOLBYVISION_PROFILE9:
612       return "dolby vision profile 9";
613     case gpu::THEORAPROFILE_ANY:
614       return "theora";
615     case gpu::AV1PROFILE_PROFILE_MAIN:
616       return "av1 profile main";
617     case gpu::AV1PROFILE_PROFILE_HIGH:
618       return "av1 profile high";
619     case gpu::AV1PROFILE_PROFILE_PRO:
620       return "av1 profile pro";
621   }
622   NOTREACHED();
623   return "";
624 }
625 
GetVideoAcceleratorsInfo()626 std::unique_ptr<base::ListValue> GetVideoAcceleratorsInfo() {
627   gpu::GPUInfo gpu_info = GpuDataManagerImpl::GetInstance()->GetGPUInfo();
628   auto info = std::make_unique<base::ListValue>();
629 
630   for (const auto& profile :
631        gpu_info.video_decode_accelerator_capabilities.supported_profiles) {
632     std::string codec_string =
633         base::StringPrintf("Decode %s", GetProfileName(profile.profile));
634     std::string resolution_string = base::StringPrintf(
635         "%s to %s pixels%s", profile.min_resolution.ToString().c_str(),
636         profile.max_resolution.ToString().c_str(),
637         profile.encrypted_only ? " (encrypted)" : "");
638     info->Append(NewDescriptionValuePair(codec_string, resolution_string));
639   }
640 
641   for (const auto& profile :
642        gpu_info.video_encode_accelerator_supported_profiles) {
643     std::string codec_string =
644         base::StringPrintf("Encode %s", GetProfileName(profile.profile));
645     std::string resolution_string = base::StringPrintf(
646         "%s to %s pixels, and/or %.3f fps",
647         profile.min_resolution.ToString().c_str(),
648         profile.max_resolution.ToString().c_str(),
649         static_cast<double>(profile.max_framerate_numerator) /
650             profile.max_framerate_denominator);
651     info->Append(NewDescriptionValuePair(codec_string, resolution_string));
652   }
653   return info;
654 }
655 
GetANGLEFeatures()656 std::unique_ptr<base::ListValue> GetANGLEFeatures() {
657   gfx::GpuExtraInfo gpu_extra_info =
658       GpuDataManagerImpl::GetInstance()->GetGpuExtraInfo();
659   auto angle_features_list = std::make_unique<base::ListValue>();
660   for (const auto& feature : gpu_extra_info.angle_features) {
661     auto angle_feature = std::make_unique<base::DictionaryValue>();
662     angle_feature->SetString("name", feature.name);
663     angle_feature->SetString("category", feature.category);
664     angle_feature->SetString("description", feature.description);
665     angle_feature->SetString("bug", feature.bug);
666     angle_feature->SetString("status", feature.status);
667     angle_feature->SetString("condition", feature.condition);
668     angle_features_list->Append(std::move(angle_feature));
669   }
670 
671   return angle_features_list;
672 }
673 
674 // This class receives javascript messages from the renderer.
675 // Note that the WebUI infrastructure runs on the UI thread, therefore all of
676 // this class's methods are expected to run on the UI thread.
677 class GpuMessageHandler
678     : public WebUIMessageHandler,
679       public base::SupportsWeakPtr<GpuMessageHandler>,
680       public GpuDataManagerObserver,
681       public ui::GpuSwitchingObserver {
682  public:
683   GpuMessageHandler();
684   ~GpuMessageHandler() override;
685 
686   // WebUIMessageHandler implementation.
687   void RegisterMessages() override;
688 
689   // GpuDataManagerObserver implementation.
690   void OnGpuInfoUpdate() override;
691 
692   // ui::GpuSwitchingObserver implementation.
693   void OnGpuSwitched(gl::GpuPreference) override;
694 
695   // Messages
696   void OnBrowserBridgeInitialized(const base::ListValue* list);
697   void OnCallAsync(const base::ListValue* list);
698 
699   // Submessages dispatched from OnCallAsync
700   std::unique_ptr<base::DictionaryValue> OnRequestClientInfo(
701       const base::ListValue* list);
702   std::unique_ptr<base::ListValue> OnRequestLogMessages(
703       const base::ListValue* list);
704 
705  private:
706   // True if observing the GpuDataManager (re-attaching as observer would
707   // DCHECK).
708   bool observing_;
709 
710   DISALLOW_COPY_AND_ASSIGN(GpuMessageHandler);
711 };
712 
713 ////////////////////////////////////////////////////////////////////////////////
714 //
715 // GpuMessageHandler
716 //
717 ////////////////////////////////////////////////////////////////////////////////
718 
GpuMessageHandler()719 GpuMessageHandler::GpuMessageHandler()
720     : observing_(false) {
721 }
722 
~GpuMessageHandler()723 GpuMessageHandler::~GpuMessageHandler() {
724   ui::GpuSwitchingManager::GetInstance()->RemoveObserver(this);
725   GpuDataManagerImpl::GetInstance()->RemoveObserver(this);
726 }
727 
728 /* BrowserBridge.callAsync prepends a requestID to these messages. */
RegisterMessages()729 void GpuMessageHandler::RegisterMessages() {
730   DCHECK_CURRENTLY_ON(BrowserThread::UI);
731 
732   web_ui()->RegisterMessageCallback(
733       "browserBridgeInitialized",
734       base::BindRepeating(&GpuMessageHandler::OnBrowserBridgeInitialized,
735                           base::Unretained(this)));
736   web_ui()->RegisterMessageCallback(
737       "callAsync", base::BindRepeating(&GpuMessageHandler::OnCallAsync,
738                                        base::Unretained(this)));
739 }
740 
OnCallAsync(const base::ListValue * args)741 void GpuMessageHandler::OnCallAsync(const base::ListValue* args) {
742   DCHECK_GE(args->GetSize(), static_cast<size_t>(2));
743   // unpack args into requestId, submessage and submessageArgs
744   bool ok;
745   const base::Value* requestId;
746   ok = args->Get(0, &requestId);
747   DCHECK(ok);
748 
749   std::string submessage;
750   ok = args->GetString(1, &submessage);
751   DCHECK(ok);
752 
753   auto submessageArgs = std::make_unique<base::ListValue>();
754   for (size_t i = 2; i < args->GetSize(); ++i) {
755     const base::Value* arg;
756     ok = args->Get(i, &arg);
757     DCHECK(ok);
758 
759     submessageArgs->Append(arg->CreateDeepCopy());
760   }
761 
762   // call the submessage handler
763   std::unique_ptr<base::Value> ret;
764   if (submessage == "requestClientInfo") {
765     ret = OnRequestClientInfo(submessageArgs.get());
766   } else if (submessage == "requestLogMessages") {
767     ret = OnRequestLogMessages(submessageArgs.get());
768   } else {  // unrecognized submessage
769     NOTREACHED();
770     return;
771   }
772 
773   // call BrowserBridge.onCallAsyncReply with result
774   if (ret) {
775     web_ui()->CallJavascriptFunctionUnsafe("browserBridge.onCallAsyncReply",
776                                            *requestId, *ret);
777   } else {
778     web_ui()->CallJavascriptFunctionUnsafe("browserBridge.onCallAsyncReply",
779                                            *requestId);
780   }
781 }
782 
OnBrowserBridgeInitialized(const base::ListValue * args)783 void GpuMessageHandler::OnBrowserBridgeInitialized(
784     const base::ListValue* args) {
785   DCHECK_CURRENTLY_ON(BrowserThread::UI);
786 
787   // Watch for changes in GPUInfo
788   if (!observing_) {
789     GpuDataManagerImpl::GetInstance()->AddObserver(this);
790     ui::GpuSwitchingManager::GetInstance()->AddObserver(this);
791   }
792   observing_ = true;
793 
794   // Tell GpuDataManager it should have full GpuInfo. If the
795   // Gpu process has not run yet, this will trigger its launch.
796   GpuDataManagerImpl::GetInstance()->RequestDxdiagDx12VulkanGpuInfoIfNeeded(
797       kGpuInfoRequestAll, /*delayed=*/false);
798 
799   // Run callback immediately in case the info is ready and no update in the
800   // future.
801   OnGpuInfoUpdate();
802 }
803 
OnRequestClientInfo(const base::ListValue * list)804 std::unique_ptr<base::DictionaryValue> GpuMessageHandler::OnRequestClientInfo(
805     const base::ListValue* list) {
806   DCHECK_CURRENTLY_ON(BrowserThread::UI);
807 
808   auto dict = std::make_unique<base::DictionaryValue>();
809 
810   dict->SetString("version", GetContentClient()->browser()->GetProduct());
811   dict->SetString("command_line",
812       base::CommandLine::ForCurrentProcess()->GetCommandLineString());
813   dict->SetString("operating_system",
814                   base::SysInfo::OperatingSystemName() + " " +
815                   base::SysInfo::OperatingSystemVersion());
816   dict->SetString("angle_commit_id", ANGLE_COMMIT_HASH);
817   dict->SetString("graphics_backend",
818                   std::string("Skia/" STRINGIZE(SK_MILESTONE)
819                               " " SKIA_COMMIT_HASH));
820   dict->SetString("revision_identifier", GPU_LISTS_VERSION);
821 
822   return dict;
823 }
824 
OnRequestLogMessages(const base::ListValue *)825 std::unique_ptr<base::ListValue> GpuMessageHandler::OnRequestLogMessages(
826     const base::ListValue*) {
827   DCHECK_CURRENTLY_ON(BrowserThread::UI);
828 
829   return GpuDataManagerImpl::GetInstance()->GetLogMessages();
830 }
831 
OnGpuInfoUpdate()832 void GpuMessageHandler::OnGpuInfoUpdate() {
833   // Get GPU Info.
834   const gpu::GPUInfo gpu_info = GpuDataManagerImpl::GetInstance()->GetGPUInfo();
835   const gfx::GpuExtraInfo gpu_extra_info =
836       GpuDataManagerImpl::GetInstance()->GetGpuExtraInfo();
837   auto gpu_info_val = GpuInfoAsDictionaryValue();
838 
839   // Add in blocklisting features
840   auto feature_status = std::make_unique<base::DictionaryValue>();
841   feature_status->Set("featureStatus", GetFeatureStatus());
842   feature_status->Set("problems", GetProblems());
843   auto workarounds = std::make_unique<base::ListValue>();
844   for (const auto& workaround : GetDriverBugWorkarounds())
845     workarounds->AppendString(workaround);
846   feature_status->Set("workarounds", std::move(workarounds));
847   gpu_info_val->Set("featureStatus", std::move(feature_status));
848   if (!GpuDataManagerImpl::GetInstance()->IsGpuProcessUsingHardwareGpu()) {
849     auto feature_status_for_hardware_gpu =
850         std::make_unique<base::DictionaryValue>();
851     feature_status_for_hardware_gpu->Set("featureStatus",
852                                          GetFeatureStatusForHardwareGpu());
853     feature_status_for_hardware_gpu->Set("problems",
854                                          GetProblemsForHardwareGpu());
855     auto workarounds_for_hardware_gpu = std::make_unique<base::ListValue>();
856     for (const auto& workaround : GetDriverBugWorkaroundsForHardwareGpu())
857       workarounds_for_hardware_gpu->AppendString(workaround);
858     feature_status_for_hardware_gpu->Set(
859         "workarounds", std::move(workarounds_for_hardware_gpu));
860     gpu_info_val->Set("featureStatusForHardwareGpu",
861                       std::move(feature_status_for_hardware_gpu));
862     const gpu::GPUInfo gpu_info_for_hardware_gpu =
863         GpuDataManagerImpl::GetInstance()->GetGPUInfoForHardwareGpu();
864     const gpu::GpuFeatureInfo gpu_feature_info_for_hardware_gpu =
865         GpuDataManagerImpl::GetInstance()->GetGpuFeatureInfoForHardwareGpu();
866     auto gpu_info_for_hardware_gpu_val = BasicGpuInfoAsListValue(
867         gpu_info_for_hardware_gpu, gpu_feature_info_for_hardware_gpu,
868         gfx::GpuExtraInfo{});
869     gpu_info_val->Set("basicInfoForHardwareGpu",
870                       std::move(gpu_info_for_hardware_gpu_val));
871   }
872   gpu_info_val->Set("compositorInfo", CompositorInfo());
873   gpu_info_val->Set("gpuMemoryBufferInfo", GpuMemoryBufferInfo(gpu_extra_info));
874   gpu_info_val->Set("displayInfo", GetDisplayInfo());
875   gpu_info_val->Set("videoAcceleratorsInfo", GetVideoAcceleratorsInfo());
876   gpu_info_val->Set("ANGLEFeatures", GetANGLEFeatures());
877   gpu_info_val->Set("devicePerfInfo", GetDevicePerfInfo());
878 
879   // Send GPU Info to javascript.
880   web_ui()->CallJavascriptFunctionUnsafe("browserBridge.onGpuInfoUpdate",
881                                          *(gpu_info_val.get()));
882 }
883 
OnGpuSwitched(gl::GpuPreference active_gpu_heuristic)884 void GpuMessageHandler::OnGpuSwitched(gl::GpuPreference active_gpu_heuristic) {
885   // Currently, about:gpu page does not update GPU info after the GPU switch.
886   // If there is something to be updated, the code should be added here.
887 }
888 
889 }  // namespace
890 
891 
892 ////////////////////////////////////////////////////////////////////////////////
893 //
894 // GpuInternalsUI
895 //
896 ////////////////////////////////////////////////////////////////////////////////
897 
GpuInternalsUI(WebUI * web_ui)898 GpuInternalsUI::GpuInternalsUI(WebUI* web_ui)
899     : WebUIController(web_ui) {
900   web_ui->AddMessageHandler(std::make_unique<GpuMessageHandler>());
901 
902   // Set up the chrome://gpu/ source.
903   BrowserContext* browser_context =
904       web_ui->GetWebContents()->GetBrowserContext();
905   WebUIDataSource::Add(browser_context, CreateGpuHTMLSource());
906 }
907 
908 }  // namespace content
909