| // 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/gpu_data_manager_impl.h" |
| |
| #include "base/bind.h" |
| #include "base/bind_helpers.h" |
| #include "base/command_line.h" |
| #include "base/file_util.h" |
| #include "base/metrics/field_trial.h" |
| #include "base/string_piece.h" |
| #include "base/stringprintf.h" |
| #include "base/sys_info.h" |
| #include "base/values.h" |
| #include "base/version.h" |
| #include "content/browser/gpu/gpu_process_host.h" |
| #include "content/browser/gpu/gpu_util.h" |
| #include "content/common/gpu/gpu_messages.h" |
| #include "content/gpu/gpu_info_collector.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/browser/gpu_data_manager_observer.h" |
| #include "content/public/common/content_client.h" |
| #include "content/public/common/content_constants.h" |
| #include "content/public/common/content_switches.h" |
| #include "grit/content_resources.h" |
| #include "ui/base/ui_base_switches.h" |
| #include "ui/gl/gl_implementation.h" |
| #include "ui/gl/gl_switches.h" |
| #include "webkit/plugins/plugin_switches.h" |
| |
| #if defined(OS_WIN) |
| #include "base/win/windows_version.h" |
| #endif |
| |
| using content::BrowserThread; |
| using content::GpuDataManagerObserver; |
| using content::GpuFeatureType; |
| using content::GpuSwitchingOption; |
| |
| namespace { |
| |
| // Strip out the non-digital info; if after that, we get an empty string, |
| // return "0". |
| std::string ProcessVersionString(const std::string& raw_string) { |
| const std::string valid_set = "0123456789."; |
| size_t start_pos = raw_string.find_first_of(valid_set); |
| if (start_pos == std::string::npos) |
| return "0"; |
| size_t end_pos = raw_string.find_first_not_of(raw_string, start_pos); |
| std::string version_string = raw_string.substr( |
| start_pos, end_pos - start_pos); |
| if (version_string.empty()) |
| return "0"; |
| return version_string; |
| } |
| |
| } // namespace anonymous |
| |
| // static |
| content::GpuDataManager* content::GpuDataManager::GetInstance() { |
| return GpuDataManagerImpl::GetInstance(); |
| } |
| |
| // static |
| GpuDataManagerImpl* GpuDataManagerImpl::GetInstance() { |
| return Singleton<GpuDataManagerImpl>::get(); |
| } |
| |
| GpuDataManagerImpl::GpuDataManagerImpl() |
| : complete_gpu_info_already_requested_(false), |
| gpu_feature_type_(content::GPU_FEATURE_TYPE_UNKNOWN), |
| preliminary_gpu_feature_type_(content::GPU_FEATURE_TYPE_UNKNOWN), |
| gpu_switching_(content::GPU_SWITCHING_OPTION_AUTOMATIC), |
| observer_list_(new GpuDataManagerObserverList), |
| software_rendering_(false), |
| card_blacklisted_(false), |
| update_histograms_(true) { |
| CommandLine* command_line = CommandLine::ForCurrentProcess(); |
| if (command_line->HasSwitch(switches::kDisableAcceleratedCompositing)) { |
| command_line->AppendSwitch(switches::kDisableAccelerated2dCanvas); |
| command_line->AppendSwitch(switches::kDisableAcceleratedLayers); |
| } |
| if (command_line->HasSwitch(switches::kDisableGpu)) |
| BlacklistCard(); |
| if (command_line->HasSwitch(switches::kGpuSwitching)) { |
| std::string option_string = command_line->GetSwitchValueASCII( |
| switches::kGpuSwitching); |
| GpuSwitchingOption option = gpu_util::StringToGpuSwitchingOption( |
| option_string); |
| if (option != content::GPU_SWITCHING_OPTION_UNKNOWN) |
| gpu_switching_ = option; |
| } |
| } |
| |
| void GpuDataManagerImpl::Initialize() { |
| CommandLine* command_line = CommandLine::ForCurrentProcess(); |
| if (command_line->HasSwitch(switches::kSkipGpuDataLoading)) |
| return; |
| |
| content::GPUInfo gpu_info; |
| gpu_info_collector::CollectPreliminaryGraphicsInfo(&gpu_info); |
| #if defined(ARCH_CPU_X86_FAMILY) |
| if (!gpu_info.gpu.vendor_id || !gpu_info.gpu.device_id) |
| gpu_info.finalized = true; |
| #endif |
| |
| std::string gpu_blacklist_string; |
| if (!command_line->HasSwitch(switches::kIgnoreGpuBlacklist)) { |
| const base::StringPiece gpu_blacklist_json = |
| content::GetContentClient()->GetDataResource( |
| IDR_GPU_BLACKLIST, ui::SCALE_FACTOR_NONE); |
| gpu_blacklist_string = gpu_blacklist_json.as_string(); |
| } |
| |
| InitializeImpl(gpu_blacklist_string, gpu_info); |
| } |
| |
| void GpuDataManagerImpl::InitializeForTesting( |
| const std::string& gpu_blacklist_json, |
| const content::GPUInfo& gpu_info) { |
| // This function is for testing only, so disable histograms. |
| update_histograms_ = false; |
| |
| InitializeImpl(gpu_blacklist_json, gpu_info); |
| } |
| |
| void GpuDataManagerImpl::InitializeImpl( |
| const std::string& gpu_blacklist_json, |
| const content::GPUInfo& gpu_info) { |
| { |
| // This function should only be called in testing. |
| // We need clean up the gpu_info_ for a clean initialization. |
| const content::GPUInfo empty_gpu_info; |
| base::AutoLock auto_lock(gpu_info_lock_); |
| gpu_info_ = empty_gpu_info; |
| } |
| |
| if (!gpu_blacklist_json.empty()) { |
| std::string browser_version_string = ProcessVersionString( |
| content::GetContentClient()->GetProduct()); |
| CHECK(!browser_version_string.empty()); |
| gpu_blacklist_.reset(new GpuBlacklist()); |
| bool succeed = gpu_blacklist_->LoadGpuBlacklist( |
| browser_version_string, |
| gpu_blacklist_json, |
| GpuBlacklist::kCurrentOsOnly); |
| CHECK(succeed); |
| } |
| |
| UpdateGpuInfo(gpu_info); |
| UpdatePreliminaryBlacklistedFeatures(); |
| } |
| |
| GpuDataManagerImpl::~GpuDataManagerImpl() { |
| } |
| |
| void GpuDataManagerImpl::RequestCompleteGpuInfoIfNeeded() { |
| if (complete_gpu_info_already_requested_ || gpu_info_.finalized) |
| return; |
| complete_gpu_info_already_requested_ = true; |
| |
| GpuProcessHost::SendOnIO( |
| GpuProcessHost::GPU_PROCESS_KIND_UNSANDBOXED, |
| content::CAUSE_FOR_GPU_LAUNCH_GPUDATAMANAGER_REQUESTCOMPLETEGPUINFOIFNEEDED, |
| new GpuMsg_CollectGraphicsInfo()); |
| } |
| |
| bool GpuDataManagerImpl::IsCompleteGpuInfoAvailable() const { |
| return gpu_info_.finalized; |
| } |
| |
| void GpuDataManagerImpl::UpdateGpuInfo(const content::GPUInfo& gpu_info) { |
| if (gpu_info_.finalized) |
| return; |
| |
| content::GetContentClient()->SetGpuInfo(gpu_info); |
| |
| if (gpu_blacklist_.get()) { |
| GpuBlacklist::Decision decision = |
| gpu_blacklist_->MakeBlacklistDecision( |
| GpuBlacklist::kOsAny, NULL, gpu_info); |
| if (update_histograms_) { |
| gpu_util::UpdateStats(gpu_blacklist_.get(), |
| decision.blacklisted_features); |
| } |
| UpdateBlacklistedFeatures(decision.blacklisted_features); |
| if (decision.gpu_switching != content::GPU_SWITCHING_OPTION_UNKNOWN) |
| gpu_switching_ = decision.gpu_switching; |
| } |
| |
| { |
| base::AutoLock auto_lock(gpu_info_lock_); |
| gpu_info_ = gpu_info; |
| complete_gpu_info_already_requested_ = |
| complete_gpu_info_already_requested_ || gpu_info_.finalized; |
| } |
| |
| // We have to update GpuFeatureType before notify all the observers. |
| NotifyGpuInfoUpdate(); |
| } |
| |
| content::GPUInfo GpuDataManagerImpl::GetGPUInfo() const { |
| content::GPUInfo gpu_info; |
| { |
| base::AutoLock auto_lock(gpu_info_lock_); |
| gpu_info = gpu_info_; |
| } |
| return gpu_info; |
| } |
| |
| void GpuDataManagerImpl::RequestVideoMemoryUsageStatsUpdate() const { |
| GpuProcessHost::SendOnIO( |
| GpuProcessHost::GPU_PROCESS_KIND_SANDBOXED, |
| content::CAUSE_FOR_GPU_LAUNCH_NO_LAUNCH, |
| new GpuMsg_GetVideoMemoryUsageStats()); |
| } |
| |
| void GpuDataManagerImpl::AddLogMessage( |
| int level, const std::string& header, const std::string& message) { |
| base::AutoLock auto_lock(log_messages_lock_); |
| DictionaryValue* dict = new DictionaryValue(); |
| dict->SetInteger("level", level); |
| dict->SetString("header", header); |
| dict->SetString("message", message); |
| log_messages_.Append(dict); |
| } |
| |
| base::ListValue* GpuDataManagerImpl::GetLogMessages() const { |
| base::ListValue* value; |
| { |
| base::AutoLock auto_lock(log_messages_lock_); |
| value = log_messages_.DeepCopy(); |
| } |
| return value; |
| } |
| |
| std::string GpuDataManagerImpl::GetBlacklistVersion() const { |
| if (gpu_blacklist_.get()) |
| return gpu_blacklist_->GetVersion(); |
| return "0"; |
| } |
| |
| GpuFeatureType GpuDataManagerImpl::GetBlacklistedFeatures() const { |
| if (software_rendering_) { |
| GpuFeatureType flags; |
| |
| // Skia's software rendering is probably more efficient than going through |
| // software emulation of the GPU, so use that. |
| flags = content::GPU_FEATURE_TYPE_ACCELERATED_2D_CANVAS; |
| return flags; |
| } |
| |
| return gpu_feature_type_; |
| } |
| |
| GpuSwitchingOption GpuDataManagerImpl::GetGpuSwitchingOption() const { |
| return gpu_switching_; |
| } |
| |
| base::ListValue* GpuDataManagerImpl::GetBlacklistReasons() const { |
| ListValue* reasons = new ListValue(); |
| if (gpu_blacklist_.get()) |
| gpu_blacklist_->GetBlacklistReasons(reasons); |
| return reasons; |
| } |
| |
| bool GpuDataManagerImpl::GpuAccessAllowed() const { |
| if (software_rendering_) |
| return true; |
| |
| if (!gpu_info_.gpu_accessible) |
| return false; |
| |
| if (card_blacklisted_) |
| return false; |
| |
| // We only need to block GPU process if more features are disallowed other |
| // 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; |
| } |
| |
| void GpuDataManagerImpl::AddObserver(GpuDataManagerObserver* observer) { |
| observer_list_->AddObserver(observer); |
| } |
| |
| void GpuDataManagerImpl::RemoveObserver(GpuDataManagerObserver* observer) { |
| observer_list_->RemoveObserver(observer); |
| } |
| |
| void GpuDataManagerImpl::AppendRendererCommandLine( |
| CommandLine* command_line) const { |
| DCHECK(command_line); |
| |
| uint32 flags = GetBlacklistedFeatures(); |
| if ((flags & content::GPU_FEATURE_TYPE_WEBGL)) { |
| #if !defined(OS_ANDROID) |
| if (!command_line->HasSwitch(switches::kDisableExperimentalWebGL)) |
| command_line->AppendSwitch(switches::kDisableExperimentalWebGL); |
| #endif |
| if (!command_line->HasSwitch(switches::kDisablePepper3dForUntrustedUse)) |
| command_line->AppendSwitch(switches::kDisablePepper3dForUntrustedUse); |
| } |
| if ((flags & content::GPU_FEATURE_TYPE_MULTISAMPLING) && |
| !command_line->HasSwitch(switches::kDisableGLMultisampling)) |
| command_line->AppendSwitch(switches::kDisableGLMultisampling); |
| if ((flags & content::GPU_FEATURE_TYPE_ACCELERATED_COMPOSITING) && |
| !command_line->HasSwitch(switches::kDisableAcceleratedCompositing)) |
| command_line->AppendSwitch(switches::kDisableAcceleratedCompositing); |
| if ((flags & content::GPU_FEATURE_TYPE_ACCELERATED_2D_CANVAS) && |
| !command_line->HasSwitch(switches::kDisableAccelerated2dCanvas)) |
| command_line->AppendSwitch(switches::kDisableAccelerated2dCanvas); |
| if ((flags & content::GPU_FEATURE_TYPE_ACCELERATED_VIDEO_DECODE) && |
| !command_line->HasSwitch(switches::kDisableAcceleratedVideoDecode)) |
| command_line->AppendSwitch(switches::kDisableAcceleratedVideoDecode); |
| if (ShouldUseSoftwareRendering()) |
| command_line->AppendSwitch(switches::kDisableFlashFullscreen3d); |
| } |
| |
| void GpuDataManagerImpl::AppendGpuCommandLine( |
| CommandLine* command_line) const { |
| DCHECK(command_line); |
| |
| std::string use_gl = |
| CommandLine::ForCurrentProcess()->GetSwitchValueASCII(switches::kUseGL); |
| FilePath swiftshader_path = |
| CommandLine::ForCurrentProcess()->GetSwitchValuePath( |
| switches::kSwiftShaderPath); |
| uint32 flags = GetBlacklistedFeatures(); |
| if ((flags & content::GPU_FEATURE_TYPE_MULTISAMPLING) && |
| !command_line->HasSwitch(switches::kDisableGLMultisampling)) |
| command_line->AppendSwitch(switches::kDisableGLMultisampling); |
| if (flags & content::GPU_FEATURE_TYPE_TEXTURE_SHARING) |
| command_line->AppendSwitch(switches::kDisableImageTransportSurface); |
| |
| if (software_rendering_) { |
| command_line->AppendSwitchASCII(switches::kUseGL, "swiftshader"); |
| if (swiftshader_path.empty()) |
| swiftshader_path = swiftshader_path_; |
| } else if ((flags & (content::GPU_FEATURE_TYPE_WEBGL | |
| content::GPU_FEATURE_TYPE_ACCELERATED_COMPOSITING | |
| content::GPU_FEATURE_TYPE_ACCELERATED_2D_CANVAS)) && |
| (use_gl == "any")) { |
| command_line->AppendSwitchASCII( |
| switches::kUseGL, gfx::kGLImplementationOSMesaName); |
| } else if (!use_gl.empty()) { |
| command_line->AppendSwitchASCII(switches::kUseGL, use_gl); |
| } |
| switch (gpu_switching_) { |
| case content::GPU_SWITCHING_OPTION_FORCE_DISCRETE: |
| command_line->AppendSwitchASCII(switches::kGpuSwitching, |
| switches::kGpuSwitchingOptionNameForceDiscrete); |
| break; |
| case content::GPU_SWITCHING_OPTION_FORCE_INTEGRATED: |
| command_line->AppendSwitchASCII(switches::kGpuSwitching, |
| switches::kGpuSwitchingOptionNameForceIntegrated); |
| break; |
| default: |
| break; |
| } |
| |
| if (!swiftshader_path.empty()) |
| command_line->AppendSwitchPath(switches::kSwiftShaderPath, |
| swiftshader_path); |
| |
| { |
| base::AutoLock auto_lock(gpu_info_lock_); |
| if (gpu_info_.optimus) |
| command_line->AppendSwitch(switches::kReduceGpuSandbox); |
| if (gpu_info_.amd_switchable) { |
| // The image transport surface currently doesn't work with AMD Dynamic |
| // Switchable graphics. |
| command_line->AppendSwitch(switches::kReduceGpuSandbox); |
| command_line->AppendSwitch(switches::kDisableImageTransportSurface); |
| } |
| // Pass GPU and driver information to GPU process. We try to avoid full GPU |
| // info collection at GPU process startup, but we need gpu vendor_id, |
| // device_id, driver_vendor, driver_version for deciding whether we need to |
| // collect full info (on Linux) and for crash reporting purpose. |
| command_line->AppendSwitchASCII(switches::kGpuVendorID, |
| base::StringPrintf("0x%04x", gpu_info_.gpu.vendor_id)); |
| command_line->AppendSwitchASCII(switches::kGpuDeviceID, |
| base::StringPrintf("0x%04x", gpu_info_.gpu.device_id)); |
| command_line->AppendSwitchASCII(switches::kGpuDriverVendor, |
| gpu_info_.driver_vendor); |
| command_line->AppendSwitchASCII(switches::kGpuDriverVersion, |
| gpu_info_.driver_version); |
| } |
| } |
| |
| #if defined(OS_WIN) |
| bool GpuDataManagerImpl::IsUsingAcceleratedSurface() const { |
| if (base::win::GetVersion() < base::win::VERSION_VISTA) |
| return false; |
| |
| if (gpu_info_.amd_switchable) |
| return false; |
| if (software_rendering_) |
| return false; |
| CommandLine* command_line = CommandLine::ForCurrentProcess(); |
| if (command_line->HasSwitch(switches::kDisableImageTransportSurface)) |
| return false; |
| uint32 flags = GetBlacklistedFeatures(); |
| if (flags & content::GPU_FEATURE_TYPE_TEXTURE_SHARING) |
| return false; |
| |
| return true; |
| } |
| #endif |
| |
| void GpuDataManagerImpl::AppendPluginCommandLine( |
| CommandLine* command_line) const { |
| DCHECK(command_line); |
| |
| #if defined(OS_MACOSX) |
| uint32 flags = GetBlacklistedFeatures(); |
| // TODO(jbauman): Add proper blacklist support for core animation plugins so |
| // special-casing this video card won't be necessary. See |
| // http://crbug.com/134015 |
| if ((flags & content::GPU_FEATURE_TYPE_ACCELERATED_COMPOSITING) || |
| CommandLine::ForCurrentProcess()->HasSwitch( |
| switches::kDisableAcceleratedCompositing)) { |
| if (!command_line->HasSwitch( |
| switches::kDisableCoreAnimationPlugins)) |
| command_line->AppendSwitch( |
| switches::kDisableCoreAnimationPlugins); |
| } |
| #endif |
| } |
| |
| void GpuDataManagerImpl::UpdatePreliminaryBlacklistedFeatures() { |
| preliminary_gpu_feature_type_ = gpu_feature_type_; |
| } |
| |
| void GpuDataManagerImpl::NotifyGpuInfoUpdate() { |
| observer_list_->Notify(&GpuDataManagerObserver::OnGpuInfoUpdate); |
| } |
| |
| void GpuDataManagerImpl::UpdateVideoMemoryUsageStats( |
| const content::GPUVideoMemoryUsageStats& video_memory_usage_stats) { |
| observer_list_->Notify(&GpuDataManagerObserver::OnVideoMemoryUsageStatsUpdate, |
| video_memory_usage_stats); |
| } |
| |
| // Experiment to determine whether Stage3D should be blacklisted on XP. |
| bool Stage3DBlacklisted() { |
| return base::FieldTrialList::FindFullName(content::kStage3DFieldTrialName) == |
| content::kStage3DFieldTrialBlacklistedName; |
| } |
| |
| void GpuDataManagerImpl::UpdateBlacklistedFeatures( |
| GpuFeatureType features) { |
| CommandLine* command_line = CommandLine::ForCurrentProcess(); |
| int flags = features; |
| |
| // Force disable using the GPU for these features, even if they would |
| // otherwise be allowed. |
| if (card_blacklisted_ || |
| command_line->HasSwitch(switches::kBlacklistAcceleratedCompositing)) { |
| flags |= content::GPU_FEATURE_TYPE_ACCELERATED_COMPOSITING; |
| } |
| if (card_blacklisted_ || |
| command_line->HasSwitch(switches::kBlacklistWebGL)) { |
| flags |= content::GPU_FEATURE_TYPE_WEBGL; |
| } |
| if (Stage3DBlacklisted()) { |
| flags |= content::GPU_FEATURE_TYPE_FLASH_STAGE3D; |
| } |
| gpu_feature_type_ = static_cast<GpuFeatureType>(flags); |
| |
| EnableSoftwareRenderingIfNecessary(); |
| } |
| |
| void GpuDataManagerImpl::RegisterSwiftShaderPath(const FilePath& path) { |
| swiftshader_path_ = path; |
| EnableSoftwareRenderingIfNecessary(); |
| } |
| |
| void GpuDataManagerImpl::EnableSoftwareRenderingIfNecessary() { |
| if (!GpuAccessAllowed() || |
| (gpu_feature_type_ & content::GPU_FEATURE_TYPE_WEBGL)) { |
| if (!swiftshader_path_.empty() && |
| !CommandLine::ForCurrentProcess()->HasSwitch( |
| switches::kDisableSoftwareRasterizer)) |
| software_rendering_ = true; |
| } |
| } |
| |
| bool GpuDataManagerImpl::ShouldUseSoftwareRendering() const { |
| return software_rendering_; |
| } |
| |
| void GpuDataManagerImpl::BlacklistCard() { |
| card_blacklisted_ = true; |
| |
| gpu_feature_type_ = content::GPU_FEATURE_TYPE_ALL; |
| |
| EnableSoftwareRenderingIfNecessary(); |
| NotifyGpuInfoUpdate(); |
| } |