blob: faeb664b63c9985e7e60b39b9c0266fafa7dfe05 [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 "base/android/build_info.h"
#include "base/android/jni_android.h"
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/native_library.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/strings/stringprintf.h"
#include "gpu/config/gpu_switches.h"
#include "ui/gl/egl_util.h"
#include "ui/gl/gl_bindings.h"
#include "ui/gl/gl_context.h"
#include "ui/gl/gl_surface.h"
namespace {
std::pair<std::string, size_t> GetVersionFromString(
const std::string& version_string,
size_t begin = 0) {
begin = version_string.find_first_of("0123456789", begin);
if (begin == std::string::npos)
return std::make_pair("", 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 std::make_pair(pieces[0] + "." + pieces[1], end);
else
return std::make_pair("", end);
}
std::string GetDriverVersionFromString(const std::string& version_string) {
// We expect that android GL_VERSION strings will be of a form
// similar to: "OpenGL ES 2.0 V@6.0 AU@ (CL@2946718)" where the
// first match to [0-9][0-9.]* is the OpenGL ES version number, and
// the second match to [0-9][0-9.]* is the driver version (in this
// case, 6.0).
// It is currently assumed that the driver version has at least one
// period in it, and only the first two components are significant.
size_t begin = GetVersionFromString(version_string).second;
if (begin == std::string::npos)
return "0";
std::pair<std::string, size_t> driver_version =
GetVersionFromString(version_string, begin);
if (driver_version.first == "")
return "0";
return driver_version.first;
}
gpu::CollectInfoResult CollectDriverInfo(gpu::GPUInfo* gpu_info) {
// Go through the process of loading GL libs and initializing an EGL
// context so that we can get GL vendor/version/renderer strings.
base::NativeLibrary gles_library, egl_library;
base::NativeLibraryLoadError error;
gles_library =
base::LoadNativeLibrary(base::FilePath("libGLESv2.so"), &error);
if (!gles_library)
LOG(FATAL) << "Failed to load libGLESv2.so";
egl_library = base::LoadNativeLibrary(base::FilePath("libEGL.so"), &error);
if (!egl_library)
LOG(FATAL) << "Failed to load libEGL.so";
typedef void* (*eglGetProcAddressProc)(const char* name);
auto eglGetProcAddressFn = reinterpret_cast<eglGetProcAddressProc>(
base::GetFunctionPointerFromNativeLibrary(egl_library,
"eglGetProcAddress"));
if (!eglGetProcAddressFn)
LOG(FATAL) << "eglGetProcAddress not found.";
auto get_func = [eglGetProcAddressFn, gles_library, egl_library](
const char* name) {
void *proc;
proc = base::GetFunctionPointerFromNativeLibrary(egl_library, name);
if (proc)
return proc;
proc = base::GetFunctionPointerFromNativeLibrary(gles_library, name);
if (proc)
return proc;
proc = eglGetProcAddressFn(name);
if (proc)
return proc;
LOG(FATAL) << "Failed to look up " << name;
return (void *)nullptr;
};
#define LOOKUP_FUNC(x) auto x##Fn = reinterpret_cast<gl::x##Proc>(get_func(#x))
LOOKUP_FUNC(eglGetError);
LOOKUP_FUNC(eglQueryString);
LOOKUP_FUNC(eglGetCurrentContext);
LOOKUP_FUNC(eglGetCurrentDisplay);
LOOKUP_FUNC(eglGetCurrentSurface);
LOOKUP_FUNC(eglGetDisplay);
LOOKUP_FUNC(eglInitialize);
LOOKUP_FUNC(eglChooseConfig);
LOOKUP_FUNC(eglCreateContext);
LOOKUP_FUNC(eglCreatePbufferSurface);
LOOKUP_FUNC(eglMakeCurrent);
LOOKUP_FUNC(eglDestroySurface);
LOOKUP_FUNC(eglDestroyContext);
LOOKUP_FUNC(glGetString);
LOOKUP_FUNC(glGetIntegerv);
#undef LOOKUP_FUNC
EGLDisplay curr_display = eglGetCurrentDisplayFn();
EGLContext curr_context = eglGetCurrentContextFn();
EGLSurface curr_draw_surface = eglGetCurrentSurfaceFn(EGL_DRAW);
EGLSurface curr_read_surface = eglGetCurrentSurfaceFn(EGL_READ);
EGLDisplay temp_display = EGL_NO_DISPLAY;
EGLContext temp_context = EGL_NO_CONTEXT;
EGLSurface temp_surface = EGL_NO_SURFACE;
const EGLint kConfigAttribs[] = {
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
EGL_NONE};
const EGLint kContextAttribs[] = {
EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_EXT,
EGL_LOSE_CONTEXT_ON_RESET_EXT,
EGL_CONTEXT_CLIENT_VERSION, 2,
EGL_NONE};
const EGLint kSurfaceAttribs[] = {
EGL_WIDTH, 1,
EGL_HEIGHT, 1,
EGL_NONE};
EGLint major, minor;
EGLConfig config;
EGLint num_configs;
auto errorstr = [eglGetErrorFn]() {
uint32_t err = eglGetErrorFn();
return base::StringPrintf("%s (%x)", ui::GetEGLErrorString(err), err);
};
temp_display = eglGetDisplayFn(EGL_DEFAULT_DISPLAY);
if (temp_display == EGL_NO_DISPLAY) {
LOG(FATAL) << "failed to get display. " << errorstr();
}
eglInitializeFn(temp_display, &major, &minor);
bool egl_create_context_robustness_supported =
strstr(reinterpret_cast<const char*>(
eglQueryStringFn(temp_display, EGL_EXTENSIONS)),
"EGL_EXT_create_context_robustness") != NULL;
if (!eglChooseConfigFn(temp_display, kConfigAttribs, &config, 1,
&num_configs)) {
LOG(FATAL) << "failed to choose an egl config. " << errorstr();
}
temp_context = eglCreateContextFn(
temp_display, config, EGL_NO_CONTEXT,
kContextAttribs + (egl_create_context_robustness_supported ? 0 : 2));
if (temp_context == EGL_NO_CONTEXT) {
LOG(FATAL)
<< "failed to create a temporary context for fetching driver strings. "
<< errorstr();
}
temp_surface =
eglCreatePbufferSurfaceFn(temp_display, config, kSurfaceAttribs);
if (temp_surface == EGL_NO_SURFACE) {
eglDestroyContextFn(temp_display, temp_context);
LOG(FATAL)
<< "failed to create a pbuffer surface for fetching driver strings. "
<< errorstr();
}
eglMakeCurrentFn(temp_display, temp_surface, temp_surface, temp_context);
gpu_info->gl_vendor = reinterpret_cast<const char*>(glGetStringFn(GL_VENDOR));
gpu_info->gl_version =
reinterpret_cast<const char*>(glGetStringFn(GL_VERSION));
gpu_info->gl_renderer =
reinterpret_cast<const char*>(glGetStringFn(GL_RENDERER));
gpu_info->gl_extensions =
reinterpret_cast<const char*>(glGetStringFn(GL_EXTENSIONS));
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
if (command_line->HasSwitch(switches::kGpuTestingGLVendor)) {
gpu_info->gl_vendor =
command_line->GetSwitchValueASCII(switches::kGpuTestingGLVendor);
}
if (command_line->HasSwitch(switches::kGpuTestingGLRenderer)) {
gpu_info->gl_renderer =
command_line->GetSwitchValueASCII(switches::kGpuTestingGLRenderer);
}
if (command_line->HasSwitch(switches::kGpuTestingGLVersion)) {
gpu_info->gl_version =
command_line->GetSwitchValueASCII(switches::kGpuTestingGLVersion);
}
GLint max_samples = 0;
glGetIntegervFn(GL_MAX_SAMPLES, &max_samples);
gpu_info->max_msaa_samples = base::IntToString(max_samples);
bool supports_robustness =
gpu_info->gl_extensions.find("GL_EXT_robustness") != std::string::npos ||
gpu_info->gl_extensions.find("GL_KHR_robustness") != std::string::npos ||
gpu_info->gl_extensions.find("GL_ARB_robustness") != std::string::npos;
if (supports_robustness) {
glGetIntegervFn(
GL_RESET_NOTIFICATION_STRATEGY_ARB,
reinterpret_cast<GLint*>(&gpu_info->gl_reset_notification_strategy));
}
std::string glsl_version_string;
if (const char* glsl_version_cstring = reinterpret_cast<const char*>(
glGetStringFn(GL_SHADING_LANGUAGE_VERSION)))
glsl_version_string = glsl_version_cstring;
std::string glsl_version = GetVersionFromString(glsl_version_string).first;
gpu_info->pixel_shader_version = glsl_version;
gpu_info->vertex_shader_version = glsl_version;
if (curr_display != EGL_NO_DISPLAY &&
curr_context != EGL_NO_CONTEXT) {
eglMakeCurrentFn(curr_display, curr_draw_surface, curr_read_surface,
curr_context);
} else {
eglMakeCurrentFn(temp_display, EGL_NO_SURFACE, EGL_NO_SURFACE,
EGL_NO_CONTEXT);
}
eglDestroySurfaceFn(temp_display, temp_surface);
eglDestroyContextFn(temp_display, temp_context);
return gpu::kCollectInfoSuccess;
}
}
namespace gpu {
CollectInfoResult CollectContextGraphicsInfo(GPUInfo* gpu_info) {
/// TODO(tobiasjs) Check if CollectGraphicsInfo in gpu_main.cc
/// really only needs basic graphics info on all platforms, and if
/// so switch it to using that and make this the NOP that it really
/// should be, to avoid potential double collection of info.
return CollectBasicGraphicsInfo(gpu_info);
}
CollectInfoResult CollectGpuID(uint32_t* vendor_id, uint32_t* device_id) {
DCHECK(vendor_id && device_id);
*vendor_id = 0;
*device_id = 0;
return kCollectInfoNonFatalFailure;
}
CollectInfoResult CollectBasicGraphicsInfo(GPUInfo* gpu_info) {
// When command buffer is compiled as a standalone library, the process might
// not have a Java environment.
if (base::android::IsVMInitialized()) {
gpu_info->machine_model_name =
base::android::BuildInfo::GetInstance()->model();
}
// Create a short-lived context on the UI thread to collect the GL strings.
// Make sure we restore the existing context if there is one.
CollectInfoResult result = CollectDriverInfo(gpu_info);
if (result == kCollectInfoSuccess)
result = CollectDriverInfoGL(gpu_info);
gpu_info->basic_info_state = result;
gpu_info->context_info_state = result;
return result;
}
CollectInfoResult CollectDriverInfoGL(GPUInfo* gpu_info) {
gpu_info->driver_version = GetDriverVersionFromString(
gpu_info->gl_version);
gpu_info->gpu.vendor_string = gpu_info->gl_vendor;
gpu_info->gpu.device_string = gpu_info->gl_renderer;
return kCollectInfoSuccess;
}
void MergeGPUInfo(GPUInfo* basic_gpu_info,
const GPUInfo& context_gpu_info) {
MergeGPUInfoGL(basic_gpu_info, context_gpu_info);
}
} // namespace gpu