From 265c851a8f2ac3cd1f93d523736d4cce0454fe2c Mon Sep 17 00:00:00 2001 From: Jean-Philippe Andre Date: Wed, 8 Mar 2017 11:52:59 +0900 Subject: [PATCH] evas gl: Fix version detection for GLES 3.1 It was assumed that GLES 3 would only work with EGL but in fact OpenGL 4.3 & 4.5 are supersets of GLES 3.0 & 3.1 respectively. So GLX should also support GLES 3.0 or GLES 3.1 for evas gl, if the driver supports it, of course. Of course while doing this patch things didn't go like they were supposed to go. I'm currently using NVIDIA's proprietary driver, that conveniently provides EGL with GLES 3.2. But wait, there's a catch: GL_VERSION is "OpenGL ES 3.2 NVIDIA" except that none of the functions of GLES 3.1 or GLES 3.2 are actually supported. Those functions are only present in the GLX/OpenGL variant of the driver. Thanks so much for making my life easier... So yeah, this patch contains a hack for those invalid versions of GLES 3.x. What was supposed to be a small fix became a huge mess. Also add a comment about the possibly invalid auto-upgrade from GLES 2 to GLES 3. This adds a test case in elm_test, but only to verify that elm_glview_version_add(3) actually works. We need a proper GLES 3 test case, eventually (and 3.1, 3.2 of course). --- src/bin/elementary/test.c | 2 + src/bin/elementary/test_glview.c | 21 +++++- .../evas/engines/gl_common/evas_gl_api.c | 71 +++++++++---------- .../evas/engines/gl_common/evas_gl_common.h | 2 +- .../evas/engines/gl_common/evas_gl_context.c | 55 ++++++++++---- .../evas/engines/gl_common/evas_gl_core.c | 36 ++++++++-- .../engines/gl_common/evas_gl_core_private.h | 3 +- src/modules/evas/engines/gl_x11/evas_engine.c | 9 +++ 8 files changed, 139 insertions(+), 60 deletions(-) diff --git a/src/bin/elementary/test.c b/src/bin/elementary/test.c index e3d9dc6413..a850816d18 100644 --- a/src/bin/elementary/test.c +++ b/src/bin/elementary/test.c @@ -253,6 +253,7 @@ void test_grid_static(void *data, Evas_Object *obj, void *event_info); void test_glview_simple(void *data, Evas_Object *obj, void *event_info); void test_glview(void *data, Evas_Object *obj, void *event_info); void test_glview_manygears(void *data, Evas_Object *obj, void *event_info); +void test_glview_gles3(void *data, Evas_Object *obj, void *event_info); void test_3d(void *data, Evas_Object *obj, void *event_info); void test_naviframe(void *data, Evas_Object *obj, void *event_info); void test_naviframe2(void *data, Evas_Object *obj, void *event_info); @@ -859,6 +860,7 @@ add_tests: ADD_TEST(NULL, "3D", "GLViewSimple", test_glview_simple); ADD_TEST(NULL, "3D", "GLView Gears", test_glview); ADD_TEST(NULL, "3D", "GLView Many Gears", test_glview_manygears); + ADD_TEST(NULL, "3D", "GLView GL ES 3.x", test_glview_gles3); //------------------------------// ADD_TEST(NULL, "Web", "Web", test_web); diff --git a/src/bin/elementary/test_glview.c b/src/bin/elementary/test_glview.c index 2b68a73ca7..beb291a693 100644 --- a/src/bin/elementary/test_glview.c +++ b/src/bin/elementary/test_glview.c @@ -427,6 +427,9 @@ _init_gl(Evas_Object *obj) { GLData *gld = evas_object_data_get(obj, "gld"); + printf("GL_VERSION: %s\n", gld->glapi->glGetString(GL_VERSION)); + fflush(stdout); + gears_init(gld); } @@ -611,8 +614,8 @@ _mouse_up(void *data EINA_UNUSED, Evas *e EINA_UNUSED, Evas_Object *obj, void *e gld->mouse_down = 0; } -void -test_glview(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) +static void +_test_glview_do(Evas_GL_Context_Version version) { Evas_Object *win, *bx, *bt, *gl, *lb; Ecore_Animator *ani; @@ -649,7 +652,7 @@ test_glview(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_in evas_object_show(bx); // Add a GLView - gl = elm_glview_add(win); + gl = elm_glview_version_add(win, version); if (gl) { evas_object_size_hint_align_set(gl, EVAS_HINT_FILL, EVAS_HINT_FILL); @@ -723,3 +726,15 @@ test_glview(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_in evas_object_resize(win, 320, 480); evas_object_show(win); } + +void +test_glview(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) +{ + _test_glview_do(EVAS_GL_GLES_2_X); +} + +void +test_glview_gles3(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) +{ + _test_glview_do(EVAS_GL_GLES_3_X); +} diff --git a/src/modules/evas/engines/gl_common/evas_gl_api.c b/src/modules/evas/engines/gl_common/evas_gl_api.c index 3ccb5b95b6..14378143e0 100644 --- a/src/modules/evas/engines/gl_common/evas_gl_api.c +++ b/src/modules/evas/engines/gl_common/evas_gl_api.c @@ -1324,12 +1324,13 @@ _evgl_glGetString(GLenum name) { static char _version[128] = {0}; static char _glsl[128] = {0}; + const char *ret, *version_extra; EVGL_Resource *rsc; - const char *ret; + EVGL_Context *ctx; /* We wrap two values here: * - * VERSION: Since OpenGL ES 3 is not supported yet, we return OpenGL ES 2.0 + * VERSION: Since OpenGL ES 3 is not supported yet*, we return OpenGL ES 2.0 * The string is not modified on desktop GL (eg. 4.4.0 NVIDIA 343.22) * GLES 3 support is not exposed because apps can't use GLES 3 core * functions yet. @@ -1337,6 +1338,8 @@ _evgl_glGetString(GLenum name) * EXTENSIONS: This should return only the list of GL extensions supported * by Evas GL. This means as many extensions as possible should be * added to the whitelist. + * + * *: GLES 3.0/3.1 is not fully supported... we also have buggy drivers! */ /* @@ -1353,11 +1356,12 @@ _evgl_glGetString(GLenum name) if ((!(rsc = _evgl_tls_resource_get())) || !rsc->current_ctx) { ERR("Current context is NULL, not calling glGetString"); - // This sets evas_gl_error_get instead of glGetError... + // This sets evas_gl_error_get instead of eglGetError... evas_gl_common_error_set(NULL, EVAS_GL_BAD_CONTEXT); return NULL; } + ctx = rsc->current_ctx; switch (name) { case GL_VENDOR: @@ -1369,6 +1373,7 @@ _evgl_glGetString(GLenum name) ret = (const char *) glGetString(GL_SHADING_LANGUAGE_VERSION); if (!ret) return NULL; #ifdef GL_GLES + // FIXME: We probably shouldn't wrap anything for EGL if (ret[18] != '1') { // We try not to remove the vendor fluff @@ -1388,20 +1393,14 @@ _evgl_glGetString(GLenum name) ret = (const char *) glGetString(GL_VERSION); if (!ret) return NULL; #ifdef GL_GLES - if ((ret[10] != '2') && (ret[10] != '3')) - { - // We try not to remove the vendor fluff - snprintf(_version, sizeof(_version), "OpenGL ES 2.0 Evas GL (%s)", ret + 10); - _version[sizeof(_version) - 1] = '\0'; - return (const GLubyte *) _version; - } - return (const GLubyte *) ret; + version_extra = ret + 10; #else - // Desktop GL, we still keep the official name - snprintf(_version, sizeof(_version), "OpenGL ES 2.0 Evas GL (%s)", (char *) ret); + version_extra = ret; +#endif + snprintf(_version, sizeof(_version), "OpenGL ES %d.%d Evas GL (%s)", + (int) ctx->version, ctx->version_minor, version_extra); _version[sizeof(_version) - 1] = '\0'; return (const GLubyte *) _version; -#endif case GL_EXTENSIONS: // Passing the version - GLESv2/GLESv3. @@ -3242,17 +3241,19 @@ _debug_gles3_api_get(Evas_GL_API *funcs, int minor_version) static Eina_Bool -_evgl_load_gles3_apis(void *dl_handle, Evas_GL_API *funcs, int minor_version) +_evgl_load_gles3_apis(void *dl_handle, Evas_GL_API *funcs, int minor_version, + void *(*get_proc_address)(const char *)) { if (!dl_handle) return EINA_FALSE; + Eina_Bool ret_value = EINA_FALSE; -#define ORD(name) \ +#define ORD(name) do { \ funcs->name = dlsym(dl_handle, #name); \ - if (!funcs->name) \ - { \ + if (!funcs->name && get_proc_address) get_proc_address(#name); \ + if (!funcs->name) { \ WRN("%s symbol not found", #name); \ - return EINA_FALSE; \ - } + return ret_value; \ + } } while (0) // Used to update extensions ORD(glGetString); @@ -3366,6 +3367,7 @@ _evgl_load_gles3_apis(void *dl_handle, Evas_GL_API *funcs, int minor_version) if (minor_version > 0) { //GLES 3.1 + ret_value = EINA_TRUE; ORD(glDispatchCompute); ORD(glDispatchComputeIndirect); ORD(glDrawArraysIndirect); @@ -3435,15 +3437,15 @@ _evgl_load_gles3_apis(void *dl_handle, Evas_GL_API *funcs, int minor_version) ORD(glVertexAttribBinding); ORD(glVertexBindingDivisor); } - #undef ORD + return EINA_TRUE; } static Eina_Bool -_evgl_gles3_api_init(int minor_version) +_evgl_gles3_api_init(int minor_version, void *(*get_proc_address)(const char *)) { static Eina_Bool _initialized = EINA_FALSE; if (_initialized) return EINA_TRUE; @@ -3475,7 +3477,7 @@ _evgl_gles3_api_init(int minor_version) return EINA_FALSE; } - if (!_evgl_load_gles3_apis(_gles3_handle, &_gles3_api, minor_version)) + if (!_evgl_load_gles3_apis(_gles3_handle, &_gles3_api, minor_version, get_proc_address)) { return EINA_FALSE; } @@ -3484,26 +3486,23 @@ _evgl_gles3_api_init(int minor_version) return EINA_TRUE; } - void -_evgl_api_gles3_get(Evas_GL_API *funcs, Eina_Bool debug) +_evgl_api_gles3_get(Evas_GL_API *funcs, void *(*get_proc_address)(const char *), + Eina_Bool debug, int minor_version) { - const char *ret = (const char *) glGetString(GL_VERSION); - int minor_version = ret[12] - '0'; + int effective_minor = minor_version; - if (minor_version > 9 || minor_version < 0) - { - ERR("OpenGL ES version is invalid."); - return; - } - - if (!_evgl_gles3_api_init(minor_version)) + if (!_evgl_gles3_api_init(minor_version, get_proc_address)) return; + // Hack for NVIDIA. See also evas_gl_core.c:_context_ext_check() + if (!_gles3_api.glVertexBindingDivisor) + effective_minor = 0; + if (debug) - _debug_gles3_api_get(funcs, minor_version); + _debug_gles3_api_get(funcs, effective_minor); else - _normal_gles3_api_get(funcs, minor_version); + _normal_gles3_api_get(funcs, effective_minor); if (evgl_engine->direct_scissor_off) _direct_scissor_off_api_get(funcs); diff --git a/src/modules/evas/engines/gl_common/evas_gl_common.h b/src/modules/evas/engines/gl_common/evas_gl_common.h index 59549efe84..d78c21a5b1 100644 --- a/src/modules/evas/engines/gl_common/evas_gl_common.h +++ b/src/modules/evas/engines/gl_common/evas_gl_common.h @@ -543,7 +543,7 @@ typedef void (*Evas_Gl_Symbols)(void *(*GetProcAddress)(const char *sym)); EAPI void __evas_gl_err(int err, const char *file, const char *func, int line, const char *op); -int evas_gl_common_version_check(void); +int evas_gl_common_version_check(int *minor_version); void evas_gl_common_tiling_start(Evas_Engine_GL_Context *gc, int rot, int gw, int gh, int cx, int cy, int cw, int ch, diff --git a/src/modules/evas/engines/gl_common/evas_gl_context.c b/src/modules/evas/engines/gl_common/evas_gl_context.c index ec8fd826b6..45ea67776b 100644 --- a/src/modules/evas/engines/gl_common/evas_gl_context.c +++ b/src/modules/evas/engines/gl_common/evas_gl_context.c @@ -493,7 +493,7 @@ matrix_ortho(GLfloat *m, } int -evas_gl_common_version_check(void) +evas_gl_common_version_check(int *minor_version) { char *version; char *tmp; @@ -506,6 +506,8 @@ evas_gl_common_version_check(void) * GL_VERSION is used to get the version of the connection */ + if (minor_version) *minor_version = 0; + version = (char *)glGetString(GL_VERSION); if (!version) { @@ -541,6 +543,12 @@ evas_gl_common_version_check(void) if (strstr(version, "OpenGL ES 3")) { /* Supported */ + if (minor_version) + { + if ((version[11] == '.') && isdigit(version[12])) + *minor_version = atoi(&version[12]); + else *minor_version = 0; + } return 3; } @@ -549,6 +557,13 @@ evas_gl_common_version_check(void) if (strstr(version, "OpenGL ES ")) { /* Supported */ + if (minor_version) + { + if ((version[10] == '2') && + (version[11] == '.') && isdigit(version[12])) + *minor_version = atoi(&version[12]); + else *minor_version = 0; + } return 2; } @@ -590,22 +605,32 @@ evas_gl_common_version_check(void) fail: free(version); - if (((major == 1) && (minor >= 4)) || (major >= 2)) + // OpenGL 4.5 is supposed to be a superset of GLES 3.1 + if ((major == 4) && (minor >= 5)) { - /* Map GL to GLES version: Refer http://en.wikipedia.org/wiki/OpenGL_ES */ - if ((major >= 4) && (minor >= 3)) - return 3; - else if ((major > 3) || ((major == 3) && (minor >= 3))) /* >= 3.3 */ - { - const char *exts = NULL; - int num = 0; - - if (_has_ext("GL_ARB_ES3_compatibility", &exts, &num)) - return 3; - } - return 2; /* emulated support */ + if (minor_version) *minor_version = 1; + return 3; } + // OpenGL 4.3 is supposed to be a superset of GLES 3.0 + if ((major == 4) && (minor >= 3)) + return 3; + + // Extension GL_ARB_ES3_compatibility means OpenGL is a superset of GLES 3.0 + if ((major > 3) || ((major == 3) && (minor >= 3))) + { + const char *exts = NULL; + int num = 0; + + if (_has_ext("GL_ARB_ES3_compatibility", &exts, &num)) + return 3; + } + + // OpenGL >= 1.4 is a superset of the features of GLES 2 (albeit not an + // exact function match) + if (((major == 1) && (minor >= 4)) || (major >= 2)) + return 2; /* emulated support */ + return 0; } @@ -787,7 +812,7 @@ evas_gl_common_context_new(void) if (!glsym_glGetStringi) glsym_glGetStringi = dlsym(RTLD_DEFAULT, "glGetStringi"); - gles_version = evas_gl_common_version_check(); + gles_version = evas_gl_common_version_check(NULL); if (!gles_version) return NULL; gc = calloc(1, sizeof(Evas_Engine_GL_Context)); diff --git a/src/modules/evas/engines/gl_common/evas_gl_core.c b/src/modules/evas/engines/gl_common/evas_gl_core.c index b50adfcfac..2c91183baf 100644 --- a/src/modules/evas/engines/gl_common/evas_gl_core.c +++ b/src/modules/evas/engines/gl_common/evas_gl_core.c @@ -494,7 +494,7 @@ _fbo_surface_cap_test(GLint color_ifmt, GLenum color_fmt, int depth_stencil = 0; int fb_status = 0; int w = 2, h = 2; // Test it with a simple (2,2) surface. Should I test it with NPOT? - Evas_GL_Context_Version ver = evas_gl_common_version_check(); + Evas_GL_Context_Version ver = evas_gl_common_version_check(NULL); // Gen FBO glGenFramebuffers(1, &fbo); @@ -912,7 +912,7 @@ _surface_cap_init(void *eng_data) evgl_engine->caps.max_h = max_size; DBG("Max Surface Width: %d Height: %d", evgl_engine->caps.max_w, evgl_engine->caps.max_h); - gles_version = evas_gl_common_version_check(); + gles_version = evas_gl_common_version_check(NULL); // Check for MSAA support if (gles_version == 3) @@ -1019,6 +1019,20 @@ _context_ext_check(EVGL_Context *ctx) } #endif + if (ctx->version == EVAS_GL_GLES_3_X) + { + /* HACK, as of 2017/03/08: + * Some NVIDIA drivers pretend to support GLES 3.1 with EGL but in + * fact none of the new functions are available, neither through + * dlsym() nor eglGetProcAddress(). GLX/OpenGL should work though. + * This is a fixup for glGetString(GL_VERSION). + */ + if (!gles3_funcs->glVertexBindingDivisor) + ctx->version_minor = 0; + else + ctx->version_minor = 1; + } + ctx->extension_checked = 1; return 1; @@ -2315,6 +2329,7 @@ evgl_context_create(void *eng_data, EVGL_Context *share_ctx, // Set default values ctx->version = version; + ctx->version_minor = 0; ctx->scissor_coord[0] = 0; ctx->scissor_coord[1] = 0; ctx->scissor_coord[2] = evgl_engine->caps.max_w; @@ -3116,6 +3131,7 @@ Evas_GL_API * evgl_api_get(void *eng_data, Evas_GL_Context_Version version, Eina_Bool alloc_only) { Evas_GL_API *api = NULL; + int minor_version = 0; if (version == EVAS_GL_GLES_2_X) { @@ -3129,10 +3145,15 @@ evgl_api_get(void *eng_data, Evas_GL_Context_Version version, Eina_Bool alloc_on } else if (version == EVAS_GL_GLES_3_X) { + if (evas_gl_common_version_check(&minor_version) < 3) + { + ERR("OpenGL ES 3.x is not supported."); + return NULL; + } if (!gles3_funcs) gles3_funcs = calloc(1, EVAS_GL_API_STRUCT_SIZE); api = gles3_funcs; } - else return NULL; + if (!api) return NULL; if (alloc_only && (api->version == EVAS_GL_API_VERSION)) return api; @@ -3154,7 +3175,14 @@ evgl_api_get(void *eng_data, Evas_GL_Context_Version version, Eina_Bool alloc_on } else if (version == EVAS_GL_GLES_3_X) { - _evgl_api_gles3_get(api, evgl_engine->api_debug_mode); + void *(*get_proc_address)(const char *) = NULL; + const char *egl_exts; + + egl_exts = evgl_engine->funcs->ext_string_get(eng_data); + if (egl_exts && strstr(egl_exts, "EGL_KHR_get_all_proc_addresses")) + get_proc_address = evgl_engine->funcs->proc_address_get; + + _evgl_api_gles3_get(api, get_proc_address, evgl_engine->api_debug_mode, minor_version); evgl_api_gles3_ext_get(api, evgl_engine->funcs->proc_address_get, evgl_engine->funcs->ext_string_get(eng_data)); } diff --git a/src/modules/evas/engines/gl_common/evas_gl_core_private.h b/src/modules/evas/engines/gl_common/evas_gl_core_private.h index 71deebe4c4..e6afff639c 100644 --- a/src/modules/evas/engines/gl_common/evas_gl_core_private.h +++ b/src/modules/evas/engines/gl_common/evas_gl_core_private.h @@ -163,6 +163,7 @@ struct _EVGL_Context EVGLNative_Context context; Evas_GL_Context_Version version; + int version_minor; // Context FBO GLuint surface_fbo; @@ -352,7 +353,7 @@ extern EVGL_Engine *evgl_engine; // Internally used functions extern void _evgl_api_gles2_get(Evas_GL_API *api, Eina_Bool debug); extern void _evgl_api_gles1_get(Evas_GL_API *api, Eina_Bool debug); -extern void _evgl_api_gles3_get(Evas_GL_API *api, Eina_Bool debug); +extern void _evgl_api_gles3_get(Evas_GL_API *funcs, void *(*get_proc_address)(const char *), Eina_Bool debug, int minor_version); extern EVGL_Resource *_evgl_tls_resource_get(void); extern EVGL_Resource *_evgl_tls_resource_create(void *data); extern void _evgl_tls_resource_destroy(void *data); diff --git a/src/modules/evas/engines/gl_x11/evas_engine.c b/src/modules/evas/engines/gl_x11/evas_engine.c index 94bfdedbb9..13929aaf19 100644 --- a/src/modules/evas/engines/gl_x11/evas_engine.c +++ b/src/modules/evas/engines/gl_x11/evas_engine.c @@ -494,6 +494,15 @@ evgl_eng_context_create(void *data, void *share_ctx, Evas_GL_Context_Version ver EGLContext context = EGL_NO_CONTEXT; int context_attrs[3]; + /* Upgrade GLES 2 to GLES 3. + * + * FIXME: Maybe we don't want to do this, unless we have no choice. + * An alternative would be to use eglCreateImage() to share the indirect + * rendering FBO between two contexts of incompatible version. For now, + * we always upgrade the real context version to GLES 3 when it's available. + * But this leads to some issues, namely that the list of extensions is + * different, and MSAA surfaces also work differently. + */ if (eng_get_ob(re)->gles3 && (version >= EVAS_GL_GLES_2_X)) version = 3;