[Cogl] [PATCH 4/8] cogl-gles2-context: Flip the rendering when framebuffer is offscreen

Robert Bragg robert at sixbynine.org
Thu Aug 9 10:49:27 PDT 2012


This looks good to land to me:

Reviewed-by: Robert Bragg <robert at linux.intel.com>

thanks,
- Robert

On Thu, Aug 9, 2012 at 5:08 PM, Neil Roberts <neil at linux.intel.com> wrote:
> Cogl has a different origin for texture coordinates than OpenGL so
> that the results of rendering to a texture should leave the top of the
> image at the texture coordinate 0,0 rather than the bottom. When a
> GLES2 context is used to render to a Cogl texture via a CoglOffscreen
> we don't really want the application to have to be aware of the
> mismatch and flip the texture coordinates. To get that to work, this
> patch now tracks all of the programs that the application generates
> using the context and sneaks in an extra vertex shader with an
> alternative main function. This main function multiplies the final
> calculated gl_Position by a vector uniform which we can use to flip
> the image. When the application uploads the source code for a vertex
> shader we now replace any occurrences of the token 'main' with '_c31'
> and this renamed function gets called from the replacement main
> function. The token has a weird name so that it will be unlikely to
> conflict with a variable name in the application's source but it also
> needs to have the same number of characters as the original token so
> that it won't affect column numbers in the error reporting.
>
> We are also wrapping glGetShaderSource so that we can try to revert
> the token name. The same goes for the error logs just in case the
> error report mentions function names.
>
> Both places that cause drawing to occur (glDrawElements and
> glDrawArrays) are now also wrapped so that we can update the uniform
> value whenever the program is used with a different type of
> framebuffer from last time.
>
> We additionally need to manually track the state for the viewport, the
> stencil box and the front face because all of these will be affected
> by whether we are flipping the image or not. Any attempts to change
> these states will be queued and instead flushed at the last minute
> before drawing.
>
> There are still some known issues with this patch:
>
> • glCopyTexImage2D and glCopyTexSubImage2D will do the wrong thing
>   when copying data from a CoglOffscreen. This could be quite fiddly
>   to solve.
>
> • Point sprites won't flip correctly. To make this work we would need
>   to flip the gl_PointSprite builtin variable somehow. This is done in
>   the fragment shader not the vertex shader so flipping the calculated
>   gl_Position doesn't help here.
>
> • The patch doesn't attempt to flip rendering to framebuffers for
>   textures created within the GLES2 context. This probably makes sense
>   because those textures are likely to be used within the GLES2
>   context in which case we want to leave the texture coordinates as
>   they are. However, if the texture is shared back out to Cogl with
>   cogl_gles2_texture_2d_new_from_handle then the texture will be
>   upside-down.
>
> • The application can discover our secret uniform that we added via
>   glGetActiveUniform. It might be worth trying to disguise this by
>   wrapping that function although that could be quite fiddly.
> ---
>  cogl/cogl-gles2-context-private.h |  36 ++
>  cogl/cogl-gles2-context.c         | 752 ++++++++++++++++++++++++++++++++++++++
>  2 files changed, 788 insertions(+)
>
> diff --git a/cogl/cogl-gles2-context-private.h b/cogl/cogl-gles2-context-private.h
> index a45214a..1325ad4 100644
> --- a/cogl/cogl-gles2-context-private.h
> +++ b/cogl/cogl-gles2-context-private.h
> @@ -68,6 +68,13 @@ typedef struct
>    CoglBool deleted;
>  } CoglGLES2ShaderData;
>
> +typedef enum
> +{
> +  COGL_GLES2_FLIP_STATE_UNKNOWN,
> +  COGL_GLES2_FLIP_STATE_NORMAL,
> +  COGL_GLES2_FLIP_STATE_FLIPPED
> +} CoglGLES2FlipState;
> +
>  typedef struct
>  {
>    /* GL's ID for the program */
> @@ -89,6 +96,12 @@ typedef struct
>     * application calls glDeleteProgram multiple times */
>    CoglBool deleted;
>
> +  GLuint flip_vector_location;
> +
> +  /* A cache of what value we've put in the flip vector uniform so
> +   * that we don't flush unless it's changed */
> +  CoglGLES2FlipState flip_vector_state;
> +
>    CoglGLES2Context *context;
>  } CoglGLES2ProgramData;
>
> @@ -129,6 +142,29 @@ struct _CoglGLES2Context
>     * current */
>    CoglGLES2ProgramData *current_program;
>
> +  /* A shader to provide a wrapper 'main' function. A single shader is
> +   * used for all programs */
> +  GLuint wrapper_shader;
> +
> +  /* Whether the currently bound framebuffer needs flipping. This is
> +   * used to check for changes so that we can dirty the following
> +   * state flags */
> +  CoglGLES2FlipState current_flip_state;
> +
> +  /* The following state is tracked separately from the GL context
> +   * because we need to modify it depending on whether we are flipping
> +   * the geometry. */
> +  CoglBool viewport_dirty;
> +  int viewport[4];
> +  CoglBool scissor_dirty;
> +  int scissor[4];
> +  CoglBool front_face_dirty;
> +  GLenum front_face;
> +
> +  /* We need to keep track of the pack alignment so we can flip the
> +   * results of glReadPixels read from a CoglOffscreen */
> +  int pack_alignment;
> +
>    void *winsys;
>  };
>
> diff --git a/cogl/cogl-gles2-context.c b/cogl/cogl-gles2-context.c
> index deb889b..c339ba1 100644
> --- a/cogl/cogl-gles2-context.c
> +++ b/cogl/cogl-gles2-context.c
> @@ -30,6 +30,8 @@
>  #include "config.h"
>  #endif
>
> +#include <string.h>
> +
>  #include "cogl-gles2.h"
>  #include "cogl-gles2-context-private.h"
>
> @@ -49,6 +51,30 @@ static CoglGLES2Context *current_gles2_context;
>
>  static CoglUserDataKey offscreen_wrapper_key;
>
> +/* The application's main function is renamed to this so that we can
> + * provide an alternative main function */
> +#define MAIN_WRAPPER_REPLACEMENT_NAME "_c31"
> +/* This uniform is used to flip the rendering or not depending on
> + * whether we are rendering to an offscreen buffer or not */
> +#define MAIN_WRAPPER_FLIP_UNIFORM "_cogl_flip_vector"
> +
> +/* This wrapper function around 'main' is appended to every program in
> + * a separate shader so that we can add some extra code to flip the
> + * rendering when rendering to an offscreen buffer */
> +static const char
> +main_wrapper_function[] =
> +  "uniform vec4 " MAIN_WRAPPER_FLIP_UNIFORM ";\n"
> +  "\n"
> +  "void\n"
> +  MAIN_WRAPPER_REPLACEMENT_NAME " ();\n"
> +  "\n"
> +  "void\n"
> +  "main ()\n"
> +  "{\n"
> +  "  " MAIN_WRAPPER_REPLACEMENT_NAME " ();\n"
> +  "  gl_Position *= " MAIN_WRAPPER_FLIP_UNIFORM ";\n"
> +  "}\n";
> +
>  enum {
>    RESTORE_FB_NONE,
>    RESTORE_FB_FROM_OFFSCREEN,
> @@ -98,6 +124,63 @@ detach_shader (CoglGLES2ProgramData *program_data,
>      }
>  }
>
> +static CoglBool
> +is_symbol_character (char ch)
> +{
> +  return g_ascii_isalnum (ch) || ch == '_';
> +}
> +
> +static void
> +replace_token (char *string,
> +               const char *token,
> +               const char *replacement,
> +               int length)
> +{
> +  char *token_pos;
> +  char *last_pos = string;
> +  char *end = string + length;
> +  int token_length = strlen (token);
> +
> +  /* NOTE: this assumes token and replacement are the same length */
> +
> +  while ((token_pos = _cogl_util_memmem (last_pos,
> +                                         end - last_pos,
> +                                         token,
> +                                         token_length)))
> +    {
> +      /* Make sure this isn't in the middle of some longer token */
> +      if ((token_pos <= string ||
> +           !is_symbol_character (token_pos[-1])) &&
> +          (token_pos + token_length == end ||
> +           !is_symbol_character (token_pos[token_length])))
> +        memcpy (token_pos, replacement, token_length);
> +
> +      last_pos = token_pos + token_length;
> +    }
> +}
> +
> +static void
> +update_current_flip_state (CoglGLES2Context *gles2_ctx)
> +{
> +  CoglGLES2FlipState new_flip_state;
> +
> +  if (gles2_ctx->current_fbo_handle == 0 &&
> +      cogl_is_offscreen (gles2_ctx->write_buffer))
> +    new_flip_state = COGL_GLES2_FLIP_STATE_FLIPPED;
> +  else
> +    new_flip_state = COGL_GLES2_FLIP_STATE_NORMAL;
> +
> +  /* If the flip state has changed then we need to reflush all of the
> +   * dependent state */
> +  if (new_flip_state != gles2_ctx->current_flip_state)
> +    {
> +      gles2_ctx->viewport_dirty = TRUE;
> +      gles2_ctx->scissor_dirty = TRUE;
> +      gles2_ctx->front_face_dirty = TRUE;
> +      gles2_ctx->current_flip_state = new_flip_state;
> +    }
> +}
> +
>  /* We wrap glBindFramebuffer so that when framebuffer 0 is bound
>   * we can instead bind the write_framebuffer passed to
>   * cogl_push_gles2_context().
> @@ -116,6 +199,8 @@ gl_bind_framebuffer_wrapper (GLenum target, GLuint framebuffer)
>      }
>
>    gles2_ctx->context->glBindFramebuffer (target, framebuffer);
> +
> +  update_current_flip_state (gles2_ctx);
>  }
>
>  static int
> @@ -192,6 +277,93 @@ gl_read_pixels_wrapper (GLint x,
>    gles2_ctx->context->glReadPixels (x, y, width, height, format, type, pixels);
>
>    restore_write_buffer (gles2_ctx, restore_mode);
> +
> +  /* If the read buffer is a CoglOffscreen then the data will be
> +   * upside down compared to what GL expects so we need to flip it */
> +  if (gles2_ctx->current_fbo_handle == 0 &&
> +      cogl_is_offscreen (gles2_ctx->read_buffer))
> +    {
> +      int bpp, bytes_per_row, stride, y;
> +      uint8_t *bytes = pixels;
> +      uint8_t *temprow;
> +
> +      /* Try to determine the bytes per pixel for the given
> +       * format/type combination. If there's a format which doesn't
> +       * make sense then we'll just give up because GL will probably
> +       * have just thrown an error */
> +      switch (format)
> +        {
> +        case GL_RGB:
> +          switch (type)
> +            {
> +            case GL_UNSIGNED_BYTE:
> +              bpp = 3;
> +              break;
> +
> +            case GL_UNSIGNED_SHORT_5_6_5:
> +              bpp = 2;
> +              break;
> +
> +            default:
> +              return;
> +            }
> +          break;
> +
> +        case GL_RGBA:
> +          switch (type)
> +            {
> +            case GL_UNSIGNED_BYTE:
> +              bpp = 4;
> +              break;
> +
> +            case GL_UNSIGNED_SHORT_4_4_4_4:
> +            case GL_UNSIGNED_SHORT_5_5_5_1:
> +              bpp = 2;
> +              break;
> +
> +            default:
> +              return;
> +            }
> +          break;
> +
> +        case GL_ALPHA:
> +          switch (type)
> +            {
> +            case GL_UNSIGNED_BYTE:
> +              bpp = 1;
> +              break;
> +
> +            default:
> +              return;
> +            }
> +          break;
> +
> +        default:
> +          return;
> +        }
> +
> +      bytes_per_row = bpp * width;
> +      stride = ((bytes_per_row + gles2_ctx->pack_alignment - 1) &
> +                ~(gles2_ctx->pack_alignment - 1));
> +      temprow = g_alloca (bytes_per_row);
> +
> +      /* vertically flip the buffer in-place */
> +      for (y = 0; y < height / 2; y++)
> +        {
> +          if (y != height - y - 1) /* skip center row */
> +            {
> +              memcpy (temprow,
> +                      bytes + y * stride,
> +                      bytes_per_row);
> +              memcpy (bytes + y * stride,
> +                      bytes + (height - y - 1) * stride,
> +                      bytes_per_row);
> +              memcpy (bytes + (height - y - 1) * stride,
> +                      temprow,
> +                      bytes_per_row);
> +            }
> +        }
> +    }
>  }
>
>  static void
> @@ -292,10 +464,14 @@ gl_create_program_wrapper (void)
>        data->ref_count = 1;
>        data->deleted = FALSE;
>        data->context = gles2_ctx;
> +      data->flip_vector_location = 0;
> +      data->flip_vector_state = COGL_GLES2_FLIP_STATE_UNKNOWN;
>
>        g_hash_table_insert (gles2_ctx->program_map,
>                             GINT_TO_POINTER (id),
>                             data);
> +
> +      gles2_ctx->context->glAttachShader (id, gles2_ctx->wrapper_shader);
>      }
>
>    return id;
> @@ -378,6 +554,521 @@ gl_detach_shader_wrapper (GLuint program,
>  }
>
>  static void
> +gl_shader_source_wrapper (GLuint shader,
> +                          GLsizei count,
> +                          const char *const *string,
> +                          const GLint *length)
> +{
> +  CoglGLES2Context *gles2_ctx = current_gles2_context;
> +  CoglGLES2ShaderData *shader_data;
> +
> +  if ((shader_data = g_hash_table_lookup (gles2_ctx->shader_map,
> +                                          GINT_TO_POINTER (shader))) &&
> +      shader_data->type == GL_VERTEX_SHADER)
> +    {
> +      char **string_copy = g_alloca (count * sizeof (char *));
> +      int i;
> +
> +      /* Replace any occurences of the symbol 'main' with a different
> +       * symbol so that we can provide our own wrapper main
> +       * function */
> +
> +      for (i = 0; i < count; i++)
> +        {
> +          int string_length = length ? length[i] : strlen (string[i]);
> +
> +          string_copy[i] = g_memdup (string[i], string_length);
> +
> +          replace_token (string_copy[i],
> +                         "main",
> +                         MAIN_WRAPPER_REPLACEMENT_NAME,
> +                         string_length);
> +        }
> +
> +      gles2_ctx->context->glShaderSource (shader,
> +                                          count,
> +                                          (const char *const *) string_copy,
> +                                          length);
> +
> +      for (i = 0; i < count; i++)
> +        g_free (string_copy[i]);
> +    }
> +  else
> +    gles2_ctx->context->glShaderSource (shader, count, string, length);
> +}
> +
> +static void
> +gl_get_shader_source_wrapper (GLuint shader,
> +                              GLsizei buf_size,
> +                              GLsizei *length_out,
> +                              GLchar *source)
> +{
> +  CoglGLES2Context *gles2_ctx = current_gles2_context;
> +  CoglGLES2ShaderData *shader_data;
> +  GLsizei length;
> +
> +  gles2_ctx->context->glGetShaderSource (shader,
> +                                         buf_size,
> +                                         &length,
> +                                         source);
> +
> +  if ((shader_data = g_hash_table_lookup (gles2_ctx->shader_map,
> +                                          GINT_TO_POINTER (shader))) &&
> +      shader_data->type == GL_VERTEX_SHADER)
> +    {
> +      GLsizei copy_length = MIN (length, buf_size - 1);
> +      replace_token (source,
> +                     MAIN_WRAPPER_REPLACEMENT_NAME,
> +                     "main",
> +                     copy_length);
> +    }
> +
> +  if (length_out)
> +    *length_out = length;
> +}
> +
> +static void
> +gl_link_program_wrapper (GLuint program)
> +{
> +  CoglGLES2Context *gles2_ctx = current_gles2_context;
> +  CoglGLES2ProgramData *program_data;
> +
> +  gles2_ctx->context->glLinkProgram (program);
> +
> +  program_data = g_hash_table_lookup (gles2_ctx->program_map,
> +                                      GINT_TO_POINTER (program));
> +
> +  if (program_data)
> +    {
> +      GLint status;
> +
> +      gles2_ctx->context->glGetProgramiv (program, GL_LINK_STATUS, &status);
> +
> +      if (status)
> +        program_data->flip_vector_location =
> +          gles2_ctx->context->glGetUniformLocation (program,
> +                                                    MAIN_WRAPPER_FLIP_UNIFORM);
> +    }
> +}
> +
> +static void
> +gl_get_program_iv_wrapper (GLuint program,
> +                           GLenum pname,
> +                           GLint *params)
> +{
> +  CoglGLES2Context *gles2_ctx = current_gles2_context;
> +
> +  gles2_ctx->context->glGetProgramiv (program, pname, params);
> +
> +  switch (pname)
> +    {
> +    case GL_ATTACHED_SHADERS:
> +      /* Decrease the number of shaders to try and hide the shader
> +       * wrapper we added */
> +      if (*params > 1)
> +        (*params)--;
> +      break;
> +    }
> +}
> +
> +static void
> +gl_get_attached_shaders_wrapper (GLuint program,
> +                                 GLsizei max_count,
> +                                 GLsizei *count_ret,
> +                                 GLuint *obj)
> +{
> +  CoglGLES2Context *gles2_ctx = current_gles2_context;
> +  GLuint *tmp_buf;
> +  GLsizei count, count_out;
> +  int i;
> +
> +  /* We need to remove the wrapper shader we added from this list */
> +
> +  /* Allocate a temporary buffer that is one larger than the buffer
> +   * passed in in case the application allocated exactly the size
> +   * returned by GL_ATTACHED_SHADERS. */
> +  tmp_buf = g_alloca (sizeof (GLuint) * (max_count + 1));
> +
> +  gles2_ctx->context->glGetAttachedShaders (program,
> +                                            max_count + 1,
> +                                            &count,
> +                                            tmp_buf);
> +
> +  for (i = 0, count_out = 0; i < count; i++)
> +    if (tmp_buf[i] != gles2_ctx->wrapper_shader)
> +      obj[count_out++] = tmp_buf[i];
> +
> +  if (count_ret)
> +    *count_ret = count_out;
> +}
> +
> +static void
> +flush_viewport_state (CoglGLES2Context *gles2_ctx)
> +{
> +  if (gles2_ctx->viewport_dirty)
> +    {
> +      int y;
> +
> +      if (gles2_ctx->current_flip_state == COGL_GLES2_FLIP_STATE_FLIPPED)
> +        {
> +          /* We need to know the height of the current framebuffer in
> +           * order to flip the viewport. Fortunately we don't need to
> +           * track the height of the FBOs created within the GLES2
> +           * context because we would never be flipping if they are
> +           * bound so we can just assume Cogl's framebuffer is bound
> +           * when we are flipping */
> +          int fb_height = cogl_framebuffer_get_height (gles2_ctx->write_buffer);
> +          y = fb_height - (gles2_ctx->viewport[1] + gles2_ctx->viewport[3]);
> +        }
> +      else
> +        y = gles2_ctx->viewport[1];
> +
> +      gles2_ctx->context->glViewport (gles2_ctx->viewport[0],
> +                                      y,
> +                                      gles2_ctx->viewport[2],
> +                                      gles2_ctx->viewport[3]);
> +
> +      gles2_ctx->viewport_dirty = FALSE;
> +    }
> +}
> +
> +static void
> +flush_scissor_state (CoglGLES2Context *gles2_ctx)
> +{
> +  if (gles2_ctx->scissor_dirty)
> +    {
> +      int y;
> +
> +      if (gles2_ctx->current_flip_state == COGL_GLES2_FLIP_STATE_FLIPPED)
> +        {
> +          /* See comment above about the viewport flipping */
> +          int fb_height = cogl_framebuffer_get_height (gles2_ctx->write_buffer);
> +          y = fb_height - (gles2_ctx->scissor[1] + gles2_ctx->scissor[3]);
> +        }
> +      else
> +        y = gles2_ctx->scissor[1];
> +
> +      gles2_ctx->context->glScissor (gles2_ctx->scissor[0],
> +                                     y,
> +                                     gles2_ctx->scissor[2],
> +                                     gles2_ctx->scissor[3]);
> +
> +      gles2_ctx->scissor_dirty = FALSE;
> +    }
> +}
> +
> +static void
> +flush_front_face_state (CoglGLES2Context *gles2_ctx)
> +{
> +  if (gles2_ctx->front_face_dirty)
> +    {
> +      GLenum front_face;
> +
> +      if (gles2_ctx->current_flip_state == COGL_GLES2_FLIP_STATE_FLIPPED)
> +        {
> +          if (gles2_ctx->front_face == GL_CW)
> +            front_face = GL_CCW;
> +          else
> +            front_face = GL_CW;
> +        }
> +      else
> +        front_face = gles2_ctx->front_face;
> +
> +      gles2_ctx->context->glFrontFace (front_face);
> +
> +      gles2_ctx->front_face_dirty = FALSE;
> +    }
> +}
> +
> +static void
> +pre_draw_wrapper (CoglGLES2Context *gles2_ctx)
> +{
> +  /* If there's no current program then we'll just let GL report an
> +   * error */
> +  if (gles2_ctx->current_program == NULL)
> +    return;
> +
> +  flush_viewport_state (gles2_ctx);
> +  flush_scissor_state (gles2_ctx);
> +  flush_front_face_state (gles2_ctx);
> +
> +  /* We want to flip rendering when the application is rendering to a
> +   * Cogl offscreen buffer in order to maintain the flipped texture
> +   * coordinate origin */
> +  if (gles2_ctx->current_flip_state !=
> +      gles2_ctx->current_program->flip_vector_state)
> +    {
> +      GLuint location =
> +        gles2_ctx->current_program->flip_vector_location;
> +      float value[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
> +
> +      if (gles2_ctx->current_flip_state == COGL_GLES2_FLIP_STATE_FLIPPED)
> +        value[1] = -1.0f;
> +
> +      gles2_ctx->context->glUniform4fv (location, 1, value);
> +
> +      gles2_ctx->current_program->flip_vector_state =
> +        gles2_ctx->current_flip_state;
> +    }
> +}
> +
> +static void
> +gl_clear_wrapper (GLbitfield mask)
> +{
> +  CoglGLES2Context *gles2_ctx = current_gles2_context;
> +
> +  /* Clearing is affected by the scissor state so we need to ensure
> +   * that's flushed */
> +  flush_scissor_state (gles2_ctx);
> +
> +  gles2_ctx->context->glClear (mask);
> +}
> +
> +static void
> +gl_draw_elements_wrapper (GLenum mode,
> +                          GLsizei count,
> +                          GLenum type,
> +                          const GLvoid *indices)
> +{
> +  CoglGLES2Context *gles2_ctx = current_gles2_context;
> +
> +  pre_draw_wrapper (gles2_ctx);
> +
> +  gles2_ctx->context->glDrawElements (mode, count, type, indices);
> +}
> +
> +static void
> +gl_draw_arrays_wrapper (GLenum mode,
> +                        GLint first,
> +                        GLsizei count)
> +{
> +  CoglGLES2Context *gles2_ctx = current_gles2_context;
> +
> +  pre_draw_wrapper (gles2_ctx);
> +
> +  gles2_ctx->context->glDrawArrays (mode, first, count);
> +}
> +
> +static void
> +gl_get_program_info_log_wrapper (GLuint program,
> +                                 GLsizei buf_size,
> +                                 GLsizei *length_out,
> +                                 GLchar *info_log)
> +{
> +  CoglGLES2Context *gles2_ctx = current_gles2_context;
> +  GLsizei length;
> +
> +  gles2_ctx->context->glGetProgramInfoLog (program,
> +                                           buf_size,
> +                                           &length,
> +                                           info_log);
> +
> +  replace_token (info_log,
> +                 MAIN_WRAPPER_REPLACEMENT_NAME,
> +                 "main",
> +                 MIN (length, buf_size));
> +
> +  if (length_out)
> +    *length_out = length;
> +}
> +
> +static void
> +gl_get_shader_info_log_wrapper (GLuint shader,
> +                                GLsizei buf_size,
> +                                GLsizei *length_out,
> +                                GLchar *info_log)
> +{
> +  CoglGLES2Context *gles2_ctx = current_gles2_context;
> +  GLsizei length;
> +
> +  gles2_ctx->context->glGetShaderInfoLog (shader,
> +                                          buf_size,
> +                                          &length,
> +                                          info_log);
> +
> +  replace_token (info_log,
> +                 MAIN_WRAPPER_REPLACEMENT_NAME,
> +                 "main",
> +                 MIN (length, buf_size));
> +
> +  if (length_out)
> +    *length_out = length;
> +}
> +
> +static void
> +gl_front_face_wrapper (GLenum mode)
> +{
> +  CoglGLES2Context *gles2_ctx = current_gles2_context;
> +
> +  /* If the mode doesn't make any sense then we'll just let the
> +   * context deal with it directly so that it will throw an error */
> +  if (mode != GL_CW && mode != GL_CCW)
> +    gles2_ctx->context->glFrontFace (mode);
> +  else
> +    {
> +      gles2_ctx->front_face = mode;
> +      gles2_ctx->front_face_dirty = TRUE;
> +    }
> +}
> +
> +static void
> +gl_viewport_wrapper (GLint x,
> +                     GLint y,
> +                     GLsizei width,
> +                     GLsizei height)
> +{
> +  CoglGLES2Context *gles2_ctx = current_gles2_context;
> +
> +  /* If the viewport is invalid then we'll just let the context deal
> +   * with it directly so that it will throw an error */
> +  if (width < 0 || height < 0)
> +    gles2_ctx->context->glViewport (x, y, width, height);
> +  else
> +    {
> +      gles2_ctx->viewport[0] = x;
> +      gles2_ctx->viewport[1] = y;
> +      gles2_ctx->viewport[2] = width;
> +      gles2_ctx->viewport[3] = height;
> +      gles2_ctx->viewport_dirty = TRUE;
> +    }
> +}
> +
> +static void
> +gl_scissor_wrapper (GLint x,
> +                    GLint y,
> +                    GLsizei width,
> +                    GLsizei height)
> +{
> +  CoglGLES2Context *gles2_ctx = current_gles2_context;
> +
> +  /* If the scissor is invalid then we'll just let the context deal
> +   * with it directly so that it will throw an error */
> +  if (width < 0 || height < 0)
> +    gles2_ctx->context->glScissor (x, y, width, height);
> +  else
> +    {
> +      gles2_ctx->scissor[0] = x;
> +      gles2_ctx->scissor[1] = y;
> +      gles2_ctx->scissor[2] = width;
> +      gles2_ctx->scissor[3] = height;
> +      gles2_ctx->scissor_dirty = TRUE;
> +    }
> +}
> +
> +static void
> +gl_get_boolean_v_wrapper (GLenum pname,
> +                          GLboolean *params)
> +{
> +  CoglGLES2Context *gles2_ctx = current_gles2_context;
> +
> +  switch (pname)
> +    {
> +    case GL_VIEWPORT:
> +      {
> +        int i;
> +
> +        for (i = 0; i < 4; i++)
> +          params[i] = !!gles2_ctx->viewport[i];
> +      }
> +      break;
> +
> +    case GL_SCISSOR_BOX:
> +      {
> +        int i;
> +
> +        for (i = 0; i < 4; i++)
> +          params[i] = !!gles2_ctx->scissor[i];
> +      }
> +      break;
> +
> +    default:
> +      gles2_ctx->context->glGetBooleanv (pname, params);
> +    }
> +}
> +
> +static void
> +gl_get_integer_v_wrapper (GLenum pname,
> +                          GLint *params)
> +{
> +  CoglGLES2Context *gles2_ctx = current_gles2_context;
> +
> +  switch (pname)
> +    {
> +    case GL_VIEWPORT:
> +      {
> +        int i;
> +
> +        for (i = 0; i < 4; i++)
> +          params[i] = gles2_ctx->viewport[i];
> +      }
> +      break;
> +
> +    case GL_SCISSOR_BOX:
> +      {
> +        int i;
> +
> +        for (i = 0; i < 4; i++)
> +          params[i] = gles2_ctx->scissor[i];
> +      }
> +      break;
> +
> +    case GL_FRONT_FACE:
> +      params[0] = gles2_ctx->front_face;
> +      break;
> +
> +    default:
> +      gles2_ctx->context->glGetIntegerv (pname, params);
> +    }
> +}
> +
> +static void
> +gl_get_float_v_wrapper (GLenum pname,
> +                        GLfloat *params)
> +{
> +  CoglGLES2Context *gles2_ctx = current_gles2_context;
> +
> +  switch (pname)
> +    {
> +    case GL_VIEWPORT:
> +      {
> +        int i;
> +
> +        for (i = 0; i < 4; i++)
> +          params[i] = gles2_ctx->viewport[i];
> +      }
> +      break;
> +
> +    case GL_SCISSOR_BOX:
> +      {
> +        int i;
> +
> +        for (i = 0; i < 4; i++)
> +          params[i] = gles2_ctx->scissor[i];
> +      }
> +      break;
> +
> +    case GL_FRONT_FACE:
> +      params[0] = gles2_ctx->front_face;
> +      break;
> +
> +    default:
> +      gles2_ctx->context->glGetFloatv (pname, params);
> +    }
> +}
> +
> +static void
> +gl_pixel_store_i_wrapper (GLenum pname, GLint param)
> +{
> +  CoglGLES2Context *gles2_ctx = current_gles2_context;
> +
> +  gles2_ctx->context->glPixelStorei (pname, param);
> +
> +  if (pname == GL_PACK_ALIGNMENT &&
> +      (param == 1 || param == 2 || param == 4 || param == 8))
> +    gles2_ctx->pack_alignment = param;
> +}
> +
> +static void
>  _cogl_gles2_offscreen_free (CoglGLES2Offscreen *gles2_offscreen)
>  {
>    COGL_LIST_REMOVE (gles2_offscreen, list_node);
> @@ -415,6 +1106,8 @@ _cogl_gles2_context_free (CoglGLES2Context *gles2_context)
>    const CoglWinsysVtable *winsys;
>    GList *objects, *l;
>
> +  ctx->glDeleteShader (gles2_context->wrapper_shader);
> +
>    if (gles2_context->current_program)
>      program_data_unref (gles2_context->current_program);
>
> @@ -480,6 +1173,33 @@ free_program_data (CoglGLES2ProgramData *data)
>    g_slice_free (CoglGLES2ProgramData, data);
>  }
>
> +static GLuint
> +create_wrapper_shader (CoglContext *ctx)
> +{
> +  const char *strings = main_wrapper_function;
> +  GLint length = sizeof (main_wrapper_function) - 1;
> +  GLint status;
> +  GLuint shader;
> +
> +  shader = ctx->glCreateShader (GL_VERTEX_SHADER);
> +  ctx->glShaderSource (shader, 1, &strings, &length);
> +  ctx->glCompileShader (shader);
> +  ctx->glGetShaderiv (shader, GL_COMPILE_STATUS, &status);
> +
> +  if (!status)
> +    {
> +      char buf[512];
> +
> +      ctx->glGetShaderInfoLog (shader,
> +                               sizeof (buf),
> +                               NULL, /* length */
> +                               buf);
> +      g_warning ("Compiling wrapper shader failed:\n%s", buf);
> +    }
> +
> +  return shader;
> +}
> +
>  CoglGLES2Context *
>  cogl_gles2_context_new (CoglContext *ctx, GError **error)
>  {
> @@ -511,6 +1231,13 @@ cogl_gles2_context_new (CoglContext *ctx, GError **error)
>        return NULL;
>      }
>
> +  gles2_ctx->current_flip_state = COGL_GLES2_FLIP_STATE_UNKNOWN;
> +  gles2_ctx->viewport_dirty = TRUE;
> +  gles2_ctx->scissor_dirty = TRUE;
> +  gles2_ctx->front_face_dirty = TRUE;
> +  gles2_ctx->front_face = GL_CCW;
> +  gles2_ctx->pack_alignment = 4;
> +
>    gles2_ctx->vtable = g_malloc0 (sizeof (CoglGLES2Vtable));
>  #define COGL_EXT_BEGIN(name, \
>                         min_gl_major, min_gl_minor, \
> @@ -528,6 +1255,8 @@ cogl_gles2_context_new (CoglContext *ctx, GError **error)
>  #undef COGL_EXT_FUNCTION
>  #undef COGL_EXT_END
>
> +  gles2_ctx->wrapper_shader = create_wrapper_shader (ctx);
> +
>    gles2_ctx->vtable->glBindFramebuffer = gl_bind_framebuffer_wrapper;
>    gles2_ctx->vtable->glReadPixels = gl_read_pixels_wrapper;
>    gles2_ctx->vtable->glCopyTexImage2D = gl_copy_tex_image_2d_wrapper;
> @@ -539,6 +1268,27 @@ cogl_gles2_context_new (CoglContext *ctx, GError **error)
>    gles2_ctx->vtable->glUseProgram = gl_use_program_wrapper;
>    gles2_ctx->vtable->glAttachShader = gl_attach_shader_wrapper;
>    gles2_ctx->vtable->glDetachShader = gl_detach_shader_wrapper;
> +  gles2_ctx->vtable->glShaderSource = gl_shader_source_wrapper;
> +  gles2_ctx->vtable->glGetShaderSource = gl_get_shader_source_wrapper;
> +  gles2_ctx->vtable->glLinkProgram = gl_link_program_wrapper;
> +  gles2_ctx->vtable->glGetProgramiv = gl_get_program_iv_wrapper;
> +  gles2_ctx->vtable->glGetAttachedShaders = gl_get_attached_shaders_wrapper;
> +  gles2_ctx->vtable->glGetProgramInfoLog = gl_get_program_info_log_wrapper;
> +  gles2_ctx->vtable->glGetShaderInfoLog = gl_get_shader_info_log_wrapper;
> +  gles2_ctx->vtable->glClear = gl_clear_wrapper;
> +  gles2_ctx->vtable->glDrawElements = gl_draw_elements_wrapper;
> +  gles2_ctx->vtable->glDrawArrays = gl_draw_arrays_wrapper;
> +  gles2_ctx->vtable->glFrontFace = gl_front_face_wrapper;
> +  gles2_ctx->vtable->glViewport = gl_viewport_wrapper;
> +  gles2_ctx->vtable->glScissor = gl_scissor_wrapper;
> +  gles2_ctx->vtable->glGetBooleanv = gl_get_boolean_v_wrapper;
> +  gles2_ctx->vtable->glGetIntegerv = gl_get_integer_v_wrapper;
> +  gles2_ctx->vtable->glGetFloatv = gl_get_float_v_wrapper;
> +  gles2_ctx->vtable->glPixelStorei = gl_pixel_store_i_wrapper;
> +
> +  /* FIXME: we need to do something with glCopyTexImage2D and
> +   * glCopySubTexImage2D so that it will flip the data if it is read
> +   * from a CoglOffscreen */
>
>    gles2_ctx->shader_map =
>      g_hash_table_new_full (g_direct_hash,
> @@ -719,6 +1469,8 @@ cogl_push_gles2_context (CoglContext *ctx,
>        if (gles2_ctx->write_buffer)
>          cogl_object_unref (gles2_ctx->write_buffer);
>        gles2_ctx->write_buffer = cogl_object_ref (write_buffer);
> +
> +      update_current_flip_state (gles2_ctx);
>      }
>
>    if (!winsys->set_gles2_context (gles2_ctx, &internal_error))
> --
> 1.7.11.3.g3c3efa5
>
> _______________________________________________
> Cogl mailing list
> Cogl at lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/cogl


More information about the Cogl mailing list