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 | * \mainpage Epoxy |
26 | * |
27 | * \section intro_sec Introduction |
28 | * |
29 | * Epoxy is a library for handling OpenGL function pointer management for |
30 | * you. |
31 | * |
32 | * It hides the complexity of `dlopen()`, `dlsym()`, `glXGetProcAddress()`, |
33 | * `eglGetProcAddress()`, etc. from the app developer, with very little |
34 | * knowledge needed on their part. They get to read GL specs and write |
35 | * code using undecorated function names like `glCompileShader()`. |
36 | * |
37 | * Don't forget to check for your extensions or versions being present |
38 | * before you use them, just like before! We'll tell you what you forgot |
39 | * to check for instead of just segfaulting, though. |
40 | * |
41 | * \section features_sec Features |
42 | * |
43 | * - Automatically initializes as new GL functions are used. |
44 | * - GL 4.6 core and compatibility context support. |
45 | * - GLES 1/2/3 context support. |
46 | * - Knows about function aliases so (e.g.) `glBufferData()` can be |
47 | * used with `GL_ARB_vertex_buffer_object` implementations, along |
48 | * with GL 1.5+ implementations. |
49 | * - EGL, GLX, and WGL support. |
50 | * - Can be mixed with non-epoxy GL usage. |
51 | * |
52 | * \section using_sec Using Epoxy |
53 | * |
54 | * Using Epoxy should be as easy as replacing: |
55 | * |
56 | * ```cpp |
57 | * #include <GL/gl.h> |
58 | * #include <GL/glx.h> |
59 | * #include <GL/glext.h> |
60 | * ``` |
61 | * |
62 | * with: |
63 | * |
64 | * ```cpp |
65 | * #include <epoxy/gl.h> |
66 | * #include <epoxy/glx.h> |
67 | * ``` |
68 | * |
69 | * \subsection using_include_sec Headers |
70 | * |
71 | * Epoxy comes with the following public headers: |
72 | * |
73 | * - `epoxy/gl.h` - For GL API |
74 | * - `epoxy/egl.h` - For EGL API |
75 | * - `epoxy/glx.h` - For GLX API |
76 | * - `epoxy/wgl.h` - For WGL API |
77 | * |
78 | * \section links_sec Additional links |
79 | * |
80 | * The latest version of the Epoxy code is available on [GitHub](https://github.com/anholt/libepoxy). |
81 | * |
82 | * For bug reports and enhancements, please use the [Issues](https://github.com/anholt/libepoxy/issues) |
83 | * link. |
84 | * |
85 | * The scope of this API reference does not include the documentation for |
86 | * OpenGL and OpenGL ES. For more information on those programming interfaces |
87 | * please visit: |
88 | * |
89 | * - [Khronos](https://www.khronos.org/) |
90 | * - [OpenGL page on Khronos.org](https://www.khronos.org/opengl/) |
91 | * - [OpenGL ES page on Khronos.org](https://www.khronos.org/opengles/) |
92 | * - [docs.GL](http://docs.gl/) |
93 | */ |
94 | |
95 | /** |
96 | * @file dispatch_common.c |
97 | * |
98 | * @brief Implements common code shared by the generated GL/EGL/GLX dispatch code. |
99 | * |
100 | * A collection of some important specs on getting GL function pointers. |
101 | * |
102 | * From the linux GL ABI (http://www.opengl.org/registry/ABI/): |
103 | * |
104 | * "3.4. The libraries must export all OpenGL 1.2, GLU 1.3, GLX 1.3, and |
105 | * ARB_multitexture entry points statically. |
106 | * |
107 | * 3.5. Because non-ARB extensions vary so widely and are constantly |
108 | * increasing in number, it's infeasible to require that they all be |
109 | * supported, and extensions can always be added to hardware drivers |
110 | * after the base link libraries are released. These drivers are |
111 | * dynamically loaded by libGL, so extensions not in the base |
112 | * library must also be obtained dynamically. |
113 | * |
114 | * 3.6. To perform the dynamic query, libGL also must export an entry |
115 | * point called |
116 | * |
117 | * void (*glXGetProcAddressARB(const GLubyte *))(); |
118 | * |
119 | * The full specification of this function is available separately. It |
120 | * takes the string name of a GL or GLX entry point and returns a pointer |
121 | * to a function implementing that entry point. It is functionally |
122 | * identical to the wglGetProcAddress query defined by the Windows OpenGL |
123 | * library, except that the function pointers returned are context |
124 | * independent, unlike the WGL query." |
125 | * |
126 | * From the EGL 1.4 spec: |
127 | * |
128 | * "Client API function pointers returned by eglGetProcAddress are |
129 | * independent of the display and the currently bound client API context, |
130 | * and may be used by any client API context which supports the extension. |
131 | * |
132 | * eglGetProcAddress may be queried for all of the following functions: |
133 | * |
134 | * • All EGL and client API extension functions supported by the |
135 | * implementation (whether those extensions are supported by the current |
136 | * client API context or not). This includes any mandatory OpenGL ES |
137 | * extensions. |
138 | * |
139 | * eglGetProcAddress may not be queried for core (non-extension) functions |
140 | * in EGL or client APIs 20 . |
141 | * |
142 | * For functions that are queryable with eglGetProcAddress, |
143 | * implementations may choose to also export those functions statically |
144 | * from the object libraries im- plementing those functions. However, |
145 | * portable clients cannot rely on this behavior. |
146 | * |
147 | * From the GLX 1.4 spec: |
148 | * |
149 | * "glXGetProcAddress may be queried for all of the following functions: |
150 | * |
151 | * • All GL and GLX extension functions supported by the implementation |
152 | * (whether those extensions are supported by the current context or |
153 | * not). |
154 | * |
155 | * • All core (non-extension) functions in GL and GLX from version 1.0 up |
156 | * to and including the versions of those specifications supported by |
157 | * the implementation, as determined by glGetString(GL VERSION) and |
158 | * glXQueryVersion queries." |
159 | */ |
160 | |
161 | #include <assert.h> |
162 | #include <stdlib.h> |
163 | #ifdef _WIN32 |
164 | #include <windows.h> |
165 | #else |
166 | #include <dlfcn.h> |
167 | #include <err.h> |
168 | #include <pthread.h> |
169 | #endif |
170 | #include <string.h> |
171 | #include <ctype.h> |
172 | #include <stdio.h> |
173 | |
174 | #include "dispatch_common.h" |
175 | |
176 | #if defined(__APPLE__) |
177 | #define GLX_LIB "/opt/X11/lib/libGL.1.dylib" |
178 | #define OPENGL_LIB "/System/Library/Frameworks/OpenGL.framework/Versions/Current/OpenGL" |
179 | #define GLES1_LIB "libGLESv1_CM.so" |
180 | #define GLES2_LIB "libGLESv2.so" |
181 | #elif defined(__ANDROID__) |
182 | #define GLX_LIB "libGLESv2.so" |
183 | #define EGL_LIB "libEGL.so" |
184 | #define GLES1_LIB "libGLESv1_CM.so" |
185 | #define GLES2_LIB "libGLESv2.so" |
186 | #elif defined(_WIN32) |
187 | #define EGL_LIB "libEGL.dll" |
188 | #define GLES1_LIB "libGLES_CM.dll" |
189 | #define GLES2_LIB "libGLESv2.dll" |
190 | #define OPENGL_LIB "OPENGL32" |
191 | #else |
192 | #define GLVND_GLX_LIB "libGLX.so.1" |
193 | #define GLX_LIB "libGL.so.1" |
194 | #define EGL_LIB "libEGL.so.1" |
195 | #define GLES1_LIB "libGLESv1_CM.so.1" |
196 | #define GLES2_LIB "libGLESv2.so.2" |
197 | #define OPENGL_LIB "libOpenGL.so.0" |
198 | #endif |
199 | |
200 | #ifdef __GNUC__ |
201 | #define CONSTRUCT(_func) static void _func (void) __attribute__((constructor)); |
202 | #define DESTRUCT(_func) static void _func (void) __attribute__((destructor)); |
203 | #elif defined (_MSC_VER) && (_MSC_VER >= 1500) |
204 | #define CONSTRUCT(_func) \ |
205 | static void _func(void); \ |
206 | static int _func ## _wrapper(void) { _func(); return 0; } \ |
207 | __pragma(section(".CRT$XCU",read)) \ |
208 | __declspec(allocate(".CRT$XCU")) static int (* _array ## _func)(void) = _func ## _wrapper; |
209 | |
210 | #define DESTRUCT(_func) \ |
211 | static void _func(void); \ |
212 | static int _func ## _constructor(void) { atexit (_func); return 0; } \ |
213 | __pragma(section(".CRT$XCU",read)) \ |
214 | __declspec(allocate(".CRT$XCU")) static int (* _array ## _func)(void) = _func ## _constructor; |
215 | |
216 | #else |
217 | #error "You will need constructor support for your compiler" |
218 | #endif |
219 | |
220 | struct api { |
221 | #ifndef _WIN32 |
222 | /* |
223 | * Locking for making sure we don't double-dlopen(). |
224 | */ |
225 | pthread_mutex_t mutex; |
226 | #endif |
227 | |
228 | /* |
229 | * dlopen() return value for the GLX API. This is libGLX.so.1 if the |
230 | * runtime is glvnd-enabled, else libGL.so.1 |
231 | */ |
232 | void *glx_handle; |
233 | |
234 | /* |
235 | * dlopen() return value for the desktop GL library. |
236 | * |
237 | * On Windows this is OPENGL32. On OSX this is classic libGL. On Linux |
238 | * this is either libOpenGL (if the runtime is glvnd-enabled) or |
239 | * classic libGL.so.1 |
240 | */ |
241 | void *gl_handle; |
242 | |
243 | /* dlopen() return value for libEGL.so.1 */ |
244 | void *egl_handle; |
245 | |
246 | /* dlopen() return value for libGLESv1_CM.so.1 */ |
247 | void *gles1_handle; |
248 | |
249 | /* dlopen() return value for libGLESv2.so.2 */ |
250 | void *gles2_handle; |
251 | |
252 | /* |
253 | * This value gets incremented when any thread is in |
254 | * glBegin()/glEnd() called through epoxy. |
255 | * |
256 | * We're not guaranteed to be called through our wrapper, so the |
257 | * conservative paths also try to handle the failure cases they'll |
258 | * see if begin_count didn't reflect reality. It's also a bit of |
259 | * a bug that the conservative paths might return success because |
260 | * some other thread was in epoxy glBegin/glEnd while our thread |
261 | * is trying to resolve, but given that it's basically just for |
262 | * informative error messages, we shouldn't need to care. |
263 | */ |
264 | long begin_count; |
265 | }; |
266 | |
267 | static struct api api = { |
268 | #ifndef _WIN32 |
269 | .mutex = PTHREAD_MUTEX_INITIALIZER, |
270 | #else |
271 | 0, |
272 | #endif |
273 | }; |
274 | |
275 | static bool library_initialized; |
276 | |
277 | static bool epoxy_current_context_is_glx(void); |
278 | |
279 | #if PLATFORM_HAS_EGL |
280 | static EGLenum |
281 | epoxy_egl_get_current_gl_context_api(void); |
282 | #endif |
283 | |
284 | CONSTRUCT (library_init) |
285 | |
286 | static void |
287 | library_init(void) |
288 | { |
289 | library_initialized = true; |
290 | } |
291 | |
292 | static bool |
293 | get_dlopen_handle(void **handle, const char *lib_name, bool exit_on_fail, bool load) |
294 | { |
295 | if (*handle) |
296 | return true; |
297 | |
298 | if (!library_initialized) { |
299 | fputs(s: "Attempting to dlopen() while in the dynamic linker.\n" , stderr); |
300 | abort(); |
301 | } |
302 | |
303 | #ifdef _WIN32 |
304 | *handle = LoadLibraryA(lib_name); |
305 | #else |
306 | pthread_mutex_lock(mutex: &api.mutex); |
307 | if (!*handle) { |
308 | int flags = RTLD_LAZY | RTLD_LOCAL; |
309 | if (!load) |
310 | flags |= RTLD_NOLOAD; |
311 | |
312 | *handle = dlopen(file: lib_name, mode: flags); |
313 | if (!*handle) { |
314 | if (exit_on_fail) { |
315 | fprintf(stderr, format: "Couldn't open %s: %s\n" , lib_name, dlerror()); |
316 | abort(); |
317 | } else { |
318 | (void)dlerror(); |
319 | } |
320 | } |
321 | } |
322 | pthread_mutex_unlock(mutex: &api.mutex); |
323 | #endif |
324 | |
325 | return *handle != NULL; |
326 | } |
327 | |
328 | static void * |
329 | do_dlsym(void **handle, const char *name, bool exit_on_fail) |
330 | { |
331 | void *result; |
332 | const char *error = "" ; |
333 | |
334 | #ifdef _WIN32 |
335 | result = GetProcAddress(*handle, name); |
336 | #else |
337 | result = dlsym(handle: *handle, name: name); |
338 | if (!result) |
339 | error = dlerror(); |
340 | #endif |
341 | if (!result && exit_on_fail) { |
342 | fprintf(stderr, format: "%s() not found: %s\n" , name, error); |
343 | abort(); |
344 | } |
345 | |
346 | return result; |
347 | } |
348 | |
349 | /** |
350 | * @brief Checks whether we're using OpenGL or OpenGL ES |
351 | * |
352 | * @return `true` if we're using OpenGL |
353 | */ |
354 | bool |
355 | epoxy_is_desktop_gl(void) |
356 | { |
357 | const char *es_prefix = "OpenGL ES" ; |
358 | const char *version; |
359 | |
360 | #if PLATFORM_HAS_EGL |
361 | /* PowerVR's OpenGL ES implementation (and perhaps other) don't |
362 | * comply with the standard, which states that |
363 | * "glGetString(GL_VERSION)" should return a string starting with |
364 | * "OpenGL ES". Therefore, to distinguish desktop OpenGL from |
365 | * OpenGL ES, we must also check the context type through EGL (we |
366 | * can do that as PowerVR is only usable through EGL). |
367 | */ |
368 | if (!epoxy_current_context_is_glx()) { |
369 | switch (epoxy_egl_get_current_gl_context_api()) { |
370 | case EGL_OPENGL_API: return true; |
371 | case EGL_OPENGL_ES_API: return false; |
372 | case EGL_NONE: |
373 | default: break; |
374 | } |
375 | } |
376 | #endif |
377 | |
378 | if (api.begin_count) |
379 | return true; |
380 | |
381 | version = (const char *)glGetString(GL_VERSION); |
382 | |
383 | /* If we didn't get a version back, there are only two things that |
384 | * could have happened: either malloc failure (which basically |
385 | * doesn't exist), or we were called within a glBegin()/glEnd(). |
386 | * Assume the second, which only exists for desktop GL. |
387 | */ |
388 | if (!version) |
389 | return true; |
390 | |
391 | return strncmp(s1: es_prefix, s2: version, n: strlen(s: es_prefix)); |
392 | } |
393 | |
394 | static int |
395 | epoxy_internal_gl_version(GLenum version_string, int error_version, int factor) |
396 | { |
397 | const char *version = (const char *)glGetString(version_string); |
398 | GLint major, minor; |
399 | int scanf_count; |
400 | |
401 | if (!version) |
402 | return error_version; |
403 | |
404 | /* skip to version number */ |
405 | while (!isdigit(*version) && *version != '\0') |
406 | version++; |
407 | |
408 | /* Interpret version number */ |
409 | scanf_count = sscanf(s: version, format: "%i.%i" , &major, &minor); |
410 | if (scanf_count != 2) { |
411 | fprintf(stderr, format: "Unable to interpret GL_VERSION string: %s\n" , |
412 | version); |
413 | abort(); |
414 | } |
415 | |
416 | return factor * major + minor; |
417 | } |
418 | |
419 | /** |
420 | * @brief Returns the version of OpenGL we are using |
421 | * |
422 | * The version is encoded as: |
423 | * |
424 | * ``` |
425 | * |
426 | * version = major * 10 + minor |
427 | * |
428 | * ``` |
429 | * |
430 | * So it can be easily used for version comparisons. |
431 | * |
432 | * @return The encoded version of OpenGL we are using |
433 | */ |
434 | int |
435 | epoxy_gl_version(void) |
436 | { |
437 | return epoxy_internal_gl_version(GL_VERSION, error_version: 0, factor: 10); |
438 | } |
439 | |
440 | int |
441 | epoxy_conservative_gl_version(void) |
442 | { |
443 | if (api.begin_count) |
444 | return 100; |
445 | |
446 | return epoxy_internal_gl_version(GL_VERSION, error_version: 100, factor: 10); |
447 | } |
448 | |
449 | /** |
450 | * @brief Returns the version of the GL Shading Language we are using |
451 | * |
452 | * The version is encoded as: |
453 | * |
454 | * ``` |
455 | * |
456 | * version = major * 100 + minor |
457 | * |
458 | * ``` |
459 | * |
460 | * So it can be easily used for version comparisons. |
461 | * |
462 | * @return The encoded version of the GL Shading Language we are using |
463 | */ |
464 | int |
465 | epoxy_glsl_version(void) |
466 | { |
467 | if (epoxy_gl_version() >= 20 || |
468 | epoxy_has_gl_extension (extension: "GL_ARB_shading_language_100" )) |
469 | return epoxy_internal_gl_version(GL_SHADING_LANGUAGE_VERSION, error_version: 0, factor: 100); |
470 | |
471 | return 0; |
472 | } |
473 | |
474 | /** |
475 | * @brief Checks for the presence of an extension in an OpenGL extension string |
476 | * |
477 | * @param extension_list The string containing the list of extensions to check |
478 | * @param ext The name of the GL extension |
479 | * @return `true` if the extension is available' |
480 | * |
481 | * @note If you are looking to check whether a normal GL, EGL or GLX extension |
482 | * is supported by the client, this probably isn't the function you want. |
483 | * |
484 | * Some parts of the spec for OpenGL and friends will return an OpenGL formatted |
485 | * extension string that is separate from the usual extension strings for the |
486 | * spec. This function provides easy parsing of those strings. |
487 | * |
488 | * @see epoxy_has_gl_extension() |
489 | * @see epoxy_has_egl_extension() |
490 | * @see epoxy_has_glx_extension() |
491 | */ |
492 | bool |
493 | epoxy_extension_in_string(const char *extension_list, const char *ext) |
494 | { |
495 | const char *ptr = extension_list; |
496 | int len; |
497 | |
498 | if (!ext) |
499 | return false; |
500 | |
501 | len = strlen(s: ext); |
502 | |
503 | if (extension_list == NULL || *extension_list == '\0') |
504 | return false; |
505 | |
506 | /* Make sure that don't just find an extension with our name as a prefix. */ |
507 | while (true) { |
508 | ptr = strstr(haystack: ptr, needle: ext); |
509 | if (!ptr) |
510 | return false; |
511 | |
512 | if (ptr[len] == ' ' || ptr[len] == 0) |
513 | return true; |
514 | ptr += len; |
515 | } |
516 | } |
517 | |
518 | static bool |
519 | epoxy_internal_has_gl_extension(const char *ext, bool invalid_op_mode) |
520 | { |
521 | if (epoxy_gl_version() < 30) { |
522 | const char *exts = (const char *)glGetString(GL_EXTENSIONS); |
523 | if (!exts) |
524 | return invalid_op_mode; |
525 | return epoxy_extension_in_string(extension_list: exts, ext); |
526 | } else { |
527 | int num_extensions; |
528 | int i; |
529 | |
530 | glGetIntegerv(GL_NUM_EXTENSIONS, &num_extensions); |
531 | if (num_extensions == 0) |
532 | return invalid_op_mode; |
533 | |
534 | for (i = 0; i < num_extensions; i++) { |
535 | const char *gl_ext = (const char *)glGetStringi(GL_EXTENSIONS, i); |
536 | if (!gl_ext) |
537 | return false; |
538 | if (strcmp(s1: ext, s2: gl_ext) == 0) |
539 | return true; |
540 | } |
541 | |
542 | return false; |
543 | } |
544 | } |
545 | |
546 | bool |
547 | epoxy_load_glx(bool exit_if_fails, bool load) |
548 | { |
549 | #if PLATFORM_HAS_GLX |
550 | # ifdef GLVND_GLX_LIB |
551 | /* prefer the glvnd library if it exists */ |
552 | if (!api.glx_handle) |
553 | get_dlopen_handle(handle: &api.glx_handle, GLVND_GLX_LIB, false, load); |
554 | # endif |
555 | if (!api.glx_handle) |
556 | get_dlopen_handle(handle: &api.glx_handle, GLX_LIB, exit_on_fail: exit_if_fails, load); |
557 | #endif |
558 | return api.glx_handle != NULL; |
559 | } |
560 | |
561 | void * |
562 | epoxy_conservative_glx_dlsym(const char *name, bool exit_if_fails) |
563 | { |
564 | #if PLATFORM_HAS_GLX |
565 | if (epoxy_load_glx(exit_if_fails, load: exit_if_fails)) |
566 | return do_dlsym(handle: &api.glx_handle, name, exit_on_fail: exit_if_fails); |
567 | #endif |
568 | return NULL; |
569 | } |
570 | |
571 | /** |
572 | * Tests whether the currently bound context is EGL or GLX, trying to |
573 | * avoid loading libraries unless necessary. |
574 | */ |
575 | static bool |
576 | epoxy_current_context_is_glx(void) |
577 | { |
578 | #if !PLATFORM_HAS_GLX |
579 | return false; |
580 | #else |
581 | void *sym; |
582 | |
583 | sym = epoxy_conservative_glx_dlsym(name: "glXGetCurrentContext" , false); |
584 | if (sym) { |
585 | if (glXGetCurrentContext()) |
586 | return true; |
587 | } else { |
588 | (void)dlerror(); |
589 | } |
590 | |
591 | #if PLATFORM_HAS_EGL |
592 | sym = epoxy_conservative_egl_dlsym(name: "eglGetCurrentContext" , false); |
593 | if (sym) { |
594 | if (epoxy_egl_get_current_gl_context_api() != EGL_NONE) |
595 | return false; |
596 | } else { |
597 | (void)dlerror(); |
598 | } |
599 | #endif /* PLATFORM_HAS_EGL */ |
600 | |
601 | return false; |
602 | #endif /* PLATFORM_HAS_GLX */ |
603 | } |
604 | |
605 | /** |
606 | * @brief Returns true if the given GL extension is supported in the current context. |
607 | * |
608 | * @param ext The name of the GL extension |
609 | * @return `true` if the extension is available |
610 | * |
611 | * @note that this function can't be called from within `glBegin()` and `glEnd()`. |
612 | * |
613 | * @see epoxy_has_egl_extension() |
614 | * @see epoxy_has_glx_extension() |
615 | */ |
616 | bool |
617 | epoxy_has_gl_extension(const char *ext) |
618 | { |
619 | return epoxy_internal_has_gl_extension(ext, false); |
620 | } |
621 | |
622 | bool |
623 | epoxy_conservative_has_gl_extension(const char *ext) |
624 | { |
625 | if (api.begin_count) |
626 | return true; |
627 | |
628 | return epoxy_internal_has_gl_extension(ext, true); |
629 | } |
630 | |
631 | bool |
632 | epoxy_load_egl(bool exit_if_fails, bool load) |
633 | { |
634 | #if PLATFORM_HAS_EGL |
635 | return get_dlopen_handle(handle: &api.egl_handle, EGL_LIB, exit_on_fail: exit_if_fails, load); |
636 | #else |
637 | return false; |
638 | #endif |
639 | } |
640 | |
641 | void * |
642 | epoxy_conservative_egl_dlsym(const char *name, bool exit_if_fails) |
643 | { |
644 | #if PLATFORM_HAS_EGL |
645 | if (epoxy_load_egl(exit_if_fails, load: exit_if_fails)) |
646 | return do_dlsym(handle: &api.egl_handle, name, exit_on_fail: exit_if_fails); |
647 | #endif |
648 | return NULL; |
649 | } |
650 | |
651 | void * |
652 | epoxy_egl_dlsym(const char *name) |
653 | { |
654 | return epoxy_conservative_egl_dlsym(name, true); |
655 | } |
656 | |
657 | void * |
658 | epoxy_glx_dlsym(const char *name) |
659 | { |
660 | return epoxy_conservative_glx_dlsym(name, true); |
661 | } |
662 | |
663 | static void |
664 | epoxy_load_gl(void) |
665 | { |
666 | if (api.gl_handle) |
667 | return; |
668 | |
669 | #if defined(_WIN32) || defined(__APPLE__) |
670 | get_dlopen_handle(&api.gl_handle, OPENGL_LIB, true, true); |
671 | #else |
672 | |
673 | // Prefer GLX_LIB over OPENGL_LIB to maintain existing behavior. |
674 | // Using the inverse ordering OPENGL_LIB -> GLX_LIB, causes issues such as: |
675 | // https://github.com/anholt/libepoxy/issues/240 (apitrace missing calls) |
676 | // https://github.com/anholt/libepoxy/issues/252 (Xorg boot crash) |
677 | get_dlopen_handle(handle: &api.glx_handle, GLX_LIB, false, true); |
678 | api.gl_handle = api.glx_handle; |
679 | |
680 | #if defined(OPENGL_LIB) |
681 | if (!api.gl_handle) |
682 | get_dlopen_handle(handle: &api.gl_handle, OPENGL_LIB, false, true); |
683 | #endif |
684 | |
685 | if (!api.gl_handle) { |
686 | fprintf(stderr, format: "Couldn't open %s or %s\n" , GLX_LIB, OPENGL_LIB); |
687 | abort(); |
688 | } |
689 | |
690 | #endif |
691 | } |
692 | |
693 | void * |
694 | epoxy_gl_dlsym(const char *name) |
695 | { |
696 | epoxy_load_gl(); |
697 | |
698 | return do_dlsym(handle: &api.gl_handle, name, true); |
699 | } |
700 | |
701 | void * |
702 | epoxy_gles1_dlsym(const char *name) |
703 | { |
704 | if (epoxy_current_context_is_glx()) { |
705 | return epoxy_get_proc_address(name); |
706 | } else { |
707 | get_dlopen_handle(handle: &api.gles1_handle, GLES1_LIB, true, true); |
708 | return do_dlsym(handle: &api.gles1_handle, name, true); |
709 | } |
710 | } |
711 | |
712 | void * |
713 | epoxy_gles2_dlsym(const char *name) |
714 | { |
715 | if (epoxy_current_context_is_glx()) { |
716 | return epoxy_get_proc_address(name); |
717 | } else { |
718 | get_dlopen_handle(handle: &api.gles2_handle, GLES2_LIB, true, true); |
719 | return do_dlsym(handle: &api.gles2_handle, name, true); |
720 | } |
721 | } |
722 | |
723 | /** |
724 | * Does the appropriate dlsym() or eglGetProcAddress() for GLES3 |
725 | * functions. |
726 | * |
727 | * Mesa interpreted GLES as intending that the GLES3 functions were |
728 | * available only through eglGetProcAddress() and not dlsym(), while |
729 | * ARM's Mali drivers interpreted GLES as intending that GLES3 |
730 | * functions were available only through dlsym() and not |
731 | * eglGetProcAddress(). Thanks, Khronos. |
732 | */ |
733 | void * |
734 | epoxy_gles3_dlsym(const char *name) |
735 | { |
736 | if (epoxy_current_context_is_glx()) { |
737 | return epoxy_get_proc_address(name); |
738 | } else { |
739 | if (get_dlopen_handle(handle: &api.gles2_handle, GLES2_LIB, false, true)) { |
740 | void *func = do_dlsym(handle: &api.gles2_handle, name, false); |
741 | |
742 | if (func) |
743 | return func; |
744 | } |
745 | |
746 | return epoxy_get_proc_address(name); |
747 | } |
748 | } |
749 | |
750 | /** |
751 | * Performs either the dlsym or glXGetProcAddress()-equivalent for |
752 | * core functions in desktop GL. |
753 | */ |
754 | void * |
755 | epoxy_get_core_proc_address(const char *name, int core_version) |
756 | { |
757 | #ifdef _WIN32 |
758 | int core_symbol_support = 11; |
759 | #elif defined(__ANDROID__) |
760 | /** |
761 | * All symbols must be resolved through eglGetProcAddress |
762 | * on Android |
763 | */ |
764 | int core_symbol_support = 0; |
765 | #else |
766 | int core_symbol_support = 12; |
767 | #endif |
768 | |
769 | if (core_version <= core_symbol_support) { |
770 | return epoxy_gl_dlsym(name); |
771 | } else { |
772 | return epoxy_get_proc_address(name); |
773 | } |
774 | } |
775 | |
776 | #if PLATFORM_HAS_EGL |
777 | static EGLenum |
778 | epoxy_egl_get_current_gl_context_api(void) |
779 | { |
780 | EGLint curapi; |
781 | |
782 | if (eglQueryContext(eglGetCurrentDisplay(), eglGetCurrentContext(), |
783 | EGL_CONTEXT_CLIENT_TYPE, &curapi) == EGL_FALSE) { |
784 | (void)eglGetError(); |
785 | return EGL_NONE; |
786 | } |
787 | |
788 | return (EGLenum) curapi; |
789 | } |
790 | #endif /* PLATFORM_HAS_EGL */ |
791 | |
792 | /** |
793 | * Performs the dlsym() for the core GL 1.0 functions that we use for |
794 | * determining version and extension support for deciding on dlsym |
795 | * versus glXGetProcAddress() for all other functions. |
796 | * |
797 | * This needs to succeed on implementations without GLX (since |
798 | * glGetString() and glGetIntegerv() are both in GLES1/2 as well, and |
799 | * at call time we don't know for sure what API they're trying to use |
800 | * without inspecting contexts ourselves). |
801 | */ |
802 | void * |
803 | epoxy_get_bootstrap_proc_address(const char *name) |
804 | { |
805 | /* If we already have a library that links to libglapi loaded, |
806 | * use that. |
807 | */ |
808 | #if PLATFORM_HAS_GLX |
809 | if (api.glx_handle && glXGetCurrentContext()) |
810 | return epoxy_gl_dlsym(name); |
811 | #endif |
812 | |
813 | /* If epoxy hasn't loaded any API-specific library yet, try to |
814 | * figure out what API the context is using and use that library, |
815 | * since future calls will also use that API (this prevents a |
816 | * non-X11 ES2 context from loading a bunch of X11 junk). |
817 | */ |
818 | #if PLATFORM_HAS_EGL |
819 | get_dlopen_handle(handle: &api.egl_handle, EGL_LIB, false, true); |
820 | if (api.egl_handle) { |
821 | int version = 0; |
822 | switch (epoxy_egl_get_current_gl_context_api()) { |
823 | case EGL_OPENGL_API: |
824 | return epoxy_gl_dlsym(name); |
825 | case EGL_OPENGL_ES_API: |
826 | if (eglQueryContext(eglGetCurrentDisplay(), |
827 | eglGetCurrentContext(), |
828 | EGL_CONTEXT_CLIENT_VERSION, |
829 | &version)) { |
830 | if (version >= 2) |
831 | return epoxy_gles2_dlsym(name); |
832 | else |
833 | return epoxy_gles1_dlsym(name); |
834 | } |
835 | } |
836 | } |
837 | #endif /* PLATFORM_HAS_EGL */ |
838 | |
839 | /* Fall back to GLX */ |
840 | return epoxy_gl_dlsym(name); |
841 | } |
842 | |
843 | void * |
844 | epoxy_get_proc_address(const char *name) |
845 | { |
846 | #if PLATFORM_HAS_EGL |
847 | GLenum egl_api = EGL_NONE; |
848 | |
849 | if (!epoxy_current_context_is_glx()) |
850 | egl_api = epoxy_egl_get_current_gl_context_api(); |
851 | |
852 | switch (egl_api) { |
853 | case EGL_OPENGL_API: |
854 | case EGL_OPENGL_ES_API: |
855 | return eglGetProcAddress(name); |
856 | case EGL_NONE: |
857 | break; |
858 | } |
859 | #endif |
860 | |
861 | #if defined(_WIN32) |
862 | return wglGetProcAddress(name); |
863 | #elif defined(__APPLE__) |
864 | return epoxy_gl_dlsym(name); |
865 | #elif PLATFORM_HAS_GLX |
866 | if (epoxy_current_context_is_glx()) |
867 | return glXGetProcAddressARB((const GLubyte *)name); |
868 | assert(0 && "Couldn't find current GLX or EGL context.\n" ); |
869 | #endif |
870 | |
871 | return NULL; |
872 | } |
873 | |
874 | WRAPPER_VISIBILITY (void) |
875 | WRAPPER(epoxy_glBegin)(GLenum primtype) |
876 | { |
877 | #ifdef _WIN32 |
878 | InterlockedIncrement(&api.begin_count); |
879 | #else |
880 | pthread_mutex_lock(mutex: &api.mutex); |
881 | api.begin_count++; |
882 | pthread_mutex_unlock(mutex: &api.mutex); |
883 | #endif |
884 | |
885 | epoxy_glBegin_unwrapped(primtype); |
886 | } |
887 | |
888 | WRAPPER_VISIBILITY (void) |
889 | WRAPPER(epoxy_glEnd)(void) |
890 | { |
891 | epoxy_glEnd_unwrapped(); |
892 | |
893 | #ifdef _WIN32 |
894 | InterlockedDecrement(&api.begin_count); |
895 | #else |
896 | pthread_mutex_lock(mutex: &api.mutex); |
897 | api.begin_count--; |
898 | pthread_mutex_unlock(mutex: &api.mutex); |
899 | #endif |
900 | } |
901 | |
902 | PFNGLBEGINPROC epoxy_glBegin = epoxy_glBegin_wrapped; |
903 | PFNGLENDPROC epoxy_glEnd = epoxy_glEnd_wrapped; |
904 | |
905 | epoxy_resolver_failure_handler_t epoxy_resolver_failure_handler; |
906 | |
907 | /** |
908 | * Sets the function that will be called every time Epoxy fails to |
909 | * resolve a symbol. |
910 | * |
911 | * @param handler The new handler function |
912 | * @return The previous handler function |
913 | */ |
914 | epoxy_resolver_failure_handler_t |
915 | epoxy_set_resolver_failure_handler(epoxy_resolver_failure_handler_t handler) |
916 | { |
917 | #ifdef _WIN32 |
918 | return InterlockedExchangePointer((void**)&epoxy_resolver_failure_handler, |
919 | handler); |
920 | #else |
921 | epoxy_resolver_failure_handler_t old; |
922 | pthread_mutex_lock(mutex: &api.mutex); |
923 | old = epoxy_resolver_failure_handler; |
924 | epoxy_resolver_failure_handler = handler; |
925 | pthread_mutex_unlock(mutex: &api.mutex); |
926 | return old; |
927 | #endif |
928 | } |
929 | |