Commit 5607cc45c507b18d08b0a770bfd5cfc5bbf77c47

Ryan C. Gordon 2013-10-05T00:29:57

Avoid redundant state changes in the GLES2 renderer.

diff --git a/src/render/opengles2/SDL_render_gles2.c b/src/render/opengles2/SDL_render_gles2.c
index 6f79802..536fec9 100644
--- a/src/render/opengles2/SDL_render_gles2.c
+++ b/src/render/opengles2/SDL_render_gles2.c
@@ -81,6 +81,7 @@ typedef struct GLES2_ShaderCacheEntry
     GLES2_ShaderType type;
     const GLES2_ShaderInstance *instance;
     int references;
+    Uint8 modulation_r, modulation_g, modulation_b, modulation_a;
     struct GLES2_ShaderCacheEntry *prev;
     struct GLES2_ShaderCacheEntry *next;
 } GLES2_ShaderCacheEntry;
@@ -98,6 +99,9 @@ typedef struct GLES2_ProgramCacheEntry
     GLES2_ShaderCacheEntry *vertex_shader;
     GLES2_ShaderCacheEntry *fragment_shader;
     GLuint uniform_locations[16];
+    Uint8 color_r, color_g, color_b, color_a;
+    Uint8 modulation_r, modulation_g, modulation_b, modulation_a;
+    GLfloat projection[4][4];
     struct GLES2_ProgramCacheEntry *prev;
     struct GLES2_ProgramCacheEntry *next;
 } GLES2_ProgramCacheEntry;
@@ -156,6 +160,7 @@ typedef struct GLES2_DriverContext
     GLES2_ShaderCache shader_cache;
     GLES2_ProgramCache program_cache;
     GLES2_ProgramCacheEntry *current_program;
+    Uint8 clear_r, clear_g, clear_b, clear_a;
 } GLES2_DriverContext;
 
 #define GLES2_MAX_CACHED_PROGRAMS 8
@@ -492,8 +497,6 @@ GLES2_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture)
         return -1;
     }
     texture->driverdata = data;
-
-    renderdata->glActiveTexture(GL_TEXTURE0);
     renderdata->glBindTexture(data->texture_type, data->texture);
     renderdata->glTexParameteri(data->texture_type, GL_TEXTURE_MIN_FILTER, scaleMode);
     renderdata->glTexParameteri(data->texture_type, GL_TEXTURE_MAG_FILTER, scaleMode);
@@ -549,9 +552,7 @@ GLES2_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect
     }
 
     /* Create a texture subimage with the supplied data */
-    data->glActiveTexture(GL_TEXTURE0);
     data->glBindTexture(tdata->texture_type, tdata->texture);
