Mac and Linux SDL2 binary snapshots
Edward Rudd
2020-05-02 03f8528315fa46c95991a34f3325d7b33ae5538c
source/src/render/opengles/SDL_render_gles.c
@@ -1,6 +1,6 @@
/*
  Simple DirectMedia Layer
  Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
  Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
  This software is provided 'as-is', without any express or implied
  warranty.  In no event will the authors be held liable for any damages
@@ -22,12 +22,13 @@
#if SDL_VIDEO_RENDER_OGL_ES && !SDL_RENDER_DISABLED
#include "SDL_assert.h"
#include "SDL_hints.h"
#include "SDL_opengles.h"
#include "../SDL_sysrender.h"
/* To prevent unnecessary window recreation,
 * these should match the defaults selected in SDL_GL_ResetAttributes
/* To prevent unnecessary window recreation,
 * these should match the defaults selected in SDL_GL_ResetAttributes
 */
#define RENDERER_CONTEXT_MAJOR 1
@@ -52,45 +53,6 @@
static const float inv255f = 1.0f / 255.0f;
static SDL_Renderer *GLES_CreateRenderer(SDL_Window * window, Uint32 flags);
static void GLES_WindowEvent(SDL_Renderer * renderer,
                             const SDL_WindowEvent *event);
static int GLES_GetOutputSize(SDL_Renderer * renderer, int *w, int *h);
static SDL_bool GLES_SupportsBlendMode(SDL_Renderer * renderer, SDL_BlendMode blendMode);
static int GLES_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture);
static int GLES_UpdateTexture(SDL_Renderer * renderer, SDL_Texture * texture,
                              const SDL_Rect * rect, const void *pixels,
                              int pitch);
static int GLES_LockTexture(SDL_Renderer * renderer, SDL_Texture * texture,
                            const SDL_Rect * rect, void **pixels, int *pitch);
static void GLES_UnlockTexture(SDL_Renderer * renderer,
                               SDL_Texture * texture);
static int GLES_SetRenderTarget(SDL_Renderer * renderer,
                                 SDL_Texture * texture);
static int GLES_UpdateViewport(SDL_Renderer * renderer);
static int GLES_UpdateClipRect(SDL_Renderer * renderer);
static int GLES_RenderClear(SDL_Renderer * renderer);
static int GLES_RenderDrawPoints(SDL_Renderer * renderer,
                                 const SDL_FPoint * points, int count);
static int GLES_RenderDrawLines(SDL_Renderer * renderer,
                                const SDL_FPoint * points, int count);
static int GLES_RenderFillRects(SDL_Renderer * renderer,
                                const SDL_FRect * rects, int count);
static int GLES_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture,
                           const SDL_Rect * srcrect,
                           const SDL_FRect * dstrect);
static int GLES_RenderCopyEx(SDL_Renderer * renderer, SDL_Texture * texture,
                         const SDL_Rect * srcrect, const SDL_FRect * dstrect,
                         const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip);
static int GLES_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect,
                    Uint32 pixel_format, void * pixels, int pitch);
static void GLES_RenderPresent(SDL_Renderer * renderer);
static void GLES_DestroyTexture(SDL_Renderer * renderer,
                                SDL_Texture * texture);
static void GLES_DestroyRenderer(SDL_Renderer * renderer);
static int GLES_BindTexture (SDL_Renderer * renderer, SDL_Texture *texture, float *texw, float *texh);
static int GLES_UnbindTexture (SDL_Renderer * renderer, SDL_Texture *texture);
typedef struct GLES_FBOList GLES_FBOList;
struct GLES_FBOList
@@ -100,26 +62,27 @@
   GLES_FBOList *next;
};
SDL_RenderDriver GLES_RenderDriver = {
    GLES_CreateRenderer,
    {
     "opengles",
     (SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC),
     1,
     {SDL_PIXELFORMAT_ABGR8888},
     0,
     0}
};
typedef struct
{
    SDL_Rect viewport;
    SDL_bool viewport_dirty;
    SDL_Texture *texture;
    SDL_Texture *target;
    int drawablew;
    int drawableh;
    SDL_BlendMode blend;
    SDL_bool cliprect_enabled_dirty;
    SDL_bool cliprect_enabled;
    SDL_bool cliprect_dirty;
    SDL_Rect cliprect;
    SDL_bool texturing;
    Uint32 color;
    Uint32 clear_color;
} GLES_DrawStateCache;
typedef struct
{
    SDL_GLContext context;
    struct {
        Uint32 color;
        SDL_BlendMode blendMode;
        SDL_bool tex_coords;
    } current;
#define SDL_PROC(ret,func,params) ret (APIENTRY *func) params;
#define SDL_PROC_OES SDL_PROC
@@ -133,6 +96,8 @@
    SDL_bool GL_OES_blend_func_separate_supported;
    SDL_bool GL_OES_blend_equation_separate_supported;
    SDL_bool GL_OES_blend_subtract_supported;
    GLES_DrawStateCache drawstate;
} GLES_RenderData;
typedef struct
@@ -206,7 +171,7 @@
#define SDL_PROC_OES(ret,func,params) \
    do { \
        data->func = SDL_GL_GetProcAddress(#func); \
    } while ( 0 );
    } while ( 0 );
