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)
This commit is contained in:
parent
0cd88bd676
commit
12a1ef72c0
@ -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;
|
||||
};
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
@ -203,11 +384,62 @@ _cogl_gles2_offscreen_free (CoglGLES2Offscreen *gles2_offscreen)
|
||||
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
|
||||
_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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user