mutter/cogl/cogl-gles2-context.c

1957 lines
61 KiB
C
Raw Normal View History

/*
* Cogl
*
* An object oriented GL/GLES Abstraction/Utility Layer
*
* Copyright (C) 2011 Collabora Ltd.
* Copyright (C) 2012 Intel Corporation.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <http://www.gnu.org/licenses/>.
*
* Authors:
* Tomeu Vizoso <tomeu.vizoso@collabora.com>
* Robert Bragg <robert@linux.intel.com>
* Neil Roberts <neil@linux.intel.com>
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
cogl-gles2-context: Flip the rendering when framebuffer is offscreen 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. Reviewed-by: Robert Bragg <robert@linux.intel.com> (cherry picked from commit d589bf19e51f22c3241b2a18db10f22131ac126a)
2012-08-07 06:48:56 -04:00
#include <string.h>
#include "cogl-gles2.h"
#include "cogl-gles2-context-private.h"
#include "cogl-context-private.h"
#include "cogl-display-private.h"
#include "cogl-framebuffer-private.h"
#include "cogl-onscreen-template-private.h"
#include "cogl-renderer-private.h"
#include "cogl-swap-chain-private.h"
#include "cogl-texture-2d-private.h"
cogl-gles2-context: Wrap glCopyTex{Sub,}Image2D to flip the result When the CoglGLES2Context is bound to read from a CoglOffscreen then the result will be upside down from what GL expects if glCopyTexImage2D is used directly. To fix that, this patch now wraps glCopyTexImage2D and glCopyTexSubImage2D so that the copy is doing by binding an FBO to the target texture and then rendering a quad sampling from the texture in the offscreen framebuffer. The rendering is done using the Cogl context rather than the GLES2 context because otherwise it would have to do a fair bit of work to try and stash the old state on the context before setting up the state to do the blit. The down side of this is that the contexts need to be synchronized so that the rendering will be up-to-date. As far as I understand from the GL spec, this requires a glFinish and then the texture needs to be rebound in the new context because updates to shared objects are guaranteed to be reflected until the object is rebound. GLES2 supports using glCopyTexImage2D for cube map textures. As Cogl doesn't currently have support for cube maps, it is quite hard to get that to work with this patch. For now attempts to copy to a cube map texture will just be sliently ignored. This patch also includes a test case which renders an image to the framebuffer and then copies it to a texture. The texture is then rendered back to the framebuffer and the contents are checked for the correct orientation using glReadPixels. Reviewed-by: Robert Bragg <robert@linux.intel.com> (cherry picked from commit 30b6da8134bad95267265e26685c7475f6c351c9)
2012-08-09 11:03:35 -04:00
#include "cogl-pipeline-opengl-private.h"
static void _cogl_gles2_context_free (CoglGLES2Context *gles2_context);
COGL_OBJECT_DEFINE (GLES2Context, gles2_context);
static CoglGLES2Context *current_gles2_context;
static CoglUserDataKey offscreen_wrapper_key;
cogl-gles2-context: Flip the rendering when framebuffer is offscreen 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. Reviewed-by: Robert Bragg <robert@linux.intel.com> (cherry picked from commit d589bf19e51f22c3241b2a18db10f22131ac126a)
2012-08-07 06:48:56 -04:00
/* 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_prelude[] =
cogl-gles2-context: Flip the rendering when framebuffer is offscreen 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. Reviewed-by: Robert Bragg <robert@linux.intel.com> (cherry picked from commit d589bf19e51f22c3241b2a18db10f22131ac126a)
2012-08-07 06:48:56 -04:00
"uniform vec4 " MAIN_WRAPPER_FLIP_UNIFORM ";\n"
"\n";
static const char
main_wrapper_function[] =
cogl-gles2-context: Flip the rendering when framebuffer is offscreen 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. Reviewed-by: Robert Bragg <robert@linux.intel.com> (cherry picked from commit d589bf19e51f22c3241b2a18db10f22131ac126a)
2012-08-07 06:48:56 -04:00
"\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,
RESTORE_FB_FROM_ONSCREEN,
};
GQuark
_cogl_gles2_context_error_quark (void)
{
return g_quark_from_static_string ("cogl-gles2-context-error-quark");
}
static void
shader_data_unref (CoglGLES2Context *context,
CoglGLES2ShaderData *shader_data)
{
if (--shader_data->ref_count < 1)
/* Removing the hash table entry should also destroy the data */
g_hash_table_remove (context->shader_map,
GINT_TO_POINTER (shader_data->object_id));
}
static void
program_data_unref (CoglGLES2ProgramData *program_data)
{
if (--program_data->ref_count < 1)
/* Removing the hash table entry should also destroy the data */
g_hash_table_remove (program_data->context->program_map,
GINT_TO_POINTER (program_data->object_id));
}
static void
detach_shader (CoglGLES2ProgramData *program_data,
CoglGLES2ShaderData *shader_data)
{
GList *l;
for (l = program_data->attached_shaders; l; l = l->next)
{
if (l->data == shader_data)
{
shader_data_unref (program_data->context, shader_data);
program_data->attached_shaders =
g_list_delete_link (program_data->attached_shaders, l);
break;
}
}
}
cogl-gles2-context: Flip the rendering when framebuffer is offscreen 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. Reviewed-by: Robert Bragg <robert@linux.intel.com> (cherry picked from commit d589bf19e51f22c3241b2a18db10f22131ac126a)
2012-08-07 06:48:56 -04:00
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;
}
}
static GLuint
get_current_texture_2d_object (CoglGLES2Context *gles2_ctx)
{
return g_array_index (gles2_ctx->texture_units,
CoglGLES2TextureUnitData,
gles2_ctx->current_texture_unit).current_texture_2d;
}
static void
set_texture_object_data (CoglGLES2Context *gles2_ctx,
GLenum target,
GLint level,
GLenum internal_format,
GLsizei width,
GLsizei height)
{
GLuint texture_id = get_current_texture_2d_object (gles2_ctx);
CoglGLES2TextureObjectData *texture_object;
/* We want to keep track of all texture objects where the data is
* created by this context so that we can delete them later */
texture_object = g_hash_table_lookup (gles2_ctx->texture_object_map,
GUINT_TO_POINTER (texture_id));
if (texture_object == NULL)
{
texture_object = g_slice_new0 (CoglGLES2TextureObjectData);
texture_object->object_id = texture_id;
g_hash_table_insert (gles2_ctx->texture_object_map,
GUINT_TO_POINTER (texture_id),
texture_object);
}
switch (target)
{
case GL_TEXTURE_2D:
texture_object->target = GL_TEXTURE_2D;
/* We want to keep track of the dimensions of any texture object
* setting the GL_TEXTURE_2D target */
if (level == 0)
{
texture_object->width = width;
texture_object->height = height;
texture_object->format = internal_format;
}
break;
case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
texture_object->target = GL_TEXTURE_CUBE_MAP;
break;
}
}
cogl-gles2-context: Wrap glCopyTex{Sub,}Image2D to flip the result When the CoglGLES2Context is bound to read from a CoglOffscreen then the result will be upside down from what GL expects if glCopyTexImage2D is used directly. To fix that, this patch now wraps glCopyTexImage2D and glCopyTexSubImage2D so that the copy is doing by binding an FBO to the target texture and then rendering a quad sampling from the texture in the offscreen framebuffer. The rendering is done using the Cogl context rather than the GLES2 context because otherwise it would have to do a fair bit of work to try and stash the old state on the context before setting up the state to do the blit. The down side of this is that the contexts need to be synchronized so that the rendering will be up-to-date. As far as I understand from the GL spec, this requires a glFinish and then the texture needs to be rebound in the new context because updates to shared objects are guaranteed to be reflected until the object is rebound. GLES2 supports using glCopyTexImage2D for cube map textures. As Cogl doesn't currently have support for cube maps, it is quite hard to get that to work with this patch. For now attempts to copy to a cube map texture will just be sliently ignored. This patch also includes a test case which renders an image to the framebuffer and then copies it to a texture. The texture is then rendered back to the framebuffer and the contents are checked for the correct orientation using glReadPixels. Reviewed-by: Robert Bragg <robert@linux.intel.com> (cherry picked from commit 30b6da8134bad95267265e26685c7475f6c351c9)
2012-08-09 11:03:35 -04:00
static void
copy_flipped_texture (CoglGLES2Context *gles2_ctx,
int level,
int src_x,
int src_y,
int dst_x,
int dst_y,
int width,
int height)
{
GLuint tex_id = get_current_texture_2d_object (gles2_ctx);
CoglGLES2TextureObjectData *tex_object_data;
CoglContext *ctx;
const CoglWinsysVtable *winsys;
CoglTexture2D *dst_texture;
CoglPixelFormat internal_format;
tex_object_data = g_hash_table_lookup (gles2_ctx->texture_object_map,
GUINT_TO_POINTER (tex_id));
/* We can't do anything if the application hasn't set a level 0
* image on this texture object */
if (tex_object_data == NULL ||
tex_object_data->target != GL_TEXTURE_2D ||
tex_object_data->width <= 0 ||
tex_object_data->height <= 0)
return;
switch (tex_object_data->format)
{
case GL_RGB:
internal_format = COGL_PIXEL_FORMAT_RGB_888;
break;
case GL_RGBA:
internal_format = COGL_PIXEL_FORMAT_RGBA_8888_PRE;
break;
case GL_ALPHA:
internal_format = COGL_PIXEL_FORMAT_A_8;
break;
case GL_LUMINANCE:
internal_format = COGL_PIXEL_FORMAT_G_8;
break;
default:
/* We can't handle this format so just give up */
return;
}
ctx = gles2_ctx->context;
winsys = ctx->display->renderer->winsys_vtable;
/* We need to make sure the rendering on the GLES2 context is
* complete before the blit will be ready in the GLES2 context */
ctx->glFinish ();
/* We need to force Cogl to rebind the texture because according to
* the GL spec a shared texture isn't guaranteed to be updated until
* is rebound */
_cogl_get_texture_unit (0)->dirty_gl_texture = TRUE;
/* Temporarily switch back to the Cogl context */
winsys->restore_context (ctx);
dst_texture =
cogl_gles2_texture_2d_new_from_handle (gles2_ctx->context,
gles2_ctx,
tex_id,
tex_object_data->width,
tex_object_data->height,
internal_format,
NULL /* error */);
if (dst_texture)
{
CoglTexture *src_texture =
COGL_OFFSCREEN (gles2_ctx->read_buffer)->texture;
CoglPipeline *pipeline = cogl_pipeline_new (ctx);
const CoglOffscreenFlags flags = COGL_OFFSCREEN_DISABLE_DEPTH_AND_STENCIL;
CoglOffscreen *offscreen =
_cogl_offscreen_new_to_texture_full (COGL_TEXTURE (dst_texture),
flags, level);
int src_width = cogl_texture_get_width (src_texture);
int src_height = cogl_texture_get_height (src_texture);
/* The framebuffer size might be different from the texture size
* if a level > 0 is used */
int dst_width =
cogl_framebuffer_get_width (COGL_FRAMEBUFFER (offscreen));
int dst_height =
cogl_framebuffer_get_height (COGL_FRAMEBUFFER (offscreen));
float x_1, y_1, x_2, y_2, s_1, t_1, s_2, t_2;
cogl_pipeline_set_layer_texture (pipeline, 0, src_texture);
cogl_pipeline_set_blend (pipeline,
"RGBA = ADD(SRC_COLOR, 0)",
NULL);
cogl_pipeline_set_layer_filters (pipeline,
0, /* layer_num */
COGL_PIPELINE_FILTER_NEAREST,
COGL_PIPELINE_FILTER_NEAREST);
x_1 = dst_x * 2.0f / dst_width - 1.0f;
y_1 = dst_y * 2.0f / dst_height - 1.0f;
x_2 = x_1 + width * 2.0f / dst_width;
y_2 = y_1 + height * 2.0f / dst_height;
s_1 = src_x / (float) src_width;
t_1 = 1.0f - src_y / (float) src_height;
s_2 = (src_x + width) / (float) src_width;
t_2 = 1.0f - (src_y + height) / (float) src_height;
cogl_framebuffer_draw_textured_rectangle (COGL_FRAMEBUFFER (offscreen),
pipeline,
x_1, y_1,
x_2, y_2,
s_1, t_1,
s_2, t_2);
_cogl_framebuffer_flush_journal (COGL_FRAMEBUFFER (offscreen));
/* We need to make sure the rendering is complete before the
* blit will be ready in the GLES2 context */
ctx->glFinish ();
cogl_object_unref (pipeline);
cogl_object_unref (dst_texture);
cogl_object_unref (offscreen);
}
winsys->set_gles2_context (gles2_ctx, NULL);
/* From what I understand of the GL spec, changes to a shared object
* are not guaranteed to be propagated to another context until that
* object is rebound in that context so we can just rebind it
* here */
gles2_ctx->vtable->glBindTexture (GL_TEXTURE_2D, tex_id);
}
/* We wrap glBindFramebuffer so that when framebuffer 0 is bound
* we can instead bind the write_framebuffer passed to
* cogl_push_gles2_context().
*/
static void
gl_bind_framebuffer_wrapper (GLenum target, GLuint framebuffer)
{
CoglGLES2Context *gles2_ctx = current_gles2_context;
gles2_ctx->current_fbo_handle = framebuffer;
if (framebuffer == 0 && cogl_is_offscreen (gles2_ctx->write_buffer))
{
CoglGLES2Offscreen *write = gles2_ctx->gles2_write_buffer;
framebuffer = write->gl_framebuffer.fbo_handle;
}
gles2_ctx->context->glBindFramebuffer (target, framebuffer);
cogl-gles2-context: Flip the rendering when framebuffer is offscreen 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. Reviewed-by: Robert Bragg <robert@linux.intel.com> (cherry picked from commit d589bf19e51f22c3241b2a18db10f22131ac126a)
2012-08-07 06:48:56 -04:00
update_current_flip_state (gles2_ctx);
}
static int
transient_bind_read_buffer (CoglGLES2Context *gles2_ctx)
{
if (gles2_ctx->current_fbo_handle == 0)
{
if (cogl_is_offscreen (gles2_ctx->read_buffer))
{
CoglGLES2Offscreen *read = gles2_ctx->gles2_read_buffer;
GLuint read_fbo_handle = read->gl_framebuffer.fbo_handle;
gles2_ctx->context->glBindFramebuffer (GL_FRAMEBUFFER,
read_fbo_handle);
return RESTORE_FB_FROM_OFFSCREEN;
}
else
{
_cogl_gl_framebuffer_bind (gles2_ctx->read_buffer,
0 /* target ignored */);
return RESTORE_FB_FROM_ONSCREEN;
}
}
else
return RESTORE_FB_NONE;
}
static void
restore_write_buffer (CoglGLES2Context *gles2_ctx,
int restore_mode)
{
switch (restore_mode)
{
case RESTORE_FB_FROM_OFFSCREEN:
gl_bind_framebuffer_wrapper (GL_FRAMEBUFFER, 0);
break;
case RESTORE_FB_FROM_ONSCREEN:
/* Note: we can't restore the original write buffer using
* _cogl_gl_framebuffer_bind() if it's an offscreen
* framebuffer because _cogl_gl_framebuffer_bind() doesn't
* know about the fbo handle owned by the gles2 context.
*/
if (cogl_is_offscreen (gles2_ctx->write_buffer))
gl_bind_framebuffer_wrapper (GL_FRAMEBUFFER, 0);
else
_cogl_gl_framebuffer_bind (gles2_ctx->write_buffer, GL_FRAMEBUFFER);
break;
case RESTORE_FB_NONE:
break;
}
}
/* We wrap glReadPixels so when framebuffer 0 is bound then we can
* read from the read_framebuffer passed to cogl_push_gles2_context().
*/
static void
gl_read_pixels_wrapper (GLint x,
GLint y,
GLsizei width,
GLsizei height,
GLenum format,
GLenum type,
GLvoid *pixels)
{
CoglGLES2Context *gles2_ctx = current_gles2_context;
int restore_mode = transient_bind_read_buffer (gles2_ctx);
gles2_ctx->context->glReadPixels (x, y, width, height, format, type, pixels);
restore_write_buffer (gles2_ctx, restore_mode);
cogl-gles2-context: Flip the rendering when framebuffer is offscreen 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. Reviewed-by: Robert Bragg <robert@linux.intel.com> (cherry picked from commit d589bf19e51f22c3241b2a18db10f22131ac126a)
2012-08-07 06:48:56 -04:00
/* 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
gl_copy_tex_image_2d_wrapper (GLenum target,
GLint level,
GLenum internal_format,
GLint x,
GLint y,
GLsizei width,
GLsizei height,
GLint border)
{
CoglGLES2Context *gles2_ctx = current_gles2_context;
cogl-gles2-context: Wrap glCopyTex{Sub,}Image2D to flip the result When the CoglGLES2Context is bound to read from a CoglOffscreen then the result will be upside down from what GL expects if glCopyTexImage2D is used directly. To fix that, this patch now wraps glCopyTexImage2D and glCopyTexSubImage2D so that the copy is doing by binding an FBO to the target texture and then rendering a quad sampling from the texture in the offscreen framebuffer. The rendering is done using the Cogl context rather than the GLES2 context because otherwise it would have to do a fair bit of work to try and stash the old state on the context before setting up the state to do the blit. The down side of this is that the contexts need to be synchronized so that the rendering will be up-to-date. As far as I understand from the GL spec, this requires a glFinish and then the texture needs to be rebound in the new context because updates to shared objects are guaranteed to be reflected until the object is rebound. GLES2 supports using glCopyTexImage2D for cube map textures. As Cogl doesn't currently have support for cube maps, it is quite hard to get that to work with this patch. For now attempts to copy to a cube map texture will just be sliently ignored. This patch also includes a test case which renders an image to the framebuffer and then copies it to a texture. The texture is then rendered back to the framebuffer and the contents are checked for the correct orientation using glReadPixels. Reviewed-by: Robert Bragg <robert@linux.intel.com> (cherry picked from commit 30b6da8134bad95267265e26685c7475f6c351c9)
2012-08-09 11:03:35 -04:00
/* If we are reading from a CoglOffscreen buffer then the image will
* be upside down with respect to what GL expects so we can't use
* glCopyTexImage2D. Instead we we'll try to use the Cogl API to
* flip it */
if (gles2_ctx->current_fbo_handle == 0 &&
cogl_is_offscreen (gles2_ctx->read_buffer))
{
/* This will only work with the GL_TEXTURE_2D target. FIXME:
* GLES2 also supports setting cube map textures with
* glTexImage2D so we need to handle that too */
if (target != GL_TEXTURE_2D)
return;
/* Create an empty texture to hold the data */
gles2_ctx->vtable->glTexImage2D (target,
level,
internal_format,
width, height,
border,
internal_format, /* format */
GL_UNSIGNED_BYTE, /* type */
NULL /* data */);
copy_flipped_texture (gles2_ctx,
level,
x, y, /* src_x/src_y */
0, 0, /* dst_x/dst_y */
width, height);
}
else
{
int restore_mode = transient_bind_read_buffer (gles2_ctx);
cogl-gles2-context: Wrap glCopyTex{Sub,}Image2D to flip the result When the CoglGLES2Context is bound to read from a CoglOffscreen then the result will be upside down from what GL expects if glCopyTexImage2D is used directly. To fix that, this patch now wraps glCopyTexImage2D and glCopyTexSubImage2D so that the copy is doing by binding an FBO to the target texture and then rendering a quad sampling from the texture in the offscreen framebuffer. The rendering is done using the Cogl context rather than the GLES2 context because otherwise it would have to do a fair bit of work to try and stash the old state on the context before setting up the state to do the blit. The down side of this is that the contexts need to be synchronized so that the rendering will be up-to-date. As far as I understand from the GL spec, this requires a glFinish and then the texture needs to be rebound in the new context because updates to shared objects are guaranteed to be reflected until the object is rebound. GLES2 supports using glCopyTexImage2D for cube map textures. As Cogl doesn't currently have support for cube maps, it is quite hard to get that to work with this patch. For now attempts to copy to a cube map texture will just be sliently ignored. This patch also includes a test case which renders an image to the framebuffer and then copies it to a texture. The texture is then rendered back to the framebuffer and the contents are checked for the correct orientation using glReadPixels. Reviewed-by: Robert Bragg <robert@linux.intel.com> (cherry picked from commit 30b6da8134bad95267265e26685c7475f6c351c9)
2012-08-09 11:03:35 -04:00
gles2_ctx->context->glCopyTexImage2D (target, level, internal_format,
x, y, width, height, border);
restore_write_buffer (gles2_ctx, restore_mode);
cogl-gles2-context: Wrap glCopyTex{Sub,}Image2D to flip the result When the CoglGLES2Context is bound to read from a CoglOffscreen then the result will be upside down from what GL expects if glCopyTexImage2D is used directly. To fix that, this patch now wraps glCopyTexImage2D and glCopyTexSubImage2D so that the copy is doing by binding an FBO to the target texture and then rendering a quad sampling from the texture in the offscreen framebuffer. The rendering is done using the Cogl context rather than the GLES2 context because otherwise it would have to do a fair bit of work to try and stash the old state on the context before setting up the state to do the blit. The down side of this is that the contexts need to be synchronized so that the rendering will be up-to-date. As far as I understand from the GL spec, this requires a glFinish and then the texture needs to be rebound in the new context because updates to shared objects are guaranteed to be reflected until the object is rebound. GLES2 supports using glCopyTexImage2D for cube map textures. As Cogl doesn't currently have support for cube maps, it is quite hard to get that to work with this patch. For now attempts to copy to a cube map texture will just be sliently ignored. This patch also includes a test case which renders an image to the framebuffer and then copies it to a texture. The texture is then rendered back to the framebuffer and the contents are checked for the correct orientation using glReadPixels. Reviewed-by: Robert Bragg <robert@linux.intel.com> (cherry picked from commit 30b6da8134bad95267265e26685c7475f6c351c9)
2012-08-09 11:03:35 -04:00
set_texture_object_data (gles2_ctx,
target,
level,
internal_format,
width, height);
}
}
static void
gl_copy_tex_sub_image_2d_wrapper (GLenum target,
GLint level,
GLint xoffset,
GLint yoffset,
GLint x,
GLint y,
GLsizei width,
GLsizei height)
{
CoglGLES2Context *gles2_ctx = current_gles2_context;
cogl-gles2-context: Wrap glCopyTex{Sub,}Image2D to flip the result When the CoglGLES2Context is bound to read from a CoglOffscreen then the result will be upside down from what GL expects if glCopyTexImage2D is used directly. To fix that, this patch now wraps glCopyTexImage2D and glCopyTexSubImage2D so that the copy is doing by binding an FBO to the target texture and then rendering a quad sampling from the texture in the offscreen framebuffer. The rendering is done using the Cogl context rather than the GLES2 context because otherwise it would have to do a fair bit of work to try and stash the old state on the context before setting up the state to do the blit. The down side of this is that the contexts need to be synchronized so that the rendering will be up-to-date. As far as I understand from the GL spec, this requires a glFinish and then the texture needs to be rebound in the new context because updates to shared objects are guaranteed to be reflected until the object is rebound. GLES2 supports using glCopyTexImage2D for cube map textures. As Cogl doesn't currently have support for cube maps, it is quite hard to get that to work with this patch. For now attempts to copy to a cube map texture will just be sliently ignored. This patch also includes a test case which renders an image to the framebuffer and then copies it to a texture. The texture is then rendered back to the framebuffer and the contents are checked for the correct orientation using glReadPixels. Reviewed-by: Robert Bragg <robert@linux.intel.com> (cherry picked from commit 30b6da8134bad95267265e26685c7475f6c351c9)
2012-08-09 11:03:35 -04:00
/* If we are reading from a CoglOffscreen buffer then the image will
* be upside down with respect to what GL expects so we can't use
* glCopyTexSubImage2D. Instead we we'll try to use the Cogl API to
* flip it */
if (gles2_ctx->current_fbo_handle == 0 &&
cogl_is_offscreen (gles2_ctx->read_buffer))
{
/* This will only work with the GL_TEXTURE_2D target. FIXME:
* GLES2 also supports setting cube map textures with
* glTexImage2D so we need to handle that too */
if (target != GL_TEXTURE_2D)
return;
copy_flipped_texture (gles2_ctx,
level,
x, y, /* src_x/src_y */
xoffset, yoffset, /* dst_x/dst_y */
width, height);
}
else
{
int restore_mode = transient_bind_read_buffer (gles2_ctx);
cogl-gles2-context: Wrap glCopyTex{Sub,}Image2D to flip the result When the CoglGLES2Context is bound to read from a CoglOffscreen then the result will be upside down from what GL expects if glCopyTexImage2D is used directly. To fix that, this patch now wraps glCopyTexImage2D and glCopyTexSubImage2D so that the copy is doing by binding an FBO to the target texture and then rendering a quad sampling from the texture in the offscreen framebuffer. The rendering is done using the Cogl context rather than the GLES2 context because otherwise it would have to do a fair bit of work to try and stash the old state on the context before setting up the state to do the blit. The down side of this is that the contexts need to be synchronized so that the rendering will be up-to-date. As far as I understand from the GL spec, this requires a glFinish and then the texture needs to be rebound in the new context because updates to shared objects are guaranteed to be reflected until the object is rebound. GLES2 supports using glCopyTexImage2D for cube map textures. As Cogl doesn't currently have support for cube maps, it is quite hard to get that to work with this patch. For now attempts to copy to a cube map texture will just be sliently ignored. This patch also includes a test case which renders an image to the framebuffer and then copies it to a texture. The texture is then rendered back to the framebuffer and the contents are checked for the correct orientation using glReadPixels. Reviewed-by: Robert Bragg <robert@linux.intel.com> (cherry picked from commit 30b6da8134bad95267265e26685c7475f6c351c9)
2012-08-09 11:03:35 -04:00
gles2_ctx->context->glCopyTexSubImage2D (target, level,
xoffset, yoffset,
x, y, width, height);
restore_write_buffer (gles2_ctx, restore_mode);
}
}
static GLuint
gl_create_shader_wrapper (GLenum type)
{
CoglGLES2Context *gles2_ctx = current_gles2_context;
GLuint id;
id = gles2_ctx->context->glCreateShader (type);
if (id != 0)
{
CoglGLES2ShaderData *data = g_slice_new (CoglGLES2ShaderData);
data->object_id = id;
data->type = type;
data->ref_count = 1;
data->deleted = FALSE;
g_hash_table_insert (gles2_ctx->shader_map,
GINT_TO_POINTER (id),
data);
}
return id;
}
static void
gl_delete_shader_wrapper (GLuint shader)
{
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->deleted)
{
shader_data->deleted = TRUE;
shader_data_unref (gles2_ctx, shader_data);
}
gles2_ctx->context->glDeleteShader (shader);
}
static GLuint
gl_create_program_wrapper (void)
{
CoglGLES2Context *gles2_ctx = current_gles2_context;
GLuint id;
id = gles2_ctx->context->glCreateProgram ();
if (id != 0)
{
CoglGLES2ProgramData *data = g_slice_new (CoglGLES2ProgramData);
data->object_id = id;
data->attached_shaders = NULL;
data->ref_count = 1;
data->deleted = FALSE;
data->context = gles2_ctx;
cogl-gles2-context: Flip the rendering when framebuffer is offscreen 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. Reviewed-by: Robert Bragg <robert@linux.intel.com> (cherry picked from commit d589bf19e51f22c3241b2a18db10f22131ac126a)
2012-08-07 06:48:56 -04:00
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);
}
return id;
}
static void
gl_delete_program_wrapper (GLuint program)
{
CoglGLES2Context *gles2_ctx = current_gles2_context;
CoglGLES2ProgramData *program_data;
if ((program_data = g_hash_table_lookup (gles2_ctx->program_map,
GINT_TO_POINTER (program))) &&
!program_data->deleted)
{
program_data->deleted = TRUE;
program_data_unref (program_data);
}
gles2_ctx->context->glDeleteProgram (program);
}
static void
gl_use_program_wrapper (GLuint program)
{
CoglGLES2Context *gles2_ctx = current_gles2_context;
CoglGLES2ProgramData *program_data;
program_data = g_hash_table_lookup (gles2_ctx->program_map,
GINT_TO_POINTER (program));
if (program_data)
program_data->ref_count++;
if (gles2_ctx->current_program)
program_data_unref (gles2_ctx->current_program);
gles2_ctx->current_program = program_data;
gles2_ctx->context->glUseProgram (program);
}
static void
gl_attach_shader_wrapper (GLuint program,
GLuint shader)
{
CoglGLES2Context *gles2_ctx = current_gles2_context;
CoglGLES2ProgramData *program_data;
CoglGLES2ShaderData *shader_data;
if ((program_data = g_hash_table_lookup (gles2_ctx->program_map,
GINT_TO_POINTER (program))) &&
(shader_data = g_hash_table_lookup (gles2_ctx->shader_map,
GINT_TO_POINTER (shader))) &&
/* Ignore attempts to attach a shader that is already attached */
g_list_find (program_data->attached_shaders, shader_data) == NULL)
{
shader_data->ref_count++;
program_data->attached_shaders =
g_list_prepend (program_data->attached_shaders, shader_data);
}
gles2_ctx->context->glAttachShader (program, shader);
}
static void
gl_detach_shader_wrapper (GLuint program,
GLuint shader)
{
CoglGLES2Context *gles2_ctx = current_gles2_context;
CoglGLES2ProgramData *program_data;
CoglGLES2ShaderData *shader_data;
if ((program_data = g_hash_table_lookup (gles2_ctx->program_map,
GINT_TO_POINTER (program))) &&
(shader_data = g_hash_table_lookup (gles2_ctx->shader_map,
GINT_TO_POINTER (shader))))
detach_shader (program_data, shader_data);
gles2_ctx->context->glDetachShader (program, shader);
}
cogl-gles2-context: Flip the rendering when framebuffer is offscreen 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. Reviewed-by: Robert Bragg <robert@linux.intel.com> (cherry picked from commit d589bf19e51f22c3241b2a18db10f22131ac126a)
2012-08-07 06:48:56 -04:00
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 *));
GLint *length_copy = g_alloca (count * sizeof (GLint));
cogl-gles2-context: Flip the rendering when framebuffer is offscreen 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. Reviewed-by: Robert Bragg <robert@linux.intel.com> (cherry picked from commit d589bf19e51f22c3241b2a18db10f22131ac126a)
2012-08-07 06:48:56 -04:00
int i;
/* First, copy the shader into a new string; then, replace all
* instances of the symbol 'main' with our replacement symbol
* so we can provide our own wrapper main function; then append
* that wrapper function. */
cogl-gles2-context: Flip the rendering when framebuffer is offscreen 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. Reviewed-by: Robert Bragg <robert@linux.intel.com> (cherry picked from commit d589bf19e51f22c3241b2a18db10f22131ac126a)
2012-08-07 06:48:56 -04:00
for (i = 0; i < count; i++)
{
int string_length = length ? length[i] : strlen (string[i]);
int prelude_length = strlen (main_wrapper_prelude);
int wrapper_length = strlen (main_wrapper_function);
cogl-gles2-context: Flip the rendering when framebuffer is offscreen 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. Reviewed-by: Robert Bragg <robert@linux.intel.com> (cherry picked from commit d589bf19e51f22c3241b2a18db10f22131ac126a)
2012-08-07 06:48:56 -04:00
length_copy[i] = string_length + prelude_length + wrapper_length;
string_copy[i] = g_malloc (length_copy[i] + 1);
memcpy (string_copy[i], string[i], string_length);
string_copy[i][string_length] = '\0';
cogl-gles2-context: Flip the rendering when framebuffer is offscreen 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. Reviewed-by: Robert Bragg <robert@linux.intel.com> (cherry picked from commit d589bf19e51f22c3241b2a18db10f22131ac126a)
2012-08-07 06:48:56 -04:00
replace_token (string_copy[i],
"main",
MAIN_WRAPPER_REPLACEMENT_NAME,
string_length);
memmove (&string_copy[i][prelude_length], string_copy[i],
string_length);
memcpy (string_copy[i], main_wrapper_prelude, prelude_length);
memcpy (&string_copy[i][prelude_length + string_length],
main_wrapper_function, wrapper_length);
string_copy[i][length_copy[i]] = '\0';
cogl-gles2-context: Flip the rendering when framebuffer is offscreen 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. Reviewed-by: Robert Bragg <robert@linux.intel.com> (cherry picked from commit d589bf19e51f22c3241b2a18db10f22131ac126a)
2012-08-07 06:48:56 -04:00
}
gles2_ctx->context->glShaderSource (shader,
count,
(const char *const *) string_copy,
length_copy);
cogl-gles2-context: Flip the rendering when framebuffer is offscreen 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. Reviewed-by: Robert Bragg <robert@linux.intel.com> (cherry picked from commit d589bf19e51f22c3241b2a18db10f22131ac126a)
2012-08-07 06:48:56 -04:00
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++)
obj[count_out++] = tmp_buf[i];
cogl-gles2-context: Flip the rendering when framebuffer is offscreen 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. Reviewed-by: Robert Bragg <robert@linux.intel.com> (cherry picked from commit d589bf19e51f22c3241b2a18db10f22131ac126a)
2012-08-07 06:48:56 -04:00
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
gl_active_texture_wrapper (GLenum texture)
{
CoglGLES2Context *gles2_ctx = current_gles2_context;
int texture_unit;
gles2_ctx->context->glActiveTexture (texture);
texture_unit = texture - GL_TEXTURE0;
/* If the application is binding some odd looking texture unit
* numbers then we'll just ignore it and hope that GL has generated
* an error */
if (texture_unit >= 0 && texture_unit < 512)
{
gles2_ctx->current_texture_unit = texture_unit;
g_array_set_size (gles2_ctx->texture_units,
MAX (texture_unit, gles2_ctx->texture_units->len));
}
}
static void
gl_delete_textures_wrapper (GLsizei n,
const GLuint *textures)
{
CoglGLES2Context *gles2_ctx = current_gles2_context;
int texture_index;
int texture_unit;
gles2_ctx->context->glDeleteTextures (n, textures);
for (texture_index = 0; texture_index < n; texture_index++)
{
/* Reset any texture units that have any of these textures bound */
for (texture_unit = 0;
texture_unit < gles2_ctx->texture_units->len;
texture_unit++)
{
CoglGLES2TextureUnitData *unit =
&g_array_index (gles2_ctx->texture_units,
CoglGLES2TextureUnitData,
texture_unit);
if (unit->current_texture_2d == textures[texture_index])
unit->current_texture_2d = 0;
}
/* Remove the binding. We can do this immediately because unlike
* shader objects the deletion isn't delayed until the object is
* unbound */
g_hash_table_remove (gles2_ctx->texture_object_map,
GUINT_TO_POINTER (textures[texture_index]));
}
}
static void
gl_bind_texture_wrapper (GLenum target,
GLuint texture)
{
CoglGLES2Context *gles2_ctx = current_gles2_context;
gles2_ctx->context->glBindTexture (target, texture);
if (target == GL_TEXTURE_2D)
{
CoglGLES2TextureUnitData *unit =
&g_array_index (gles2_ctx->texture_units,
CoglGLES2TextureUnitData,
gles2_ctx->current_texture_unit);
unit->current_texture_2d = texture;
}
}
static void
gl_tex_image_2d_wrapper (GLenum target,
GLint level,
GLint internal_format,
GLsizei width,
GLsizei height,
GLint border,
GLenum format,
GLenum type,
const GLvoid *pixels)
{
CoglGLES2Context *gles2_ctx = current_gles2_context;
gles2_ctx->context->glTexImage2D (target,
level,
internal_format,
width, height,
border,
format,
type,
pixels);
set_texture_object_data (gles2_ctx,
target,
level,
internal_format,
width, height);
}
static void
_cogl_gles2_offscreen_free (CoglGLES2Offscreen *gles2_offscreen)
{
COGL_LIST_REMOVE (gles2_offscreen, list_node);
g_slice_free (CoglGLES2Offscreen, gles2_offscreen);
}
static void
force_delete_program_object (CoglGLES2Context *context,
CoglGLES2ProgramData *program_data)
{
if (!program_data->deleted)
{
context->context->glDeleteProgram (program_data->object_id);
program_data->deleted = TRUE;
program_data_unref (program_data);
}
}
static void
force_delete_shader_object (CoglGLES2Context *context,
CoglGLES2ShaderData *shader_data)
{
if (!shader_data->deleted)
{
context->context->glDeleteShader (shader_data->object_id);
shader_data->deleted = TRUE;
shader_data_unref (context, shader_data);
}
}
static void
force_delete_texture_object (CoglGLES2Context *context,
CoglGLES2TextureObjectData *texture_data)
{
context->context->glDeleteTextures (1, &texture_data->object_id);
}
static void
_cogl_gles2_context_free (CoglGLES2Context *gles2_context)
{
CoglContext *ctx = gles2_context->context;
const CoglWinsysVtable *winsys;
GList *objects, *l;
if (gles2_context->current_program)
program_data_unref (gles2_context->current_program);
/* Try to forcibly delete any shaders, programs and textures so that
* they won't get leaked. Because all GLES2 contexts are in the same
* share list as Cogl's context these won't get deleted by default.
* FIXME: we should do this for all of the other resources too, like
* textures */
objects = g_hash_table_get_values (gles2_context->program_map);
for (l = objects; l; l = l->next)
force_delete_program_object (gles2_context, l->data);
g_list_free (objects);
objects = g_hash_table_get_values (gles2_context->shader_map);
for (l = objects; l; l = l->next)
force_delete_shader_object (gles2_context, l->data);
g_list_free (objects);
objects = g_hash_table_get_values (gles2_context->texture_object_map);
for (l = objects; l; l = l->next)
force_delete_texture_object (gles2_context, l->data);
g_list_free (objects);
/* All of the program and shader objects should now be destroyed */
if (g_hash_table_size (gles2_context->program_map) > 0)
g_warning ("Program objects have been leaked from a CoglGLES2Context");
if (g_hash_table_size (gles2_context->shader_map) > 0)
g_warning ("Shader objects have been leaked from a CoglGLES2Context");
g_hash_table_destroy (gles2_context->program_map);
g_hash_table_destroy (gles2_context->shader_map);
g_hash_table_destroy (gles2_context->texture_object_map);
g_array_free (gles2_context->texture_units, TRUE);
winsys = ctx->display->renderer->winsys_vtable;
winsys->destroy_gles2_context (gles2_context);
while (gles2_context->foreign_offscreens.lh_first)
{
CoglGLES2Offscreen *gles2_offscreen =
gles2_context->foreign_offscreens.lh_first;
/* Note: this will also indirectly free the gles2_offscreen by
* calling the destroy notify for the _user_data */
cogl_object_set_user_data (COGL_OBJECT (gles2_offscreen->original_offscreen),
&offscreen_wrapper_key,
NULL,
NULL);
}
g_free (gles2_context->vtable);
g_free (gles2_context);
}
static void
free_shader_data (CoglGLES2ShaderData *data)
{
g_slice_free (CoglGLES2ShaderData, data);
}
static void
free_program_data (CoglGLES2ProgramData *data)
{
while (data->attached_shaders)
detach_shader (data,
data->attached_shaders->data);
g_slice_free (CoglGLES2ProgramData, data);
}
static void
free_texture_object_data (CoglGLES2TextureObjectData *data)
{
g_slice_free (CoglGLES2TextureObjectData, data);
}
CoglGLES2Context *
cogl_gles2_context_new (CoglContext *ctx, GError **error)
{
CoglGLES2Context *gles2_ctx;
const CoglWinsysVtable *winsys;
if (!cogl_has_feature (ctx, COGL_FEATURE_ID_GLES2_CONTEXT))
{
g_set_error (error, COGL_GLES2_CONTEXT_ERROR,
COGL_GLES2_CONTEXT_ERROR_UNSUPPORTED,
"Backend doesn't support creating GLES2 contexts");
return NULL;
}
gles2_ctx = g_malloc0 (sizeof (CoglGLES2Context));
gles2_ctx->context = ctx;
COGL_LIST_INIT (&gles2_ctx->foreign_offscreens);
winsys = ctx->display->renderer->winsys_vtable;
gles2_ctx->winsys = winsys->context_create_gles2_context (ctx, error);
if (gles2_ctx->winsys == NULL)
{
g_free (gles2_ctx);
return NULL;
}
cogl-gles2-context: Flip the rendering when framebuffer is offscreen 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. Reviewed-by: Robert Bragg <robert@linux.intel.com> (cherry picked from commit d589bf19e51f22c3241b2a18db10f22131ac126a)
2012-08-07 06:48:56 -04:00
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, \
gles_availability, \
extension_suffixes, extension_names)
#define COGL_EXT_FUNCTION(ret, name, args) \
gles2_ctx->vtable->name = ctx->name;
#define COGL_EXT_END()
#include "gl-prototypes/cogl-gles2-functions.h"
#undef COGL_EXT_BEGIN
#undef COGL_EXT_FUNCTION
#undef COGL_EXT_END
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;
gles2_ctx->vtable->glCopyTexSubImage2D = gl_copy_tex_sub_image_2d_wrapper;
gles2_ctx->vtable->glCreateShader = gl_create_shader_wrapper;
gles2_ctx->vtable->glDeleteShader = gl_delete_shader_wrapper;
gles2_ctx->vtable->glCreateProgram = gl_create_program_wrapper;
gles2_ctx->vtable->glDeleteProgram = gl_delete_program_wrapper;
gles2_ctx->vtable->glUseProgram = gl_use_program_wrapper;
gles2_ctx->vtable->glAttachShader = gl_attach_shader_wrapper;
gles2_ctx->vtable->glDetachShader = gl_detach_shader_wrapper;
cogl-gles2-context: Flip the rendering when framebuffer is offscreen 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. Reviewed-by: Robert Bragg <robert@linux.intel.com> (cherry picked from commit d589bf19e51f22c3241b2a18db10f22131ac126a)
2012-08-07 06:48:56 -04:00
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;
gles2_ctx->vtable->glActiveTexture = gl_active_texture_wrapper;
gles2_ctx->vtable->glDeleteTextures = gl_delete_textures_wrapper;
gles2_ctx->vtable->glBindTexture = gl_bind_texture_wrapper;
gles2_ctx->vtable->glTexImage2D = gl_tex_image_2d_wrapper;
cogl-gles2-context: Flip the rendering when framebuffer is offscreen 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. Reviewed-by: Robert Bragg <robert@linux.intel.com> (cherry picked from commit d589bf19e51f22c3241b2a18db10f22131ac126a)
2012-08-07 06:48:56 -04:00
/* 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,
g_direct_equal,
NULL, /* key_destroy */
(GDestroyNotify) free_shader_data);
gles2_ctx->program_map =
g_hash_table_new_full (g_direct_hash,
g_direct_equal,
NULL, /* key_destroy */
(GDestroyNotify) free_program_data);
gles2_ctx->texture_object_map =
g_hash_table_new_full (g_direct_hash,
g_direct_equal,
NULL, /* key_destroy */
(GDestroyNotify) free_texture_object_data);
gles2_ctx->texture_units = g_array_new (FALSE, /* not zero terminated */
TRUE, /* clear */
sizeof (CoglGLES2TextureUnitData));
gles2_ctx->current_texture_unit = 0;
g_array_set_size (gles2_ctx->texture_units, 1);
return _cogl_gles2_context_object_new (gles2_ctx);
}
const CoglGLES2Vtable *
cogl_gles2_context_get_vtable (CoglGLES2Context *gles2_ctx)
{
return gles2_ctx->vtable;
}
/* When drawing to a CoglFramebuffer from a separate context we have
* to be able to allocate ancillary buffers for that context...
*/
static CoglGLES2Offscreen *
_cogl_gles2_offscreen_allocate (CoglOffscreen *offscreen,
CoglGLES2Context *gles2_context,
GError **error)
{
CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (offscreen);
const CoglWinsysVtable *winsys;
GError *internal_error = NULL;
CoglGLES2Offscreen *gles2_offscreen;
if (!framebuffer->allocated &&
!cogl_framebuffer_allocate (framebuffer, error))
{
return NULL;
}
for (gles2_offscreen = gles2_context->foreign_offscreens.lh_first;
gles2_offscreen;
gles2_offscreen = gles2_offscreen->list_node.le_next)
{
if (gles2_offscreen->original_offscreen == offscreen)
return gles2_offscreen;
}
winsys = _cogl_framebuffer_get_winsys (framebuffer);
winsys->save_context (framebuffer->context);
if (!winsys->set_gles2_context (gles2_context, &internal_error))
{
winsys->restore_context (framebuffer->context);
g_error_free (internal_error);
g_set_error (error, COGL_FRAMEBUFFER_ERROR,
COGL_FRAMEBUFFER_ERROR_ALLOCATE,
"Failed to bind gles2 context to create framebuffer");
return NULL;
}
gles2_offscreen = g_slice_new0 (CoglGLES2Offscreen);
if (!_cogl_framebuffer_try_creating_gl_fbo (gles2_context->context,
offscreen->texture,
offscreen->texture_level,
offscreen->texture_level_width,
offscreen->texture_level_height,
&COGL_FRAMEBUFFER (offscreen)->config,
offscreen->allocation_flags,
&gles2_offscreen->gl_framebuffer))
{
winsys->restore_context (framebuffer->context);
g_slice_free (CoglGLES2Offscreen, gles2_offscreen);
g_set_error (error, COGL_FRAMEBUFFER_ERROR,
COGL_FRAMEBUFFER_ERROR_ALLOCATE,
"Failed to create an OpenGL framebuffer object");
return NULL;
}
winsys->restore_context (framebuffer->context);
gles2_offscreen->original_offscreen = offscreen;
COGL_LIST_INSERT_HEAD (&gles2_context->foreign_offscreens,
gles2_offscreen,
list_node);
/* So we avoid building up an ever growing collection of ancillary
* buffers for wrapped framebuffers, we make sure that the wrappers
* get freed when the original offscreen framebuffer is freed. */
cogl_object_set_user_data (COGL_OBJECT (framebuffer),
&offscreen_wrapper_key,
gles2_offscreen,
(CoglUserDataDestroyCallback)
_cogl_gles2_offscreen_free);
return gles2_offscreen;
}
CoglBool
cogl_push_gles2_context (CoglContext *ctx,
CoglGLES2Context *gles2_ctx,
CoglFramebuffer *read_buffer,
CoglFramebuffer *write_buffer,
GError **error)
{
const CoglWinsysVtable *winsys = ctx->display->renderer->winsys_vtable;
GError *internal_error = NULL;
_COGL_RETURN_VAL_IF_FAIL (gles2_ctx != NULL, FALSE);
/* The read/write buffers are properties of the gles2 context and we
* don't currently track the read/write buffers as part of the stack
* entries so we explicitly don't allow the same context to be
* pushed multiple times. */
if (g_queue_find (&ctx->gles2_context_stack, gles2_ctx))
{
g_critical ("Pushing the same GLES2 context multiple times isn't "
"supported");
return FALSE;
}
if (ctx->gles2_context_stack.length == 0)
{
_cogl_journal_flush (read_buffer->journal);
if (write_buffer != read_buffer)
_cogl_journal_flush (write_buffer->journal);
winsys->save_context (ctx);
}
else
gles2_ctx->vtable->glFlush ();
if (gles2_ctx->read_buffer != read_buffer)
{
if (cogl_is_offscreen (read_buffer))
{
gles2_ctx->gles2_read_buffer =
_cogl_gles2_offscreen_allocate (COGL_OFFSCREEN (read_buffer),
gles2_ctx,
error);
/* XXX: what consistency guarantees should this api have?
*
* It should be safe to return at this point but we provide
* no guarantee to the caller whether their given buffers
* may be referenced and old buffers unreferenced even
* if the _push fails. */
if (!gles2_ctx->gles2_read_buffer)
return FALSE;
}
else
gles2_ctx->gles2_read_buffer = NULL;
if (gles2_ctx->read_buffer)
cogl_object_unref (gles2_ctx->read_buffer);
gles2_ctx->read_buffer = cogl_object_ref (read_buffer);
}
if (gles2_ctx->write_buffer != write_buffer)
{
if (cogl_is_offscreen (write_buffer))
{
gles2_ctx->gles2_write_buffer =
_cogl_gles2_offscreen_allocate (COGL_OFFSCREEN (write_buffer),
gles2_ctx,
error);
/* XXX: what consistency guarantees should this api have?
*
* It should be safe to return at this point but we provide
* no guarantee to the caller whether their given buffers
* may be referenced and old buffers unreferenced even
* if the _push fails. */
if (!gles2_ctx->gles2_write_buffer)
return FALSE;
}
else
gles2_ctx->gles2_write_buffer = NULL;
if (gles2_ctx->write_buffer)
cogl_object_unref (gles2_ctx->write_buffer);
gles2_ctx->write_buffer = cogl_object_ref (write_buffer);
cogl-gles2-context: Flip the rendering when framebuffer is offscreen 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. Reviewed-by: Robert Bragg <robert@linux.intel.com> (cherry picked from commit d589bf19e51f22c3241b2a18db10f22131ac126a)
2012-08-07 06:48:56 -04:00
update_current_flip_state (gles2_ctx);
}
if (!winsys->set_gles2_context (gles2_ctx, &internal_error))
{
winsys->restore_context (ctx);
g_error_free (internal_error);
g_set_error (error, COGL_GLES2_CONTEXT_ERROR,
COGL_GLES2_CONTEXT_ERROR_DRIVER,
"Driver failed to make GLES2 context current");
return FALSE;
}
g_queue_push_tail (&ctx->gles2_context_stack, gles2_ctx);
/* The last time this context was pushed may have been with a
* different offscreen draw framebuffer and so if GL framebuffer 0
* is bound for this GLES2 context we may need to bind a new,
* corresponding, window system framebuffer... */
if (gles2_ctx->current_fbo_handle == 0)
{
if (cogl_is_offscreen (gles2_ctx->write_buffer))
{
CoglGLES2Offscreen *write = gles2_ctx->gles2_write_buffer;
GLuint handle = write->gl_framebuffer.fbo_handle;
gles2_ctx->context->glBindFramebuffer (GL_FRAMEBUFFER, handle);
}
}
current_gles2_context = gles2_ctx;
/* If this is the first time this gles2 context has been used then
* we'll force the viewport and scissor to the right size. GL has
* the semantics that the viewport and scissor default to the size
* of the first surface the context is used with. If the first
* CoglFramebuffer that this context is used with is an offscreen,
* then the surface from GL's point of view will be the 1x1 dummy
* surface so the viewport will be wrong. Therefore we just override
* the default viewport and scissor here */
if (!gles2_ctx->has_been_bound)
{
int fb_width = cogl_framebuffer_get_width (write_buffer);
int fb_height = cogl_framebuffer_get_height (write_buffer);
gles2_ctx->vtable->glViewport (0, 0, /* x/y */
fb_width, fb_height);
gles2_ctx->vtable->glScissor (0, 0, /* x/y */
fb_width, fb_height);
gles2_ctx->has_been_bound = TRUE;
}
return TRUE;
}
CoglGLES2Vtable *
cogl_gles2_get_current_vtable (void)
{
return current_gles2_context ? current_gles2_context->vtable : NULL;
}
void
cogl_pop_gles2_context (CoglContext *ctx)
{
CoglGLES2Context *gles2_ctx;
const CoglWinsysVtable *winsys = ctx->display->renderer->winsys_vtable;
_COGL_RETURN_IF_FAIL (ctx->gles2_context_stack.length > 0);
g_queue_pop_tail (&ctx->gles2_context_stack);
gles2_ctx = g_queue_peek_tail (&ctx->gles2_context_stack);
if (gles2_ctx)
{
winsys->set_gles2_context (gles2_ctx, NULL);
current_gles2_context = gles2_ctx;
}
else
{
winsys->restore_context (ctx);
current_gles2_context = NULL;
}
}
CoglTexture2D *
cogl_gles2_texture_2d_new_from_handle (CoglContext *ctx,
CoglGLES2Context *gles2_ctx,
unsigned int handle,
int width,
int height,
CoglPixelFormat internal_format,
GError **error)
{
return cogl_texture_2d_new_from_foreign (ctx,
handle,
width,
height,
internal_format,
error);
}
CoglBool
cogl_gles2_texture_get_handle (CoglTexture *texture,
unsigned int *handle,
unsigned int *target)
{
return cogl_texture_get_gl_texture (texture, handle, target);
}