[cogl/wip/rib/cogl-1.12: 127/139] cogl-gles2-context: Keep some extra data for shaders and programs
- From: Robert Bragg <rbragg src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [cogl/wip/rib/cogl-1.12: 127/139] cogl-gles2-context: Keep some extra data for shaders and programs
- Date: Wed, 15 Aug 2012 13:14:07 +0000 (UTC)
commit 12a1ef72c0b7543178e0470047db5c4d8378de5c
Author: Neil Roberts <neil linux intel com>
Date: Mon Aug 6 18:57:15 2012 +0100
cogl-gles2-context: Keep some extra data for shaders and programs
All of the functions that create and destroy shaders are now wrapped
in the CoglGLES2Context so that we can track some extra data for them.
There are hash tables mapping object IDs to the corresponding data.
The data is currently not used for anything but will be in later
patches.
The glUseProgram, glAttachShader and glDetachShader functions
additionally need to be wrapped because GL does not delete shader
objects that are in use. Therefore we need to have a reference count
on the data so we can recognise when the last use has been removed.
The IDs are assumed to be specific to an individual CoglGLES2Context.
This is technically not the case because all of the CoglGLES2Contexts
are in the same share list. However we don't really want this to be
the case so currently we will assume sharing the object IDs between
contexts is undefined behaviour. Eventually we may want to actually
enforce this.
Reviewed-by: Robert Bragg <robert linux intel com>
(cherry picked from commit 05dc1e34785ae5f5484cd398ecc5464bd8bd3dcd)
cogl/cogl-gles2-context-private.h | 61 +++++++++
cogl/cogl-gles2-context.c | 266 +++++++++++++++++++++++++++++++++++++
2 files changed, 327 insertions(+), 0 deletions(-)
---
diff --git a/cogl/cogl-gles2-context-private.h b/cogl/cogl-gles2-context-private.h
index 7902c94..a45214a 100644
--- a/cogl/cogl-gles2-context-private.h
+++ b/cogl/cogl-gles2-context-private.h
@@ -46,6 +46,52 @@ struct _CoglGLES2Offscreen
CoglGLFramebuffer gl_framebuffer;
};
+typedef struct
+{
+ /* GL's ID for the shader */
+ GLuint object_id;
+ /* Shader type */
+ GLenum type;
+
+ /* Number of references to this shader. The shader will have one
+ * reference when it is created. This reference will be removed when
+ * glDeleteShader is called. An additional reference will be taken
+ * whenever the shader is attached to a program. This is necessary
+ * to correctly detect when a shader is destroyed because
+ * glDeleteShader doesn't actually delete the object if it is
+ * attached to a program */
+ int ref_count;
+
+ /* Set once this object has had glDeleteShader called on it. We need
+ * to keep track of this so we don't deref the data twice if the
+ * application calls glDeleteShader multiple times */
+ CoglBool deleted;
+} CoglGLES2ShaderData;
+
+typedef struct
+{
+ /* GL's ID for the program */
+ GLuint object_id;
+
+ /* List of shaders attached to this program */
+ GList *attached_shaders;
+
+ /* Reference count. There can be up to two references. One of these
+ * will exist between glCreateProgram and glDeleteShader, the other
+ * will exist while the program is made current. This is necessary
+ * to correctly detect when the program is deleted because
+ * glDeleteShader will delay the deletion if the program is
+ * current */
+ int ref_count;
+
+ /* Set once this object has had glDeleteProgram called on it. We need
+ * to keep track of this so we don't deref the data twice if the
+ * application calls glDeleteProgram multiple times */
+ CoglBool deleted;
+
+ CoglGLES2Context *context;
+} CoglGLES2ProgramData;
+
struct _CoglGLES2Context
{
CoglObject _parent;
@@ -68,6 +114,21 @@ struct _CoglGLES2Context
CoglGLES2Vtable *vtable;
+ /* Hash table mapping GL's IDs for shaders and objects to ShaderData
+ * and ProgramData so that we can maintain extra data for these
+ * objects. Although technically the IDs will end up global across
+ * all GLES2 contexts because they will all be in the same share
+ * list, we don't really want to expose this outside of the Cogl API
+ * so we will assume it is undefined behaviour if an application
+ * relies on this. */
+ GHashTable *shader_map;
+ GHashTable *program_map;
+
+ /* Currently in use program. We need to keep track of this so that
+ * we can keep a reference to the data for the program while it is
+ * current */
+ CoglGLES2ProgramData *current_program;
+
void *winsys;
};
diff --git a/cogl/cogl-gles2-context.c b/cogl/cogl-gles2-context.c
index ea5e1cf..deb889b 100644
--- a/cogl/cogl-gles2-context.c
+++ b/cogl/cogl-gles2-context.c
@@ -61,6 +61,43 @@ _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;
+ }
+ }
+}
+
/* We wrap glBindFramebuffer so that when framebuffer 0 is bound
* we can instead bind the write_framebuffer passed to
* cogl_push_gles2_context().
@@ -196,6 +233,150 @@ gl_copy_tex_sub_image_2d_wrapper (GLenum target,
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;
+
+ 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
_cogl_gles2_offscreen_free (CoglGLES2Offscreen *gles2_offscreen)
{
@@ -204,10 +385,61 @@ _cogl_gles2_offscreen_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
_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 and programs 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);
+
+ /* 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);
winsys = ctx->display->renderer->winsys_vtable;
winsys->destroy_gles2_context (gles2_context);
@@ -232,6 +464,22 @@ _cogl_gles2_context_free (CoglGLES2Context *gles2_context)
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);
+}
+
CoglGLES2Context *
cogl_gles2_context_new (CoglContext *ctx, GError **error)
{
@@ -284,6 +532,24 @@ cogl_gles2_context_new (CoglContext *ctx, GError **error)
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;
+
+ 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);
return _cogl_gles2_context_object_new (gles2_ctx);
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]