#endif /* __SDL_NOGETPROCADDR__ */
#include "SDL_glesfuncs.h"
@@ -214,8 +179,6 @@
#undef SDL_PROC_OES
    return 0;
}
static SDL_GLContext SDL_CurrentContext = NULL;
static GLES_FBOList *
GLES_GetFBO(GLES_RenderData *data, Uint32 w, Uint32 h)
@@ -241,191 +204,19 @@
{
    GLES_RenderData *data = (GLES_RenderData *) renderer->driverdata;
    if (SDL_CurrentContext != data->context) {
    if (SDL_GL_GetCurrentContext() != data->context) {
        if (SDL_GL_MakeCurrent(renderer->window, data->context) < 0) {
            return -1;
        }
        SDL_CurrentContext = data->context;
        GLES_UpdateViewport(renderer);
    }
    return 0;
}
/* This is called if we need to invalidate all of the SDL OpenGL state */
static void
GLES_ResetState(SDL_Renderer *renderer)
{
    GLES_RenderData *data = (GLES_RenderData *) renderer->driverdata;
    if (SDL_CurrentContext == data->context) {
        GLES_UpdateViewport(renderer);
    } else {
        GLES_ActivateRenderer(renderer);
    }
    data->current.color = 0xffffffff;
    data->current.blendMode = SDL_BLENDMODE_INVALID;
    data->current.tex_coords = SDL_FALSE;
    data->glDisable(GL_DEPTH_TEST);
    data->glDisable(GL_CULL_FACE);
    data->glMatrixMode(GL_MODELVIEW);
    data->glLoadIdentity();
    data->glEnableClientState(GL_VERTEX_ARRAY);
    data->glDisableClientState(GL_TEXTURE_COORD_ARRAY);
}
SDL_Renderer *
GLES_CreateRenderer(SDL_Window * window, Uint32 flags)
{
    SDL_Renderer *renderer;
    GLES_RenderData *data;
    GLint value;
    Uint32 window_flags;
    int profile_mask = 0, major = 0, minor = 0;
    SDL_bool changed_window = SDL_FALSE;
    SDL_GL_GetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, &profile_mask);
    SDL_GL_GetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, &major);
    SDL_GL_GetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, &minor);
    window_flags = SDL_GetWindowFlags(window);
    if (!(window_flags & SDL_WINDOW_OPENGL) ||
        profile_mask != SDL_GL_CONTEXT_PROFILE_ES || major != RENDERER_CONTEXT_MAJOR || minor != RENDERER_CONTEXT_MINOR) {
        changed_window = SDL_TRUE;
        SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
        SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, RENDERER_CONTEXT_MAJOR);
        SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, RENDERER_CONTEXT_MINOR);
        if (SDL_RecreateWindow(window, window_flags | SDL_WINDOW_OPENGL) < 0) {
            goto error;
        }
    }
    renderer = (SDL_Renderer *) SDL_calloc(1, sizeof(*renderer));
    if (!renderer) {
        SDL_OutOfMemory();
        goto error;
    }
    data = (GLES_RenderData *) SDL_calloc(1, sizeof(*data));
    if (!data) {
        GLES_DestroyRenderer(renderer);
        SDL_OutOfMemory();
        goto error;
    }
    renderer->WindowEvent = GLES_WindowEvent;
    renderer->GetOutputSize = GLES_GetOutputSize;
    renderer->SupportsBlendMode = GLES_SupportsBlendMode;
    renderer->CreateTexture = GLES_CreateTexture;
    renderer->UpdateTexture = GLES_UpdateTexture;
    renderer->LockTexture = GLES_LockTexture;
    renderer->UnlockTexture = GLES_UnlockTexture;
    renderer->SetRenderTarget = GLES_SetRenderTarget;
    renderer->UpdateViewport = GLES_UpdateViewport;
    renderer->UpdateClipRect = GLES_UpdateClipRect;
    renderer->RenderClear = GLES_RenderClear;
    renderer->RenderDrawPoints = GLES_RenderDrawPoints;
    renderer->RenderDrawLines = GLES_RenderDrawLines;
    renderer->RenderFillRects = GLES_RenderFillRects;
    renderer->RenderCopy = GLES_RenderCopy;
    renderer->RenderCopyEx = GLES_RenderCopyEx;
    renderer->RenderReadPixels = GLES_RenderReadPixels;
    renderer->RenderPresent = GLES_RenderPresent;
    renderer->DestroyTexture = GLES_DestroyTexture;
    renderer->DestroyRenderer = GLES_DestroyRenderer;
    renderer->GL_BindTexture = GLES_BindTexture;
    renderer->GL_UnbindTexture = GLES_UnbindTexture;
    renderer->info = GLES_RenderDriver.info;
    renderer->info.flags = SDL_RENDERER_ACCELERATED;
    renderer->driverdata = data;
    renderer->window = window;
    data->context = SDL_GL_CreateContext(window);
    if (!data->context) {
        GLES_DestroyRenderer(renderer);
        goto error;
    }
    if (SDL_GL_MakeCurrent(window, data->context) < 0) {
        GLES_DestroyRenderer(renderer);
        goto error;
    }
    if (GLES_LoadFunctions(data) < 0) {
        GLES_DestroyRenderer(renderer);
        goto error;
    }
    if (flags & SDL_RENDERER_PRESENTVSYNC) {
        SDL_GL_SetSwapInterval(1);
    } else {
        SDL_GL_SetSwapInterval(0);
    }
    if (SDL_GL_GetSwapInterval() > 0) {
        renderer->info.flags |= SDL_RENDERER_PRESENTVSYNC;
    }
    value = 0;
    data->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &value);
    renderer->info.max_texture_width = value;
    value = 0;
    data->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &value);
    renderer->info.max_texture_height = value;
    /* Android does not report GL_OES_framebuffer_object but the functionality seems to be there anyway */
    if (SDL_GL_ExtensionSupported("GL_OES_framebuffer_object") || data->glGenFramebuffersOES) {
        data->GL_OES_framebuffer_object_supported = SDL_TRUE;
        renderer->info.flags |= SDL_RENDERER_TARGETTEXTURE;
        value = 0;
        data->glGetIntegerv(GL_FRAMEBUFFER_BINDING_OES, &value);
        data->window_framebuffer = (GLuint)value;
    }
    data->framebuffers = NULL;
    if (SDL_GL_ExtensionSupported("GL_OES_blend_func_separate")) {
        data->GL_OES_blend_func_separate_supported = SDL_TRUE;
    }
    if (SDL_GL_ExtensionSupported("GL_OES_blend_equation_separate")) {
        data->GL_OES_blend_equation_separate_supported = SDL_TRUE;
    }
    if (SDL_GL_ExtensionSupported("GL_OES_blend_subtract")) {
        data->GL_OES_blend_subtract_supported = SDL_TRUE;
    }
    /* Set up parameters for rendering */
    GLES_ResetState(renderer);
    return renderer;
error:
    if (changed_window) {
        /* Uh oh, better try to put it back... */
        SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, profile_mask);
        SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, major);
        SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, minor);
        SDL_RecreateWindow(window, window_flags);
    }
    return NULL;
}
static void
GLES_WindowEvent(SDL_Renderer * renderer, const SDL_WindowEvent *event)
{
    GLES_RenderData *data = (GLES_RenderData *) renderer->driverdata;
    if (event->event == SDL_WINDOWEVENT_SIZE_CHANGED ||
        event->event == SDL_WINDOWEVENT_SHOWN ||
        event->event == SDL_WINDOWEVENT_HIDDEN) {
        /* Rebind the context to the window area and update matrices */
        SDL_CurrentContext = NULL;
    }
    if (event->event == SDL_WINDOWEVENT_MINIMIZED) {
        /* According to Apple documentation, we need to finish drawing NOW! */
@@ -561,7 +352,7 @@
        }
    }
    if (texture->access == SDL_TEXTUREACCESS_TARGET) {
        if (!renderdata->GL_OES_framebuffer_object_supported) {
            SDL_free(data);
@@ -571,7 +362,7 @@
    } else {
        data->fbo = NULL;
    }
    renderdata->glGetError();
    renderdata->glEnable(GL_TEXTURE_2D);
@@ -601,13 +392,15 @@
    renderdata->glTexImage2D(data->type, 0, internalFormat, texture_w,
                             texture_h, 0, format, type, NULL);
    renderdata->glDisable(GL_TEXTURE_2D);
    renderdata->drawstate.texture = texture;
    renderdata->drawstate.texturing = SDL_FALSE;
    result = renderdata->glGetError();
    if (result != GL_NO_ERROR) {
        SDL_free(data);
        return GLES_SetError("glTexImage2D()", result);
    }
    texture->driverdata = data;
    return 0;
}
@@ -664,6 +457,9 @@
    renderdata->glDisable(data->type);
    SDL_free(blob);
    renderdata->drawstate.texture = texture;
    renderdata->drawstate.texturing = SDL_FALSE;
    if (renderdata->glGetError() != GL_NO_ERROR) {
        return SDL_SetError("Failed to update texture");
    }
