src/video/SDL_egl.c
author David Ludwig <dludwig@pobox.com>
Thu, 28 Nov 2013 22:59:21 -0500
changeset 8545 7cf4f8916997
parent 8543 b9dd3cf38585
child 8583 fb2933ca805f
permissions -rw-r--r--
WinRT: got the SDL-official OpenGL ES 2 changes working, in an experimental state
     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 || SDL_VIDEO_DRIVER_WINRT
    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 || SDL_VIDEO_DRIVER_WINRT
   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 || SDL_VIDEO_DRIVER_WINRT
   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     /* We need to select a config here to satisfy some video backends such as X11 */
   229     return SDL_EGL_ChooseConfig(_this);
   230 }
   231 
   232 int
   233 SDL_EGL_ChooseConfig(_THIS) 
   234 {
   235     /* 64 seems nice. */
   236     EGLint attribs[64];
   237     EGLint found_configs = 0, value;
   238     /* 128 seems even nicer here */
   239     EGLConfig configs[128];
   240     int i, j, best_bitdiff = -1, bitdiff;
   241     
   242     if (!_this->egl_data) {
   243         /* The EGL library wasn't loaded, SDL_GetError() should have info */
   244         return -1;
   245     }
   246   
   247     /* Get a valid EGL configuration */
   248     i = 0;
   249     attribs[i++] = EGL_RED_SIZE;
   250     attribs[i++] = _this->gl_config.red_size;
   251     attribs[i++] = EGL_GREEN_SIZE;
   252     attribs[i++] = _this->gl_config.green_size;
   253     attribs[i++] = EGL_BLUE_SIZE;
   254     attribs[i++] = _this->gl_config.blue_size;
   255     
   256     if (_this->gl_config.alpha_size) {
   257         attribs[i++] = EGL_ALPHA_SIZE;
   258         attribs[i++] = _this->gl_config.alpha_size;
   259     }
   260     
   261     if (_this->gl_config.buffer_size) {
   262         attribs[i++] = EGL_BUFFER_SIZE;
   263         attribs[i++] = _this->gl_config.buffer_size;
   264     }
   265     
   266     attribs[i++] = EGL_DEPTH_SIZE;
   267     attribs[i++] = _this->gl_config.depth_size;
   268     
   269     if (_this->gl_config.stencil_size) {
   270         attribs[i++] = EGL_STENCIL_SIZE;
   271         attribs[i++] = _this->gl_config.stencil_size;
   272     }
   273     
   274     if (_this->gl_config.multisamplebuffers) {
   275         attribs[i++] = EGL_SAMPLE_BUFFERS;
   276         attribs[i++] = _this->gl_config.multisamplebuffers;
   277     }
   278     
   279     if (_this->gl_config.multisamplesamples) {
   280         attribs[i++] = EGL_SAMPLES;
   281         attribs[i++] = _this->gl_config.multisamplesamples;
   282     }
   283     
   284     attribs[i++] = EGL_RENDERABLE_TYPE;
   285     if (_this->gl_config.major_version == 2) {
   286         attribs[i++] = EGL_OPENGL_ES2_BIT;
   287     } else {
   288         attribs[i++] = EGL_OPENGL_ES_BIT;
   289     }
   290     
   291     attribs[i++] = EGL_NONE;
   292     
   293     if (_this->egl_data->eglChooseConfig(_this->egl_data->egl_display,
   294         attribs,
   295         configs, SDL_arraysize(configs),
   296         &found_configs) == EGL_FALSE ||
   297         found_configs == 0) {
   298         return SDL_SetError("Couldn't find matching EGL config");
   299     }
   300     
   301     /* eglChooseConfig returns a number of configurations that match or exceed the requested attribs. */
   302     /* From those, we select the one that matches our requirements more closely via a makeshift algorithm */
   303 
   304     for ( i=0; i<found_configs; i++ ) {
   305         bitdiff = 0;
   306         for (j = 0; j < SDL_arraysize(attribs) - 1; j += 2) {
   307             if (attribs[j] == EGL_NONE) {
   308                break;
   309             }
   310             
   311             if ( attribs[j+1] != EGL_DONT_CARE && (
   312                 attribs[j] == EGL_RED_SIZE ||
   313                 attribs[j] == EGL_GREEN_SIZE ||
   314                 attribs[j] == EGL_BLUE_SIZE ||
   315                 attribs[j] == EGL_ALPHA_SIZE ||
   316                 attribs[j] == EGL_DEPTH_SIZE ||
   317                 attribs[j] == EGL_STENCIL_SIZE)) {
   318                 _this->egl_data->eglGetConfigAttrib(_this->egl_data->egl_display, configs[i], attribs[j], &value);
   319                 bitdiff += value - attribs[j + 1]; /* value is always >= attrib */
   320             }
   321         }
   322 
   323         if (bitdiff < best_bitdiff || best_bitdiff == -1) {
   324             _this->egl_data->egl_config = configs[i];
   325             
   326             best_bitdiff = bitdiff;
   327         }
   328            
   329         if (bitdiff == 0) break; /* we found an exact match! */
   330     }
   331     
   332     return 0;
   333 }
   334 
   335 SDL_GLContext
   336 SDL_EGL_CreateContext(_THIS, EGLSurface egl_surface)
   337 {
   338     EGLint context_attrib_list[] = {
   339         EGL_CONTEXT_CLIENT_VERSION,
   340         1,
   341         EGL_NONE
   342     };
   343     
   344     EGLContext egl_context;
   345     
   346     if (!_this->egl_data) {
   347         /* The EGL library wasn't loaded, SDL_GetError() should have info */
   348         return NULL;
   349     }
   350     
   351     if (_this->gl_config.major_version) {
   352         context_attrib_list[1] = _this->gl_config.major_version;
   353     }
   354 
   355     egl_context =
   356     _this->egl_data->eglCreateContext(_this->egl_data->egl_display,
   357                                       _this->egl_data->egl_config,
   358                                       EGL_NO_CONTEXT, context_attrib_list);
   359     
   360     if (egl_context == EGL_NO_CONTEXT) {
   361         SDL_SetError("Could not create EGL context");
   362         return NULL;
   363     }
   364     
   365     _this->egl_data->egl_swapinterval = 0;
   366     
   367     if (SDL_EGL_MakeCurrent(_this, egl_surface, egl_context) < 0) {
   368         SDL_EGL_DeleteContext(_this, egl_context);
   369         SDL_SetError("Could not make EGL context current");
   370         return NULL;
   371     }
   372   
   373     return (SDL_GLContext) egl_context;
   374 }
   375 
   376 int
   377 SDL_EGL_MakeCurrent(_THIS, EGLSurface egl_surface, SDL_GLContext context)
   378 {
   379     EGLContext egl_context = (EGLContext) context;
   380 
   381     if (!_this->egl_data) {
   382         return SDL_SetError("OpenGL not initialized");
   383     }
   384     
   385     /* The android emulator crashes badly if you try to eglMakeCurrent 
   386      * with a valid context and invalid surface, so we have to check for both here.
   387      */
   388     if (!egl_context || !egl_surface) {
   389          _this->egl_data->eglMakeCurrent(_this->egl_data->egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
   390     }
   391     else {
   392         if (!_this->egl_data->eglMakeCurrent(_this->egl_data->egl_display,
   393             egl_surface, egl_surface, egl_context)) {
   394             return SDL_SetError("Unable to make EGL context current");
   395         }
   396     }
   397       
   398     return 0;
   399 }
   400 
   401 int
   402 SDL_EGL_SetSwapInterval(_THIS, int interval)
   403 {
   404     EGLBoolean status;
   405     
   406     if (!_this->egl_data) {
   407         return SDL_SetError("EGL not initialized");
   408     }
   409     
   410     status = _this->egl_data->eglSwapInterval(_this->egl_data->egl_display, interval);
   411     if (status == EGL_TRUE) {
   412         _this->egl_data->egl_swapinterval = interval;
   413         return 0;
   414     }
   415     
   416     return SDL_SetError("Unable to set the EGL swap interval");
   417 }
   418 
   419 int
   420 SDL_EGL_GetSwapInterval(_THIS)
   421 {
   422     if (!_this->egl_data) {
   423         return SDL_SetError("EGL not initialized");
   424     }
   425     
   426     return _this->egl_data->egl_swapinterval;
   427 }
   428 
   429 void
   430 SDL_EGL_SwapBuffers(_THIS, EGLSurface egl_surface)
   431 {
   432     _this->egl_data->eglSwapBuffers(_this->egl_data->egl_display, egl_surface);
   433 }
   434 
   435 void
   436 SDL_EGL_DeleteContext(_THIS, SDL_GLContext context)
   437 {
   438     EGLContext egl_context = (EGLContext) context;
   439 
   440     /* Clean up GLES and EGL */
   441     if (!_this->egl_data) {
   442         return;
   443     }
   444     
   445     if (egl_context != NULL && egl_context != EGL_NO_CONTEXT) {
   446         SDL_EGL_MakeCurrent(_this, NULL, NULL);
   447         _this->egl_data->eglDestroyContext(_this->egl_data->egl_display, egl_context);
   448     }
   449         
   450 }
   451 
   452 EGLSurface *
   453 SDL_EGL_CreateSurface(_THIS, NativeWindowType nw) 
   454 {
   455     return _this->egl_data->eglCreateWindowSurface(
   456             _this->egl_data->egl_display,
   457             _this->egl_data->egl_config,
   458             nw, NULL);
   459 }
   460 
   461 void
   462 SDL_EGL_DestroySurface(_THIS, EGLSurface egl_surface) 
   463 {
   464     if (!_this->egl_data) {
   465         return;
   466     }
   467     
   468     if (egl_surface != EGL_NO_SURFACE) {
   469         _this->egl_data->eglDestroySurface(_this->egl_data->egl_display, egl_surface);
   470     }
   471 }
   472 
   473 #endif /* SDL_VIDEO_OPENGL_EGL */
   474 
   475 /* vi: set ts=4 sw=4 expandtab: */
   476