Browse Source

render: first shot at reworking opengl backend for new batching system.

Ryan C. Gordon 6 years ago
parent
commit
55cb9966db
2 changed files with 549 additions and 482 deletions
  1. 548 482
      src/render/opengl/SDL_render_gl.c
  2. 1 0
      src/render/opengl/SDL_shaders_gl.h

+ 548 - 482
src/render/opengl/SDL_render_gl.c

@@ -51,9 +51,8 @@ extern int SDL_RecreateWindow(SDL_Window * window, Uint32 flags);
 
 static const float inv255f = 1.0f / 255.0f;
 
+/* !!! FIXME: delete these predeclarations and just move the functions before their use. */
 static SDL_Renderer *GL_CreateRenderer(SDL_Window * window, Uint32 flags);
-static void GL_WindowEvent(SDL_Renderer * renderer,
-                           const SDL_WindowEvent *event);
 static int GL_GetOutputSize(SDL_Renderer * renderer, int *w, int *h);
 static SDL_bool GL_SupportsBlendMode(SDL_Renderer * renderer, SDL_BlendMode blendMode);
 static int GL_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture);
@@ -69,20 +68,17 @@ static int GL_LockTexture(SDL_Renderer * renderer, SDL_Texture * texture,
                           const SDL_Rect * rect, void **pixels, int *pitch);
 static void GL_UnlockTexture(SDL_Renderer * renderer, SDL_Texture * texture);
 static int GL_SetRenderTarget(SDL_Renderer * renderer, SDL_Texture * texture);
-static int GL_UpdateViewport(SDL_Renderer * renderer);
-static int GL_UpdateClipRect(SDL_Renderer * renderer);
-static int GL_RenderClear(SDL_Renderer * renderer);
-static int GL_RenderDrawPoints(SDL_Renderer * renderer,
-                               const SDL_FPoint * points, int count);
-static int GL_RenderDrawLines(SDL_Renderer * renderer,
+static int GL_QueueDrawPoints(SDL_Renderer * renderer, SDL_RenderCommand *cmd,
                               const SDL_FPoint * points, int count);
-static int GL_RenderFillRects(SDL_Renderer * renderer,
-                              const SDL_FRect * rects, int count);
-static int GL_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture,
-                         const SDL_Rect * srcrect, const SDL_FRect * dstrect);
-static int GL_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 GL_QueueFillRects(SDL_Renderer * renderer, SDL_RenderCommand *cmd,
+                             const SDL_FRect * rects, int count);
+static int GL_QueueCopy(SDL_Renderer *renderer, SDL_RenderCommand *cmd, SDL_Texture *texture,
+                        const SDL_Rect *srcrect, const SDL_FRect *dstrect);
+static int GL_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);
+static int GL_RunCommandQueue(SDL_Renderer *renderer, SDL_RenderCommand *cmd, void *vertices, size_t vertsize);
 static int GL_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect,
                                Uint32 pixel_format, void * pixels, int pitch);
 static void GL_RenderPresent(SDL_Renderer * renderer);
@@ -122,14 +118,10 @@ typedef struct
     GLDEBUGPROCARB next_error_callback;
     GLvoid *next_error_userparam;
 
+    GLenum textype;
+
     SDL_bool GL_ARB_texture_non_power_of_two_supported;
     SDL_bool GL_ARB_texture_rectangle_supported;
-    struct {
-        GL_Shader shader;
-        Uint32 color;
-        SDL_BlendMode blendMode;
-    } current;
-
     SDL_bool GL_EXT_framebuffer_object_supported;
     GL_FBOList *framebuffers;
 
@@ -157,7 +149,6 @@ typedef struct
 typedef struct
 {
     GLuint texture;
-    GLenum type;
     GLfloat texw;
     GLfloat texh;
     GLenum format;
@@ -284,21 +275,15 @@ GL_LoadFunctions(GL_RenderData * data)
     return 0;
 }
 
-static SDL_GLContext SDL_CurrentContext = NULL;
-
 static int
 GL_ActivateRenderer(SDL_Renderer * renderer)
 {
     GL_RenderData *data = (GL_RenderData *) renderer->driverdata;
 
-    if (SDL_CurrentContext != data->context ||
-        SDL_GL_GetCurrentContext() != data->context) {
+    if (SDL_GL_GetCurrentContext() != data->context) {
         if (SDL_GL_MakeCurrent(renderer->window, data->context) < 0) {
             return -1;
         }
-        SDL_CurrentContext = data->context;
-
-        GL_UpdateViewport(renderer);
     }
 
     GL_ClearErrors(renderer);
@@ -306,33 +291,6 @@ GL_ActivateRenderer(SDL_Renderer * renderer)
     return 0;
 }
 
-/* This is called if we need to invalidate all of the SDL OpenGL state */
-static void
-GL_ResetState(SDL_Renderer *renderer)
-{
-    GL_RenderData *data = (GL_RenderData *) renderer->driverdata;
-
-    if (SDL_GL_GetCurrentContext() == data->context) {
-        GL_UpdateViewport(renderer);
-    } else {
-        GL_ActivateRenderer(renderer);
-    }
-
-    data->current.shader = SHADER_NONE;
-    data->current.color = 0xffffffff;
-    data->current.blendMode = SDL_BLENDMODE_INVALID;
-
-    data->glDisable(GL_DEPTH_TEST);
-    data->glDisable(GL_CULL_FACE);
-    /* This ended up causing video discrepancies between OpenGL and Direct3D */
-    /* data->glEnable(GL_LINE_SMOOTH); */
-
-    data->glMatrixMode(GL_MODELVIEW);
-    data->glLoadIdentity();
-
-    GL_CheckError("", renderer);
-}
-
 static void APIENTRY
 GL_HandleDebugMessage(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const char *message, const void *userParam)
 {
@@ -425,7 +383,6 @@ GL_CreateRenderer(SDL_Window * window, Uint32 flags)
         goto error;
     }
 
-    renderer->WindowEvent = GL_WindowEvent;
     renderer->GetOutputSize = GL_GetOutputSize;
     renderer->SupportsBlendMode = GL_SupportsBlendMode;
     renderer->CreateTexture = GL_CreateTexture;
@@ -434,14 +391,12 @@ GL_CreateRenderer(SDL_Window * window, Uint32 flags)
     renderer->LockTexture = GL_LockTexture;
     renderer->UnlockTexture = GL_UnlockTexture;
     renderer->SetRenderTarget = GL_SetRenderTarget;
-    renderer->UpdateViewport = GL_UpdateViewport;
-    renderer->UpdateClipRect = GL_UpdateClipRect;
-    renderer->RenderClear = GL_RenderClear;
-    renderer->RenderDrawPoints = GL_RenderDrawPoints;
-    renderer->RenderDrawLines = GL_RenderDrawLines;
-    renderer->RenderFillRects = GL_RenderFillRects;
-    renderer->RenderCopy = GL_RenderCopy;
-    renderer->RenderCopyEx = GL_RenderCopyEx;
+    renderer->QueueDrawPoints = GL_QueueDrawPoints;
+    renderer->QueueDrawLines = GL_QueueDrawPoints;  /* lines and points queue vertices the same way. */
+    renderer->QueueFillRects = GL_QueueFillRects;
+    renderer->QueueCopy = GL_QueueCopy;
+    renderer->QueueCopyEx = GL_QueueCopyEx;
+    renderer->RunCommandQueue = GL_RunCommandQueue;
     renderer->RenderReadPixels = GL_RenderReadPixels;
     renderer->RenderPresent = GL_RenderPresent;
     renderer->DestroyTexture = GL_DestroyTexture;
@@ -501,11 +456,13 @@ GL_CreateRenderer(SDL_Window * window, Uint32 flags)
         data->glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB);
     }
 
