src/video/SDL_egl.c
author Sam Lantinga <slouken@libsdl.org>
Wed, 23 Apr 2014 13:47:52 -0700
changeset 8734 cb61954403ca
parent 8733 02b7b0448811
child 8740 fbe394d76273
permissions -rw-r--r--
Fixed compiler warning
     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) 
    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 = "none";
   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 (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) {
   202             return SDL_SetError("Could not load EGL library");
   203         }
   204     }
   205 
   206     _this->egl_data->dll_handle = dll_handle;
   207 
   208     /* Load new function pointers */
   209     LOAD_FUNC(eglGetDisplay);
   210     LOAD_FUNC(eglInitialize);
   211     LOAD_FUNC(eglTerminate);
   212     LOAD_FUNC(eglGetProcAddress);
   213     LOAD_FUNC(eglChooseConfig);
   214     LOAD_FUNC(eglGetConfigAttrib);
   215     LOAD_FUNC(eglCreateContext);
   216     LOAD_FUNC(eglDestroyContext);
   217     LOAD_FUNC(eglCreateWindowSurface);
   218     LOAD_FUNC(eglDestroySurface);
   219     LOAD_FUNC(eglMakeCurrent);
   220     LOAD_FUNC(eglSwapBuffers);
   221     LOAD_FUNC(eglSwapInterval);
   222     LOAD_FUNC(eglWaitNative);
   223     LOAD_FUNC(eglWaitGL);
   224     LOAD_FUNC(eglBindAPI);
   225     
   226 #if !defined(__WINRT__)
   227     _this->egl_data->egl_display = _this->egl_data->eglGetDisplay(native_display);
   228     if (!_this->egl_data->egl_display) {
   229         return SDL_SetError("Could not get EGL display");
   230     }
   231     
   232     if (_this->egl_data->eglInitialize(_this->egl_data->egl_display, NULL, NULL) != EGL_TRUE) {
   233         return SDL_SetError("Could not initialize EGL");
   234     }
   235 #endif
   236 
   237     _this->egl_data->dll_handle = dll_handle;
   238     _this->egl_data->egl_dll_handle = egl_dll_handle;
   239     _this->gl_config.driver_loaded = 1;
   240 
   241     if (path) {
   242         SDL_strlcpy(_this->gl_config.driver_path, path, sizeof(_this->gl_config.driver_path) - 1);
   243     } else {
   244         *_this->gl_config.driver_path = '\0';
   245     }
   246     
   247     return 0;
   248 }
   249 
   250 int
   251 SDL_EGL_ChooseConfig(_THIS) 
   252 {
   253     /* 64 seems nice. */
   254     EGLint attribs[64];
   255     EGLint found_configs = 0, value;
   256     /* 128 seems even nicer here */
   257     EGLConfig configs[128];
   258     int i, j, best_bitdiff = -1, bitdiff;
   259     
   260     if (!_this->egl_data) {
   261         /* The EGL library wasn't loaded, SDL_GetError() should have info */
   262         return -1;
   263     }
   264   
   265     /* Get a valid EGL configuration */
   266     i = 0;
   267     attribs[i++] = EGL_RED_SIZE;
   268     attribs[i++] = _this->gl_config.red_size;
   269     attribs[i++] = EGL_GREEN_SIZE;
   270     attribs[i++] = _this->gl_config.green_size;
   271     attribs[i++] = EGL_BLUE_SIZE;
   272     attribs[i++] = _this->gl_config.blue_size;
   273     
   274     if (_this->gl_config.alpha_size) {
   275         attribs[i++] = EGL_ALPHA_SIZE;
   276         attribs[i++] = _this->gl_config.alpha_size;
   277     }
   278     
   279     if (_this->gl_config.buffer_size) {
   280         attribs[i++] = EGL_BUFFER_SIZE;
   281         attribs[i++] = _this->gl_config.buffer_size;
   282     }
   283     
   284     attribs[i++] = EGL_DEPTH_SIZE;
   285     attribs[i++] = _this->gl_config.depth_size;
   286     
   287     if (_this->gl_config.stencil_size) {
   288         attribs[i++] = EGL_STENCIL_SIZE;
   289         attribs[i++] = _this->gl_config.stencil_size;
   290     }
   291     
   292     if (_this->gl_config.multisamplebuffers) {
   293         attribs[i++] = EGL_SAMPLE_BUFFERS;
   294         attribs[i++] = _this->gl_config.multisamplebuffers;
   295     }
   296     
   297     if (_this->gl_config.multisamplesamples) {
   298         attribs[i++] = EGL_SAMPLES;
   299         attribs[i++] = _this->gl_config.multisamplesamples;
   300     }
   301     
   302     attribs[i++] = EGL_RENDERABLE_TYPE;
   303     if(_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_ES) {
   304         if (_this->gl_config.major_version == 2) {
   305             attribs[i++] = EGL_OPENGL_ES2_BIT;
   306         } else {
   307             attribs[i++] = EGL_OPENGL_ES_BIT;
   308         }
   309         _this->egl_data->eglBindAPI(EGL_OPENGL_ES_API);
   310     }
   311     else {
   312         attribs[i++] = EGL_OPENGL_BIT;
   313         _this->egl_data->eglBindAPI(EGL_OPENGL_API);
   314     }
   315     
   316     attribs[i++] = EGL_NONE;
   317    
   318     if (_this->egl_data->eglChooseConfig(_this->egl_data->egl_display,
   319         attribs,
   320         configs, SDL_arraysize(configs),
   321         &found_configs) == EGL_FALSE ||
   322         found_configs == 0) {
   323         return SDL_SetError("Couldn't find matching EGL config");
   324     }
   325     
   326     /* eglChooseConfig returns a number of configurations that match or exceed the requested attribs. */
   327     /* From those, we select the one that matches our requirements more closely via a makeshift algorithm */
   328 
   329     for ( i=0; i<found_configs; i++ ) {
   330         bitdiff = 0;
   331         for (j = 0; j < SDL_arraysize(attribs) - 1; j += 2) {
   332             if (attribs[j] == EGL_NONE) {
   333                break;
   334             }
   335             
   336             if ( attribs[j+1] != EGL_DONT_CARE && (
   337                 attribs[j] == EGL_RED_SIZE ||
   338                 attribs[j] == EGL_GREEN_SIZE ||
   339                 attribs[j] == EGL_BLUE_SIZE ||
   340                 attribs[j] == EGL_ALPHA_SIZE ||
   341                 attribs[j] == EGL_DEPTH_SIZE ||
   342                 attribs[j] == EGL_STENCIL_SIZE)) {
   343                 _this->egl_data->eglGetConfigAttrib(_this->egl_data->egl_display, configs[i], attribs[j], &value);
   344                 bitdiff += value - attribs[j + 1]; /* value is always >= attrib */
   345             }
   346         }
   347 
   348         if (bitdiff < best_bitdiff || best_bitdiff == -1) {
   349             _this->egl_data->egl_config = configs[i];
   350             
   351             best_bitdiff = bitdiff;
   352         }
   353            
   354         if (bitdiff == 0) break; /* we found an exact match! */
   355     }
   356     
   357     return 0;
   358 }
   359 
   360 SDL_GLContext
   361 SDL_EGL_CreateContext(_THIS, EGLSurface egl_surface)
   362 {
   363     EGLint context_attrib_list[] = {
   364         EGL_CONTEXT_CLIENT_VERSION,
   365         1,
   366         EGL_NONE
   367     };
   368     
   369     EGLContext egl_context, share_context = EGL_NO_CONTEXT;
   370     
   371     if (!_this->egl_data) {
   372         /* The EGL library wasn't loaded, SDL_GetError() should have info */
   373         return NULL;
   374     }
   375     
   376     if (_this->gl_config.share_with_current_context) {
   377         share_context = (EGLContext)SDL_GL_GetCurrentContext();
   378     }
   379     
   380     /* Bind the API */
   381     if(_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_ES) {
   382         _this->egl_data->eglBindAPI(EGL_OPENGL_ES_API);
   383         if (_this->gl_config.major_version) {
   384             context_attrib_list[1] = _this->gl_config.major_version;
   385         }
   386 
   387         egl_context = _this->egl_data->eglCreateContext(_this->egl_data->egl_display,
   388                                           _this->egl_data->egl_config,
   389                                           share_context, context_attrib_list);
   390     }
   391     else {
   392         _this->egl_data->eglBindAPI(EGL_OPENGL_API);
   393         egl_context = _this->egl_data->eglCreateContext(_this->egl_data->egl_display,
   394                                           _this->egl_data->egl_config,
   395                                           share_context, NULL);
   396     }
   397     
   398     if (egl_context == EGL_NO_CONTEXT) {
   399         SDL_SetError("Could not create EGL context");
   400         return NULL;
   401     }
   402     
   403     _this->egl_data->egl_swapinterval = 0;
   404     
   405     if (SDL_EGL_MakeCurrent(_this, egl_surface, egl_context) < 0) {
   406         SDL_EGL_DeleteContext(_this, egl_context);
   407         SDL_SetError("Could not make EGL context current");
   408         return NULL;
   409     }
   410   
   411     return (SDL_GLContext) egl_context;
   412 }
   413 
   414 int
   415 SDL_EGL_MakeCurrent(_THIS, EGLSurface egl_surface, SDL_GLContext context)
   416 {
   417     EGLContext egl_context = (EGLContext) context;
   418 
   419     if (!_this->egl_data) {
   420         return SDL_SetError("OpenGL not initialized");
   421     }
   422     
   423     /* The android emulator crashes badly if you try to eglMakeCurrent 
   424      * with a valid context and invalid surface, so we have to check for both here.
   425      */
   426     if (!egl_context || !egl_surface) {
   427          _this->egl_data->eglMakeCurrent(_this->egl_data->egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
   428     }
   429     else {
   430         if (!_this->egl_data->eglMakeCurrent(_this->egl_data->egl_display,
   431             egl_surface, egl_surface, egl_context)) {
   432             return SDL_SetError("Unable to make EGL context current");
   433         }
   434     }
   435       
   436     return 0;
   437 }
   438 
   439 int
   440 SDL_EGL_SetSwapInterval(_THIS, int interval)
   441 {
   442     EGLBoolean status;
   443     
   444     if (!_this->egl_data) {
   445         return SDL_SetError("EGL not initialized");
   446     }
   447     
   448     status = _this->egl_data->eglSwapInterval(_this->egl_data->egl_display, interval);
   449     if (status == EGL_TRUE) {
   450         _this->egl_data->egl_swapinterval = interval;
   451         return 0;
   452     }
   453     
   454     return SDL_SetError("Unable to set the EGL swap interval");
   455 }
   456 
   457 int
   458 SDL_EGL_GetSwapInterval(_THIS)
   459 {
   460     if (!_this->egl_data) {
   461         return SDL_SetError("EGL not initialized");
   462     }
   463     
   464     return _this->egl_data->egl_swapinterval;
   465 }
   466 
   467 void
   468 SDL_EGL_SwapBuffers(_THIS, EGLSurface egl_surface)
   469 {
   470     _this->egl_data->eglSwapBuffers(_this->egl_data->egl_display, egl_surface);
   471 }
   472 
   473 void
   474 SDL_EGL_DeleteContext(_THIS, SDL_GLContext context)
   475 {
   476     EGLContext egl_context = (EGLContext) context;
   477 
   478     /* Clean up GLES and EGL */
   479     if (!_this->egl_data) {
   480         return;
   481     }
   482     
   483     if (egl_context != NULL && egl_context != EGL_NO_CONTEXT) {
   484         SDL_EGL_MakeCurrent(_this, NULL, NULL);
   485         _this->egl_data->eglDestroyContext(_this->egl_data->egl_display, egl_context);
   486     }
   487         
   488 }
   489 
   490 EGLSurface *
   491 SDL_EGL_CreateSurface(_THIS, NativeWindowType nw) 
   492 {
   493     if (SDL_EGL_ChooseConfig(_this) != 0) {
   494         return EGL_NO_SURFACE;
   495     }
   496     
   497     return _this->egl_data->eglCreateWindowSurface(
   498             _this->egl_data->egl_display,
   499             _this->egl_data->egl_config,
   500             nw, NULL);
   501 }
   502 
   503 void
   504 SDL_EGL_DestroySurface(_THIS, EGLSurface egl_surface) 
   505 {
   506     if (!_this->egl_data) {
   507         return;
   508     }
   509     
   510     if (egl_surface != EGL_NO_SURFACE) {
   511         _this->egl_data->eglDestroySurface(_this->egl_data->egl_display, egl_surface);
   512     }
   513 }
   514 
   515 #endif /* SDL_VIDEO_OPENGL_EGL */
   516 
   517 /* vi: set ts=4 sw=4 expandtab: */
   518