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