1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "gpu/config/gpu_info_collector.h"
7 #include "base/android/build_info.h"
8 #include "base/command_line.h"
9 #include "base/files/file_path.h"
10 #include "base/logging.h"
11 #include "base/native_library.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/strings/string_piece.h"
14 #include "base/strings/string_split.h"
15 #include "base/strings/string_util.h"
16 #include "base/strings/stringprintf.h"
17 #include "ui/gl/egl_util.h"
18 #include "ui/gl/gl_bindings.h"
19 #include "ui/gl/gl_context.h"
20 #include "ui/gl/gl_surface.h"
24 std::pair
<std::string
, size_t> GetVersionFromString(
25 const std::string
& version_string
,
27 begin
= version_string
.find_first_of("0123456789", begin
);
28 if (begin
== std::string::npos
)
29 return std::make_pair("", std::string::npos
);
31 size_t end
= version_string
.find_first_not_of("01234567890.", begin
);
32 std::string sub_string
;
33 if (end
!= std::string::npos
)
34 sub_string
= version_string
.substr(begin
, end
- begin
);
36 sub_string
= version_string
.substr(begin
);
37 std::vector
<std::string
> pieces
= base::SplitString(
38 sub_string
, ".", base::TRIM_WHITESPACE
, base::SPLIT_WANT_ALL
);
39 if (pieces
.size() >= 2)
40 return std::make_pair(pieces
[0] + "." + pieces
[1], end
);
42 return std::make_pair("", end
);
45 std::string
GetDriverVersionFromString(const std::string
& version_string
) {
46 // We expect that android GL_VERSION strings will be of a form
47 // similar to: "OpenGL ES 2.0 V@6.0 AU@ (CL@2946718)" where the
48 // first match to [0-9][0-9.]* is the OpenGL ES version number, and
49 // the second match to [0-9][0-9.]* is the driver version (in this
51 // It is currently assumed that the driver version has at least one
52 // period in it, and only the first two components are significant.
53 size_t begin
= GetVersionFromString(version_string
).second
;
54 if (begin
== std::string::npos
)
57 std::pair
<std::string
, size_t> driver_version
=
58 GetVersionFromString(version_string
, begin
);
59 if (driver_version
.first
== "")
62 return driver_version
.first
;
65 gpu::CollectInfoResult
CollectDriverInfo(gpu::GPUInfo
* gpu_info
) {
66 // Go through the process of loading GL libs and initializing an EGL
67 // context so that we can get GL vendor/version/renderer strings.
68 base::NativeLibrary gles_library
, egl_library
;
69 base::NativeLibraryLoadError error
;
71 base::LoadNativeLibrary(base::FilePath("libGLESv2.so"), &error
);
73 LOG(FATAL
) << "Failed to load libGLESv2.so";
75 egl_library
= base::LoadNativeLibrary(base::FilePath("libEGL.so"), &error
);
77 LOG(FATAL
) << "Failed to load libEGL.so";
79 typedef void* (*eglGetProcAddressProc
)(const char* name
);
81 auto eglGetProcAddressFn
= reinterpret_cast<eglGetProcAddressProc
>(
82 base::GetFunctionPointerFromNativeLibrary(egl_library
,
83 "eglGetProcAddress"));
84 if (!eglGetProcAddressFn
)
85 LOG(FATAL
) << "eglGetProcAddress not found.";
87 auto get_func
= [eglGetProcAddressFn
, gles_library
, egl_library
](
90 proc
= base::GetFunctionPointerFromNativeLibrary(egl_library
, name
);
93 proc
= base::GetFunctionPointerFromNativeLibrary(gles_library
, name
);
96 proc
= eglGetProcAddressFn(name
);
99 LOG(FATAL
) << "Failed to look up " << name
;
100 return (void *)nullptr;
103 #define LOOKUP_FUNC(x) auto x##Fn = reinterpret_cast<gfx::x##Proc>(get_func(#x))
105 LOOKUP_FUNC(eglGetError
);
106 LOOKUP_FUNC(eglQueryString
);
107 LOOKUP_FUNC(eglGetCurrentContext
);
108 LOOKUP_FUNC(eglGetCurrentDisplay
);
109 LOOKUP_FUNC(eglGetCurrentSurface
);
110 LOOKUP_FUNC(eglGetDisplay
);
111 LOOKUP_FUNC(eglInitialize
);
112 LOOKUP_FUNC(eglChooseConfig
);
113 LOOKUP_FUNC(eglCreateContext
);
114 LOOKUP_FUNC(eglCreatePbufferSurface
);
115 LOOKUP_FUNC(eglMakeCurrent
);
116 LOOKUP_FUNC(eglDestroySurface
);
117 LOOKUP_FUNC(eglDestroyContext
);
119 LOOKUP_FUNC(glGetString
);
120 LOOKUP_FUNC(glGetIntegerv
);
124 EGLDisplay curr_display
= eglGetCurrentDisplayFn();
125 EGLContext curr_context
= eglGetCurrentContextFn();
126 EGLSurface curr_draw_surface
= eglGetCurrentSurfaceFn(EGL_DRAW
);
127 EGLSurface curr_read_surface
= eglGetCurrentSurfaceFn(EGL_READ
);
129 EGLDisplay temp_display
= EGL_NO_DISPLAY
;
130 EGLContext temp_context
= EGL_NO_CONTEXT
;
131 EGLSurface temp_surface
= EGL_NO_SURFACE
;
133 const EGLint kConfigAttribs
[] = {
134 EGL_RENDERABLE_TYPE
, EGL_OPENGL_ES2_BIT
,
135 EGL_SURFACE_TYPE
, EGL_PBUFFER_BIT
,
137 const EGLint kContextAttribs
[] = {
138 EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_EXT
,
139 EGL_LOSE_CONTEXT_ON_RESET_EXT
,
140 EGL_CONTEXT_CLIENT_VERSION
, 2,
142 const EGLint kSurfaceAttribs
[] = {
152 auto errorstr
= [eglGetErrorFn
]() {
153 uint32_t err
= eglGetErrorFn();
154 return base::StringPrintf("%s (%x)", ui::GetEGLErrorString(err
), err
);
157 temp_display
= eglGetDisplayFn(EGL_DEFAULT_DISPLAY
);
159 if (temp_display
== EGL_NO_DISPLAY
) {
160 LOG(FATAL
) << "failed to get display. " << errorstr();
163 eglInitializeFn(temp_display
, &major
, &minor
);
165 bool egl_create_context_robustness_supported
=
166 strstr(reinterpret_cast<const char*>(
167 eglQueryStringFn(temp_display
, EGL_EXTENSIONS
)),
168 "EGL_EXT_create_context_robustness") != NULL
;
170 if (!eglChooseConfigFn(temp_display
, kConfigAttribs
, &config
, 1,
172 LOG(FATAL
) << "failed to choose an egl config. " << errorstr();
175 temp_context
= eglCreateContextFn(
176 temp_display
, config
, EGL_NO_CONTEXT
,
177 kContextAttribs
+ (egl_create_context_robustness_supported
? 0 : 2));
178 if (temp_context
== EGL_NO_CONTEXT
) {
180 << "failed to create a temporary context for fetching driver strings. "
185 eglCreatePbufferSurfaceFn(temp_display
, config
, kSurfaceAttribs
);
187 if (temp_surface
== EGL_NO_SURFACE
) {
188 eglDestroyContextFn(temp_display
, temp_context
);
190 << "failed to create a pbuffer surface for fetching driver strings. "
194 eglMakeCurrentFn(temp_display
, temp_surface
, temp_surface
, temp_context
);
196 gpu_info
->gl_vendor
= reinterpret_cast<const char*>(glGetStringFn(GL_VENDOR
));
197 gpu_info
->gl_version
=
198 reinterpret_cast<const char*>(glGetStringFn(GL_VERSION
));
199 gpu_info
->gl_renderer
=
200 reinterpret_cast<const char*>(glGetStringFn(GL_RENDERER
));
201 gpu_info
->gl_extensions
=
202 reinterpret_cast<const char*>(glGetStringFn(GL_EXTENSIONS
));
204 GLint max_samples
= 0;
205 glGetIntegervFn(GL_MAX_SAMPLES
, &max_samples
);
206 gpu_info
->max_msaa_samples
= base::IntToString(max_samples
);
208 bool supports_robustness
=
209 gpu_info
->gl_extensions
.find("GL_EXT_robustness") != std::string::npos
||
210 gpu_info
->gl_extensions
.find("GL_KHR_robustness") != std::string::npos
||
211 gpu_info
->gl_extensions
.find("GL_ARB_robustness") != std::string::npos
;
213 if (supports_robustness
) {
215 GL_RESET_NOTIFICATION_STRATEGY_ARB
,
216 reinterpret_cast<GLint
*>(&gpu_info
->gl_reset_notification_strategy
));
219 std::string glsl_version_string
=
220 reinterpret_cast<const char*>(glGetStringFn(GL_SHADING_LANGUAGE_VERSION
));
222 std::string glsl_version
= GetVersionFromString(glsl_version_string
).first
;
223 gpu_info
->pixel_shader_version
= glsl_version
;
224 gpu_info
->vertex_shader_version
= glsl_version
;
226 if (curr_display
!= EGL_NO_DISPLAY
&&
227 curr_context
!= EGL_NO_CONTEXT
) {
228 eglMakeCurrentFn(curr_display
, curr_draw_surface
, curr_read_surface
,
231 eglMakeCurrentFn(temp_display
, EGL_NO_SURFACE
, EGL_NO_SURFACE
,
235 eglDestroySurfaceFn(temp_display
, temp_surface
);
236 eglDestroyContextFn(temp_display
, temp_context
);
238 return gpu::kCollectInfoSuccess
;
245 CollectInfoResult
CollectContextGraphicsInfo(GPUInfo
* gpu_info
) {
246 /// TODO(tobiasjs) Check if CollectGraphicsInfo in gpu_main.cc
247 /// really only needs basic graphics info on all platforms, and if
248 /// so switch it to using that and make this the NOP that it really
249 /// should be, to avoid potential double collection of info.
250 return CollectBasicGraphicsInfo(gpu_info
);
253 CollectInfoResult
CollectGpuID(uint32
* vendor_id
, uint32
* device_id
) {
254 DCHECK(vendor_id
&& device_id
);
257 return kCollectInfoNonFatalFailure
;
260 CollectInfoResult
CollectBasicGraphicsInfo(GPUInfo
* gpu_info
) {
261 gpu_info
->can_lose_context
= false;
263 gpu_info
->machine_model_name
=
264 base::android::BuildInfo::GetInstance()->model();
266 // Create a short-lived context on the UI thread to collect the GL strings.
267 // Make sure we restore the existing context if there is one.
268 CollectInfoResult result
= CollectDriverInfo(gpu_info
);
269 if (result
== kCollectInfoSuccess
)
270 result
= CollectDriverInfoGL(gpu_info
);
271 gpu_info
->basic_info_state
= result
;
272 gpu_info
->context_info_state
= result
;
276 CollectInfoResult
CollectDriverInfoGL(GPUInfo
* gpu_info
) {
277 gpu_info
->driver_version
= GetDriverVersionFromString(
278 gpu_info
->gl_version
);
279 gpu_info
->gpu
.vendor_string
= gpu_info
->gl_vendor
;
280 gpu_info
->gpu
.device_string
= gpu_info
->gl_renderer
;
281 return kCollectInfoSuccess
;
284 void MergeGPUInfo(GPUInfo
* basic_gpu_info
,
285 const GPUInfo
& context_gpu_info
) {
286 MergeGPUInfoGL(basic_gpu_info
, context_gpu_info
);