src/video/SDL_egl.c
author Sam Lantinga <slouken@libsdl.org>
Wed, 02 Aug 2017 10:22:48 -0700
changeset 11175 cbc6a8a5b701
parent 10878 7fa08228d9e3
child 11192 3c8377f1a323
permissions -rw-r--r--
Fixed bug 3690 - SDL2 KMS/DRM render context support

Manuel

The attached patch adds support for KMS/DRM context graphics.

It builds with no problem on X86_64 GNU/Linux systems, provided the needed libraries are present, and on ARM GNU/Linux systems that have KMS/DRM support and a GLES2 implementation.
Tested on Raspberry Pi: KMS/DRM is what the Raspberry Pi will use as default in the near future, once the propietary DispmanX API by Broadcom is overtaken by open graphics stack, it's possible to boot current Raspbian system in KMS mode by adding "dtoverlay=vc4-kms-v3d" to config.txt on Raspbian's boot partition.
X86 systems use KMS right away in every current GNU/Linux system.

Simple build instructions:

$./autogen.sh
$./configure --enable-video-kmsdrm
$make
     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         SDL_LoadObject(d3dcompiler);
   273     }
   274 #endif
   275 
   276     /* A funny thing, loading EGL.so first does not work on the Raspberry, so we load libGL* first */
   277     path = SDL_getenv("SDL_VIDEO_GL_DRIVER");
   278     if (path != NULL) {
   279         egl_dll_handle = SDL_LoadObject(path);
   280     }
   281 
   282     if (egl_dll_handle == NULL) {
   283         if (_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_ES) {
   284             if (_this->gl_config.major_version > 1) {
   285                 path = DEFAULT_OGL_ES2;
   286                 egl_dll_handle = SDL_LoadObject(path);
   287             } else {
   288                 path = DEFAULT_OGL_ES;
   289                 egl_dll_handle = SDL_LoadObject(path);
   290                 if (egl_dll_handle == NULL) {
   291                     path = DEFAULT_OGL_ES_PVR;
   292                     egl_dll_handle = SDL_LoadObject(path);
   293                 }
   294             }
   295         }
   296 #ifdef DEFAULT_OGL         
   297         else {
   298             path = DEFAULT_OGL;
   299             egl_dll_handle = SDL_LoadObject(path);
   300         }
   301 #endif        
   302     }
   303     _this->egl_data->egl_dll_handle = egl_dll_handle;
   304 
   305     if (egl_dll_handle == NULL) {
   306         return SDL_SetError("Could not initialize OpenGL / GLES library");
   307     }
   308 
   309     /* Loading libGL* in the previous step took care of loading libEGL.so, but we future proof by double checking */
   310     if (egl_path != NULL) {
   311         dll_handle = SDL_LoadObject(egl_path);
   312     }   
   313     /* Try loading a EGL symbol, if it does not work try the default library paths */
   314     if (dll_handle == NULL || SDL_LoadFunction(dll_handle, "eglChooseConfig") == NULL) {
   315         if (dll_handle != NULL) {
   316             SDL_UnloadObject(dll_handle);
   317         }
   318         path = SDL_getenv("SDL_VIDEO_EGL_DRIVER");
   319         if (path == NULL) {
   320             path = DEFAULT_EGL;
   321         }
   322         dll_handle = SDL_LoadObject(path);
   323         if (dll_handle == NULL || SDL_LoadFunction(dll_handle, "eglChooseConfig") == NULL) {
   324             if (dll_handle != NULL) {
   325                 SDL_UnloadObject(dll_handle);
   326             }
   327             return SDL_SetError("Could not load EGL library");
   328         }
   329         SDL_ClearError();
   330     }
   331 
   332     _this->egl_data->dll_handle = dll_handle;
   333 
   334     /* Load new function pointers */
   335     LOAD_FUNC(eglGetDisplay);
   336     LOAD_FUNC(eglInitialize);
   337     LOAD_FUNC(eglTerminate);
   338     LOAD_FUNC(eglGetProcAddress);
   339     LOAD_FUNC(eglChooseConfig);
   340     LOAD_FUNC(eglGetConfigAttrib);
   341     LOAD_FUNC(eglCreateContext);
   342     LOAD_FUNC(eglDestroyContext);
   343     LOAD_FUNC(eglCreateWindowSurface);
   344     LOAD_FUNC(eglDestroySurface);
   345     LOAD_FUNC(eglMakeCurrent);
   346     LOAD_FUNC(eglSwapBuffers);
   347     LOAD_FUNC(eglSwapInterval);
   348     LOAD_FUNC(eglWaitNative);
   349     LOAD_FUNC(eglWaitGL);
   350     LOAD_FUNC(eglBindAPI);
   351     LOAD_FUNC(eglQueryString);
   352     LOAD_FUNC(eglGetError);
   353 
   354     if (_this->egl_data->eglQueryString) {
   355         /* EGL 1.5 allows querying for client version */
   356         const char *egl_version = _this->egl_data->eglQueryString(EGL_NO_DISPLAY, EGL_VERSION);
   357         if (egl_version != NULL) {
   358             if (SDL_sscanf(egl_version, "%d.%d", &egl_version_major, &egl_version_minor) != 2) {
   359                 egl_version_major = 0;
   360                 egl_version_minor = 0;
   361                 SDL_LogWarn(SDL_LOG_CATEGORY_VIDEO, "Could not parse EGL version string: %s", egl_version);
   362             }
   363         }
   364     }
   365 
   366     if (egl_version_major == 1 && egl_version_minor == 5) {
   367         LOAD_FUNC(eglGetPlatformDisplay);
   368     }
   369 
   370     _this->egl_data->egl_display = EGL_NO_DISPLAY;
   371 #if !defined(__WINRT__)
   372     if (platform) {
   373         if (egl_version_major == 1 && egl_version_minor == 5) {
   374             _this->egl_data->egl_display = _this->egl_data->eglGetPlatformDisplay(platform, native_display, NULL);
   375         } else {
   376             if (SDL_EGL_HasExtension(_this, SDL_EGL_CLIENT_EXTENSION, "EGL_EXT_platform_base")) {
   377                 _this->egl_data->eglGetPlatformDisplayEXT = SDL_EGL_GetProcAddress(_this, "eglGetPlatformDisplayEXT");
   378                 if (_this->egl_data->eglGetPlatformDisplayEXT) {
   379                     _this->egl_data->egl_display = _this->egl_data->eglGetPlatformDisplayEXT(platform, native_display, NULL);
   380                 }
   381             }
   382         }
   383     }
   384     /* Try the implementation-specific eglGetDisplay even if eglGetPlatformDisplay fails */
   385     if (_this->egl_data->egl_display == EGL_NO_DISPLAY) {
   386         _this->egl_data->egl_display = _this->egl_data->eglGetDisplay(native_display);
   387     }
   388     if (_this->egl_data->egl_display == EGL_NO_DISPLAY) {
   389         return SDL_SetError("Could not get EGL display");
   390     }
   391     
   392     if (_this->egl_data->eglInitialize(_this->egl_data->egl_display, NULL, NULL) != EGL_TRUE) {
   393         return SDL_SetError("Could not initialize EGL");
   394     }
   395 #endif
   396 
   397     if (path) {
   398         SDL_strlcpy(_this->gl_config.driver_path, path, sizeof(_this->gl_config.driver_path) - 1);
   399     } else {
   400         *_this->gl_config.driver_path = '\0';
   401     }
   402     
   403     return 0;
   404 }
   405 
   406 int
   407 SDL_EGL_ChooseConfig(_THIS) 
   408 {
   409 /* 64 seems nice. */
   410     EGLint attribs[64];
   411     EGLint found_configs = 0, value;
   412 #ifdef SDL_VIDEO_DRIVER_KMSDRM
   413     /* Intel EGL on KMS/DRM (al least) returns invalid configs that confuse the bitdiff search used */
   414     /* later in this function, so we simply use the first one when using the KMSDRM driver for now. */
   415     EGLConfig configs[1];
   416 #else
   417     /* 128 seems even nicer here */
   418     EGLConfig configs[128];
   419 #endif
   420     int i, j, best_bitdiff = -1, bitdiff;
   421    
   422     if (!_this->egl_data) {
   423         /* The EGL library wasn't loaded, SDL_GetError() should have info */
   424         return -1;
   425     }
   426   
   427     /* Get a valid EGL configuration */
   428     i = 0;
   429     attribs[i++] = EGL_RED_SIZE;
   430     attribs[i++] = _this->gl_config.red_size;
   431     attribs[i++] = EGL_GREEN_SIZE;
   432     attribs[i++] = _this->gl_config.green_size;
   433     attribs[i++] = EGL_BLUE_SIZE;
   434     attribs[i++] = _this->gl_config.blue_size;
   435     
   436     if (_this->gl_config.alpha_size) {
   437         attribs[i++] = EGL_ALPHA_SIZE;
   438         attribs[i++] = _this->gl_config.alpha_size;
   439     }
   440     
   441     if (_this->gl_config.buffer_size) {
   442         attribs[i++] = EGL_BUFFER_SIZE;
   443         attribs[i++] = _this->gl_config.buffer_size;
   444     }
   445     
   446     attribs[i++] = EGL_DEPTH_SIZE;
   447     attribs[i++] = _this->gl_config.depth_size;
   448     
   449     if (_this->gl_config.stencil_size) {
   450         attribs[i++] = EGL_STENCIL_SIZE;
   451         attribs[i++] = _this->gl_config.stencil_size;
   452     }
   453     
   454     if (_this->gl_config.multisamplebuffers) {
   455         attribs[i++] = EGL_SAMPLE_BUFFERS;
   456         attribs[i++] = _this->gl_config.multisamplebuffers;
   457     }
   458     
   459     if (_this->gl_config.multisamplesamples) {
   460         attribs[i++] = EGL_SAMPLES;
   461         attribs[i++] = _this->gl_config.multisamplesamples;
   462     }
   463 
   464     if (_this->gl_config.framebuffer_srgb_capable) {
   465 #ifdef EGL_KHR_gl_colorspace
   466         if (SDL_EGL_HasExtension(_this, SDL_EGL_DISPLAY_EXTENSION, "EGL_KHR_gl_colorspace")) {
   467             attribs[i++] = EGL_GL_COLORSPACE_KHR;
   468             attribs[i++] = EGL_GL_COLORSPACE_SRGB_KHR;
   469         } else
   470 #endif
   471         {
   472             return SDL_SetError("EGL implementation does not support sRGB system framebuffers");
   473         }
   474     }
   475 
   476     attribs[i++] = EGL_RENDERABLE_TYPE;
   477     if (_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_ES) {
   478 #ifdef EGL_KHR_create_context
   479         if (_this->gl_config.major_version >= 3 &&
   480             SDL_EGL_HasExtension(_this, SDL_EGL_DISPLAY_EXTENSION, "EGL_KHR_create_context")) {
   481             attribs[i++] = EGL_OPENGL_ES3_BIT_KHR;
   482         } else
   483 #endif
   484         if (_this->gl_config.major_version >= 2) {
   485             attribs[i++] = EGL_OPENGL_ES2_BIT;
   486         } else {
   487             attribs[i++] = EGL_OPENGL_ES_BIT;
   488         }
   489         _this->egl_data->eglBindAPI(EGL_OPENGL_ES_API);
   490     } else {
   491         attribs[i++] = EGL_OPENGL_BIT;
   492         _this->egl_data->eglBindAPI(EGL_OPENGL_API);
   493     }
   494 
   495     attribs[i++] = EGL_NONE;
   496 
   497     if (_this->egl_data->eglChooseConfig(_this->egl_data->egl_display,
   498         attribs,
   499         configs, SDL_arraysize(configs),
   500         &found_configs) == EGL_FALSE ||
   501         found_configs == 0) {
   502         return SDL_EGL_SetError("Couldn't find matching EGL config", "eglChooseConfig");
   503     }
   504 
   505     /* eglChooseConfig returns a number of configurations that match or exceed the requested attribs. */
   506     /* From those, we select the one that matches our requirements more closely via a makeshift algorithm */
   507 
   508     for (i = 0; i < found_configs; i++ ) {
   509         bitdiff = 0;
   510         for (j = 0; j < SDL_arraysize(attribs) - 1; j += 2) {
   511             if (attribs[j] == EGL_NONE) {
   512                break;
   513             }
   514             
   515             if ( attribs[j+1] != EGL_DONT_CARE && (
   516                 attribs[j] == EGL_RED_SIZE ||
   517                 attribs[j] == EGL_GREEN_SIZE ||
   518                 attribs[j] == EGL_BLUE_SIZE ||
   519                 attribs[j] == EGL_ALPHA_SIZE ||
   520                 attribs[j] == EGL_DEPTH_SIZE ||
   521                 attribs[j] == EGL_STENCIL_SIZE)) {
   522                 _this->egl_data->eglGetConfigAttrib(_this->egl_data->egl_display, configs[i], attribs[j], &value);
   523                 bitdiff += value - attribs[j + 1]; /* value is always >= attrib */
   524             }
   525         }
   526 
   527         if (bitdiff < best_bitdiff || best_bitdiff == -1) {
   528             _this->egl_data->egl_config = configs[i];
   529             
   530             best_bitdiff = bitdiff;
   531         }
   532 
   533         if (bitdiff == 0) {
   534             break; /* we found an exact match! */
   535         }
   536     }
   537     
   538     return 0;
   539 }
   540 
   541 SDL_GLContext
   542 SDL_EGL_CreateContext(_THIS, EGLSurface egl_surface)
   543 {
   544     /* max 14 values plus terminator. */
   545     EGLint attribs[15];
   546     int attr = 0;
   547 
   548     EGLContext egl_context, share_context = EGL_NO_CONTEXT;
   549     EGLint profile_mask = _this->gl_config.profile_mask;
   550     EGLint major_version = _this->gl_config.major_version;
   551     EGLint minor_version = _this->gl_config.minor_version;
   552     SDL_bool profile_es = (profile_mask == SDL_GL_CONTEXT_PROFILE_ES);
   553 
   554     if (!_this->egl_data) {
   555         /* The EGL library wasn't loaded, SDL_GetError() should have info */
   556         return NULL;
   557     }
   558 
   559     if (_this->gl_config.share_with_current_context) {
   560         share_context = (EGLContext)SDL_GL_GetCurrentContext();
   561     }
   562 
   563     /* Set the context version and other attributes. */
   564     if ((major_version < 3 || (minor_version == 0 && profile_es)) &&
   565         _this->gl_config.flags == 0 &&
   566         (profile_mask == 0 || profile_es)) {
   567         /* Create a context without using EGL_KHR_create_context attribs.
   568          * When creating a GLES context without EGL_KHR_create_context we can
   569          * only specify the major version. When creating a desktop GL context
   570          * we can't specify any version, so we only try in that case when the
   571          * version is less than 3.0 (matches SDL's GLX/WGL behavior.)
   572          */
   573         if (profile_es) {
   574             attribs[attr++] = EGL_CONTEXT_CLIENT_VERSION;
   575             attribs[attr++] = SDL_max(major_version, 1);
   576         }
   577     } else {
   578 #ifdef EGL_KHR_create_context
   579         /* The Major/minor version, context profiles, and context flags can
   580          * only be specified when this extension is available.
   581          */
   582         if (SDL_EGL_HasExtension(_this, SDL_EGL_DISPLAY_EXTENSION, "EGL_KHR_create_context")) {
   583             attribs[attr++] = EGL_CONTEXT_MAJOR_VERSION_KHR;
   584             attribs[attr++] = major_version;
   585             attribs[attr++] = EGL_CONTEXT_MINOR_VERSION_KHR;
   586             attribs[attr++] = minor_version;
   587 
   588             /* SDL profile bits match EGL profile bits. */
   589             if (profile_mask != 0 && profile_mask != SDL_GL_CONTEXT_PROFILE_ES) {
   590                 attribs[attr++] = EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR;
   591                 attribs[attr++] = profile_mask;
   592             }
   593 
   594             /* SDL flags match EGL flags. */
   595             if (_this->gl_config.flags != 0) {
   596                 attribs[attr++] = EGL_CONTEXT_FLAGS_KHR;
   597                 attribs[attr++] = _this->gl_config.flags;
   598             }
   599         } else
   600 #endif /* EGL_KHR_create_context */
   601         {
   602             SDL_SetError("Could not create EGL context (context attributes are not supported)");
   603             return NULL;
   604         }
   605     }
   606 
   607     attribs[attr++] = EGL_NONE;
   608 
   609     /* Bind the API */
   610     if (profile_es) {
   611         _this->egl_data->eglBindAPI(EGL_OPENGL_ES_API);
   612     } else {
   613         _this->egl_data->eglBindAPI(EGL_OPENGL_API);
   614     }
   615 
   616     egl_context = _this->egl_data->eglCreateContext(_this->egl_data->egl_display,
   617                                       _this->egl_data->egl_config,
   618                                       share_context, attribs);
   619 
   620     if (egl_context == EGL_NO_CONTEXT) {
   621         SDL_EGL_SetError("Could not create EGL context", "eglCreateContext");
   622         return NULL;
   623     }
   624 
   625     _this->egl_data->egl_swapinterval = 0;
   626 
   627     if (SDL_EGL_MakeCurrent(_this, egl_surface, egl_context) < 0) {
   628         /* Save the SDL error set by SDL_EGL_MakeCurrent */
   629         char errorText[1024];
   630         SDL_strlcpy(errorText, SDL_GetError(), SDL_arraysize(errorText));
   631 
   632         /* Delete the context, which may alter the value returned by SDL_GetError() */
   633         SDL_EGL_DeleteContext(_this, egl_context);
   634 
   635         /* Restore the SDL error */
   636         SDL_SetError("%s", errorText);
   637 
   638         return NULL;
   639     }
   640 
   641     return (SDL_GLContext) egl_context;
   642 }
   643 
   644 int
   645 SDL_EGL_MakeCurrent(_THIS, EGLSurface egl_surface, SDL_GLContext context)
   646 {
   647     EGLContext egl_context = (EGLContext) context;
   648 
   649     if (!_this->egl_data) {
   650         return SDL_SetError("OpenGL not initialized");
   651     }
   652     
   653     /* The android emulator crashes badly if you try to eglMakeCurrent 
   654      * with a valid context and invalid surface, so we have to check for both here.
   655      */
   656     if (!egl_context || !egl_surface) {
   657          _this->egl_data->eglMakeCurrent(_this->egl_data->egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
   658     } else {
   659         if (!_this->egl_data->eglMakeCurrent(_this->egl_data->egl_display,
   660             egl_surface, egl_surface, egl_context)) {
   661             return SDL_EGL_SetError("Unable to make EGL context current", "eglMakeCurrent");
   662         }
   663     }
   664       
   665     return 0;
   666 }
   667 
   668 int
   669 SDL_EGL_SetSwapInterval(_THIS, int interval)
   670 {
   671     EGLBoolean status;
   672     
   673     if (!_this->egl_data) {
   674         return SDL_SetError("EGL not initialized");
   675     }
   676     
   677     status = _this->egl_data->eglSwapInterval(_this->egl_data->egl_display, interval);
   678     if (status == EGL_TRUE) {
   679         _this->egl_data->egl_swapinterval = interval;
   680         return 0;
   681     }
   682     
   683     return SDL_EGL_SetError("Unable to set the EGL swap interval", "eglSwapInterval");
   684 }
   685 
   686 int
   687 SDL_EGL_GetSwapInterval(_THIS)
   688 {
   689     if (!_this->egl_data) {
   690         SDL_SetError("EGL not initialized");
   691         return 0;
   692     }
   693     
   694     return _this->egl_data->egl_swapinterval;
   695 }
   696 
   697 int
   698 SDL_EGL_SwapBuffers(_THIS, EGLSurface egl_surface)
   699 {
   700     if (!_this->egl_data->eglSwapBuffers(_this->egl_data->egl_display, egl_surface)) {
   701         return SDL_EGL_SetError("unable to show color buffer in an OS-native window", "eglSwapBuffers");
   702     }
   703     return 0;
   704 }
   705 
   706 void
   707 SDL_EGL_DeleteContext(_THIS, SDL_GLContext context)
   708 {
   709     EGLContext egl_context = (EGLContext) context;
   710 
   711     /* Clean up GLES and EGL */
   712     if (!_this->egl_data) {
   713         return;
   714     }
   715     
   716     if (egl_context != NULL && egl_context != EGL_NO_CONTEXT) {
   717         SDL_EGL_MakeCurrent(_this, NULL, NULL);
   718         _this->egl_data->eglDestroyContext(_this->egl_data->egl_display, egl_context);
   719     }
   720         
   721 }
   722 
   723 EGLSurface *
   724 SDL_EGL_CreateSurface(_THIS, NativeWindowType nw) 
   725 {
   726     EGLSurface * surface;
   727 
   728     if (SDL_EGL_ChooseConfig(_this) != 0) {
   729         return EGL_NO_SURFACE;
   730     }
   731     
   732 #if SDL_VIDEO_DRIVER_ANDROID
   733     {
   734         /* Android docs recommend doing this!
   735          * Ref: http://developer.android.com/reference/android/app/NativeActivity.html 
   736          */
   737         EGLint format;
   738         _this->egl_data->eglGetConfigAttrib(_this->egl_data->egl_display,
   739                                             _this->egl_data->egl_config, 
   740                                             EGL_NATIVE_VISUAL_ID, &format);
   741 
   742         ANativeWindow_setBuffersGeometry(nw, 0, 0, format);
   743     }
   744 #endif    
   745     
   746     surface = _this->egl_data->eglCreateWindowSurface(
   747             _this->egl_data->egl_display,
   748             _this->egl_data->egl_config,
   749             nw, NULL);
   750     if (surface == EGL_NO_SURFACE) {
   751         SDL_EGL_SetError("unable to create an EGL window surface", "eglCreateWindowSurface");
   752     }
   753     return surface;
   754 }
   755 
   756 void
   757 SDL_EGL_DestroySurface(_THIS, EGLSurface egl_surface) 
   758 {
   759     if (!_this->egl_data) {
   760         return;
   761     }
   762     
   763     if (egl_surface != EGL_NO_SURFACE) {
   764         _this->egl_data->eglDestroySurface(_this->egl_data->egl_display, egl_surface);
   765     }
   766 }
   767 
   768 #endif /* SDL_VIDEO_OPENGL_EGL */
   769 
   770 /* vi: set ts=4 sw=4 expandtab: */
   771