summaryrefslogtreecommitdiff
path: root/dist/libepoxy/src/dispatch_common.c
diff options
context:
space:
mode:
authorJonathan Gray <jsg@cvs.openbsd.org>2015-04-15 07:32:54 +0000
committerJonathan Gray <jsg@cvs.openbsd.org>2015-04-15 07:32:54 +0000
commit7b94e05d840b2a7626a8f7bd93a340aac0024de3 (patch)
tree72f53bb94b610660547f2d1d1469274053d742aa /dist/libepoxy/src/dispatch_common.c
parentbf8362af7f86f1b845cf507a54050b41ec9a17ee (diff)
Import libepoxy 1.2
Diffstat (limited to 'dist/libepoxy/src/dispatch_common.c')
-rw-r--r--dist/libepoxy/src/dispatch_common.c666
1 files changed, 666 insertions, 0 deletions
diff --git a/dist/libepoxy/src/dispatch_common.c b/dist/libepoxy/src/dispatch_common.c
new file mode 100644
index 000000000..9d1787844
--- /dev/null
+++ b/dist/libepoxy/src/dispatch_common.c
@@ -0,0 +1,666 @@
+/*
+ * 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.
+ */
+
+/**
+ * @file dispatch_common.c
+ *
+ * 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"
+
+#ifdef __APPLE__
+#define GLX_LIB "/opt/X11/lib/libGL.1.dylib"
+#else
+#define GLX_LIB "libGL.so.1"
+#endif
+
+struct api {
+#ifndef _WIN32
+ /**
+ * Locking for making sure we don't double-dlopen().
+ */
+ pthread_mutex_t mutex;
+#endif
+
+ /** dlopen() return value for libGL.so.1. */
+ void *glx_handle;
+
+ /**
+ * dlopen() return value for OS X's GL library.
+ *
+ * On linux, glx_handle is used instead.
+ */
+ 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,
+#endif
+};
+
+static bool library_initialized;
+
+#if PLATFORM_HAS_EGL
+static EGLenum
+epoxy_egl_get_current_gl_context_api(void);
+#endif
+
+static void
+library_init(void) __attribute__((constructor));
+
+static void
+library_init(void)
+{
+ library_initialized = true;
+}
+
+static bool
+get_dlopen_handle(void **handle, const char *lib_name, bool exit_on_fail)
+{
+ if (*handle)
+ return true;
+
+ if (!library_initialized) {
+ fprintf(stderr,
+ "Attempting to dlopen() while in the dynamic linker.\n");
+ abort();
+ }
+
+#ifdef _WIN32
+ *handle = LoadLibraryA(lib_name);
+#else
+ pthread_mutex_lock(&api.mutex);
+ if (!*handle) {
+ *handle = dlopen(lib_name, RTLD_LAZY | RTLD_LOCAL);
+ if (!*handle) {
+ if (exit_on_fail) {
+ fprintf(stderr, "Couldn't open %s: %s\n", lib_name, dlerror());
+ exit(1);
+ } else {
+ (void)dlerror();
+ }
+ }
+ }
+ pthread_mutex_unlock(&api.mutex);
+#endif
+
+ return *handle != NULL;
+}
+
+static void *
+do_dlsym(void **handle, const char *lib_name, const char *name,
+ bool exit_on_fail)
+{
+ void *result;
+ const char *error = "";
+
+ if (!get_dlopen_handle(handle, lib_name, exit_on_fail))
+ return NULL;
+
+#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 in %s: %s\n", name, lib_name, error);
+ exit(1);
+ }
+
+ return result;
+}
+
+PUBLIC bool
+epoxy_is_desktop_gl(void)
+{
+ const char *es_prefix = "OpenGL ES";
+ 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));
+}
+
+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++;
+
+ /* 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);
+ exit(1);
+ }
+ 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);
+}
+
+bool
+epoxy_extension_in_string(const char *extension_list, const char *ext)
+{
+ const char *ptr = extension_list;
+ int len = strlen(ext);
+
+ /* 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;
+
+ 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)
+ return true;
+ }
+
+ return false;
+ }
+}
+
+/**
+ * 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
+ /* If the application hasn't explicitly called some of our GLX
+ * or EGL code but has presumably set up a context on its own,
+ * then we need to figure out how to getprocaddress anyway.
+ *
+ * If there's a public GetProcAddress loaded in the
+ * application's namespace, then use that.
+ */
+ void *sym;
+
+ sym = dlsym(NULL, "glXGetCurrentContext");
+ if (sym) {
+ if (glXGetCurrentContext())
+ return true;
+ } else {
+ (void)dlerror();
+ }
+
+#if PLATFORM_HAS_EGL
+ sym = dlsym(NULL, "eglGetCurrentContext");
+ if (sym) {
+ if (epoxy_egl_get_current_gl_context_api() != EGL_NONE)
+ return true;
+ } else {
+ (void)dlerror();
+ }
+#endif /* PLATFORM_HAS_EGL */
+
+ /* OK, couldn't find anything in the app's address space.
+ * Presumably they dlopened with RTLD_LOCAL, which hides it
+ * from us. Just go dlopen()ing likely libraries and try them.
+ */
+ sym = do_dlsym(&api.glx_handle, GLX_LIB, "glXGetCurrentContext", false);
+ if (sym && glXGetCurrentContext())
+ return true;
+
+#if PLATFORM_HAS_EGL
+ sym = do_dlsym(&api.egl_handle, "libEGL.so.1", "eglGetCurrentContext",
+ false);
+ if (sym && epoxy_egl_get_current_gl_context_api() != EGL_NONE)
+ return false;
+#endif /* PLATFORM_HAS_EGL */
+
+ return false;
+#endif /* PLATFORM_HAS_GLX */
+}
+
+/**
+ * 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);
+}
+
+void *
+epoxy_egl_dlsym(const char *name)
+{
+ return do_dlsym(&api.egl_handle, "libEGL.so.1", name, true);
+}
+
+void *
+epoxy_glx_dlsym(const char *name)
+{
+ return do_dlsym(&api.glx_handle, GLX_LIB, name, true);
+}
+
+void *
+epoxy_gl_dlsym(const char *name)
+{
+#ifdef _WIN32
+ return do_dlsym(&api.gl_handle, "OPENGL32", name, true);
+#elif defined(__APPLE__)
+ return do_dlsym(&api.gl_handle,
+ "/System/Library/Frameworks/OpenGL.framework/Versions/Current/OpenGL",
+ name, true);
+#else
+ /* There's no library for desktop GL support independent of GLX. */
+ return epoxy_glx_dlsym(name);
+#endif
+}
+
+void *
+epoxy_gles1_dlsym(const char *name)
+{
+ if (epoxy_current_context_is_glx()) {
+ return epoxy_get_proc_address(name);
+ } else {
+ return do_dlsym(&api.gles1_handle, "libGLESv1_CM.so.1", name, true);
+ }
+}
+
+void *
+epoxy_gles2_dlsym(const char *name)
+{
+ if (epoxy_current_context_is_glx()) {
+ return epoxy_get_proc_address(name);
+ } else {
+ return do_dlsym(&api.gles2_handle, "libGLESv2.so.2", 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 {
+ void *func = do_dlsym(&api.gles2_handle, "libGLESv2.so.2", 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 = 10;
+#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)
+{
+ EGLenum save_api = eglQueryAPI();
+ EGLContext ctx;
+
+ if (eglBindAPI(EGL_OPENGL_API)) {
+ ctx = eglGetCurrentContext();
+ if (ctx) {
+ eglBindAPI(save_api);
+ return EGL_OPENGL_API;
+ }
+ } else {
+ (void)eglGetError();
+ }
+
+ if (eglBindAPI(EGL_OPENGL_ES_API)) {
+ ctx = eglGetCurrentContext();
+ eglBindAPI(save_api);
+ if (ctx) {
+ eglBindAPI(save_api);
+ return EGL_OPENGL_ES_API;
+ }
+ } else {
+ (void)eglGetError();
+ }
+
+ return EGL_NONE;
+}
+#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, "libEGL.so.1", false);
+ if (api.egl_handle) {
+ switch (epoxy_egl_get_current_gl_context_api()) {
+ case EGL_OPENGL_API:
+ return epoxy_gl_dlsym(name);
+ case EGL_OPENGL_ES_API:
+ /* We can't resolve the GL version, because
+ * epoxy_glGetString() is one of the two things calling
+ * us. Try the GLES2 implementation first, and fall back
+ * to GLES1 otherwise.
+ */
+ get_dlopen_handle(&api.gles2_handle, "libGLESv2.so.2", false);
+ if (api.gles2_handle)
+ 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)
+{
+#ifdef _WIN32
+ return wglGetProcAddress(name);
+#elif defined(__APPLE__)
+ return epoxy_gl_dlsym(name);
+#else
+ if (epoxy_current_context_is_glx()) {
+ return glXGetProcAddressARB((const GLubyte *)name);
+ } else {
+#if PLATFORM_HAS_EGL
+ GLenum 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
+ }
+ errx(1, "Couldn't find current GLX or EGL context.\n");
+#endif
+}
+
+void
+epoxy_print_failure_reasons(const char *name,
+ const char **provider_names,
+ const int *providers)
+{
+ int i;
+
+ fprintf(stderr, "No provider of %s found. Requires one of:\n", name);
+
+ for (i = 0; providers[i] != 0; i++)
+ fprintf(stderr, " %s\n",
+ provider_names[providers[i]]);
+
+ if (providers[0] == 0) {
+ fprintf(stderr, " No known providers. This is likely a bug "
+ "in libepoxy code generation\n");
+ }
+}
+
+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
+}
+
+PUBLIC PFNGLBEGINPROC epoxy_glBegin = epoxy_glBegin_wrapped;
+PUBLIC PFNGLENDPROC epoxy_glEnd = epoxy_glEnd_wrapped;