dispatch_common.c revision e52adb7b
1/*
2 * Copyright © 2013-2014 Intel Corporation
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 * IN THE SOFTWARE.
22 */
23
24/**
25 * @file dispatch_common.c
26 *
27 * Implements common code shared by the generated GL/EGL/GLX dispatch code.
28 *
29 * A collection of some important specs on getting GL function pointers.
30 *
31 * From the linux GL ABI (http://www.opengl.org/registry/ABI/):
32 *
33 *     "3.4. The libraries must export all OpenGL 1.2, GLU 1.3, GLX 1.3, and
34 *           ARB_multitexture entry points statically.
35 *
36 *      3.5. Because non-ARB extensions vary so widely and are constantly
37 *           increasing in number, it's infeasible to require that they all be
38 *           supported, and extensions can always be added to hardware drivers
39 *           after the base link libraries are released. These drivers are
40 *           dynamically loaded by libGL, so extensions not in the base
41 *           library must also be obtained dynamically.
42 *
43 *      3.6. To perform the dynamic query, libGL also must export an entry
44 *           point called
45 *
46 *           void (*glXGetProcAddressARB(const GLubyte *))();
47 *
48 *      The full specification of this function is available separately. It
49 *      takes the string name of a GL or GLX entry point and returns a pointer
50 *      to a function implementing that entry point. It is functionally
51 *      identical to the wglGetProcAddress query defined by the Windows OpenGL
52 *      library, except that the function pointers returned are context
53 *      independent, unlike the WGL query."
54 *
55 * From the EGL 1.4 spec:
56 *
57 *    "Client API function pointers returned by eglGetProcAddress are
58 *     independent of the display and the currently bound client API context,
59 *     and may be used by any client API context which supports the extension.
60 *
61 *     eglGetProcAddress may be queried for all of the following functions:
62 *
63 *     • All EGL and client API extension functions supported by the
64 *       implementation (whether those extensions are supported by the current
65 *       client API context or not). This includes any mandatory OpenGL ES
66 *       extensions.
67 *
68 *     eglGetProcAddress may not be queried for core (non-extension) functions
69 *     in EGL or client APIs 20 .
70 *
71 *     For functions that are queryable with eglGetProcAddress,
72 *     implementations may choose to also export those functions statically
73 *     from the object libraries im- plementing those functions. However,
74 *     portable clients cannot rely on this behavior.
75 *
76 * From the GLX 1.4 spec:
77 *
78 *     "glXGetProcAddress may be queried for all of the following functions:
79 *
80 *      • All GL and GLX extension functions supported by the implementation
81 *        (whether those extensions are supported by the current context or
82 *        not).
83 *
84 *      • All core (non-extension) functions in GL and GLX from version 1.0 up
85 *        to and including the versions of those specifications supported by
86 *        the implementation, as determined by glGetString(GL VERSION) and
87 *        glXQueryVersion queries."
88 */
89
90#include <assert.h>
91#include <stdlib.h>
92#ifdef _WIN32
93#include <windows.h>
94#else
95#include <dlfcn.h>
96#include <err.h>
97#include <pthread.h>
98#endif
99#include <string.h>
100#include <ctype.h>
101#include <stdio.h>
102
103#include "dispatch_common.h"
104
105#ifdef __APPLE__
106#define GLX_LIB "/opt/X11/lib/libGL.1.dylib"
107#elif defined(ANDROID)
108#define GLX_LIB "libGLESv2.so"
109#else
110#define GLX_LIB "libGL.so.1"
111#endif
112
113#ifdef ANDROID
114#define EGL_LIB "libEGL.so"
115#define GLES1_LIB "libGLESv1_CM.so"
116#define GLES2_LIB "libGLESv2.so"
117#else
118#define EGL_LIB "libEGL.so.1"
119#define GLES1_LIB "libGLESv1_CM.so.1"
120#define GLES2_LIB "libGLESv2.so.2"
121#endif
122
123#ifdef __GNUC__
124#define CONSTRUCT(_func) static void _func (void) __attribute__((constructor));
125#define DESTRUCT(_func) static void _func (void) __attribute__((destructor));
126#elif defined (_MSC_VER) && (_MSC_VER >= 1500)
127#define CONSTRUCT(_func) \
128  static void _func(void); \
129  static int _func ## _wrapper(void) { _func(); return 0; } \
130  __pragma(section(".CRT$XCU",read)) \
131  __declspec(allocate(".CRT$XCU")) static int (* _array ## _func)(void) = _func ## _wrapper;
132
133#define DESTRUCT(_func) \
134  static void _func(void); \
135  static int _func ## _constructor(void) { atexit (_func); return 0; } \
136  __pragma(section(".CRT$XCU",read)) \
137  __declspec(allocate(".CRT$XCU")) static int (* _array ## _func)(void) = _func ## _constructor;
138
139#else
140#error "You will need constructor support for your compiler"
141#endif
142
143struct api {
144#ifndef _WIN32
145    /**
146     * Locking for making sure we don't double-dlopen().
147     */
148    pthread_mutex_t mutex;
149#endif
150
151    /** dlopen() return value for libGL.so.1. */
152    void *glx_handle;
153
154    /**
155     * dlopen() return value for OS X's GL library.
156     *
157     * On linux, glx_handle is used instead.
158     */
159    void *gl_handle;
160
161    /** dlopen() return value for libEGL.so.1 */
162    void *egl_handle;
163
164    /** dlopen() return value for libGLESv1_CM.so.1 */
165    void *gles1_handle;
166
167    /** dlopen() return value for libGLESv2.so.2 */
168    void *gles2_handle;
169
170    /**
171     * This value gets incremented when any thread is in
172     * glBegin()/glEnd() called through epoxy.
173     *
174     * We're not guaranteed to be called through our wrapper, so the
175     * conservative paths also try to handle the failure cases they'll
176     * see if begin_count didn't reflect reality.  It's also a bit of
177     * a bug that the conservative paths might return success because
178     * some other thread was in epoxy glBegin/glEnd while our thread
179     * is trying to resolve, but given that it's basically just for
180     * informative error messages, we shouldn't need to care.
181     */
182    long begin_count;
183};
184
185static struct api api = {
186#ifndef _WIN32
187    .mutex = PTHREAD_MUTEX_INITIALIZER,
188#else
189	0,
190#endif
191};
192
193static bool library_initialized;
194
195static bool epoxy_current_context_is_glx(void);
196
197#if PLATFORM_HAS_EGL
198static EGLenum
199epoxy_egl_get_current_gl_context_api(void);
200#endif
201
202CONSTRUCT (library_init)
203
204static void
205library_init(void)
206{
207    library_initialized = true;
208}
209
210static bool
211get_dlopen_handle(void **handle, const char *lib_name, bool exit_on_fail)
212{
213    if (*handle)
214        return true;
215
216    if (!library_initialized) {
217        fprintf(stderr,
218                "Attempting to dlopen() while in the dynamic linker.\n");
219        abort();
220    }
221
222#ifdef _WIN32
223    *handle = LoadLibraryA(lib_name);
224#else
225    pthread_mutex_lock(&api.mutex);
226    if (!*handle) {
227        *handle = dlopen(lib_name, RTLD_LAZY | RTLD_LOCAL);
228        if (!*handle) {
229            if (exit_on_fail) {
230                fprintf(stderr, "Couldn't open %s: %s\n", lib_name, dlerror());
231                exit(1);
232            } else {
233                (void)dlerror();
234            }
235        }
236    }
237    pthread_mutex_unlock(&api.mutex);
238#endif
239
240    return *handle != NULL;
241}
242
243static void *
244do_dlsym(void **handle, const char *lib_name, const char *name,
245         bool exit_on_fail)
246{
247    void *result;
248    const char *error = "";
249
250    if (!get_dlopen_handle(handle, lib_name, exit_on_fail))
251        return NULL;
252
253#ifdef _WIN32
254    result = GetProcAddress(*handle, name);
255#else
256    result = dlsym(*handle, name);
257    if (!result)
258        error = dlerror();
259#endif
260    if (!result && exit_on_fail) {
261        fprintf(stderr,"%s() not found in %s: %s\n", name, lib_name, error);
262        exit(1);
263    }
264
265    return result;
266}
267
268PUBLIC bool
269epoxy_is_desktop_gl(void)
270{
271    const char *es_prefix = "OpenGL ES";
272    const char *version;
273
274#if PLATFORM_HAS_EGL
275    /* PowerVR's OpenGL ES implementation (and perhaps other) don't
276     * comply with the standard, which states that
277     * "glGetString(GL_VERSION)" should return a string starting with
278     * "OpenGL ES". Therefore, to distinguish desktop OpenGL from
279     * OpenGL ES, we must also check the context type through EGL (we
280     * can do that as PowerVR is only usable through EGL).
281     */
282    if (!epoxy_current_context_is_glx()) {
283        switch (epoxy_egl_get_current_gl_context_api()) {
284        case EGL_OPENGL_API:     return true;
285        case EGL_OPENGL_ES_API:  return false;
286        case EGL_NONE:
287        default:  break;
288        }
289    }
290#endif
291
292    if (api.begin_count)
293        return true;
294
295    version = (const char *)glGetString(GL_VERSION);
296
297    /* If we didn't get a version back, there are only two things that
298     * could have happened: either malloc failure (which basically
299     * doesn't exist), or we were called within a glBegin()/glEnd().
300     * Assume the second, which only exists for desktop GL.
301     */
302    if (!version)
303        return true;
304
305    return strncmp(es_prefix, version, strlen(es_prefix));
306}
307
308static int
309epoxy_internal_gl_version(int error_version)
310{
311    const char *version = (const char *)glGetString(GL_VERSION);
312    GLint major, minor;
313    int scanf_count;
314
315    if (!version)
316        return error_version;
317
318    /* skip to version number */
319    while (!isdigit(*version) && *version != '\0')
320        version++;
321
322    /* Interpret version number */
323    scanf_count = sscanf(version, "%i.%i", &major, &minor);
324    if (scanf_count != 2) {
325        fprintf(stderr, "Unable to interpret GL_VERSION string: %s\n",
326                version);
327        exit(1);
328    }
329    return 10 * major + minor;
330}
331
332PUBLIC int
333epoxy_gl_version(void)
334{
335    return epoxy_internal_gl_version(0);
336}
337
338int
339epoxy_conservative_gl_version(void)
340{
341    if (api.begin_count)
342        return 100;
343
344    return epoxy_internal_gl_version(100);
345}
346
347bool
348epoxy_extension_in_string(const char *extension_list, const char *ext)
349{
350    const char *ptr = extension_list;
351    int len = strlen(ext);
352
353    /* Make sure that don't just find an extension with our name as a prefix. */
354    while (true) {
355        ptr = strstr(ptr, ext);
356        if (!ptr)
357            return false;
358
359        if (ptr[len] == ' ' || ptr[len] == 0)
360            return true;
361        ptr += len;
362    }
363}
364
365static bool
366epoxy_internal_has_gl_extension(const char *ext, bool invalid_op_mode)
367{
368    if (epoxy_gl_version() < 30) {
369        const char *exts = (const char *)glGetString(GL_EXTENSIONS);
370        if (!exts)
371            return invalid_op_mode;
372        return epoxy_extension_in_string(exts, ext);
373    } else {
374        int num_extensions;
375        int i;
376
377        glGetIntegerv(GL_NUM_EXTENSIONS, &num_extensions);
378        if (num_extensions == 0)
379            return invalid_op_mode;
380
381        for (i = 0; i < num_extensions; i++) {
382            const char *gl_ext = (const char *)glGetStringi(GL_EXTENSIONS, i);
383            if (strcmp(ext, gl_ext) == 0)
384                return true;
385        }
386
387        return false;
388    }
389}
390
391/**
392 * Tests whether the currently bound context is EGL or GLX, trying to
393 * avoid loading libraries unless necessary.
394 */
395static bool
396epoxy_current_context_is_glx(void)
397{
398#if !PLATFORM_HAS_GLX
399    return false;
400#else
401    /* If the application hasn't explicitly called some of our GLX
402     * or EGL code but has presumably set up a context on its own,
403     * then we need to figure out how to getprocaddress anyway.
404     *
405     * If there's a public GetProcAddress loaded in the
406     * application's namespace, then use that.
407     */
408    void *sym;
409
410    sym = dlsym(NULL, "glXGetCurrentContext");
411    if (sym) {
412        if (glXGetCurrentContext())
413            return true;
414    } else {
415        (void)dlerror();
416    }
417
418#if PLATFORM_HAS_EGL
419    sym = dlsym(NULL, "eglGetCurrentContext");
420    if (sym) {
421        if (epoxy_egl_get_current_gl_context_api() != EGL_NONE)
422            return false;
423    } else {
424        (void)dlerror();
425    }
426#endif /* PLATFORM_HAS_EGL */
427
428    /* OK, couldn't find anything in the app's address space.
429     * Presumably they dlopened with RTLD_LOCAL, which hides it
430     * from us.  Just go dlopen()ing likely libraries and try them.
431     */
432    sym = do_dlsym(&api.glx_handle, GLX_LIB, "glXGetCurrentContext", false);
433    if (sym && glXGetCurrentContext())
434        return true;
435
436#if PLATFORM_HAS_EGL
437    sym = do_dlsym(&api.egl_handle, EGL_LIB, "eglGetCurrentContext",
438                   false);
439    if (sym && epoxy_egl_get_current_gl_context_api() != EGL_NONE)
440        return false;
441#endif /* PLATFORM_HAS_EGL */
442
443    return false;
444#endif /* PLATFORM_HAS_GLX */
445}
446
447/**
448 * Returns true if the given GL extension is supported in the current context.
449 *
450 * Note that this function can't be called from within glBegin()/glEnd().
451 *
452 * \sa epoxy_has_egl_extension()
453 * \sa epoxy_has_glx_extension()
454 */
455PUBLIC bool
456epoxy_has_gl_extension(const char *ext)
457{
458    return epoxy_internal_has_gl_extension(ext, false);
459}
460
461bool
462epoxy_conservative_has_gl_extension(const char *ext)
463{
464    if (api.begin_count)
465        return true;
466
467    return epoxy_internal_has_gl_extension(ext, true);
468}
469
470void *
471epoxy_egl_dlsym(const char *name)
472{
473    return do_dlsym(&api.egl_handle, EGL_LIB, name, true);
474}
475
476void *
477epoxy_glx_dlsym(const char *name)
478{
479    return do_dlsym(&api.glx_handle, GLX_LIB, name, true);
480}
481
482void *
483epoxy_gl_dlsym(const char *name)
484{
485#ifdef _WIN32
486    return do_dlsym(&api.gl_handle, "OPENGL32", name, true);
487#elif defined(__APPLE__)
488    return do_dlsym(&api.gl_handle,
489                    "/System/Library/Frameworks/OpenGL.framework/Versions/Current/OpenGL",
490                    name, true);
491#else
492    /* There's no library for desktop GL support independent of GLX. */
493    return epoxy_glx_dlsym(name);
494#endif
495}
496
497void *
498epoxy_gles1_dlsym(const char *name)
499{
500    if (epoxy_current_context_is_glx()) {
501        return epoxy_get_proc_address(name);
502    } else {
503        return do_dlsym(&api.gles1_handle, GLES1_LIB, name, true);
504    }
505}
506
507void *
508epoxy_gles2_dlsym(const char *name)
509{
510    if (epoxy_current_context_is_glx()) {
511        return epoxy_get_proc_address(name);
512    } else {
513        return do_dlsym(&api.gles2_handle, GLES2_LIB, name, true);
514    }
515}
516
517/**
518 * Does the appropriate dlsym() or eglGetProcAddress() for GLES3
519 * functions.
520 *
521 * Mesa interpreted GLES as intending that the GLES3 functions were
522 * available only through eglGetProcAddress() and not dlsym(), while
523 * ARM's Mali drivers interpreted GLES as intending that GLES3
524 * functions were available only through dlsym() and not
525 * eglGetProcAddress().  Thanks, Khronos.
526 */
527void *
528epoxy_gles3_dlsym(const char *name)
529{
530    if (epoxy_current_context_is_glx()) {
531        return epoxy_get_proc_address(name);
532    } else {
533        void *func = do_dlsym(&api.gles2_handle, GLES2_LIB, name, false);
534
535        if (func)
536            return func;
537
538        return epoxy_get_proc_address(name);
539    }
540}
541
542/**
543 * Performs either the dlsym or glXGetProcAddress()-equivalent for
544 * core functions in desktop GL.
545 */
546void *
547epoxy_get_core_proc_address(const char *name, int core_version)
548{
549#ifdef _WIN32
550    int core_symbol_support = 11;
551#elif defined(ANDROID)
552    /**
553     * All symbols must be resolved through eglGetProcAddress
554     * on Android
555     */
556    int core_symbol_support = 0;
557#else
558    int core_symbol_support = 12;
559#endif
560
561    if (core_version <= core_symbol_support) {
562        return epoxy_gl_dlsym(name);
563    } else {
564        return epoxy_get_proc_address(name);
565    }
566}
567
568#if PLATFORM_HAS_EGL
569static EGLenum
570epoxy_egl_get_current_gl_context_api(void)
571{
572    EGLenum save_api = eglQueryAPI();
573    EGLContext ctx;
574
575    if (eglBindAPI(EGL_OPENGL_API)) {
576        ctx = eglGetCurrentContext();
577        if (ctx) {
578            eglBindAPI(save_api);
579            return EGL_OPENGL_API;
580        }
581    } else {
582        (void)eglGetError();
583    }
584
585    if (eglBindAPI(EGL_OPENGL_ES_API)) {
586        ctx = eglGetCurrentContext();
587        eglBindAPI(save_api);
588        if (ctx) {
589            eglBindAPI(save_api);
590            return EGL_OPENGL_ES_API;
591        }
592    } else {
593        (void)eglGetError();
594    }
595
596    return EGL_NONE;
597}
598#endif /* PLATFORM_HAS_EGL */
599
600/**
601 * Performs the dlsym() for the core GL 1.0 functions that we use for
602 * determining version and extension support for deciding on dlsym
603 * versus glXGetProcAddress() for all other functions.
604 *
605 * This needs to succeed on implementations without GLX (since
606 * glGetString() and glGetIntegerv() are both in GLES1/2 as well, and
607 * at call time we don't know for sure what API they're trying to use
608 * without inspecting contexts ourselves).
609 */
610void *
611epoxy_get_bootstrap_proc_address(const char *name)
612{
613    /* If we already have a library that links to libglapi loaded,
614     * use that.
615     */
616#if PLATFORM_HAS_GLX
617    if (api.glx_handle && glXGetCurrentContext())
618        return epoxy_gl_dlsym(name);
619#endif
620
621    /* If epoxy hasn't loaded any API-specific library yet, try to
622     * figure out what API the context is using and use that library,
623     * since future calls will also use that API (this prevents a
624     * non-X11 ES2 context from loading a bunch of X11 junk).
625     */
626#if PLATFORM_HAS_EGL
627    get_dlopen_handle(&api.egl_handle, EGL_LIB, false);
628    if (api.egl_handle) {
629        switch (epoxy_egl_get_current_gl_context_api()) {
630        case EGL_OPENGL_API:
631            return epoxy_gl_dlsym(name);
632        case EGL_OPENGL_ES_API:
633            /* We can't resolve the GL version, because
634             * epoxy_glGetString() is one of the two things calling
635             * us.  Try the GLES2 implementation first, and fall back
636             * to GLES1 otherwise.
637             */
638            get_dlopen_handle(&api.gles2_handle, GLES2_LIB, false);
639            if (api.gles2_handle)
640                return epoxy_gles2_dlsym(name);
641            else
642                return epoxy_gles1_dlsym(name);
643        }
644    }
645#endif /* PLATFORM_HAS_EGL */
646
647    /* Fall back to GLX */
648    return epoxy_gl_dlsym(name);
649}
650
651void *
652epoxy_get_proc_address(const char *name)
653{
654#ifdef _WIN32
655    return wglGetProcAddress(name);
656#elif defined(__APPLE__)
657    return epoxy_gl_dlsym(name);
658#else
659    if (epoxy_current_context_is_glx()) {
660        return glXGetProcAddressARB((const GLubyte *)name);
661    } else {
662#if PLATFORM_HAS_EGL
663        GLenum egl_api = epoxy_egl_get_current_gl_context_api();
664
665        switch (egl_api) {
666        case EGL_OPENGL_API:
667        case EGL_OPENGL_ES_API:
668            return eglGetProcAddress(name);
669        case EGL_NONE:
670            break;
671        }
672#endif
673    }
674    errx(1, "Couldn't find current GLX or EGL context.\n");
675#endif
676}
677
678WRAPPER_VISIBILITY (void)
679WRAPPER(epoxy_glBegin)(GLenum primtype)
680{
681#ifdef _WIN32
682    InterlockedIncrement(&api.begin_count);
683#else
684    pthread_mutex_lock(&api.mutex);
685    api.begin_count++;
686    pthread_mutex_unlock(&api.mutex);
687#endif
688
689    epoxy_glBegin_unwrapped(primtype);
690}
691
692WRAPPER_VISIBILITY (void)
693WRAPPER(epoxy_glEnd)(void)
694{
695    epoxy_glEnd_unwrapped();
696
697#ifdef _WIN32
698    InterlockedDecrement(&api.begin_count);
699#else
700    pthread_mutex_lock(&api.mutex);
701    api.begin_count--;
702    pthread_mutex_unlock(&api.mutex);
703#endif
704}
705
706PUBLIC PFNGLBEGINPROC epoxy_glBegin = epoxy_glBegin_wrapped;
707PUBLIC PFNGLENDPROC epoxy_glEnd = epoxy_glEnd_wrapped;
708