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