blob: 07f5f721e5ffc2c4e7a47ff684e55c75a6f8f9f7 [file] [log] [blame]
Marat Dukhanc40c7312017-09-21 16:08:18 -07001#include <stdio.h>
2#include <stdlib.h>
3#include <stdbool.h>
4#include <string.h>
5#include <inttypes.h>
6
7#include <dlfcn.h>
8
9#include <EGL/egl.h>
10#include <GLES2/gl2.h>
11
12#include <api.h>
13#include <log.h>
14
15
16typedef EGLint (EGLAPIENTRY *egl_get_error_t)(void);
17typedef EGLDisplay (EGLAPIENTRY *egl_get_display_t)(EGLNativeDisplayType);
18typedef EGLBoolean (EGLAPIENTRY *egl_initialize_t)(EGLDisplay, EGLint*, EGLint*);
19typedef EGLBoolean (EGLAPIENTRY *egl_terminate_t)(EGLDisplay);
20typedef EGLBoolean (EGLAPIENTRY *egl_choose_config_t)(EGLDisplay dpy, const EGLint*, EGLConfig*, EGLint, EGLint*);
21typedef EGLSurface (EGLAPIENTRY *egl_create_pbuffer_surface_t)(EGLDisplay, EGLConfig, const EGLint*);
22typedef EGLBoolean (EGLAPIENTRY *egl_destroy_surface_t)(EGLDisplay, EGLSurface);
23typedef EGLContext (EGLAPIENTRY *egl_create_context_t)(EGLDisplay, EGLConfig, EGLContext, const EGLint*);
24typedef EGLBoolean (EGLAPIENTRY *egl_destroy_context_t)(EGLDisplay, EGLContext);
25typedef EGLBoolean (EGLAPIENTRY *egl_make_current_t)(EGLDisplay, EGLSurface, EGLSurface, EGLContext);
26typedef EGLContext (EGLAPIENTRY *egl_get_current_context_t)(void);
27typedef EGLSurface (EGLAPIENTRY *egl_get_current_surface_t)(EGLint);
28
29static const char egl_function_names[] =
30 "eglGetError\0"
31 "eglGetDisplay\0"
32 "eglInitialize\0"
33 "eglTerminate\0"
34 "eglChooseConfig\0"
35 "eglCreatePbufferSurface\0"
36 "eglDestroySurface\0"
37 "eglCreateContext\0"
38 "eglDestroyContext\0"
39 "eglMakeCurrent\0"
40 "eglGetCurrentContext\0"
41 "eglGetCurrentSurface\0";
42
43#define LIBEGL_FUNCTION_COUNT 12
44
45struct libegl {
46 void* handle;
47 union {
48 struct {
49 egl_get_error_t get_error;
50 egl_get_display_t get_display;
51 egl_initialize_t initialize;
52 egl_terminate_t terminate;
53 egl_choose_config_t choose_config;
54 egl_create_pbuffer_surface_t create_pbuffer_surface;
55 egl_destroy_surface_t destroy_surface;
56 egl_create_context_t create_context;
57 egl_destroy_context_t destroy_context;
58 egl_make_current_t make_current;
59 egl_get_current_context_t get_current_context;
60 egl_get_current_surface_t get_current_surface;
61 };
62 void* functions[LIBEGL_FUNCTION_COUNT];
63 };
64};
65
Marat Dukhanc7c09992017-09-22 12:12:19 -070066typedef const GLubyte* (GL_APIENTRY *gl_get_string_t)(GLenum);
67
Marat Dukhanc40c7312017-09-21 16:08:18 -070068static const char gles2_function_names[] =
69 "glGetString\0";
70
71#define LIBGLES2_FUNCTION_COUNT 1
72
73struct libgles2 {
74 void* handle;
75 union {
Marat Dukhanc7c09992017-09-22 12:12:19 -070076 gl_get_string_t get_string;
Marat Dukhanc40c7312017-09-21 16:08:18 -070077 void* functions[LIBGLES2_FUNCTION_COUNT];
78 };
79};
80
81static bool cpuinfo_gpu_load_libegl(struct libegl egl[restrict static 1]) {
82 egl->handle = dlopen("libEGL.so", RTLD_LAZY | RTLD_LOCAL);
83 if (egl->handle == NULL) {
84 cpuinfo_log_warning("failed to load libEGL.so: %s", dlerror());
85 goto failed;
86 }
87
88 const char* function_name = egl_function_names;
89 for (size_t i = 0; i < LIBEGL_FUNCTION_COUNT; i++) {
90 void* function = dlsym(egl->handle, function_name);
91 if (function == NULL) {
92 cpuinfo_log_warning("failed to locate %s in libEGL.so: %s", function_name, dlerror());
93 goto failed;
94 }
95 egl->functions[i] = function;
96 function_name += strlen(function_name) + 1;
97 }
98
99 return true;
100
101failed:
102 if (egl->handle != NULL) {
103 if (dlclose(egl->handle) != 0) {
104 cpuinfo_log_info("failed to unload libEGL.so: %s", dlerror());
105 }
106 }
107 memset(egl, 0, sizeof(struct libegl));
108 return false;
109}
110
111static bool cpuinfo_gpu_load_gles2(struct libgles2 gles[restrict static 1]) {
112 gles->handle = dlopen("libGLESv2.so", RTLD_LAZY | RTLD_LOCAL);
113 if (gles->handle == NULL) {
114 cpuinfo_log_warning("failed to load libGLESv2.so: %s", dlerror());
115 goto failed;
116 }
117
118 const char* function_name = gles2_function_names;
119 for (size_t i = 0; i < LIBGLES2_FUNCTION_COUNT; i++) {
120 void* function = dlsym(gles->handle, function_name);
121 if (function == NULL) {
122 cpuinfo_log_warning("failed to locate %s in libGLESv2.so: %s", function_name, dlerror());
123 goto failed;
124 }
125 gles->functions[i] = function;
126 function_name += strlen(function_name) + 1;
127 }
128
129 return true;
130
131failed:
132 if (gles->handle != NULL) {
133 if (dlclose(gles->handle) != 0) {
134 cpuinfo_log_info("failed to unload libGLESv2.so: %s", dlerror());
135 }
136 }
137 memset(gles, 0, sizeof(struct libgles2));
138 return false;
139}
140
141bool cpuinfo_gpu_query_gles2(char gpu_name[restrict static CPUINFO_GPU_NAME_MAX]) {
142 bool success = false;
Marat Dukhanc40c7312017-09-21 16:08:18 -0700143 EGLDisplay display = EGL_NO_DISPLAY;
144 EGLSurface surface = EGL_NO_SURFACE;
145 EGLSurface previous_draw_surface = EGL_NO_SURFACE;
146 EGLSurface previous_read_surface = EGL_NO_SURFACE;
147 EGLContext context = EGL_NO_CONTEXT;
148 EGLContext previous_context = EGL_NO_CONTEXT;
149 EGLBoolean egl_init_success = EGL_FALSE;
150 EGLBoolean egl_make_current_success = EGL_FALSE;
151 EGLBoolean egl_success;
152 struct libegl egl;
153 struct libgles2 gles2;
154
155 if (!cpuinfo_gpu_load_libegl(&egl)) {
156 return false;
157 }
158 if (!cpuinfo_gpu_load_gles2(&gles2)) {
159 goto cleanup;
160 }
161
162 display = egl.get_display(EGL_DEFAULT_DISPLAY);
163 if (display == EGL_NO_DISPLAY) {
164 cpuinfo_log_warning("failed to get default EGL display: error %"PRId32, (int32_t) egl.get_error());
165 goto cleanup;
166 }
167
168 EGLint egl_major = 0, egl_minor = 0;
169 egl_init_success = egl.initialize(display, &egl_major, &egl_minor);
170 if (egl_init_success != EGL_TRUE) {
171 cpuinfo_log_warning("failed to initialize EGL display connection: error %"PRId32, (int32_t) egl.get_error());
172 goto cleanup;
173 }
174 cpuinfo_log_debug("initialized EGL %"PRId32".%"PRId32" display connection",
175 (int32_t) egl_major, (int32_t) egl_minor);
176
177 /* Get previous state to restore it later */
178 previous_context = egl.get_current_context();
179 previous_draw_surface = egl.get_current_surface(EGL_DRAW);
180 previous_read_surface = egl.get_current_surface(EGL_READ);
181
182 EGLint const config_attributes[] = {
183 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
184 EGL_CONFORMANT, EGL_OPENGL_ES2_BIT,
185 EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
186 EGL_NONE,
187 };
188 EGLConfig config = NULL;
189 EGLint config_count = 0;
190 egl_success = egl.choose_config(display, config_attributes, &config, 1, &config_count);
191 if (egl_success != EGL_TRUE || config_count == 0 || config == NULL) {
192 cpuinfo_log_warning("failed to find EGL framebuffer configuration with required attributes");
193 goto cleanup;
194 }
195
196 EGLint const surface_attributes[] = {
197 EGL_HEIGHT, 1,
198 EGL_WIDTH, 1,
199 EGL_NONE,
200 };
201 surface = egl.create_pbuffer_surface(display, config, surface_attributes);
202 if (surface == EGL_NO_SURFACE) {
203 cpuinfo_log_warning("failed to create EGL PBuffer surface: error %"PRId32, (int32_t) egl.get_error());
204 goto cleanup;
205 }
206
207 EGLint const context_attributes[] = {
208 EGL_CONTEXT_CLIENT_VERSION, 2,
209 EGL_NONE,
210 };
211 context = egl.create_context(display, config, EGL_NO_CONTEXT, context_attributes);
212 if (context == EGL_NO_CONTEXT) {
213 cpuinfo_log_warning("failed to create OpenGL ES2 context: error %"PRId32, (int32_t) egl.get_error());
214 goto cleanup;
215 }
216
217 egl_make_current_success = egl.make_current(display, surface, surface, context);
218 if (egl_make_current_success != EGL_TRUE) {
219 cpuinfo_log_warning("failed to attach OpenGL ES rendering context: error %"PRId32, (int32_t) egl.get_error());
220 goto cleanup;
221 }
222
223 const char* vendor = (const char*) gles2.get_string(GL_VENDOR);
224 if (vendor == NULL) {
225 cpuinfo_log_warning("failed to get GL_VENDOR for OpenGL ES2 context");
226 goto cleanup;
227 }
228 cpuinfo_log_debug("GL_VENDOR = \"%s\"", vendor);
229
230 const char* renderer = (const char*) gles2.get_string(GL_RENDERER);
Marat Dukhanb1662f42017-10-23 15:14:30 -0700231 if (renderer == NULL) {
Marat Dukhanc40c7312017-09-21 16:08:18 -0700232 cpuinfo_log_warning("failed to get GL_RENDERER for OpenGL ES2 context");
233 goto cleanup;
234 }
235 cpuinfo_log_debug("GL_RENDERER = \"%s\"", renderer);
236
237 snprintf(gpu_name, CPUINFO_GPU_NAME_MAX, "%s %s", vendor, renderer);
238 success = true;
239
240cleanup:
241 if (egl_make_current_success == EGL_TRUE) {
242 egl.make_current(display, previous_draw_surface, previous_read_surface, previous_context);
243 }
244 if (context != EGL_NO_CONTEXT) {
245 egl.destroy_context(display, context);
246 }
247 if (surface != EGL_NO_SURFACE) {
248 egl.destroy_surface(display, surface);
249 }
250 if (egl_init_success == EGL_TRUE) {
251 egl.terminate(display);
252 }
253 if (egl.handle != NULL) {
254 dlclose(egl.handle);
255 }
256 if (gles2.handle != NULL) {
257 dlclose(gles2.handle);
258 }
259 return success;
260}