src/render/opengles2/SDL_render_gles2.c
author Sam Lantinga <slouken@libsdl.org>
Sun, 13 Mar 2011 11:18:35 -0700
changeset 5484 e20c93bc9122
parent 5402 5d102cd8aef3
child 5503 be88d105e91c
permissions -rw-r--r--
Added the SDL_HINT_RENDER_SCALE_QUALITY hint, which defaults to nearest pixel sampling.
     1 /*
     2     SDL - Simple DirectMedia Layer
     3     Copyright (C) 1997-2011 Sam Lantinga
     4     Copyright (C) 2010 itsnotabigtruck.
     5 
     6     This library is free software; you can redistribute it and/or
     7     modify it under the terms of the GNU Lesser General Public
     8     License as published by the Free Software Foundation; either
     9     version 2.1 of the License, or (at your option) any later version.
    10 
    11     This library is distributed in the hope that it will be useful,
    12     but WITHOUT ANY WARRANTY; without even the implied warranty of
    13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    14     Lesser General Public License for more details.
    15 
    16     You should have received a copy of the GNU Lesser General Public
    17     License along with this library; if not, write to the Free Software
    18     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
    19 
    20     Sam Lantinga
    21     slouken@libsdl.org
    22 */
    23 #include "SDL_config.h"
    24 
    25 #if SDL_VIDEO_RENDER_OGL_ES2 && !SDL_RENDER_DISABLED
    26 
    27 #include "SDL_hints.h"
    28 #include "SDL_opengles2.h"
    29 #include "../SDL_sysrender.h"
    30 #include "SDL_shaders_gles2.h"
    31 
    32 /*************************************************************************************************
    33  * Bootstrap data                                                                                *
    34  *************************************************************************************************/
    35 
    36 static SDL_Renderer *GLES2_CreateRenderer(SDL_Window *window, Uint32 flags);
    37 
    38 SDL_RenderDriver GLES2_RenderDriver = {
    39     GLES2_CreateRenderer,
    40     {
    41         "opengles2",
    42         (SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC),
    43         1,
    44         {SDL_PIXELFORMAT_ABGR8888},
    45         0,
    46         0
    47     }
    48 };
    49 
    50 /*************************************************************************************************
    51  * Context structures                                                                            *
    52  *************************************************************************************************/
    53 
    54 typedef struct GLES2_TextureData
    55 {
    56     GLenum texture;
    57     GLenum texture_type;
    58     GLenum pixel_format;
    59     GLenum pixel_type;
    60     void *pixel_data;
    61     size_t pitch;
    62     GLenum scaleMode;
    63 } GLES2_TextureData;
    64 
    65 typedef struct GLES2_ShaderCacheEntry
    66 {
    67     GLuint id;
    68     GLES2_ShaderType type;
    69     const GLES2_ShaderInstance *instance;
    70     int references;
    71     struct GLES2_ShaderCacheEntry *prev;
    72     struct GLES2_ShaderCacheEntry *next;
    73 } GLES2_ShaderCacheEntry;
    74 
    75 typedef struct GLES2_ShaderCache
    76 {
    77     int count;
    78     GLES2_ShaderCacheEntry *head;
    79 } GLES2_ShaderCache;
    80 
    81 typedef struct GLES2_ProgramCacheEntry
    82 {
    83     GLuint id;
    84     SDL_BlendMode blend_mode;
    85     GLES2_ShaderCacheEntry *vertex_shader;
    86     GLES2_ShaderCacheEntry *fragment_shader;
    87     GLuint uniform_locations[16];
    88     struct GLES2_ProgramCacheEntry *prev;
    89     struct GLES2_ProgramCacheEntry *next;
    90 } GLES2_ProgramCacheEntry;
    91 
    92 typedef struct GLES2_ProgramCache
    93 {
    94     int count;
    95     GLES2_ProgramCacheEntry *head;
    96     GLES2_ProgramCacheEntry *tail;
    97 } GLES2_ProgramCache;
    98 
    99 typedef enum
   100 {
   101     GLES2_ATTRIBUTE_POSITION = 0,
   102     GLES2_ATTRIBUTE_TEXCOORD = 1
   103 } GLES2_Attribute;
   104 
   105 typedef enum
   106 {
   107     GLES2_UNIFORM_PROJECTION,
   108     GLES2_UNIFORM_TEXTURE,
   109     GLES2_UNIFORM_MODULATION,
   110     GLES2_UNIFORM_COLOR,
   111     GLES2_UNIFORM_COLORTABLE
   112 } GLES2_Uniform;
   113 
   114 typedef enum
   115 {
   116     GLES2_IMAGESOURCE_SOLID,
   117     GLES2_IMAGESOURCE_TEXTURE
   118 } GLES2_ImageSource;
   119 
   120 typedef struct GLES2_DriverContext
   121 {
   122     SDL_GLContext *context;
   123     struct {
   124         int blendMode;
   125         GLenum scaleMode;
   126         SDL_bool tex_coords;
   127     } current;
   128 
   129     int shader_format_count;
   130     GLenum *shader_formats;
   131     GLES2_ShaderCache shader_cache;
   132     GLES2_ProgramCache program_cache;
   133     GLES2_ProgramCacheEntry *current_program;
   134 } GLES2_DriverContext;
   135 
   136 #define GLES2_MAX_CACHED_PROGRAMS 8
   137 
   138 /*************************************************************************************************
   139  * Renderer state APIs                                                                           *
   140  *************************************************************************************************/
   141 
   142 static int GLES2_ActivateRenderer(SDL_Renderer *renderer);
   143 static void GLES2_WindowEvent(SDL_Renderer * renderer,
   144                               const SDL_WindowEvent *event);
   145 static int GLES2_UpdateViewport(SDL_Renderer * renderer);
   146 static void GLES2_DestroyRenderer(SDL_Renderer *renderer);
   147 
   148 static SDL_GLContext SDL_CurrentContext = NULL;
   149 
   150 static int
   151 GLES2_ActivateRenderer(SDL_Renderer * renderer)
   152 {
   153     GLES2_DriverContext *rdata = (GLES2_DriverContext *)renderer->driverdata;
   154 
   155     if (SDL_CurrentContext != rdata->context) {
   156         /* Null out the current program to ensure we set it again */
   157         rdata->current_program = NULL;
   158 
   159         if (SDL_GL_MakeCurrent(renderer->window, rdata->context) < 0) {
   160             return -1;
   161         }
   162         SDL_CurrentContext = rdata->context;
   163 
   164         GLES2_UpdateViewport(renderer);
   165     }
   166     return 0;
   167 }
   168 
   169 static void
   170 GLES2_WindowEvent(SDL_Renderer * renderer, const SDL_WindowEvent *event)
   171 {
   172     if (event->event == SDL_WINDOWEVENT_SIZE_CHANGED) {
   173         /* Rebind the context to the window area */
   174         SDL_CurrentContext = NULL;
   175     }
   176 }
   177 
   178 static int
   179 GLES2_UpdateViewport(SDL_Renderer * renderer)
   180 {
   181     GLES2_DriverContext *rdata = (GLES2_DriverContext *)renderer->driverdata;
   182 
   183     if (SDL_CurrentContext != rdata->context) {
   184         /* We'll update the viewport after we rebind the context */
   185         return 0;
   186     }
   187 
   188     glViewport(renderer->viewport.x, renderer->viewport.y,
   189                renderer->viewport.w, renderer->viewport.h);
   190     return 0;
   191 }
   192 
   193 static void
   194 GLES2_DestroyRenderer(SDL_Renderer *renderer)
   195 {
   196     GLES2_DriverContext *rdata = (GLES2_DriverContext *)renderer->driverdata;
   197     GLES2_ProgramCacheEntry *entry;
   198     GLES2_ProgramCacheEntry *next;
   199 
   200     /* Deallocate everything */
   201     if (rdata) {
   202         GLES2_ActivateRenderer(renderer);
   203 
   204         entry = rdata->program_cache.head;
   205         while (entry) {
   206             glDeleteShader(entry->vertex_shader->id);
   207             glDeleteShader(entry->fragment_shader->id);
   208             SDL_free(entry->vertex_shader);
   209             SDL_free(entry->fragment_shader);
   210             glDeleteProgram(entry->id);
   211             next = entry->next;
   212             SDL_free(entry);
   213             entry = next;
   214         }
   215         if (rdata->context) {
   216             SDL_GL_DeleteContext(rdata->context);
   217         }
   218         if (rdata->shader_formats) {
   219             SDL_free(rdata->shader_formats);
   220         }
   221         SDL_free(rdata);
   222     }
   223     SDL_free(renderer);
   224 }
   225 
   226 /*************************************************************************************************
   227  * Texture APIs                                                                                  *
   228  *************************************************************************************************/
   229 
   230 static int GLES2_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture);
   231 static void GLES2_DestroyTexture(SDL_Renderer *renderer, SDL_Texture *texture);
   232 static int GLES2_LockTexture(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect *rect,
   233                              void **pixels, int *pitch);
   234 static void GLES2_UnlockTexture(SDL_Renderer *renderer, SDL_Texture *texture);
   235 static int GLES2_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect *rect,
   236                                const void *pixels, int pitch);
   237 
   238 static GLenum
   239 GetScaleQuality(void)
   240 {
   241     const char *hint = SDL_GetHint(SDL_HINT_RENDER_SCALE_QUALITY);
   242 
   243     if (!hint || *hint == '0' || SDL_strcasecmp(hint, "nearest") == 0) {
   244         return GL_NEAREST;
   245     } else {
   246         return GL_LINEAR;
   247     }
   248 }
   249 
   250 static int
   251 GLES2_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture)
   252 {
   253     GLES2_TextureData *tdata;
   254     GLenum format;
   255     GLenum type;
   256 
   257     GLES2_ActivateRenderer(renderer);
   258 
   259     /* Determine the corresponding GLES texture format params */
   260     switch (texture->format)
   261     {
   262     case SDL_PIXELFORMAT_ABGR8888:
   263         format = GL_RGBA;
   264         type = GL_UNSIGNED_BYTE;
   265         break;
   266     default:
   267         SDL_SetError("Texture format not supported");
   268         return -1;
   269     }
   270 
   271     /* Allocate a texture struct */
   272     tdata = (GLES2_TextureData *)SDL_calloc(1, sizeof(GLES2_TextureData));
   273     if (!tdata)
   274     {
   275         SDL_OutOfMemory();
   276         return -1;
   277     }
   278     tdata->texture = 0;
   279     tdata->texture_type = GL_TEXTURE_2D;
   280     tdata->pixel_format = format;
   281     tdata->pixel_type = type;
   282     tdata->scaleMode = GetScaleQuality();
   283 
   284     /* Allocate a blob for image data */
   285     if (texture->access == SDL_TEXTUREACCESS_STREAMING)
   286     {
   287         tdata->pitch = texture->w * SDL_BYTESPERPIXEL(texture->format);
   288         tdata->pixel_data = SDL_calloc(1, tdata->pitch * texture->h);
   289         if (!tdata->pixel_data)
   290         {
   291             SDL_OutOfMemory();
   292             SDL_free(tdata);
   293             return -1;
   294         }
   295     }
   296 
   297     /* Allocate the texture */
   298     glGetError();
   299     glGenTextures(1, &tdata->texture);
   300     glActiveTexture(GL_TEXTURE0);
   301     glBindTexture(tdata->texture_type, tdata->texture);
   302     glTexParameteri(tdata->texture_type, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
   303     glTexParameteri(tdata->texture_type, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
   304     glTexImage2D(tdata->texture_type, 0, format, texture->w, texture->h, 0, format, type, NULL);
   305     if (glGetError() != GL_NO_ERROR)
   306     {
   307         SDL_SetError("Texture creation failed");
   308         glDeleteTextures(1, &tdata->texture);
   309         SDL_free(tdata);
   310         return -1;
   311     }
   312     texture->driverdata = tdata;
   313     return 0;
   314 }
   315 
   316 static void
   317 GLES2_DestroyTexture(SDL_Renderer *renderer, SDL_Texture *texture)
   318 {
   319     GLES2_TextureData *tdata = (GLES2_TextureData *)texture->driverdata;
   320 
   321     GLES2_ActivateRenderer(renderer);
   322 
   323     /* Destroy the texture */
   324     if (tdata)
   325     {
   326         glDeleteTextures(1, &tdata->texture);
   327         SDL_free(tdata->pixel_data);
   328         SDL_free(tdata);
   329         texture->driverdata = NULL;
   330     }
   331 }
   332 
   333 static int
   334 GLES2_LockTexture(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect *rect,
   335                   void **pixels, int *pitch)
   336 {
   337     GLES2_TextureData *tdata = (GLES2_TextureData *)texture->driverdata;
   338 
   339     /* Retrieve the buffer/pitch for the specified region */
   340     *pixels = (Uint8 *)tdata->pixel_data +
   341               (tdata->pitch * rect->y) +
   342               (rect->x * SDL_BYTESPERPIXEL(texture->format));
   343     *pitch = tdata->pitch;
   344 
   345     return 0;
   346 }
   347 
   348 static void
   349 GLES2_UnlockTexture(SDL_Renderer *renderer, SDL_Texture *texture)
   350 {
   351     GLES2_TextureData *tdata = (GLES2_TextureData *)texture->driverdata;
   352     SDL_Rect rect;
   353 
   354     /* We do whole texture updates, at least for now */
   355     rect.x = 0;
   356     rect.y = 0;
   357     rect.w = texture->w;
   358     rect.h = texture->h;
   359     GLES2_UpdateTexture(renderer, texture, &rect, tdata->pixel_data, tdata->pitch);
   360 }
   361 
   362 static int
   363 GLES2_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect *rect,
   364                     const void *pixels, int pitch)
   365 {
   366     GLES2_TextureData *tdata = (GLES2_TextureData *)texture->driverdata;
   367     Uint8 *blob = NULL;
   368     Uint8 *src;
   369     int srcPitch;
   370     int y;
   371 
   372     GLES2_ActivateRenderer(renderer);
   373 
   374     /* Bail out if we're supposed to update an empty rectangle */
   375     if (rect->w <= 0 || rect->h <= 0)
   376         return 0;
   377 
   378     /* Reformat the texture data into a tightly packed array */
   379     srcPitch = rect->w * SDL_BYTESPERPIXEL(texture->format);
   380     src = (Uint8 *)pixels;
   381     if (pitch != srcPitch)
   382     {
   383         blob = (Uint8 *)SDL_malloc(srcPitch * rect->h);
   384         if (!blob)
   385         {
   386             SDL_OutOfMemory();
   387             return -1;
   388         }
   389         src = blob;
   390         for (y = 0; y < rect->h; ++y)
   391         {
   392             SDL_memcpy(src, pixels, srcPitch);
   393             src += srcPitch;
   394             pixels = (Uint8 *)pixels + pitch;
   395         }
   396         src = blob;
   397     }
   398 
   399     /* Create a texture subimage with the supplied data */
   400     glGetError();
   401     glActiveTexture(GL_TEXTURE0);
   402     glBindTexture(tdata->texture_type, tdata->texture);
   403     glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
   404     glTexSubImage2D(tdata->texture_type,
   405                     0,
   406                     rect->x,
   407                     rect->y,
   408                     rect->w,
   409                     rect->h,
   410                     tdata->pixel_format,
   411                     tdata->pixel_type,
   412                     src);
   413     if (blob) {
   414         SDL_free(blob);
   415     }
   416 
   417     if (glGetError() != GL_NO_ERROR)
   418     {
   419         SDL_SetError("Failed to update texture");
   420         return -1;
   421     }
   422     return 0;
   423 }
   424 
   425 /*************************************************************************************************
   426  * Shader management functions                                                                   *
   427  *************************************************************************************************/
   428 
   429 static GLES2_ShaderCacheEntry *GLES2_CacheShader(SDL_Renderer *renderer, GLES2_ShaderType type,
   430                                                  SDL_BlendMode blendMode);
   431 static void GLES2_EvictShader(SDL_Renderer *renderer, GLES2_ShaderCacheEntry *entry);
   432 static GLES2_ProgramCacheEntry *GLES2_CacheProgram(SDL_Renderer *renderer,
   433                                                    GLES2_ShaderCacheEntry *vertex,
   434                                                    GLES2_ShaderCacheEntry *fragment,
   435                                                    SDL_BlendMode blendMode);
   436 static int GLES2_SelectProgram(SDL_Renderer *renderer, GLES2_ImageSource source,
   437                                SDL_BlendMode blendMode);
   438 static int GLES2_SetOrthographicProjection(SDL_Renderer *renderer);
   439 
   440 static GLES2_ProgramCacheEntry *
   441 GLES2_CacheProgram(SDL_Renderer *renderer, GLES2_ShaderCacheEntry *vertex,
   442                    GLES2_ShaderCacheEntry *fragment, SDL_BlendMode blendMode)
   443 {
   444     GLES2_DriverContext *rdata = (GLES2_DriverContext *)renderer->driverdata;
   445     GLES2_ProgramCacheEntry *entry;
   446     GLES2_ShaderCacheEntry *shaderEntry;
   447     GLint linkSuccessful;
   448 
   449     /* Check if we've already cached this program */
   450     entry = rdata->program_cache.head;
   451     while (entry)
   452     {
   453         if (entry->vertex_shader == vertex && entry->fragment_shader == fragment)
   454             break;
   455         entry = entry->next;
   456     }
   457     if (entry)
   458     {
   459         if (rdata->program_cache.count > 1)
   460         {
   461             if (entry->next)
   462                 entry->next->prev = entry->prev;
   463             if (entry->prev)
   464                 entry->prev->next = entry->next;
   465             entry->prev = NULL;
   466             entry->next = rdata->program_cache.head;
   467             rdata->program_cache.head->prev = entry;
   468             rdata->program_cache.head = entry;
   469         }
   470         return entry;
   471     }
   472 
   473     /* Create a program cache entry */
   474     entry = (GLES2_ProgramCacheEntry *)SDL_calloc(1, sizeof(GLES2_ProgramCacheEntry));
   475     if (!entry)
   476     {
   477         SDL_OutOfMemory();
   478         return NULL;
   479     }
   480     entry->vertex_shader = vertex;
   481     entry->fragment_shader = fragment;
   482     entry->blend_mode = blendMode;
   483     
   484     /* Create the program and link it */
   485     glGetError();
   486     entry->id = glCreateProgram();
   487     glAttachShader(entry->id, vertex->id);
   488     glAttachShader(entry->id, fragment->id);
   489     glBindAttribLocation(entry->id, GLES2_ATTRIBUTE_POSITION, "a_position");
   490     glBindAttribLocation(entry->id, GLES2_ATTRIBUTE_TEXCOORD, "a_texCoord");
   491     glLinkProgram(entry->id);
   492     glGetProgramiv(entry->id, GL_LINK_STATUS, &linkSuccessful);
   493     if (glGetError() != GL_NO_ERROR || !linkSuccessful)
   494     {
   495         SDL_SetError("Failed to link shader program");
   496         glDeleteProgram(entry->id);
   497         SDL_free(entry);
   498         return NULL;
   499     }
   500     
   501     /* Predetermine locations of uniform variables */
   502     entry->uniform_locations[GLES2_UNIFORM_PROJECTION] =
   503         glGetUniformLocation(entry->id, "u_projection");
   504     entry->uniform_locations[GLES2_UNIFORM_TEXTURE] =
   505         glGetUniformLocation(entry->id, "u_texture");
   506     entry->uniform_locations[GLES2_UNIFORM_MODULATION] =
   507         glGetUniformLocation(entry->id, "u_modulation");
   508     entry->uniform_locations[GLES2_UNIFORM_COLOR] =
   509         glGetUniformLocation(entry->id, "u_color");
   510     entry->uniform_locations[GLES2_UNIFORM_COLORTABLE] =
   511         glGetUniformLocation(entry->id, "u_colorTable");
   512 
   513     /* Cache the linked program */
   514     if (rdata->program_cache.head)
   515     {
   516         entry->next = rdata->program_cache.head;
   517         rdata->program_cache.head->prev = entry;
   518     }
   519     else
   520     {
   521         rdata->program_cache.tail = entry;
   522     }
   523     rdata->program_cache.head = entry;
   524     ++rdata->program_cache.count;
   525 
   526     /* Increment the refcount of the shaders we're using */
   527     ++vertex->references;
   528     ++fragment->references;
   529 
   530     /* Evict the last entry from the cache if we exceed the limit */
   531     if (rdata->program_cache.count > GLES2_MAX_CACHED_PROGRAMS)
   532     {
   533         shaderEntry = rdata->program_cache.tail->vertex_shader;
   534         if (--shaderEntry->references <= 0)
   535             GLES2_EvictShader(renderer, shaderEntry);
   536         shaderEntry = rdata->program_cache.tail->fragment_shader;
   537         if (--shaderEntry->references <= 0)
   538             GLES2_EvictShader(renderer, shaderEntry);
   539         glDeleteProgram(rdata->program_cache.tail->id);
   540         rdata->program_cache.tail = rdata->program_cache.tail->prev;
   541         SDL_free(rdata->program_cache.tail->next);
   542         rdata->program_cache.tail->next = NULL;
   543         --rdata->program_cache.count;
   544     }
   545     return entry;
   546 }
   547 
   548 static GLES2_ShaderCacheEntry *
   549 GLES2_CacheShader(SDL_Renderer *renderer, GLES2_ShaderType type, SDL_BlendMode blendMode)
   550 {
   551     GLES2_DriverContext *rdata = (GLES2_DriverContext *)renderer->driverdata;
   552     const GLES2_Shader *shader;
   553     const GLES2_ShaderInstance *instance = NULL;
   554     GLES2_ShaderCacheEntry *entry = NULL;
   555     GLint compileSuccessful = GL_FALSE;
   556     int i, j;
   557 
   558     /* Find the corresponding shader */
   559     shader = GLES2_GetShader(type, blendMode);
   560     if (!shader)
   561     {
   562         SDL_SetError("No shader matching the requested characteristics was found");
   563         return NULL;
   564     }
   565     
   566     /* Find a matching shader instance that's supported on this hardware */
   567     for (i = 0; i < shader->instance_count && !instance; ++i)
   568     {
   569         for (j = 0; j < rdata->shader_format_count && !instance; ++j)
   570         {
   571             if (!shader->instances)
   572                 continue;
   573             if (!shader->instances[i])
   574                 continue;
   575             if (shader->instances[i]->format != rdata->shader_formats[j])
   576                 continue;
   577             instance = shader->instances[i];
   578         }
   579     }
   580     if (!instance)
   581     {
   582         SDL_SetError("The specified shader cannot be loaded on the current platform");
   583         return NULL;
   584     }
   585 
   586     /* Check if we've already cached this shader */
   587     entry = rdata->shader_cache.head;
   588     while (entry)
   589     {
   590         if (entry->instance == instance)
   591             break;
   592         entry = entry->next;
   593     }
   594     if (entry)
   595         return entry;
   596 
   597     /* Create a shader cache entry */
   598     entry = (GLES2_ShaderCacheEntry *)SDL_calloc(1, sizeof(GLES2_ShaderCacheEntry));
   599     if (!entry)
   600     {
   601         SDL_OutOfMemory();
   602         return NULL;
   603     }
   604     entry->type = type;
   605     entry->instance = instance;
   606 
   607     /* Compile or load the selected shader instance */
   608     glGetError();
   609     entry->id = glCreateShader(instance->type);
   610     if (instance->format == (GLenum)-1)
   611     {
   612         glShaderSource(entry->id, 1, (const char **)&instance->data, NULL);
   613         glCompileShader(entry->id);
   614         glGetShaderiv(entry->id, GL_COMPILE_STATUS, &compileSuccessful);
   615     }
   616     else
   617     {
   618         glShaderBinary(1, &entry->id, instance->format, instance->data, instance->length);
   619         compileSuccessful = GL_TRUE;
   620     }
   621     if (glGetError() != GL_NO_ERROR || !compileSuccessful)
   622     {
   623         char *info = NULL;
   624         int length;
   625 
   626         glGetShaderiv(entry->id, GL_INFO_LOG_LENGTH, &length);
   627         if (length > 0) {
   628             info = SDL_stack_alloc(char, length);
   629             if (info) {
   630                 glGetShaderInfoLog(entry->id, length, &length, info);
   631             }
   632         }
   633         if (info) {
   634             SDL_SetError("Failed to load the shader: %s", info);
   635             SDL_stack_free(info);
   636         } else {
   637             SDL_SetError("Failed to load the shader");
   638         }
   639         glDeleteShader(entry->id);
   640         SDL_free(entry);
   641         return NULL;
   642     }
   643 
   644     /* Link the shader entry in at the front of the cache */
   645     if (rdata->shader_cache.head)
   646     {
   647         entry->next = rdata->shader_cache.head;
   648         rdata->shader_cache.head->prev = entry;
   649     }
   650     rdata->shader_cache.head = entry;
   651     ++rdata->shader_cache.count;
   652     return entry;
   653 }
   654 
   655 static void
   656 GLES2_EvictShader(SDL_Renderer *renderer, GLES2_ShaderCacheEntry *entry)
   657 {
   658     GLES2_DriverContext *rdata = (GLES2_DriverContext *)renderer->driverdata;
   659 
   660     /* Unlink the shader from the cache */
   661     if (entry->next)
   662         entry->next->prev = entry->prev;
   663     if (entry->prev)
   664         entry->prev->next = entry->next;
   665     if (rdata->shader_cache.head == entry)
   666         rdata->shader_cache.head = entry->next;
   667     --rdata->shader_cache.count;
   668 
   669     /* Deallocate the shader */
   670     glDeleteShader(entry->id);
   671     SDL_free(entry);
   672 }
   673 
   674 static int
   675 GLES2_SelectProgram(SDL_Renderer *renderer, GLES2_ImageSource source, SDL_BlendMode blendMode)
   676 {
   677     GLES2_DriverContext *rdata = (GLES2_DriverContext *)renderer->driverdata;
   678     GLES2_ShaderCacheEntry *vertex = NULL;
   679     GLES2_ShaderCacheEntry *fragment = NULL;
   680     GLES2_ShaderType vtype, ftype;
   681     GLES2_ProgramCacheEntry *program;
   682 
   683     /* Select an appropriate shader pair for the specified modes */
   684     vtype = GLES2_SHADER_VERTEX_DEFAULT;
   685     switch (source)
   686     {
   687     case GLES2_IMAGESOURCE_SOLID:
   688         ftype = GLES2_SHADER_FRAGMENT_SOLID_SRC;
   689         break;
   690     case GLES2_IMAGESOURCE_TEXTURE:
   691         ftype = GLES2_SHADER_FRAGMENT_TEXTURE_SRC;
   692         break;
   693     }
   694 
   695     /* Load the requested shaders */
   696     vertex = GLES2_CacheShader(renderer, vtype, blendMode);
   697     if (!vertex)
   698         goto fault;
   699     fragment = GLES2_CacheShader(renderer, ftype, blendMode);
   700     if (!fragment)
   701         goto fault;
   702 
   703     /* Check if we need to change programs at all */
   704     if (rdata->current_program &&
   705         rdata->current_program->vertex_shader == vertex &&
   706         rdata->current_program->fragment_shader == fragment)
   707         return 0;
   708 
   709     /* Generate a matching program */
   710     program = GLES2_CacheProgram(renderer, vertex, fragment, blendMode);
   711     if (!program)
   712         goto fault;
   713 
   714     /* Select that program in OpenGL */
   715     glGetError();
   716     glUseProgram(program->id);
   717     if (glGetError() != GL_NO_ERROR)
   718     {
   719         SDL_SetError("Failed to select program");
   720         goto fault;
   721     }
   722 
   723     /* Set the current program */
   724     rdata->current_program = program;
   725 
   726     /* Activate an orthographic projection */
   727     if (GLES2_SetOrthographicProjection(renderer) < 0)
   728         goto fault;
   729 
   730     /* Clean up and return */
   731     return 0;
   732 fault:
   733     if (vertex && vertex->references <= 0)
   734         GLES2_EvictShader(renderer, vertex);
   735     if (fragment && fragment->references <= 0)
   736         GLES2_EvictShader(renderer, fragment);
   737     rdata->current_program = NULL;
   738     return -1;
   739 }
   740 
   741 static int
   742 GLES2_SetOrthographicProjection(SDL_Renderer *renderer)
   743 {
   744     GLES2_DriverContext *rdata = (GLES2_DriverContext *)renderer->driverdata;
   745     GLfloat projection[4][4];
   746     GLuint locProjection;
   747 
   748     /* Prepare an orthographic projection */
   749     projection[0][0] = 2.0f / renderer->viewport.w;
   750     projection[0][1] = 0.0f;
   751     projection[0][2] = 0.0f;
   752     projection[0][3] = 0.0f;
   753     projection[1][0] = 0.0f;
   754     projection[1][1] = -2.0f / renderer->viewport.h;
   755     projection[1][2] = 0.0f;
   756     projection[1][3] = 0.0f;
   757     projection[2][0] = 0.0f;
   758     projection[2][1] = 0.0f;
   759     projection[2][2] = 1.0f;
   760     projection[2][3] = 0.0f;
   761     projection[3][0] = -1.0f;
   762     projection[3][1] = 1.0f;
   763     projection[3][2] = 0.0f;
   764     projection[3][3] = 1.0f;
   765 
   766     /* Set the projection matrix */
   767     locProjection = rdata->current_program->uniform_locations[GLES2_UNIFORM_PROJECTION];
   768     glGetError();
   769     glUniformMatrix4fv(locProjection, 1, GL_FALSE, (GLfloat *)projection);
   770     if (glGetError() != GL_NO_ERROR)
   771     {
   772         SDL_SetError("Failed to set orthographic projection");
   773         return -1;
   774     }
   775     return 0;
   776 }
   777 
   778 /*************************************************************************************************
   779  * Rendering functions                                                                           *
   780  *************************************************************************************************/
   781 
   782 static const float inv255f = 1.0f / 255.0f;
   783 
   784 static int GLES2_RenderClear(SDL_Renderer *renderer);
   785 static int GLES2_RenderDrawPoints(SDL_Renderer *renderer, const SDL_Point *points, int count);
   786 static int GLES2_RenderDrawLines(SDL_Renderer *renderer, const SDL_Point *points, int count);
   787 static int GLES2_RenderFillRects(SDL_Renderer *renderer, const SDL_Rect *rects, int count);
   788 static int GLES2_RenderCopy(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect *srcrect,
   789                             const SDL_Rect *dstrect);
   790 static void GLES2_RenderPresent(SDL_Renderer *renderer);
   791 
   792 
   793 static int
   794 GLES2_RenderClear(SDL_Renderer * renderer)
   795 {
   796     GLES2_ActivateRenderer(renderer);
   797 
   798     glClearColor((GLfloat) renderer->r * inv255f,
   799                  (GLfloat) renderer->g * inv255f,
   800                  (GLfloat) renderer->b * inv255f,
   801                  (GLfloat) renderer->a * inv255f);
   802 
   803     glClear(GL_COLOR_BUFFER_BIT);
   804 
   805     return 0;
   806 }
   807 
   808 static void
   809 GLES2_SetBlendMode(GLES2_DriverContext *rdata, int blendMode)
   810 {
   811     if (blendMode != rdata->current.blendMode) {
   812         switch (blendMode) {
   813         default:
   814         case SDL_BLENDMODE_NONE:
   815             glDisable(GL_BLEND);
   816             break;
   817         case SDL_BLENDMODE_BLEND:
   818             glEnable(GL_BLEND);
   819             glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
   820             break;
   821         case SDL_BLENDMODE_ADD:
   822             glEnable(GL_BLEND);
   823             glBlendFunc(GL_SRC_ALPHA, GL_ONE);
   824             break;
   825         case SDL_BLENDMODE_MOD:
   826             glEnable(GL_BLEND);
   827             glBlendFunc(GL_ZERO, GL_SRC_COLOR);
   828             break;
   829         }
   830         rdata->current.blendMode = blendMode;
   831     }
   832 }
   833 
   834 static void
   835 GLES2_SetTexCoords(GLES2_DriverContext * rdata, SDL_bool enabled)
   836 {
   837     if (enabled != rdata->current.tex_coords) {
   838         if (enabled) {
   839             glEnableVertexAttribArray(GLES2_ATTRIBUTE_TEXCOORD);
   840         } else {
   841             glDisableVertexAttribArray(GLES2_ATTRIBUTE_TEXCOORD);
   842         }
   843         rdata->current.tex_coords = enabled;
   844     }
   845 }
   846 
   847 static int
   848 GLES2_SetDrawingState(SDL_Renderer * renderer)
   849 {
   850     GLES2_DriverContext *rdata = (GLES2_DriverContext *)renderer->driverdata;
   851     int blendMode = renderer->blendMode;
   852     GLuint locColor;
   853 
   854     glGetError();
   855 
   856     GLES2_ActivateRenderer(renderer);
   857 
   858     GLES2_SetBlendMode(rdata, blendMode);
   859 
   860     GLES2_SetTexCoords(rdata, SDL_FALSE);
   861 
   862     /* Activate an appropriate shader and set the projection matrix */
   863     if (GLES2_SelectProgram(renderer, GLES2_IMAGESOURCE_SOLID, blendMode) < 0)
   864         return -1;
   865 
   866     /* Select the color to draw with */
   867     locColor = rdata->current_program->uniform_locations[GLES2_UNIFORM_COLOR];
   868     glUniform4f(locColor,
   869                 renderer->r * inv255f,
   870                 renderer->g * inv255f,
   871                 renderer->b * inv255f,
   872                 renderer->a * inv255f);
   873     return 0;
   874 }
   875 
   876 static int
   877 GLES2_RenderDrawPoints(SDL_Renderer *renderer, const SDL_Point *points, int count)
   878 {
   879     GLES2_DriverContext *rdata = (GLES2_DriverContext *)renderer->driverdata;
   880     GLfloat *vertices;
   881     int idx;
   882 
   883     if (GLES2_SetDrawingState(renderer) < 0) {
   884         return -1;
   885     }
   886 
   887     /* Emit the specified vertices as points */
   888     vertices = SDL_stack_alloc(GLfloat, count * 2);
   889     for (idx = 0; idx < count; ++idx)
   890     {
   891         GLfloat x = (GLfloat)points[idx].x + 0.5f;
   892         GLfloat y = (GLfloat)points[idx].y + 0.5f;
   893 
   894         vertices[idx * 2] = x;
   895         vertices[(idx * 2) + 1] = y;
   896     }
   897     glGetError();
   898     glVertexAttribPointer(GLES2_ATTRIBUTE_POSITION, 2, GL_FLOAT, GL_FALSE, 0, vertices);
   899     glDrawArrays(GL_POINTS, 0, count);
   900     SDL_stack_free(vertices);
   901     if (glGetError() != GL_NO_ERROR)
   902     {
   903         SDL_SetError("Failed to render lines");
   904         return -1;
   905     }
   906     return 0;
   907 }
   908 
   909 static int
   910 GLES2_RenderDrawLines(SDL_Renderer *renderer, const SDL_Point *points, int count)
   911 {
   912     GLES2_DriverContext *rdata = (GLES2_DriverContext *)renderer->driverdata;
   913     GLfloat *vertices;
   914     int idx;
   915 
   916     if (GLES2_SetDrawingState(renderer) < 0) {
   917         return -1;
   918     }
   919 
   920     /* Emit a line strip including the specified vertices */
   921     vertices = SDL_stack_alloc(GLfloat, count * 2);
   922     for (idx = 0; idx < count; ++idx)
   923     {
   924         GLfloat x = (GLfloat)points[idx].x + 0.5f;
   925         GLfloat y = (GLfloat)points[idx].y + 0.5f;
   926 
   927         vertices[idx * 2] = x;
   928         vertices[(idx * 2) + 1] = y;
   929     }
   930     glGetError();
   931     glVertexAttribPointer(GLES2_ATTRIBUTE_POSITION, 2, GL_FLOAT, GL_FALSE, 0, vertices);
   932     glDrawArrays(GL_LINE_STRIP, 0, count);
   933     SDL_stack_free(vertices);
   934     if (glGetError() != GL_NO_ERROR)
   935     {
   936         SDL_SetError("Failed to render lines");
   937         return -1;
   938     }
   939     return 0;
   940 }
   941 
   942 static int
   943 GLES2_RenderFillRects(SDL_Renderer *renderer, const SDL_Rect *rects, int count)
   944 {
   945     GLES2_DriverContext *rdata = (GLES2_DriverContext *)renderer->driverdata;
   946     GLfloat vertices[8];
   947     int idx;
   948 
   949     if (GLES2_SetDrawingState(renderer) < 0) {
   950         return -1;
   951     }
   952 
   953     /* Emit a line loop for each rectangle */
   954     glGetError();
   955     for (idx = 0; idx < count; ++idx) {
   956         const SDL_Rect *rect = &rects[idx];
   957 
   958         GLfloat xMin = (GLfloat)rect->x;
   959         GLfloat xMax = (GLfloat)(rect->x + rect->w);
   960         GLfloat yMin = (GLfloat)rect->y;
   961         GLfloat yMax = (GLfloat)(rect->y + rect->h);
   962 
   963         vertices[0] = xMin;
   964         vertices[1] = yMin;
   965         vertices[2] = xMax;
   966         vertices[3] = yMin;
   967         vertices[4] = xMin;
   968         vertices[5] = yMax;
   969         vertices[6] = xMax;
   970         vertices[7] = yMax;
   971         glVertexAttribPointer(GLES2_ATTRIBUTE_POSITION, 2, GL_FLOAT, GL_FALSE, 0, vertices);
   972         glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
   973     }
   974     if (glGetError() != GL_NO_ERROR)
   975     {
   976         SDL_SetError("Failed to render lines");
   977         return -1;
   978     }
   979     return 0;
   980 }
   981 
   982 static int
   983 GLES2_RenderCopy(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect *srcrect,
   984                  const SDL_Rect *dstrect)
   985 {
   986     GLES2_DriverContext *rdata = (GLES2_DriverContext *)renderer->driverdata;
   987     GLES2_TextureData *tdata = (GLES2_TextureData *)texture->driverdata;
   988     GLES2_ImageSource sourceType;
   989     SDL_BlendMode blendMode;
   990     GLfloat vertices[8];
   991     GLfloat texCoords[8];
   992     GLuint locTexture;
   993     GLuint locModulation;
   994 
   995     GLES2_ActivateRenderer(renderer);
   996 
   997     /* Activate an appropriate shader and set the projection matrix */
   998     blendMode = texture->blendMode;
   999     sourceType = GLES2_IMAGESOURCE_TEXTURE;
  1000     if (GLES2_SelectProgram(renderer, sourceType, blendMode) < 0)
  1001         return -1;
  1002 
  1003     /* Select the target texture */
  1004     locTexture = rdata->current_program->uniform_locations[GLES2_UNIFORM_TEXTURE];
  1005     glGetError();
  1006     glActiveTexture(GL_TEXTURE0);
  1007     glBindTexture(tdata->texture_type, tdata->texture);
  1008     glUniform1i(locTexture, 0);
  1009 
  1010     if (tdata->scaleMode != rdata->current.scaleMode) {
  1011         glTexParameteri(tdata->texture_type, GL_TEXTURE_MIN_FILTER,
  1012                         tdata->scaleMode);
  1013         glTexParameteri(tdata->texture_type, GL_TEXTURE_MAG_FILTER,
  1014                         tdata->scaleMode);
  1015         rdata->current.scaleMode = tdata->scaleMode;
  1016     }
  1017 
  1018     /* Configure color modulation */
  1019     locModulation = rdata->current_program->uniform_locations[GLES2_UNIFORM_MODULATION];
  1020     glUniform4f(locModulation,
  1021                 texture->r * inv255f,
  1022                 texture->g * inv255f,
  1023                 texture->b * inv255f,
  1024                 texture->a * inv255f);
  1025 
  1026     /* Configure texture blending */
  1027     GLES2_SetBlendMode(rdata, blendMode);
  1028 
  1029     GLES2_SetTexCoords(rdata, SDL_TRUE);
  1030 
  1031     /* Emit the textured quad */
  1032     vertices[0] = (GLfloat)dstrect->x;
  1033     vertices[1] = (GLfloat)dstrect->y;
  1034     vertices[2] = (GLfloat)(dstrect->x + dstrect->w);
  1035     vertices[3] = (GLfloat)dstrect->y;
  1036     vertices[4] = (GLfloat)dstrect->x;
  1037     vertices[5] = (GLfloat)(dstrect->y + dstrect->h);
  1038     vertices[6] = (GLfloat)(dstrect->x + dstrect->w);
  1039     vertices[7] = (GLfloat)(dstrect->y + dstrect->h);
  1040     glVertexAttribPointer(GLES2_ATTRIBUTE_POSITION, 2, GL_FLOAT, GL_FALSE, 0, vertices);
  1041     texCoords[0] = srcrect->x / (GLfloat)texture->w;
  1042     texCoords[1] = srcrect->y / (GLfloat)texture->h;
  1043     texCoords[2] = (srcrect->x + srcrect->w) / (GLfloat)texture->w;
  1044     texCoords[3] = srcrect->y / (GLfloat)texture->h;
  1045     texCoords[4] = srcrect->x / (GLfloat)texture->w;
  1046     texCoords[5] = (srcrect->y + srcrect->h) / (GLfloat)texture->h;
  1047     texCoords[6] = (srcrect->x + srcrect->w) / (GLfloat)texture->w;
  1048     texCoords[7] = (srcrect->y + srcrect->h) / (GLfloat)texture->h;
  1049     glVertexAttribPointer(GLES2_ATTRIBUTE_TEXCOORD, 2, GL_FLOAT, GL_FALSE, 0, texCoords);
  1050     glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
  1051     if (glGetError() != GL_NO_ERROR)
  1052     {
  1053         SDL_SetError("Failed to render texture");
  1054         return -1;
  1055     }
  1056     return 0;
  1057 }
  1058 
  1059 static void
  1060 GLES2_RenderPresent(SDL_Renderer *renderer)
  1061 {
  1062     GLES2_ActivateRenderer(renderer);
  1063 
  1064     /* Tell the video driver to swap buffers */
  1065     SDL_GL_SwapWindow(renderer->window);
  1066 }
  1067 
  1068 /*************************************************************************************************
  1069  * Renderer instantiation                                                                        *
  1070  *************************************************************************************************/
  1071 
  1072 #define GL_NVIDIA_PLATFORM_BINARY_NV 0x890B
  1073 
  1074 static void
  1075 GLES2_ResetState(SDL_Renderer *renderer)
  1076 {
  1077     GLES2_DriverContext *rdata = (GLES2_DriverContext *) renderer->driverdata;
  1078 
  1079     if (SDL_CurrentContext == rdata->context) {
  1080         GLES2_UpdateViewport(renderer);
  1081     } else {
  1082         GLES2_ActivateRenderer(renderer);
  1083     }
  1084 
  1085     rdata->current.blendMode = -1;
  1086     rdata->current.scaleMode = 0;
  1087     rdata->current.tex_coords = SDL_FALSE;
  1088 
  1089     glEnableVertexAttribArray(GLES2_ATTRIBUTE_POSITION);
  1090     glDisableVertexAttribArray(GLES2_ATTRIBUTE_TEXCOORD);
  1091 }
  1092 
  1093 static SDL_Renderer *
  1094 GLES2_CreateRenderer(SDL_Window *window, Uint32 flags)
  1095 {
  1096     SDL_Renderer *renderer;
  1097     GLES2_DriverContext *rdata;
  1098     GLint nFormats;
  1099 #ifndef ZUNE_HD
  1100     GLboolean hasCompiler;
  1101 #endif
  1102 
  1103     /* Create the renderer struct */
  1104     renderer = (SDL_Renderer *)SDL_calloc(1, sizeof(SDL_Renderer));
  1105     if (!renderer) {
  1106         SDL_OutOfMemory();
  1107         return NULL;
  1108     }
  1109 
  1110     rdata = (GLES2_DriverContext *)SDL_calloc(1, sizeof(GLES2_DriverContext));
  1111     if (!rdata) {
  1112         GLES2_DestroyRenderer(renderer);
  1113         SDL_OutOfMemory();
  1114         return NULL;
  1115     }
  1116     renderer->info = GLES2_RenderDriver.info;
  1117     renderer->info.flags = SDL_RENDERER_ACCELERATED;
  1118     renderer->driverdata = rdata;
  1119 
  1120     /* Create an OpenGL ES 2.0 context */
  1121     SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
  1122     SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
  1123 
  1124     rdata->context = SDL_GL_CreateContext(window);
  1125     if (!rdata->context)
  1126     {
  1127         GLES2_DestroyRenderer(renderer);
  1128         return NULL;
  1129     }
  1130     if (SDL_GL_MakeCurrent(window, rdata->context) < 0) {
  1131         GLES2_DestroyRenderer(renderer);
  1132         return NULL;
  1133     }
  1134 
  1135     if (flags & SDL_RENDERER_PRESENTVSYNC) {
  1136         SDL_GL_SetSwapInterval(1);
  1137     } else {
  1138         SDL_GL_SetSwapInterval(0);
  1139     }
  1140     if (SDL_GL_GetSwapInterval() > 0) {
  1141         renderer->info.flags |= SDL_RENDERER_PRESENTVSYNC;
  1142     }
  1143 
  1144     /* Determine supported shader formats */
  1145     /* HACK: glGetInteger is broken on the Zune HD's compositor, so we just hardcode this */
  1146     glGetError();
  1147 #ifdef ZUNE_HD
  1148     nFormats = 1;
  1149 #else /* !ZUNE_HD */
  1150     glGetIntegerv(GL_NUM_SHADER_BINARY_FORMATS, &nFormats);
  1151     glGetBooleanv(GL_SHADER_COMPILER, &hasCompiler);
  1152     if (hasCompiler)
  1153         ++nFormats;
  1154 #endif /* ZUNE_HD */
  1155     rdata->shader_formats = (GLenum *)SDL_calloc(nFormats, sizeof(GLenum));
  1156     if (!rdata->shader_formats)
  1157     {
  1158         GLES2_DestroyRenderer(renderer);
  1159         SDL_OutOfMemory();
  1160         return NULL;
  1161     }
  1162     rdata->shader_format_count = nFormats;
  1163 #ifdef ZUNE_HD
  1164     rdata->shader_formats[0] = GL_NVIDIA_PLATFORM_BINARY_NV;
  1165 #else /* !ZUNE_HD */
  1166     glGetIntegerv(GL_SHADER_BINARY_FORMATS, (GLint *)rdata->shader_formats);
  1167     if (glGetError() != GL_NO_ERROR)
  1168     {
  1169         GLES2_DestroyRenderer(renderer);
  1170         SDL_SetError("Failed to query supported shader formats");
  1171         return NULL;
  1172     }
  1173     if (hasCompiler)
  1174         rdata->shader_formats[nFormats - 1] = (GLenum)-1;
  1175 #endif /* ZUNE_HD */
  1176 
  1177     /* Populate the function pointers for the module */
  1178     renderer->WindowEvent         = &GLES2_WindowEvent;
  1179     renderer->CreateTexture       = &GLES2_CreateTexture;
  1180     renderer->UpdateTexture       = &GLES2_UpdateTexture;
  1181     renderer->LockTexture         = &GLES2_LockTexture;
  1182     renderer->UnlockTexture       = &GLES2_UnlockTexture;
  1183     renderer->UpdateViewport      = &GLES2_UpdateViewport;
  1184     renderer->RenderClear         = &GLES2_RenderClear;
  1185     renderer->RenderDrawPoints    = &GLES2_RenderDrawPoints;
  1186     renderer->RenderDrawLines     = &GLES2_RenderDrawLines;
  1187     renderer->RenderFillRects     = &GLES2_RenderFillRects;
  1188     renderer->RenderCopy          = &GLES2_RenderCopy;
  1189     renderer->RenderPresent       = &GLES2_RenderPresent;
  1190     renderer->DestroyTexture      = &GLES2_DestroyTexture;
  1191     renderer->DestroyRenderer     = &GLES2_DestroyRenderer;
  1192 
  1193     GLES2_ResetState(renderer);
  1194 
  1195     return renderer;
  1196 }
  1197 
  1198 #endif /* SDL_VIDEO_RENDER_OGL_ES2 && !SDL_RENDER_DISABLED */
  1199 
  1200 /* vi: set ts=4 sw=4 expandtab: */