[email protected] | 3b7efa4 | 2012-02-08 19:41:44 | [diff] [blame] | 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. |
| 4 | |
[email protected] | d7b5cc7 | 2013-05-23 20:05:00 | [diff] [blame] | 5 | #include "gpu/config/gpu_info_collector.h" |
[email protected] | 3b7efa4 | 2012-02-08 19:41:44 | [diff] [blame] | 6 | |
avi | f15d60a | 2015-12-21 17:06:33 | [diff] [blame] | 7 | #include <stddef.h> |
| 8 | #include <stdint.h> |
| 9 | |
[email protected] | 33eeba7b9 | 2013-02-06 01:47:03 | [diff] [blame] | 10 | #include "base/android/build_info.h" |
kkinnunen | ef49d53 | 2016-02-03 07:47:00 | [diff] [blame] | 11 | #include "base/android/jni_android.h" |
[email protected] | f34ffdf2 | 2012-12-15 00:41:27 | [diff] [blame] | 12 | #include "base/command_line.h" |
tobiasjs | ca238b3b | 2015-06-24 22:53:54 | [diff] [blame] | 13 | #include "base/files/file_path.h" |
[email protected] | 3b7efa4 | 2012-02-08 19:41:44 | [diff] [blame] | 14 | #include "base/logging.h" |
tobiasjs | ca238b3b | 2015-06-24 22:53:54 | [diff] [blame] | 15 | #include "base/native_library.h" |
[email protected] | f439096 | 2013-06-11 07:29:22 | [diff] [blame] | 16 | #include "base/strings/string_number_conversions.h" |
[email protected] | b9e7c479f | 2013-04-12 04:33:24 | [diff] [blame] | 17 | #include "base/strings/string_piece.h" |
[email protected] | 27c0573 | 2013-02-15 21:55:49 | [diff] [blame] | 18 | #include "base/strings/string_split.h" |
[email protected] | f439096 | 2013-06-11 07:29:22 | [diff] [blame] | 19 | #include "base/strings/string_util.h" |
tobiasjs | ca238b3b | 2015-06-24 22:53:54 | [diff] [blame] | 20 | #include "base/strings/stringprintf.h" |
j.isorce | 47305c8a | 2016-08-17 14:35:01 | [diff] [blame] | 21 | #include "gpu/config/gpu_switches.h" |
tobiasjs | ca238b3b | 2015-06-24 22:53:54 | [diff] [blame] | 22 | #include "ui/gl/egl_util.h" |
[email protected] | 1a21247 | 2013-10-01 01:40:48 | [diff] [blame] | 23 | #include "ui/gl/gl_bindings.h" |
| 24 | #include "ui/gl/gl_context.h" |
| 25 | #include "ui/gl/gl_surface.h" |
[email protected] | bd4d2f73 | 2012-11-19 22:19:36 | [diff] [blame] | 26 | |
| 27 | namespace { |
| 28 | |
tobiasjs | ca238b3b | 2015-06-24 22:53:54 | [diff] [blame] | 29 | std::pair<std::string, size_t> GetVersionFromString( |
| 30 | const std::string& version_string, |
| 31 | size_t begin = 0) { |
| 32 | begin = version_string.find_first_of("0123456789", begin); |
| 33 | if (begin == std::string::npos) |
| 34 | return std::make_pair("", std::string::npos); |
| 35 | |
| 36 | size_t end = version_string.find_first_not_of("01234567890.", begin); |
| 37 | std::string sub_string; |
| 38 | if (end != std::string::npos) |
| 39 | sub_string = version_string.substr(begin, end - begin); |
| 40 | else |
| 41 | sub_string = version_string.substr(begin); |
brettw | 26dab8f0 | 2015-08-08 00:28:47 | [diff] [blame] | 42 | std::vector<std::string> pieces = base::SplitString( |
| 43 | sub_string, ".", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); |
tobiasjs | ca238b3b | 2015-06-24 22:53:54 | [diff] [blame] | 44 | if (pieces.size() >= 2) |
| 45 | return std::make_pair(pieces[0] + "." + pieces[1], end); |
| 46 | else |
| 47 | return std::make_pair("", end); |
| 48 | } |
| 49 | |
[email protected] | bd4d2f73 | 2012-11-19 22:19:36 | [diff] [blame] | 50 | std::string GetDriverVersionFromString(const std::string& version_string) { |
tobiasjs | f926896 | 2015-06-05 12:38:11 | [diff] [blame] | 51 | // We expect that android GL_VERSION strings will be of a form |
| 52 | // similar to: "OpenGL ES 2.0 [email protected] AU@ (CL@2946718)" where the |
| 53 | // first match to [0-9][0-9.]* is the OpenGL ES version number, and |
| 54 | // the second match to [0-9][0-9.]* is the driver version (in this |
| 55 | // case, 6.0). |
| 56 | // It is currently assumed that the driver version has at least one |
| 57 | // period in it, and only the first two components are significant. |
tobiasjs | ca238b3b | 2015-06-24 22:53:54 | [diff] [blame] | 58 | size_t begin = GetVersionFromString(version_string).second; |
[email protected] | bd4d2f73 | 2012-11-19 22:19:36 | [diff] [blame] | 59 | if (begin == std::string::npos) |
| 60 | return "0"; |
[email protected] | bd4d2f73 | 2012-11-19 22:19:36 | [diff] [blame] | 61 | |
tobiasjs | ca238b3b | 2015-06-24 22:53:54 | [diff] [blame] | 62 | std::pair<std::string, size_t> driver_version = |
| 63 | GetVersionFromString(version_string, begin); |
| 64 | if (driver_version.first == "") |
[email protected] | bd4d2f73 | 2012-11-19 22:19:36 | [diff] [blame] | 65 | return "0"; |
tobiasjs | ca238b3b | 2015-06-24 22:53:54 | [diff] [blame] | 66 | |
| 67 | return driver_version.first; |
[email protected] | bd4d2f73 | 2012-11-19 22:19:36 | [diff] [blame] | 68 | } |
| 69 | |
tobiasjs | ca238b3b | 2015-06-24 22:53:54 | [diff] [blame] | 70 | gpu::CollectInfoResult CollectDriverInfo(gpu::GPUInfo* gpu_info) { |
| 71 | // Go through the process of loading GL libs and initializing an EGL |
| 72 | // context so that we can get GL vendor/version/renderer strings. |
| 73 | base::NativeLibrary gles_library, egl_library; |
| 74 | base::NativeLibraryLoadError error; |
| 75 | gles_library = |
| 76 | base::LoadNativeLibrary(base::FilePath("libGLESv2.so"), &error); |
| 77 | if (!gles_library) |
| 78 | LOG(FATAL) << "Failed to load libGLESv2.so"; |
[email protected] | 1a21247 | 2013-10-01 01:40:48 | [diff] [blame] | 79 | |
tobiasjs | ca238b3b | 2015-06-24 22:53:54 | [diff] [blame] | 80 | egl_library = base::LoadNativeLibrary(base::FilePath("libEGL.so"), &error); |
| 81 | if (!egl_library) |
| 82 | LOG(FATAL) << "Failed to load libEGL.so"; |
[email protected] | 1a21247 | 2013-10-01 01:40:48 | [diff] [blame] | 83 | |
tobiasjs | ca238b3b | 2015-06-24 22:53:54 | [diff] [blame] | 84 | typedef void* (*eglGetProcAddressProc)(const char* name); |
[email protected] | 1a21247 | 2013-10-01 01:40:48 | [diff] [blame] | 85 | |
tobiasjs | ca238b3b | 2015-06-24 22:53:54 | [diff] [blame] | 86 | auto eglGetProcAddressFn = reinterpret_cast<eglGetProcAddressProc>( |
| 87 | base::GetFunctionPointerFromNativeLibrary(egl_library, |
| 88 | "eglGetProcAddress")); |
| 89 | if (!eglGetProcAddressFn) |
| 90 | LOG(FATAL) << "eglGetProcAddress not found."; |
[email protected] | 1a21247 | 2013-10-01 01:40:48 | [diff] [blame] | 91 | |
tobiasjs | ca238b3b | 2015-06-24 22:53:54 | [diff] [blame] | 92 | auto get_func = [eglGetProcAddressFn, gles_library, egl_library]( |
| 93 | const char* name) { |
| 94 | void *proc; |
| 95 | proc = base::GetFunctionPointerFromNativeLibrary(egl_library, name); |
| 96 | if (proc) |
| 97 | return proc; |
| 98 | proc = base::GetFunctionPointerFromNativeLibrary(gles_library, name); |
| 99 | if (proc) |
| 100 | return proc; |
| 101 | proc = eglGetProcAddressFn(name); |
| 102 | if (proc) |
| 103 | return proc; |
| 104 | LOG(FATAL) << "Failed to look up " << name; |
| 105 | return (void *)nullptr; |
| 106 | }; |
[email protected] | 1a21247 | 2013-10-01 01:40:48 | [diff] [blame] | 107 | |
kylechar | 7a46384 | 2016-05-26 14:46:12 | [diff] [blame] | 108 | #define LOOKUP_FUNC(x) auto x##Fn = reinterpret_cast<gl::x##Proc>(get_func(#x)) |
tobiasjs | ca238b3b | 2015-06-24 22:53:54 | [diff] [blame] | 109 | |
| 110 | LOOKUP_FUNC(eglGetError); |
| 111 | LOOKUP_FUNC(eglQueryString); |
| 112 | LOOKUP_FUNC(eglGetCurrentContext); |
| 113 | LOOKUP_FUNC(eglGetCurrentDisplay); |
| 114 | LOOKUP_FUNC(eglGetCurrentSurface); |
| 115 | LOOKUP_FUNC(eglGetDisplay); |
| 116 | LOOKUP_FUNC(eglInitialize); |
| 117 | LOOKUP_FUNC(eglChooseConfig); |
| 118 | LOOKUP_FUNC(eglCreateContext); |
| 119 | LOOKUP_FUNC(eglCreatePbufferSurface); |
| 120 | LOOKUP_FUNC(eglMakeCurrent); |
| 121 | LOOKUP_FUNC(eglDestroySurface); |
| 122 | LOOKUP_FUNC(eglDestroyContext); |
| 123 | |
| 124 | LOOKUP_FUNC(glGetString); |
| 125 | LOOKUP_FUNC(glGetIntegerv); |
| 126 | |
| 127 | #undef LOOKUP_FUNC |
| 128 | |
| 129 | EGLDisplay curr_display = eglGetCurrentDisplayFn(); |
| 130 | EGLContext curr_context = eglGetCurrentContextFn(); |
| 131 | EGLSurface curr_draw_surface = eglGetCurrentSurfaceFn(EGL_DRAW); |
| 132 | EGLSurface curr_read_surface = eglGetCurrentSurfaceFn(EGL_READ); |
| 133 | |
| 134 | EGLDisplay temp_display = EGL_NO_DISPLAY; |
| 135 | EGLContext temp_context = EGL_NO_CONTEXT; |
| 136 | EGLSurface temp_surface = EGL_NO_SURFACE; |
| 137 | |
| 138 | const EGLint kConfigAttribs[] = { |
| 139 | EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, |
| 140 | EGL_SURFACE_TYPE, EGL_PBUFFER_BIT, |
| 141 | EGL_NONE}; |
| 142 | const EGLint kContextAttribs[] = { |
| 143 | EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_EXT, |
| 144 | EGL_LOSE_CONTEXT_ON_RESET_EXT, |
| 145 | EGL_CONTEXT_CLIENT_VERSION, 2, |
| 146 | EGL_NONE}; |
| 147 | const EGLint kSurfaceAttribs[] = { |
| 148 | EGL_WIDTH, 1, |
| 149 | EGL_HEIGHT, 1, |
| 150 | EGL_NONE}; |
| 151 | |
| 152 | EGLint major, minor; |
| 153 | |
| 154 | EGLConfig config; |
| 155 | EGLint num_configs; |
| 156 | |
| 157 | auto errorstr = [eglGetErrorFn]() { |
| 158 | uint32_t err = eglGetErrorFn(); |
| 159 | return base::StringPrintf("%s (%x)", ui::GetEGLErrorString(err), err); |
| 160 | }; |
| 161 | |
| 162 | temp_display = eglGetDisplayFn(EGL_DEFAULT_DISPLAY); |
| 163 | |
| 164 | if (temp_display == EGL_NO_DISPLAY) { |
| 165 | LOG(FATAL) << "failed to get display. " << errorstr(); |
| 166 | } |
| 167 | |
| 168 | eglInitializeFn(temp_display, &major, &minor); |
| 169 | |
| 170 | bool egl_create_context_robustness_supported = |
| 171 | strstr(reinterpret_cast<const char*>( |
| 172 | eglQueryStringFn(temp_display, EGL_EXTENSIONS)), |
| 173 | "EGL_EXT_create_context_robustness") != NULL; |
| 174 | |
| 175 | if (!eglChooseConfigFn(temp_display, kConfigAttribs, &config, 1, |
| 176 | &num_configs)) { |
| 177 | LOG(FATAL) << "failed to choose an egl config. " << errorstr(); |
| 178 | } |
| 179 | |
| 180 | temp_context = eglCreateContextFn( |
| 181 | temp_display, config, EGL_NO_CONTEXT, |
| 182 | kContextAttribs + (egl_create_context_robustness_supported ? 0 : 2)); |
| 183 | if (temp_context == EGL_NO_CONTEXT) { |
| 184 | LOG(FATAL) |
| 185 | << "failed to create a temporary context for fetching driver strings. " |
| 186 | << errorstr(); |
| 187 | } |
| 188 | |
| 189 | temp_surface = |
| 190 | eglCreatePbufferSurfaceFn(temp_display, config, kSurfaceAttribs); |
| 191 | |
| 192 | if (temp_surface == EGL_NO_SURFACE) { |
| 193 | eglDestroyContextFn(temp_display, temp_context); |
| 194 | LOG(FATAL) |
| 195 | << "failed to create a pbuffer surface for fetching driver strings. " |
| 196 | << errorstr(); |
| 197 | } |
| 198 | |
| 199 | eglMakeCurrentFn(temp_display, temp_surface, temp_surface, temp_context); |
| 200 | |
| 201 | gpu_info->gl_vendor = reinterpret_cast<const char*>(glGetStringFn(GL_VENDOR)); |
| 202 | gpu_info->gl_version = |
| 203 | reinterpret_cast<const char*>(glGetStringFn(GL_VERSION)); |
| 204 | gpu_info->gl_renderer = |
| 205 | reinterpret_cast<const char*>(glGetStringFn(GL_RENDERER)); |
| 206 | gpu_info->gl_extensions = |
| 207 | reinterpret_cast<const char*>(glGetStringFn(GL_EXTENSIONS)); |
| 208 | |
j.isorce | 47305c8a | 2016-08-17 14:35:01 | [diff] [blame] | 209 | base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); |
| 210 | if (command_line->HasSwitch(switches::kGpuTestingGLVendor)) { |
| 211 | gpu_info->gl_vendor = |
| 212 | command_line->GetSwitchValueASCII(switches::kGpuTestingGLVendor); |
| 213 | } |
| 214 | if (command_line->HasSwitch(switches::kGpuTestingGLRenderer)) { |
| 215 | gpu_info->gl_renderer = |
| 216 | command_line->GetSwitchValueASCII(switches::kGpuTestingGLRenderer); |
| 217 | } |
| 218 | if (command_line->HasSwitch(switches::kGpuTestingGLVersion)) { |
| 219 | gpu_info->gl_version = |
| 220 | command_line->GetSwitchValueASCII(switches::kGpuTestingGLVersion); |
| 221 | } |
| 222 | |
tobiasjs | ca238b3b | 2015-06-24 22:53:54 | [diff] [blame] | 223 | GLint max_samples = 0; |
| 224 | glGetIntegervFn(GL_MAX_SAMPLES, &max_samples); |
ricea | a01edead | 2015-07-01 15:56:50 | [diff] [blame] | 225 | gpu_info->max_msaa_samples = base::IntToString(max_samples); |
tobiasjs | ca238b3b | 2015-06-24 22:53:54 | [diff] [blame] | 226 | |
| 227 | bool supports_robustness = |
| 228 | gpu_info->gl_extensions.find("GL_EXT_robustness") != std::string::npos || |
| 229 | gpu_info->gl_extensions.find("GL_KHR_robustness") != std::string::npos || |
| 230 | gpu_info->gl_extensions.find("GL_ARB_robustness") != std::string::npos; |
| 231 | |
| 232 | if (supports_robustness) { |
| 233 | glGetIntegervFn( |
| 234 | GL_RESET_NOTIFICATION_STRATEGY_ARB, |
| 235 | reinterpret_cast<GLint*>(&gpu_info->gl_reset_notification_strategy)); |
| 236 | } |
| 237 | |
davve | 4af61ccf | 2015-10-09 07:51:24 | [diff] [blame] | 238 | std::string glsl_version_string; |
| 239 | if (const char* glsl_version_cstring = reinterpret_cast<const char*>( |
| 240 | glGetStringFn(GL_SHADING_LANGUAGE_VERSION))) |
| 241 | glsl_version_string = glsl_version_cstring; |
tobiasjs | ca238b3b | 2015-06-24 22:53:54 | [diff] [blame] | 242 | |
| 243 | std::string glsl_version = GetVersionFromString(glsl_version_string).first; |
| 244 | gpu_info->pixel_shader_version = glsl_version; |
| 245 | gpu_info->vertex_shader_version = glsl_version; |
| 246 | |
| 247 | if (curr_display != EGL_NO_DISPLAY && |
| 248 | curr_context != EGL_NO_CONTEXT) { |
| 249 | eglMakeCurrentFn(curr_display, curr_draw_surface, curr_read_surface, |
| 250 | curr_context); |
| 251 | } else { |
| 252 | eglMakeCurrentFn(temp_display, EGL_NO_SURFACE, EGL_NO_SURFACE, |
| 253 | EGL_NO_CONTEXT); |
| 254 | } |
| 255 | |
| 256 | eglDestroySurfaceFn(temp_display, temp_surface); |
| 257 | eglDestroyContextFn(temp_display, temp_context); |
| 258 | |
| 259 | return gpu::kCollectInfoSuccess; |
[email protected] | 1a21247 | 2013-10-01 01:40:48 | [diff] [blame] | 260 | } |
| 261 | |
[email protected] | bd4d2f73 | 2012-11-19 22:19:36 | [diff] [blame] | 262 | } |
[email protected] | 3b7efa4 | 2012-02-08 19:41:44 | [diff] [blame] | 263 | |
[email protected] | d7b5cc7 | 2013-05-23 20:05:00 | [diff] [blame] | 264 | namespace gpu { |
[email protected] | 3b7efa4 | 2012-02-08 19:41:44 | [diff] [blame] | 265 | |
[email protected] | 0e8cac7 | 2014-03-22 00:37:18 | [diff] [blame] | 266 | CollectInfoResult CollectContextGraphicsInfo(GPUInfo* gpu_info) { |
tobiasjs | ca238b3b | 2015-06-24 22:53:54 | [diff] [blame] | 267 | /// TODO(tobiasjs) Check if CollectGraphicsInfo in gpu_main.cc |
| 268 | /// really only needs basic graphics info on all platforms, and if |
| 269 | /// so switch it to using that and make this the NOP that it really |
| 270 | /// should be, to avoid potential double collection of info. |
[email protected] | 289126f | 2013-05-17 00:12:38 | [diff] [blame] | 271 | return CollectBasicGraphicsInfo(gpu_info); |
[email protected] | 3b7efa4 | 2012-02-08 19:41:44 | [diff] [blame] | 272 | } |
| 273 | |
avi | f15d60a | 2015-12-21 17:06:33 | [diff] [blame] | 274 | CollectInfoResult CollectGpuID(uint32_t* vendor_id, uint32_t* device_id) { |
[email protected] | 12e74bb | 2013-02-07 22:08:44 | [diff] [blame] | 275 | DCHECK(vendor_id && device_id); |
| 276 | *vendor_id = 0; |
| 277 | *device_id = 0; |
zmo | 84eae5e | 2014-09-05 01:36:23 | [diff] [blame] | 278 | return kCollectInfoNonFatalFailure; |
[email protected] | 12e74bb | 2013-02-07 22:08:44 | [diff] [blame] | 279 | } |
| 280 | |
[email protected] | 0e8cac7 | 2014-03-22 00:37:18 | [diff] [blame] | 281 | CollectInfoResult CollectBasicGraphicsInfo(GPUInfo* gpu_info) { |
kkinnunen | ef49d53 | 2016-02-03 07:47:00 | [diff] [blame] | 282 | // When command buffer is compiled as a standalone library, the process might |
| 283 | // not have a Java environment. |
| 284 | if (base::android::IsVMInitialized()) { |
| 285 | gpu_info->machine_model_name = |
| 286 | base::android::BuildInfo::GetInstance()->model(); |
| 287 | } |
[email protected] | 289126f | 2013-05-17 00:12:38 | [diff] [blame] | 288 | |
[email protected] | bd4d2f73 | 2012-11-19 22:19:36 | [diff] [blame] | 289 | // Create a short-lived context on the UI thread to collect the GL strings. |
[email protected] | 1a21247 | 2013-10-01 01:40:48 | [diff] [blame] | 290 | // Make sure we restore the existing context if there is one. |
tobiasjs | ca238b3b | 2015-06-24 22:53:54 | [diff] [blame] | 291 | CollectInfoResult result = CollectDriverInfo(gpu_info); |
| 292 | if (result == kCollectInfoSuccess) |
| 293 | result = CollectDriverInfoGL(gpu_info); |
zmo | 84eae5e | 2014-09-05 01:36:23 | [diff] [blame] | 294 | gpu_info->basic_info_state = result; |
| 295 | gpu_info->context_info_state = result; |
| 296 | return result; |
[email protected] | 3b7efa4 | 2012-02-08 19:41:44 | [diff] [blame] | 297 | } |
| 298 | |
[email protected] | 0e8cac7 | 2014-03-22 00:37:18 | [diff] [blame] | 299 | CollectInfoResult CollectDriverInfoGL(GPUInfo* gpu_info) { |
[email protected] | bd4d2f73 | 2012-11-19 22:19:36 | [diff] [blame] | 300 | gpu_info->driver_version = GetDriverVersionFromString( |
[email protected] | 39c54262 | 2014-05-07 22:16:36 | [diff] [blame] | 301 | gpu_info->gl_version); |
[email protected] | 53d6c60 | 2013-10-14 11:50:36 | [diff] [blame] | 302 | gpu_info->gpu.vendor_string = gpu_info->gl_vendor; |
| 303 | gpu_info->gpu.device_string = gpu_info->gl_renderer; |
[email protected] | 0e8cac7 | 2014-03-22 00:37:18 | [diff] [blame] | 304 | return kCollectInfoSuccess; |
[email protected] | 3b7efa4 | 2012-02-08 19:41:44 | [diff] [blame] | 305 | } |
| 306 | |
[email protected] | d7b5cc7 | 2013-05-23 20:05:00 | [diff] [blame] | 307 | void MergeGPUInfo(GPUInfo* basic_gpu_info, |
| 308 | const GPUInfo& context_gpu_info) { |
[email protected] | 4df0884f | 2012-12-17 21:10:09 | [diff] [blame] | 309 | MergeGPUInfoGL(basic_gpu_info, context_gpu_info); |
| 310 | } |
| 311 | |
[email protected] | d7b5cc7 | 2013-05-23 20:05:00 | [diff] [blame] | 312 | } // namespace gpu |