0

Figure out whether we have enough information to make the final GPU blacklisting decision.

If yes and all GPU features are blacklisted, we don't launch GPU process.

BUG=154130
TEST=content_unittests
Review URL: https://codereview.chromium.org/11047011

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@160923 0039d316-1c4b-4281-b951-d872f2087c98
This commit is contained in:
zmo@chromium.org
2012-10-09 20:58:19 +00:00
parent 99245a080c
commit 3a62be0997
9 changed files with 265 additions and 41 deletions

@ -1013,7 +1013,7 @@ bool GpuBlacklist::GpuBlacklistEntry::Contains(
if (is_not_primary_gpu && is_not_secondary_gpu)
return false;
break;
default:
case kMultiGpuCategoryNone:
break;
}
switch (multi_gpu_style_) {
@ -1025,13 +1025,13 @@ bool GpuBlacklist::GpuBlacklistEntry::Contains(
if (!gpu_info.amd_switchable)
return false;
break;
default:
case kMultiGpuStyleNone:
break;
}
if (driver_vendor_info_.get() != NULL &&
if (driver_vendor_info_.get() != NULL && !gpu_info.driver_vendor.empty() &&
!driver_vendor_info_->Contains(gpu_info.driver_vendor))
return false;
if (driver_version_info_.get() != NULL) {
if (driver_version_info_.get() != NULL && !gpu_info.driver_version.empty()) {
std::string processed_driver_version;
if (driver_version_info_->IsLexical())
processed_driver_version = NumericalToLexical(gpu_info.driver_version);
@ -1042,16 +1042,16 @@ bool GpuBlacklist::GpuBlacklistEntry::Contains(
!driver_version_info_->Contains(driver_version))
return false;
}
if (driver_date_info_.get() != NULL) {
if (driver_date_info_.get() != NULL && !gpu_info.driver_date.empty()) {
Version driver_date;
GetDateFromString(gpu_info.driver_date, &driver_date);
if (!driver_date.IsValid() || !driver_date_info_->Contains(driver_date))
return false;
}
if (gl_vendor_info_.get() != NULL &&
if (gl_vendor_info_.get() != NULL && !gpu_info.gl_vendor.empty() &&
!gl_vendor_info_->Contains(gpu_info.gl_vendor))
return false;
if (gl_renderer_info_.get() != NULL &&
if (gl_renderer_info_.get() != NULL && !gpu_info.gl_renderer.empty() &&
!gl_renderer_info_->Contains(gpu_info.gl_renderer))
return false;
if (perf_graphics_info_.get() != NULL &&
@ -1080,12 +1080,34 @@ bool GpuBlacklist::GpuBlacklistEntry::Contains(
for (size_t i = 0; i < exceptions_.size(); ++i) {
if (exceptions_[i]->Contains(os_type, os_version, machine_model_name,
machine_model_version, gpu_info))
machine_model_version, gpu_info) &&
!exceptions_[i]->NeedsMoreInfo(gpu_info))
return false;
}
return true;
}
bool GpuBlacklist::GpuBlacklistEntry::NeedsMoreInfo(
const content::GPUInfo& gpu_info) const {
// We only check for missing info that might be collected with a gl context.
// If certain info is missing due to some error, say, we fail to collect
// vendor_id/device_id, then even if we launch GPU process and create a gl
// context, we won't gather such missing info, so we still return false.
if (driver_vendor_info_.get() && gpu_info.driver_vendor.empty())
return true;
if (driver_version_info_.get() && gpu_info.driver_version.empty())
return true;
if (gl_vendor_info_.get() && gpu_info.gl_vendor.empty())
return true;
if (gl_renderer_info_.get() && gpu_info.gl_renderer.empty())
return true;
for (size_t i = 0; i < exceptions_.size(); ++i) {
if (exceptions_[i]->NeedsMoreInfo(gpu_info))
return true;
}
return false;
}
GpuBlacklist::OsType GpuBlacklist::GpuBlacklistEntry::GetOsType() const {
if (os_info_.get() == NULL)
return kOsAny;
@ -1111,7 +1133,8 @@ GpuBlacklist::GpuBlacklistEntry::GetGpuSwitchingOption() const {
GpuBlacklist::GpuBlacklist()
: max_entry_id_(0),
contains_unknown_fields_(false) {
contains_unknown_fields_(false),
needs_more_info_(false) {
}
GpuBlacklist::~GpuBlacklist() {
@ -1209,6 +1232,10 @@ GpuBlacklist::Decision GpuBlacklist::MakeBlacklistDecision(
int type = 0;
GpuSwitchingOption switching = content::GPU_SWITCHING_OPTION_UNKNOWN;
needs_more_info_ = false;
int possible_type = 0;
GpuSwitchingOption possible_switching = content::GPU_SWITCHING_OPTION_UNKNOWN;
if (os == kOsAny)
os = GetOsType();
scoped_ptr<Version> my_os_version;
@ -1227,14 +1254,28 @@ GpuBlacklist::Decision GpuBlacklist::MakeBlacklistDecision(
if (blacklist_[i]->Contains(os, *os_version, current_machine_model_name_,
*current_machine_model_version_, gpu_info)) {
if (!blacklist_[i]->disabled()) {
type |= blacklist_[i]->GetGpuFeatureType();
bool not_final = blacklist_[i]->NeedsMoreInfo(gpu_info);
if (not_final)
possible_type |= blacklist_[i]->GetGpuFeatureType();
else
type |= blacklist_[i]->GetGpuFeatureType();
if (blacklist_[i]->GetGpuSwitchingOption() !=
content::GPU_SWITCHING_OPTION_UNKNOWN)
switching = blacklist_[i]->GetGpuSwitchingOption();
content::GPU_SWITCHING_OPTION_UNKNOWN) {
if (not_final)
possible_switching = blacklist_[i]->GetGpuSwitchingOption();
else
switching = blacklist_[i]->GetGpuSwitchingOption();
}
}
active_entries_.push_back(blacklist_[i]);
}
}
if ((possible_type != 0 && (possible_type | type) != type) ||
(possible_switching != content::GPU_SWITCHING_OPTION_UNKNOWN &&
switching == content::GPU_SWITCHING_OPTION_UNKNOWN))
needs_more_info_ = true;
Decision decision;
decision.blacklisted_features = static_cast<GpuFeatureType>(type);
decision.gpu_switching = switching;

@ -92,6 +92,11 @@ class CONTENT_EXPORT GpuBlacklist {
// Returns the version of the current blacklist.
std::string GetVersion() const;
// Check if we needs more gpu info to make the blacklisting decisions.
// This is computed from the last MakeBlacklistDecision() call.
// If yes, we should create a gl context and do a full gpu info collection.
bool needs_more_info() const { return needs_more_info_; }
private:
friend class GpuBlacklistTest;
FRIEND_TEST_ALL_PREFIXES(GpuBlacklistTest, ChromeVersionEntry);
@ -275,6 +280,10 @@ class CONTENT_EXPORT GpuBlacklist {
const Version& machine_model_version,
const content::GPUInfo& gpu_info) const;
// Determines whether we needs more gpu info to make the blacklisting
// decision. It should only be checked if Contains() returns true.
bool NeedsMoreInfo(const content::GPUInfo& gpu_info) const;
// Returns the OsType.
OsType GetOsType() const;
@ -466,6 +475,8 @@ class CONTENT_EXPORT GpuBlacklist {
bool contains_unknown_fields_;
bool needs_more_info_;
std::string current_machine_model_name_;
scoped_ptr<Version> current_machine_model_version_;

@ -1201,3 +1201,178 @@ TEST_F(GpuBlacklistTest, Video) {
EXPECT_EQ(type, content::GPU_FEATURE_TYPE_ACCELERATED_VIDEO);
}
TEST_F(GpuBlacklistTest, NeedsMoreInfo) {
const std::string json =
"{\n"
" \"name\": \"gpu blacklist\",\n"
" \"version\": \"0.1\",\n"
" \"entries\": [\n"
" {\n"
" \"id\": 1,\n"
" \"os\": {\n"
" \"type\": \"linux\"\n"
" },\n"
" \"vendor_id\": \"0x8086\",\n"
" \"driver_version\": {\n"
" \"op\": \"<\",\n"
" \"number\": \"10.7\"\n"
" },\n"
" \"blacklist\": [\n"
" \"webgl\"\n"
" ]\n"
" }\n"
" ]\n"
"}";
content::GPUInfo gpu_info;
gpu_info.gpu.vendor_id = 0x8086;
Version os_version("10.7");
scoped_ptr<GpuBlacklist> blacklist(Create());
EXPECT_TRUE(blacklist->LoadGpuBlacklist(json, GpuBlacklist::kAllOs));
// The case this entry does not apply.
GpuFeatureType type = blacklist->MakeBlacklistDecision(
GpuBlacklist::kOsMacosx, &os_version,
gpu_info).blacklisted_features;
EXPECT_EQ(0, type);
EXPECT_FALSE(blacklist->needs_more_info());
// The case this entry might apply, but need more info.
type = blacklist->MakeBlacklistDecision(
GpuBlacklist::kOsLinux, &os_version,
gpu_info).blacklisted_features;
EXPECT_EQ(0, type);
EXPECT_TRUE(blacklist->needs_more_info());
// The case we have full info, and this entry applies.
gpu_info.driver_version = "10.6";
type = blacklist->MakeBlacklistDecision(
GpuBlacklist::kOsLinux, &os_version,
gpu_info).blacklisted_features;
EXPECT_EQ(content::GPU_FEATURE_TYPE_WEBGL, type);
EXPECT_FALSE(blacklist->needs_more_info());
// The case we have full info, and this entry does not apply.
gpu_info.driver_version = "10.8";
type = blacklist->MakeBlacklistDecision(
GpuBlacklist::kOsLinux, &os_version,
gpu_info).blacklisted_features;
EXPECT_EQ(0, type);
EXPECT_FALSE(blacklist->needs_more_info());
}
TEST_F(GpuBlacklistTest, NeedsMoreInfoForExceptions) {
const std::string json =
"{\n"
" \"name\": \"gpu blacklist\",\n"
" \"version\": \"0.1\",\n"
" \"entries\": [\n"
" {\n"
" \"id\": 1,\n"
" \"os\": {\n"
" \"type\": \"linux\"\n"
" },\n"
" \"vendor_id\": \"0x8086\",\n"
" \"exceptions\": [\n"
" {\n"
" \"gl_renderer\": {\n"
" \"op\": \"contains\",\n"
" \"value\": \"mesa\"\n"
" }\n"
" }\n"
" ],\n"
" \"blacklist\": [\n"
" \"webgl\"\n"
" ]\n"
" }\n"
" ]\n"
"}";
content::GPUInfo gpu_info;
gpu_info.gpu.vendor_id = 0x8086;
Version os_version("10.7");
scoped_ptr<GpuBlacklist> blacklist(Create());
EXPECT_TRUE(blacklist->LoadGpuBlacklist(json, GpuBlacklist::kAllOs));
// The case this entry does not apply.
GpuFeatureType type = blacklist->MakeBlacklistDecision(
GpuBlacklist::kOsMacosx, &os_version,
gpu_info).blacklisted_features;
EXPECT_EQ(0, type);
EXPECT_FALSE(blacklist->needs_more_info());
// The case this entry might apply, but need more info.
type = blacklist->MakeBlacklistDecision(
GpuBlacklist::kOsLinux, &os_version,
gpu_info).blacklisted_features;
EXPECT_EQ(0, type);
EXPECT_TRUE(blacklist->needs_more_info());
// The case we have full info, and the exception applies (so the entry
// does not apply).
gpu_info.gl_renderer = "mesa";
type = blacklist->MakeBlacklistDecision(
GpuBlacklist::kOsLinux, &os_version,
gpu_info).blacklisted_features;
EXPECT_EQ(0, type);
EXPECT_FALSE(blacklist->needs_more_info());
// The case we have full info, and this entry applies.
gpu_info.gl_renderer = "my renderer";
type = blacklist->MakeBlacklistDecision(
GpuBlacklist::kOsLinux, &os_version,
gpu_info).blacklisted_features;
EXPECT_EQ(content::GPU_FEATURE_TYPE_WEBGL, type);
EXPECT_FALSE(blacklist->needs_more_info());
}
TEST_F(GpuBlacklistTest, IgnorableEntries) {
// If an entry will not change the blacklist decisions, then it should not
// trigger the needs_more_info flag.
const std::string json =
"{\n"
" \"name\": \"gpu blacklist\",\n"
" \"version\": \"0.1\",\n"
" \"entries\": [\n"
" {\n"
" \"id\": 1,\n"
" \"os\": {\n"
" \"type\": \"linux\"\n"
" },\n"
" \"vendor_id\": \"0x8086\",\n"
" \"blacklist\": [\n"
" \"webgl\"\n"
" ]\n"
" },\n"
" {\n"
" \"id\": 2,\n"
" \"os\": {\n"
" \"type\": \"linux\"\n"
" },\n"
" \"vendor_id\": \"0x8086\",\n"
" \"driver_version\": {\n"
" \"op\": \"<\",\n"
" \"number\": \"10.7\"\n"
" },\n"
" \"blacklist\": [\n"
" \"webgl\"\n"
" ]\n"
" }\n"
" ]\n"
"}";
content::GPUInfo gpu_info;
gpu_info.gpu.vendor_id = 0x8086;
Version os_version("10.7");
scoped_ptr<GpuBlacklist> blacklist(Create());
EXPECT_TRUE(blacklist->LoadGpuBlacklist(json, GpuBlacklist::kAllOs));
GpuFeatureType type = blacklist->MakeBlacklistDecision(
GpuBlacklist::kOsLinux, &os_version,
gpu_info).blacklisted_features;
EXPECT_EQ(content::GPU_FEATURE_TYPE_WEBGL, type);
EXPECT_FALSE(blacklist->needs_more_info());
}

@ -299,7 +299,15 @@ bool GpuDataManagerImpl::GpuAccessAllowed() const {
// than those in the preliminary gpu feature flags because the latter work
// through renderer commandline switches.
uint32 mask = ~(preliminary_gpu_feature_type_);
return (gpu_feature_type_ & mask) == 0;
if ((gpu_feature_type_ & mask) != 0)
return false;
if (gpu_feature_type_ == content::GPU_FEATURE_TYPE_ALL) {
if (gpu_blacklist_.get() && !gpu_blacklist_->needs_more_info())
return false;
}
return true;
}
void GpuDataManagerImpl::HandleGpuSwitch() {
@ -411,6 +419,9 @@ void GpuDataManagerImpl::AppendGpuCommandLine(
command_line->AppendSwitchASCII(switches::kSupportsDualGpus, "false");
}
if (!gpu_blacklist_.get() || !gpu_blacklist_->needs_more_info())
command_line->AppendSwitch(switches::kSkipGpuFullInfoCollection);
if (!swiftshader_path.empty())
command_line->AppendSwitchPath(switches::kSwiftShaderPath,
swiftshader_path);

@ -146,8 +146,7 @@ TEST_F(GpuDataManagerImplTest, GpuSideExceptions) {
manager->InitializeForTesting(blacklist_json, gpu_info);
EXPECT_TRUE(manager->GpuAccessAllowed());
EXPECT_EQ(content::GPU_FEATURE_TYPE_WEBGL,
manager->GetBlacklistedFeatures());
EXPECT_EQ(0, manager->GetBlacklistedFeatures());
// Now assue gpu process launches and full GPU info is collected.
gpu_info.gl_renderer = "NVIDIA GeForce GT 120";

@ -267,10 +267,8 @@ GpuProcessHost* GpuProcessHost::Get(GpuProcessKind kind,
// Don't grant further access to GPU if it is not allowed.
GpuDataManagerImpl* gpu_data_manager = GpuDataManagerImpl::GetInstance();
if (gpu_data_manager != NULL &&
(!gpu_data_manager->GpuAccessAllowed() ||
gpu_data_manager->GetBlacklistedFeatures() ==
content::GPU_FEATURE_TYPE_ALL))
DCHECK(gpu_data_manager);
if (!gpu_data_manager->GpuAccessAllowed())
return NULL;
if (g_gpu_process_hosts[kind] && HostIsValid(g_gpu_process_hosts[kind]))

@ -48,9 +48,6 @@
namespace {
void WarmUpSandbox(const content::GPUInfo&, bool);
#if defined(OS_LINUX)
void CollectGraphicsInfo(content::GPUInfo*);
#endif
}
// Main function for starting the Gpu process.
@ -122,19 +119,16 @@ int GpuMain(const content::MainFunctionParams& parameters) {
bool initialized_gl_context = false;
// Load and initialize the GL implementation and locate the GL entry points.
if (gfx::GLSurface::InitializeOneOff()) {
#if defined(OS_LINUX)
// We collect full GPU info on demand in Win/Mac, i.e., when about:gpu
// page opens. This is because we can make blacklist decisions based on
// preliminary GPU info.
// However, on Linux, we may not have enough info for blacklisting.
if (!gpu_info.gpu.vendor_id || !gpu_info.gpu.device_id ||
gpu_info.driver_vendor.empty() || gpu_info.driver_version.empty()) {
CollectGraphicsInfo(&gpu_info);
if (!command_line.HasSwitch(switches::kSkipGpuFullInfoCollection)) {
if (!gpu_info_collector::CollectGraphicsInfo(&gpu_info))
VLOG(1) << "gpu_info_collector::CollectGraphicsInfo failed";
content::GetContentClient()->SetGpuInfo(gpu_info);
// We know that CollectGraphicsInfo will initialize a GLContext.
initialized_gl_context = true;
}
#if !defined(OS_CHROMEOS)
#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
if (gpu_info.gpu.vendor_id == 0x10de && // NVIDIA
gpu_info.driver_vendor == "NVIDIA") {
base::ThreadRestrictions::AssertIOAllowed();
@ -145,7 +139,6 @@ int GpuMain(const content::MainFunctionParams& parameters) {
}
}
#endif // OS_CHROMEOS
#endif // OS_LINUX
} else {
VLOG(1) << "gfx::GLSurface::InitializeOneOff failed";
gpu_info.gpu_accessible = false;
@ -313,13 +306,5 @@ void WarmUpSandbox(const content::GPUInfo& gpu_info,
#endif
}
#if defined(OS_LINUX)
void CollectGraphicsInfo(content::GPUInfo* gpu_info) {
if (!gpu_info_collector::CollectGraphicsInfo(gpu_info))
VLOG(1) << "gpu_info_collector::CollectGraphicsInfo failed";
content::GetContentClient()->SetGpuInfo(*gpu_info);
}
#endif
} // namespace.

@ -599,6 +599,9 @@ const char kSingleProcess[] = "single-process";
// content. The switch is intended only for tests.
const char kSkipGpuDataLoading[] = "skip-gpu-data-loading";
// Skip collecting full GPU info upon GPU process launch.
const char kSkipGpuFullInfoCollection[] = "skip-gpu-full-info-collection";
// GestureTapDown events are deferred by this many miillseconds before
// sending them to the renderer.
const char kTapDownDeferralTimeMs[] = "tap-down-deferral-time";

@ -183,7 +183,8 @@ extern const char kEnableAcceleratedCompositingForOverflowScroll[];
extern const char kShowPaintRects[];
CONTENT_EXPORT extern const char kSimulateTouchScreenWithMouse[];
CONTENT_EXPORT extern const char kSingleProcess[];
CONTENT_EXPORT extern const char kSkipGpuDataLoading[];
extern const char kSkipGpuDataLoading[];
extern const char kSkipGpuFullInfoCollection[];
extern const char kTapDownDeferralTimeMs[];
CONTENT_EXPORT extern const char kTestSandbox[];
CONTENT_EXPORT extern const char kTestingFixedHttpPort[];