src/render/opengles/SDL_render_gles.c
author Ryan C. Gordon
Sat, 19 Sep 2020 14:01:57 -0400
changeset 14040 fe260271ec7a
parent 13536 4ba421b1e88f
permissions -rw-r--r--
jack: Fixed memory leak on device close.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2020 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_RENDER_OGL_ES && !SDL_RENDER_DISABLED
    24 
    25 #include "SDL_assert.h"
    26 #include "SDL_hints.h"
    27 #include "SDL_opengles.h"
    28 #include "../SDL_sysrender.h"
    29 
    30 /* To prevent unnecessary window recreation,
    31  * these should match the defaults selected in SDL_GL_ResetAttributes
    32  */
    33 
    34 #define RENDERER_CONTEXT_MAJOR 1
    35 #define RENDERER_CONTEXT_MINOR 1
    36 
    37 #if defined(SDL_VIDEO_DRIVER_PANDORA)
    38 
    39 /* Empty function stub to get OpenGL ES 1.x support without  */
    40 /* OpenGL ES extension GL_OES_draw_texture supported         */
    41 GL_API void GL_APIENTRY
    42 glDrawTexiOES(GLint x, GLint y, GLint z, GLint width, GLint height)
    43 {
    44     return;
    45 }
    46 
    47 #endif /* SDL_VIDEO_DRIVER_PANDORA */
    48 
    49 /* OpenGL ES 1.1 renderer implementation, based on the OpenGL renderer */
    50 
    51 /* Used to re-create the window with OpenGL ES capability */
    52 extern int SDL_RecreateWindow(SDL_Window * window, Uint32 flags);
    53 
    54 static const float inv255f = 1.0f / 255.0f;
    55 
    56 typedef struct GLES_FBOList GLES_FBOList;
    57 
    58 struct GLES_FBOList
    59 {
    60    Uint32 w, h;
    61    GLuint FBO;
    62    GLES_FBOList *next;
    63 };
    64 
    65 typedef struct
    66 {
    67     SDL_Rect viewport;
    68     SDL_bool viewport_dirty;
    69     SDL_Texture *texture;
    70     SDL_Texture *target;
    71     int drawablew;
    72     int drawableh;
    73     SDL_BlendMode blend;
    74     SDL_bool cliprect_enabled_dirty;
    75     SDL_bool cliprect_enabled;
    76     SDL_bool cliprect_dirty;
    77     SDL_Rect cliprect;
    78     SDL_bool texturing;
    79     Uint32 color;
    80     Uint32 clear_color;
    81 } GLES_DrawStateCache;
    82 
    83 typedef struct
    84 {
    85     SDL_GLContext context;
    86 
    87 #define SDL_PROC(ret,func,params) ret (APIENTRY *func) params;
    88 #define SDL_PROC_OES SDL_PROC
    89 #include "SDL_glesfuncs.h"
    90 #undef SDL_PROC
    91 #undef SDL_PROC_OES
    92     SDL_bool GL_OES_framebuffer_object_supported;
    93     GLES_FBOList *framebuffers;
    94     GLuint window_framebuffer;
    95 
    96     SDL_bool GL_OES_blend_func_separate_supported;
    97     SDL_bool GL_OES_blend_equation_separate_supported;
    98     SDL_bool GL_OES_blend_subtract_supported;
    99 
   100     GLES_DrawStateCache drawstate;
   101 } GLES_RenderData;
   102 
   103 typedef struct
   104 {
   105     GLuint texture;
   106     GLenum type;
   107     GLfloat texw;
   108     GLfloat texh;
   109     GLenum format;
   110     GLenum formattype;
   111     void *pixels;
   112     int pitch;
   113     GLES_FBOList *fbo;
   114 } GLES_TextureData;
   115 
   116 static int
   117 GLES_SetError(const char *prefix, GLenum result)
   118 {
   119     const char *error;
   120 
   121     switch (result) {
   122     case GL_NO_ERROR:
   123         error = "GL_NO_ERROR";
   124         break;
   125     case GL_INVALID_ENUM:
   126         error = "GL_INVALID_ENUM";
   127         break;
   128     case GL_INVALID_VALUE:
   129         error = "GL_INVALID_VALUE";
   130         break;
   131     case GL_INVALID_OPERATION:
   132         error = "GL_INVALID_OPERATION";
   133         break;
   134     case GL_STACK_OVERFLOW:
   135         error = "GL_STACK_OVERFLOW";
   136         break;
   137     case GL_STACK_UNDERFLOW:
   138         error = "GL_STACK_UNDERFLOW";
   139         break;
   140     case GL_OUT_OF_MEMORY:
   141         error = "GL_OUT_OF_MEMORY";
   142         break;
   143     default:
   144         error = "UNKNOWN";
   145         break;
   146     }
   147     return SDL_SetError("%s: %s", prefix, error);
   148 }
   149 
   150 static int GLES_LoadFunctions(GLES_RenderData * data)
   151 {
   152 #if SDL_VIDEO_DRIVER_UIKIT
   153 #define __SDL_NOGETPROCADDR__
   154 #elif SDL_VIDEO_DRIVER_ANDROID
   155 #define __SDL_NOGETPROCADDR__
   156 #elif SDL_VIDEO_DRIVER_PANDORA
   157 #define __SDL_NOGETPROCADDR__
   158 #endif
   159 
   160 #ifdef __SDL_NOGETPROCADDR__
   161 #define SDL_PROC(ret,func,params) data->func=func;
   162 #define SDL_PROC_OES(ret,func,params) data->func=func;
   163 #else
   164 #define SDL_PROC(ret,func,params) \
   165     do { \
   166         data->func = SDL_GL_GetProcAddress(#func); \
   167         if ( ! data->func ) { \
   168             return SDL_SetError("Couldn't load GLES function %s: %s", #func, SDL_GetError()); \
   169         } \
   170     } while ( 0 );
   171 #define SDL_PROC_OES(ret,func,params) \
   172     do { \
   173         data->func = SDL_GL_GetProcAddress(#func); \
   174     } while ( 0 );
   175 #endif /* __SDL_NOGETPROCADDR__ */
   176 
   177 #include "SDL_glesfuncs.h"
   178 #undef SDL_PROC
   179 #undef SDL_PROC_OES
   180     return 0;
   181 }
   182 
   183 static GLES_FBOList *
   184 GLES_GetFBO(GLES_RenderData *data, Uint32 w, Uint32 h)
   185 {
   186    GLES_FBOList *result = data->framebuffers;
   187    while ((result) && ((result->w != w) || (result->h != h)) ) {
   188        result = result->next;
   189    }
   190    if (result == NULL) {
   191        result = SDL_malloc(sizeof(GLES_FBOList));
   192        result->w = w;
   193        result->h = h;
   194        data->glGenFramebuffersOES(1, &result->FBO);
   195        result->next = data->framebuffers;
   196        data->framebuffers = result;
   197    }
   198    return result;
   199 }
   200 
   201 
   202 static int
   203 GLES_ActivateRenderer(SDL_Renderer * renderer)
   204 {
   205     GLES_RenderData *data = (GLES_RenderData *) renderer->driverdata;
   206 
   207     if (SDL_GL_GetCurrentContext() != data->context) {
   208         if (SDL_GL_MakeCurrent(renderer->window, data->context) < 0) {
   209             return -1;
   210         }
   211     }
   212 
   213     return 0;
   214 }
   215 
   216 static void
   217 GLES_WindowEvent(SDL_Renderer * renderer, const SDL_WindowEvent *event)
   218 {
   219     GLES_RenderData *data = (GLES_RenderData *) renderer->driverdata;
   220 
   221     if (event->event == SDL_WINDOWEVENT_MINIMIZED) {
   222         /* According to Apple documentation, we need to finish drawing NOW! */
   223         data->glFinish();
   224     }
   225 }
   226 
   227 static int
   228 GLES_GetOutputSize(SDL_Renderer * renderer, int *w, int *h)
   229 {
   230     SDL_GL_GetDrawableSize(renderer->window, w, h);
   231     return 0;
   232 }
   233 
   234 static GLenum GetBlendFunc(SDL_BlendFactor factor)
   235 {
   236     switch (factor) {
   237     case SDL_BLENDFACTOR_ZERO:
   238         return GL_ZERO;
   239     case SDL_BLENDFACTOR_ONE:
   240         return GL_ONE;
   241     case SDL_BLENDFACTOR_SRC_COLOR:
   242         return GL_SRC_COLOR;
   243     case SDL_BLENDFACTOR_ONE_MINUS_SRC_COLOR:
   244         return GL_ONE_MINUS_SRC_COLOR;
   245     case SDL_BLENDFACTOR_SRC_ALPHA:
   246         return GL_SRC_ALPHA;
   247     case SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA:
   248         return GL_ONE_MINUS_SRC_ALPHA;
   249     case SDL_BLENDFACTOR_DST_COLOR:
   250         return GL_DST_COLOR;
   251     case SDL_BLENDFACTOR_ONE_MINUS_DST_COLOR:
   252         return GL_ONE_MINUS_DST_COLOR;
   253     case SDL_BLENDFACTOR_DST_ALPHA:
   254         return GL_DST_ALPHA;
   255     case SDL_BLENDFACTOR_ONE_MINUS_DST_ALPHA:
   256         return GL_ONE_MINUS_DST_ALPHA;
   257     default:
   258         return GL_INVALID_ENUM;
   259     }
   260 }
   261 
   262 static GLenum GetBlendEquation(SDL_BlendOperation operation)
   263 {
   264     switch (operation) {
   265     case SDL_BLENDOPERATION_ADD:
   266         return GL_FUNC_ADD_OES;
   267     case SDL_BLENDOPERATION_SUBTRACT:
   268         return GL_FUNC_SUBTRACT_OES;
   269     case SDL_BLENDOPERATION_REV_SUBTRACT:
   270         return GL_FUNC_REVERSE_SUBTRACT_OES;
   271     default:
   272         return GL_INVALID_ENUM;
   273     }
   274 }
   275 
   276 static SDL_bool
   277 GLES_SupportsBlendMode(SDL_Renderer * renderer, SDL_BlendMode blendMode)
   278 {
   279     GLES_RenderData *data = (GLES_RenderData *) renderer->driverdata;
   280     SDL_BlendFactor srcColorFactor = SDL_GetBlendModeSrcColorFactor(blendMode);
   281     SDL_BlendFactor srcAlphaFactor = SDL_GetBlendModeSrcAlphaFactor(blendMode);
   282     SDL_BlendOperation colorOperation = SDL_GetBlendModeColorOperation(blendMode);
   283     SDL_BlendFactor dstColorFactor = SDL_GetBlendModeDstColorFactor(blendMode);
   284     SDL_BlendFactor dstAlphaFactor = SDL_GetBlendModeDstAlphaFactor(blendMode);
   285     SDL_BlendOperation alphaOperation = SDL_GetBlendModeAlphaOperation(blendMode);
   286 
   287     if (GetBlendFunc(srcColorFactor) == GL_INVALID_ENUM ||
   288         GetBlendFunc(srcAlphaFactor) == GL_INVALID_ENUM ||
   289         GetBlendEquation(colorOperation) == GL_INVALID_ENUM ||
   290         GetBlendFunc(dstColorFactor) == GL_INVALID_ENUM ||
   291         GetBlendFunc(dstAlphaFactor) == GL_INVALID_ENUM ||
   292         GetBlendEquation(alphaOperation) == GL_INVALID_ENUM) {
   293         return SDL_FALSE;
   294     }
   295     if ((srcColorFactor != srcAlphaFactor || dstColorFactor != dstAlphaFactor) && !data->GL_OES_blend_func_separate_supported) {
   296         return SDL_FALSE;
   297     }
   298     if (colorOperation != alphaOperation && !data->GL_OES_blend_equation_separate_supported) {
   299         return SDL_FALSE;
   300     }
   301     if (colorOperation != SDL_BLENDOPERATION_ADD && !data->GL_OES_blend_subtract_supported) {
   302         return SDL_FALSE;
   303     }
   304     return SDL_TRUE;
   305 }
   306 
   307 static SDL_INLINE int
   308 power_of_2(int input)
   309 {
   310     int value = 1;
   311 
   312     while (value < input) {
   313         value <<= 1;
   314     }
   315     return value;
   316 }
   317 
   318 static int
   319 GLES_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture)
   320 {
   321     GLES_RenderData *renderdata = (GLES_RenderData *) renderer->driverdata;
   322     GLES_TextureData *data;
   323     GLint internalFormat;
   324     GLenum format, type;
   325     int texture_w, texture_h;
   326     GLenum scaleMode;
   327     GLenum result;
   328 
   329     GLES_ActivateRenderer(renderer);
   330 
   331     switch (texture->format) {
   332     case SDL_PIXELFORMAT_ABGR8888:
   333         internalFormat = GL_RGBA;
   334         format = GL_RGBA;
   335         type = GL_UNSIGNED_BYTE;
   336         break;
   337     default:
   338         return SDL_SetError("Texture format not supported");
   339     }
   340 
   341     data = (GLES_TextureData *) SDL_calloc(1, sizeof(*data));
   342     if (!data) {
   343         return SDL_OutOfMemory();
   344     }
   345 
   346     if (texture->access == SDL_TEXTUREACCESS_STREAMING) {
   347         data->pitch = texture->w * SDL_BYTESPERPIXEL(texture->format);
   348         data->pixels = SDL_calloc(1, texture->h * data->pitch);
   349         if (!data->pixels) {
   350             SDL_free(data);
   351             return SDL_OutOfMemory();
   352         }
   353     }
   354 
   355 
   356     if (texture->access == SDL_TEXTUREACCESS_TARGET) {
   357         if (!renderdata->GL_OES_framebuffer_object_supported) {
   358             SDL_free(data);
   359             return SDL_SetError("GL_OES_framebuffer_object not supported");
   360         }
   361         data->fbo = GLES_GetFBO(renderer->driverdata, texture->w, texture->h);
   362     } else {
   363         data->fbo = NULL;
   364     }
   365 
   366 
   367     renderdata->glGetError();
   368     renderdata->glEnable(GL_TEXTURE_2D);
   369     renderdata->glGenTextures(1, &data->texture);
   370     result = renderdata->glGetError();
   371     if (result != GL_NO_ERROR) {
   372         SDL_free(data);
   373         return GLES_SetError("glGenTextures()", result);
   374     }
   375 
   376     data->type = GL_TEXTURE_2D;
   377     /* no NPOV textures allowed in OpenGL ES (yet) */
   378     texture_w = power_of_2(texture->w);
   379     texture_h = power_of_2(texture->h);
   380     data->texw = (GLfloat) texture->w / texture_w;
   381     data->texh = (GLfloat) texture->h / texture_h;
   382 
   383     data->format = format;
   384     data->formattype = type;
   385     scaleMode = (texture->scaleMode == SDL_ScaleModeNearest) ? GL_NEAREST : GL_LINEAR;
   386     renderdata->glBindTexture(data->type, data->texture);
   387     renderdata->glTexParameteri(data->type, GL_TEXTURE_MIN_FILTER, scaleMode);
   388     renderdata->glTexParameteri(data->type, GL_TEXTURE_MAG_FILTER, scaleMode);
   389     renderdata->glTexParameteri(data->type, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
   390     renderdata->glTexParameteri(data->type, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
   391 
   392     renderdata->glTexImage2D(data->type, 0, internalFormat, texture_w,
   393                              texture_h, 0, format, type, NULL);
   394     renderdata->glDisable(GL_TEXTURE_2D);
   395     renderdata->drawstate.texture = texture;
   396     renderdata->drawstate.texturing = SDL_FALSE;
   397 
   398     result = renderdata->glGetError();
   399     if (result != GL_NO_ERROR) {
   400         SDL_free(data);
   401         return GLES_SetError("glTexImage2D()", result);
   402     }
   403 
   404     texture->driverdata = data;
   405     return 0;
   406 }
   407 
   408 static int
   409 GLES_UpdateTexture(SDL_Renderer * renderer, SDL_Texture * texture,
   410                    const SDL_Rect * rect, const void *pixels, int pitch)
   411 {
   412     GLES_RenderData *renderdata = (GLES_RenderData *) renderer->driverdata;
   413     GLES_TextureData *data = (GLES_TextureData *) texture->driverdata;
   414     Uint8 *blob = NULL;
   415     Uint8 *src;
   416     int srcPitch;
   417     int y;
   418 
   419     GLES_ActivateRenderer(renderer);
   420 
   421     /* Bail out if we're supposed to update an empty rectangle */
   422     if (rect->w <= 0 || rect->h <= 0) {
   423         return 0;
   424     }
   425 
   426     /* Reformat the texture data into a tightly packed array */
   427     srcPitch = rect->w * SDL_BYTESPERPIXEL(texture->format);
   428     src = (Uint8 *)pixels;
   429     if (pitch != srcPitch) {
   430         blob = (Uint8 *)SDL_malloc(srcPitch * rect->h);
   431         if (!blob) {
   432             return SDL_OutOfMemory();
   433         }
   434         src = blob;
   435         for (y = 0; y < rect->h; ++y) {
   436             SDL_memcpy(src, pixels, srcPitch);
   437             src += srcPitch;
   438             pixels = (Uint8 *)pixels + pitch;
   439         }
   440         src = blob;
   441     }
   442 
   443     /* Create a texture subimage with the supplied data */
   444     renderdata->glGetError();
   445     renderdata->glEnable(data->type);
   446     renderdata->glBindTexture(data->type, data->texture);
   447     renderdata->glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
   448     renderdata->glTexSubImage2D(data->type,
   449                     0,
   450                     rect->x,
   451                     rect->y,
   452                     rect->w,
   453                     rect->h,
   454                     data->format,
   455                     data->formattype,
   456                     src);
   457     renderdata->glDisable(data->type);
   458     SDL_free(blob);
   459 
   460     renderdata->drawstate.texture = texture;
   461     renderdata->drawstate.texturing = SDL_FALSE;
   462 
   463     if (renderdata->glGetError() != GL_NO_ERROR) {
   464         return SDL_SetError("Failed to update texture");
   465     }
   466     return 0;
   467 }
   468 
   469 static int
   470 GLES_LockTexture(SDL_Renderer * renderer, SDL_Texture * texture,
   471                  const SDL_Rect * rect, void **pixels, int *pitch)
   472 {
   473     GLES_TextureData *data = (GLES_TextureData *) texture->driverdata;
   474 
   475     *pixels =
   476         (void *) ((Uint8 *) data->pixels + rect->y * data->pitch +
   477                   rect->x * SDL_BYTESPERPIXEL(texture->format));
   478     *pitch = data->pitch;
   479     return 0;
   480 }
   481 
   482 static void
   483 GLES_UnlockTexture(SDL_Renderer * renderer, SDL_Texture * texture)
   484 {
   485     GLES_TextureData *data = (GLES_TextureData *) texture->driverdata;
   486     SDL_Rect rect;
   487 
   488     /* We do whole texture updates, at least for now */
   489     rect.x = 0;
   490     rect.y = 0;
   491     rect.w = texture->w;
   492     rect.h = texture->h;
   493     GLES_UpdateTexture(renderer, texture, &rect, data->pixels, data->pitch);
   494 }
   495 
   496 static void
   497 GLES_SetTextureScaleMode(SDL_Renderer * renderer, SDL_Texture * texture, SDL_ScaleMode scaleMode)
   498 {
   499     GLES_RenderData *renderdata = (GLES_RenderData *) renderer->driverdata;
   500     GLES_TextureData *data = (GLES_TextureData *) texture->driverdata;
   501     GLenum glScaleMode = (scaleMode == SDL_ScaleModeNearest) ? GL_NEAREST : GL_LINEAR;
   502 
   503     renderdata->glBindTexture(data->type, data->texture);
   504     renderdata->glTexParameteri(data->type, GL_TEXTURE_MIN_FILTER, glScaleMode);
   505     renderdata->glTexParameteri(data->type, GL_TEXTURE_MAG_FILTER, glScaleMode);
   506 }
   507 
   508 static int
   509 GLES_SetRenderTarget(SDL_Renderer * renderer, SDL_Texture * texture)
   510 {
   511     GLES_RenderData *data = (GLES_RenderData *) renderer->driverdata;
   512     GLES_TextureData *texturedata = NULL;
   513     GLenum status;
   514 
   515     if (!data->GL_OES_framebuffer_object_supported) {
   516         return SDL_SetError("Can't enable render target support in this renderer");
   517     }
   518 
   519     data->drawstate.viewport_dirty = SDL_TRUE;
   520 
   521     if (texture == NULL) {
   522         data->glBindFramebufferOES(GL_FRAMEBUFFER_OES, data->window_framebuffer);
   523         return 0;
   524     }
   525 
   526     texturedata = (GLES_TextureData *) texture->driverdata;
   527     data->glBindFramebufferOES(GL_FRAMEBUFFER_OES, texturedata->fbo->FBO);
   528     /* TODO: check if texture pixel format allows this operation */
   529     data->glFramebufferTexture2DOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, texturedata->type, texturedata->texture, 0);
   530     /* Check FBO status */
   531     status = data->glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES);
   532     if (status != GL_FRAMEBUFFER_COMPLETE_OES) {
   533         return SDL_SetError("glFramebufferTexture2DOES() failed");
   534     }
   535     return 0;
   536 }
   537 
   538 
   539 static int
   540 GLES_QueueSetViewport(SDL_Renderer * renderer, SDL_RenderCommand *cmd)
   541 {
   542     return 0;  /* nothing to do in this backend. */
   543 }
   544 
   545 static int
   546 GLES_QueueDrawPoints(SDL_Renderer * renderer, SDL_RenderCommand *cmd, const SDL_FPoint * points, int count)
   547 {
   548     GLfloat *verts = (GLfloat *) SDL_AllocateRenderVertices(renderer, count * 2 * sizeof (GLfloat), 0, &cmd->data.draw.first);
   549     int i;
   550 
   551     if (!verts) {
   552         return -1;
   553     }
   554 
   555     cmd->data.draw.count = count;
   556     for (i = 0; i < count; i++) {
   557         *(verts++) = 0.5f + points[i].x;
   558         *(verts++) = 0.5f + points[i].y;
   559     }
   560 
   561     return 0;
   562 }
   563 
   564 static int
   565 GLES_QueueFillRects(SDL_Renderer * renderer, SDL_RenderCommand *cmd, const SDL_FRect * rects, int count)
   566 {
   567     GLfloat *verts = (GLfloat *) SDL_AllocateRenderVertices(renderer, count * 8 * sizeof (GLfloat), 0, &cmd->data.draw.first);
   568     int i;
   569 
   570     if (!verts) {
   571         return -1;
   572     }
   573 
   574     cmd->data.draw.count = count;
   575 
   576     for (i = 0; i < count; i++) {
   577         const SDL_FRect *rect = &rects[i];
   578         const GLfloat minx = rect->x;
   579         const GLfloat maxx = rect->x + rect->w;
   580         const GLfloat miny = rect->y;
   581         const GLfloat maxy = rect->y + rect->h;
   582         *(verts++) = minx;
   583         *(verts++) = miny;
   584         *(verts++) = maxx;
   585         *(verts++) = miny;
   586         *(verts++) = minx;
   587         *(verts++) = maxy;
   588         *(verts++) = maxx;
   589         *(verts++) = maxy;
   590     }
   591 
   592     return 0;
   593 }
   594 
   595 static int
   596 GLES_QueueCopy(SDL_Renderer * renderer, SDL_RenderCommand *cmd, SDL_Texture * texture,
   597                           const SDL_Rect * srcrect, const SDL_FRect * dstrect)
   598 {
   599     GLES_TextureData *texturedata = (GLES_TextureData *) texture->driverdata;
   600     GLfloat minx, miny, maxx, maxy;
   601     GLfloat minu, maxu, minv, maxv;
   602     GLfloat *verts = (GLfloat *) SDL_AllocateRenderVertices(renderer, 16 * sizeof (GLfloat), 0, &cmd->data.draw.first);
   603 
   604     if (!verts) {
   605         return -1;
   606     }
   607 
   608     cmd->data.draw.count = 1;
   609 
   610     minx = dstrect->x;
   611     miny = dstrect->y;
   612     maxx = dstrect->x + dstrect->w;
   613     maxy = dstrect->y + dstrect->h;
   614 
   615     minu = (GLfloat) srcrect->x / texture->w;
   616     minu *= texturedata->texw;
   617     maxu = (GLfloat) (srcrect->x + srcrect->w) / texture->w;
   618     maxu *= texturedata->texw;
   619     minv = (GLfloat) srcrect->y / texture->h;
   620     minv *= texturedata->texh;
   621     maxv = (GLfloat) (srcrect->y + srcrect->h) / texture->h;
   622     maxv *= texturedata->texh;
   623 
   624     *(verts++) = minx;
   625     *(verts++) = miny;
   626     *(verts++) = maxx;
   627     *(verts++) = miny;
   628     *(verts++) = minx;
   629     *(verts++) = maxy;
   630     *(verts++) = maxx;
   631     *(verts++) = maxy;
   632 
   633     *(verts++) = minu;
   634     *(verts++) = minv;
   635     *(verts++) = maxu;
   636     *(verts++) = minv;
   637     *(verts++) = minu;
   638     *(verts++) = maxv;
   639     *(verts++) = maxu;
   640     *(verts++) = maxv;
   641 
   642     return 0;
   643 }
   644 
   645 static int
   646 GLES_QueueCopyEx(SDL_Renderer * renderer, SDL_RenderCommand *cmd, SDL_Texture * texture,
   647                         const SDL_Rect * srcquad, const SDL_FRect * dstrect,
   648                         const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip)
   649 {
   650     GLES_TextureData *texturedata = (GLES_TextureData *) texture->driverdata;
   651     GLfloat minx, miny, maxx, maxy;
   652     GLfloat centerx, centery;
   653     GLfloat minu, maxu, minv, maxv;
   654     GLfloat *verts = (GLfloat *) SDL_AllocateRenderVertices(renderer, 19 * sizeof (GLfloat), 0, &cmd->data.draw.first);
   655 
   656     if (!verts) {
   657         return -1;
   658     }
   659 
   660     centerx = center->x;
   661     centery = center->y;
   662 
   663     if (flip & SDL_FLIP_HORIZONTAL) {
   664         minx =  dstrect->w - centerx;
   665         maxx = -centerx;
   666     }
   667     else {
   668         minx = -centerx;
   669         maxx =  dstrect->w - centerx;
   670     }
   671 
   672     if (flip & SDL_FLIP_VERTICAL) {
   673         miny =  dstrect->h - centery;
   674         maxy = -centery;
   675     }
   676     else {
   677         miny = -centery;
   678         maxy =  dstrect->h - centery;
   679     }
   680 
   681     minu = (GLfloat) srcquad->x / texture->w;
   682     minu *= texturedata->texw;
   683     maxu = (GLfloat) (srcquad->x + srcquad->w) / texture->w;
   684     maxu *= texturedata->texw;
   685     minv = (GLfloat) srcquad->y / texture->h;
   686     minv *= texturedata->texh;
   687     maxv = (GLfloat) (srcquad->y + srcquad->h) / texture->h;
   688     maxv *= texturedata->texh;
   689 
   690     cmd->data.draw.count = 1;
   691 
   692     *(verts++) = minx;
   693     *(verts++) = miny;
   694     *(verts++) = maxx;
   695     *(verts++) = miny;
   696     *(verts++) = minx;
   697     *(verts++) = maxy;
   698     *(verts++) = maxx;
   699     *(verts++) = maxy;
   700 
   701     *(verts++) = minu;
   702     *(verts++) = minv;
   703     *(verts++) = maxu;
   704     *(verts++) = minv;
   705     *(verts++) = minu;
   706     *(verts++) = maxv;
   707     *(verts++) = maxu;
   708     *(verts++) = maxv;
   709 
   710     *(verts++) = (GLfloat) dstrect->x + centerx;
   711     *(verts++) = (GLfloat) dstrect->y + centery;
   712     *(verts++) = (GLfloat) angle;
   713 
   714     return 0;
   715 }
   716 
   717 static void
   718 SetDrawState(GLES_RenderData *data, const SDL_RenderCommand *cmd)
   719 {
   720     const SDL_BlendMode blend = cmd->data.draw.blend;
   721     const Uint8 r = cmd->data.draw.r;
   722     const Uint8 g = cmd->data.draw.g;
   723     const Uint8 b = cmd->data.draw.b;
   724     const Uint8 a = cmd->data.draw.a;
   725     const Uint32 color = ((a << 24) | (r << 16) | (g << 8) | b);
   726 
   727     if (color != data->drawstate.color) {
   728         const GLfloat fr = ((GLfloat) r) * inv255f;
   729         const GLfloat fg = ((GLfloat) g) * inv255f;
   730         const GLfloat fb = ((GLfloat) b) * inv255f;
   731         const GLfloat fa = ((GLfloat) a) * inv255f;
   732         data->glColor4f(fr, fg, fb, fa);
   733         data->drawstate.color = color;
   734     }
   735 
   736     if (data->drawstate.viewport_dirty) {
   737         const SDL_Rect *viewport = &data->drawstate.viewport;
   738         const SDL_bool istarget = (data->drawstate.target != NULL);
   739         data->glMatrixMode(GL_PROJECTION);
   740         data->glLoadIdentity();
   741         data->glViewport(viewport->x,
   742                          istarget ? viewport->y : (data->drawstate.drawableh - viewport->y - viewport->h),
   743                          viewport->w, viewport->h);
   744         if (viewport->w && viewport->h) {
   745             data->glOrthof((GLfloat) 0, (GLfloat) viewport->w,
   746                            (GLfloat) (istarget ? 0 : viewport->h),
   747                            (GLfloat) (istarget ? viewport->h : 0),
   748                            0.0, 1.0);
   749         }
   750         data->glMatrixMode(GL_MODELVIEW);
   751         data->drawstate.viewport_dirty = SDL_FALSE;
   752     }
   753 
   754     if (data->drawstate.cliprect_enabled_dirty) {
   755         if (data->drawstate.cliprect_enabled) {
   756             data->glEnable(GL_SCISSOR_TEST);
   757         } else {
   758             data->glDisable(GL_SCISSOR_TEST);
   759         }
   760         data->drawstate.cliprect_enabled_dirty = SDL_FALSE;
   761     }
   762 
   763     if (data->drawstate.cliprect_enabled && data->drawstate.cliprect_dirty) {
   764         const SDL_Rect *viewport = &data->drawstate.viewport;
   765         const SDL_Rect *rect = &data->drawstate.cliprect;
   766         const SDL_bool istarget = (data->drawstate.target != NULL);
   767         data->glScissor(viewport->x + rect->x,
   768                         istarget ? viewport->y + rect->y : data->drawstate.drawableh - viewport->y - rect->y - rect->h,
   769                         rect->w, rect->h);
   770         data->drawstate.cliprect_dirty = SDL_FALSE;
   771     }
   772 
   773     if (blend != data->drawstate.blend) {
   774         if (blend == SDL_BLENDMODE_NONE) {
   775             data->glDisable(GL_BLEND);
   776         } else {
   777             data->glEnable(GL_BLEND);
   778             if (data->GL_OES_blend_func_separate_supported) {
   779                 data->glBlendFuncSeparateOES(GetBlendFunc(SDL_GetBlendModeSrcColorFactor(blend)),
   780                                              GetBlendFunc(SDL_GetBlendModeDstColorFactor(blend)),
   781                                              GetBlendFunc(SDL_GetBlendModeSrcAlphaFactor(blend)),
   782                                              GetBlendFunc(SDL_GetBlendModeDstAlphaFactor(blend)));
   783             } else {
   784                 data->glBlendFunc(GetBlendFunc(SDL_GetBlendModeSrcColorFactor(blend)),
   785                                   GetBlendFunc(SDL_GetBlendModeDstColorFactor(blend)));
   786             }
   787             if (data->GL_OES_blend_equation_separate_supported) {
   788                 data->glBlendEquationSeparateOES(GetBlendEquation(SDL_GetBlendModeColorOperation(blend)),
   789                                                  GetBlendEquation(SDL_GetBlendModeAlphaOperation(blend)));
   790             } else if (data->GL_OES_blend_subtract_supported) {
   791                 data->glBlendEquationOES(GetBlendEquation(SDL_GetBlendModeColorOperation(blend)));
   792             }
   793         }
   794         data->drawstate.blend = blend;
   795     }
   796 
   797     if ((cmd->data.draw.texture != NULL) != data->drawstate.texturing) {
   798         if (cmd->data.draw.texture == NULL) {
   799             data->glDisable(GL_TEXTURE_2D);
   800             data->glDisableClientState(GL_TEXTURE_COORD_ARRAY);
   801             data->drawstate.texturing = SDL_FALSE;
   802         } else {
   803             data->glEnable(GL_TEXTURE_2D);
   804             data->glEnableClientState(GL_TEXTURE_COORD_ARRAY);
   805             data->drawstate.texturing = SDL_TRUE;
   806         }
   807     }
   808 }
   809 
   810 static void
   811 SetCopyState(GLES_RenderData *data, const SDL_RenderCommand *cmd)
   812 {
   813     SDL_Texture *texture = cmd->data.draw.texture;
   814     SetDrawState(data, cmd);
   815 
   816     if (texture != data->drawstate.texture) {
   817         GLES_TextureData *texturedata = (GLES_TextureData *) texture->driverdata;
   818         data->glBindTexture(GL_TEXTURE_2D, texturedata->texture);
   819         data->drawstate.texture = texture;
   820     }
   821 }
   822 
   823 static int
   824 GLES_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *vertices, size_t vertsize)
   825 {
   826     GLES_RenderData *data = (GLES_RenderData *) renderer->driverdata;
   827     size_t i;
   828 
   829     if (GLES_ActivateRenderer(renderer) < 0) {
   830         return -1;
   831     }
   832 
   833     data->drawstate.target = renderer->target;
   834 
   835     if (!renderer->target) {
   836         SDL_GL_GetDrawableSize(renderer->window, &data->drawstate.drawablew, &data->drawstate.drawableh);
   837     }
   838 
   839     while (cmd) {
   840         switch (cmd->command) {
   841             case SDL_RENDERCMD_SETDRAWCOLOR: {
   842                 break;  /* not used in this render backend. */
   843             }
   844 
   845             case SDL_RENDERCMD_SETVIEWPORT: {
   846                 SDL_Rect *viewport = &data->drawstate.viewport;
   847                 if (SDL_memcmp(viewport, &cmd->data.viewport.rect, sizeof (SDL_Rect)) != 0) {
   848                     SDL_memcpy(viewport, &cmd->data.viewport.rect, sizeof (SDL_Rect));
   849                     data->drawstate.viewport_dirty = SDL_TRUE;
   850                 }
   851                 break;
   852             }
   853 
   854             case SDL_RENDERCMD_SETCLIPRECT: {
   855                 const SDL_Rect *rect = &cmd->data.cliprect.rect;
   856                 if (data->drawstate.cliprect_enabled != cmd->data.cliprect.enabled) {
   857                     data->drawstate.cliprect_enabled = cmd->data.cliprect.enabled;
   858                     data->drawstate.cliprect_enabled_dirty = SDL_TRUE;
   859                 }
   860                 if (SDL_memcmp(&data->drawstate.cliprect, rect, sizeof (SDL_Rect)) != 0) {
   861                     SDL_memcpy(&data->drawstate.cliprect, rect, sizeof (SDL_Rect));
   862                     data->drawstate.cliprect_dirty = SDL_TRUE;
   863                 }
   864                 break;
   865             }
   866 
   867             case SDL_RENDERCMD_CLEAR: {
   868                 const Uint8 r = cmd->data.color.r;
   869                 const Uint8 g = cmd->data.color.g;
   870                 const Uint8 b = cmd->data.color.b;
   871                 const Uint8 a = cmd->data.color.a;
   872                 const Uint32 color = ((a << 24) | (r << 16) | (g << 8) | b);
   873                 if (color != data->drawstate.clear_color) {
   874                     const GLfloat fr = ((GLfloat) r) * inv255f;
   875                     const GLfloat fg = ((GLfloat) g) * inv255f;
   876                     const GLfloat fb = ((GLfloat) b) * inv255f;
   877                     const GLfloat fa = ((GLfloat) a) * inv255f;
   878                     data->glClearColor(fr, fg, fb, fa);
   879                     data->drawstate.clear_color = color;
   880                 }
   881 
   882                 if (data->drawstate.cliprect_enabled || data->drawstate.cliprect_enabled_dirty) {
   883                     data->glDisable(GL_SCISSOR_TEST);
   884                     data->drawstate.cliprect_enabled_dirty = data->drawstate.cliprect_enabled;
   885                 }
   886 
   887                 data->glClear(GL_COLOR_BUFFER_BIT);
   888 
   889                 break;
   890             }
   891 
   892             case SDL_RENDERCMD_DRAW_POINTS: {
   893                 const size_t count = cmd->data.draw.count;
   894                 const GLfloat *verts = (GLfloat *) (((Uint8 *) vertices) + cmd->data.draw.first);
   895                 SetDrawState(data, cmd);
   896                 data->glVertexPointer(2, GL_FLOAT, 0, verts);
   897                 data->glDrawArrays(GL_POINTS, 0, (GLsizei) count);
   898                 break;
   899             }
   900 
   901             case SDL_RENDERCMD_DRAW_LINES: {
   902                 const GLfloat *verts = (GLfloat *) (((Uint8 *) vertices) + cmd->data.draw.first);
   903                 const size_t count = cmd->data.draw.count;
   904                 SetDrawState(data, cmd);
   905                 data->glVertexPointer(2, GL_FLOAT, 0, verts);
   906                 if (count > 2 && (verts[0] == verts[(count-1)*2]) && (verts[1] == verts[(count*2)-1])) {
   907                     /* GL_LINE_LOOP takes care of the final segment */
   908                     data->glDrawArrays(GL_LINE_LOOP, 0, (GLsizei) (count - 1));
   909                 } else {
   910                     data->glDrawArrays(GL_LINE_STRIP, 0, (GLsizei) count);
   911                     /* We need to close the endpoint of the line */
   912                     data->glDrawArrays(GL_POINTS, (GLsizei) (count - 1), 1);
   913                 }
   914                 break;
   915             }
   916 
   917             case SDL_RENDERCMD_FILL_RECTS: {
   918                 const size_t count = cmd->data.draw.count;
   919                 const GLfloat *verts = (GLfloat *) (((Uint8 *) vertices) + cmd->data.draw.first);
   920                 GLsizei offset = 0;
   921                 SetDrawState(data, cmd);
   922                 data->glVertexPointer(2, GL_FLOAT, 0, verts);
   923                 for (i = 0; i < count; ++i, offset += 4) {
   924                     data->glDrawArrays(GL_TRIANGLE_STRIP, offset, 4);
   925                 }
   926                 break;
   927             }
   928 
   929             case SDL_RENDERCMD_COPY: {
   930                 const GLfloat *verts = (GLfloat *) (((Uint8 *) vertices) + cmd->data.draw.first);
   931                 SetCopyState(data, cmd);
   932                 data->glVertexPointer(2, GL_FLOAT, 0, verts);
   933                 data->glTexCoordPointer(2, GL_FLOAT, 0, verts + 8);
   934                 data->glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
   935                 break;
   936             }
   937 
   938             case SDL_RENDERCMD_COPY_EX: {
   939                 const GLfloat *verts = (GLfloat *) (((Uint8 *) vertices) + cmd->data.draw.first);
   940                 const GLfloat translatex = verts[16];
   941                 const GLfloat translatey = verts[17];
   942                 const GLfloat angle = verts[18];
   943                 SetCopyState(data, cmd);
   944                 data->glVertexPointer(2, GL_FLOAT, 0, verts);
   945                 data->glTexCoordPointer(2, GL_FLOAT, 0, verts + 8);
   946 
   947                 /* Translate to flip, rotate, translate to position */
   948                 data->glPushMatrix();
   949                 data->glTranslatef(translatex, translatey, 0.0f);
   950                 data->glRotatef(angle, 0.0, 0.0, 1.0);
   951                 data->glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
   952                 data->glPopMatrix();
   953                 break;
   954             }
   955 
   956             case SDL_RENDERCMD_NO_OP:
   957                 break;
   958         }
   959 
   960         cmd = cmd->next;
   961     }
   962 
   963     return 0;
   964 }
   965 
   966 static int
   967 GLES_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect,
   968                       Uint32 pixel_format, void * pixels, int pitch)
   969 {
   970     GLES_RenderData *data = (GLES_RenderData *) renderer->driverdata;
   971     Uint32 temp_format = renderer->target ? renderer->target->format : SDL_PIXELFORMAT_ABGR8888;
   972     void *temp_pixels;
   973     int temp_pitch;
   974     Uint8 *src, *dst, *tmp;
   975     int w, h, length, rows;
   976     int status;
   977 
   978     GLES_ActivateRenderer(renderer);
   979 
   980     temp_pitch = rect->w * SDL_BYTESPERPIXEL(temp_format);
   981     temp_pixels = SDL_malloc(rect->h * temp_pitch);
   982     if (!temp_pixels) {
   983         return SDL_OutOfMemory();
   984     }
   985 
   986     SDL_GetRendererOutputSize(renderer, &w, &h);
   987 
   988     data->glPixelStorei(GL_PACK_ALIGNMENT, 1);
   989 
   990     data->glReadPixels(rect->x, renderer->target ? rect->y : (h-rect->y)-rect->h,
   991                        rect->w, rect->h, GL_RGBA, GL_UNSIGNED_BYTE, temp_pixels);
   992 
   993     /* Flip the rows to be top-down if necessary */
   994     if (!renderer->target) {
   995         SDL_bool isstack;
   996         length = rect->w * SDL_BYTESPERPIXEL(temp_format);
   997         src = (Uint8*)temp_pixels + (rect->h-1)*temp_pitch;
   998         dst = (Uint8*)temp_pixels;
   999         tmp = SDL_small_alloc(Uint8, length, &isstack);
  1000         rows = rect->h / 2;
  1001         while (rows--) {
  1002             SDL_memcpy(tmp, dst, length);
  1003             SDL_memcpy(dst, src, length);
  1004             SDL_memcpy(src, tmp, length);
  1005             dst += temp_pitch;
  1006             src -= temp_pitch;
  1007         }
  1008         SDL_small_free(tmp, isstack);
  1009     }
  1010 
  1011     status = SDL_ConvertPixels(rect->w, rect->h,
  1012                                temp_format, temp_pixels, temp_pitch,
  1013                                pixel_format, pixels, pitch);
  1014     SDL_free(temp_pixels);
  1015 
  1016     return status;
  1017 }
  1018 
  1019 static void
  1020 GLES_RenderPresent(SDL_Renderer * renderer)
  1021 {
  1022     GLES_ActivateRenderer(renderer);
  1023 
  1024     SDL_GL_SwapWindow(renderer->window);
  1025 }
  1026 
  1027 static void
  1028 GLES_DestroyTexture(SDL_Renderer * renderer, SDL_Texture * texture)
  1029 {
  1030     GLES_RenderData *renderdata = (GLES_RenderData *) renderer->driverdata;
  1031 
  1032     GLES_TextureData *data = (GLES_TextureData *) texture->driverdata;
  1033 
  1034     GLES_ActivateRenderer(renderer);
  1035 
  1036     if (renderdata->drawstate.texture == texture) {
  1037         renderdata->drawstate.texture = NULL;
  1038     }
  1039     if (renderdata->drawstate.target == texture) {
  1040         renderdata->drawstate.target = NULL;
  1041     }
  1042 
  1043     if (!data) {
  1044         return;
  1045     }
  1046     if (data->texture) {
  1047         renderdata->glDeleteTextures(1, &data->texture);
  1048     }
  1049     SDL_free(data->pixels);
  1050     SDL_free(data);
  1051     texture->driverdata = NULL;
  1052 }
  1053 
  1054 static void
  1055 GLES_DestroyRenderer(SDL_Renderer * renderer)
  1056 {
  1057     GLES_RenderData *data = (GLES_RenderData *) renderer->driverdata;
  1058 
  1059     if (data) {
  1060         if (data->context) {
  1061             while (data->framebuffers) {
  1062                GLES_FBOList *nextnode = data->framebuffers->next;
  1063                data->glDeleteFramebuffersOES(1, &data->framebuffers->FBO);
  1064                SDL_free(data->framebuffers);
  1065                data->framebuffers = nextnode;
  1066             }
  1067             SDL_GL_DeleteContext(data->context);
  1068         }
  1069         SDL_free(data);
  1070     }
  1071     SDL_free(renderer);
  1072 }
  1073 
  1074 static int GLES_BindTexture (SDL_Renderer * renderer, SDL_Texture *texture, float *texw, float *texh)
  1075 {
  1076     GLES_RenderData *data = (GLES_RenderData *) renderer->driverdata;
  1077     GLES_TextureData *texturedata = (GLES_TextureData *) texture->driverdata;
  1078     GLES_ActivateRenderer(renderer);
  1079 
  1080     data->glEnable(GL_TEXTURE_2D);
  1081     data->glBindTexture(texturedata->type, texturedata->texture);
  1082 
  1083     data->drawstate.texture = texture;
  1084     data->drawstate.texturing = SDL_TRUE;
  1085 
  1086     if (texw) {
  1087         *texw = (float)texturedata->texw;
  1088     }
  1089     if (texh) {
  1090         *texh = (float)texturedata->texh;
  1091     }
  1092 
  1093     return 0;
  1094 }
  1095 
  1096 static int GLES_UnbindTexture (SDL_Renderer * renderer, SDL_Texture *texture)
  1097 {
  1098     GLES_RenderData *data = (GLES_RenderData *) renderer->driverdata;
  1099     GLES_TextureData *texturedata = (GLES_TextureData *) texture->driverdata;
  1100     GLES_ActivateRenderer(renderer);
  1101     data->glDisable(texturedata->type);
  1102 
  1103     data->drawstate.texture = NULL;
  1104     data->drawstate.texturing = SDL_FALSE;
  1105 
  1106     return 0;
  1107 }
  1108 
  1109 static SDL_Renderer *
  1110 GLES_CreateRenderer(SDL_Window * window, Uint32 flags)
  1111 {
  1112     SDL_Renderer *renderer;
  1113     GLES_RenderData *data;
  1114     GLint value;
  1115     Uint32 window_flags;
  1116     int profile_mask = 0, major = 0, minor = 0;
  1117     SDL_bool changed_window = SDL_FALSE;
  1118 
  1119     SDL_GL_GetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, &profile_mask);
  1120     SDL_GL_GetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, &major);
  1121     SDL_GL_GetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, &minor);
  1122 
  1123     window_flags = SDL_GetWindowFlags(window);
  1124     if (!(window_flags & SDL_WINDOW_OPENGL) ||
  1125         profile_mask != SDL_GL_CONTEXT_PROFILE_ES || major != RENDERER_CONTEXT_MAJOR || minor != RENDERER_CONTEXT_MINOR) {
  1126 
  1127         changed_window = SDL_TRUE;
  1128         SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
  1129         SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, RENDERER_CONTEXT_MAJOR);
  1130         SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, RENDERER_CONTEXT_MINOR);
  1131 
  1132         if (SDL_RecreateWindow(window, window_flags | SDL_WINDOW_OPENGL) < 0) {
  1133             goto error;
  1134         }
  1135     }
  1136 
  1137     renderer = (SDL_Renderer *) SDL_calloc(1, sizeof(*renderer));
  1138     if (!renderer) {
  1139         SDL_OutOfMemory();
  1140         goto error;
  1141     }
  1142 
  1143     data = (GLES_RenderData *) SDL_calloc(1, sizeof(*data));
  1144     if (!data) {
  1145         GLES_DestroyRenderer(renderer);
  1146         SDL_OutOfMemory();
  1147         goto error;
  1148     }
  1149 
  1150     renderer->WindowEvent = GLES_WindowEvent;
  1151     renderer->GetOutputSize = GLES_GetOutputSize;
  1152     renderer->SupportsBlendMode = GLES_SupportsBlendMode;
  1153     renderer->CreateTexture = GLES_CreateTexture;
  1154     renderer->UpdateTexture = GLES_UpdateTexture;
  1155     renderer->LockTexture = GLES_LockTexture;
  1156     renderer->UnlockTexture = GLES_UnlockTexture;
  1157     renderer->SetTextureScaleMode = GLES_SetTextureScaleMode;
  1158     renderer->SetRenderTarget = GLES_SetRenderTarget;
  1159     renderer->QueueSetViewport = GLES_QueueSetViewport;
  1160     renderer->QueueSetDrawColor = GLES_QueueSetViewport;  /* SetViewport and SetDrawColor are (currently) no-ops. */
  1161     renderer->QueueDrawPoints = GLES_QueueDrawPoints;
  1162     renderer->QueueDrawLines = GLES_QueueDrawPoints;  /* lines and points queue vertices the same way. */
  1163     renderer->QueueFillRects = GLES_QueueFillRects;
  1164     renderer->QueueCopy = GLES_QueueCopy;
  1165     renderer->QueueCopyEx = GLES_QueueCopyEx;
  1166     renderer->RunCommandQueue = GLES_RunCommandQueue;
  1167     renderer->RenderReadPixels = GLES_RenderReadPixels;
  1168     renderer->RenderPresent = GLES_RenderPresent;
  1169     renderer->DestroyTexture = GLES_DestroyTexture;
  1170     renderer->DestroyRenderer = GLES_DestroyRenderer;
  1171     renderer->GL_BindTexture = GLES_BindTexture;
  1172     renderer->GL_UnbindTexture = GLES_UnbindTexture;
  1173     renderer->info = GLES_RenderDriver.info;
  1174     renderer->info.flags = SDL_RENDERER_ACCELERATED;
  1175     renderer->driverdata = data;
  1176     renderer->window = window;
  1177 
  1178     data->context = SDL_GL_CreateContext(window);
  1179     if (!data->context) {
  1180         GLES_DestroyRenderer(renderer);
  1181         goto error;
  1182     }
  1183     if (SDL_GL_MakeCurrent(window, data->context) < 0) {
  1184         GLES_DestroyRenderer(renderer);
  1185         goto error;
  1186     }
  1187 
  1188     if (GLES_LoadFunctions(data) < 0) {
  1189         GLES_DestroyRenderer(renderer);
  1190         goto error;
  1191     }
  1192 
  1193     if (flags & SDL_RENDERER_PRESENTVSYNC) {
  1194         SDL_GL_SetSwapInterval(1);
  1195     } else {
  1196         SDL_GL_SetSwapInterval(0);
  1197     }
  1198     if (SDL_GL_GetSwapInterval() > 0) {
  1199         renderer->info.flags |= SDL_RENDERER_PRESENTVSYNC;
  1200     }
  1201 
  1202     value = 0;
  1203     data->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &value);
  1204     renderer->info.max_texture_width = value;
  1205     value = 0;
  1206     data->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &value);
  1207     renderer->info.max_texture_height = value;
  1208 
  1209     /* Android does not report GL_OES_framebuffer_object but the functionality seems to be there anyway */
  1210     if (SDL_GL_ExtensionSupported("GL_OES_framebuffer_object") || data->glGenFramebuffersOES) {
  1211         data->GL_OES_framebuffer_object_supported = SDL_TRUE;
  1212         renderer->info.flags |= SDL_RENDERER_TARGETTEXTURE;
  1213 
  1214         value = 0;
  1215         data->glGetIntegerv(GL_FRAMEBUFFER_BINDING_OES, &value);
  1216         data->window_framebuffer = (GLuint)value;
  1217     }
  1218     data->framebuffers = NULL;
  1219 
  1220     if (SDL_GL_ExtensionSupported("GL_OES_blend_func_separate")) {
  1221         data->GL_OES_blend_func_separate_supported = SDL_TRUE;
  1222     }
  1223     if (SDL_GL_ExtensionSupported("GL_OES_blend_equation_separate")) {
  1224         data->GL_OES_blend_equation_separate_supported = SDL_TRUE;
  1225     }
  1226     if (SDL_GL_ExtensionSupported("GL_OES_blend_subtract")) {
  1227         data->GL_OES_blend_subtract_supported = SDL_TRUE;
  1228     }
  1229 
  1230     /* Set up parameters for rendering */
  1231     data->glDisable(GL_DEPTH_TEST);
  1232     data->glDisable(GL_CULL_FACE);
  1233 
  1234     data->glMatrixMode(GL_MODELVIEW);
  1235     data->glLoadIdentity();
  1236 
  1237     data->glEnableClientState(GL_VERTEX_ARRAY);
  1238     data->glDisableClientState(GL_TEXTURE_COORD_ARRAY);
  1239 
  1240     data->glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
  1241 
  1242     data->drawstate.blend = SDL_BLENDMODE_INVALID;
  1243     data->drawstate.color = 0xFFFFFFFF;
  1244     data->drawstate.clear_color = 0xFFFFFFFF;
  1245 
  1246     return renderer;
  1247 
  1248 error:
  1249     if (changed_window) {
  1250         /* Uh oh, better try to put it back... */
  1251         SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, profile_mask);
  1252         SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, major);
  1253         SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, minor);
  1254         SDL_RecreateWindow(window, window_flags);
  1255     }
  1256     return NULL;
  1257 }
  1258 
  1259 SDL_RenderDriver GLES_RenderDriver = {
  1260     GLES_CreateRenderer,
  1261     {
  1262      "opengles",
  1263      (SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC),
  1264      1,
  1265      {SDL_PIXELFORMAT_ABGR8888},
  1266      0,
  1267      0
  1268     }
  1269 };
  1270 
  1271 #endif /* SDL_VIDEO_RENDER_OGL_ES && !SDL_RENDER_DISABLED */
  1272 
  1273 /* vi: set ts=4 sw=4 expandtab: */