summaryrefslogtreecommitdiff
path: root/src/dispatch_common.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/dispatch_common.c')
-rw-r--r--src/dispatch_common.c923
1 files changed, 923 insertions, 0 deletions
diff --git a/src/dispatch_common.c b/src/dispatch_common.c
new file mode 100644
index 0000000..b3e4f5f
--- /dev/null
+++ b/src/dispatch_common.c
@@ -0,0 +1,923 @@
+/*
+ * Copyright © 2013-2014 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+/**
+ * \mainpage Epoxy
+ *
+ * \section intro_sec Introduction
+ *
+ * Epoxy is a library for handling OpenGL function pointer management for
+ * you.
+ *
+ * It hides the complexity of `dlopen()`, `dlsym()`, `glXGetProcAddress()`,
+ * `eglGetProcAddress()`, etc. from the app developer, with very little
+ * knowledge needed on their part. They get to read GL specs and write
+ * code using undecorated function names like `glCompileShader()`.
+ *
+ * Don't forget to check for your extensions or versions being present
+ * before you use them, just like before! We'll tell you what you forgot
+ * to check for instead of just segfaulting, though.
+ *
+ * \section features_sec Features
+ *
+ * - Automatically initializes as new GL functions are used.
+ * - GL 4.6 core and compatibility context support.
+ * - GLES 1/2/3 context support.
+ * - Knows about function aliases so (e.g.) `glBufferData()` can be
+ * used with `GL_ARB_vertex_buffer_object` implementations, along
+ * with GL 1.5+ implementations.
+ * - EGL, GLX, and WGL support.
+ * - Can be mixed with non-epoxy GL usage.
+ *
+ * \section using_sec Using Epoxy
+ *
+ * Using Epoxy should be as easy as replacing:
+ *
+ * ```cpp
+ * #include <GL/gl.h>
+ * #include <GL/glx.h>
+ * #include <GL/glext.h>
+ * ```
+ *
+ * with:
+ *
+ * ```cpp
+ * #include <epoxy/gl.h>
+ * #include <epoxy/glx.h>
+ * ```
+ *
+ * \subsection using_include_sec Headers
+ *
+ * Epoxy comes with the following public headers:
+ *
+ * - `epoxy/gl.h` - For GL API
+ * - `epoxy/egl.h` - For EGL API
+ * - `epoxy/glx.h` - For GLX API
+ * - `epoxy/wgl.h` - For WGL API
+ *
+ * \section links_sec Additional links
+ *
+ * The latest version of the Epoxy code is available on [GitHub](https://github.com/anholt/libepoxy).
+ *
+ * For bug reports and enhancements, please use the [Issues](https://github.com/anholt/libepoxy/issues)
+ * link.
+ *
+ * The scope of this API reference does not include the documentation for
+ * OpenGL and OpenGL ES. For more information on those programming interfaces
+ * please visit:
+ *
+ * - [Khronos](https://www.khronos.org/)
+ * - [OpenGL page on Khronos.org](https://www.khronos.org/opengl/)
+ * - [OpenGL ES page on Khronos.org](https://www.khronos.org/opengles/)
+ * - [docs.GL](http://docs.gl/)
+ */
+
+/**
+ * @file dispatch_common.c
+ *
+ * @brief Implements common code shared by the generated GL/EGL/GLX dispatch code.
+ *
+ * A collection of some important specs on getting GL function pointers.
+ *
+ * From the linux GL ABI (http://www.opengl.org/registry/ABI/):
+ *
+ * "3.4. The libraries must export all OpenGL 1.2, GLU 1.3, GLX 1.3, and
+ * ARB_multitexture entry points statically.
+ *
+ * 3.5. Because non-ARB extensions vary so widely and are constantly
+ * increasing in number, it's infeasible to require that they all be
+ * supported, and extensions can always be added to hardware drivers
+ * after the base link libraries are released. These drivers are
+ * dynamically loaded by libGL, so extensions not in the base
+ * library must also be obtained dynamically.
+ *
+ * 3.6. To perform the dynamic query, libGL also must export an entry
+ * point called
+ *
+ * void (*glXGetProcAddressARB(const GLubyte *))();
+ *
+ * The full specification of this function is available separately. It
+ * takes the string name of a GL or GLX entry point and returns a pointer
+ * to a function implementing that entry point. It is functionally
+ * identical to the wglGetProcAddress query defined by the Windows OpenGL
+ * library, except that the function pointers returned are context
+ * independent, unlike the WGL query."
+ *
+ * From the EGL 1.4 spec:
+ *
+ * "Client API function pointers returned by eglGetProcAddress are
+ * independent of the display and the currently bound client API context,
+ * and may be used by any client API context which supports the extension.
+ *
+ * eglGetProcAddress may be queried for all of the following functions:
+ *
+ * • All EGL and client API extension functions supported by the
+ * implementation (whether those extensions are supported by the current
+ * client API context or not). This includes any mandatory OpenGL ES
+ * extensions.
+ *
+ * eglGetProcAddress may not be queried for core (non-extension) functions
+ * in EGL or client APIs 20 .
+ *
+ * For functions that are queryable with eglGetProcAddress,
+ * implementations may choose to also export those functions statically
+ * from the object libraries im- plementing those functions. However,
+ * portable clients cannot rely on this behavior.
+ *
+ * From the GLX 1.4 spec:
+ *
+ * "glXGetProcAddress may be queried for all of the following functions:
+ *
+ * • All GL and GLX extension functions supported by the implementation
+ * (whether those extensions are supported by the current context or
+ * not).
+ *
+ * • All core (non-extension) functions in GL and GLX from version 1.0 up
+ * to and including the versions of those specifications supported by
+ * the implementation, as determined by glGetString(GL VERSION) and
+ * glXQueryVersion queries."
+ */
+
+#include <assert.h>
+#include <stdlib.h>
+#ifdef _WIN32
+#include <windows.h>
+#else
+#include <dlfcn.h>
+#include <err.h>
+#include <pthread.h>
+#endif
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+
+#include "dispatch_common.h"
+
+#if defined(__APPLE__)
+#define GLX_LIB "/opt/X11/lib/libGL.1.dylib"
+#define OPENGL_LIB "/System/Library/Frameworks/OpenGL.framework/Versions/Current/OpenGL"
+#define GLES1_LIB "libGLESv1_CM.so"
+#define GLES2_LIB "libGLESv2.so"
+#elif defined(__ANDROID__)
+#define GLX_LIB "libGLESv2.so"
+#define EGL_LIB "libEGL.so"
+#define GLES1_LIB "libGLESv1_CM.so"
+#define GLES2_LIB "libGLESv2.so"
+#elif defined(_WIN32)
+#define EGL_LIB "libEGL.dll"
+#define GLES1_LIB "libGLES_CM.dll"
+#define GLES2_LIB "libGLESv2.dll"
+#define OPENGL_LIB "OPENGL32"
+#else
+#define GLVND_GLX_LIB "libGLX.so.1"
+#define GLX_LIB "libGL.so.1"
+#define EGL_LIB "libEGL.so.1"
+#define GLES1_LIB "libGLESv1_CM.so.1"
+#define GLES2_LIB "libGLESv2.so.2"
+#define OPENGL_LIB "libOpenGL.so.0"
+#endif
+
+#ifdef __GNUC__
+#define CONSTRUCT(_func) static void _func (void) __attribute__((constructor));
+#define DESTRUCT(_func) static void _func (void) __attribute__((destructor));
+#elif defined (_MSC_VER) && (_MSC_VER >= 1500)
+#define CONSTRUCT(_func) \
+ static void _func(void); \
+ static int _func ## _wrapper(void) { _func(); return 0; } \
+ __pragma(section(".CRT$XCU",read)) \
+ __declspec(allocate(".CRT$XCU")) static int (* _array ## _func)(void) = _func ## _wrapper;
+
+#define DESTRUCT(_func) \
+ static void _func(void); \
+ static int _func ## _constructor(void) { atexit (_func); return 0; } \
+ __pragma(section(".CRT$XCU",read)) \
+ __declspec(allocate(".CRT$XCU")) static int (* _array ## _func)(void) = _func ## _constructor;
+
+#else
+#error "You will need constructor support for your compiler"
+#endif
+
+struct api {
+#ifndef _WIN32
+ /*
+ * Locking for making sure we don't double-dlopen().
+ */
+ pthread_mutex_t mutex;
+#endif
+
+ /*
+ * dlopen() return value for the GLX API. This is libGLX.so.1 if the
+ * runtime is glvnd-enabled, else libGL.so.1
+ */
+ void *glx_handle;
+
+ /*
+ * dlopen() return value for the desktop GL library.
+ *
+ * On Windows this is OPENGL32. On OSX this is classic libGL. On Linux
+ * this is either libOpenGL (if the runtime is glvnd-enabled) or
+ * classic libGL.so.1
+ */
+ void *gl_handle;
+
+ /* dlopen() return value for libEGL.so.1 */
+ void *egl_handle;
+
+ /* dlopen() return value for libGLESv1_CM.so.1 */
+ void *gles1_handle;
+
+ /* 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.
+ */
+ long begin_count;
+};
+
+static struct api api = {
+#ifndef _WIN32
+ .mutex = PTHREAD_MUTEX_INITIALIZER,
+#else
+ 0,
+#endif
+};
+
+static bool library_initialized;
+
+static bool epoxy_current_context_is_glx(void);
+
+#if PLATFORM_HAS_EGL
+static EGLenum
+epoxy_egl_get_current_gl_context_api(void);
+#endif
+
+CONSTRUCT (library_init)
+
+static void
+library_init(void)
+{
+ library_initialized = true;
+}
+
+static bool
+get_dlopen_handle(void **handle, const char *lib_name, bool exit_on_fail, bool load)
+{
+ if (*handle)
+ return true;
+
+ if (!library_initialized) {
+ fputs("Attempting to dlopen() while in the dynamic linker.\n", stderr);
+ abort();
+ }
+
+#ifdef _WIN32
+ *handle = LoadLibraryA(lib_name);
+#else
+ pthread_mutex_lock(&api.mutex);
+ if (!*handle) {
+ int flags = RTLD_LAZY | RTLD_LOCAL;
+ if (!load)
+ flags |= RTLD_NOLOAD;
+
+ *handle = dlopen(lib_name, flags);
+ if (!*handle) {
+ if (exit_on_fail) {
+ fprintf(stderr, "Couldn't open %s: %s\n", lib_name, dlerror());
+ abort();
+ } else {
+ (void)dlerror();
+ }
+ }
+ }
+ pthread_mutex_unlock(&api.mutex);
+#endif
+
+ return *handle != NULL;
+}
+
+static void *
+do_dlsym(void **handle, const char *name, bool exit_on_fail)
+{
+ void *result;
+ const char *error = "";
+
+#ifdef _WIN32
+ result = GetProcAddress(*handle, name);
+#else
+ result = dlsym(*handle, name);
+ if (!result)
+ error = dlerror();
+#endif
+ if (!result && exit_on_fail) {
+ fprintf(stderr, "%s() not found: %s\n", name, error);
+ abort();
+ }
+
+ return result;
+}
+
+/**
+ * @brief Checks whether we're using OpenGL or OpenGL ES
+ *
+ * @return `true` if we're using OpenGL
+ */
+bool
+epoxy_is_desktop_gl(void)
+{
+ const char *es_prefix = "OpenGL ES";
+ const char *version;
+
+#if PLATFORM_HAS_EGL
+ /* PowerVR's OpenGL ES implementation (and perhaps other) don't
+ * comply with the standard, which states that
+ * "glGetString(GL_VERSION)" should return a string starting with
+ * "OpenGL ES". Therefore, to distinguish desktop OpenGL from
+ * OpenGL ES, we must also check the context type through EGL (we
+ * can do that as PowerVR is only usable through EGL).
+ */
+ if (!epoxy_current_context_is_glx()) {
+ switch (epoxy_egl_get_current_gl_context_api()) {
+ case EGL_OPENGL_API: return true;
+ case EGL_OPENGL_ES_API: return false;
+ case EGL_NONE:
+ default: break;
+ }
+ }
+#endif
+
+ 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));
+}
+
+static int
+epoxy_internal_gl_version(GLenum version_string, int error_version)
+{
+ const char *version = (const char *)glGetString(version_string);
+ GLint major, minor, factor;
+ int scanf_count;
+
+ if (!version)
+ return error_version;
+
+ /* skip to version number */
+ while (!isdigit(*version) && *version != '\0')
+ version++;
+
+ /* Interpret version number */
+ scanf_count = sscanf(version, "%i.%i", &major, &minor);
+ if (scanf_count != 2) {
+ fprintf(stderr, "Unable to interpret GL_VERSION string: %s\n",
+ version);
+ abort();
+ }
+
+ if (minor >= 10)
+ factor = 100;
+ else
+ factor = 10;
+
+ return factor * major + minor;
+}
+
+/**
+ * @brief Returns the version of OpenGL we are using
+ *
+ * The version is encoded as:
+ *
+ * ```
+ *
+ * version = major * 10 + minor
+ *
+ * ```
+ *
+ * So it can be easily used for version comparisons.
+ *
+ * @return The encoded version of OpenGL we are using
+ */
+int
+epoxy_gl_version(void)
+{
+ return epoxy_internal_gl_version(GL_VERSION, 0);
+}
+
+int
+epoxy_conservative_gl_version(void)
+{
+ if (api.begin_count)
+ return 100;
+
+ return epoxy_internal_gl_version(GL_VERSION, 100);
+}
+
+/**
+ * @brief Returns the version of the GL Shading Language we are using
+ *
+ * The version is encoded as:
+ *
+ * ```
+ *
+ * version = major * 100 + minor
+ *
+ * ```
+ *
+ * So it can be easily used for version comparisons.
+ *
+ * @return The encoded version of the GL Shading Language we are using
+ */
+int
+epoxy_glsl_version(void)
+{
+ if (epoxy_gl_version() >= 20 ||
+ epoxy_has_gl_extension ("GL_ARB_shading_language_100"))
+ return epoxy_internal_gl_version(GL_SHADING_LANGUAGE_VERSION, 0);
+
+ return 0;
+}
+
+/**
+ * @brief Checks for the presence of an extension in an OpenGL extension string
+ *
+ * @param extension_list The string containing the list of extensions to check
+ * @param ext The name of the GL extension
+ * @return `true` if the extension is available'
+ *
+ * @note If you are looking to check whether a normal GL, EGL or GLX extension
+ * is supported by the client, this probably isn't the function you want.
+ *
+ * Some parts of the spec for OpenGL and friends will return an OpenGL formatted
+ * extension string that is separate from the usual extension strings for the
+ * spec. This function provides easy parsing of those strings.
+ *
+ * @see epoxy_has_gl_extension()
+ * @see epoxy_has_egl_extension()
+ * @see epoxy_has_glx_extension()
+ */
+bool
+epoxy_extension_in_string(const char *extension_list, const char *ext)
+{
+ const char *ptr = extension_list;
+ int len;
+
+ if (!ext)
+ return false;
+
+ len = strlen(ext);
+
+ if (extension_list == NULL || *extension_list == '\0')
+ return false;
+
+ /* Make sure that don't just find an extension with our name as a prefix. */
+ while (true) {
+ ptr = strstr(ptr, ext);
+ if (!ptr)
+ return false;
+
+ if (ptr[len] == ' ' || ptr[len] == 0)
+ return true;
+ ptr += len;
+ }
+}
+
+static bool
+epoxy_internal_has_gl_extension(const char *ext, bool invalid_op_mode)
+{
+ if (epoxy_gl_version() < 30) {
+ 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;
+ int i;
+
+ glGetIntegerv(GL_NUM_EXTENSIONS, &num_extensions);
+ if (num_extensions == 0)
+ return invalid_op_mode;
+
+ for (i = 0; i < num_extensions; i++) {
+ const char *gl_ext = (const char *)glGetStringi(GL_EXTENSIONS, i);
+ if (!gl_ext)
+ return false;
+ if (strcmp(ext, gl_ext) == 0)
+ return true;
+ }
+
+ return false;
+ }
+}
+
+bool
+epoxy_load_glx(bool exit_if_fails, bool load)
+{
+#if PLATFORM_HAS_GLX
+# ifdef GLVND_GLX_LIB
+ /* prefer the glvnd library if it exists */
+ if (!api.glx_handle)
+ get_dlopen_handle(&api.glx_handle, GLVND_GLX_LIB, false, load);
+# endif
+ if (!api.glx_handle)
+ get_dlopen_handle(&api.glx_handle, GLX_LIB, exit_if_fails, load);
+#endif
+ return api.glx_handle != NULL;
+}
+
+void *
+epoxy_conservative_glx_dlsym(const char *name, bool exit_if_fails)
+{
+#if PLATFORM_HAS_GLX
+ if (epoxy_load_glx(exit_if_fails, exit_if_fails))
+ return do_dlsym(&api.glx_handle, name, exit_if_fails);
+#endif
+ return NULL;
+}
+
+/**
+ * Tests whether the currently bound context is EGL or GLX, trying to
+ * avoid loading libraries unless necessary.
+ */
+static bool
+epoxy_current_context_is_glx(void)
+{
+#if !PLATFORM_HAS_GLX
+ return false;
+#else
+ void *sym;
+
+ sym = epoxy_conservative_glx_dlsym("glXGetCurrentContext", false);
+ if (sym) {
+ if (glXGetCurrentContext())
+ return true;
+ } else {
+ (void)dlerror();
+ }
+
+#if PLATFORM_HAS_EGL
+ sym = epoxy_conservative_egl_dlsym("eglGetCurrentContext", false);
+ if (sym) {
+ if (epoxy_egl_get_current_gl_context_api() != EGL_NONE)
+ return false;
+ } else {
+ (void)dlerror();
+ }
+#endif /* PLATFORM_HAS_EGL */
+
+ return false;
+#endif /* PLATFORM_HAS_GLX */
+}
+
+/**
+ * @brief Returns true if the given GL extension is supported in the current context.
+ *
+ * @param ext The name of the GL extension
+ * @return `true` if the extension is available
+ *
+ * @note that this function can't be called from within `glBegin()` and `glEnd()`.
+ *
+ * @see epoxy_has_egl_extension()
+ * @see epoxy_has_glx_extension()
+ */
+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_load_egl(bool exit_if_fails, bool load)
+{
+#if PLATFORM_HAS_EGL
+ return get_dlopen_handle(&api.egl_handle, EGL_LIB, exit_if_fails, load);
+#else
+ return false;
+#endif
+}
+
+void *
+epoxy_conservative_egl_dlsym(const char *name, bool exit_if_fails)
+{
+#if PLATFORM_HAS_EGL
+ if (epoxy_load_egl(exit_if_fails, exit_if_fails))
+ return do_dlsym(&api.egl_handle, name, exit_if_fails);
+#endif
+ return NULL;
+}
+
+void *
+epoxy_egl_dlsym(const char *name)
+{
+ return epoxy_conservative_egl_dlsym(name, true);
+}
+
+void *
+epoxy_glx_dlsym(const char *name)
+{
+ return epoxy_conservative_glx_dlsym(name, true);
+}
+
+static void
+epoxy_load_gl(void)
+{
+ if (api.gl_handle)
+ return;
+
+#if defined(_WIN32) || defined(__APPLE__)
+ get_dlopen_handle(&api.gl_handle, OPENGL_LIB, true, true);
+#else
+
+#if defined(OPENGL_LIB)
+ if (!api.gl_handle)
+ get_dlopen_handle(&api.gl_handle, OPENGL_LIB, false, true);
+#endif
+
+ get_dlopen_handle(&api.glx_handle, GLX_LIB, true, true);
+ api.gl_handle = api.glx_handle;
+#endif
+}
+
+void *
+epoxy_gl_dlsym(const char *name)
+{
+ epoxy_load_gl();
+
+ return do_dlsym(&api.gl_handle, name, true);
+}
+
+void *
+epoxy_gles1_dlsym(const char *name)
+{
+ if (epoxy_current_context_is_glx()) {
+ return epoxy_get_proc_address(name);
+ } else {
+ get_dlopen_handle(&api.gles1_handle, GLES1_LIB, true, true);
+ return do_dlsym(&api.gles1_handle, name, true);
+ }
+}
+
+void *
+epoxy_gles2_dlsym(const char *name)
+{
+ if (epoxy_current_context_is_glx()) {
+ return epoxy_get_proc_address(name);
+ } else {
+ get_dlopen_handle(&api.gles2_handle, GLES2_LIB, true, true);
+ return do_dlsym(&api.gles2_handle, name, true);
+ }
+}
+
+/**
+ * Does the appropriate dlsym() or eglGetProcAddress() for GLES3
+ * functions.
+ *
+ * Mesa interpreted GLES as intending that the GLES3 functions were
+ * available only through eglGetProcAddress() and not dlsym(), while
+ * ARM's Mali drivers interpreted GLES as intending that GLES3
+ * functions were available only through dlsym() and not
+ * eglGetProcAddress(). Thanks, Khronos.
+ */
+void *
+epoxy_gles3_dlsym(const char *name)
+{
+ if (epoxy_current_context_is_glx()) {
+ return epoxy_get_proc_address(name);
+ } else {
+ if (get_dlopen_handle(&api.gles2_handle, GLES2_LIB, false, true)) {
+ void *func = do_dlsym(&api.gles2_handle, name, false);
+
+ if (func)
+ return func;
+ }
+
+ return epoxy_get_proc_address(name);
+ }
+}
+
+/**
+ * Performs either the dlsym or glXGetProcAddress()-equivalent for
+ * core functions in desktop GL.
+ */
+void *
+epoxy_get_core_proc_address(const char *name, int core_version)
+{
+#ifdef _WIN32
+ int core_symbol_support = 11;
+#elif defined(__ANDROID__)
+ /**
+ * All symbols must be resolved through eglGetProcAddress
+ * on Android
+ */
+ int core_symbol_support = 0;
+#else
+ int core_symbol_support = 12;
+#endif
+
+ if (core_version <= core_symbol_support) {
+ return epoxy_gl_dlsym(name);
+ } else {
+ return epoxy_get_proc_address(name);
+ }
+}
+
+#if PLATFORM_HAS_EGL
+static EGLenum
+epoxy_egl_get_current_gl_context_api(void)
+{
+ EGLint curapi;
+
+ if (eglQueryContext(eglGetCurrentDisplay(), eglGetCurrentContext(),
+ EGL_CONTEXT_CLIENT_TYPE, &curapi) == EGL_FALSE) {
+ (void)eglGetError();
+ return EGL_NONE;
+ }
+
+ return (EGLenum) curapi;
+}
+#endif /* PLATFORM_HAS_EGL */
+
+/**
+ * Performs the dlsym() for the core GL 1.0 functions that we use for
+ * determining version and extension support for deciding on dlsym
+ * versus glXGetProcAddress() for all other functions.
+ *
+ * This needs to succeed on implementations without GLX (since
+ * glGetString() and glGetIntegerv() are both in GLES1/2 as well, and
+ * at call time we don't know for sure what API they're trying to use
+ * without inspecting contexts ourselves).
+ */
+void *
+epoxy_get_bootstrap_proc_address(const char *name)
+{
+ /* If we already have a library that links to libglapi loaded,
+ * use that.
+ */
+#if PLATFORM_HAS_GLX
+ if (api.glx_handle && glXGetCurrentContext())
+ return epoxy_gl_dlsym(name);
+#endif
+
+ /* If epoxy hasn't loaded any API-specific library yet, try to
+ * figure out what API the context is using and use that library,
+ * since future calls will also use that API (this prevents a
+ * non-X11 ES2 context from loading a bunch of X11 junk).
+ */
+#if PLATFORM_HAS_EGL
+ get_dlopen_handle(&api.egl_handle, EGL_LIB, false, true);
+ if (api.egl_handle) {
+ int version = 0;
+ switch (epoxy_egl_get_current_gl_context_api()) {
+ case EGL_OPENGL_API:
+ return epoxy_gl_dlsym(name);
+ case EGL_OPENGL_ES_API:
+ if (eglQueryContext(eglGetCurrentDisplay(),
+ eglGetCurrentContext(),
+ EGL_CONTEXT_CLIENT_VERSION,
+ &version)) {
+ if (version >= 2)
+ return epoxy_gles2_dlsym(name);
+ else
+ return epoxy_gles1_dlsym(name);
+ }
+ }
+ }
+#endif /* PLATFORM_HAS_EGL */
+
+ /* Fall back to GLX */
+ return epoxy_gl_dlsym(name);
+}
+
+void *
+epoxy_get_proc_address(const char *name)
+{
+#if PLATFORM_HAS_EGL
+ GLenum egl_api = EGL_NONE;
+
+ if (!epoxy_current_context_is_glx())
+ egl_api = epoxy_egl_get_current_gl_context_api();
+
+ switch (egl_api) {
+ case EGL_OPENGL_API:
+ case EGL_OPENGL_ES_API:
+ return eglGetProcAddress(name);
+ case EGL_NONE:
+ break;
+ }
+#endif
+
+#if defined(_WIN32)
+ return wglGetProcAddress(name);
+#elif defined(__APPLE__)
+ return epoxy_gl_dlsym(name);
+#elif PLATFORM_HAS_GLX
+ if (epoxy_current_context_is_glx())
+ return glXGetProcAddressARB((const GLubyte *)name);
+ assert(0 && "Couldn't find current GLX or EGL context.\n");
+#endif
+
+ return NULL;
+}
+
+WRAPPER_VISIBILITY (void)
+WRAPPER(epoxy_glBegin)(GLenum primtype)
+{
+#ifdef _WIN32
+ InterlockedIncrement(&api.begin_count);
+#else
+ pthread_mutex_lock(&api.mutex);
+ api.begin_count++;
+ pthread_mutex_unlock(&api.mutex);
+#endif
+
+ epoxy_glBegin_unwrapped(primtype);
+}
+
+WRAPPER_VISIBILITY (void)
+WRAPPER(epoxy_glEnd)(void)
+{
+ epoxy_glEnd_unwrapped();
+
+#ifdef _WIN32
+ InterlockedDecrement(&api.begin_count);
+#else
+ pthread_mutex_lock(&api.mutex);
+ api.begin_count--;
+ pthread_mutex_unlock(&api.mutex);
+#endif
+}
+
+PFNGLBEGINPROC epoxy_glBegin = epoxy_glBegin_wrapped;
+PFNGLENDPROC epoxy_glEnd = epoxy_glEnd_wrapped;
+
+epoxy_resolver_failure_handler_t epoxy_resolver_failure_handler;
+
+/**
+ * Sets the function that will be called every time Epoxy fails to
+ * resolve a symbol.
+ *
+ * @param handler The new handler function
+ * @return The previous handler function
+ */
+epoxy_resolver_failure_handler_t
+epoxy_set_resolver_failure_handler(epoxy_resolver_failure_handler_t handler)
+{
+#ifdef _WIN32
+ return InterlockedExchangePointer((void**)&epoxy_resolver_failure_handler,
+ handler);
+#else
+ epoxy_resolver_failure_handler_t old;
+ pthread_mutex_lock(&api.mutex);
+ old = epoxy_resolver_failure_handler;
+ epoxy_resolver_failure_handler = handler;
+ pthread_mutex_unlock(&api.mutex);
+ return old;
+#endif
+}