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