+    data->textype = GL_TEXTURE_2D;
     if (SDL_GL_ExtensionSupported("GL_ARB_texture_non_power_of_two")) {
         data->GL_ARB_texture_non_power_of_two_supported = SDL_TRUE;
     } else if (SDL_GL_ExtensionSupported("GL_ARB_texture_rectangle") ||
                SDL_GL_ExtensionSupported("GL_EXT_texture_rectangle")) {
         data->GL_ARB_texture_rectangle_supported = SDL_TRUE;
+        data->textype = GL_TEXTURE_RECTANGLE_ARB;
     }
     if (data->GL_ARB_texture_rectangle_supported) {
         data->glGetIntegerv(GL_MAX_RECTANGLE_TEXTURE_SIZE_ARB, &value);
@@ -562,7 +519,10 @@ GL_CreateRenderer(SDL_Window * window, Uint32 flags)
     data->framebuffers = NULL;
 
     /* Set up parameters for rendering */
-    GL_ResetState(renderer);
+    data->glDisable(GL_DEPTH_TEST);
+    data->glDisable(GL_CULL_FACE);
+    /* This ended up causing video discrepancies between OpenGL and Direct3D */
+    /* data->glEnable(GL_LINE_SMOOTH); */
 
     return renderer;
 
@@ -577,17 +537,6 @@ error:
     return NULL;
 }
 
-static void
-GL_WindowEvent(SDL_Renderer * renderer, const SDL_WindowEvent *event)
-{
-    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;
-    }
-}
-
 static int
 GL_GetOutputSize(SDL_Renderer * renderer, int *w, int *h)
 {
@@ -707,6 +656,7 @@ static int
 GL_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture)
 {
     GL_RenderData *renderdata = (GL_RenderData *) renderer->driverdata;
+    const GLenum textype = renderdata->textype;
     GL_TextureData *data;
     GLint internalFormat;
     GLenum format, type;
@@ -770,19 +720,16 @@ GL_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture)
     texture->driverdata = data;
 
     if (renderdata->GL_ARB_texture_non_power_of_two_supported) {
-        data->type = GL_TEXTURE_2D;
         texture_w = texture->w;
         texture_h = texture->h;
         data->texw = 1.0f;
         data->texh = 1.0f;
     } else if (renderdata->GL_ARB_texture_rectangle_supported) {
-        data->type = GL_TEXTURE_RECTANGLE_ARB;
         texture_w = texture->w;
         texture_h = texture->h;
         data->texw = (GLfloat) texture_w;
         data->texh = (GLfloat) texture_h;
     } else {
-        data->type = GL_TEXTURE_2D;
         texture_w = power_of_2(texture->w);
         texture_h = power_of_2(texture->h);
         data->texw = (GLfloat) (texture->w) / texture_w;
@@ -792,17 +739,17 @@ GL_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture)
     data->format = format;
     data->formattype = type;
     scaleMode = (texture->scaleMode == SDL_ScaleModeNearest) ? GL_NEAREST : GL_LINEAR;
-    renderdata->glEnable(data->type);
-    renderdata->glBindTexture(data->type, data->texture);
-    renderdata->glTexParameteri(data->type, GL_TEXTURE_MIN_FILTER, scaleMode);
-    renderdata->glTexParameteri(data->type, GL_TEXTURE_MAG_FILTER, scaleMode);
+    renderdata->glEnable(textype);
+    renderdata->glBindTexture(textype, data->texture);
+    renderdata->glTexParameteri(textype, GL_TEXTURE_MIN_FILTER, scaleMode);
+    renderdata->glTexParameteri(textype, GL_TEXTURE_MAG_FILTER, scaleMode);
     /* According to the spec, CLAMP_TO_EDGE is the default for TEXTURE_RECTANGLE
        and setting it causes an INVALID_ENUM error in the latest NVidia drivers.
     */
