blob: 4f705b185550b0785d408e36f2ea8172a450a1da [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/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/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/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_switches.h"
#include "ui/gfx/gl/gl_implementation.h"
#include "ui/gfx/gl/gl_switches.h"
#include "webkit/plugins/plugin_switches.h"
using content::BrowserThread;
using content::GpuDataManagerObserver;
using content::GpuFeatureType;
// 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),
complete_gpu_info_available_(false),
gpu_feature_type_(content::GPU_FEATURE_TYPE_UNKNOWN),
preliminary_gpu_feature_type_(content::GPU_FEATURE_TYPE_UNKNOWN),
observer_list_(new GpuDataManagerObserverList),
software_rendering_(false),
card_blacklisted_(false) {
Initialize();
}
void GpuDataManagerImpl::Initialize() {
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::kSkipGpuDataLoading)) {
content::GPUInfo gpu_info;
gpu_info_collector::CollectPreliminaryGraphicsInfo(&gpu_info);
{
base::AutoLock auto_lock(gpu_info_lock_);
gpu_info_ = gpu_info;
}
}
}
GpuDataManagerImpl::~GpuDataManagerImpl() {
}
void GpuDataManagerImpl::RequestCompleteGpuInfoIfNeeded() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
if (complete_gpu_info_already_requested_ || complete_gpu_info_available_)
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 complete_gpu_info_available_;
}
void GpuDataManagerImpl::UpdateGpuInfo(const content::GPUInfo& gpu_info) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
complete_gpu_info_available_ =
complete_gpu_info_available_ || gpu_info.finalized;
complete_gpu_info_already_requested_ =
complete_gpu_info_already_requested_ || gpu_info.finalized;
{
base::AutoLock auto_lock(gpu_info_lock_);
if (!Merge(&gpu_info_, gpu_info))
return;
content::GetContentClient()->SetGpuInfo(gpu_info_);
}
// We have to update GpuFeatureType before notify all the observers.
NotifyGpuInfoUpdate();
}
content::GPUInfo GpuDataManagerImpl::GetGPUInfo() const {
return gpu_info_;
}
void GpuDataManagerImpl::AddLogMessage(Value* msg) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
log_messages_.Append(msg);
}
GpuFeatureType GpuDataManagerImpl::GetGpuFeatureType() {
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_;
}
bool GpuDataManagerImpl::GpuAccessAllowed() {
if (software_rendering_)
return true;
if (!gpu_info_.gpu_accessible)
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) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DCHECK(command_line);
uint32 flags = GetGpuFeatureType();
if ((flags & content::GPU_FEATURE_TYPE_WEBGL)) {
if (!command_line->HasSwitch(switches::kDisableExperimentalWebGL))
command_line->AppendSwitch(switches::kDisableExperimentalWebGL);
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);
}
void GpuDataManagerImpl::AppendGpuCommandLine(
CommandLine* command_line) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
DCHECK(command_line);
std::string use_gl =
CommandLine::ForCurrentProcess()->GetSwitchValueASCII(switches::kUseGL);
FilePath swiftshader_path =
CommandLine::ForCurrentProcess()->GetSwitchValuePath(
switches::kSwiftShaderPath);
uint32 flags = GetGpuFeatureType();
if ((flags & content::GPU_FEATURE_TYPE_MULTISAMPLING) &&
!command_line->HasSwitch(switches::kDisableGLMultisampling))
command_line->AppendSwitch(switches::kDisableGLMultisampling);
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);
}
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);
}
}
}
void GpuDataManagerImpl::SetGpuFeatureType(GpuFeatureType feature_type) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
UpdateGpuFeatureType(feature_type);
preliminary_gpu_feature_type_ = gpu_feature_type_;
}
void GpuDataManagerImpl::NotifyGpuInfoUpdate() {
observer_list_->Notify(&GpuDataManagerObserver::OnGpuInfoUpdate);
}
void GpuDataManagerImpl::UpdateGpuFeatureType(
GpuFeatureType embedder_feature_type) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
CommandLine* command_line = CommandLine::ForCurrentProcess();
int flags = embedder_feature_type;
// 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;
}
gpu_feature_type_ = static_cast<GpuFeatureType>(flags);
EnableSoftwareRenderingIfNecessary();
}
void GpuDataManagerImpl::RegisterSwiftShaderPath(const FilePath& path) {
swiftshader_path_ = path;
EnableSoftwareRenderingIfNecessary();
}
const base::ListValue& GpuDataManagerImpl::GetLogMessages() const {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
return log_messages_;
}
void GpuDataManagerImpl::EnableSoftwareRenderingIfNecessary() {
if (!GpuAccessAllowed() ||
(gpu_feature_type_ & content::GPU_FEATURE_TYPE_WEBGL)) {
#if defined(ENABLE_SWIFTSHADER)
if (!swiftshader_path_.empty() &&
!CommandLine::ForCurrentProcess()->HasSwitch(
switches::kDisableSoftwareRasterizer))
software_rendering_ = true;
#endif
}
}
bool GpuDataManagerImpl::ShouldUseSoftwareRendering() {
return software_rendering_;
}
void GpuDataManagerImpl::BlacklistCard() {
card_blacklisted_ = true;
{
base::AutoLock auto_lock(gpu_info_lock_);
int flags = gpu_feature_type_;
flags |= content::GPU_FEATURE_TYPE_ACCELERATED_COMPOSITING |
content::GPU_FEATURE_TYPE_WEBGL;
gpu_feature_type_ = static_cast<GpuFeatureType>(flags);
}
EnableSoftwareRenderingIfNecessary();
NotifyGpuInfoUpdate();
}
bool GpuDataManagerImpl::Merge(content::GPUInfo* object,
const content::GPUInfo& other) {
if (object->device_id != other.device_id ||
object->vendor_id != other.vendor_id) {
*object = other;
return true;
}
bool changed = false;
if (!object->finalized) {
object->finalized = other.finalized;
object->initialization_time = other.initialization_time;
object->optimus |= other.optimus;
object->amd_switchable |= other.amd_switchable;
if (object->driver_vendor.empty()) {
changed |= object->driver_vendor != other.driver_vendor;
object->driver_vendor = other.driver_vendor;
}
if (object->driver_version.empty()) {
changed |= object->driver_version != other.driver_version;
object->driver_version = other.driver_version;
}
if (object->driver_date.empty()) {
changed |= object->driver_date != other.driver_date;
object->driver_date = other.driver_date;
}
if (object->pixel_shader_version.empty()) {
changed |= object->pixel_shader_version != other.pixel_shader_version;
object->pixel_shader_version = other.pixel_shader_version;
}
if (object->vertex_shader_version.empty()) {
changed |= object->vertex_shader_version != other.vertex_shader_version;
object->vertex_shader_version = other.vertex_shader_version;
}
if (object->gl_version.empty()) {
changed |= object->gl_version != other.gl_version;
object->gl_version = other.gl_version;
}
if (object->gl_version_string.empty()) {
changed |= object->gl_version_string != other.gl_version_string;
object->gl_version_string = other.gl_version_string;
}
if (object->gl_vendor.empty()) {
changed |= object->gl_vendor != other.gl_vendor;
object->gl_vendor = other.gl_vendor;
}
if (object->gl_renderer.empty()) {
changed |= object->gl_renderer != other.gl_renderer;
object->gl_renderer = other.gl_renderer;
}
if (object->gl_extensions.empty()) {
changed |= object->gl_extensions != other.gl_extensions;
object->gl_extensions = other.gl_extensions;
}
object->can_lose_context = other.can_lose_context;
object->software_rendering = other.software_rendering;
object->gpu_accessible = other.gpu_accessible;
#if defined(OS_WIN)
if (object->dx_diagnostics.values.size() == 0 &&
object->dx_diagnostics.children.size() == 0) {
object->dx_diagnostics = other.dx_diagnostics;
changed = true;
}
#endif
}
return changed;
}