src/video/SDL_egl.c
author Gabriel Jacobo <gabomdq@gmail.com>
Tue, 14 Jan 2014 21:29:38 -0300
changeset 8105 f74bf1bbe472
parent 8093 b43765095a6f
child 8149 681eb46b8ac4
permissions -rw-r--r--
Make EGL loading more resilient.

Lesson learned while trying to run L4D2 under Wayland :)
     1 /*
     2  *  Simple DirectMedia Layer
     3  *  Copyright (C) 1997-2013 Sam Lantinga <slouken@libsdl.org>
     4  * 
     5  *  This software is provided 'as-is', without any express or implied
     6  *  warranty.  In no event will the authors be held liable for any damages
     7  *  arising from the use of this software.
     8  * 
     9  *  Permission is granted to anyone to use this software for any purpose,
    10  *  including commercial applications, and to alter it and redistribute it
    11  *  freely, subject to the following restrictions:
    12  * 
    13  *  1. The origin of this software must not be misrepresented; you must not
    14  *     claim that you wrote the original software. If you use this software
    15  *     in a product, an acknowledgment in the product documentation would be
    16  *     appreciated but is not required.
    17  *  2. Altered source versions must be plainly marked as such, and must not be
    18  *     misrepresented as being the original software.
    19  *  3. This notice may not be removed or altered from any source distribution.
    20  */
    21 #include "../SDL_internal.h"
    22 
    23 #if SDL_VIDEO_OPENGL_EGL
    24 
    25 #include "SDL_sysvideo.h"
    26 #include "SDL_egl_c.h"
    27 #include "SDL_loadso.h"
    28 #include "SDL_hints.h"
    29 
    30 #if SDL_VIDEO_DRIVER_RPI
    31 /* Raspbian places the OpenGL ES/EGL binaries in a non standard path */
    32 #define DEFAULT_EGL "/opt/vc/lib/libEGL.so"
    33 #define DEFAULT_OGL_ES2 "/opt/vc/lib/libGLESv2.so"
    34 #define DEFAULT_OGL_ES_PVR "/opt/vc/lib/libGLES_CM.so"
    35 #define DEFAULT_OGL_ES "/opt/vc/lib/libGLESv1_CM.so"
    36 
    37 #elif SDL_VIDEO_DRIVER_ANDROID
    38 /* Android */
    39 #define DEFAULT_EGL "libEGL.so"
    40 #define DEFAULT_OGL_ES2 "libGLESv2.so"
    41 #define DEFAULT_OGL_ES_PVR "libGLES_CM.so"
    42 #define DEFAULT_OGL_ES "libGLESv1_CM.so"
    43 
    44 #elif SDL_VIDEO_DRIVER_WINDOWS
    45 /* EGL AND OpenGL ES support via ANGLE */
    46 #define DEFAULT_EGL "libEGL.dll"
    47 #define DEFAULT_OGL_ES2 "libGLESv2.dll"
    48 #define DEFAULT_OGL_ES_PVR "libGLES_CM.dll"
    49 #define DEFAULT_OGL_ES "libGLESv1_CM.dll"
    50 
    51 #else
    52 /* Desktop Linux */
    53 #define DEFAULT_OGL "libGL.so.1"
    54 #define DEFAULT_EGL "libEGL.so.1"
    55 #define DEFAULT_OGL_ES2 "libGLESv2.so.2"
    56 #define DEFAULT_OGL_ES_PVR "libGLES_CM.so.1"
    57 #define DEFAULT_OGL_ES "libGLESv1_CM.so.1"
    58 #endif /* SDL_VIDEO_DRIVER_RPI */
    59 
    60 #define LOAD_FUNC(NAME) \
    61 *((void**)&_this->egl_data->NAME) = SDL_LoadFunction(_this->egl_data->dll_handle, #NAME); \
    62 if (!_this->egl_data->NAME) \
    63 { \
    64     return SDL_SetError("Could not retrieve EGL function " #NAME); \
    65 }
    66     
    67 /* EGL implementation of SDL OpenGL ES support */
    68 
    69 void *
    70 SDL_EGL_GetProcAddress(_THIS, const char *proc)
    71 {
    72     static char procname[1024];
    73     void *retval;
    74     
    75     /* eglGetProcAddress is busted on Android http://code.google.com/p/android/issues/detail?id=7681 */
    76 #if !defined(SDL_VIDEO_DRIVER_ANDROID) 
    77     if (_this->egl_data->eglGetProcAddress) {
    78         retval = _this->egl_data->eglGetProcAddress(proc);
    79         if (retval) {
    80             return retval;
    81         }
    82     }
    83 #endif
    84     
    85     retval = SDL_LoadFunction(_this->egl_data->egl_dll_handle, proc);
    86     if (!retval && SDL_strlen(proc) <= 1022) {
    87         procname[0] = '_';
    88         SDL_strlcpy(procname + 1, proc, 1022);
    89         retval = SDL_LoadFunction(_this->egl_data->egl_dll_handle, procname);
    90     }
    91     return retval;
    92 }
    93 
    94 void
    95 SDL_EGL_UnloadLibrary(_THIS)
    96 {
    97     if (_this->egl_data) {
    98         if (_this->egl_data->egl_display) {
    99             _this->egl_data->eglTerminate(_this->egl_data->egl_display);
   100             _this->egl_data->egl_display = NULL;
   101         }
   102 
   103         if (_this->egl_data->dll_handle) {
   104             SDL_UnloadObject(_this->egl_data->dll_handle);
   105             _this->egl_data->dll_handle = NULL;
   106         }
   107         if (_this->egl_data->egl_dll_handle) {
   108             SDL_UnloadObject(_this->egl_data->egl_dll_handle);
   109             _this->egl_data->egl_dll_handle = NULL;
   110         }
   111         
   112         SDL_free(_this->egl_data);
   113         _this->egl_data = NULL;
   114     }
   115 }
   116 
   117 int
   118 SDL_EGL_LoadLibrary(_THIS, const char *egl_path, NativeDisplayType native_display)
   119 {
   120     void *dll_handle = NULL, *egl_dll_handle = NULL; /* The naming is counter intuitive, but hey, I just work here -- Gabriel */
   121     char *path = NULL;
   122 #if SDL_VIDEO_DRIVER_WINDOWS
   123     const char *d3dcompiler;
   124 #endif
   125 
   126     if (_this->egl_data) {
   127         return SDL_SetError("OpenGL ES context already created");
   128     }
   129 
   130     _this->egl_data = (struct SDL_EGL_VideoData *) SDL_calloc(1, sizeof(SDL_EGL_VideoData));
   131     if (!_this->egl_data) {
   132         return SDL_OutOfMemory();
   133     }
   134 
   135 #if SDL_VIDEO_DRIVER_WINDOWS
   136     d3dcompiler = SDL_GetHint(SDL_HINT_VIDEO_WIN_D3DCOMPILER);
   137     if (!d3dcompiler) {
   138         /* By default we load the Vista+ compatible compiler */
   139         d3dcompiler = "d3dcompiler_46.dll";
   140     }
   141     if (SDL_strcasecmp(d3dcompiler, "none") != 0) {
   142         SDL_LoadObject(d3dcompiler);
   143     }
   144 #endif
   145 
   146     /* A funny thing, loading EGL.so first does not work on the Raspberry, so we load libGL* first */
   147     path = SDL_getenv("SDL_VIDEO_GL_DRIVER");
   148     if (path != NULL) {
   149         egl_dll_handle = SDL_LoadObject(path);
   150     }
   151 
   152     if (egl_dll_handle == NULL) {
   153         if(_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_ES) {
   154             if (_this->gl_config.major_version > 1) {
   155                 path = DEFAULT_OGL_ES2;
   156                 egl_dll_handle = SDL_LoadObject(path);
   157             }
   158             else {
   159                 path = DEFAULT_OGL_ES;
   160                 egl_dll_handle = SDL_LoadObject(path);
   161                 if (egl_dll_handle == NULL) {
   162                     path = DEFAULT_OGL_ES_PVR;
   163                     egl_dll_handle = SDL_LoadObject(path);
   164                 }
   165             }
   166         }
   167 #ifdef DEFAULT_OGL         
   168         else {
   169             path = DEFAULT_OGL;
   170             egl_dll_handle = SDL_LoadObject(path);
   171         }
   172 #endif        
   173     }
   174     _this->egl_data->egl_dll_handle = egl_dll_handle;
   175 
   176     if (egl_dll_handle == NULL) {
   177         return SDL_SetError("Could not initialize OpenGL / GLES library");
   178     }
   179 
   180     /* Loading libGL* in the previous step took care of loading libEGL.so, but we future proof by double checking */
   181     if (egl_path != NULL) {
   182         dll_handle = SDL_LoadObject(egl_path);
   183     }   
   184     /* Try loading a EGL symbol, if it does not work try the default library paths */
   185     if (SDL_LoadFunction(dll_handle, "eglChooseConfig") == NULL) {
   186         if (dll_handle != NULL) {
   187             SDL_UnloadObject(dll_handle);
   188         }
   189         path = SDL_getenv("SDL_VIDEO_EGL_DRIVER");
   190         if (path == NULL) {
   191             path = DEFAULT_EGL;
   192         }
   193         dll_handle = SDL_LoadObject(path);
   194         if (dll_handle == NULL) {
   195             return SDL_SetError("Could not load EGL library");
   196         }
   197     }
   198 
   199     _this->egl_data->dll_handle = dll_handle;
   200 
   201     /* Load new function pointers */
   202     LOAD_FUNC(eglGetDisplay);
   203     LOAD_FUNC(eglInitialize);
   204     LOAD_FUNC(eglTerminate);
   205     LOAD_FUNC(eglGetProcAddress);
   206     LOAD_FUNC(eglChooseConfig);
   207     LOAD_FUNC(eglGetConfigAttrib);
   208     LOAD_FUNC(eglCreateContext);
   209     LOAD_FUNC(eglDestroyContext);
   210     LOAD_FUNC(eglCreateWindowSurface);
   211     LOAD_FUNC(eglDestroySurface);
   212     LOAD_FUNC(eglMakeCurrent);
   213     LOAD_FUNC(eglSwapBuffers);
   214     LOAD_FUNC(eglSwapInterval);
   215     LOAD_FUNC(eglWaitNative);
   216     LOAD_FUNC(eglWaitGL);
   217     LOAD_FUNC(eglBindAPI);
   218     
   219     _this->egl_data->egl_display = _this->egl_data->eglGetDisplay(native_display);
   220     if (!_this->egl_data->egl_display) {
   221         return SDL_SetError("Could not get EGL display");
   222     }
   223     
   224     if (_this->egl_data->eglInitialize(_this->egl_data->egl_display, NULL, NULL) != EGL_TRUE) {
   225         return SDL_SetError("Could not initialize EGL");
   226     }
   227 
   228     _this->egl_data->dll_handle = dll_handle;
   229     _this->egl_data->egl_dll_handle = egl_dll_handle;
   230     _this->gl_config.driver_loaded = 1;
   231 
   232     if (path) {
   233         SDL_strlcpy(_this->gl_config.driver_path, path, sizeof(_this->gl_config.driver_path) - 1);
   234     } else {
   235         *_this->gl_config.driver_path = '\0';
   236     }
   237     
   238     return 0;
   239 }
   240 
   241 int
   242 SDL_EGL_ChooseConfig(_THIS) 
   243 {
   244     /* 64 seems nice. */
   245     EGLint attribs[64];
   246     EGLint found_configs = 0, value;
   247     /* 128 seems even nicer here */
   248     EGLConfig configs[128];
   249     int i, j, best_bitdiff = -1, bitdiff;
   250     
   251     if (!_this->egl_data) {
   252         /* The EGL library wasn't loaded, SDL_GetError() should have info */
   253         return -1;
   254     }
   255   
   256     /* Get a valid EGL configuration */
   257     i = 0;
   258     attribs[i++] = EGL_RED_SIZE;
   259     attribs[i++] = _this->gl_config.red_size;
   260     attribs[i++] = EGL_GREEN_SIZE;
   261     attribs[i++] = _this->gl_config.green_size;
   262     attribs[i++] = EGL_BLUE_SIZE;
   263     attribs[i++] = _this->gl_config.blue_size;
   264     
   265     if (_this->gl_config.alpha_size) {
   266         attribs[i++] = EGL_ALPHA_SIZE;
   267         attribs[i++] = _this->gl_config.alpha_size;
   268     }
   269     
   270     if (_this->gl_config.buffer_size) {
   271         attribs[i++] = EGL_BUFFER_SIZE;
   272         attribs[i++] = _this->gl_config.buffer_size;
   273     }
   274     
   275     attribs[i++] = EGL_DEPTH_SIZE;
   276     attribs[i++] = _this->gl_config.depth_size;
   277     
   278     if (_this->gl_config.stencil_size) {
   279         attribs[i++] = EGL_STENCIL_SIZE;
   280         attribs[i++] = _this->gl_config.stencil_size;
   281     }
   282     
   283     if (_this->gl_config.multisamplebuffers) {
   284         attribs[i++] = EGL_SAMPLE_BUFFERS;
   285         attribs[i++] = _this->gl_config.multisamplebuffers;
   286     }
   287     
   288     if (_this->gl_config.multisamplesamples) {
   289         attribs[i++] = EGL_SAMPLES;
   290         attribs[i++] = _this->gl_config.multisamplesamples;
   291     }
   292     
   293     attribs[i++] = EGL_RENDERABLE_TYPE;
   294     if(_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_ES) {
   295         if (_this->gl_config.major_version == 2) {
   296             attribs[i++] = EGL_OPENGL_ES2_BIT;
   297         } else {
   298             attribs[i++] = EGL_OPENGL_ES_BIT;
   299         }
   300         _this->egl_data->eglBindAPI(EGL_OPENGL_ES_API);
   301     }
   302     else {
   303         attribs[i++] = EGL_OPENGL_BIT;
   304         _this->egl_data->eglBindAPI(EGL_OPENGL_API);
   305     }
   306     
   307     attribs[i++] = EGL_NONE;
   308    
   309     if (_this->egl_data->eglChooseConfig(_this->egl_data->egl_display,
   310         attribs,
   311         configs, SDL_arraysize(configs),
   312         &found_configs) == EGL_FALSE ||
   313         found_configs == 0) {
   314         return SDL_SetError("Couldn't find matching EGL config");
   315     }
   316     
   317     /* eglChooseConfig returns a number of configurations that match or exceed the requested attribs. */
   318     /* From those, we select the one that matches our requirements more closely via a makeshift algorithm */
   319 
   320     for ( i=0; i<found_configs; i++ ) {
   321         bitdiff = 0;
   322         for (j = 0; j < SDL_arraysize(attribs) - 1; j += 2) {
   323             if (attribs[j] == EGL_NONE) {
   324                break;
   325             }
   326             
   327             if ( attribs[j+1] != EGL_DONT_CARE && (
   328                 attribs[j] == EGL_RED_SIZE ||
   329                 attribs[j] == EGL_GREEN_SIZE ||
   330                 attribs[j] == EGL_BLUE_SIZE ||
   331                 attribs[j] == EGL_ALPHA_SIZE ||
   332                 attribs[j] == EGL_DEPTH_SIZE ||
   333                 attribs[j] == EGL_STENCIL_SIZE)) {
   334                 _this->egl_data->eglGetConfigAttrib(_this->egl_data->egl_display, configs[i], attribs[j], &value);
   335                 bitdiff += value - attribs[j + 1]; /* value is always >= attrib */
   336             }
   337         }
   338 
   339         if (bitdiff < best_bitdiff || best_bitdiff == -1) {
   340             _this->egl_data->egl_config = configs[i];
   341             
   342             best_bitdiff = bitdiff;
   343         }
   344            
   345         if (bitdiff == 0) break; /* we found an exact match! */
   346     }
   347     
   348     return 0;
   349 }
   350 
   351 SDL_GLContext
   352 SDL_EGL_CreateContext(_THIS, EGLSurface egl_surface)
   353 {
   354     EGLint context_attrib_list[] = {
   355         EGL_CONTEXT_CLIENT_VERSION,
   356         1,
   357         EGL_NONE
   358     };
   359     
   360     EGLContext egl_context, share_context = EGL_NO_CONTEXT;
   361     
   362     if (!_this->egl_data) {
   363         /* The EGL library wasn't loaded, SDL_GetError() should have info */
   364         return NULL;
   365     }
   366     
   367     if (_this->gl_config.share_with_current_context) {
   368         share_context = (EGLContext)SDL_GL_GetCurrentContext();
   369     }
   370     
   371     /* Bind the API */
   372     if(_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_ES) {
   373         _this->egl_data->eglBindAPI(EGL_OPENGL_ES_API);
   374         if (_this->gl_config.major_version) {
   375             context_attrib_list[1] = _this->gl_config.major_version;
   376         }
   377 
   378         egl_context = _this->egl_data->eglCreateContext(_this->egl_data->egl_display,
   379                                           _this->egl_data->egl_config,
   380                                           share_context, context_attrib_list);
   381     }
   382     else {
   383         _this->egl_data->eglBindAPI(EGL_OPENGL_API);
   384         egl_context = _this->egl_data->eglCreateContext(_this->egl_data->egl_display,
   385                                           _this->egl_data->egl_config,
   386                                           share_context, NULL);
   387     }
   388     
   389     if (egl_context == EGL_NO_CONTEXT) {
   390         SDL_SetError("Could not create EGL context");
   391         return NULL;
   392     }
   393     
   394     _this->egl_data->egl_swapinterval = 0;
   395     
   396     if (SDL_EGL_MakeCurrent(_this, egl_surface, egl_context) < 0) {
   397         SDL_EGL_DeleteContext(_this, egl_context);
   398         SDL_SetError("Could not make EGL context current");
   399         return NULL;
   400     }
   401   
   402     return (SDL_GLContext) egl_context;
   403 }
   404 
   405 int
   406 SDL_EGL_MakeCurrent(_THIS, EGLSurface egl_surface, SDL_GLContext context)
   407 {
   408     EGLContext egl_context = (EGLContext) context;
   409 
   410     if (!_this->egl_data) {
   411         return SDL_SetError("OpenGL not initialized");
   412     }
   413     
   414     /* The android emulator crashes badly if you try to eglMakeCurrent 
   415      * with a valid context and invalid surface, so we have to check for both here.
   416      */
   417     if (!egl_context || !egl_surface) {
   418          _this->egl_data->eglMakeCurrent(_this->egl_data->egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
   419     }
   420     else {
   421         if (!_this->egl_data->eglMakeCurrent(_this->egl_data->egl_display,
   422             egl_surface, egl_surface, egl_context)) {
   423             return SDL_SetError("Unable to make EGL context current");
   424         }
   425     }
   426       
   427     return 0;
   428 }
   429 
   430 int
   431 SDL_EGL_SetSwapInterval(_THIS, int interval)
   432 {
   433     EGLBoolean status;
   434     
   435     if (!_this->egl_data) {
   436         return SDL_SetError("EGL not initialized");
   437     }
   438     
   439     status = _this->egl_data->eglSwapInterval(_this->egl_data->egl_display, interval);
   440     if (status == EGL_TRUE) {
   441         _this->egl_data->egl_swapinterval = interval;
   442         return 0;
   443     }
   444     
   445     return SDL_SetError("Unable to set the EGL swap interval");
   446 }
   447 
   448 int
   449 SDL_EGL_GetSwapInterval(_THIS)
   450 {
   451     if (!_this->egl_data) {
   452         return SDL_SetError("EGL not initialized");
   453     }
   454     
   455     return _this->egl_data->egl_swapinterval;
   456 }
   457 
   458 void
   459 SDL_EGL_SwapBuffers(_THIS, EGLSurface egl_surface)
   460 {
   461     _this->egl_data->eglSwapBuffers(_this->egl_data->egl_display, egl_surface);
   462 }
   463 
   464 void
   465 SDL_EGL_DeleteContext(_THIS, SDL_GLContext context)
   466 {
   467     EGLContext egl_context = (EGLContext) context;
   468 
   469     /* Clean up GLES and EGL */
   470     if (!_this->egl_data) {
   471         return;
   472     }
   473     
   474     if (egl_context != NULL && egl_context != EGL_NO_CONTEXT) {
   475         SDL_EGL_MakeCurrent(_this, NULL, NULL);
   476         _this->egl_data->eglDestroyContext(_this->egl_data->egl_display, egl_context);
   477     }
   478         
   479 }
   480 
   481 EGLSurface *
   482 SDL_EGL_CreateSurface(_THIS, NativeWindowType nw) 
   483 {
   484     if (SDL_EGL_ChooseConfig(_this) != 0) {
   485         return EGL_NO_SURFACE;
   486     }
   487     
   488     return _this->egl_data->eglCreateWindowSurface(
   489             _this->egl_data->egl_display,
   490             _this->egl_data->egl_config,
   491             nw, NULL);
   492 }
   493 
   494 void
   495 SDL_EGL_DestroySurface(_THIS, EGLSurface egl_surface) 
   496 {
   497     if (!_this->egl_data) {
   498         return;
   499     }
   500     
   501     if (egl_surface != EGL_NO_SURFACE) {
   502         _this->egl_data->eglDestroySurface(_this->egl_data->egl_display, egl_surface);
   503     }
   504 }
   505 
   506 #endif /* SDL_VIDEO_OPENGL_EGL */
   507 
   508 /* vi: set ts=4 sw=4 expandtab: */
   509