blob: 07f5f721e5ffc2c4e7a47ff684e55c75a6f8f9f7 [file] [log] [blame]
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <inttypes.h>
#include <dlfcn.h>
#include <EGL/egl.h>
#include <GLES2/gl2.h>
#include <api.h>
#include <log.h>
typedef EGLint (EGLAPIENTRY *egl_get_error_t)(void);
typedef EGLDisplay (EGLAPIENTRY *egl_get_display_t)(EGLNativeDisplayType);
typedef EGLBoolean (EGLAPIENTRY *egl_initialize_t)(EGLDisplay, EGLint*, EGLint*);
typedef EGLBoolean (EGLAPIENTRY *egl_terminate_t)(EGLDisplay);
typedef EGLBoolean (EGLAPIENTRY *egl_choose_config_t)(EGLDisplay dpy, const EGLint*, EGLConfig*, EGLint, EGLint*);
typedef EGLSurface (EGLAPIENTRY *egl_create_pbuffer_surface_t)(EGLDisplay, EGLConfig, const EGLint*);
typedef EGLBoolean (EGLAPIENTRY *egl_destroy_surface_t)(EGLDisplay, EGLSurface);
typedef EGLContext (EGLAPIENTRY *egl_create_context_t)(EGLDisplay, EGLConfig, EGLContext, const EGLint*);
typedef EGLBoolean (EGLAPIENTRY *egl_destroy_context_t)(EGLDisplay, EGLContext);
typedef EGLBoolean (EGLAPIENTRY *egl_make_current_t)(EGLDisplay, EGLSurface, EGLSurface, EGLContext);
typedef EGLContext (EGLAPIENTRY *egl_get_current_context_t)(void);
typedef EGLSurface (EGLAPIENTRY *egl_get_current_surface_t)(EGLint);
static const char egl_function_names[] =
"eglGetError\0"
"eglGetDisplay\0"
"eglInitialize\0"
"eglTerminate\0"
"eglChooseConfig\0"
"eglCreatePbufferSurface\0"
"eglDestroySurface\0"
"eglCreateContext\0"
"eglDestroyContext\0"
"eglMakeCurrent\0"
"eglGetCurrentContext\0"
"eglGetCurrentSurface\0";
#define LIBEGL_FUNCTION_COUNT 12
struct libegl {
void* handle;
union {
struct {
egl_get_error_t get_error;
egl_get_display_t get_display;
egl_initialize_t initialize;
egl_terminate_t terminate;
egl_choose_config_t choose_config;
egl_create_pbuffer_surface_t create_pbuffer_surface;
egl_destroy_surface_t destroy_surface;
egl_create_context_t create_context;
egl_destroy_context_t destroy_context;
egl_make_current_t make_current;
egl_get_current_context_t get_current_context;
egl_get_current_surface_t get_current_surface;
};
void* functions[LIBEGL_FUNCTION_COUNT];
};
};
typedef const GLubyte* (GL_APIENTRY *gl_get_string_t)(GLenum);
static const char gles2_function_names[] =
"glGetString\0";
#define LIBGLES2_FUNCTION_COUNT 1
struct libgles2 {
void* handle;
union {
gl_get_string_t get_string;
void* functions[LIBGLES2_FUNCTION_COUNT];
};
};
static bool cpuinfo_gpu_load_libegl(struct libegl egl[restrict static 1]) {
egl->handle = dlopen("libEGL.so", RTLD_LAZY | RTLD_LOCAL);
if (egl->handle == NULL) {
cpuinfo_log_warning("failed to load libEGL.so: %s", dlerror());
goto failed;
}
const char* function_name = egl_function_names;
for (size_t i = 0; i < LIBEGL_FUNCTION_COUNT; i++) {
void* function = dlsym(egl->handle, function_name);
if (function == NULL) {
cpuinfo_log_warning("failed to locate %s in libEGL.so: %s", function_name, dlerror());
goto failed;
}
egl->functions[i] = function;
function_name += strlen(function_name) + 1;
}
return true;
failed:
if (egl->handle != NULL) {
if (dlclose(egl->handle) != 0) {
cpuinfo_log_info("failed to unload libEGL.so: %s", dlerror());
}
}
memset(egl, 0, sizeof(struct libegl));
return false;
}
static bool cpuinfo_gpu_load_gles2(struct libgles2 gles[restrict static 1]) {
gles->handle = dlopen("libGLESv2.so", RTLD_LAZY | RTLD_LOCAL);
if (gles->handle == NULL) {
cpuinfo_log_warning("failed to load libGLESv2.so: %s", dlerror());
goto failed;
}
const char* function_name = gles2_function_names;
for (size_t i = 0; i < LIBGLES2_FUNCTION_COUNT; i++) {
void* function = dlsym(gles->handle, function_name);
if (function == NULL) {
cpuinfo_log_warning("failed to locate %s in libGLESv2.so: %s", function_name, dlerror());
goto failed;
}
gles->functions[i] = function;
function_name += strlen(function_name) + 1;
}
return true;
failed:
if (gles->handle != NULL) {
if (dlclose(gles->handle) != 0) {
cpuinfo_log_info("failed to unload libGLESv2.so: %s", dlerror());
}
}
memset(gles, 0, sizeof(struct libgles2));
return false;
}
bool cpuinfo_gpu_query_gles2(char gpu_name[restrict static CPUINFO_GPU_NAME_MAX]) {
bool success = false;
EGLDisplay display = EGL_NO_DISPLAY;
EGLSurface surface = EGL_NO_SURFACE;
EGLSurface previous_draw_surface = EGL_NO_SURFACE;
EGLSurface previous_read_surface = EGL_NO_SURFACE;
EGLContext context = EGL_NO_CONTEXT;
EGLContext previous_context = EGL_NO_CONTEXT;
EGLBoolean egl_init_success = EGL_FALSE;
EGLBoolean egl_make_current_success = EGL_FALSE;
EGLBoolean egl_success;
struct libegl egl;
struct libgles2 gles2;
if (!cpuinfo_gpu_load_libegl(&egl)) {
return false;
}
if (!cpuinfo_gpu_load_gles2(&gles2)) {
goto cleanup;
}
display = egl.get_display(EGL_DEFAULT_DISPLAY);
if (display == EGL_NO_DISPLAY) {
cpuinfo_log_warning("failed to get default EGL display: error %"PRId32, (int32_t) egl.get_error());
goto cleanup;
}
EGLint egl_major = 0, egl_minor = 0;
egl_init_success = egl.initialize(display, &egl_major, &egl_minor);
if (egl_init_success != EGL_TRUE) {
cpuinfo_log_warning("failed to initialize EGL display connection: error %"PRId32, (int32_t) egl.get_error());
goto cleanup;
}
cpuinfo_log_debug("initialized EGL %"PRId32".%"PRId32" display connection",
(int32_t) egl_major, (int32_t) egl_minor);
/* Get previous state to restore it later */
previous_context = egl.get_current_context();
previous_draw_surface = egl.get_current_surface(EGL_DRAW);
previous_read_surface = egl.get_current_surface(EGL_READ);
EGLint const config_attributes[] = {
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL_CONFORMANT, EGL_OPENGL_ES2_BIT,
EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
EGL_NONE,
};
EGLConfig config = NULL;
EGLint config_count = 0;
egl_success = egl.choose_config(display, config_attributes, &config, 1, &config_count);
if (egl_success != EGL_TRUE || config_count == 0 || config == NULL) {
cpuinfo_log_warning("failed to find EGL framebuffer configuration with required attributes");
goto cleanup;
}
EGLint const surface_attributes[] = {
EGL_HEIGHT, 1,
EGL_WIDTH, 1,
EGL_NONE,
};
surface = egl.create_pbuffer_surface(display, config, surface_attributes);
if (surface == EGL_NO_SURFACE) {
cpuinfo_log_warning("failed to create EGL PBuffer surface: error %"PRId32, (int32_t) egl.get_error());
goto cleanup;
}
EGLint const context_attributes[] = {
EGL_CONTEXT_CLIENT_VERSION, 2,
EGL_NONE,
};
context = egl.create_context(display, config, EGL_NO_CONTEXT, context_attributes);
if (context == EGL_NO_CONTEXT) {
cpuinfo_log_warning("failed to create OpenGL ES2 context: error %"PRId32, (int32_t) egl.get_error());
goto cleanup;
}
egl_make_current_success = egl.make_current(display, surface, surface, context);
if (egl_make_current_success != EGL_TRUE) {
cpuinfo_log_warning("failed to attach OpenGL ES rendering context: error %"PRId32, (int32_t) egl.get_error());
goto cleanup;
}
const char* vendor = (const char*) gles2.get_string(GL_VENDOR);
if (vendor == NULL) {
cpuinfo_log_warning("failed to get GL_VENDOR for OpenGL ES2 context");
goto cleanup;
}
cpuinfo_log_debug("GL_VENDOR = \"%s\"", vendor);
const char* renderer = (const char*) gles2.get_string(GL_RENDERER);
if (renderer == NULL) {
cpuinfo_log_warning("failed to get GL_RENDERER for OpenGL ES2 context");
goto cleanup;
}
cpuinfo_log_debug("GL_RENDERER = \"%s\"", renderer);
snprintf(gpu_name, CPUINFO_GPU_NAME_MAX, "%s %s", vendor, renderer);
success = true;
cleanup:
if (egl_make_current_success == EGL_TRUE) {
egl.make_current(display, previous_draw_surface, previous_read_surface, previous_context);
}
if (context != EGL_NO_CONTEXT) {
egl.destroy_context(display, context);
}
if (surface != EGL_NO_SURFACE) {
egl.destroy_surface(display, surface);
}
if (egl_init_success == EGL_TRUE) {
egl.terminate(display);
}
if (egl.handle != NULL) {
dlclose(egl.handle);
}
if (gles2.handle != NULL) {
dlclose(gles2.handle);
}
return success;
}