sdl

FORK: Simple Directmedia Layer
git clone https://git.neptards.moe/neptards/sdl.git
Log | Files | Refs

SDL_render_gles.c (42420B)


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