[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