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