cogl-gles2-context: Keep track of extra data per texture object
This patch adds a hash table mapping texture object IDs to a struct so that we can keep track of some of the state for each texture object. Currently it will just track the width and height of the texture 2D target. Additionally it will now try to delete any texture objects that have data created for them by the GLES2 context so that it won't leak them. It only tracks objects that get data set on them, not all objects that are bound because it is possible to use the GLES2 context with foreign textures via cogl_gles2_texture_get_handle() and we don't want to delete those. In order to keep track of the currently bound texture object it also needs to track the active texture unit. Note that this state tracking will probably go wrong if GL throws an error for invalid state. For example if glActiveTexture is called with an invalid texture unit then GL will ignore the binding but Cogl will assume it is valid and the state tracking will get out of sync. Perhaps it would be good if Cogl could detect the errors but this is difficult to do without consuming them. Reviewed-by: Robert Bragg <robert@linux.intel.com> (cherry picked from commit d8c72bb56cf3598fc57d629edc618f1bfa79f125)
This commit is contained in:
parent
d12399b823
commit
77edc1f204
@ -105,6 +105,26 @@ typedef struct
|
||||
CoglGLES2Context *context;
|
||||
} CoglGLES2ProgramData;
|
||||
|
||||
/* State tracked for each texture unit */
|
||||
typedef struct
|
||||
{
|
||||
/* The currently bound texture for the GL_TEXTURE_2D */
|
||||
GLuint current_texture_2d;
|
||||
} CoglGLES2TextureUnitData;
|
||||
|
||||
/* State tracked for each texture object */
|
||||
typedef struct
|
||||
{
|
||||
/* GL's ID for this object */
|
||||
GLuint object_id;
|
||||
|
||||
GLenum target;
|
||||
|
||||
/* The details for texture when it has a 2D target */
|
||||
int width, height;
|
||||
GLenum format;
|
||||
} CoglGLES2TextureObjectData;
|
||||
|
||||
struct _CoglGLES2Context
|
||||
{
|
||||
CoglObject _parent;
|
||||
@ -165,6 +185,18 @@ struct _CoglGLES2Context
|
||||
* results of glReadPixels read from a CoglOffscreen */
|
||||
int pack_alignment;
|
||||
|
||||
/* A hash table of CoglGLES2TextureObjects indexed by the texture
|
||||
* object ID so that we can track some state */
|
||||
GHashTable *texture_object_map;
|
||||
|
||||
/* Array of CoglGLES2TextureUnits to keep track of state for each
|
||||
* texture unit */
|
||||
GArray *texture_units;
|
||||
|
||||
/* The currently active texture unit indexed from 0 (not from
|
||||
* GL_TEXTURE0) */
|
||||
int current_texture_unit;
|
||||
|
||||
void *winsys;
|
||||
};
|
||||
|
||||
|
@ -23,6 +23,7 @@
|
||||
* Authors:
|
||||
* Tomeu Vizoso <tomeu.vizoso@collabora.com>
|
||||
* Robert Bragg <robert@linux.intel.com>
|
||||
* Neil Roberts <neil@linux.intel.com>
|
||||
*
|
||||
*/
|
||||
|
||||
@ -181,6 +182,65 @@ update_current_flip_state (CoglGLES2Context *gles2_ctx)
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/* We wrap glBindFramebuffer so that when framebuffer 0 is bound
|
||||
* we can instead bind the write_framebuffer passed to
|
||||
* cogl_push_gles2_context().
|
||||
@ -369,7 +429,7 @@ gl_read_pixels_wrapper (GLint x,
|
||||
static void
|
||||
gl_copy_tex_image_2d_wrapper (GLenum target,
|
||||
GLint level,
|
||||
GLenum internalformat,
|
||||
GLenum internal_format,
|
||||
GLint x,
|
||||
GLint y,
|
||||
GLsizei width,
|
||||
@ -379,10 +439,16 @@ gl_copy_tex_image_2d_wrapper (GLenum target,
|
||||
CoglGLES2Context *gles2_ctx = current_gles2_context;
|
||||
int restore_mode = transient_bind_read_buffer (gles2_ctx);
|
||||
|
||||
gles2_ctx->context->glCopyTexImage2D (target, level, internalformat,
|
||||
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
|
||||
@ -1068,6 +1134,108 @@ gl_pixel_store_i_wrapper (GLenum pname, GLint param)
|
||||
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)
|
||||
{
|
||||
@ -1099,6 +1267,13 @@ force_delete_shader_object (CoglGLES2Context *context,
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
@ -1111,8 +1286,8 @@ _cogl_gles2_context_free (CoglGLES2Context *gles2_context)
|
||||
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
|
||||
/* 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 */
|
||||
@ -1124,6 +1299,10 @@ _cogl_gles2_context_free (CoglGLES2Context *gles2_context)
|
||||
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)
|
||||
@ -1134,6 +1313,9 @@ _cogl_gles2_context_free (CoglGLES2Context *gles2_context)
|
||||
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);
|
||||
|
||||
@ -1173,6 +1355,12 @@ free_program_data (CoglGLES2ProgramData *data)
|
||||
g_slice_free (CoglGLES2ProgramData, data);
|
||||
}
|
||||
|
||||
static void
|
||||
free_texture_object_data (CoglGLES2TextureObjectData *data)
|
||||
{
|
||||
g_slice_free (CoglGLES2TextureObjectData, data);
|
||||
}
|
||||
|
||||
static GLuint
|
||||
create_wrapper_shader (CoglContext *ctx)
|
||||
{
|
||||
@ -1285,6 +1473,10 @@ cogl_gles2_context_new (CoglContext *ctx, GError **error)
|
||||
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;
|
||||
|
||||
/* FIXME: we need to do something with glCopyTexImage2D and
|
||||
* glCopySubTexImage2D so that it will flip the data if it is read
|
||||
@ -1301,6 +1493,18 @@ cogl_gles2_context_new (CoglContext *ctx, GError **error)
|
||||
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);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user