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).
This commit is contained in:
Jean-Philippe Andre 2017-03-08 11:52:59 +09:00
parent 9dcf2d3965
commit 265c851a8f
8 changed files with 139 additions and 60 deletions

View File

@ -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);

View File

@ -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);
}

View File

@ -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);

View File

@ -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,

View File

@ -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));

View File

@ -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));
}

View File

@ -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);

View File

@ -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;