@@ -697,6 +493,18 @@
    GLES_UpdateTexture(renderer, texture, &rect, data->pixels, data->pitch);
}
static void
GLES_SetTextureScaleMode(SDL_Renderer * renderer, SDL_Texture * texture, SDL_ScaleMode scaleMode)
{
    GLES_RenderData *renderdata = (GLES_RenderData *) renderer->driverdata;
    GLES_TextureData *data = (GLES_TextureData *) texture->driverdata;
    GLenum glScaleMode = (scaleMode == SDL_ScaleModeNearest) ? GL_NEAREST : GL_LINEAR;
    renderdata->glBindTexture(data->type, data->texture);
    renderdata->glTexParameteri(data->type, GL_TEXTURE_MIN_FILTER, glScaleMode);
    renderdata->glTexParameteri(data->type, GL_TEXTURE_MAG_FILTER, glScaleMode);
}
static int
GLES_SetRenderTarget(SDL_Renderer * renderer, SDL_Texture * texture)
{
@@ -707,6 +515,8 @@
    if (!data->GL_OES_framebuffer_object_supported) {
        return SDL_SetError("Can't enable render target support in this renderer");
    }
    data->drawstate.viewport_dirty = SDL_TRUE;
    if (texture == NULL) {
        data->glBindFramebufferOES(GL_FRAMEBUFFER_OES, data->window_framebuffer);
@@ -725,293 +535,77 @@
    return 0;
}
static int
GLES_UpdateViewport(SDL_Renderer * renderer)
GLES_QueueSetViewport(SDL_Renderer * renderer, SDL_RenderCommand *cmd)
{
    GLES_RenderData *data = (GLES_RenderData *) renderer->driverdata;
    if (SDL_CurrentContext != data->context) {
        /* We'll update the viewport after we rebind the context */
        return 0;
    }
    if (renderer->target) {
        data->glViewport(renderer->viewport.x, renderer->viewport.y,
                         renderer->viewport.w, renderer->viewport.h);
    } else {
        int w, h;
        SDL_GL_GetDrawableSize(renderer->window, &w, &h);
        data->glViewport(renderer->viewport.x, (h - renderer->viewport.y - renderer->viewport.h),
                         renderer->viewport.w, renderer->viewport.h);
    }
    data->glMatrixMode(GL_PROJECTION);
    data->glLoadIdentity();
    if (renderer->viewport.w && renderer->viewport.h) {
        if (renderer->target) {
            data->glOrthof((GLfloat) 0,
                           (GLfloat) renderer->viewport.w,
                           (GLfloat) 0,
                           (GLfloat) renderer->viewport.h,
                           0.0, 1.0);
        } else {
            data->glOrthof((GLfloat) 0,
                           (GLfloat) renderer->viewport.w,
                           (GLfloat) renderer->viewport.h,
                           (GLfloat) 0,
                           0.0, 1.0);
        }
    }
    data->glMatrixMode(GL_MODELVIEW);
    return 0;
    return 0;  /* nothing to do in this backend. */
}
static int
GLES_UpdateClipRect(SDL_Renderer * renderer)
GLES_QueueDrawPoints(SDL_Renderer * renderer, SDL_RenderCommand *cmd, const SDL_FPoint * points, int count)
{
    GLES_RenderData *data = (GLES_RenderData *) renderer->driverdata;
    if (SDL_CurrentContext != data->context) {
        /* We'll update the clip rect after we rebind the context */
        return 0;
    }
    if (renderer->clipping_enabled) {
        const SDL_Rect *rect = &renderer->clip_rect;
        data->glEnable(GL_SCISSOR_TEST);
        if (renderer->target) {
            data->glScissor(renderer->viewport.x + rect->x, renderer->viewport.y + rect->y, rect->w, rect->h);
        } else {
            int w, h;
            SDL_GL_GetDrawableSize(renderer->window, &w, &h);
            data->glScissor(renderer->viewport.x + rect->x, h - renderer->viewport.y - rect->y - rect->h, rect->w, rect->h);
        }
    } else {
        data->glDisable(GL_SCISSOR_TEST);
    }
    return 0;
}
static void
GLES_SetColor(GLES_RenderData * data, Uint8 r, Uint8 g, Uint8 b, Uint8 a)
{
    Uint32 color = ((a << 24) | (r << 16) | (g << 8) | b);
    if (color != data->current.color) {
        data->glColor4f((GLfloat) r * inv255f,
                        (GLfloat) g * inv255f,
                        (GLfloat) b * inv255f,
                        (GLfloat) a * inv255f);
        data->current.color = color;
    }
}
static void
GLES_SetBlendMode(GLES_RenderData * data, SDL_BlendMode blendMode)
{
    if (blendMode != data->current.blendMode) {
        if (blendMode == SDL_BLENDMODE_NONE) {
            data->glDisable(GL_BLEND);
        } else {
            data->glEnable(GL_BLEND);
            if (data->GL_OES_blend_func_separate_supported) {
                data->glBlendFuncSeparateOES(GetBlendFunc(SDL_GetBlendModeSrcColorFactor(blendMode)),
                                             GetBlendFunc(SDL_GetBlendModeDstColorFactor(blendMode)),
                                             GetBlendFunc(SDL_GetBlendModeSrcAlphaFactor(blendMode)),
                                             GetBlendFunc(SDL_GetBlendModeDstAlphaFactor(blendMode)));
            } else {
                data->glBlendFunc(GetBlendFunc(SDL_GetBlendModeSrcColorFactor(blendMode)),
                                  GetBlendFunc(SDL_GetBlendModeDstColorFactor(blendMode)));
            }
            if (data->GL_OES_blend_equation_separate_supported) {
                data->glBlendEquationSeparateOES(GetBlendEquation(SDL_GetBlendModeColorOperation(blendMode)),
                                                 GetBlendEquation(SDL_GetBlendModeAlphaOperation(blendMode)));
            } else if (data->GL_OES_blend_subtract_supported) {
                data->glBlendEquationOES(GetBlendEquation(SDL_GetBlendModeColorOperation(blendMode)));
            }
        }
        data->current.blendMode = blendMode;
    }
}
static void
GLES_SetTexCoords(GLES_RenderData * data, SDL_bool enabled)
{
    if (enabled != data->current.tex_coords) {
        if (enabled) {
            data->glEnableClientState(GL_TEXTURE_COORD_ARRAY);
        } else {
            data->glDisableClientState(GL_TEXTURE_COORD_ARRAY);
        }
        data->current.tex_coords = enabled;
    }
}
static void
GLES_SetDrawingState(SDL_Renderer * renderer)
{
    GLES_RenderData *data = (GLES_RenderData *) renderer->driverdata;
    GLES_ActivateRenderer(renderer);
    GLES_SetColor(data, (GLfloat) renderer->r,
                        (GLfloat) renderer->g,
                        (GLfloat) renderer->b,
                        (GLfloat) renderer->a);
    GLES_SetBlendMode(data, renderer->blendMode);
    GLES_SetTexCoords(data, SDL_FALSE);
}
static int
GLES_RenderClear(SDL_Renderer * renderer)
{
    GLES_RenderData *data = (GLES_RenderData *) renderer->driverdata;
    GLES_ActivateRenderer(renderer);
    data->glClearColor((GLfloat) renderer->r * inv255f,
                 (GLfloat) renderer->g * inv255f,
                 (GLfloat) renderer->b * inv255f,
                 (GLfloat) renderer->a * inv255f);
    if (renderer->clipping_enabled) {
        data->glDisable(GL_SCISSOR_TEST);
    }
    data->glClear(GL_COLOR_BUFFER_BIT);
    if (renderer->clipping_enabled) {
        data->glEnable(GL_SCISSOR_TEST);
    }
    return 0;
}
static int
GLES_RenderDrawPoints(SDL_Renderer * renderer, const SDL_FPoint * points,
                      int count)
{
    GLES_RenderData *data = (GLES_RenderData *) renderer->driverdata;
    GLfloat *vertices;
    int idx;
    GLES_SetDrawingState(renderer);
    /* Emit the specified vertices as points */
    vertices = SDL_stack_alloc(GLfloat, count * 2);
    for (idx = 0; idx < count; ++idx) {
        GLfloat x = points[idx].x + 0.5f;
        GLfloat y = points[idx].y + 0.5f;
        vertices[idx * 2] = x;
        vertices[(idx * 2) + 1] = y;
    }
    data->glVertexPointer(2, GL_FLOAT, 0, vertices);
    data->glDrawArrays(GL_POINTS, 0, count);
    SDL_stack_free(vertices);
    return 0;
}
static int
GLES_RenderDrawLines(SDL_Renderer * renderer, const SDL_FPoint * points,
                     int count)
{
    GLES_RenderData *data = (GLES_RenderData *) renderer->driverdata;
    GLfloat *vertices;
    int idx;
    GLES_SetDrawingState(renderer);
    /* Emit a line strip including the specified vertices */
    vertices = SDL_stack_alloc(GLfloat, count * 2);
    for (idx = 0; idx < count; ++idx) {
        GLfloat x = points[idx].x + 0.5f;
        GLfloat y = points[idx].y + 0.5f;
        vertices[idx * 2] = x;
        vertices[(idx * 2) + 1] = y;
    }
    data->glVertexPointer(2, GL_FLOAT, 0, vertices);
    if (count > 2 &&
        points[0].x == points[count-1].x && points[0].y == points[count-1].y) {
        /* GL_LINE_LOOP takes care of the final segment */
        --count;
        data->glDrawArrays(GL_LINE_LOOP, 0, count);
    } else {
        data->glDrawArrays(GL_LINE_STRIP, 0, count);
        /* We need to close the endpoint of the line */
        data->glDrawArrays(GL_POINTS, count-1, 1);
    }
    SDL_stack_free(vertices);
    return 0;
}
static int
GLES_RenderFillRects(SDL_Renderer * renderer, const SDL_FRect * rects,
                     int count)
{
    GLES_RenderData *data = (GLES_RenderData *) renderer->driverdata;
    GLfloat *verts = (GLfloat *) SDL_AllocateRenderVertices(renderer, count * 2 * sizeof (GLfloat), 0, &cmd->data.draw.first);
    int i;
    GLES_SetDrawingState(renderer);
    if (!verts) {
        return -1;
    }
    for (i = 0; i < count; ++i) {
        const SDL_FRect *rect = &rects[i];
        GLfloat minx = rect->x;
        GLfloat maxx = rect->x + rect->w;
        GLfloat miny = rect->y;
        GLfloat maxy = rect->y + rect->h;
        GLfloat vertices[8];
        vertices[0] = minx;
        vertices[1] = miny;
        vertices[2] = maxx;
        vertices[3] = miny;
        vertices[4] = minx;
        vertices[5] = maxy;
        vertices[6] = maxx;
        vertices[7] = maxy;
        data->glVertexPointer(2, GL_FLOAT, 0, vertices);
        data->glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
    cmd->data.draw.count = count;
    for (i = 0; i < count; i++) {
        *(verts++) = 0.5f + points[i].x;
        *(verts++) = 0.5f + points[i].y;
    }
    return 0;
}
static int
GLES_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture,
                const SDL_Rect * srcrect, const SDL_FRect * dstrect)
GLES_QueueFillRects(SDL_Renderer * renderer, SDL_RenderCommand *cmd, const SDL_FRect * rects, int count)
{
    GLES_RenderData *data = (GLES_RenderData *) renderer->driverdata;
    GLfloat *verts = (GLfloat *) SDL_AllocateRenderVertices(renderer, count * 8 * sizeof (GLfloat), 0, &cmd->data.draw.first);
    int i;
    if (!verts) {
        return -1;
    }
    cmd->data.draw.count = count;
    for (i = 0; i < count; i++) {
        const SDL_FRect *rect = &rects[i];
        const GLfloat minx = rect->x;
        const GLfloat maxx = rect->x + rect->w;
        const GLfloat miny = rect->y;
        const GLfloat maxy = rect->y + rect->h;
        *(verts++) = minx;
        *(verts++) = miny;
        *(verts++) = maxx;
        *(verts++) = miny;
        *(verts++) = minx;
        *(verts++) = maxy;
        *(verts++) = maxx;
        *(verts++) = maxy;
    }
    return 0;
}
static int
GLES_QueueCopy(SDL_Renderer * renderer, SDL_RenderCommand *cmd, SDL_Texture * texture,
                          const SDL_Rect * srcrect, const SDL_FRect * dstrect)
{
    GLES_TextureData *texturedata = (GLES_TextureData *) texture->driverdata;
    GLfloat minx, miny, maxx, maxy;
    GLfloat minu, maxu, minv, maxv;
    GLfloat vertices[8];
    GLfloat texCoords[8];
    GLfloat *verts = (GLfloat *) SDL_AllocateRenderVertices(renderer, 16 * sizeof (GLfloat), 0, &cmd->data.draw.first);
    GLES_ActivateRenderer(renderer);
    data->glEnable(GL_TEXTURE_2D);
    data->glBindTexture(texturedata->type, texturedata->texture);
    if (texture->modMode) {
        GLES_SetColor(data, texture->r, texture->g, texture->b, texture->a);
    } else {
        GLES_SetColor(data, 255, 255, 255, 255);
    if (!verts) {
        return -1;
    }
    GLES_SetBlendMode(data, texture->blendMode);
    GLES_SetTexCoords(data, SDL_TRUE);
    cmd->data.draw.count = 1;
    minx = dstrect->x;
    miny = dstrect->y;
@@ -1027,126 +621,351 @@
    maxv = (GLfloat) (srcrect->y + srcrect->h) / texture->h;
    maxv *= texturedata->texh;
    vertices[0] = minx;
    vertices[1] = miny;
    vertices[2] = maxx;
    vertices[3] = miny;
    vertices[4] = minx;
    vertices[5] = maxy;
    vertices[6] = maxx;
    vertices[7] = maxy;
    *(verts++) = minx;
    *(verts++) = miny;
    *(verts++) = maxx;
    *(verts++) = miny;
    *(verts++) = minx;
    *(verts++) = maxy;
    *(verts++) = maxx;
    *(verts++) = maxy;
    texCoords[0] = minu;
    texCoords[1] = minv;
    texCoords[2] = maxu;
    texCoords[3] = minv;
    texCoords[4] = minu;
    texCoords[5] = maxv;
    texCoords[6] = maxu;
    texCoords[7] = maxv;
    data->glVertexPointer(2, GL_FLOAT, 0, vertices);
    data->glTexCoordPointer(2, GL_FLOAT, 0, texCoords);
    data->glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
    data->glDisable(GL_TEXTURE_2D);
    *(verts++) = minu;
    *(verts++) = minv;
    *(verts++) = maxu;
    *(verts++) = minv;
    *(verts++) = minu;
    *(verts++) = maxv;
    *(verts++) = maxu;
    *(verts++) = maxv;
    return 0;
}
static int
GLES_RenderCopyEx(SDL_Renderer * renderer, SDL_Texture * texture,
                const SDL_Rect * srcrect, const SDL_FRect * dstrect,
                const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip)
GLES_QueueCopyEx(SDL_Renderer * renderer, SDL_RenderCommand *cmd, SDL_Texture * texture,
                        const SDL_Rect * srcquad, const SDL_FRect * dstrect,
                        const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip)
{
    GLES_RenderData *data = (GLES_RenderData *) renderer->driverdata;
    GLES_TextureData *texturedata = (GLES_TextureData *) texture->driverdata;
    GLfloat minx, miny, maxx, maxy;
    GLfloat minu, maxu, minv, maxv;
    GLfloat centerx, centery;
    GLfloat vertices[8];
    GLfloat texCoords[8];
    GLfloat minu, maxu, minv, maxv;
    GLfloat *verts = (GLfloat *) SDL_AllocateRenderVertices(renderer, 19 * sizeof (GLfloat), 0, &cmd->data.draw.first);
    GLES_ActivateRenderer(renderer);
    data->glEnable(GL_TEXTURE_2D);
    data->glBindTexture(texturedata->type, texturedata->texture);
    if (texture->modMode) {
        GLES_SetColor(data, texture->r, texture->g, texture->b, texture->a);
    } else {
        GLES_SetColor(data, 255, 255, 255, 255);
    if (!verts) {
        return -1;
    }
    GLES_SetBlendMode(data, texture->blendMode);
    GLES_SetTexCoords(data, SDL_TRUE);
    centerx = center->x;
    centery = center->y;
    /* Rotate and translate */
    data->glPushMatrix();
    data->glTranslatef(dstrect->x + centerx, dstrect->y + centery, 0.0f);
    data->glRotatef((GLfloat)angle, 0.0f, 0.0f, 1.0f);
    if (flip & SDL_FLIP_HORIZONTAL) {
        minx =  dstrect->w - centerx;
        maxx = -centerx;
    } else {
    }
    else {
        minx = -centerx;
        maxx = dstrect->w - centerx;
        maxx =  dstrect->w - centerx;
    }
    if (flip & SDL_FLIP_VERTICAL) {
        miny = dstrect->h - centery;
        miny =  dstrect->h - centery;
        maxy = -centery;
    } else {
    }
    else {
        miny = -centery;
        maxy = dstrect->h - centery;
        maxy =  dstrect->h - centery;
    }
    minu = (GLfloat) srcrect->x / texture->w;
    minu = (GLfloat) srcquad->x / texture->w;
    minu *= texturedata->texw;
    maxu = (GLfloat) (srcrect->x + srcrect->w) / texture->w;
    maxu = (GLfloat) (srcquad->x + srcquad->w) / texture->w;
    maxu *= texturedata->texw;
    minv = (GLfloat) srcrect->y / texture->h;
    minv = (GLfloat) srcquad->y / texture->h;
    minv *= texturedata->texh;
    maxv = (GLfloat) (srcrect->y + srcrect->h) / texture->h;
    maxv = (GLfloat) (srcquad->y + srcquad->h) / texture->h;
    maxv *= texturedata->texh;
    vertices[0] = minx;
    vertices[1] = miny;
    vertices[2] = maxx;
    vertices[3] = miny;
    vertices[4] = minx;
    vertices[5] = maxy;
    vertices[6] = maxx;
    vertices[7] = maxy;
    cmd->data.draw.count = 1;
    texCoords[0] = minu;
    texCoords[1] = minv;
    texCoords[2] = maxu;
    texCoords[3] = minv;
    texCoords[4] = minu;
    texCoords[5] = maxv;
    texCoords[6] = maxu;
    texCoords[7] = maxv;
    data->glVertexPointer(2, GL_FLOAT, 0, vertices);
    data->glTexCoordPointer(2, GL_FLOAT, 0, texCoords);
    data->glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
    data->glPopMatrix();
    data->glDisable(GL_TEXTURE_2D);
    *(verts++) = minx;
    *(verts++) = miny;
    *(verts++) = maxx;
    *(verts++) = miny;
    *(verts++) = minx;
    *(verts++) = maxy;
    *(verts++) = maxx;
    *(verts++) = maxy;
    *(verts++) = minu;
    *(verts++) = minv;
    *(verts++) = maxu;
    *(verts++) = minv;
    *(verts++) = minu;
    *(verts++) = maxv;
    *(verts++) = maxu;
    *(verts++) = maxv;
    *(verts++) = (GLfloat) dstrect->x + centerx;
    *(verts++) = (GLfloat) dstrect->y + centery;
    *(verts++) = (GLfloat) angle;
    return 0;
}
static void
SetDrawState(GLES_RenderData *data, const SDL_RenderCommand *cmd)
{
    const SDL_BlendMode blend = cmd->data.draw.blend;
    const Uint8 r = cmd->data.draw.r;
    const Uint8 g = cmd->data.draw.g;
    const Uint8 b = cmd->data.draw.b;
    const Uint8 a = cmd->data.draw.a;
    const Uint32 color = ((a << 24) | (r << 16) | (g << 8) | b);
    if (color != data->drawstate.color) {
        const GLfloat fr = ((GLfloat) r) * inv255f;
        const GLfloat fg = ((GLfloat) g) * inv255f;
        const GLfloat fb = ((GLfloat) b) * inv255f;
        const GLfloat fa = ((GLfloat) a) * inv255f;
        data->glColor4f(fr, fg, fb, fa);
        data->drawstate.color = color;
    }
    if (data->drawstate.viewport_dirty) {
        const SDL_Rect *viewport = &data->drawstate.viewport;
        const SDL_bool istarget = (data->drawstate.target != NULL);
        data->glMatrixMode(GL_PROJECTION);
        data->glLoadIdentity();
        data->glViewport(viewport->x,
                         istarget ? viewport->y : (data->drawstate.drawableh - viewport->y - viewport->h),
                         viewport->w, viewport->h);
        if (viewport->w && viewport->h) {
            data->glOrthof((GLfloat) 0, (GLfloat) viewport->w,
                           (GLfloat) (istarget ? 0 : viewport->h),
                           (GLfloat) (istarget ? viewport->h : 0),
                           0.0, 1.0);
        }
        data->glMatrixMode(GL_MODELVIEW);
        data->drawstate.viewport_dirty = SDL_FALSE;
    }
    if (data->drawstate.cliprect_enabled_dirty) {
        if (data->drawstate.cliprect_enabled) {
            data->glEnable(GL_SCISSOR_TEST);
        } else {
            data->glDisable(GL_SCISSOR_TEST);
        }
        data->drawstate.cliprect_enabled_dirty = SDL_FALSE;
    }
    if (data->drawstate.cliprect_enabled && data->drawstate.cliprect_dirty) {
        const SDL_Rect *viewport = &data->drawstate.viewport;
        const SDL_Rect *rect = &data->drawstate.cliprect;
        const SDL_bool istarget = (data->drawstate.target != NULL);
        data->glScissor(viewport->x + rect->x,
                        istarget ? viewport->y + rect->y : data->drawstate.drawableh - viewport->y - rect->y - rect->h,
                        rect->w, rect->h);
        data->drawstate.cliprect_dirty = SDL_FALSE;
    }
    if (blend != data->drawstate.blend) {
        if (blend == SDL_BLENDMODE_NONE) {
            data->glDisable(GL_BLEND);
        } else {
            data->glEnable(GL_BLEND);
            if (data->GL_OES_blend_func_separate_supported) {
                data->glBlendFuncSeparateOES(GetBlendFunc(SDL_GetBlendModeSrcColorFactor(blend)),
                                             GetBlendFunc(SDL_GetBlendModeDstColorFactor(blend)),
                                             GetBlendFunc(SDL_GetBlendModeSrcAlphaFactor(blend)),
                                             GetBlendFunc(SDL_GetBlendModeDstAlphaFactor(blend)));
            } else {
                data->glBlendFunc(GetBlendFunc(SDL_GetBlendModeSrcColorFactor(blend)),
                                  GetBlendFunc(SDL_GetBlendModeDstColorFactor(blend)));
            }
            if (data->GL_OES_blend_equation_separate_supported) {
                data->glBlendEquationSeparateOES(GetBlendEquation(SDL_GetBlendModeColorOperation(blend)),
                                                 GetBlendEquation(SDL_GetBlendModeAlphaOperation(blend)));
            } else if (data->GL_OES_blend_subtract_supported) {
                data->glBlendEquationOES(GetBlendEquation(SDL_GetBlendModeColorOperation(blend)));
            }
        }
        data->drawstate.blend = blend;
    }
    if ((cmd->data.draw.texture != NULL) != data->drawstate.texturing) {
        if (cmd->data.draw.texture == NULL) {
            data->glDisable(GL_TEXTURE_2D);
            data->glDisableClientState(GL_TEXTURE_COORD_ARRAY);
            data->drawstate.texturing = SDL_FALSE;
        } else {
            data->glEnable(GL_TEXTURE_2D);
            data->glEnableClientState(GL_TEXTURE_COORD_ARRAY);
            data->drawstate.texturing = SDL_TRUE;
        }
    }
}
static void
SetCopyState(GLES_RenderData *data, const SDL_RenderCommand *cmd)
{
    SDL_Texture *texture = cmd->data.draw.texture;
    SetDrawState(data, cmd);
    if (texture != data->drawstate.texture) {
        GLES_TextureData *texturedata = (GLES_TextureData *) texture->driverdata;
        data->glBindTexture(GL_TEXTURE_2D, texturedata->texture);
        data->drawstate.texture = texture;
    }
}
static int
GLES_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *vertices, size_t vertsize)
{
    GLES_RenderData *data = (GLES_RenderData *) renderer->driverdata;
    size_t i;
    if (GLES_ActivateRenderer(renderer) < 0) {
        return -1;
    }
    data->drawstate.target = renderer->target;
    if (!renderer->target) {
        SDL_GL_GetDrawableSize(renderer->window, &data->drawstate.drawablew, &data->drawstate.drawableh);
    }
    while (cmd) {
        switch (cmd->command) {
            case SDL_RENDERCMD_SETDRAWCOLOR: {
                break;  /* not used in this render backend. */
            }
            case SDL_RENDERCMD_SETVIEWPORT: {
                SDL_Rect *viewport = &data->drawstate.viewport;
                if (SDL_memcmp(viewport, &cmd->data.viewport.rect, sizeof (SDL_Rect)) != 0) {
                    SDL_memcpy(viewport, &cmd->data.viewport.rect, sizeof (SDL_Rect));
                    data->drawstate.viewport_dirty = SDL_TRUE;
                }
                break;
            }
            case SDL_RENDERCMD_SETCLIPRECT: {
                const SDL_Rect *rect = &cmd->data.cliprect.rect;
                if (data->drawstate.cliprect_enabled != cmd->data.cliprect.enabled) {
                    data->drawstate.cliprect_enabled = cmd->data.cliprect.enabled;
                    data->drawstate.cliprect_enabled_dirty = SDL_TRUE;
                }
                if (SDL_memcmp(&data->drawstate.cliprect, rect, sizeof (SDL_Rect)) != 0) {
                    SDL_memcpy(&data->drawstate.cliprect, rect, sizeof (SDL_Rect));
                    data->drawstate.cliprect_dirty = SDL_TRUE;
                }
                break;
            }
            case SDL_RENDERCMD_CLEAR: {
                const Uint8 r = cmd->data.color.r;
                const Uint8 g = cmd->data.color.g;
                const Uint8 b = cmd->data.color.b;
                const Uint8 a = cmd->data.color.a;
                const Uint32 color = ((a << 24) | (r << 16) | (g << 8) | b);
                if (color != data->drawstate.clear_color) {
                    const GLfloat fr = ((GLfloat) r) * inv255f;
                    const GLfloat fg = ((GLfloat) g) * inv255f;
                    const GLfloat fb = ((GLfloat) b) * inv255f;
                    const GLfloat fa = ((GLfloat) a) * inv255f;
                    data->glClearColor(fr, fg, fb, fa);
                    data->drawstate.clear_color = color;
                }
                if (data->drawstate.cliprect_enabled || data->drawstate.cliprect_enabled_dirty) {
                    data->glDisable(GL_SCISSOR_TEST);
                    data->drawstate.cliprect_enabled_dirty = data->drawstate.cliprect_enabled;
                }
                data->glClear(GL_COLOR_BUFFER_BIT);
                break;
            }
            case SDL_RENDERCMD_DRAW_POINTS: {
                const size_t count = cmd->data.draw.count;
                const GLfloat *verts = (GLfloat *) (((Uint8 *) vertices) + cmd->data.draw.first);
                SetDrawState(data, cmd);
                data->glVertexPointer(2, GL_FLOAT, 0, verts);
                data->glDrawArrays(GL_POINTS, 0, (GLsizei) count);
                break;
            }
            case SDL_RENDERCMD_DRAW_LINES: {
                const GLfloat *verts = (GLfloat *) (((Uint8 *) vertices) + cmd->data.draw.first);
                const size_t count = cmd->data.draw.count;
                SetDrawState(data, cmd);
                data->glVertexPointer(2, GL_FLOAT, 0, verts);
                if (count > 2 && (verts[0] == verts[(count-1)*2]) && (verts[1] == verts[(count*2)-1])) {
                    /* GL_LINE_LOOP takes care of the final segment */
                    data->glDrawArrays(GL_LINE_LOOP, 0, (GLsizei) (count - 1));
                } else {
                    data->glDrawArrays(GL_LINE_STRIP, 0, (GLsizei) count);
                    /* We need to close the endpoint of the line */
                    data->glDrawArrays(GL_POINTS, (GLsizei) (count - 1), 1);
                }
                break;
            }
            case SDL_RENDERCMD_FILL_RECTS: {
                const size_t count = cmd->data.draw.count;
                const GLfloat *verts = (GLfloat *) (((Uint8 *) vertices) + cmd->data.draw.first);
                GLsizei offset = 0;
                SetDrawState(data, cmd);
                data->glVertexPointer(2, GL_FLOAT, 0, verts);
                for (i = 0; i < count; ++i, offset += 4) {
                    data->glDrawArrays(GL_TRIANGLE_STRIP, offset, 4);
                }
                break;
            }
            case SDL_RENDERCMD_COPY: {
                const GLfloat *verts = (GLfloat *) (((Uint8 *) vertices) + cmd->data.draw.first);
                SetCopyState(data, cmd);
                data->glVertexPointer(2, GL_FLOAT, 0, verts);
                data->glTexCoordPointer(2, GL_FLOAT, 0, verts + 8);
                data->glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
                break;
            }
            case SDL_RENDERCMD_COPY_EX: {
                const GLfloat *verts = (GLfloat *) (((Uint8 *) vertices) + cmd->data.draw.first);
                const GLfloat translatex = verts[16];
                const GLfloat translatey = verts[17];
                const GLfloat angle = verts[18];
                SetCopyState(data, cmd);
                data->glVertexPointer(2, GL_FLOAT, 0, verts);
                data->glTexCoordPointer(2, GL_FLOAT, 0, verts + 8);
                /* Translate to flip, rotate, translate to position */
                data->glPushMatrix();
                data->glTranslatef(translatex, translatey, 0.0f);
                data->glRotatef(angle, 0.0, 0.0, 1.0);
                data->glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
                data->glPopMatrix();
                break;
            }
            case SDL_RENDERCMD_NO_OP:
                break;
        }
        cmd = cmd->next;
    }
    return 0;
}
static int
GLES_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect,
                    Uint32 pixel_format, void * pixels, int pitch)
                      Uint32 pixel_format, void * pixels, int pitch)
{
    GLES_RenderData *data = (GLES_RenderData *) renderer->driverdata;
    Uint32 temp_format = renderer->target ? renderer->target->format : SDL_PIXELFORMAT_ABGR8888;
@@ -1173,10 +992,11 @@
    /* Flip the rows to be top-down if necessary */
    if (!renderer->target) {
        SDL_bool isstack;
        length = rect->w * SDL_BYTESPERPIXEL(temp_format);
        src = (Uint8*)temp_pixels + (rect->h-1)*temp_pitch;
        dst = (Uint8*)temp_pixels;
        tmp = SDL_stack_alloc(Uint8, length);
        tmp = SDL_small_alloc(Uint8, length, &isstack);
        rows = rect->h / 2;
        while (rows--) {
            SDL_memcpy(tmp, dst, length);
@@ -1185,7 +1005,7 @@
            dst += temp_pitch;
            src -= temp_pitch;
        }
        SDL_stack_free(tmp);
        SDL_small_free(tmp, isstack);
    }
    status = SDL_ConvertPixels(rect->w, rect->h,
@@ -1212,6 +1032,13 @@
    GLES_TextureData *data = (GLES_TextureData *) texture->driverdata;
    GLES_ActivateRenderer(renderer);
    if (renderdata->drawstate.texture == texture) {
        renderdata->drawstate.texture = NULL;
    }
    if (renderdata->drawstate.target == texture) {
        renderdata->drawstate.target = NULL;
    }
    if (!data) {
        return;
@@ -1253,6 +1080,9 @@
    data->glEnable(GL_TEXTURE_2D);
    data->glBindTexture(texturedata->type, texturedata->texture);
    data->drawstate.texture = texture;
    data->drawstate.texturing = SDL_TRUE;
    if (texw) {
        *texw = (float)texturedata->texw;
    }
@@ -1270,9 +1100,174 @@
    GLES_ActivateRenderer(renderer);
    data->glDisable(texturedata->type);
    data->drawstate.texture = NULL;
    data->drawstate.texturing = SDL_FALSE;
    return 0;
}
static SDL_Renderer *
GLES_CreateRenderer(SDL_Window * window, Uint32 flags)
{
    SDL_Renderer *renderer;
    GLES_RenderData *data;
    GLint value;
    Uint32 window_flags;
    int profile_mask = 0, major = 0, minor = 0;
    SDL_bool changed_window = SDL_FALSE;
    SDL_GL_GetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, &profile_mask);
    SDL_GL_GetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, &major);
    SDL_GL_GetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, &minor);
    window_flags = SDL_GetWindowFlags(window);
    if (!(window_flags & SDL_WINDOW_OPENGL) ||
        profile_mask != SDL_GL_CONTEXT_PROFILE_ES || major != RENDERER_CONTEXT_MAJOR || minor != RENDERER_CONTEXT_MINOR) {
        changed_window = SDL_TRUE;
        SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
        SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, RENDERER_CONTEXT_MAJOR);
        SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, RENDERER_CONTEXT_MINOR);
        if (SDL_RecreateWindow(window, window_flags | SDL_WINDOW_OPENGL) < 0) {
            goto error;
        }
    }
    renderer = (SDL_Renderer *) SDL_calloc(1, sizeof(*renderer));
    if (!renderer) {
        SDL_OutOfMemory();
        goto error;
    }
    data = (GLES_RenderData *) SDL_calloc(1, sizeof(*data));
    if (!data) {
        GLES_DestroyRenderer(renderer);
        SDL_OutOfMemory();
        goto error;
    }
    renderer->WindowEvent = GLES_WindowEvent;
    renderer->GetOutputSize = GLES_GetOutputSize;
    renderer->SupportsBlendMode = GLES_SupportsBlendMode;
    renderer->CreateTexture = GLES_CreateTexture;
    renderer->UpdateTexture = GLES_UpdateTexture;
    renderer->LockTexture = GLES_LockTexture;
    renderer->UnlockTexture = GLES_UnlockTexture;
    renderer->SetTextureScaleMode = GLES_SetTextureScaleMode;
    renderer->SetRenderTarget = GLES_SetRenderTarget;
    renderer->QueueSetViewport = GLES_QueueSetViewport;
    renderer->QueueSetDrawColor = GLES_QueueSetViewport;  /* SetViewport and SetDrawColor are (currently) no-ops. */
    renderer->QueueDrawPoints = GLES_QueueDrawPoints;
    renderer->QueueDrawLines = GLES_QueueDrawPoints;  /* lines and points queue vertices the same way. */
    renderer->QueueFillRects = GLES_QueueFillRects;
    renderer->QueueCopy = GLES_QueueCopy;
    renderer->QueueCopyEx = GLES_QueueCopyEx;
    renderer->RunCommandQueue = GLES_RunCommandQueue;
    renderer->RenderReadPixels = GLES_RenderReadPixels;
    renderer->RenderPresent = GLES_RenderPresent;
    renderer->DestroyTexture = GLES_DestroyTexture;
    renderer->DestroyRenderer = GLES_DestroyRenderer;
    renderer->GL_BindTexture = GLES_BindTexture;
    renderer->GL_UnbindTexture = GLES_UnbindTexture;
    renderer->info = GLES_RenderDriver.info;
    renderer->info.flags = SDL_RENDERER_ACCELERATED;
    renderer->driverdata = data;
    renderer->window = window;
    data->context = SDL_GL_CreateContext(window);
    if (!data->context) {
        GLES_DestroyRenderer(renderer);
        goto error;
    }
    if (SDL_GL_MakeCurrent(window, data->context) < 0) {
        GLES_DestroyRenderer(renderer);
        goto error;
    }
    if (GLES_LoadFunctions(data) < 0) {
        GLES_DestroyRenderer(renderer);
        goto error;
    }
    if (flags & SDL_RENDERER_PRESENTVSYNC) {
        SDL_GL_SetSwapInterval(1);
    } else {
        SDL_GL_SetSwapInterval(0);
    }
    if (SDL_GL_GetSwapInterval() > 0) {
        renderer->info.flags |= SDL_RENDERER_PRESENTVSYNC;
    }
    value = 0;
    data->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &value);
    renderer->info.max_texture_width = value;
    value = 0;
    data->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &value);
    renderer->info.max_texture_height = value;
    /* Android does not report GL_OES_framebuffer_object but the functionality seems to be there anyway */
    if (SDL_GL_ExtensionSupported("GL_OES_framebuffer_object") || data->glGenFramebuffersOES) {
        data->GL_OES_framebuffer_object_supported = SDL_TRUE;
        renderer->info.flags |= SDL_RENDERER_TARGETTEXTURE;
        value = 0;
        data->glGetIntegerv(GL_FRAMEBUFFER_BINDING_OES, &value);
        data->window_framebuffer = (GLuint)value;
    }
    data->framebuffers = NULL;
    if (SDL_GL_ExtensionSupported("GL_OES_blend_func_separate")) {
        data->GL_OES_blend_func_separate_supported = SDL_TRUE;
    }
    if (SDL_GL_ExtensionSupported("GL_OES_blend_equation_separate")) {
        data->GL_OES_blend_equation_separate_supported = SDL_TRUE;
    }
    if (SDL_GL_ExtensionSupported("GL_OES_blend_subtract")) {
        data->GL_OES_blend_subtract_supported = SDL_TRUE;
    }
    /* Set up parameters for rendering */
    data->glDisable(GL_DEPTH_TEST);
    data->glDisable(GL_CULL_FACE);
    data->glMatrixMode(GL_MODELVIEW);
    data->glLoadIdentity();
    data->glEnableClientState(GL_VERTEX_ARRAY);
    data->glDisableClientState(GL_TEXTURE_COORD_ARRAY);
    data->glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
    data->drawstate.blend = SDL_BLENDMODE_INVALID;
    data->drawstate.color = 0xFFFFFFFF;
    data->drawstate.clear_color = 0xFFFFFFFF;
    return renderer;
error:
    if (changed_window) {
        /* Uh oh, better try to put it back... */
        SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, profile_mask);
        SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, major);
        SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, minor);
        SDL_RecreateWindow(window, window_flags);
    }
    return NULL;
}
SDL_RenderDriver GLES_RenderDriver = {
    GLES_CreateRenderer,
    {
     "opengles",
     (SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC),
     1,
     {SDL_PIXELFORMAT_ABGR8888},
     0,
     0
    }
};
#endif /* SDL_VIDEO_RENDER_OGL_ES && !SDL_RENDER_DISABLED */
/* vi: set ts=4 sw=4 expandtab: */