src/video/SDL_egl.c
author Sam Lantinga <slouken@libsdl.org>
Fri, 11 Aug 2017 10:21:19 -0700
changeset 11219 f94279990934
parent 11192 3c8377f1a323
child 11346 4d2bf1b340cd
permissions -rw-r--r--
Fixed bug 3702 - Clear error messages of SDL_LoadObject for optional libraries

Simon Hug

Some code in SDL loads libraries with SDL_LoadObject to get more information or use newer APIs. SDL_LoadObject may fail, set an error message and SDL will continue with some fallback code. Since SDL will overwrite the error or exit the function with a return value that indicates success, the error form SDL_LoadObject for the optional stuff might as well be cleared right away.
     1 /*
     2  *  Simple DirectMedia Layer
     3  *  Copyright (C) 1997-2017 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 #if SDL_VIDEO_DRIVER_ANDROID
    29 #include <android/native_window.h>
    30 #endif
    31 
    32 #include "SDL_sysvideo.h"
    33 #include "SDL_log.h"
    34 #include "SDL_egl_c.h"
    35 #include "SDL_loadso.h"
    36 #include "SDL_hints.h"
    37 
    38 #ifdef EGL_KHR_create_context
    39 /* EGL_OPENGL_ES3_BIT_KHR was added in version 13 of the extension. */
    40 #ifndef EGL_OPENGL_ES3_BIT_KHR
    41 #define EGL_OPENGL_ES3_BIT_KHR 0x00000040
    42 #endif
    43 #endif /* EGL_KHR_create_context */
    44 
    45 #if SDL_VIDEO_DRIVER_RPI
    46 /* Raspbian places the OpenGL ES/EGL binaries in a non standard path */
    47 #define DEFAULT_EGL "/opt/vc/lib/libEGL.so"
    48 #define DEFAULT_OGL_ES2 "/opt/vc/lib/libGLESv2.so"
    49 #define DEFAULT_OGL_ES_PVR "/opt/vc/lib/libGLES_CM.so"
    50 #define DEFAULT_OGL_ES "/opt/vc/lib/libGLESv1_CM.so"
    51 
    52 #elif SDL_VIDEO_DRIVER_ANDROID || SDL_VIDEO_DRIVER_VIVANTE
    53 /* Android */
    54 #define DEFAULT_EGL "libEGL.so"
    55 #define DEFAULT_OGL_ES2 "libGLESv2.so"
    56 #define DEFAULT_OGL_ES_PVR "libGLES_CM.so"
    57 #define DEFAULT_OGL_ES "libGLESv1_CM.so"
    58 
    59 #elif SDL_VIDEO_DRIVER_WINDOWS || SDL_VIDEO_DRIVER_WINRT
    60 /* EGL AND OpenGL ES support via ANGLE */
    61 #define DEFAULT_EGL "libEGL.dll"
    62 #define DEFAULT_OGL_ES2 "libGLESv2.dll"
    63 #define DEFAULT_OGL_ES_PVR "libGLES_CM.dll"
    64 #define DEFAULT_OGL_ES "libGLESv1_CM.dll"
    65 
    66 #else
    67 /* Desktop Linux */
    68 #define DEFAULT_OGL "libGL.so.1"
    69 #define DEFAULT_EGL "libEGL.so.1"
    70 #define DEFAULT_OGL_ES2 "libGLESv2.so.2"
    71 #define DEFAULT_OGL_ES_PVR "libGLES_CM.so.1"
    72 #define DEFAULT_OGL_ES "libGLESv1_CM.so.1"
    73 #endif /* SDL_VIDEO_DRIVER_RPI */
    74 
    75 #define LOAD_FUNC(NAME) \
    76 _this->egl_data->NAME = SDL_LoadFunction(_this->egl_data->dll_handle, #NAME); \
    77 if (!_this->egl_data->NAME) \
    78 { \
    79     return SDL_SetError("Could not retrieve EGL function " #NAME); \
    80 }
    81 
    82 static const char * SDL_EGL_GetErrorName(EGLint eglErrorCode)
    83 {
    84 #define SDL_EGL_ERROR_TRANSLATE(e) case e: return #e;
    85     switch (eglErrorCode) {
    86         SDL_EGL_ERROR_TRANSLATE(EGL_SUCCESS);
    87         SDL_EGL_ERROR_TRANSLATE(EGL_NOT_INITIALIZED);
    88         SDL_EGL_ERROR_TRANSLATE(EGL_BAD_ACCESS);
    89         SDL_EGL_ERROR_TRANSLATE(EGL_BAD_ALLOC);
    90         SDL_EGL_ERROR_TRANSLATE(EGL_BAD_ATTRIBUTE);
    91         SDL_EGL_ERROR_TRANSLATE(EGL_BAD_CONTEXT);
    92         SDL_EGL_ERROR_TRANSLATE(EGL_BAD_CONFIG);
    93         SDL_EGL_ERROR_TRANSLATE(EGL_BAD_CURRENT_SURFACE);
    94         SDL_EGL_ERROR_TRANSLATE(EGL_BAD_DISPLAY);
    95         SDL_EGL_ERROR_TRANSLATE(EGL_BAD_SURFACE);
    96         SDL_EGL_ERROR_TRANSLATE(EGL_BAD_MATCH);
    97         SDL_EGL_ERROR_TRANSLATE(EGL_BAD_PARAMETER);
    98         SDL_EGL_ERROR_TRANSLATE(EGL_BAD_NATIVE_PIXMAP);
    99         SDL_EGL_ERROR_TRANSLATE(EGL_BAD_NATIVE_WINDOW);
   100         SDL_EGL_ERROR_TRANSLATE(EGL_CONTEXT_LOST);
   101     }
   102     return "";
   103 }
   104 
   105 int SDL_EGL_SetErrorEx(const char * message, const char * eglFunctionName, EGLint eglErrorCode)
   106 {
   107     const char * errorText = SDL_EGL_GetErrorName(eglErrorCode);
   108     char altErrorText[32];
   109     if (errorText[0] == '\0') {
   110         /* An unknown-to-SDL error code was reported.  Report its hexadecimal value, instead of its name. */
   111         SDL_snprintf(altErrorText, SDL_arraysize(altErrorText), "0x%x", (unsigned int)eglErrorCode);
   112         errorText = altErrorText;
   113     }
   114     return SDL_SetError("%s (call to %s failed, reporting an error of %s)", message, eglFunctionName, errorText);
   115 }
   116 
   117 /* EGL implementation of SDL OpenGL ES support */
   118 typedef enum {
   119     SDL_EGL_DISPLAY_EXTENSION,
   120     SDL_EGL_CLIENT_EXTENSION
   121 } SDL_EGL_ExtensionType;
   122 
   123 static SDL_bool SDL_EGL_HasExtension(_THIS, SDL_EGL_ExtensionType type, const char *ext)
   124 {
   125     size_t ext_len;
   126     const char *ext_override;
   127     const char *egl_extstr;
   128     const char *ext_start;
   129 
   130     /* Invalid extensions can be rejected early */
   131     if (ext == NULL || *ext == 0 || SDL_strchr(ext, ' ') != NULL) {
   132         /* SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "SDL_EGL_HasExtension: Invalid EGL extension"); */
   133         return SDL_FALSE;
   134     }
   135 
   136     /* Extensions can be masked with an environment variable.
   137      * Unlike the OpenGL override, this will use the set bits of an integer
   138      * to disable the extension.
   139      *  Bit   Action
   140      *  0     If set, the display extension is masked and not present to SDL.
   141      *  1     If set, the client extension is masked and not present to SDL.
   142      */
   143     ext_override = SDL_getenv(ext);
   144     if (ext_override != NULL) {
   145         int disable_ext = SDL_atoi(ext_override);
   146         if (disable_ext & 0x01 && type == SDL_EGL_DISPLAY_EXTENSION) {
   147             return SDL_FALSE;
   148         } else if (disable_ext & 0x02 && type == SDL_EGL_CLIENT_EXTENSION) {
   149             return SDL_FALSE;
   150         }
   151     }
   152 
   153     ext_len = SDL_strlen(ext);
   154     switch (type) {
   155     case SDL_EGL_DISPLAY_EXTENSION:
   156         egl_extstr = _this->egl_data->eglQueryString(_this->egl_data->egl_display, EGL_EXTENSIONS);
   157         break;
   158     case SDL_EGL_CLIENT_EXTENSION:
   159         /* EGL_EXT_client_extensions modifies eglQueryString to return client extensions
   160          * if EGL_NO_DISPLAY is passed. Implementations without it are required to return NULL.
   161          * This behavior is included in EGL 1.5.
   162          */
   163         egl_extstr = _this->egl_data->eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);
   164         break;
   165     default:
   166         /* SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "SDL_EGL_HasExtension: Invalid extension type"); */
   167         return SDL_FALSE;
   168     }
   169 
   170     if (egl_extstr != NULL) {
   171         ext_start = egl_extstr;
   172 
   173         while (*ext_start) {
   174             ext_start = SDL_strstr(ext_start, ext);
   175             if (ext_start == NULL) {
   176                 return SDL_FALSE;
   177             }
   178             /* Check if the match is not just a substring of one of the extensions */
   179             if (ext_start == egl_extstr || *(ext_start - 1) == ' ') {
   180                 if (ext_start[ext_len] == ' ' || ext_start[ext_len] == 0) {
   181                     return SDL_TRUE;
   182                 }
   183             }
   184             /* If the search stopped in the middle of an extension, skip to the end of it */
   185             ext_start += ext_len;
   186             while (*ext_start != ' ' && *ext_start != 0) {
   187                 ext_start++;
   188             }
   189         }
   190     }
   191 
   192     return SDL_FALSE;
   193 }
   194 
   195 void *
   196 SDL_EGL_GetProcAddress(_THIS, const char *proc)
   197 {
   198     static char procname[1024];
   199     void *retval;
   200     
   201     /* eglGetProcAddress is busted on Android http://code.google.com/p/android/issues/detail?id=7681 */
   202 #if !defined(SDL_VIDEO_DRIVER_ANDROID) && !defined(SDL_VIDEO_DRIVER_MIR) 
   203     if (_this->egl_data->eglGetProcAddress) {
   204         retval = _this->egl_data->eglGetProcAddress(proc);
   205         if (retval) {
   206             return retval;
   207         }
   208     }
   209 #endif
   210     
   211     retval = SDL_LoadFunction(_this->egl_data->egl_dll_handle, proc);
   212     if (!retval && SDL_strlen(proc) <= 1022) {
   213         procname[0] = '_';
   214         SDL_strlcpy(procname + 1, proc, 1022);
   215         retval = SDL_LoadFunction(_this->egl_data->egl_dll_handle, procname);
   216     }
   217     return retval;
   218 }
   219 
   220 void
   221 SDL_EGL_UnloadLibrary(_THIS)
   222 {
   223     if (_this->egl_data) {
   224         if (_this->egl_data->egl_display) {
   225             _this->egl_data->eglTerminate(_this->egl_data->egl_display);
   226             _this->egl_data->egl_display = NULL;
   227         }
   228 
   229         if (_this->egl_data->dll_handle) {
   230             SDL_UnloadObject(_this->egl_data->dll_handle);
   231             _this->egl_data->dll_handle = NULL;
   232         }
   233         if (_this->egl_data->egl_dll_handle) {
   234             SDL_UnloadObject(_this->egl_data->egl_dll_handle);
   235             _this->egl_data->egl_dll_handle = NULL;
   236         }
   237         
   238         SDL_free(_this->egl_data);
   239         _this->egl_data = NULL;
   240     }
   241 }
   242 
   243 int
   244 SDL_EGL_LoadLibrary(_THIS, const char *egl_path, NativeDisplayType native_display, EGLenum platform)
   245 {
   246     void *dll_handle = NULL, *egl_dll_handle = NULL; /* The naming is counter intuitive, but hey, I just work here -- Gabriel */
   247     const char *path = NULL;
   248     int egl_version_major = 0, egl_version_minor = 0;
   249 #if SDL_VIDEO_DRIVER_WINDOWS || SDL_VIDEO_DRIVER_WINRT
   250     const char *d3dcompiler;
   251 #endif
   252 
   253     if (_this->egl_data) {
   254         return SDL_SetError("OpenGL ES context already created");
   255     }
   256 
   257     _this->egl_data = (struct SDL_EGL_VideoData *) SDL_calloc(1, sizeof(SDL_EGL_VideoData));
   258     if (!_this->egl_data) {
   259         return SDL_OutOfMemory();
   260     }
   261 
   262 #if SDL_VIDEO_DRIVER_WINDOWS || SDL_VIDEO_DRIVER_WINRT
   263     d3dcompiler = SDL_GetHint(SDL_HINT_VIDEO_WIN_D3DCOMPILER);
   264     if (!d3dcompiler) {
   265         if (WIN_IsWindowsVistaOrGreater()) {
   266             d3dcompiler = "d3dcompiler_46.dll";
   267         } else {
   268             d3dcompiler = "d3dcompiler_43.dll";
   269         }
   270     }
   271     if (SDL_strcasecmp(d3dcompiler, "none") != 0) {
   272         if (SDL_LoadObject(d3dcompiler) == NULL) {
   273             SDL_ClearError();
   274         }
   275     }
   276 #endif
   277 
   278     /* A funny thing, loading EGL.so first does not work on the Raspberry, so we load libGL* first */
   279     path = SDL_getenv("SDL_VIDEO_GL_DRIVER");
   280     if (path != NULL) {
   281         egl_dll_handle = SDL_LoadObject(path);
   282     }
   283 
   284     if (egl_dll_handle == NULL) {
   285         if (_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_ES) {
   286             if (_this->gl_config.major_version > 1) {
   287                 path = DEFAULT_OGL_ES2;
   288                 egl_dll_handle = SDL_LoadObject(path);
   289             } else {
   290                 path = DEFAULT_OGL_ES;
   291                 egl_dll_handle = SDL_LoadObject(path);
   292                 if (egl_dll_handle == NULL) {
   293                     path = DEFAULT_OGL_ES_PVR;
   294                     egl_dll_handle = SDL_LoadObject(path);
   295                 }
   296             }
   297         }
   298 #ifdef DEFAULT_OGL         
   299         else {
   300             path = DEFAULT_OGL;
   301             egl_dll_handle = SDL_LoadObject(path);
   302         }
   303 #endif        
   304     }
   305     _this->egl_data->egl_dll_handle = egl_dll_handle;
   306 
   307     if (egl_dll_handle == NULL) {
   308         return SDL_SetError("Could not initialize OpenGL / GLES library");
   309     }
   310 
   311     /* Loading libGL* in the previous step took care of loading libEGL.so, but we future proof by double checking */
   312     if (egl_path != NULL) {
   313         dll_handle = SDL_LoadObject(egl_path);
   314     }   
   315     /* Try loading a EGL symbol, if it does not work try the default library paths */
   316     if (dll_handle == NULL || SDL_LoadFunction(dll_handle, "eglChooseConfig") == NULL) {
   317         if (dll_handle != NULL) {
   318             SDL_UnloadObject(dll_handle);
   319         }
   320         path = SDL_getenv("SDL_VIDEO_EGL_DRIVER");
   321         if (path == NULL) {
   322             path = DEFAULT_EGL;
   323         }
   324         dll_handle = SDL_LoadObject(path);
   325         if (dll_handle == NULL || SDL_LoadFunction(dll_handle, "eglChooseConfig") == NULL) {
   326             if (dll_handle != NULL) {
   327                 SDL_UnloadObject(dll_handle);
   328             }
   329             return SDL_SetError("Could not load EGL library");
   330         }
   331         SDL_ClearError();
   332     }
   333 
   334     _this->egl_data->dll_handle = dll_handle;
   335 
   336     /* Load new function pointers */
   337     LOAD_FUNC(eglGetDisplay);
   338     LOAD_FUNC(eglInitialize);
   339     LOAD_FUNC(eglTerminate);
   340     LOAD_FUNC(eglGetProcAddress);
   341     LOAD_FUNC(eglChooseConfig);
   342     LOAD_FUNC(eglGetConfigAttrib);
   343     LOAD_FUNC(eglCreateContext);
   344     LOAD_FUNC(eglDestroyContext);
   345     LOAD_FUNC(eglCreateWindowSurface);
   346     LOAD_FUNC(eglDestroySurface);
   347     LOAD_FUNC(eglMakeCurrent);
   348     LOAD_FUNC(eglSwapBuffers);
   349     LOAD_FUNC(eglSwapInterval);
   350     LOAD_FUNC(eglWaitNative);
   351     LOAD_FUNC(eglWaitGL);
   352     LOAD_FUNC(eglBindAPI);
   353     LOAD_FUNC(eglQueryString);
   354     LOAD_FUNC(eglGetError);
   355 
   356     if (_this->egl_data->eglQueryString) {
   357         /* EGL 1.5 allows querying for client version */
   358         const char *egl_version = _this->egl_data->eglQueryString(EGL_NO_DISPLAY, EGL_VERSION);
   359         if (egl_version != NULL) {
   360             if (SDL_sscanf(egl_version, "%d.%d", &egl_version_major, &egl_version_minor) != 2) {
   361                 egl_version_major = 0;
   362                 egl_version_minor = 0;
   363                 SDL_LogWarn(SDL_LOG_CATEGORY_VIDEO, "Could not parse EGL version string: %s", egl_version);
   364             }
   365         }
   366     }
   367 
   368     if (egl_version_major == 1 && egl_version_minor == 5) {
   369         LOAD_FUNC(eglGetPlatformDisplay);
   370     }
   371 
   372     _this->egl_data->egl_display = EGL_NO_DISPLAY;
   373 #if !defined(__WINRT__)
   374     if (platform) {
   375         if (egl_version_major == 1 && egl_version_minor == 5) {
   376             _this->egl_data->egl_display = _this->egl_data->eglGetPlatformDisplay(platform, (void *)native_display, NULL);
   377         } else {
   378             if (SDL_EGL_HasExtension(_this, SDL_EGL_CLIENT_EXTENSION, "EGL_EXT_platform_base")) {
   379                 _this->egl_data->eglGetPlatformDisplayEXT = SDL_EGL_GetProcAddress(_this, "eglGetPlatformDisplayEXT");
   380                 if (_this->egl_data->eglGetPlatformDisplayEXT) {
   381                     _this->egl_data->egl_display = _this->egl_data->eglGetPlatformDisplayEXT(platform, (void *)native_display, NULL);
   382                 }
   383             }
   384         }
   385     }
   386     /* Try the implementation-specific eglGetDisplay even if eglGetPlatformDisplay fails */
   387     if (_this->egl_data->egl_display == EGL_NO_DISPLAY) {
   388         _this->egl_data->egl_display = _this->egl_data->eglGetDisplay(native_display);
   389     }
   390     if (_this->egl_data->egl_display == EGL_NO_DISPLAY) {
   391         return SDL_SetError("Could not get EGL display");
   392     }
   393     
   394     if (_this->egl_data->eglInitialize(_this->egl_data->egl_display, NULL, NULL) != EGL_TRUE) {
   395         return SDL_SetError("Could not initialize EGL");
   396     }
   397 #endif
   398 
   399     if (path) {
   400         SDL_strlcpy(_this->gl_config.driver_path, path, sizeof(_this->gl_config.driver_path) - 1);
   401     } else {
   402         *_this->gl_config.driver_path = '\0';
   403     }
   404     
   405     return 0;
   406 }
   407 
   408 int
   409 SDL_EGL_ChooseConfig(_THIS) 
   410 {
   411 /* 64 seems nice. */
   412     EGLint attribs[64];
   413     EGLint found_configs = 0, value;
   414 #ifdef SDL_VIDEO_DRIVER_KMSDRM
   415     /* Intel EGL on KMS/DRM (al least) returns invalid configs that confuse the bitdiff search used */
   416     /* later in this function, so we simply use the first one when using the KMSDRM driver for now. */
   417     EGLConfig configs[1];
   418 #else
   419     /* 128 seems even nicer here */
   420     EGLConfig configs[128];
   421 #endif
   422     int i, j, best_bitdiff = -1, bitdiff;
   423    
   424     if (!_this->egl_data) {
   425         /* The EGL library wasn't loaded, SDL_GetError() should have info */
   426         return -1;
   427     }
   428   
   429     /* Get a valid EGL configuration */
   430     i = 0;
   431     attribs[i++] = EGL_RED_SIZE;
   432     attribs[i++] = _this->gl_config.red_size;
   433     attribs[i++] = EGL_GREEN_SIZE;
   434     attribs[i++] = _this->gl_config.green_size;
   435     attribs[i++] = EGL_BLUE_SIZE;
   436     attribs[i++] = _this->gl_config.blue_size;
   437     
   438     if (_this->gl_config.alpha_size) {
   439         attribs[i++] = EGL_ALPHA_SIZE;
   440         attribs[i++] = _this->gl_config.alpha_size;
   441     }
   442     
   443     if (_this->gl_config.buffer_size) {
   444         attribs[i++] = EGL_BUFFER_SIZE;
   445         attribs[i++] = _this->gl_config.buffer_size;
   446     }
   447     
   448     attribs[i++] = EGL_DEPTH_SIZE;
   449     attribs[i++] = _this->gl_config.depth_size;
   450     
   451     if (_this->gl_config.stencil_size) {
   452         attribs[i++] = EGL_STENCIL_SIZE;
   453         attribs[i++] = _this->gl_config.stencil_size;
   454     }
   455     
   456     if (_this->gl_config.multisamplebuffers) {
   457         attribs[i++] = EGL_SAMPLE_BUFFERS;
   458         attribs[i++] = _this->gl_config.multisamplebuffers;
   459     }
   460     
   461     if (_this->gl_config.multisamplesamples) {
   462         attribs[i++] = EGL_SAMPLES;
   463         attribs[i++] = _this->gl_config.multisamplesamples;
   464     }
   465 
   466     if (_this->gl_config.framebuffer_srgb_capable) {
   467 #ifdef EGL_KHR_gl_colorspace
   468         if (SDL_EGL_HasExtension(_this, SDL_EGL_DISPLAY_EXTENSION, "EGL_KHR_gl_colorspace")) {
   469             attribs[i++] = EGL_GL_COLORSPACE_KHR;
   470             attribs[i++] = EGL_GL_COLORSPACE_SRGB_KHR;
   471         } else
   472 #endif
   473         {
   474             return SDL_SetError("EGL implementation does not support sRGB system framebuffers");
   475         }
   476     }
   477 
   478     attribs[i++] = EGL_RENDERABLE_TYPE;
   479     if (_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_ES) {
   480 #ifdef EGL_KHR_create_context
   481         if (_this->gl_config.major_version >= 3 &&
   482             SDL_EGL_HasExtension(_this, SDL_EGL_DISPLAY_EXTENSION, "EGL_KHR_create_context")) {
   483             attribs[i++] = EGL_OPENGL_ES3_BIT_KHR;
   484         } else
   485 #endif
   486         if (_this->gl_config.major_version >= 2) {
   487             attribs[i++] = EGL_OPENGL_ES2_BIT;
   488         } else {
   489             attribs[i++] = EGL_OPENGL_ES_BIT;
   490         }
   491         _this->egl_data->eglBindAPI(EGL_OPENGL_ES_API);
   492     } else {
   493         attribs[i++] = EGL_OPENGL_BIT;
   494         _this->egl_data->eglBindAPI(EGL_OPENGL_API);
   495     }
   496 
   497     attribs[i++] = EGL_NONE;
   498 
   499     if (_this->egl_data->eglChooseConfig(_this->egl_data->egl_display,
   500         attribs,
   501         configs, SDL_arraysize(configs),
   502         &found_configs) == EGL_FALSE ||
   503         found_configs == 0) {
   504         return SDL_EGL_SetError("Couldn't find matching EGL config", "eglChooseConfig");
   505     }
   506 
   507     /* eglChooseConfig returns a number of configurations that match or exceed the requested attribs. */
   508     /* From those, we select the one that matches our requirements more closely via a makeshift algorithm */
   509 
   510     for (i = 0; i < found_configs; i++ ) {
   511         bitdiff = 0;
   512         for (j = 0; j < SDL_arraysize(attribs) - 1; j += 2) {
   513             if (attribs[j] == EGL_NONE) {
   514                break;
   515             }
   516             
   517             if ( attribs[j+1] != EGL_DONT_CARE && (
   518                 attribs[j] == EGL_RED_SIZE ||
   519                 attribs[j] == EGL_GREEN_SIZE ||
   520                 attribs[j] == EGL_BLUE_SIZE ||
   521                 attribs[j] == EGL_ALPHA_SIZE ||
   522                 attribs[j] == EGL_DEPTH_SIZE ||
   523                 attribs[j] == EGL_STENCIL_SIZE)) {
   524                 _this->egl_data->eglGetConfigAttrib(_this->egl_data->egl_display, configs[i], attribs[j], &value);
   525                 bitdiff += value - attribs[j + 1]; /* value is always >= attrib */
   526             }
   527         }
   528 
   529         if (bitdiff < best_bitdiff || best_bitdiff == -1) {
   530             _this->egl_data->egl_config = configs[i];
   531             
   532             best_bitdiff = bitdiff;
   533         }
   534 
   535         if (bitdiff == 0) {
   536             break; /* we found an exact match! */
   537         }
   538     }
   539     
   540     return 0;
   541 }
   542 
   543 SDL_GLContext
   544 SDL_EGL_CreateContext(_THIS, EGLSurface egl_surface)
   545 {
   546     /* max 14 values plus terminator. */
   547     EGLint attribs[15];
   548     int attr = 0;
   549 
   550     EGLContext egl_context, share_context = EGL_NO_CONTEXT;
   551     EGLint profile_mask = _this->gl_config.profile_mask;
   552     EGLint major_version = _this->gl_config.major_version;
   553     EGLint minor_version = _this->gl_config.minor_version;
   554     SDL_bool profile_es = (profile_mask == SDL_GL_CONTEXT_PROFILE_ES);
   555 
   556     if (!_this->egl_data) {
   557         /* The EGL library wasn't loaded, SDL_GetError() should have info */
   558         return NULL;
   559     }
   560 
   561     if (_this->gl_config.share_with_current_context) {
   562         share_context = (EGLContext)SDL_GL_GetCurrentContext();
   563     }
   564 
   565     /* Set the context version and other attributes. */
   566     if ((major_version < 3 || (minor_version == 0 && profile_es)) &&
   567         _this->gl_config.flags == 0 &&
   568         (profile_mask == 0 || profile_es)) {
   569         /* Create a context without using EGL_KHR_create_context attribs.
   570          * When creating a GLES context without EGL_KHR_create_context we can
   571          * only specify the major version. When creating a desktop GL context
   572          * we can't specify any version, so we only try in that case when the
   573          * version is less than 3.0 (matches SDL's GLX/WGL behavior.)
   574          */
   575         if (profile_es) {
   576             attribs[attr++] = EGL_CONTEXT_CLIENT_VERSION;
   577             attribs[attr++] = SDL_max(major_version, 1);
   578         }
   579     } else {
   580 #ifdef EGL_KHR_create_context
   581         /* The Major/minor version, context profiles, and context flags can
   582          * only be specified when this extension is available.
   583          */
   584         if (SDL_EGL_HasExtension(_this, SDL_EGL_DISPLAY_EXTENSION, "EGL_KHR_create_context")) {
   585             attribs[attr++] = EGL_CONTEXT_MAJOR_VERSION_KHR;
   586             attribs[attr++] = major_version;
   587             attribs[attr++] = EGL_CONTEXT_MINOR_VERSION_KHR;
   588             attribs[attr++] = minor_version;
   589 
   590             /* SDL profile bits match EGL profile bits. */
   591             if (profile_mask != 0 && profile_mask != SDL_GL_CONTEXT_PROFILE_ES) {
   592                 attribs[attr++] = EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR;
   593                 attribs[attr++] = profile_mask;
   594             }
   595 
   596             /* SDL flags match EGL flags. */
   597             if (_this->gl_config.flags != 0) {
   598                 attribs[attr++] = EGL_CONTEXT_FLAGS_KHR;
   599                 attribs[attr++] = _this->gl_config.flags;
   600             }
   601         } else
   602 #endif /* EGL_KHR_create_context */
   603         {
   604             SDL_SetError("Could not create EGL context (context attributes are not supported)");
   605             return NULL;
   606         }
   607     }
   608 
   609     attribs[attr++] = EGL_NONE;
   610 
   611     /* Bind the API */
   612     if (profile_es) {
   613         _this->egl_data->eglBindAPI(EGL_OPENGL_ES_API);
   614     } else {
   615         _this->egl_data->eglBindAPI(EGL_OPENGL_API);
   616     }
   617 
   618     egl_context = _this->egl_data->eglCreateContext(_this->egl_data->egl_display,
   619                                       _this->egl_data->egl_config,
   620                                       share_context, attribs);
   621 
   622     if (egl_context == EGL_NO_CONTEXT) {
   623         SDL_EGL_SetError("Could not create EGL context", "eglCreateContext");
   624         return NULL;
   625     }
   626 
   627     _this->egl_data->egl_swapinterval = 0;
   628 
   629     if (SDL_EGL_MakeCurrent(_this, egl_surface, egl_context) < 0) {
   630         /* Save the SDL error set by SDL_EGL_MakeCurrent */
   631         char errorText[1024];
   632         SDL_strlcpy(errorText, SDL_GetError(), SDL_arraysize(errorText));
   633 
   634         /* Delete the context, which may alter the value returned by SDL_GetError() */
   635         SDL_EGL_DeleteContext(_this, egl_context);
   636 
   637         /* Restore the SDL error */
   638         SDL_SetError("%s", errorText);
   639 
   640         return NULL;
   641     }
   642 
   643     return (SDL_GLContext) egl_context;
   644 }
   645 
   646 int
   647 SDL_EGL_MakeCurrent(_THIS, EGLSurface egl_surface, SDL_GLContext context)
   648 {
   649     EGLContext egl_context = (EGLContext) context;
   650 
   651     if (!_this->egl_data) {
   652         return SDL_SetError("OpenGL not initialized");
   653     }
   654     
   655     /* The android emulator crashes badly if you try to eglMakeCurrent 
   656      * with a valid context and invalid surface, so we have to check for both here.
   657      */
   658     if (!egl_context || !egl_surface) {
   659          _this->egl_data->eglMakeCurrent(_this->egl_data->egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
   660     } else {
   661         if (!_this->egl_data->eglMakeCurrent(_this->egl_data->egl_display,
   662             egl_surface, egl_surface, egl_context)) {
   663             return SDL_EGL_SetError("Unable to make EGL context current", "eglMakeCurrent");
   664         }
   665     }
   666       
   667     return 0;
   668 }
   669 
   670 int
   671 SDL_EGL_SetSwapInterval(_THIS, int interval)
   672 {
   673     EGLBoolean status;
   674     
   675     if (!_this->egl_data) {
   676         return SDL_SetError("EGL not initialized");
   677     }
   678     
   679     status = _this->egl_data->eglSwapInterval(_this->egl_data->egl_display, interval);
   680     if (status == EGL_TRUE) {
   681         _this->egl_data->egl_swapinterval = interval;
   682         return 0;
   683     }
   684     
   685     return SDL_EGL_SetError("Unable to set the EGL swap interval", "eglSwapInterval");
   686 }
   687 
   688 int
   689 SDL_EGL_GetSwapInterval(_THIS)
   690 {
   691     if (!_this->egl_data) {
   692         SDL_SetError("EGL not initialized");
   693         return 0;
   694     }
   695     
   696     return _this->egl_data->egl_swapinterval;
   697 }
   698 
   699 int
   700 SDL_EGL_SwapBuffers(_THIS, EGLSurface egl_surface)
   701 {
   702     if (!_this->egl_data->eglSwapBuffers(_this->egl_data->egl_display, egl_surface)) {
   703         return SDL_EGL_SetError("unable to show color buffer in an OS-native window", "eglSwapBuffers");
   704     }
   705     return 0;
   706 }
   707 
   708 void
   709 SDL_EGL_DeleteContext(_THIS, SDL_GLContext context)
   710 {
   711     EGLContext egl_context = (EGLContext) context;
   712 
   713     /* Clean up GLES and EGL */
   714     if (!_this->egl_data) {
   715         return;
   716     }
   717     
   718     if (egl_context != NULL && egl_context != EGL_NO_CONTEXT) {
   719         SDL_EGL_MakeCurrent(_this, NULL, NULL);
   720         _this->egl_data->eglDestroyContext(_this->egl_data->egl_display, egl_context);
   721     }
   722         
   723 }
   724 
   725 EGLSurface *
   726 SDL_EGL_CreateSurface(_THIS, NativeWindowType nw) 
   727 {
   728     EGLSurface * surface;
   729 
   730     if (SDL_EGL_ChooseConfig(_this) != 0) {
   731         return EGL_NO_SURFACE;
   732     }
   733     
   734 #if SDL_VIDEO_DRIVER_ANDROID
   735     {
   736         /* Android docs recommend doing this!
   737          * Ref: http://developer.android.com/reference/android/app/NativeActivity.html 
   738          */
   739         EGLint format;
   740         _this->egl_data->eglGetConfigAttrib(_this->egl_data->egl_display,
   741                                             _this->egl_data->egl_config, 
   742                                             EGL_NATIVE_VISUAL_ID, &format);
   743 
   744         ANativeWindow_setBuffersGeometry(nw, 0, 0, format);
   745     }
   746 #endif    
   747     
   748     surface = _this->egl_data->eglCreateWindowSurface(
   749             _this->egl_data->egl_display,
   750             _this->egl_data->egl_config,
   751             nw, NULL);
   752     if (surface == EGL_NO_SURFACE) {
   753         SDL_EGL_SetError("unable to create an EGL window surface", "eglCreateWindowSurface");
   754     }
   755     return surface;
   756 }
   757 
   758 void
   759 SDL_EGL_DestroySurface(_THIS, EGLSurface egl_surface) 
   760 {
   761     if (!_this->egl_data) {
   762         return;
   763     }
   764     
   765     if (egl_surface != EGL_NO_SURFACE) {
   766         _this->egl_data->eglDestroySurface(_this->egl_data->egl_display, egl_surface);
   767     }
   768 }
   769 
   770 #endif /* SDL_VIDEO_OPENGL_EGL */
   771 
   772 /* vi: set ts=4 sw=4 expandtab: */
   773