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/compositor_util.h"
6 
7 #include <stddef.h>
8 
9 #include <algorithm>
10 #include <memory>
11 #include <utility>
12 
13 #include "base/command_line.h"
14 #include "base/feature_list.h"
15 #include "base/logging.h"
16 #include "base/metrics/field_trial.h"
17 #include "base/numerics/ranges.h"
18 #include "base/stl_util.h"
19 #include "base/strings/string_number_conversions.h"
20 #include "base/strings/string_split.h"
21 #include "base/system/sys_info.h"
22 #include "build/build_config.h"
23 #include "cc/base/switches.h"
24 #include "components/viz/common/features.h"
25 #include "content/browser/compositor/image_transport_factory.h"
26 #include "content/browser/gpu/gpu_data_manager_impl.h"
27 #include "content/browser/gpu/gpu_process_host.h"
28 #include "content/public/common/content_features.h"
29 #include "content/public/common/content_switches.h"
30 #include "gpu/command_buffer/service/gpu_switches.h"
31 #include "gpu/config/gpu_blocklist.h"
32 #include "gpu/config/gpu_driver_bug_list.h"
33 #include "gpu/config/gpu_driver_bug_workaround_type.h"
34 #include "gpu/config/gpu_feature_type.h"
35 #include "gpu/config/gpu_finch_features.h"
36 #include "gpu/config/gpu_switches.h"
37 #include "gpu/ipc/host/gpu_memory_buffer_support.h"
38 #include "gpu/vulkan/buildflags.h"
39 #include "media/media_buildflags.h"
40 #include "third_party/blink/public/common/switches.h"
41 #include "ui/gl/gl_switches.h"
42 
43 namespace content {
44 
45 namespace {
46 
47 const int kMinRasterThreads = 1;
48 const int kMaxRasterThreads = 4;
49 
50 const int kMinMSAASampleCount = 0;
51 
52 enum class GpuFeatureInfoType { kCurrent, kForHardwareGpu };
53 
54 struct DisableInfo {
55   // The feature being disabled will be listed as a problem with |description|.
Problemcontent::__anonf17ba3dd0111::DisableInfo56   static DisableInfo Problem(const std::string& description) {
57     return DisableInfo{true, description};
58   }
59 
60   // The feature being disabled will not be listed as a problem.
NotProblemcontent::__anonf17ba3dd0111::DisableInfo61   static DisableInfo NotProblem() { return DisableInfo{false, ""}; }
62 
63   bool is_problem;
64   std::string description;
65 };
66 
67 struct GpuFeatureData {
68   std::string name;
69   gpu::GpuFeatureStatus status;
70   bool disabled;
71   DisableInfo disabled_info;
72   bool fallback_to_software;
73 };
74 
SafeGetFeatureStatus(const gpu::GpuFeatureInfo & gpu_feature_info,gpu::GpuFeatureType feature)75 gpu::GpuFeatureStatus SafeGetFeatureStatus(
76     const gpu::GpuFeatureInfo& gpu_feature_info,
77     gpu::GpuFeatureType feature) {
78   if (!gpu_feature_info.IsInitialized()) {
79     // The GPU process probably crashed during startup, but we can't
80     // assert this as the test bots are slow, and recording the crash
81     // is racy. Be robust and just say that all features are disabled.
82     return gpu::kGpuFeatureStatusDisabled;
83   }
84   DCHECK(feature >= 0 && feature < gpu::NUMBER_OF_GPU_FEATURE_TYPES);
85   return gpu_feature_info.status_values[feature];
86 }
87 
GetGpuFeatureData(const gpu::GpuFeatureInfo & gpu_feature_info,size_t index,bool * eof)88 const GpuFeatureData GetGpuFeatureData(
89     const gpu::GpuFeatureInfo& gpu_feature_info,
90     size_t index,
91     bool* eof) {
92   const base::CommandLine& command_line =
93       *base::CommandLine::ForCurrentProcess();
94 
95   const GpuFeatureData kGpuFeatureData[] = {
96     {"2d_canvas",
97      SafeGetFeatureStatus(gpu_feature_info,
98                           gpu::GPU_FEATURE_TYPE_ACCELERATED_2D_CANVAS),
99      command_line.HasSwitch(switches::kDisableAccelerated2dCanvas),
100      DisableInfo::Problem(
101          "Accelerated 2D canvas is unavailable: either disabled "
102          "via blocklist or the command line."),
103      true},
104     {"gpu_compositing",
105      // TODO(sgilhuly): Replace with a check to see which backend is used for
106      // compositing; do the same for GPU rasterization if it's enabled. For now
107      // assume that if GL is blocklisted, then Vulkan is also. Check GL to see
108      // if GPU compositing is disabled.
109      SafeGetFeatureStatus(gpu_feature_info,
110                           gpu::GPU_FEATURE_TYPE_ACCELERATED_GL),
111      GpuDataManagerImpl::GetInstance()->IsGpuCompositingDisabled(),
112      DisableInfo::Problem(
113          "Gpu compositing has been disabled, either via blocklist, about:flags "
114          "or the command line. The browser will fall back to software "
115          "compositing and hardware acceleration will be unavailable."),
116      true},
117     {"webgl",
118      SafeGetFeatureStatus(gpu_feature_info,
119                           gpu::GPU_FEATURE_TYPE_ACCELERATED_WEBGL),
120      command_line.HasSwitch(switches::kDisableWebGL),
121      DisableInfo::Problem(
122          "WebGL has been disabled via blocklist or the command line."),
123      false},
124     {"protected_video_decode",
125      SafeGetFeatureStatus(gpu_feature_info,
126                           gpu::GPU_FEATURE_TYPE_PROTECTED_VIDEO_DECODE),
127      false,
128      DisableInfo::Problem(
129          "Protected video decode has been disabled, via blocklist."),
130      false},
131     {"video_decode",
132      SafeGetFeatureStatus(gpu_feature_info,
133                           gpu::GPU_FEATURE_TYPE_ACCELERATED_VIDEO_DECODE),
134 #if (defined(OS_LINUX) && !defined(OS_ANDROID) && !defined(OS_CHROMEOS)) || defined(OS_BSD)
135      !command_line.HasSwitch(switches::kEnableAcceleratedVideoDecode),
136 #else
137      command_line.HasSwitch(switches::kDisableAcceleratedVideoDecode),
138 #endif  // (defined(OS_LINUX) && !defined(OS_ANDROID) && !defined(OS_CHROMEOS)) || defined(OS_BSD)
139      DisableInfo::Problem(
140          "Accelerated video decode has been disabled, either via blocklist, "
141          "about:flags or the command line."),
142      true},
143     {"rasterization",
144      SafeGetFeatureStatus(gpu_feature_info,
145                           gpu::GPU_FEATURE_TYPE_GPU_RASTERIZATION),
146      (command_line.HasSwitch(switches::kDisableGpuRasterization)),
147      DisableInfo::Problem(
148          "Accelerated rasterization has been disabled, either via blocklist, "
149          "about:flags or the command line."),
150      true},
151     {"oop_rasterization",
152      SafeGetFeatureStatus(gpu_feature_info,
153                           gpu::GPU_FEATURE_TYPE_OOP_RASTERIZATION),
154      command_line.HasSwitch(switches::kDisableOopRasterization),
155      DisableInfo::NotProblem(), false},
156     {"opengl",
157      SafeGetFeatureStatus(gpu_feature_info,
158                           gpu::GPU_FEATURE_TYPE_ACCELERATED_GL),
159      false /* disabled */, DisableInfo::NotProblem(),
160      false /* fallback_to_software */},
161 #if defined(OS_MAC)
162     {"metal",
163      SafeGetFeatureStatus(gpu_feature_info, gpu::GPU_FEATURE_TYPE_METAL),
164      !base::FeatureList::IsEnabled(features::kMetal) /* disabled */,
165      DisableInfo::NotProblem(), false /* fallback_to_software */},
166 #endif
167 #if BUILDFLAG(ENABLE_VULKAN)
168     {"vulkan",
169      SafeGetFeatureStatus(gpu_feature_info, gpu::GPU_FEATURE_TYPE_VULKAN),
170      !features::IsUsingVulkan() &&
171          !command_line.HasSwitch(switches::kUseVulkan) /* disabled */,
172      DisableInfo::NotProblem(), false /* fallback_to_software */},
173 #endif
174     {"multiple_raster_threads", gpu::kGpuFeatureStatusEnabled,
175      NumberOfRendererRasterThreads() == 1,
176      DisableInfo::Problem("Raster is using a single thread."), false},
177 #if defined(OS_ANDROID)
178     {"surface_control",
179      SafeGetFeatureStatus(gpu_feature_info,
180                           gpu::GPU_FEATURE_TYPE_ANDROID_SURFACE_CONTROL),
181      !features::IsAndroidSurfaceControlEnabled(),
182      DisableInfo::Problem(
183          "Surface Control has been disabled by Finch trial or command line."),
184      false},
185 #endif
186     {"webgl2",
187      SafeGetFeatureStatus(gpu_feature_info,
188                           gpu::GPU_FEATURE_TYPE_ACCELERATED_WEBGL2),
189      (command_line.HasSwitch(switches::kDisableWebGL) ||
190       command_line.HasSwitch(switches::kDisableWebGL2)),
191      DisableInfo::Problem(
192          "WebGL2 has been disabled via blocklist or the command line."),
193      false},
194     {"skia_renderer", gpu::kGpuFeatureStatusEnabled,
195      !features::IsUsingSkiaRenderer(), DisableInfo::NotProblem(), false},
196   };
197   DCHECK(index < base::size(kGpuFeatureData));
198   *eof = (index == base::size(kGpuFeatureData) - 1);
199   return kGpuFeatureData[index];
200 }
201 
GetFeatureStatusImpl(GpuFeatureInfoType type)202 std::unique_ptr<base::DictionaryValue> GetFeatureStatusImpl(
203     GpuFeatureInfoType type) {
204   GpuDataManagerImpl* manager = GpuDataManagerImpl::GetInstance();
205   std::string gpu_access_blocked_reason;
206   bool gpu_access_blocked =
207       !manager->GpuAccessAllowed(&gpu_access_blocked_reason);
208   const gpu::GpuFeatureInfo gpu_feature_info =
209       type == GpuFeatureInfoType::kCurrent
210           ? manager->GetGpuFeatureInfo()
211           : manager->GetGpuFeatureInfoForHardwareGpu();
212 
213   auto feature_status_dict = std::make_unique<base::DictionaryValue>();
214 
215   bool eof = false;
216   for (size_t i = 0; !eof; ++i) {
217     const GpuFeatureData gpu_feature_data =
218         GetGpuFeatureData(gpu_feature_info, i, &eof);
219     std::string status;
220     // Features undergoing a finch controlled roll out.
221     if (gpu_feature_data.name == "skia_renderer" ||
222         gpu_feature_data.name == "viz_hit_test_surface_layer") {
223       status = (gpu_feature_data.disabled ? "disabled_off_ok" : "enabled_on");
224     } else if (gpu_feature_data.disabled || gpu_access_blocked ||
225                gpu_feature_data.status == gpu::kGpuFeatureStatusDisabled) {
226       status = "disabled";
227       if (gpu_feature_data.fallback_to_software)
228         status += "_software";
229       else
230         status += "_off";
231     } else if (gpu_feature_data.status == gpu::kGpuFeatureStatusBlocklisted) {
232       status = "unavailable_off";
233     } else if (gpu_feature_data.status == gpu::kGpuFeatureStatusSoftware) {
234       status = "unavailable_software";
235     } else {
236       status = "enabled";
237       if ((gpu_feature_data.name == "webgl" ||
238            gpu_feature_data.name == "webgl2") &&
239           manager->IsGpuCompositingDisabled())
240         status += "_readback";
241       if (gpu_feature_data.name == "rasterization") {
242         const base::CommandLine& command_line =
243             *base::CommandLine::ForCurrentProcess();
244         if (command_line.HasSwitch(switches::kEnableGpuRasterization))
245           status += "_force";
246       }
247       if (gpu_feature_data.name == "multiple_raster_threads") {
248         const base::CommandLine& command_line =
249             *base::CommandLine::ForCurrentProcess();
250         if (command_line.HasSwitch(switches::kNumRasterThreads))
251           status += "_force";
252         status += "_on";
253       }
254       if (gpu_feature_data.name == "opengl" ||
255           gpu_feature_data.name == "metal" ||
256           gpu_feature_data.name == "vulkan" ||
257           gpu_feature_data.name == "surface_control") {
258         status += "_on";
259       }
260     }
261     feature_status_dict->SetString(gpu_feature_data.name, status);
262   }
263   return feature_status_dict;
264 }
265 
GetProblemsImpl(GpuFeatureInfoType type)266 std::unique_ptr<base::ListValue> GetProblemsImpl(GpuFeatureInfoType type) {
267   GpuDataManagerImpl* manager = GpuDataManagerImpl::GetInstance();
268   std::string gpu_access_blocked_reason;
269   bool gpu_access_blocked =
270       !manager->GpuAccessAllowed(&gpu_access_blocked_reason);
271   const gpu::GpuFeatureInfo gpu_feature_info =
272       type == GpuFeatureInfoType::kCurrent
273           ? manager->GetGpuFeatureInfo()
274           : manager->GetGpuFeatureInfoForHardwareGpu();
275 
276   auto problem_list = std::make_unique<base::ListValue>();
277   if (!gpu_feature_info.applied_gpu_blocklist_entries.empty()) {
278     std::unique_ptr<gpu::GpuBlocklist> blocklist(gpu::GpuBlocklist::Create());
279     blocklist->GetReasons(problem_list.get(), "disabledFeatures",
280                           gpu_feature_info.applied_gpu_blocklist_entries);
281   }
282   if (!gpu_feature_info.applied_gpu_driver_bug_list_entries.empty()) {
283     std::unique_ptr<gpu::GpuDriverBugList> bug_list(
284         gpu::GpuDriverBugList::Create());
285     bug_list->GetReasons(problem_list.get(), "workarounds",
286                          gpu_feature_info.applied_gpu_driver_bug_list_entries);
287   }
288 
289   if (gpu_access_blocked) {
290     auto problem = std::make_unique<base::DictionaryValue>();
291     problem->SetString("description", "GPU process was unable to boot: " +
292                                           gpu_access_blocked_reason);
293     problem->Set("crBugs", std::make_unique<base::ListValue>());
294     auto disabled_features = std::make_unique<base::ListValue>();
295     disabled_features->AppendString("all");
296     problem->Set("affectedGpuSettings", std::move(disabled_features));
297     problem->SetString("tag", "disabledFeatures");
298     problem_list->Insert(0, std::move(problem));
299   }
300 
301   bool eof = false;
302   for (size_t i = 0; !eof; ++i) {
303     const GpuFeatureData gpu_feature_data =
304         GetGpuFeatureData(gpu_feature_info, i, &eof);
305     if (gpu_feature_data.disabled &&
306         gpu_feature_data.disabled_info.is_problem) {
307       auto problem = std::make_unique<base::DictionaryValue>();
308       problem->SetString("description",
309                          gpu_feature_data.disabled_info.description);
310       problem->Set("crBugs", std::make_unique<base::ListValue>());
311       auto disabled_features = std::make_unique<base::ListValue>();
312       disabled_features->AppendString(gpu_feature_data.name);
313       problem->Set("affectedGpuSettings", std::move(disabled_features));
314       problem->SetString("tag", "disabledFeatures");
315       problem_list->Append(std::move(problem));
316     }
317   }
318   return problem_list;
319 }
320 
GetDriverBugWorkaroundsImpl(GpuFeatureInfoType type)321 std::vector<std::string> GetDriverBugWorkaroundsImpl(GpuFeatureInfoType type) {
322   GpuDataManagerImpl* manager = GpuDataManagerImpl::GetInstance();
323   const gpu::GpuFeatureInfo gpu_feature_info =
324       type == GpuFeatureInfoType::kCurrent
325           ? manager->GetGpuFeatureInfo()
326           : manager->GetGpuFeatureInfoForHardwareGpu();
327 
328   std::vector<std::string> workarounds;
329   for (auto workaround : gpu_feature_info.enabled_gpu_driver_bug_workarounds) {
330     workarounds.push_back(gpu::GpuDriverBugWorkaroundTypeToString(
331         static_cast<gpu::GpuDriverBugWorkaroundType>(workaround)));
332   }
333   // Tell clients about the disabled extensions and disabled WebGL
334   // extensions as well, to avoid confusion. Do this in a way that's
335   // compatible with the current reporting of driver bug workarounds
336   // to DevTools and Telemetry, and from there to the GPU tests.
337   //
338   // This code must be kept in sync with
339   // GpuBenchmarking::GetGpuDriverBugWorkarounds.
340   for (auto ext : base::SplitString(gpu_feature_info.disabled_extensions,
341                                     " ",
342                                     base::TRIM_WHITESPACE,
343                                     base::SPLIT_WANT_NONEMPTY)) {
344     workarounds.push_back("disabled_extension_" + ext);
345   }
346   for (auto ext : base::SplitString(gpu_feature_info.disabled_webgl_extensions,
347                                     " ",
348                                     base::TRIM_WHITESPACE,
349                                     base::SPLIT_WANT_NONEMPTY)) {
350     workarounds.push_back("disabled_webgl_extension_" + ext);
351   }
352   return workarounds;
353 }
354 
355 }  // namespace
356 
NumberOfRendererRasterThreads()357 int NumberOfRendererRasterThreads() {
358   int num_processors = base::SysInfo::NumberOfProcessors();
359 
360 #if defined(OS_ANDROID) || \
361     (defined(OS_CHROMEOS) && defined(ARCH_CPU_ARM_FAMILY))
362   // Android and ChromeOS ARM devices may report 6 to 8 CPUs for big.LITTLE
363   // configurations. Limit the number of raster threads based on maximum of
364   // 4 big cores.
365   num_processors = std::min(num_processors, 4);
366 #endif
367 
368   int num_raster_threads = num_processors / 2;
369 
370 #if defined(OS_ANDROID)
371   // Limit the number of raster threads to 1 on Android.
372   // TODO(reveman): Remove this when we have a better mechanims to prevent
373   // pre-paint raster work from slowing down non-raster work. crbug.com/504515
374   num_raster_threads = 1;
375 #endif
376 
377   const base::CommandLine& command_line =
378       *base::CommandLine::ForCurrentProcess();
379 
380   if (command_line.HasSwitch(switches::kNumRasterThreads)) {
381     std::string string_value = command_line.GetSwitchValueASCII(
382         switches::kNumRasterThreads);
383     if (!base::StringToInt(string_value, &num_raster_threads)) {
384       DLOG(WARNING) << "Failed to parse switch " <<
385           switches::kNumRasterThreads  << ": " << string_value;
386     }
387   }
388 
389   return base::ClampToRange(num_raster_threads, kMinRasterThreads,
390                             kMaxRasterThreads);
391 }
392 
IsZeroCopyUploadEnabled()393 bool IsZeroCopyUploadEnabled() {
394   const base::CommandLine& command_line =
395       *base::CommandLine::ForCurrentProcess();
396 #if defined(OS_MAC)
397   return !command_line.HasSwitch(blink::switches::kDisableZeroCopy);
398 #else
399   return command_line.HasSwitch(blink::switches::kEnableZeroCopy);
400 #endif
401 }
402 
IsPartialRasterEnabled()403 bool IsPartialRasterEnabled() {
404   const auto& command_line = *base::CommandLine::ForCurrentProcess();
405   return !command_line.HasSwitch(blink::switches::kDisablePartialRaster);
406 }
407 
IsGpuMemoryBufferCompositorResourcesEnabled()408 bool IsGpuMemoryBufferCompositorResourcesEnabled() {
409   const base::CommandLine& command_line =
410       *base::CommandLine::ForCurrentProcess();
411   if (command_line.HasSwitch(
412           blink::switches::kEnableGpuMemoryBufferCompositorResources)) {
413     return true;
414   }
415   if (command_line.HasSwitch(
416           switches::kDisableGpuMemoryBufferCompositorResources)) {
417     return false;
418   }
419 
420 #if defined(OS_MAC)
421   return true;
422 #else
423   return false;
424 #endif
425 }
426 
GpuRasterizationMSAASampleCount()427 int GpuRasterizationMSAASampleCount() {
428   const base::CommandLine& command_line =
429       *base::CommandLine::ForCurrentProcess();
430 
431   if (!command_line.HasSwitch(
432           blink::switches::kGpuRasterizationMSAASampleCount))
433 #if defined(OS_ANDROID)
434     return 4;
435 #else
436     // Desktop platforms will compute this automatically based on DPI.
437     return -1;
438 #endif
439   std::string string_value = command_line.GetSwitchValueASCII(
440       blink::switches::kGpuRasterizationMSAASampleCount);
441   int msaa_sample_count = 0;
442   if (base::StringToInt(string_value, &msaa_sample_count) &&
443       msaa_sample_count >= kMinMSAASampleCount) {
444     return msaa_sample_count;
445   } else {
446     DLOG(WARNING) << "Failed to parse switch "
447                   << blink::switches::kGpuRasterizationMSAASampleCount << ": "
448                   << string_value;
449     return 0;
450   }
451 }
452 
IsMainFrameBeforeActivationEnabled()453 bool IsMainFrameBeforeActivationEnabled() {
454   if (base::SysInfo::NumberOfProcessors() < 4)
455     return false;
456 
457   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
458           cc::switches::kDisableMainFrameBeforeActivation))
459     return false;
460 
461   return true;
462 }
463 
GetFeatureStatus()464 std::unique_ptr<base::DictionaryValue> GetFeatureStatus() {
465   return GetFeatureStatusImpl(GpuFeatureInfoType::kCurrent);
466 }
467 
GetProblems()468 std::unique_ptr<base::ListValue> GetProblems() {
469   return GetProblemsImpl(GpuFeatureInfoType::kCurrent);
470 }
471 
GetDriverBugWorkarounds()472 std::vector<std::string> GetDriverBugWorkarounds() {
473   return GetDriverBugWorkaroundsImpl(GpuFeatureInfoType::kCurrent);
474 }
475 
GetFeatureStatusForHardwareGpu()476 std::unique_ptr<base::DictionaryValue> GetFeatureStatusForHardwareGpu() {
477   return GetFeatureStatusImpl(GpuFeatureInfoType::kForHardwareGpu);
478 }
479 
GetProblemsForHardwareGpu()480 std::unique_ptr<base::ListValue> GetProblemsForHardwareGpu() {
481   return GetProblemsImpl(GpuFeatureInfoType::kForHardwareGpu);
482 }
483 
GetDriverBugWorkaroundsForHardwareGpu()484 std::vector<std::string> GetDriverBugWorkaroundsForHardwareGpu() {
485   return GetDriverBugWorkaroundsImpl(GpuFeatureInfoType::kForHardwareGpu);
486 }
487 
488 }  // namespace content
489