-    if (data->type != GL_TEXTURE_RECTANGLE_ARB) {
-        renderdata->glTexParameteri(data->type, GL_TEXTURE_WRAP_S,
+    if (textype != GL_TEXTURE_RECTANGLE_ARB) {
+        renderdata->glTexParameteri(textype, GL_TEXTURE_WRAP_S,
                                     GL_CLAMP_TO_EDGE);
-        renderdata->glTexParameteri(data->type, GL_TEXTURE_WRAP_T,
+        renderdata->glTexParameteri(textype, GL_TEXTURE_WRAP_T,
                                     GL_CLAMP_TO_EDGE);
     }
 #ifdef __MACOSX__
@@ -816,10 +763,10 @@ GL_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture)
 #define STORAGE_SHARED_APPLE                0x85BF
 #endif
     if (texture->access == SDL_TEXTUREACCESS_STREAMING) {
-        renderdata->glTexParameteri(data->type, GL_TEXTURE_STORAGE_HINT_APPLE,
+        renderdata->glTexParameteri(textype, GL_TEXTURE_STORAGE_HINT_APPLE,
                                     GL_STORAGE_SHARED_APPLE);
     } else {
-        renderdata->glTexParameteri(data->type, GL_TEXTURE_STORAGE_HINT_APPLE,
+        renderdata->glTexParameteri(textype, GL_TEXTURE_STORAGE_HINT_APPLE,
                                     GL_STORAGE_CACHED_APPLE);
     }
     if (texture->access == SDL_TEXTUREACCESS_STREAMING
@@ -829,17 +776,17 @@ GL_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture)
         renderdata->glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
         renderdata->glPixelStorei(GL_UNPACK_ROW_LENGTH,
                           (data->pitch / SDL_BYTESPERPIXEL(texture->format)));
-        renderdata->glTexImage2D(data->type, 0, internalFormat, texture_w,
+        renderdata->glTexImage2D(textype, 0, internalFormat, texture_w,
                                  texture_h, 0, format, type, data->pixels);
         renderdata->glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
     }
     else
 #endif
     {
-        renderdata->glTexImage2D(data->type, 0, internalFormat, texture_w,
+        renderdata->glTexImage2D(textype, 0, internalFormat, texture_w,
                                  texture_h, 0, format, type, NULL);
     }
-    renderdata->glDisable(data->type);
+    renderdata->glDisable(textype);
     if (GL_CheckError("glTexImage2D()", renderer) < 0) {
         return -1;
     }
@@ -850,33 +797,33 @@ GL_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture)
 
         renderdata->glGenTextures(1, &data->utexture);
         renderdata->glGenTextures(1, &data->vtexture);
-        renderdata->glEnable(data->type);
+        renderdata->glEnable(textype);
 
-        renderdata->glBindTexture(data->type, data->utexture);
-        renderdata->glTexParameteri(data->type, GL_TEXTURE_MIN_FILTER,
+        renderdata->glBindTexture(textype, data->utexture);
+        renderdata->glTexParameteri(textype, GL_TEXTURE_MIN_FILTER,
                                     scaleMode);
-        renderdata->glTexParameteri(data->type, GL_TEXTURE_MAG_FILTER,
+        renderdata->glTexParameteri(textype, GL_TEXTURE_MAG_FILTER,
                                     scaleMode);
-        renderdata->glTexParameteri(data->type, GL_TEXTURE_WRAP_S,
+        renderdata->glTexParameteri(textype, GL_TEXTURE_WRAP_S,
                                     GL_CLAMP_TO_EDGE);
-        renderdata->glTexParameteri(data->type, GL_TEXTURE_WRAP_T,
+        renderdata->glTexParameteri(textype, GL_TEXTURE_WRAP_T,
                                     GL_CLAMP_TO_EDGE);
-        renderdata->glTexImage2D(data->type, 0, internalFormat, (texture_w+1)/2,
+        renderdata->glTexImage2D(textype, 0, internalFormat, (texture_w+1)/2,
                                  (texture_h+1)/2, 0, format, type, NULL);
 
-        renderdata->glBindTexture(data->type, data->vtexture);
-        renderdata->glTexParameteri(data->type, GL_TEXTURE_MIN_FILTER,
+        renderdata->glBindTexture(textype, data->vtexture);
+        renderdata->glTexParameteri(textype, GL_TEXTURE_MIN_FILTER,
                                     scaleMode);
-        renderdata->glTexParameteri(data->type, GL_TEXTURE_MAG_FILTER,
+        renderdata->glTexParameteri(textype, GL_TEXTURE_MAG_FILTER,
                                     scaleMode);
-        renderdata->glTexParameteri(data->type, GL_TEXTURE_WRAP_S,
+        renderdata->glTexParameteri(textype, GL_TEXTURE_WRAP_S,
                                     GL_CLAMP_TO_EDGE);
-        renderdata->glTexParameteri(data->type, GL_TEXTURE_WRAP_T,
+        renderdata->glTexParameteri(textype, GL_TEXTURE_WRAP_T,
                                     GL_CLAMP_TO_EDGE);
-        renderdata->glTexImage2D(data->type, 0, internalFormat, (texture_w+1)/2,
+        renderdata->glTexImage2D(textype, 0, internalFormat, (texture_w+1)/2,
                                  (texture_h+1)/2, 0, format, type, NULL);
 
-        renderdata->glDisable(data->type);
+        renderdata->glDisable(textype);
     }
 
     if (texture->format == SDL_PIXELFORMAT_NV12 ||
@@ -884,20 +831,20 @@ GL_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture)
         data->nv12 = SDL_TRUE;
 
         renderdata->glGenTextures(1, &data->utexture);
-        renderdata->glEnable(data->type);
+        renderdata->glEnable(textype);
 
-        renderdata->glBindTexture(data->type, data->utexture);
-        renderdata->glTexParameteri(data->type, GL_TEXTURE_MIN_FILTER,
+        renderdata->glBindTexture(textype, data->utexture);
+        renderdata->glTexParameteri(textype, GL_TEXTURE_MIN_FILTER,
                                     scaleMode);
-        renderdata->glTexParameteri(data->type, GL_TEXTURE_MAG_FILTER,
+        renderdata->glTexParameteri(textype, GL_TEXTURE_MAG_FILTER,
                                     scaleMode);
-        renderdata->glTexParameteri(data->type, GL_TEXTURE_WRAP_S,
+        renderdata->glTexParameteri(textype, GL_TEXTURE_WRAP_S,
                                     GL_CLAMP_TO_EDGE);
-        renderdata->glTexParameteri(data->type, GL_TEXTURE_WRAP_T,
+        renderdata->glTexParameteri(textype, GL_TEXTURE_WRAP_T,
                                     GL_CLAMP_TO_EDGE);
-        renderdata->glTexImage2D(data->type, 0, GL_LUMINANCE_ALPHA, (texture_w+1)/2,
+        renderdata->glTexImage2D(textype, 0, GL_LUMINANCE_ALPHA, (texture_w+1)/2,
                                  (texture_h+1)/2, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, NULL);
-        renderdata->glDisable(data->type);
+        renderdata->glDisable(textype);
     }
 
     return GL_CheckError("", renderer);
@@ -908,6 +855,7 @@ GL_UpdateTexture(SDL_Renderer * renderer, SDL_Texture * texture,
                  const SDL_Rect * rect, const void *pixels, int pitch)
 {
     GL_RenderData *renderdata = (GL_RenderData *) renderer->driverdata;
+    const GLenum textype = renderdata->textype;
     GL_TextureData *data = (GL_TextureData *) texture->driverdata;
     const int texturebpp = SDL_BYTESPERPIXEL(texture->format);
 
@@ -915,11 +863,11 @@ GL_UpdateTexture(SDL_Renderer * renderer, SDL_Texture * texture,
 
     GL_ActivateRenderer(renderer);
 
-    renderdata->glEnable(data->type);
-    renderdata->glBindTexture(data->type, data->texture);
+    renderdata->glEnable(textype);
+    renderdata->glBindTexture(textype, data->texture);
     renderdata->glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
     renderdata->glPixelStorei(GL_UNPACK_ROW_LENGTH, (pitch / texturebpp));
-    renderdata->glTexSubImage2D(data->type, 0, rect->x, rect->y, rect->w,
+    renderdata->glTexSubImage2D(textype, 0, rect->x, rect->y, rect->w,
                                 rect->h, data->format, data->formattype,
                                 pixels);
     if (data->yuv) {
@@ -928,22 +876,22 @@ GL_UpdateTexture(SDL_Renderer * renderer, SDL_Texture * texture,
         /* Skip to the correct offset into the next texture */
         pixels = (const void*)((const Uint8*)pixels + rect->h * pitch);
         if (texture->format == SDL_PIXELFORMAT_YV12) {
-            renderdata->glBindTexture(data->type, data->vtexture);
+            renderdata->glBindTexture(textype, data->vtexture);
         } else {
-            renderdata->glBindTexture(data->type, data->utexture);
+            renderdata->glBindTexture(textype, data->utexture);
         }
-        renderdata->glTexSubImage2D(data->type, 0, rect->x/2, rect->y/2,
+        renderdata->glTexSubImage2D(textype, 0, rect->x/2, rect->y/2,
                                     (rect->w+1)/2, (rect->h+1)/2,
                                     data->format, data->formattype, pixels);
 
         /* Skip to the correct offset into the next texture */
         pixels = (const void*)((const Uint8*)pixels + ((rect->h + 1) / 2) * ((pitch + 1) / 2));
         if (texture->format == SDL_PIXELFORMAT_YV12) {
-            renderdata->glBindTexture(data->type, data->utexture);
+            renderdata->glBindTexture(textype, data->utexture);
         } else {
-            renderdata->glBindTexture(data->type, data->vtexture);
+            renderdata->glBindTexture(textype, data->vtexture);
         }
-        renderdata->glTexSubImage2D(data->type, 0, rect->x/2, rect->y/2,
+        renderdata->glTexSubImage2D(textype, 0, rect->x/2, rect->y/2,
                                     (rect->w+1)/2, (rect->h+1)/2,
                                     data->format, data->formattype, pixels);
     }
@@ -953,12 +901,12 @@ GL_UpdateTexture(SDL_Renderer * renderer, SDL_Texture * texture,
 
         /* Skip to the correct offset into the next texture */
         pixels = (const void*)((const Uint8*)pixels + rect->h * pitch);
-        renderdata->glBindTexture(data->type, data->utexture);
-        renderdata->glTexSubImage2D(data->type, 0, rect->x/2, rect->y/2,
+        renderdata->glBindTexture(textype, data->utexture);
+        renderdata->glTexSubImage2D(textype, 0, rect->x/2, rect->y/2,
                                     (rect->w + 1)/2, (rect->h + 1)/2,
                                     GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, pixels);
     }
-    renderdata->glDisable(data->type);
+    renderdata->glDisable(textype);
 
     return GL_CheckError("glTexSubImage2D()", renderer);
 }
@@ -971,30 +919,31 @@ GL_UpdateTextureYUV(SDL_Renderer * renderer, SDL_Texture * texture,
                     const Uint8 *Vplane, int Vpitch)
 {
     GL_RenderData *renderdata = (GL_RenderData *) renderer->driverdata;
+    const GLenum textype = renderdata->textype;
     GL_TextureData *data = (GL_TextureData *) texture->driverdata;
 
     GL_ActivateRenderer(renderer);
 
-    renderdata->glEnable(data->type);
-    renderdata->glBindTexture(data->type, data->texture);
+    renderdata->glEnable(textype);
+    renderdata->glBindTexture(textype, data->texture);
     renderdata->glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
     renderdata->glPixelStorei(GL_UNPACK_ROW_LENGTH, Ypitch);
-    renderdata->glTexSubImage2D(data->type, 0, rect->x, rect->y, rect->w,
+    renderdata->glTexSubImage2D(textype, 0, rect->x, rect->y, rect->w,
                                 rect->h, data->format, data->formattype,
                                 Yplane);
 
     renderdata->glPixelStorei(GL_UNPACK_ROW_LENGTH, Upitch);
-    renderdata->glBindTexture(data->type, data->utexture);
-    renderdata->glTexSubImage2D(data->type, 0, rect->x/2, rect->y/2,
+    renderdata->glBindTexture(textype, data->utexture);
+    renderdata->glTexSubImage2D(textype, 0, rect->x/2, rect->y/2,
                                 (rect->w + 1)/2, (rect->h + 1)/2,
                                 data->format, data->formattype, Uplane);
 
     renderdata->glPixelStorei(GL_UNPACK_ROW_LENGTH, Vpitch);
-    renderdata->glBindTexture(data->type, data->vtexture);
-    renderdata->glTexSubImage2D(data->type, 0, rect->x/2, rect->y/2,
+    renderdata->glBindTexture(textype, data->vtexture);
+    renderdata->glTexSubImage2D(textype, 0, rect->x/2, rect->y/2,
                                 (rect->w + 1)/2, (rect->h + 1)/2,
                                 data->format, data->formattype, Vplane);
-    renderdata->glDisable(data->type);
+    renderdata->glDisable(textype);
 
     return GL_CheckError("glTexSubImage2D()", renderer);
 }
@@ -1048,7 +997,7 @@ GL_SetRenderTarget(SDL_Renderer * renderer, SDL_Texture * texture)
     texturedata = (GL_TextureData *) texture->driverdata;
     data->glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, texturedata->fbo->FBO);
     /* TODO: check if texture pixel format allows this operation */
-    data->glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, texturedata->type, texturedata->texture, 0);
+    data->glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, data->textype, texturedata->texture, 0);
     /* Check FBO status */
     status = data->glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
     if (status != GL_FRAMEBUFFER_COMPLETE_EXT) {
@@ -1057,337 +1006,65 @@ GL_SetRenderTarget(SDL_Renderer * renderer, SDL_Texture * texture)
     return 0;
 }
 
+/* !!! FIXME: all these Queue* calls set up the vertex buffer the way the immediate mode
+   !!! FIXME:  renderer wants it, but this might want to operate differently if we move to
+   !!! FIXME:  VBOs at some point. */
 static int
-GL_UpdateViewport(SDL_Renderer * renderer)
-{
-    GL_RenderData *data = (GL_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->glOrtho((GLdouble) 0,
-                          (GLdouble) renderer->viewport.w,
-                          (GLdouble) 0,
-                          (GLdouble) renderer->viewport.h,
-                           0.0, 1.0);
-        } else {
-            data->glOrtho((GLdouble) 0,
-                          (GLdouble) renderer->viewport.w,
-                          (GLdouble) renderer->viewport.h,
-                          (GLdouble) 0,
-                           0.0, 1.0);
-        }
-    }
-    data->glMatrixMode(GL_MODELVIEW);
-
-    return GL_CheckError("", renderer);
-}
-
-static int
-GL_UpdateClipRect(SDL_Renderer * renderer)
-{
-    GL_RenderData *data = (GL_RenderData *) renderer->driverdata;
-
-    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
-GL_SetShader(GL_RenderData * data, GL_Shader shader)
-{
-    if (data->shaders && shader != data->current.shader) {
-        GL_SelectShader(data->shaders, shader);
-        data->current.shader = shader;
-    }
-}
-
-static void
-GL_SetColor(GL_RenderData * data, Uint8 r, Uint8 g, Uint8 b, Uint8 a)
+GL_QueueDrawPoints(SDL_Renderer * renderer, SDL_RenderCommand *cmd, const SDL_FPoint * points, int count)
 {
-    Uint32 color = ((a << 24) | (r << 16) | (g << 8) | b);
+    GLfloat *verts = (GLfloat *) SDL_AllocateRenderVertices(renderer, count * 2 * sizeof (GLfloat), &cmd->data.draw.first);
+    size_t i;
 
-    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
-GL_SetBlendMode(GL_RenderData * data, SDL_BlendMode blendMode)
-{
-    if (blendMode != data->current.blendMode) {
-        if (blendMode == SDL_BLENDMODE_NONE) {
-            data->glDisable(GL_BLEND);
-        } else {
-            data->glEnable(GL_BLEND);
-            data->glBlendFuncSeparate(GetBlendFunc(SDL_GetBlendModeSrcColorFactor(blendMode)),
-                                      GetBlendFunc(SDL_GetBlendModeDstColorFactor(blendMode)),
-                                      GetBlendFunc(SDL_GetBlendModeSrcAlphaFactor(blendMode)),
-                                      GetBlendFunc(SDL_GetBlendModeDstAlphaFactor(blendMode)));
-            data->glBlendEquation(GetBlendEquation(SDL_GetBlendModeColorOperation(blendMode)));
-        }
-        data->current.blendMode = blendMode;
-    }
-}
-
-static void
-GL_SetDrawingState(SDL_Renderer * renderer)
-{
-    GL_RenderData *data = (GL_RenderData *) renderer->driverdata;
-
-    GL_ActivateRenderer(renderer);
-
-    GL_SetColor(data, renderer->r,
-                      renderer->g,
-                      renderer->b,
-                      renderer->a);
-
-    GL_SetBlendMode(data, renderer->blendMode);
-
-    GL_SetShader(data, SHADER_SOLID);
-}
-
-static int
-GL_RenderClear(SDL_Renderer * renderer)
-{
-    GL_RenderData *data = (GL_RenderData *) renderer->driverdata;
-
-    GL_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);
+    if (!verts) {
+        return -1;
     }
 
-    return 0;
-}
-
-static int
-GL_RenderDrawPoints(SDL_Renderer * renderer, const SDL_FPoint * points,
-                    int count)
-{
-    GL_RenderData *data = (GL_RenderData *) renderer->driverdata;
-    int i;
-
-    GL_SetDrawingState(renderer);
-
-    data->glBegin(GL_POINTS);
-    for (i = 0; i < count; ++i) {
-        data->glVertex2f(0.5f + points[i].x, 0.5f + points[i].y);
+    cmd->data.draw.count = count;
+    for (i = 0; i < count; i++) {
+        *(verts++) = 0.5f + points[i].x;
+        *(verts++) = 0.5f + points[i].y;
     }
-    data->glEnd();
 
     return 0;
 }
 
 static int
-GL_RenderDrawLines(SDL_Renderer * renderer, const SDL_FPoint * points,
-                   int count)
+GL_QueueFillRects(SDL_Renderer * renderer, SDL_RenderCommand *cmd, const SDL_FRect * rects, int count)
 {
-    GL_RenderData *data = (GL_RenderData *) renderer->driverdata;
-    int i;
-
-    GL_SetDrawingState(renderer);
-
-    if (count > 2 &&
-        points[0].x == points[count-1].x && points[0].y == points[count-1].y) {
-        data->glBegin(GL_LINE_LOOP);
-        /* GL_LINE_LOOP takes care of the final segment */
-        --count;
-        for (i = 0; i < count; ++i) {
-            data->glVertex2f(0.5f + points[i].x, 0.5f + points[i].y);
-        }
-        data->glEnd();
-    } else {
-#if defined(__MACOSX__) || defined(__WIN32__)
-#else
-        int x1, y1, x2, y2;
-#endif
+    GLfloat *verts = (GLfloat *) SDL_AllocateRenderVertices(renderer, count * 4 * sizeof (GLfloat), &cmd->data.draw.first);
+    size_t i;
 
-        data->glBegin(GL_LINE_STRIP);
-        for (i = 0; i < count; ++i) {
-            data->glVertex2f(0.5f + points[i].x, 0.5f + points[i].y);
-        }
-        data->glEnd();
-
-        /* The line is half open, so we need one more point to complete it.
-         * http://www.opengl.org/documentation/specs/version1.1/glspec1.1/node47.html
-         * If we have to, we can use vertical line and horizontal line textures
-         * for vertical and horizontal lines, and then create custom textures
-         * for diagonal lines and software render those.  It's terrible, but at
-         * least it would be pixel perfect.
-         */
-        data->glBegin(GL_POINTS);
-#if defined(__MACOSX__) || defined(__WIN32__)
-        /* Mac OS X and Windows seem to always leave the last point open */
-        data->glVertex2f(0.5f + points[count-1].x, 0.5f + points[count-1].y);
-#else
-        /* Linux seems to leave the right-most or bottom-most point open */
-        x1 = points[0].x;
-        y1 = points[0].y;
-        x2 = points[count-1].x;
-        y2 = points[count-1].y;
-
-        if (x1 > x2) {
-            data->glVertex2f(0.5f + x1, 0.5f + y1);
-        } else if (x2 > x1) {
-            data->glVertex2f(0.5f + x2, 0.5f + y2);
-        }
-        if (y1 > y2) {
-            data->glVertex2f(0.5f + x1, 0.5f + y1);
-        } else if (y2 > y1) {
-            data->glVertex2f(0.5f + x2, 0.5f + y2);
-        }
-#endif
-        data->glEnd();
+    if (!verts) {
+        return -1;
     }
-    return GL_CheckError("", renderer);
-}
-
-static int
-GL_RenderFillRects(SDL_Renderer * renderer, const SDL_FRect * rects, int count)
-{
-    GL_RenderData *data = (GL_RenderData *) renderer->driverdata;
-    int i;
 
-    GL_SetDrawingState(renderer);
-
-    for (i = 0; i < count; ++i) {
+    cmd->data.draw.count = count;
+    for (i = 0; i < count; i++) {
         const SDL_FRect *rect = &rects[i];
-
-        data->glRectf(rect->x, rect->y, rect->x + rect->w, rect->y + rect->h);
+        *(verts++) = rect->x;
+        *(verts++) = rect->y;
+        *(verts++) = rect->x + rect->w;
+        *(verts++) = rect->y + rect->h;
     }
-    return GL_CheckError("", renderer);
-}
 
-static int
-GL_SetupCopy(SDL_Renderer * renderer, SDL_Texture * texture)
-{
-    GL_RenderData *data = (GL_RenderData *) renderer->driverdata;
-    GL_TextureData *texturedata = (GL_TextureData *) texture->driverdata;
-
-    data->glEnable(texturedata->type);
-    if (texturedata->yuv) {
-        data->glActiveTextureARB(GL_TEXTURE2_ARB);
-        data->glBindTexture(texturedata->type, texturedata->vtexture);
-
-        data->glActiveTextureARB(GL_TEXTURE1_ARB);
-        data->glBindTexture(texturedata->type, texturedata->utexture);
-
-        data->glActiveTextureARB(GL_TEXTURE0_ARB);
-    }
-    if (texturedata->nv12) {
-        data->glActiveTextureARB(GL_TEXTURE1_ARB);
-        data->glBindTexture(texturedata->type, texturedata->utexture);
-
-        data->glActiveTextureARB(GL_TEXTURE0_ARB);
-    }
-    data->glBindTexture(texturedata->type, texturedata->texture);
-
-    if (texture->modMode) {
-        GL_SetColor(data, texture->r, texture->g, texture->b, texture->a);
-    } else {
-        GL_SetColor(data, 255, 255, 255, 255);
-    }
-
-    GL_SetBlendMode(data, texture->blendMode);
-
-    if (texturedata->yuv || texturedata->nv12) {
-        switch (SDL_GetYUVConversionModeForResolution(texture->w, texture->h)) {
-        case SDL_YUV_CONVERSION_JPEG:
-            if (texturedata->yuv) {
-                GL_SetShader(data, SHADER_YUV_JPEG);
-            } else if (texture->format == SDL_PIXELFORMAT_NV12) {
-                GL_SetShader(data, SHADER_NV12_JPEG);
-            } else {
-                GL_SetShader(data, SHADER_NV21_JPEG);
-            }
-            break;
-        case SDL_YUV_CONVERSION_BT601:
-            if (texturedata->yuv) {
-                GL_SetShader(data, SHADER_YUV_BT601);
-            } else if (texture->format == SDL_PIXELFORMAT_NV12) {
-                GL_SetShader(data, SHADER_NV12_BT601);
-            } else {
-                GL_SetShader(data, SHADER_NV21_BT601);
-            }
-            break;
-        case SDL_YUV_CONVERSION_BT709:
-            if (texturedata->yuv) {
-                GL_SetShader(data, SHADER_YUV_BT709);
-            } else if (texture->format == SDL_PIXELFORMAT_NV12) {
-                GL_SetShader(data, SHADER_NV12_BT709);
-            } else {
-                GL_SetShader(data, SHADER_NV21_BT709);
-            }
-            break;
-        default:
-            return SDL_SetError("Unsupported YUV conversion mode");
-        }
-    } else {
-        GL_SetShader(data, SHADER_RGB);
-    }
     return 0;
 }
 
 static int
-GL_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture,
-              const SDL_Rect * srcrect, const SDL_FRect * dstrect)
+GL_QueueCopy(SDL_Renderer * renderer, SDL_RenderCommand *cmd, SDL_Texture * texture,
+             const SDL_Rect * srcrect, const SDL_FRect * dstrect)
 {
-    GL_RenderData *data = (GL_RenderData *) renderer->driverdata;
     GL_TextureData *texturedata = (GL_TextureData *) texture->driverdata;
     GLfloat minx, miny, maxx, maxy;
     GLfloat minu, maxu, minv, maxv;
+    GLfloat *verts = (GLfloat *) SDL_AllocateRenderVertices(renderer, 8 * sizeof (GLfloat), &cmd->data.draw.first);
 
-    GL_ActivateRenderer(renderer);
-
-    if (GL_SetupCopy(renderer, texture) < 0) {
+    if (!verts) {
         return -1;
     }
 
+    cmd->data.draw.count = 1;
+
     minx = dstrect->x;
     miny = dstrect->y;
     maxx = dstrect->x + dstrect->w;
@@ -1402,36 +1079,30 @@ GL_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture,
     maxv = (GLfloat) (srcrect->y + srcrect->h) / texture->h;
     maxv *= texturedata->texh;
 
-    data->glBegin(GL_TRIANGLE_STRIP);
-    data->glTexCoord2f(minu, minv);
-    data->glVertex2f(minx, miny);
-    data->glTexCoord2f(maxu, minv);
-    data->glVertex2f(maxx, miny);
-    data->glTexCoord2f(minu, maxv);
-    data->glVertex2f(minx, maxy);
-    data->glTexCoord2f(maxu, maxv);
-    data->glVertex2f(maxx, maxy);
-    data->glEnd();
-
-    data->glDisable(texturedata->type);
-
-    return GL_CheckError("", renderer);
+    cmd->data.draw.count = 1;
+    *(verts++) = minx;
+    *(verts++) = miny;
+    *(verts++) = maxx;
+    *(verts++) = maxy;
+    *(verts++) = minu;
+    *(verts++) = maxu;
+    *(verts++) = minv;
+    *(verts++) = maxv;
+    return 0;
 }
 
 static int
-GL_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)
+GL_QueueCopyEx(SDL_Renderer * renderer, SDL_RenderCommand *cmd, SDL_Texture * texture,
+               const SDL_Rect * srcrect, const SDL_FRect * dstrect,
+               const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip)
 {
-    GL_RenderData *data = (GL_RenderData *) renderer->driverdata;
     GL_TextureData *texturedata = (GL_TextureData *) texture->driverdata;
     GLfloat minx, miny, maxx, maxy;
     GLfloat centerx, centery;
     GLfloat minu, maxu, minv, maxv;
+    GLfloat *verts = (GLfloat *) SDL_AllocateRenderVertices(renderer, 11 * sizeof (GLfloat), &cmd->data.draw.first);
 
-    GL_ActivateRenderer(renderer);
-
-    if (GL_SetupCopy(renderer, texture) < 0) {
+    if (!verts) {
         return -1;
     }
 
@@ -1465,24 +1136,415 @@ GL_RenderCopyEx(SDL_Renderer * renderer, SDL_Texture * texture,
     maxv = (GLfloat) (srcrect->y + srcrect->h) / texture->h;
     maxv *= texturedata->texh;
 
-    /* Translate to flip, rotate, translate to position */
-    data->glPushMatrix();
-    data->glTranslatef((GLfloat)dstrect->x + centerx, (GLfloat)dstrect->y + centery, (GLfloat)0.0);
-    data->glRotated(angle, (GLdouble)0.0, (GLdouble)0.0, (GLdouble)1.0);
-
-    data->glBegin(GL_TRIANGLE_STRIP);
-    data->glTexCoord2f(minu, minv);
-    data->glVertex2f(minx, miny);
-    data->glTexCoord2f(maxu, minv);
-    data->glVertex2f(maxx, miny);
-    data->glTexCoord2f(minu, maxv);
-    data->glVertex2f(minx, maxy);
-    data->glTexCoord2f(maxu, maxv);
-    data->glVertex2f(maxx, maxy);
-    data->glEnd();
-    data->glPopMatrix();
-
-    data->glDisable(texturedata->type);
+    cmd->data.draw.count = 1;
+    *(verts++) = minx;
+    *(verts++) = miny;
+    *(verts++) = maxx;
+    *(verts++) = maxy;
+    *(verts++) = minu;
+    *(verts++) = maxu;
+    *(verts++) = minv;
+    *(verts++) = maxv;
+    *(verts++) = (GLfloat) dstrect->x + centerx;
+    *(verts++) = (GLfloat) dstrect->y + centery;
+    *(verts++) = (GLfloat) angle;
+    return 0;
+}
+
+static void
+SetDrawState(const GL_RenderData *data, const SDL_RenderCommand *cmd, const GL_Shader shader,
+             Uint32 *current_color, SDL_BlendMode *current_blend, GL_Shader *current_shader,
+             SDL_bool *current_texturing)
+{
+    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);
+    const SDL_BlendMode blend = cmd->data.draw.blend;
+
+    if (color != *current_color) {
+        data->glColor4f((GLfloat) r * inv255f, (GLfloat) g * inv255f,
+                        (GLfloat) b * inv255f, (GLfloat) a * inv255f);
+        *current_color = color;
+    }
+
+    if (blend != *current_blend) {
+        if (blend == SDL_BLENDMODE_NONE) {
+            data->glDisable(GL_BLEND);
+        } else {
+            data->glEnable(GL_BLEND);
+            data->glBlendFuncSeparate(GetBlendFunc(SDL_GetBlendModeSrcColorFactor(blend)),
+                                      GetBlendFunc(SDL_GetBlendModeDstColorFactor(blend)),
+                                      GetBlendFunc(SDL_GetBlendModeSrcAlphaFactor(blend)),
+                                      GetBlendFunc(SDL_GetBlendModeDstAlphaFactor(blend)));
+            data->glBlendEquation(GetBlendEquation(SDL_GetBlendModeColorOperation(blend)));
+        }
+        *current_blend = blend;
+    }
+
+    if (data->shaders && (shader != *current_shader)) {
+        GL_SelectShader(data->shaders, shader);
+        *current_shader = shader;
+    }
+
+    if ((cmd->data.draw.texture != NULL) != *current_texturing) {
+        if (cmd->data.draw.texture == NULL) {
+            data->glDisable(data->textype);
+            *current_texturing = SDL_FALSE;
+        } else {
+            data->glEnable(data->textype);
+            *current_texturing = SDL_FALSE;
+        }
+    }
+}
+
+static void
+SetCopyState(const GL_RenderData *data, const SDL_RenderCommand *cmd,
+             Uint32 *current_color, SDL_BlendMode *current_blend, GL_Shader *current_shader,
+             SDL_bool *current_texturing, SDL_Texture **current_texture)
+{
+    SDL_Texture *texture = cmd->data.draw.texture;
+    const GL_TextureData *texturedata = (GL_TextureData *) texture->driverdata;
+    GL_Shader shader = SHADER_RGB;
+
+    if (data->shaders) {
+        if (texturedata->yuv || texturedata->nv12) {
+            switch (SDL_GetYUVConversionModeForResolution(texture->w, texture->h)) {
+                case SDL_YUV_CONVERSION_JPEG:
+                    if (texturedata->yuv) {
+                        shader = SHADER_YUV_JPEG;
+                    } else if (texture->format == SDL_PIXELFORMAT_NV12) {
+                        shader = SHADER_NV12_JPEG;
+                    } else {
+                        shader = SHADER_NV21_JPEG;
+                    }
+                    break;
+                case SDL_YUV_CONVERSION_BT601:
+                    if (texturedata->yuv) {
+                        shader = SHADER_YUV_BT601;
+                    } else if (texture->format == SDL_PIXELFORMAT_NV12) {
+                        shader = SHADER_NV12_BT601;
+                    } else {
+                        shader = SHADER_NV21_BT601;
+                    }
+                    break;
+                case SDL_YUV_CONVERSION_BT709:
+                    if (texturedata->yuv) {
+                        shader = SHADER_YUV_BT709;
+                    } else if (texture->format == SDL_PIXELFORMAT_NV12) {
+                        shader = SHADER_NV12_BT709;
+                    } else {
+                        shader = SHADER_NV21_BT709;
+                    }
+                    break;
+                default:
+                    SDL_assert(!"unsupported YUV conversion mode");
+                    break;
+            }
+        }
+    }
+
+    SetDrawState(data, cmd, shader, current_color, current_blend, current_shader, current_texturing);
+
+    if (texture != *current_texture) {
+        const GLenum textype = data->textype;
+        if (texturedata->yuv) {
+            data->glActiveTextureARB(GL_TEXTURE2_ARB);
+            data->glBindTexture(textype, texturedata->vtexture);
+
+            data->glActiveTextureARB(GL_TEXTURE1_ARB);
+            data->glBindTexture(textype, texturedata->utexture);
+        }
+        if (texturedata->nv12) {
+            data->glActiveTextureARB(GL_TEXTURE1_ARB);
+            data->glBindTexture(textype, texturedata->utexture);
+        }
+        data->glActiveTextureARB(GL_TEXTURE0_ARB);
+        data->glBindTexture(textype, texturedata->texture);
+
+        *current_texture = texture;
+    }
+}
+
+static int
+GL_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *vertices, size_t vertsize)
+{
+    /* !!! FIXME: it'd be nice to use a vertex buffer instead of immediate mode... */
+    GL_RenderData *data = (GL_RenderData *) renderer->driverdata;
+    SDL_bool clipping_enabled = renderer->clipping_enabled;
+    SDL_Rect viewport;
+    SDL_Texture *bound_texture = NULL;
+    SDL_BlendMode blend = SDL_BLENDMODE_INVALID;
+    GL_Shader shader = SHADER_INVALID;
+    int drawablew = 0, drawableh = 0;
+    SDL_bool cliprect_enabled = SDL_FALSE;
+    SDL_Rect cliprect;
+    const SDL_bool istarget = renderer->target != NULL;
+    Uint32 clear_color = ((renderer->a << 24) | (renderer->r << 16) | (renderer->g << 8) | renderer->b);
+    Uint32 draw_color = clear_color;
+    SDL_bool texturing = SDL_FALSE;
+    size_t i;
+
+    /* !!! FIXME: if we could guarantee the app isn't messing with the GL, too, we wouldn't
+       !!! FIXME:  have to default a bunch of this state at the start. */
+
+    if (GL_ActivateRenderer(renderer) < 0) {
+        return -1;
+    }
+
+    if (!istarget) {
+        SDL_GL_GetDrawableSize(renderer->window, &drawablew, &drawableh);
+    }
+
+    data->glClearColor((GLfloat) renderer->r * inv255f,
+                       (GLfloat) renderer->g * inv255f,
+                       (GLfloat) renderer->b * inv255f,
+                       (GLfloat) renderer->a * inv255f);
+
+    data->glColor4f((GLfloat) renderer->r * inv255f,
+                    (GLfloat) renderer->g * inv255f,
+                    (GLfloat) renderer->b * inv255f,
+                    (GLfloat) renderer->a * inv255f);
+
+    SDL_memcpy(&viewport, &renderer->viewport, sizeof (viewport));
+    data->glMatrixMode(GL_PROJECTION);
+    data->glLoadIdentity();
+    data->glViewport(viewport.x,
+            istarget ? viewport.y : (drawableh - viewport.y - viewport.h),
+            viewport.w, viewport.h);
+    if (viewport.w && viewport.h) {
+        data->glOrtho((GLdouble) 0, (GLdouble) renderer->viewport.w,
+                      (GLdouble) istarget ? 0 : renderer->viewport.h,
+                      (GLdouble) istarget ? renderer->viewport.h : 0,
+                      0.0, 1.0);
+    }
+    data->glMatrixMode(GL_MODELVIEW);
+    data->glLoadIdentity();
+
+    SDL_memcpy(&cliprect, &renderer->clip_rect, sizeof (cliprect));
+    cliprect_enabled = renderer->clipping_enabled;
+    if (cliprect_enabled) {
+        data->glEnable(GL_SCISSOR_TEST);
+    } else {
+        data->glDisable(GL_SCISSOR_TEST);
+    }
+
+    data->glDisable(data->textype);
+
+    data->glScissor(viewport.x + cliprect.x,
+        istarget ? viewport.y + cliprect.y : drawableh - viewport.y - cliprect.y - cliprect.h,
+        cliprect.w, cliprect.h);
+
+    while (cmd) {
+        switch (cmd->command) {
+            case SDL_RENDERCMD_SETVIEWPORT: {
+                if (SDL_memcmp(&cmd->data.viewport, &viewport, sizeof (SDL_Rect)) != 0) {
+                    SDL_memcpy(&viewport, &cmd->data.viewport, sizeof (SDL_Rect));
+                    data->glMatrixMode(GL_PROJECTION);
+                    data->glLoadIdentity();
+                    data->glViewport(viewport.x,
+                        istarget ? viewport.y : (drawableh - viewport.y - viewport.h),
+                        viewport.w, viewport.h);
+                    if (viewport.w && viewport.h) {
+                        data->glOrtho((GLdouble) 0, (GLdouble) renderer->viewport.w,
+                                      (GLdouble) istarget ? 0 : renderer->viewport.h,
+                                      (GLdouble) istarget ? renderer->viewport.h : 0,
+                                      0.0, 1.0);
+                    }
+                    data->glMatrixMode(GL_MODELVIEW);
+                }
+                break;
+            }
+
+            case SDL_RENDERCMD_SETCLIPRECT: {
+                const SDL_Rect *rect = &cmd->data.cliprect.rect;
+                const SDL_bool changed = (SDL_memcmp(&cliprect, rect, sizeof (SDL_Rect)) != 0);
+                if (cliprect_enabled != cmd->data.cliprect.enabled) {
+                    cliprect_enabled = cmd->data.cliprect.enabled;
+                    if (cliprect_enabled) {
+                        data->glEnable(GL_SCISSOR_TEST);
+                    } else {
+                        data->glDisable(GL_SCISSOR_TEST);
+                    }
+                }
+
+                if (cliprect_enabled && changed) {
+                    SDL_memcpy(&cliprect, rect, sizeof (SDL_Rect));
+                    data->glScissor(viewport.x + rect->x,
+                                    istarget ? viewport.y + rect->y : drawableh - viewport.y - rect->y - rect->h,
+                                    rect->w, rect->h);
+                }
+                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 != clear_color) {
+                    data->glClearColor((GLfloat) r * inv255f, (GLfloat) g * inv255f,
+                                       (GLfloat) b * inv255f, (GLfloat) a * inv255f);
+                    clear_color = color;
+                }
+
+                if (clipping_enabled) {
+                    data->glDisable(GL_SCISSOR_TEST);
+                }
+
+                data->glClear(GL_COLOR_BUFFER_BIT);
+
+                if (clipping_enabled) {
+                    data->glEnable(GL_SCISSOR_TEST);
+                }
+                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, SHADER_SOLID, &draw_color, &blend, &shader, &texturing);
+                data->glBegin(GL_POINTS);
+                for (i = 0; i < count; i++, verts += 2) {
+                    data->glVertex2f(verts[0], verts[1]);
+                }
+                data->glEnd();
+                break;
+            }
+
+            case SDL_RENDERCMD_DRAW_LINES: {
+                const GLfloat *verts = (GLfloat *) (((Uint8 *) vertices) + cmd->data.draw.first);
+                size_t count = cmd->data.draw.count;
+                SetDrawState(data, cmd, SHADER_SOLID, &draw_color, &blend, &shader, &texturing);
+                if (count > 2 && (verts[0] == verts[(count-1)*2]) && (verts[1] == verts[(count*2)-1])) {
+                    --count;  /* GL_LINE_LOOP takes care of the final segment */
+                    data->glBegin(GL_LINE_LOOP);
+                    for (i = 0; i < count; ++i, verts += 2) {
+                        data->glVertex2f(verts[0], verts[1]);
+                    }
+                    data->glEnd();
+                } else {
+                    #if defined(__MACOSX__) || defined(__WIN32__)
+                    #else
+                    int x1, y1, x2, y2;
+                    #endif
+
+                    data->glBegin(GL_LINE_STRIP);
+                    for (i = 0; i < count; ++i, verts += 2) {
+                        data->glVertex2f(verts[0], verts[1]);
+                    }
+                    data->glEnd();
+
+                    /* The line is half open, so we need one more point to complete it.
+                     * http://www.opengl.org/documentation/specs/version1.1/glspec1.1/node47.html
+                     * If we have to, we can use vertical line and horizontal line textures
+                     * for vertical and horizontal lines, and then create custom textures
+                     * for diagonal lines and software render those.  It's terrible, but at
+                     * least it would be pixel perfect.
+                     */
+
+                    data->glBegin(GL_POINTS);
+                    #if defined(__MACOSX__) || defined(__WIN32__)
+                    /* Mac OS X and Windows seem to always leave the last point open */
+                    data->glVertex2f(verts[(count-1)*2], verts[(count*2)-1]);
+                    #else
+                    /* Linux seems to leave the right-most or bottom-most point open */
+                    x1 = verts[0];
+                    y1 = verts[1];
+                    x2 = verts[(count-1)*2];
+                    y2 = verts[(count*2)-1];
+
+                    if (x1 > x2) {
+                        data->glVertex2f(x1, y1);
+                    } else if (x2 > x1) {
+                        data->glVertex2f(x2, y2);
+                    }
+                    if (y1 > y2) {
+                        data->glVertex2f(x1, y1);
+                    } else if (y2 > y1) {
+                        data->glVertex2f(x2, y2);
+                    }
+                    #endif
+                    data->glEnd();
+                }
+                break;
+            }
+
+            case SDL_RENDERCMD_FILL_RECTS: {
+                const size_t count = cmd->data.draw.count;
+                const GLfloat *verts = (GLfloat *) (((Uint8 *) vertices) + cmd->data.draw.first);
+                SetDrawState(data, cmd, SHADER_SOLID, &draw_color, &blend, &shader, &texturing);
+                for (i = 0; i < count; ++i, verts += 4) {
+                    data->glRectf(verts[0], verts[1], verts[2], verts[3]);
+                }
+                break;
+            }
+
+            case SDL_RENDERCMD_COPY: {
+                const GLfloat *verts = (GLfloat *) (((Uint8 *) vertices) + cmd->data.draw.first);
+                const GLfloat minx = verts[0];
+                const GLfloat miny = verts[1];
+                const GLfloat maxx = verts[2];
+                const GLfloat maxy = verts[3];
+                const GLfloat minu = verts[4];
+                const GLfloat maxu = verts[5];
+                const GLfloat minv = verts[6];
+                const GLfloat maxv = verts[7];
+                SetCopyState(data, cmd, &draw_color, &blend, &shader, &texturing, &bound_texture);
+                data->glBegin(GL_TRIANGLE_STRIP);
+                data->glTexCoord2f(minu, minv);
+                data->glVertex2f(minx, miny);
+                data->glTexCoord2f(maxu, minv);
+                data->glVertex2f(maxx, miny);
+                data->glTexCoord2f(minu, maxv);
+                data->glVertex2f(minx, maxy);
+                data->glTexCoord2f(maxu, maxv);
+                data->glVertex2f(maxx, maxy);
+                data->glEnd();
+                break;
+            }
+
+            case SDL_RENDERCMD_COPY_EX: {
+                const GLfloat *verts = (GLfloat *) (((Uint8 *) vertices) + cmd->data.draw.first);
+                const GLfloat minx = verts[0];
+                const GLfloat miny = verts[1];
+                const GLfloat maxx = verts[2];
+                const GLfloat maxy = verts[3];
+                const GLfloat minu = verts[4];
+                const GLfloat maxu = verts[5];
+                const GLfloat minv = verts[6];
+                const GLfloat maxv = verts[7];
+                const GLfloat translatex = verts[8];
+                const GLfloat translatey = verts[9];
+                const GLdouble angle = verts[10];
+                SetCopyState(data, cmd, &draw_color, &blend, &shader, &texturing, &bound_texture);
+
+                /* Translate to flip, rotate, translate to position */
+                data->glPushMatrix();
+                data->glTranslatef(translatex, translatey, 0.0f);
+                data->glRotated(angle, 0.0, 0.0, 1.0);
+                data->glBegin(GL_TRIANGLE_STRIP);
+                data->glTexCoord2f(minu, minv);
+                data->glVertex2f(minx, miny);
+                data->glTexCoord2f(maxu, minv);
+                data->glVertex2f(maxx, miny);
+                data->glTexCoord2f(minu, maxv);
+                data->glVertex2f(minx, maxy);
+                data->glTexCoord2f(maxu, maxv);
+                data->glVertex2f(maxx, maxy);
+                data->glEnd();
+                data->glPopMatrix();
+                break;
+            }
+
+            case SDL_RENDERCMD_NO_OP:
+                break;
+        }
+
+        cmd = cmd->next;
+    }
 
     return GL_CheckError("", renderer);
 }
@@ -1631,19 +1693,21 @@ GL_BindTexture (SDL_Renderer * renderer, SDL_Texture *texture, float *texw, floa
 {
     GL_RenderData *data = (GL_RenderData *) renderer->driverdata;
     GL_TextureData *texturedata = (GL_TextureData *) texture->driverdata;
+    const GLenum textype = data->textype;
+
     GL_ActivateRenderer(renderer);
 
-    data->glEnable(texturedata->type);
+    data->glEnable(textype);
     if (texturedata->yuv) {
         data->glActiveTextureARB(GL_TEXTURE2_ARB);
-        data->glBindTexture(texturedata->type, texturedata->vtexture);
+        data->glBindTexture(textype, texturedata->vtexture);
 
         data->glActiveTextureARB(GL_TEXTURE1_ARB);
-        data->glBindTexture(texturedata->type, texturedata->utexture);
+        data->glBindTexture(textype, texturedata->utexture);
 
         data->glActiveTextureARB(GL_TEXTURE0_ARB);
     }
-    data->glBindTexture(texturedata->type, texturedata->texture);
+    data->glBindTexture(textype, texturedata->texture);
 
     if(texw) *texw = (float)texturedata->texw;
     if(texh) *texh = (float)texturedata->texh;
@@ -1656,19 +1720,21 @@ GL_UnbindTexture (SDL_Renderer * renderer, SDL_Texture *texture)
 {
     GL_RenderData *data = (GL_RenderData *) renderer->driverdata;
     GL_TextureData *texturedata = (GL_TextureData *) texture->driverdata;
+    const GLenum textype = data->textype;
+
     GL_ActivateRenderer(renderer);
 
     if (texturedata->yuv) {
         data->glActiveTextureARB(GL_TEXTURE2_ARB);
-        data->glDisable(texturedata->type);
+        data->glDisable(textype);
 
         data->glActiveTextureARB(GL_TEXTURE1_ARB);
-        data->glDisable(texturedata->type);
+        data->glDisable(textype);
 
         data->glActiveTextureARB(GL_TEXTURE0_ARB);
     }
 
-    data->glDisable(texturedata->type);
+    data->glDisable(textype);
 
     return 0;
 }

+ 1 - 0
src/render/opengl/SDL_shaders_gl.h

@@ -23,6 +23,7 @@
 /* OpenGL shader implementation */
 
 typedef enum {
+    SHADER_INVALID = -1,
     SHADER_NONE,
     SHADER_SOLID,
     SHADER_RGB,