src/video/SDL_egl.c
author Gabriel Jacobo <gabomdq@gmail.com>
Fri, 13 Dec 2013 09:48:12 -0300
changeset 8061 a5f8137ccf01
parent 8045 3c2694255705
child 8062 4fc5f66d63cc
permissions -rw-r--r--
Context sharing for EGL
     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, share_context = EGL_NO_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     if (_this->gl_config.share_with_current_context) {
   355         share_context = (EGLContext)SDL_GL_GetCurrentContext();
   356     }
   357 
   358     egl_context =
   359     _this->egl_data->eglCreateContext(_this->egl_data->egl_display,
   360                                       _this->egl_data->egl_config,
   361                                       share_context, context_attrib_list);
   362     
   363     if (egl_context == EGL_NO_CONTEXT) {
   364         SDL_SetError("Could not create EGL context");
   365         return NULL;
   366     }
   367     
   368     _this->egl_data->egl_swapinterval = 0;
   369     
   370     if (SDL_EGL_MakeCurrent(_this, egl_surface, egl_context) < 0) {
   371         SDL_EGL_DeleteContext(_this, egl_context);
   372         SDL_SetError("Could not make EGL context current");
   373         return NULL;
   374     }
   375   
   376     return (SDL_GLContext) egl_context;
   377 }
   378 
   379 int
   380 SDL_EGL_MakeCurrent(_THIS, EGLSurface egl_surface, SDL_GLContext context)
   381 {
   382     EGLContext egl_context = (EGLContext) context;
   383 
   384     if (!_this->egl_data) {
   385         return SDL_SetError("OpenGL not initialized");
   386     }
   387     
   388     /* The android emulator crashes badly if you try to eglMakeCurrent 
   389      * with a valid context and invalid surface, so we have to check for both here.
   390      */
   391     if (!egl_context || !egl_surface) {
   392          _this->egl_data->eglMakeCurrent(_this->egl_data->egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
   393     }
   394     else {
   395         if (!_this->egl_data->eglMakeCurrent(_this->egl_data->egl_display,
   396             egl_surface, egl_surface, egl_context)) {
   397             return SDL_SetError("Unable to make EGL context current");
   398         }
   399     }
   400       
   401     return 0;
   402 }
   403 
   404 int
   405 SDL_EGL_SetSwapInterval(_THIS, int interval)
   406 {
   407     EGLBoolean status;
   408     
   409     if (!_this->egl_data) {
   410         return SDL_SetError("EGL not initialized");
   411     }
   412     
   413     status = _this->egl_data->eglSwapInterval(_this->egl_data->egl_display, interval);
   414     if (status == EGL_TRUE) {
   415         _this->egl_data->egl_swapinterval = interval;
   416         return 0;
   417     }
   418     
   419     return SDL_SetError("Unable to set the EGL swap interval");
   420 }
   421 
   422 int
   423 SDL_EGL_GetSwapInterval(_THIS)
   424 {
   425     if (!_this->egl_data) {
   426         return SDL_SetError("EGL not initialized");
   427     }
   428     
   429     return _this->egl_data->egl_swapinterval;
   430 }
   431 
   432 void
   433 SDL_EGL_SwapBuffers(_THIS, EGLSurface egl_surface)
   434 {
   435     _this->egl_data->eglSwapBuffers(_this->egl_data->egl_display, egl_surface);
   436 }
   437 
   438 void
   439 SDL_EGL_DeleteContext(_THIS, SDL_GLContext context)
   440 {
   441     EGLContext egl_context = (EGLContext) context;
   442 
   443     /* Clean up GLES and EGL */
   444     if (!_this->egl_data) {
   445         return;
   446     }
   447     
   448     if (egl_context != NULL && egl_context != EGL_NO_CONTEXT) {
   449         SDL_EGL_MakeCurrent(_this, NULL, NULL);
   450         _this->egl_data->eglDestroyContext(_this->egl_data->egl_display, egl_context);
   451     }
   452         
   453 }
   454 
   455 EGLSurface *
   456 SDL_EGL_CreateSurface(_THIS, NativeWindowType nw) 
   457 {
   458     if (SDL_EGL_ChooseConfig(_this) != 0) {
   459         return EGL_NO_SURFACE;
   460     }
   461     
   462     return _this->egl_data->eglCreateWindowSurface(
   463             _this->egl_data->egl_display,
   464             _this->egl_data->egl_config,
   465             nw, NULL);
   466 }
   467 
   468 void
   469 SDL_EGL_DestroySurface(_THIS, EGLSurface egl_surface) 
   470 {
   471     if (!_this->egl_data) {
   472         return;
   473     }
   474     
   475     if (egl_surface != EGL_NO_SURFACE) {
   476         _this->egl_data->eglDestroySurface(_this->egl_data->egl_display, egl_surface);
   477     }
   478 }
   479 
   480 #endif /* SDL_VIDEO_OPENGL_EGL */
   481 
   482 /* vi: set ts=4 sw=4 expandtab: */
   483