diff options
Diffstat (limited to 'cogl/cogl-gles2-context.c')
-rw-r--r-- | cogl/cogl-gles2-context.c | 1966 |
1 files changed, 0 insertions, 1966 deletions
diff --git a/cogl/cogl-gles2-context.c b/cogl/cogl-gles2-context.c deleted file mode 100644 index 33c43879..00000000 --- a/cogl/cogl-gles2-context.c +++ /dev/null @@ -1,1966 +0,0 @@ -/* - * Cogl - * - * A Low Level GPU Graphics and Utilities API - * - * Copyright (C) 2011 Collabora Ltd. - * Copyright (C) 2012 Intel Corporation. - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, copy, - * modify, merge, publish, distribute, sublicense, and/or sell copies - * of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - * 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 - -#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-framebuffer-gl-private.h" -#include "cogl-onscreen-template-private.h" -#include "cogl-renderer-private.h" -#include "cogl-swap-chain-private.h" -#include "cogl-texture-2d-gl.h" -#include "cogl-texture-2d-private.h" -#include "cogl-pipeline-opengl-private.h" -#include "cogl-error-private.h" -#include "cogl-gtype-private.h" - -static void _cogl_gles2_context_free (CoglGLES2Context *gles2_context); - -COGL_OBJECT_DEFINE (GLES2Context, gles2_context); -COGL_GTYPE_DEFINE_CLASS (GLES2Context, gles2_context); - -static CoglGLES2Context *current_gles2_context; - -static CoglUserDataKey offscreen_wrapper_key; - -/* The application's main function is renamed to this so that we can - * provide an alternative main function */ -#define MAIN_WRAPPER_REPLACEMENT_NAME "_c31" -/* This uniform is used to flip the rendering or not depending on - * whether we are rendering to an offscreen buffer or not */ -#define MAIN_WRAPPER_FLIP_UNIFORM "_cogl_flip_vector" -/* These comments are used to delimit the added wrapper snippet so - * that we can remove it again when the shader source is requested via - * glGetShaderSource */ -#define MAIN_WRAPPER_BEGIN "/*_COGL_WRAPPER_BEGIN*/" -#define MAIN_WRAPPER_END "/*_COGL_WRAPPER_END*/" - -/* This wrapper function around 'main' is appended to every vertex shader - * so that we can add some extra code to flip the rendering when - * rendering to an offscreen buffer */ -static const char -main_wrapper_function[] = - MAIN_WRAPPER_BEGIN "\n" - "uniform vec4 " MAIN_WRAPPER_FLIP_UNIFORM ";\n" - "\n" - "void\n" - "main ()\n" - "{\n" - " " MAIN_WRAPPER_REPLACEMENT_NAME " ();\n" - " gl_Position *= " MAIN_WRAPPER_FLIP_UNIFORM ";\n" - "}\n" - MAIN_WRAPPER_END; - -enum { - RESTORE_FB_NONE, - RESTORE_FB_FROM_OFFSCREEN, - RESTORE_FB_FROM_ONSCREEN, -}; - -uint32_t -_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; - } - } -} - -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; - } -} - -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); - - 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_with_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); - - 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_framebuffer_gl_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_framebuffer_gl_bind() if it's an offscreen - * framebuffer because _cogl_framebuffer_gl_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_framebuffer_gl_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); - - /* 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; - - /* 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); - - gles2_ctx->context->glCopyTexImage2D (target, level, internal_format, - x, y, width, height, border); - - restore_write_buffer (gles2_ctx, restore_mode); - - 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; - - /* 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); - - 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; - 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); -} - -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 + 1) * sizeof (char *)); - int *length_copy = g_alloca ((count + 1) * sizeof (int)); - int i; - - /* Replace any occurences of the symbol 'main' with a different - * symbol so that we can provide our own wrapper main - * function */ - - for (i = 0; i < count; i++) - { - int string_length; - - if (length == NULL || length[i] < 0) - string_length = strlen (string[i]); - else - string_length = length[i]; - - string_copy[i] = g_memdup (string[i], string_length); - - replace_token (string_copy[i], - "main", - MAIN_WRAPPER_REPLACEMENT_NAME, - string_length); - - length_copy[i] = string_length; - } - - string_copy[count] = (char *) main_wrapper_function; - length_copy[count] = sizeof (main_wrapper_function) - 1; - - gles2_ctx->context->glShaderSource (shader, - count + 1, - (const char *const *) string_copy, - length_copy); - - /* Note: we don't need to free the last entry in string_copy[] - * because it is our static wrapper string... */ - 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); - static const char wrapper_marker[] = MAIN_WRAPPER_BEGIN; - char *wrapper_start; - - /* Strip out the wrapper snippet we added when the source was - * specified */ - wrapper_start = _cogl_util_memmem (source, - copy_length, - wrapper_marker, - sizeof (wrapper_marker) - 1); - if (wrapper_start) - { - length = wrapper_start - source; - copy_length = length; - *wrapper_start = '\0'; - } - - /* Correct the name of the main function back to its original */ - 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 -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->link); - 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 (!_cogl_list_empty (&gles2_context->foreign_offscreens)) - { - CoglGLES2Offscreen *gles2_offscreen = - _cogl_container_of (gles2_context->foreign_offscreens.next, - CoglGLES2Offscreen, - link); - - /* 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, CoglError **error) -{ - CoglGLES2Context *gles2_ctx; - const CoglWinsysVtable *winsys; - - if (!cogl_has_feature (ctx, COGL_FEATURE_ID_GLES2_CONTEXT)) - { - _cogl_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; - } - - 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 = (void *) 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 = - (void *) gl_bind_framebuffer_wrapper; - gles2_ctx->vtable->glReadPixels = - (void *) gl_read_pixels_wrapper; - gles2_ctx->vtable->glCopyTexImage2D = - (void *) gl_copy_tex_image_2d_wrapper; - gles2_ctx->vtable->glCopyTexSubImage2D = - (void *) 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; - 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->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; - - 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, - CoglError **error) -{ - CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (offscreen); - const CoglWinsysVtable *winsys; - CoglError *internal_error = NULL; - CoglGLES2Offscreen *gles2_offscreen; - int level_width; - int level_height; - - if (!framebuffer->allocated && - !cogl_framebuffer_allocate (framebuffer, error)) - { - return NULL; - } - - _cogl_list_for_each (gles2_offscreen, - &gles2_context->foreign_offscreens, - link) - { - 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); - - cogl_error_free (internal_error); - _cogl_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); - - _cogl_texture_get_level_size (offscreen->texture, - offscreen->texture_level, - &level_width, - &level_height, - NULL); - - if (!_cogl_framebuffer_try_creating_gl_fbo (gles2_context->context, - offscreen->texture, - offscreen->texture_level, - level_width, - level_height, - offscreen->depth_texture, - &COGL_FRAMEBUFFER (offscreen)->config, - offscreen->allocation_flags, - &gles2_offscreen->gl_framebuffer)) - { - winsys->restore_context (framebuffer->context); - - g_slice_free (CoglGLES2Offscreen, gles2_offscreen); - - _cogl_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 (&gles2_context->foreign_offscreens, - &gles2_offscreen->link); - - /* 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, - CoglError **error) -{ - const CoglWinsysVtable *winsys = ctx->display->renderer->winsys_vtable; - CoglError *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); - - update_current_flip_state (gles2_ctx); - } - - if (!winsys->set_gles2_context (gles2_ctx, &internal_error)) - { - winsys->restore_context (ctx); - - cogl_error_free (internal_error); - _cogl_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 format) -{ - return cogl_texture_2d_gl_new_from_foreign (ctx, - handle, - width, - height, - format); -} - -CoglBool -cogl_gles2_texture_get_handle (CoglTexture *texture, - unsigned int *handle, - unsigned int *target) -{ - return cogl_texture_get_gl_texture (texture, handle, target); -} |