diff --git a/src/dispatch_common.c b/src/dispatch_common.c index 6d27cb1..7b976a5 100644 --- a/src/dispatch_common.c +++ b/src/dispatch_common.c @@ -117,6 +117,20 @@ struct api { /** dlopen() return value for libGLESv2.so.2 */ void *gles2_handle; + + /** + * This value gets incremented when any thread is in + * glBegin()/glEnd() called through epoxy. + * + * We're not guaranteed to be called through our wrapper, so the + * conservative paths also try to handle the failure cases they'll + * see if begin_count didn't reflect reality. It's also a bit of + * a bug that the conservative paths might return success because + * some other thread was in epoxy glBegin/glEnd while our thread + * is trying to resolve, but given that it's basically just for + * informative error messages, we shouldn't need to care. + */ + int begin_count; }; static struct api api = { @@ -159,18 +173,34 @@ PUBLIC bool epoxy_is_desktop_gl(void) { const char *es_prefix = "OpenGL ES "; - const char *version = (const char *)glGetString(GL_VERSION); + const char *version; + + if (api.begin_count) + return true; + + version = (const char *)glGetString(GL_VERSION); + + /* If we didn't get a version back, there are only two things that + * could have happened: either malloc failure (which basically + * doesn't exist), or we were called within a glBegin()/glEnd(). + * Assume the second, which only exists for desktop GL. + */ + if (!version) + return true; return strncmp(es_prefix, version, strlen(es_prefix)); } -PUBLIC int -epoxy_gl_version(void) +static int +epoxy_internal_gl_version(int error_version) { const char *version = (const char *)glGetString(GL_VERSION); GLint major, minor; int scanf_count; + if (!version) + return error_version; + /* skip to version number */ while (!isdigit(*version) && *version != '\0') version++; @@ -185,6 +215,21 @@ epoxy_gl_version(void) return 10 * major + minor; } +PUBLIC int +epoxy_gl_version(void) +{ + return epoxy_internal_gl_version(0); +} + +PUBLIC int +epoxy_conservative_gl_version(void) +{ + if (api.begin_count) + return 100; + + return epoxy_internal_gl_version(100); +} + /** * If we can determine the GLX version from the current context, then * return that, otherwise return a version that will just send us on @@ -268,16 +313,21 @@ epoxy_extension_in_string(const char *extension_list, const char *ext) return ptr != NULL; } -PUBLIC bool -epoxy_has_gl_extension(const char *ext) +static bool +epoxy_internal_has_gl_extension(const char *ext, bool invalid_op_mode) { if (epoxy_gl_version() < 30) { - return epoxy_extension_in_string((const char *)glGetString(GL_EXTENSIONS), - ext); + const char *exts = (const char *)glGetString(GL_EXTENSIONS); + if (!exts) + return invalid_op_mode; + return epoxy_extension_in_string(exts, ext); } else { int num_extensions; glGetIntegerv(GL_NUM_EXTENSIONS, &num_extensions); + if (num_extensions == 0) + return invalid_op_mode; + for (int i = 0; i < num_extensions; i++) { const char *gl_ext = (const char *)glGetStringi(GL_EXTENSIONS, i); if (strcmp(ext, gl_ext) == 0) @@ -288,6 +338,29 @@ epoxy_has_gl_extension(const char *ext) } } +/** + * Returns true if the given GL extension is supported in the current context. + * + * Note that this function can't be called from within glBegin()/glEnd(). + * + * \sa epoxy_has_egl_extension() + * \sa epoxy_has_glx_extension() + */ +PUBLIC bool +epoxy_has_gl_extension(const char *ext) +{ + return epoxy_internal_has_gl_extension(ext, false); +} + +bool +epoxy_conservative_has_gl_extension(const char *ext) +{ + if (api.begin_count) + return true; + + return epoxy_internal_has_gl_extension(ext, true); +} + bool epoxy_conservative_has_egl_extension(const char *ext) { @@ -424,3 +497,23 @@ epoxy_print_failure_reasons(const char *name, for (i = 0; providers[i] != 0; i++) puts(provider_names[providers[i]]); } + +PUBLIC void +epoxy_glBegin(GLenum primtype) +{ + pthread_mutex_lock(&api.mutex); + api.begin_count++; + pthread_mutex_unlock(&api.mutex); + + epoxy_glBegin_unwrapped(primtype); +} + +PUBLIC void +epoxy_glEnd(void) +{ + epoxy_glEnd_unwrapped(); + + pthread_mutex_lock(&api.mutex); + api.begin_count--; + pthread_mutex_unlock(&api.mutex); +} diff --git a/src/dispatch_common.h b/src/dispatch_common.h index 34163ec..f66f165 100644 --- a/src/dispatch_common.h +++ b/src/dispatch_common.h @@ -41,6 +41,8 @@ void *epoxy_gles1_dlsym(const char *name); void *epoxy_gles2_dlsym(const char *name); void *epoxy_get_proc_address(const char *name); +int epoxy_conservative_gl_version(void); +bool epoxy_conservative_has_gl_extension(const char *name); int epoxy_conservative_glx_version(void); bool epoxy_conservative_has_glx_extension(const char *name); int epoxy_conservative_egl_version(void); @@ -48,3 +50,6 @@ bool epoxy_conservative_has_egl_extension(const char *name); void epoxy_print_failure_reasons(const char *name, const char **provider_names, const int *providers); + +void epoxy_glBegin_unwrapped(GLenum primtype); +void epoxy_glEnd_unwrapped(void); diff --git a/src/gen_dispatch.py b/src/gen_dispatch.py index e2c5308..048785f 100755 --- a/src/gen_dispatch.py +++ b/src/gen_dispatch.py @@ -130,6 +130,14 @@ class Generator(object): # provided the name of the symbol to be requested. self.provider_loader = {} + # These are functions with hand-written wrapper code in + # dispatch_common.c. Their dispatch stubs will be replaced + # with non-public symbols with a "_unwrapped" suffix. + self.wrapped_functions = { + 'glBegin', + 'glEnd' + } + def all_text_until_element_name(self, element, element_name): text = '' @@ -265,7 +273,7 @@ class Generator(object): loader = 'epoxy_gl_dlsym({0})' else: loader = 'epoxy_get_proc_address({0})' - condition += ' && epoxy_gl_version() >= {0}'.format(version) + condition += ' && epoxy_conservative_gl_version() >= {0}'.format(version) elif api == 'gles2': human_name = 'OpenGL ES {0}'.format(feature.get('number')) condition = '!epoxy_is_desktop_gl() && epoxy_gl_version() >= {0}'.format(version) @@ -319,7 +327,7 @@ class Generator(object): self.process_require_statements(extension, condition, loader, human_name) if 'gl' in apis: human_name = 'GL extension \\"{0}\\"'.format(extname) - condition = 'epoxy_has_gl_extension("{0}")'.format(extname) + condition = 'epoxy_conservative_has_gl_extension("{0}")'.format(extname) loader = 'epoxy_get_proc_address({0})' self.process_require_statements(extension, condition, loader, human_name) @@ -460,8 +468,15 @@ class Generator(object): dispatch_table_entry = 'dispatch_table->p{0}'.format(alias_name) - self.outln('PUBLIC {0}'.format(func.ret_type)) - self.outln('epoxy_{0}({1})'.format(func.name, func.args_decl)) + if func.name in self.wrapped_functions: + function_name = func.name + '_unwrapped' + public = '' + else: + function_name = func.name + public = 'PUBLIC ' + + self.outln('{0}{1}'.format(public, func.ret_type)) + self.outln('epoxy_{0}({1})'.format(function_name, func.args_decl)) self.outln('{') self.outln(' if (!{0})'.format(dispatch_table_entry)) self.outln(' {0} = epoxy_{1}_resolver();'.format(dispatch_table_entry, diff --git a/test/Makefile.am b/test/Makefile.am index d113f6e..56b35bf 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -49,8 +49,6 @@ TESTS = \ headerguards \ $() -XFAIL_TESTS = glx_beginend - check_PROGRAMS = $(TESTS) egl_has_extension_nocontext_LDFLAGS = $(X11_LIBS) $(EPOXY) libegl_common.la