blob: ba9e8262aad8a81763ddf43d45b6f9f9e616bc92 [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 "gpu/config/gpu_info_collector.h"
#include <stddef.h>
#include <stdint.h>
#include <string>
#include <vector>
#include "base/command_line.h"
#include "base/logging.h"
#include "base/metrics/histogram_functions.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_piece.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/trace_event/trace_event.h"
#include "gpu/config/gpu_switches.h"
#include "third_party/angle/src/gpu_info_util/SystemInfo.h" // nogncheck
#include "ui/gl/gl_bindings.h"
#include "ui/gl/gl_context.h"
#include "ui/gl/gl_implementation.h"
#include "ui/gl/gl_surface.h"
#include "ui/gl/gl_version_info.h"
#include "ui/gl/init/gl_factory.h"
#if defined(OS_ANDROID)
#include "ui/gl/gl_surface_egl.h"
#endif // OS_ANDROID
#if defined(USE_X11)
#include "ui/gl/gl_visual_picker_glx.h"
#endif
namespace {
scoped_refptr<gl::GLSurface> InitializeGLSurface() {
scoped_refptr<gl::GLSurface> surface(
gl::init::CreateOffscreenGLSurface(gfx::Size()));
if (!surface.get()) {
LOG(ERROR) << "gl::GLContext::CreateOffscreenGLSurface failed";
return NULL;
}
return surface;
}
scoped_refptr<gl::GLContext> InitializeGLContext(gl::GLSurface* surface) {
gl::GLContextAttribs attribs;
attribs.client_major_es_version = 2;
scoped_refptr<gl::GLContext> context(
gl::init::CreateGLContext(nullptr, surface, attribs));
if (!context.get()) {
LOG(ERROR) << "gl::init::CreateGLContext failed";
return NULL;
}
if (!context->MakeCurrent(surface)) {
LOG(ERROR) << "gl::GLContext::MakeCurrent() failed";
return NULL;
}
return context;
}
std::string GetGLString(unsigned int pname) {
const char* gl_string =
reinterpret_cast<const char*>(glGetString(pname));
if (gl_string)
return std::string(gl_string);
return std::string();
}
// Return a version string in the format of "major.minor".
std::string GetVersionFromString(const std::string& version_string) {
size_t begin = version_string.find_first_of("0123456789");
if (begin != std::string::npos) {
size_t end = version_string.find_first_not_of("01234567890.", begin);
std::string sub_string;
if (end != std::string::npos)
sub_string = version_string.substr(begin, end - begin);
else
sub_string = version_string.substr(begin);
std::vector<std::string> pieces = base::SplitString(
sub_string, ".", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
if (pieces.size() >= 2)
return pieces[0] + "." + pieces[1];
}
return std::string();
}
// Return the array index of the found name, or return -1.
int StringContainsName(
const std::string& str, const std::string* names, size_t num_names) {
std::vector<std::string> tokens = base::SplitString(
str, " .,()-_", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
for (size_t ii = 0; ii < tokens.size(); ++ii) {
for (size_t name_index = 0; name_index < num_names; ++name_index) {
if (tokens[ii] == names[name_index]) {
return base::checked_cast<int>(name_index);
}
}
}
return -1;
}
} // namespace
namespace gpu {
CollectInfoResult CollectGraphicsInfoGL(GPUInfo* gpu_info) {
TRACE_EVENT0("startup", "gpu_info_collector::CollectGraphicsInfoGL");
DCHECK_NE(gl::GetGLImplementation(), gl::kGLImplementationNone);
scoped_refptr<gl::GLSurface> surface(InitializeGLSurface());
if (!surface.get()) {
LOG(ERROR) << "Could not create surface for info collection.";
return kCollectInfoFatalFailure;
}
scoped_refptr<gl::GLContext> context(InitializeGLContext(surface.get()));
if (!context.get()) {
LOG(ERROR) << "Could not create context for info collection.";
return kCollectInfoFatalFailure;
}
gpu_info->gl_renderer = GetGLString(GL_RENDERER);
gpu_info->gl_vendor = GetGLString(GL_VENDOR);
gpu_info->gl_version = GetGLString(GL_VERSION);
std::string glsl_version_string = GetGLString(GL_SHADING_LANGUAGE_VERSION);
gpu_info->gl_extensions = gl::GetGLExtensionsFromCurrentContext();
gl::ExtensionSet extension_set =
gl::MakeExtensionSet(gpu_info->gl_extensions);
gl::GLVersionInfo gl_info(gpu_info->gl_version.c_str(),
gpu_info->gl_renderer.c_str(), extension_set);
GLint max_samples = 0;
if (gl_info.IsAtLeastGL(3, 0) || gl_info.IsAtLeastGLES(3, 0) ||
gl::HasExtension(extension_set, "GL_ANGLE_framebuffer_multisample") ||
gl::HasExtension(extension_set, "GL_APPLE_framebuffer_multisample") ||
gl::HasExtension(extension_set, "GL_EXT_framebuffer_multisample") ||
gl::HasExtension(extension_set,
"GL_EXT_multisampled_render_to_texture") ||
gl::HasExtension(extension_set, "GL_NV_framebuffer_multisample")) {
glGetIntegerv(GL_MAX_SAMPLES, &max_samples);
}
gpu_info->max_msaa_samples = base::IntToString(max_samples);
base::UmaHistogramSparse("GPU.MaxMSAASampleCount", max_samples);
#if defined(OS_ANDROID)
gpu_info->can_support_threaded_texture_mailbox =
gl::GLSurfaceEGL::HasEGLExtension("EGL_KHR_fence_sync") &&
gl::GLSurfaceEGL::HasEGLExtension("EGL_KHR_image_base") &&
gl::GLSurfaceEGL::HasEGLExtension("EGL_KHR_gl_texture_2D_image") &&
gl::HasExtension(extension_set, "GL_OES_EGL_image");
#else
gl::GLWindowSystemBindingInfo window_system_binding_info;
if (gl::init::GetGLWindowSystemBindingInfo(&window_system_binding_info)) {
gpu_info->gl_ws_vendor = window_system_binding_info.vendor;
gpu_info->gl_ws_version = window_system_binding_info.version;
gpu_info->gl_ws_extensions = window_system_binding_info.extensions;
gpu_info->direct_rendering = window_system_binding_info.direct_rendering;
}
#endif // OS_ANDROID
bool supports_robustness =
gl::HasExtension(extension_set, "GL_EXT_robustness") ||
gl::HasExtension(extension_set, "GL_KHR_robustness") ||
gl::HasExtension(extension_set, "GL_ARB_robustness");
if (supports_robustness) {
glGetIntegerv(GL_RESET_NOTIFICATION_STRATEGY_ARB,
reinterpret_cast<GLint*>(&gpu_info->gl_reset_notification_strategy));
}
#if defined(USE_X11)
if (gl::GetGLImplementation() == gl::kGLImplementationDesktopGL) {
gl::GLVisualPickerGLX* visual_picker = gl::GLVisualPickerGLX::GetInstance();
gpu_info->system_visual = visual_picker->system_visual().visualid;
gpu_info->rgba_visual = visual_picker->rgba_visual().visualid;
}
#endif
// TODO(kbr): remove once the destruction of a current context automatically
// clears the current context.
context->ReleaseCurrent(surface.get());
std::string glsl_version = GetVersionFromString(glsl_version_string);
gpu_info->pixel_shader_version = glsl_version;
gpu_info->vertex_shader_version = glsl_version;
IdentifyActiveGPU(gpu_info);
return CollectDriverInfoGL(gpu_info);
}
void MergeGPUInfoGL(GPUInfo* basic_gpu_info,
const GPUInfo& context_gpu_info) {
DCHECK(basic_gpu_info);
// Copy over GPUs because which one is active could change.
basic_gpu_info->gpu = context_gpu_info.gpu;
basic_gpu_info->secondary_gpus = context_gpu_info.secondary_gpus;
basic_gpu_info->gl_renderer = context_gpu_info.gl_renderer;
basic_gpu_info->gl_vendor = context_gpu_info.gl_vendor;
basic_gpu_info->gl_version = context_gpu_info.gl_version;
basic_gpu_info->gl_extensions = context_gpu_info.gl_extensions;
basic_gpu_info->pixel_shader_version =
context_gpu_info.pixel_shader_version;
basic_gpu_info->vertex_shader_version =
context_gpu_info.vertex_shader_version;
basic_gpu_info->max_msaa_samples =
context_gpu_info.max_msaa_samples;
basic_gpu_info->gl_ws_vendor = context_gpu_info.gl_ws_vendor;
basic_gpu_info->gl_ws_version = context_gpu_info.gl_ws_version;
basic_gpu_info->gl_ws_extensions = context_gpu_info.gl_ws_extensions;
basic_gpu_info->gl_reset_notification_strategy =
context_gpu_info.gl_reset_notification_strategy;
basic_gpu_info->software_rendering = context_gpu_info.software_rendering;
if (!context_gpu_info.driver_vendor.empty())
basic_gpu_info->driver_vendor = context_gpu_info.driver_vendor;
if (!context_gpu_info.driver_version.empty())
basic_gpu_info->driver_version = context_gpu_info.driver_version;
basic_gpu_info->sandboxed = context_gpu_info.sandboxed;
basic_gpu_info->direct_rendering = context_gpu_info.direct_rendering;
basic_gpu_info->in_process_gpu = context_gpu_info.in_process_gpu;
basic_gpu_info->passthrough_cmd_decoder =
context_gpu_info.passthrough_cmd_decoder;
basic_gpu_info->direct_composition = context_gpu_info.direct_composition;
basic_gpu_info->supports_overlays = context_gpu_info.supports_overlays;
basic_gpu_info->context_info_state = context_gpu_info.context_info_state;
basic_gpu_info->initialization_time = context_gpu_info.initialization_time;
basic_gpu_info->video_decode_accelerator_capabilities =
context_gpu_info.video_decode_accelerator_capabilities;
basic_gpu_info->video_encode_accelerator_supported_profiles =
context_gpu_info.video_encode_accelerator_supported_profiles;
basic_gpu_info->jpeg_decode_accelerator_supported =
context_gpu_info.jpeg_decode_accelerator_supported;
#if defined(USE_X11)
basic_gpu_info->system_visual = context_gpu_info.system_visual;
basic_gpu_info->rgba_visual = context_gpu_info.rgba_visual;
#endif
}
void IdentifyActiveGPU(GPUInfo* gpu_info) {
const std::string kNVidiaName = "nvidia";
const std::string kNouveauName = "nouveau";
const std::string kIntelName = "intel";
const std::string kAMDName = "amd";
const std::string kATIName = "ati";
const std::string kVendorNames[] = {kNVidiaName, kNouveauName, kIntelName,
kAMDName, kATIName};
const uint32_t kNVidiaID = 0x10de;
const uint32_t kIntelID = 0x8086;
const uint32_t kAMDID = 0x1002;
const uint32_t kATIID = 0x1002;
const uint32_t kVendorIDs[] = {kNVidiaID, kNVidiaID, kIntelID, kAMDID,
kATIID};
DCHECK(gpu_info);
if (gpu_info->secondary_gpus.size() == 0) {
// If there is only a single GPU, that GPU is active.
gpu_info->gpu.active = true;
return;
}
uint32_t active_vendor_id = 0;
if (!gpu_info->gl_vendor.empty()) {
std::string gl_vendor_lower = base::ToLowerASCII(gpu_info->gl_vendor);
int index = StringContainsName(
gl_vendor_lower, kVendorNames, arraysize(kVendorNames));
if (index >= 0) {
active_vendor_id = kVendorIDs[index];
}
}
if (active_vendor_id == 0 && !gpu_info->gl_renderer.empty()) {
std::string gl_renderer_lower = base::ToLowerASCII(gpu_info->gl_renderer);
int index = StringContainsName(
gl_renderer_lower, kVendorNames, arraysize(kVendorNames));
if (index >= 0) {
active_vendor_id = kVendorIDs[index];
}
}
if (active_vendor_id == 0) {
// We fail to identify the GPU vendor through GL_VENDOR/GL_RENDERER.
return;
}
gpu_info->gpu.active = false;
for (size_t ii = 0; ii < gpu_info->secondary_gpus.size(); ++ii)
gpu_info->secondary_gpus[ii].active = false;
// TODO(zmo): if two GPUs are from the same vendor, this code will always
// set the first GPU as active, which could be wrong.
if (active_vendor_id == gpu_info->gpu.vendor_id) {
gpu_info->gpu.active = true;
return;
}
for (size_t ii = 0; ii < gpu_info->secondary_gpus.size(); ++ii) {
if (active_vendor_id == gpu_info->secondary_gpus[ii].vendor_id) {
gpu_info->secondary_gpus[ii].active = true;
return;
}
}
}
void FillGPUInfoFromSystemInfo(GPUInfo* gpu_info,
angle::SystemInfo* system_info) {
// We fill gpu_info even when angle::GetSystemInfo failed so that we can see
// partial information even when GPU info collection fails. Handle malformed
// angle::SystemInfo first.
if (system_info->gpus.empty()) {
return;
}
if (system_info->primaryGPUIndex < 0) {
system_info->primaryGPUIndex = 0;
}
angle::GPUDeviceInfo* primary =
&system_info->gpus[system_info->primaryGPUIndex];
gpu_info->gpu.vendor_id = primary->vendorId;
gpu_info->gpu.device_id = primary->deviceId;
if (system_info->primaryGPUIndex == system_info->activeGPUIndex) {
gpu_info->gpu.active = true;
}
gpu_info->driver_vendor = std::move(primary->driverVendor);
gpu_info->driver_version = std::move(primary->driverVersion);
gpu_info->driver_date = std::move(primary->driverDate);
for (size_t i = 0; i < system_info->gpus.size(); i++) {
if (static_cast<int>(i) == system_info->primaryGPUIndex) {
continue;
}
GPUInfo::GPUDevice device;
device.vendor_id = system_info->gpus[i].vendorId;
device.device_id = system_info->gpus[i].deviceId;
if (static_cast<int>(i) == system_info->activeGPUIndex) {
device.active = true;
}
gpu_info->secondary_gpus.push_back(device);
}
gpu_info->optimus = system_info->isOptimus;
gpu_info->amd_switchable = system_info->isAMDSwitchable;
gpu_info->machine_model_name = system_info->machineModelName;
gpu_info->machine_model_version = system_info->machineModelVersion;
}
} // namespace gpu