-    data->glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
     data->glTexSubImage2D(tdata->texture_type,
                     0,
                     rect->x,
@@ -720,7 +721,16 @@ GLES2_CacheProgram(SDL_Renderer *renderer, GLES2_ShaderCacheEntry *vertex,
     entry->uniform_locations[GLES2_UNIFORM_MODULATION] =
         data->glGetUniformLocation(entry->id, "u_modulation");
     entry->uniform_locations[GLES2_UNIFORM_COLOR] =
-        rdata->glGetUniformLocation(entry->id, "u_color");
+        data->glGetUniformLocation(entry->id, "u_color");
+
+    entry->modulation_r = entry->modulation_g = entry->modulation_b = entry->modulation_a = 1.0f;
+    entry->color_r = entry->color_g = entry->color_b = entry->color_a = 1.0f;
+
+    data->glUseProgram(entry->id);
+    data->glUniformMatrix4fv(entry->uniform_locations[GLES2_UNIFORM_PROJECTION], 1, GL_FALSE, (GLfloat *)entry->projection);
+    data->glUniform1i(entry->uniform_locations[GLES2_UNIFORM_TEXTURE], 0);  /* always texture unit 0. */
+    data->glUniform4f(entry->uniform_locations[GLES2_UNIFORM_MODULATION], 1.0f, 1.0f, 1.0f, 1.0f);
+    data->glUniform4f(entry->uniform_locations[GLES2_UNIFORM_COLOR], 1.0f, 1.0f, 1.0f, 1.0f);
 
     /* Cache the linked program */
     if (data->program_cache.head)
@@ -959,7 +969,6 @@ GLES2_SetOrthographicProjection(SDL_Renderer *renderer)
 {
     GLES2_DriverContext *data = (GLES2_DriverContext *)renderer->driverdata;
     GLfloat projection[4][4];
-    GLuint locProjection;
 
     if (!renderer->viewport.w || !renderer->viewport.h) {
         return 0;
@@ -992,8 +1001,12 @@ GLES2_SetOrthographicProjection(SDL_Renderer *renderer)
     projection[3][3] = 1.0f;
 
     /* Set the projection matrix */
-    locProjection = data->current_program->uniform_locations[GLES2_UNIFORM_PROJECTION];
-    data->glUniformMatrix4fv(locProjection, 1, GL_FALSE, (GLfloat *)projection);
+    if (SDL_memcmp(data->current_program->projection, projection, sizeof (projection)) != 0) {
+        const GLuint locProjection = data->current_program->uniform_locations[GLES2_UNIFORM_PROJECTION];
+        data->glUniformMatrix4fv(locProjection, 1, GL_FALSE, (GLfloat *)projection);
+        SDL_memcpy(data->current_program->projection, projection, sizeof (projection));
+    }
+
     return 0;
 }
 
@@ -1024,10 +1037,18 @@ GLES2_RenderClear(SDL_Renderer * renderer)
 
     GLES2_ActivateRenderer(renderer);
 
-    data->glClearColor((GLfloat) renderer->r * inv255f,
-                 (GLfloat) renderer->g * inv255f,
-                 (GLfloat) renderer->b * inv255f,
-                 (GLfloat) renderer->a * inv255f);
+    /* !!! FIXME: it'd be nice to do a single 32-bit compare here. */
+    if ( (data->clear_r != renderer->r) || (data->clear_g != renderer->g) ||
+         (data->clear_b != renderer->b) || (data->clear_a != renderer->a) ) {
+        data->glClearColor((GLfloat) renderer->r * inv255f,
+                     (GLfloat) renderer->g * inv255f,
+                     (GLfloat) renderer->b * inv255f,
+                     (GLfloat) renderer->a * inv255f);
+        data->clear_r = renderer->r;
+        data->clear_g = renderer->g;
+        data->clear_b = renderer->b;
+        data->clear_a = renderer->a;
+    }
 
     data->glClear(GL_COLOR_BUFFER_BIT);
 
@@ -1077,8 +1098,9 @@ static int
 GLES2_SetDrawingState(SDL_Renderer * renderer)
 {
     GLES2_DriverContext *data = (GLES2_DriverContext *)renderer->driverdata;
-    int blendMode = renderer->blendMode;
-    GLuint locColor;
+    const int blendMode = renderer->blendMode;
+    GLES2_ProgramCacheEntry *program;
+    Uint8 r, g, b, a;
 
     GLES2_ActivateRenderer(renderer);
 
@@ -1087,26 +1109,35 @@ GLES2_SetDrawingState(SDL_Renderer * renderer)
     GLES2_SetTexCoords(data, SDL_FALSE);
 
     /* Activate an appropriate shader and set the projection matrix */
-    if (GLES2_SelectProgram(renderer, GLES2_IMAGESOURCE_SOLID, blendMode) < 0)
+    if (GLES2_SelectProgram(renderer, GLES2_IMAGESOURCE_SOLID, blendMode) < 0) {
         return -1;
+    }
 
     /* Select the color to draw with */
-    locColor = data->current_program->uniform_locations[GLES2_UNIFORM_COLOR];
+    g = renderer->g;
+    a = renderer->a;
+
     if (renderer->target &&
-        (renderer->target->format == SDL_PIXELFORMAT_ARGB8888 ||
+         (renderer->target->format == SDL_PIXELFORMAT_ARGB8888 ||
          renderer->target->format == SDL_PIXELFORMAT_RGB888)) {
-        data->glUniform4f(locColor,
-                    renderer->b * inv255f,
-                    renderer->g * inv255f,
-                    renderer->r * inv255f,
-                    renderer->a * inv255f);
-    } else {
-        data->glUniform4f(locColor,
-                    renderer->r * inv255f,
-                    renderer->g * inv255f,
-                    renderer->b * inv255f,
-                    renderer->a * inv255f);
+        r = renderer->b;
+        b = renderer->r;
+     } else {
+        r = renderer->r;
+        b = renderer->b;
+     }
+
+    program = data->current_program;
+    /* !!! FIXME: it'd be nice to do a single 32-bit compare here. */
+    if ( (program->color_r != r) || (program->color_g != g) || (program->color_b != b) || (program->color_a != a) ) {
+        /* Select the color to draw with */
+        data->glUniform4f(program->uniform_locations[GLES2_UNIFORM_COLOR], r * inv255f, g * inv255f, b * inv255f, a * inv255f);
+        program->color_r = r;
+        program->color_g = g;
+        program->color_b = b;
+        program->color_a = a;
     }
+
     return 0;
 }
 
@@ -1213,8 +1244,8 @@ GLES2_RenderCopy(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect *s
     SDL_BlendMode blendMode;
     GLfloat vertices[8];
     GLfloat texCoords[8];
-    GLuint locTexture;
-    GLuint locModulation;
+    GLES2_ProgramCacheEntry *program;
+    Uint8 r, g, b, a;
 
     GLES2_ActivateRenderer(renderer);
 
@@ -1300,31 +1331,37 @@ GLES2_RenderCopy(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect *s
                 return -1;
         }
     }
-    if (GLES2_SelectProgram(renderer, sourceType, blendMode) < 0)
+
+    if (GLES2_SelectProgram(renderer, sourceType, blendMode) < 0) {
         return -1;
+    }
 
     /* Select the target texture */
-    locTexture = data->current_program->uniform_locations[GLES2_UNIFORM_TEXTURE];
-    data->glActiveTexture(GL_TEXTURE0);
     data->glBindTexture(tdata->texture_type, tdata->texture);
-    data->glUniform1i(locTexture, 0);
 
     /* Configure color modulation */
-    locModulation = data->current_program->uniform_locations[GLES2_UNIFORM_MODULATION];
+    g = texture->g;
+    a = texture->a;
+
     if (renderer->target &&
         (renderer->target->format == SDL_PIXELFORMAT_ARGB8888 ||
          renderer->target->format == SDL_PIXELFORMAT_RGB888)) {
-        data->glUniform4f(locModulation,
-                    texture->b * inv255f,
-                    texture->g * inv255f,
-                    texture->r * inv255f,
-                    texture->a * inv255f);
+        r = texture->b;
+        b = texture->r;
     } else {
-        data->glUniform4f(locModulation,
-                    texture->r * inv255f,
-                    texture->g * inv255f,
-                    texture->b * inv255f,
-                    texture->a * inv255f);
+        r = texture->r;
+        b = texture->b;
+    }
+
+    program = data->current_program;
+
+    /* !!! FIXME: it'd be nice to do a single 32-bit compare here. */
+    if ( (program->modulation_r != r) || (program->modulation_g != g) || (program->modulation_b != b) || (program->modulation_a != a) ) {
+        data->glUniform4f(program->uniform_locations[GLES2_UNIFORM_MODULATION], r * inv255f, g * inv255f, b * inv255f, a * inv255f);
+        program->modulation_r = r;
+        program->modulation_g = g;
+        program->modulation_b = b;
+        program->modulation_a = a;
     }
 
     /* Configure texture blending */
@@ -1363,11 +1400,11 @@ GLES2_RenderCopyEx(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect 
     GLES2_DriverContext *data = (GLES2_DriverContext *)renderer->driverdata;
     GLES2_TextureData *tdata = (GLES2_TextureData *)texture->driverdata;
     GLES2_ImageSource sourceType = GLES2_IMAGESOURCE_TEXTURE_ABGR;
+    GLES2_ProgramCacheEntry *program;
+    Uint8 r, g, b, a;
     SDL_BlendMode blendMode;
     GLfloat vertices[8];
     GLfloat texCoords[8];
-    GLuint locTexture;
-    GLuint locModulation;
     GLfloat translate[8];
     GLfloat fAngle[4];
     GLfloat tmp;
@@ -1467,27 +1504,32 @@ GLES2_RenderCopyEx(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect 
         return -1;
 
     /* Select the target texture */
-    locTexture = data->current_program->uniform_locations[GLES2_UNIFORM_TEXTURE];
-    data->glActiveTexture(GL_TEXTURE0);
     data->glBindTexture(tdata->texture_type, tdata->texture);
-    data->glUniform1i(locTexture, 0);
 
     /* Configure color modulation */
-    locModulation = data->current_program->uniform_locations[GLES2_UNIFORM_MODULATION];
+    /* !!! FIXME: grep for glUniform4f(), move that stuff to a subroutine, it's a lot of copy/paste. */
+    g = texture->g;
+    a = texture->a;
+
     if (renderer->target &&
         (renderer->target->format == SDL_PIXELFORMAT_ARGB8888 ||
          renderer->target->format == SDL_PIXELFORMAT_RGB888)) {
-        data->glUniform4f(locModulation,
-                    texture->b * inv255f,
-                    texture->g * inv255f,
-                    texture->r * inv255f,
-                    texture->a * inv255f);
+        r = texture->b;
+        b = texture->r;
     } else {
-        data->glUniform4f(locModulation,
-                    texture->r * inv255f,
-                    texture->g * inv255f,
-                    texture->b * inv255f,
-                    texture->a * inv255f);
+        r = texture->r;
+        b = texture->b;
+    }
+
+    program = data->current_program;
+
+    /* !!! FIXME: it'd be nice to do a single 32-bit compare here. */
+    if ( (program->modulation_r != r) || (program->modulation_g != g) || (program->modulation_b != b) || (program->modulation_a != a) ) {
+        data->glUniform4f(program->uniform_locations[GLES2_UNIFORM_MODULATION], r * inv255f, g * inv255f, b * inv255f, a * inv255f);
+        program->modulation_r = r;
+        program->modulation_g = g;
+        program->modulation_b = b;
+        program->modulation_a = a;
     }
 
     /* Configure texture blending */
@@ -1557,8 +1599,6 @@ GLES2_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect,
 
     SDL_GetRendererOutputSize(renderer, &w, &h);
 
-    data->glPixelStorei(GL_PACK_ALIGNMENT, 1);
-
     data->glReadPixels(rect->x, (h-rect->y)-rect->h, rect->w, rect->h,
                        GL_RGBA, GL_UNSIGNED_BYTE, temp_pixels);
     if (GL_CheckError("glReadPixels()", renderer) < 0) {
@@ -1650,6 +1690,15 @@ GLES2_ResetState(SDL_Renderer *renderer)
     data->current.blendMode = -1;
     data->current.tex_coords = SDL_FALSE;
 
+    data->glActiveTexture(GL_TEXTURE0);
+    data->glPixelStorei(GL_PACK_ALIGNMENT, 1);
+    data->glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+
+    data->glClearColor((GLfloat) data->clear_r * inv255f,
+                        (GLfloat) data->clear_g * inv255f,
+                        (GLfloat) data->clear_b * inv255f,
+                        (GLfloat) data->clear_a * inv255f);
+
     data->glEnableVertexAttribArray(GLES2_ATTRIBUTE_POSITION);
     data->glDisableVertexAttribArray(GLES2_ATTRIBUTE_TEXCOORD);
 
diff --git a/src/video/uikit/SDL_uikitopenglview.m b/src/video/uikit/SDL_uikitopenglview.m
index 9f3dc13..b43f345 100644
--- a/src/video/uikit/SDL_uikitopenglview.m
+++ b/src/video/uikit/SDL_uikitopenglview.m
@@ -121,6 +121,8 @@
         if (glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES) != GL_FRAMEBUFFER_COMPLETE_OES) {
             return NO;
         }
+
+        glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
         /* end create buffers */
 
         self.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight);
@@ -148,6 +150,8 @@
         glBindRenderbufferOES(GL_RENDERBUFFER_OES, depthRenderbuffer);
         glRenderbufferStorageOES(GL_RENDERBUFFER_OES, depthBufferFormat, backingWidth, backingHeight);
     }
+
+    glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
 }
 
 - (void)setAnimationCallback:(int)interval
@@ -197,7 +201,9 @@
 
 - (void)swapBuffers
 {
-    glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
+    /* viewRenderbuffer should always be bound here. Code that binds something
+        else is responsible for rebinding viewRenderbuffer, to reduce
+        duplicate state changes. */
     [context presentRenderbuffer:GL_RENDERBUFFER_OES];
 }