blob: 8de933ac7e3e59419d6b610dfc0a5a042163f7d5 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/gpu/compositor_util.h"
#include <stddef.h>
#include <algorithm>
#include <memory>
#include <utility>
#include "base/command_line.h"
#include "base/feature_list.h"
#include "base/logging.h"
#include "base/metrics/field_trial.h"
#include "base/numerics/ranges.h"
#include "base/stl_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/system/sys_info.h"
#include "build/build_config.h"
#include "cc/base/switches.h"
#include "components/viz/common/features.h"
#include "content/browser/compositor/image_transport_factory.h"
#include "content/browser/gpu/gpu_data_manager_impl.h"
#include "content/browser/gpu/gpu_process_host.h"
#include "content/public/common/content_features.h"
#include "content/public/common/content_switches.h"
#include "gpu/config/gpu_blacklist.h"
#include "gpu/config/gpu_driver_bug_list.h"
#include "gpu/config/gpu_driver_bug_workaround_type.h"
#include "gpu/config/gpu_feature_type.h"
#include "gpu/config/gpu_finch_features.h"
#include "gpu/config/gpu_switches.h"
#include "gpu/ipc/host/gpu_memory_buffer_support.h"
#include "media/media_buildflags.h"
#include "ui/base/ui_base_features.h"
#include "ui/gl/gl_switches.h"
namespace content {
namespace {
const int kMinRasterThreads = 1;
const int kMaxRasterThreads = 4;
const int kMinMSAASampleCount = 0;
enum class GpuFeatureInfoType { kCurrent, kForHardwareGpu };
struct GpuFeatureData {
std::string name;
gpu::GpuFeatureStatus status;
bool disabled;
std::string disabled_description;
bool fallback_to_software;
bool needs_gpu_access;
};
bool IsForceGpuRasterizationEnabled() {
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
return command_line->HasSwitch(switches::kForceGpuRasterization);
}
gpu::GpuFeatureStatus SafeGetFeatureStatus(
const gpu::GpuFeatureInfo& gpu_feature_info,
gpu::GpuFeatureType feature) {
if (!gpu_feature_info.IsInitialized()) {
// The GPU process probably crashed during startup, but we can't
// assert this as the test bots are slow, and recording the crash
// is racy. Be robust and just say that all features are disabled.
return gpu::kGpuFeatureStatusDisabled;
}
DCHECK(feature >= 0 && feature < gpu::NUMBER_OF_GPU_FEATURE_TYPES);
return gpu_feature_info.status_values[feature];
}
gpu::GpuFeatureStatus GetGpuCompositingStatus(
const gpu::GpuFeatureInfo& gpu_feature_info,
GpuFeatureInfoType type) {
if (features::IsMultiProcessMash())
return gpu::kGpuFeatureStatusEnabled;
gpu::GpuFeatureStatus status = SafeGetFeatureStatus(
gpu_feature_info, gpu::GPU_FEATURE_TYPE_GPU_COMPOSITING);
#if defined(USE_AURA) || defined(OS_MACOSX)
if (type == GpuFeatureInfoType::kCurrent &&
status == gpu::kGpuFeatureStatusEnabled &&
ImageTransportFactory::GetInstance()->IsGpuCompositingDisabled()) {
// We only adjust the status for kCurrent, because compositing status
// affects other feature status, and we want to preserve the kHardwareGpu
// feature status and don't want them to be modified by the current
// compositing status.
status = gpu::kGpuFeatureStatusDisabled;
}
#endif
return status;
}
const GpuFeatureData GetGpuFeatureData(
const gpu::GpuFeatureInfo& gpu_feature_info,
GpuFeatureInfoType type,
size_t index,
bool* eof) {
const base::CommandLine& command_line =
*base::CommandLine::ForCurrentProcess();
const GpuFeatureData kGpuFeatureData[] = {
{"2d_canvas",
SafeGetFeatureStatus(gpu_feature_info,
gpu::GPU_FEATURE_TYPE_ACCELERATED_2D_CANVAS),
command_line.HasSwitch(switches::kDisableAccelerated2dCanvas),
"Accelerated 2D canvas is unavailable: either disabled via blacklist or"
" the command line.",
true, true},
{"gpu_compositing", GetGpuCompositingStatus(gpu_feature_info, type),
command_line.HasSwitch(switches::kDisableGpuCompositing),
"Gpu compositing has been disabled, either via blacklist, about:flags "
"or the command line. The browser will fall back to software "
"compositing and hardware acceleration will be unavailable.",
true, true},
{"webgl",
SafeGetFeatureStatus(gpu_feature_info,
gpu::GPU_FEATURE_TYPE_ACCELERATED_WEBGL),
command_line.HasSwitch(switches::kDisableWebGL),
"WebGL has been disabled via blacklist or the command line.", false,
true},
{"flash_3d",
SafeGetFeatureStatus(gpu_feature_info, gpu::GPU_FEATURE_TYPE_FLASH3D),
command_line.HasSwitch(switches::kDisableFlash3d),
"Using 3d in flash has been disabled, either via blacklist, about:flags "
"or the command line.",
true, true},
{"flash_stage3d",
SafeGetFeatureStatus(gpu_feature_info,
gpu::GPU_FEATURE_TYPE_FLASH_STAGE3D),
command_line.HasSwitch(switches::kDisableFlashStage3d),
"Using Stage3d in Flash has been disabled, either via blacklist, "
"about:flags or the command line.",
true, true},
{"flash_stage3d_baseline",
SafeGetFeatureStatus(gpu_feature_info,
gpu::GPU_FEATURE_TYPE_FLASH_STAGE3D_BASELINE),
command_line.HasSwitch(switches::kDisableFlashStage3d),
"Using Stage3d Baseline profile in Flash has been disabled, either via "
"blacklist, about:flags or the command line.",
true, true},
{"protected_video_decode",
SafeGetFeatureStatus(gpu_feature_info,
gpu::GPU_FEATURE_TYPE_PROTECTED_VIDEO_DECODE),
false, "Protected video decode has been disabled, via blacklist.", false,
true},
{"video_decode",
SafeGetFeatureStatus(gpu_feature_info,
gpu::GPU_FEATURE_TYPE_ACCELERATED_VIDEO_DECODE),
command_line.HasSwitch(switches::kDisableAcceleratedVideoDecode),
"Accelerated video decode has been disabled, either via blacklist, "
"about:flags or the command line.",
true, true},
{"rasterization",
SafeGetFeatureStatus(gpu_feature_info,
gpu::GPU_FEATURE_TYPE_GPU_RASTERIZATION),
(command_line.HasSwitch(switches::kDisableGpuRasterization) &&
!IsForceGpuRasterizationEnabled()),
"Accelerated rasterization has been disabled, either via blacklist, "
"about:flags or the command line.",
true, true},
{"oop_rasterization",
SafeGetFeatureStatus(gpu_feature_info,
gpu::GPU_FEATURE_TYPE_OOP_RASTERIZATION),
command_line.HasSwitch(switches::kDisableOopRasterization),
"Out-of-process accelerated rasterization has been disabled, either "
"via blacklist, about:flags or the command line.",
false, true},
{"multiple_raster_threads", gpu::kGpuFeatureStatusEnabled,
NumberOfRendererRasterThreads() == 1, "Raster is using a single thread.",
false, true},
{"surface_control",
SafeGetFeatureStatus(gpu_feature_info,
gpu::GPU_FEATURE_TYPE_ANDROID_SURFACE_CONTROL),
#if defined(OS_ANDROID)
!features::IsAndroidSurfaceControlEnabled(),
#else
false,
#endif
"Surface Control has been disabled by Finch trial or command line.",
false, false},
{"surface_synchronization", gpu::kGpuFeatureStatusEnabled,
!features::IsSurfaceSynchronizationEnabled(),
"Surface synchronization has been disabled by Finch trial or command "
"line.",
false, false},
{"webgl2",
SafeGetFeatureStatus(gpu_feature_info,
gpu::GPU_FEATURE_TYPE_ACCELERATED_WEBGL2),
(command_line.HasSwitch(switches::kDisableWebGL) ||
command_line.HasSwitch(switches::kDisableWebGL2)),
"WebGL2 has been disabled via blacklist or the command line.", false,
true},
{"viz_display_compositor", gpu::kGpuFeatureStatusEnabled,
!features::IsVizDisplayCompositorEnabled(),
"Viz service display compositor is not enabled by default.", false,
false},
{"viz_hit_test_surface_layer", gpu::kGpuFeatureStatusEnabled,
!features::IsVizHitTestingSurfaceLayerEnabled(),
"Viz hit-test surface layer version is not enabled by default.", false,
false},
{"skia_renderer", gpu::kGpuFeatureStatusEnabled,
!features::IsUsingSkiaRenderer(),
"Skia renderer is not used by default.", false, false},
};
DCHECK(index < base::size(kGpuFeatureData));
*eof = (index == base::size(kGpuFeatureData) - 1);
return kGpuFeatureData[index];
}
std::unique_ptr<base::DictionaryValue> GetFeatureStatusImpl(
GpuFeatureInfoType type) {
GpuDataManagerImpl* manager = GpuDataManagerImpl::GetInstance();
std::string gpu_access_blocked_reason;
bool gpu_access_blocked =
!manager->GpuAccessAllowed(&gpu_access_blocked_reason);
const gpu::GpuFeatureInfo gpu_feature_info =
type == GpuFeatureInfoType::kCurrent
? manager->GetGpuFeatureInfo()
: manager->GetGpuFeatureInfoForHardwareGpu();
auto feature_status_dict = std::make_unique<base::DictionaryValue>();
bool eof = false;
for (size_t i = 0; !eof; ++i) {
const GpuFeatureData gpu_feature_data =
GetGpuFeatureData(gpu_feature_info, type, i, &eof);
std::string status;
if (gpu_feature_data.name == "surface_synchronization") {
status = (!gpu_feature_data.disabled ? "enabled_on" : "disabled_off");
} else if (gpu_feature_data.name == "viz_display_compositor") {
status = (!gpu_feature_data.disabled ? "enabled_on" : "disabled_off");
} else if (gpu_feature_data.disabled || gpu_access_blocked ||
gpu_feature_data.status == gpu::kGpuFeatureStatusDisabled) {
status = "disabled";
if (gpu_feature_data.fallback_to_software)
status += "_software";
else
status += "_off";
} else if (gpu_feature_data.status == gpu::kGpuFeatureStatusBlacklisted) {
status = "unavailable_off";
} else if (gpu_feature_data.status == gpu::kGpuFeatureStatusSoftware) {
status = "unavailable_software";
} else {
status = "enabled";
if ((gpu_feature_data.name == "webgl" ||
gpu_feature_data.name == "webgl2") &&
(GetGpuCompositingStatus(gpu_feature_info, type) !=
gpu::kGpuFeatureStatusEnabled))
status += "_readback";
if (gpu_feature_data.name == "rasterization") {
if (IsForceGpuRasterizationEnabled())
status += "_force";
}
if (gpu_feature_data.name == "multiple_raster_threads") {
const base::CommandLine& command_line =
*base::CommandLine::ForCurrentProcess();
if (command_line.HasSwitch(switches::kNumRasterThreads))
status += "_force";
status += "_on";
}
if (gpu_feature_data.name == "skia_renderer") {
if (features::IsUsingSkiaRenderer())
status += "_on";
}
if (gpu_feature_data.name == "viz_hit_test_surface_layer") {
if (features::IsVizHitTestingSurfaceLayerEnabled())
status += "_on";
}
}
feature_status_dict->SetString(gpu_feature_data.name, status);
}
return feature_status_dict;
}
std::unique_ptr<base::ListValue> GetProblemsImpl(GpuFeatureInfoType type) {
GpuDataManagerImpl* manager = GpuDataManagerImpl::GetInstance();
std::string gpu_access_blocked_reason;
bool gpu_access_blocked =
!manager->GpuAccessAllowed(&gpu_access_blocked_reason);
const gpu::GpuFeatureInfo gpu_feature_info =
type == GpuFeatureInfoType::kCurrent
? manager->GetGpuFeatureInfo()
: manager->GetGpuFeatureInfoForHardwareGpu();
auto problem_list = std::make_unique<base::ListValue>();
if (!gpu_feature_info.applied_gpu_blacklist_entries.empty()) {
std::unique_ptr<gpu::GpuBlacklist> blacklist(gpu::GpuBlacklist::Create());
blacklist->GetReasons(problem_list.get(), "disabledFeatures",
gpu_feature_info.applied_gpu_blacklist_entries);
}
if (!gpu_feature_info.applied_gpu_driver_bug_list_entries.empty()) {
std::unique_ptr<gpu::GpuDriverBugList> bug_list(
gpu::GpuDriverBugList::Create());
bug_list->GetReasons(problem_list.get(), "workarounds",
gpu_feature_info.applied_gpu_driver_bug_list_entries);
}
if (gpu_access_blocked) {
auto problem = std::make_unique<base::DictionaryValue>();
problem->SetString("description", "GPU process was unable to boot: " +
gpu_access_blocked_reason);
problem->Set("crBugs", std::make_unique<base::ListValue>());
auto disabled_features = std::make_unique<base::ListValue>();
disabled_features->AppendString("all");
problem->Set("affectedGpuSettings", std::move(disabled_features));
problem->SetString("tag", "disabledFeatures");
problem_list->Insert(0, std::move(problem));
}
bool eof = false;
for (size_t i = 0; !eof; ++i) {
const GpuFeatureData gpu_feature_data =
GetGpuFeatureData(gpu_feature_info, type, i, &eof);
if (gpu_feature_data.disabled) {
auto problem = std::make_unique<base::DictionaryValue>();
problem->SetString("description", gpu_feature_data.disabled_description);
problem->Set("crBugs", std::make_unique<base::ListValue>());
auto disabled_features = std::make_unique<base::ListValue>();
disabled_features->AppendString(gpu_feature_data.name);
problem->Set("affectedGpuSettings", std::move(disabled_features));
problem->SetString("tag", "disabledFeatures");
problem_list->Append(std::move(problem));
}
}
return problem_list;
}
std::vector<std::string> GetDriverBugWorkaroundsImpl(GpuFeatureInfoType type) {
GpuDataManagerImpl* manager = GpuDataManagerImpl::GetInstance();
const gpu::GpuFeatureInfo gpu_feature_info =
type == GpuFeatureInfoType::kCurrent
? manager->GetGpuFeatureInfo()
: manager->GetGpuFeatureInfoForHardwareGpu();
std::vector<std::string> workarounds;
for (auto workaround : gpu_feature_info.enabled_gpu_driver_bug_workarounds) {
workarounds.push_back(gpu::GpuDriverBugWorkaroundTypeToString(
static_cast<gpu::GpuDriverBugWorkaroundType>(workaround)));
}
// Tell clients about the disabled extensions and disabled WebGL
// extensions as well, to avoid confusion. Do this in a way that's
// compatible with the current reporting of driver bug workarounds
// to DevTools and Telemetry, and from there to the GPU tests.
//
// This code must be kept in sync with
// GpuBenchmarking::GetGpuDriverBugWorkarounds.
for (auto ext : base::SplitString(gpu_feature_info.disabled_extensions,
" ",
base::TRIM_WHITESPACE,
base::SPLIT_WANT_NONEMPTY)) {
workarounds.push_back("disabled_extension_" + ext);
}
for (auto ext : base::SplitString(gpu_feature_info.disabled_webgl_extensions,
" ",
base::TRIM_WHITESPACE,
base::SPLIT_WANT_NONEMPTY)) {
workarounds.push_back("disabled_webgl_extension_" + ext);
}
return workarounds;
}
} // namespace
int NumberOfRendererRasterThreads() {
int num_processors = base::SysInfo::NumberOfProcessors();
#if defined(OS_ANDROID) || \
(defined(OS_CHROMEOS) && defined(ARCH_CPU_ARM_FAMILY))
// Android and ChromeOS ARM devices may report 6 to 8 CPUs for big.LITTLE
// configurations. Limit the number of raster threads based on maximum of
// 4 big cores.
num_processors = std::min(num_processors, 4);
#endif
int num_raster_threads = num_processors / 2;
#if defined(OS_ANDROID)
// Limit the number of raster threads to 1 on Android.
// TODO(reveman): Remove this when we have a better mechanims to prevent
// pre-paint raster work from slowing down non-raster work. crbug.com/504515
num_raster_threads = 1;
#endif
const base::CommandLine& command_line =
*base::CommandLine::ForCurrentProcess();
if (command_line.HasSwitch(switches::kNumRasterThreads)) {
std::string string_value = command_line.GetSwitchValueASCII(
switches::kNumRasterThreads);
if (!base::StringToInt(string_value, &num_raster_threads)) {
DLOG(WARNING) << "Failed to parse switch " <<
switches::kNumRasterThreads << ": " << string_value;
}
}
return base::ClampToRange(num_raster_threads, kMinRasterThreads,
kMaxRasterThreads);
}
bool IsZeroCopyUploadEnabled() {
const base::CommandLine& command_line =
*base::CommandLine::ForCurrentProcess();
#if defined(OS_MACOSX)
return !command_line.HasSwitch(switches::kDisableZeroCopy);
#else
return command_line.HasSwitch(switches::kEnableZeroCopy);
#endif
}
bool IsPartialRasterEnabled() {
const auto& command_line = *base::CommandLine::ForCurrentProcess();
return !command_line.HasSwitch(switches::kDisablePartialRaster);
}
bool IsGpuMemoryBufferCompositorResourcesEnabled() {
const base::CommandLine& command_line =
*base::CommandLine::ForCurrentProcess();
if (command_line.HasSwitch(
switches::kEnableGpuMemoryBufferCompositorResources)) {
return true;
}
if (command_line.HasSwitch(
switches::kDisableGpuMemoryBufferCompositorResources)) {
return false;
}
#if defined(OS_MACOSX)
return true;
#else
return false;
#endif
}
int GpuRasterizationMSAASampleCount() {
const base::CommandLine& command_line =
*base::CommandLine::ForCurrentProcess();
if (!command_line.HasSwitch(switches::kGpuRasterizationMSAASampleCount))
#if defined(OS_ANDROID)
return 4;
#else
// Desktop platforms will compute this automatically based on DPI.
return -1;
#endif
std::string string_value = command_line.GetSwitchValueASCII(
switches::kGpuRasterizationMSAASampleCount);
int msaa_sample_count = 0;
if (base::StringToInt(string_value, &msaa_sample_count) &&
msaa_sample_count >= kMinMSAASampleCount) {
return msaa_sample_count;
} else {
DLOG(WARNING) << "Failed to parse switch "
<< switches::kGpuRasterizationMSAASampleCount << ": "
<< string_value;
return 0;
}
}
bool IsMainFrameBeforeActivationEnabled() {
if (base::SysInfo::NumberOfProcessors() < 4)
return false;
if (base::CommandLine::ForCurrentProcess()->HasSwitch(
cc::switches::kDisableMainFrameBeforeActivation))
return false;
return true;
}
std::unique_ptr<base::DictionaryValue> GetFeatureStatus() {
return GetFeatureStatusImpl(GpuFeatureInfoType::kCurrent);
}
std::unique_ptr<base::ListValue> GetProblems() {
return GetProblemsImpl(GpuFeatureInfoType::kCurrent);
}
std::vector<std::string> GetDriverBugWorkarounds() {
return GetDriverBugWorkaroundsImpl(GpuFeatureInfoType::kCurrent);
}
std::unique_ptr<base::DictionaryValue> GetFeatureStatusForHardwareGpu() {
return GetFeatureStatusImpl(GpuFeatureInfoType::kForHardwareGpu);
}
std::unique_ptr<base::ListValue> GetProblemsForHardwareGpu() {
return GetProblemsImpl(GpuFeatureInfoType::kForHardwareGpu);
}
std::vector<std::string> GetDriverBugWorkaroundsForHardwareGpu() {
return GetDriverBugWorkaroundsImpl(GpuFeatureInfoType::kForHardwareGpu);
}
} // namespace content