data->fbo = 0;
}
- GE( glBindTexture (data->dst_gl_target, dst_gl_texture) );
+ _cogl_bind_gl_texture_transient (data->dst_gl_target,
+ dst_gl_texture,
+ FALSE);
}
if (data->fbo)
cogl_handle_unref (atlas_tex->sub_texture);
- g_free (atlas_tex);
+ /* Chain up */
+ _cogl_texture_free (COGL_TEXTURE (atlas_tex));
}
static int
_cogl_atlas_texture_get_format,
_cogl_atlas_texture_get_gl_format,
_cogl_atlas_texture_get_width,
- _cogl_atlas_texture_get_height
+ _cogl_atlas_texture_get_height,
+ NULL /* is_foreign */
};
* with a layer may represent more than one GL texture) */
GLuint gl_texture;
+ /* Foreign textures are those not created or deleted by Cogl. If we ever
+ * call glBindTexture for a foreign texture then the next time we are
+ * asked to glBindTexture we can't try and optimize a redundant state
+ * change because we don't know if the original texture name was deleted
+ * and now we are being asked to bind a recycled name. */
+ gboolean is_foreign;
+
+ /* We have many components in Cogl that need to temporarily bind arbitrary
+ * textures e.g. to query texture object parameters and since we don't
+ * want that to result in too much redundant reflushing of layer state
+ * when all that's needed is to re-bind the layers gl_texture we use this
+ * to track when the .layer_gl_texture state is invalid.
+ *
+ * XXX: as a further optimization cogl-material.c uses a convention
+ * of always leaving texture unit 1 active when not dealing with the
+ * flushing of layer state, so we can assume this is only ever TRUE
+ * for unit 1.
+ */
+ gboolean dirty_gl_texture;
+
/* A matrix stack giving us the means to associate a texture
* transform matrix with the texture unit. */
CoglMatrixStack *matrix_stack;
void
_cogl_destroy_texture_units (void);
+void
+_cogl_bind_gl_texture_transient (GLenum gl_target,
+ GLuint gl_texture,
+ gboolean is_foreign);
+
typedef enum _CoglMaterialEqualFlags
{
/* Return FALSE if any component of either material isn't set to its
CoglHandle program);
void
+_cogl_delete_gl_texture (GLuint gl_texture);
+
+void
_cogl_material_apply_legacy_state (CoglHandle handle);
void
unit->enabled = FALSE;
unit->enabled_gl_target = 0;
unit->gl_texture = 0;
+ unit->is_foreign = FALSE;
+ unit->dirty_gl_texture = FALSE;
unit->matrix_stack = _cogl_matrix_stack_new ();
unit->layer = NULL;
}
}
+/* Note: this conceptually has slightly different semantics to
+ * OpenGL's glBindTexture because Cogl never cares about tracking
+ * multiple textures bound to different targets on the same texture
+ * unit.
+ *
+ * glBindTexture lets you bind multiple textures to a single texture
+ * unit if they are bound to different targets. So it does something
+ * like:
+ * unit->current_texture[target] = texture;
+ *
+ * Cogl only lets you associate one texture with the currently active
+ * texture unit, so the target is basically a redundant parameter
+ * that's implicitly set on that texture.
+ *
+ * Technically this is just a thin wrapper around glBindTexture so
+ * actually it does have the GL semantics but it seems worth
+ * mentioning the conceptual difference in case anyone wonders why we
+ * don't associate the gl_texture with a gl_target in the
+ * CoglTextureUnit.
+ */
+void
+_cogl_bind_gl_texture_transient (GLenum gl_target,
+ GLuint gl_texture,
+ gboolean is_foreign)
+{
+ CoglTextureUnit *unit;
+
+ _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+ unit = _cogl_get_texture_unit (ctx->active_texture_unit);
+
+ /* NB: If we have previously bound a foreign texture to this texture
+ * unit we don't know if that texture has since been deleted and we
+ * are seeing the texture name recycled */
+ if (unit->gl_texture == gl_texture &&
+ !unit->dirty_gl_texture &&
+ !unit->is_foreign)
+ return;
+
+ GE (glBindTexture (gl_target, gl_texture));
+
+ unit->dirty_gl_texture = TRUE;
+ unit->is_foreign = is_foreign;
+}
+
+void
+_cogl_delete_gl_texture (GLuint gl_texture)
+{
+ int i;
+
+ _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+ for (i = 0; i < ctx->texture_units->len; i++)
+ {
+ CoglTextureUnit *unit =
+ &g_array_index (ctx->texture_units, CoglTextureUnit, i);
+
+ if (unit->gl_texture == gl_texture)
+ {
+ unit->gl_texture = 0;
+ unit->dirty_gl_texture = FALSE;
+ }
+ }
+}
+
/* Asserts that a layer corresponding to the given index exists. If no
* match is found, then a new empty layer is added.
*/
unit->layer0_overridden = layer0_override_texture ? TRUE : FALSE;
unit->fallback = fallback;
- /* FIXME: We could be more clever here and only bind the texture
- if it is different from gl_layer_info->gl_texture to avoid
- redundant GL calls. However a few other places in Cogl and
- Clutter call glBindTexture such as ClutterGLXTexturePixmap so
- we'd need to ensure they affect the cache. Also deleting a
- texture should clear it from the cache in case a new texture
- is generated with the same number */
+ /* NB: There are several Cogl components and some code in
+ * Clutter that will temporarily bind arbitrary GL textures to
+ * query and modify texture object parameters. If you look at
+ * the end of _cogl_material_flush_gl_state() you can see we
+ * make sure that such code always binds to texture unit 1 by
+ * always leaving texture unit 1 active. This means we can't
+ * rely on the unit->gl_texture state if unit->index == 1.
+ * Because texture unit 1 is a bit special we actually defer any
+ * necessary glBindTexture for it until the end of
+ * _cogl_material_flush_gl_state().
+ *
+ * NB: we get notified whenever glDeleteTextures is used (see
+ * _cogl_delete_gl_texture()) where we invalidate
+ * unit->gl_texture references to deleted textures so it's safe
+ * to compare unit->gl_texture with gl_texture. (Without the
+ * hook it would be possible to delete a GL texture and create a
+ * new one with the same name and comparing unit->gl_texture and
+ * gl_texture wouldn't detect that.)
+ *
+ * NB: for foreign textures we don't know how the deletion of
+ * the GL texture objects correspond to the deletion of the
+ * CoglTextures so if there was previously a foreign texture
+ * associated with the texture unit then we can't assume that we
+ * aren't seeing a recycled texture name so we have to bind.
+ */
+#ifndef DISABLE_MATERIAL_CACHE
+ if (unit->gl_texture != gl_texture || unit->is_foreign)
+ {
+ if (unit->index != 1)
+ GE (glBindTexture (gl_target, gl_texture));
+ unit->gl_texture = gl_texture;
+ }
+#else
GE (glBindTexture (gl_target, gl_texture));
+#endif
+ unit->is_foreign = _cogl_texture_is_foreign (texture);
/* Disable the previous target if it was different and it's
* still enabled */
* unless multitexturing is being used.
*/
unit1 = _cogl_get_texture_unit (1);
- if (unit1->enabled)
+ if (unit1->enabled && unit1->dirty_gl_texture)
{
set_active_texture_unit (1);
GE (glBindTexture (unit1->enabled_gl_target, unit1->gl_texture));
+ unit1->dirty_gl_texture = FALSE;
}
/* Since there are several places where Cogl will temporarily bind a
cogl_handle_unref (sub_tex->next_texture);
cogl_handle_unref (sub_tex->full_texture);
- g_free (sub_tex);
+ /* Chain up */
+ _cogl_texture_free (COGL_TEXTURE (sub_tex));
}
CoglHandle
_cogl_sub_texture_get_format,
_cogl_sub_texture_get_gl_format,
_cogl_sub_texture_get_width,
- _cogl_sub_texture_get_height
+ _cogl_sub_texture_get_height,
+ NULL /* is_foreign */
};
#include "cogl-handle.h"
#include "cogl-spans.h"
#include "cogl-journal-private.h"
+#include "cogl-material-private.h"
#include <string.h>
#include <stdlib.h>
_cogl_texture_driver_upload_subregion_to_gl (
tex_2ds->gl_target,
gl_handle,
+ tex_2ds->is_foreign,
x_span->start, /* src x */
y_span->start, /* src y */
0, /* dst x */
_cogl_texture_driver_upload_subregion_to_gl (tex_2ds->gl_target,
gl_handle,
+ tex_2ds->is_foreign,
source_x,
source_y,
local_x, /* dst x */
{
GLuint texnum = g_array_index (tex_2ds->slice_gl_handles, GLuint, i);
- GE( glBindTexture (tex_2ds->gl_target, texnum) );
+ _cogl_bind_gl_texture_transient (tex_2ds->gl_target,
+ texnum,
+ tex_2ds->is_foreign);
GE( glTexParameteri (tex_2ds->gl_target,
GL_TEXTURE_WRAP_S, wrap_mode_s) );
GE( glTexParameteri (tex_2ds->gl_target,
y_span->size - y_span->waste);
/* Setup texture parameters */
- GE( glBindTexture (tex_2ds->gl_target,
- gl_handles[y * n_x_slices + x] ) );
+ _cogl_bind_gl_texture_transient (tex_2ds->gl_target,
+ gl_handles[y * n_x_slices + x],
+ FALSE);
_cogl_texture_driver_try_setting_gl_border_color (tex_2ds->gl_target,
transparent_color);
if (tex_2ds->slice_gl_handles != NULL)
{
+ int i;
if (tex_2ds->is_foreign == FALSE)
- {
- GE( glDeleteTextures (tex_2ds->slice_gl_handles->len,
- (GLuint*) tex_2ds->slice_gl_handles->data) );
- }
+ for (i = 0; i < tex_2ds->slice_gl_handles->len; i++)
+ {
+ GLuint texture =
+ g_array_index (tex_2ds->slice_gl_handles, GLuint, i);
+ _cogl_delete_gl_texture (texture);
+ }
g_array_free (tex_2ds->slice_gl_handles, TRUE);
}
_cogl_texture_2d_sliced_free (CoglTexture2DSliced *tex_2ds)
{
_cogl_texture_2d_sliced_slices_free (tex_2ds);
- g_free (tex_2ds);
+
+ /* Chain up */
+ _cogl_texture_free (COGL_TEXTURE (tex_2ds));
}
static gboolean
/* Make sure binding succeeds */
while ((gl_error = glGetError ()) != GL_NO_ERROR)
;
- glBindTexture (gl_target, gl_handle);
+
+ _cogl_bind_gl_texture_transient (gl_target, gl_handle, TRUE);
if (glGetError () != GL_NO_ERROR)
return COGL_INVALID_HANDLE;
return _cogl_texture_2d_sliced_handle_new (tex_2ds);
}
+static gboolean
+_cogl_texture_2d_sliced_is_foreign (CoglTexture *tex)
+{
+ return COGL_TEXTURE_2D_SLICED (tex)->is_foreign;
+}
+
static int
_cogl_texture_2d_sliced_get_max_waste (CoglTexture *tex)
{
for (i=0; i<tex_2ds->slice_gl_handles->len; ++i)
{
gl_handle = g_array_index (tex_2ds->slice_gl_handles, GLuint, i);
- GE( glBindTexture (tex_2ds->gl_target, gl_handle) );
+ _cogl_bind_gl_texture_transient (tex_2ds->gl_target,
+ gl_handle,
+ tex_2ds->is_foreign);
GE( glTexParameteri (tex_2ds->gl_target, GL_TEXTURE_MAG_FILTER,
tex_2ds->mag_filter) );
GE( glTexParameteri (tex_2ds->gl_target, GL_TEXTURE_MIN_FILTER,
for (i = 0; i < tex_2ds->slice_gl_handles->len; i++)
{
GLuint gl_handle = g_array_index (tex_2ds->slice_gl_handles, GLuint, i);
- GE( glBindTexture (tex_2ds->gl_target, gl_handle) );
+ _cogl_bind_gl_texture_transient (tex_2ds->gl_target,
+ gl_handle,
+ tex_2ds->is_foreign);
/* glGenerateMipmap is defined in the FBO extension */
if (cogl_features_available (COGL_FEATURE_OFFSCREEN))
bpp);
/* Download slice image data into temp bmp */
- GE( glBindTexture (tex_2ds->gl_target, gl_handle) );
+ _cogl_bind_gl_texture_transient (tex_2ds->gl_target,
+ gl_handle,
+ tex_2ds->is_foreign);
if (!_cogl_texture_driver_gl_get_tex_image (tex_2ds->gl_target,
target_gl_format,
bpp);
/* Download slice image data */
- GE( glBindTexture (tex_2ds->gl_target, gl_handle) );
+ _cogl_bind_gl_texture_transient (tex_2ds->gl_target,
+ gl_handle,
+ tex_2ds->is_foreign);
if (!_cogl_texture_driver_gl_get_tex_image (tex_2ds->gl_target,
target_gl_format,
_cogl_texture_2d_sliced_get_format,
_cogl_texture_2d_sliced_get_gl_format,
_cogl_texture_2d_sliced_get_width,
- _cogl_texture_2d_sliced_get_height
+ _cogl_texture_2d_sliced_get_height,
+ _cogl_texture_2d_sliced_is_foreign
};
#include "cogl-context.h"
#include "cogl-handle.h"
#include "cogl-journal-private.h"
+#include "cogl-material-private.h"
#include <string.h>
#include <math.h>
if (tex_2d->wrap_mode_s != wrap_mode_s ||
tex_2d->wrap_mode_t != wrap_mode_t)
{
- GE( glBindTexture (GL_TEXTURE_2D, tex_2d->gl_texture) );
+ _cogl_bind_gl_texture_transient (GL_TEXTURE_2D,
+ tex_2d->gl_texture,
+ FALSE);
GE( glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap_mode_s) );
GE( glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap_mode_t) );
static void
_cogl_texture_2d_free (CoglTexture2D *tex_2d)
{
- GE( glDeleteTextures (1, &tex_2d->gl_texture) );
- g_free (tex_2d);
+ _cogl_delete_gl_texture (tex_2d->gl_texture);
+
+ /* Chain up */
+ _cogl_texture_free (COGL_TEXTURE (tex_2d));
}
static gboolean
tex_2d = _cogl_texture_2d_create_base (width, height, flags, internal_format);
_cogl_texture_driver_gen (GL_TEXTURE_2D, 1, &tex_2d->gl_texture);
- GE( glBindTexture (GL_TEXTURE_2D, tex_2d->gl_texture) );
+ _cogl_bind_gl_texture_transient (GL_TEXTURE_2D,
+ tex_2d->gl_texture,
+ FALSE);
GE( glTexImage2D (GL_TEXTURE_2D, 0, gl_intformat,
width, height, 0, gl_format, gl_type, NULL) );
_cogl_texture_driver_gen (GL_TEXTURE_2D, 1, &tex_2d->gl_texture);
_cogl_texture_driver_upload_to_gl (GL_TEXTURE_2D,
tex_2d->gl_texture,
+ FALSE,
&dst_bmp,
gl_intformat,
gl_format,
tex_2d->mag_filter = mag_filter;
/* Apply new filters to the texture */
- GE( glBindTexture (GL_TEXTURE_2D, tex_2d->gl_texture) );
+ _cogl_bind_gl_texture_transient (GL_TEXTURE_2D,
+ tex_2d->gl_texture,
+ FALSE);
GE( glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag_filter) );
GE( glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, min_filter) );
}
if (!tex_2d->auto_mipmap || !tex_2d->mipmaps_dirty)
return;
- GE( glBindTexture (GL_TEXTURE_2D, tex_2d->gl_texture) );
+ _cogl_bind_gl_texture_transient (GL_TEXTURE_2D,
+ tex_2d->gl_texture,
+ FALSE);
/* glGenerateMipmap is defined in the FBO extension. We only allow
CoglTexture2D instances to be created if this feature is
available so we don't need to check for the extension */
/* Send data to GL */
_cogl_texture_driver_upload_subregion_to_gl (GL_TEXTURE_2D,
tex_2d->gl_texture,
+ FALSE,
src_x, src_y,
dst_x, dst_y,
dst_width, dst_height,
_cogl_texture_driver_prep_gl_for_pixels_download (target_bmp.rowstride,
closest_bpp);
- GE( glBindTexture (GL_TEXTURE_2D, tex_2d->gl_texture) );
+ _cogl_bind_gl_texture_transient (GL_TEXTURE_2D,
+ tex_2d->gl_texture,
+ FALSE);
if (!_cogl_texture_driver_gl_get_tex_image (GL_TEXTURE_2D,
closest_gl_format,
closest_gl_type,
_cogl_texture_2d_get_format,
_cogl_texture_2d_get_gl_format,
_cogl_texture_2d_get_width,
- _cogl_texture_2d_get_height
+ _cogl_texture_2d_get_height,
+ NULL /* is_foreign */
};
void
_cogl_texture_driver_upload_subregion_to_gl (GLenum gl_target,
GLuint gl_handle,
+ gboolean is_foreign,
int src_x,
int src_y,
int dst_x,
void
_cogl_texture_driver_upload_to_gl (GLenum gl_target,
GLuint gl_handle,
+ gboolean is_foreign,
CoglBitmap *source_bmp,
GLint internal_gl_format,
GLuint source_gl_format,
GLenum (* get_gl_format) (CoglTexture *tex);
int (* get_width) (CoglTexture *tex);
int (* get_height) (CoglTexture *tex);
+
+ gboolean (* is_foreign) (CoglTexture *tex);
};
struct _CoglTexture
const CoglTextureVtable *vtable;
};
+typedef enum _CoglTextureChangeFlags
+{
+ /* Whenever the internals of a texture are changed such that the
+ * underlying GL textures that represent the CoglTexture change then
+ * we notify cogl-material.c via
+ * _cogl_material_texture_pre_change_notify
+ */
+ COGL_TEXTURE_CHANGE_GL_TEXTURES
+
+} CoglTextureChangeFlags;
+
+void
+_cogl_texture_free (CoglTexture *texture);
+
void
_cogl_texture_foreach_sub_texture_in_region (CoglHandle handle,
float virtual_tx_1,
GLuint target_gl_format,
GLuint target_gl_type);
+gboolean
+_cogl_texture_is_foreign (CoglHandle handle);
+
#endif /* __COGL_TEXTURE_PRIVATE_H */
cogl_handle_unref (handle);
}
+void
+_cogl_texture_free (CoglTexture *texture)
+{
+ g_free (texture);
+}
+
static gboolean
_cogl_texture_needs_premult_conversion (CoglPixelFormat src_format,
CoglPixelFormat dst_format)
GLuint y_pot_waste,
CoglPixelFormat format)
{
+ /* FIXME: only create a sliced texture if x or y waste was specified
+ */
return _cogl_texture_2d_sliced_new_from_foreign (gl_handle,
gl_target,
width,
format);
}
+gboolean
+_cogl_texture_is_foreign (CoglHandle handle)
+{
+ CoglTexture *tex;
+
+ g_return_val_if_fail (cogl_is_texture (handle), FALSE);
+
+ tex = COGL_TEXTURE (handle);
+
+ if (tex->vtable->is_foreign)
+ return tex->vtable->is_foreign (tex);
+ else
+ return FALSE;
+}
+
CoglHandle
cogl_texture_new_from_sub_texture (CoglHandle full_texture,
int sub_x,
#include "cogl-context.h"
#include "cogl-handle.h"
#include "cogl-primitives.h"
+#include "cogl-material-private.h"
#include <string.h>
#include <stdlib.h>
for (i = 0; i < n; i++)
{
- GE (glBindTexture (gl_target, textures[i]));
+ _cogl_bind_gl_texture_transient (gl_target,
+ textures[i],
+ FALSE);
switch (gl_target)
{
void
_cogl_texture_driver_upload_subregion_to_gl (GLenum gl_target,
GLuint gl_handle,
+ gboolean is_foreign,
int src_x,
int src_y,
int dst_x,
src_y,
bpp);
- GE( glBindTexture (gl_target, gl_handle) );
+ _cogl_bind_gl_texture_transient (gl_target, gl_handle, is_foreign);
GE( glTexSubImage2D (gl_target, 0,
dst_x, dst_y,
void
_cogl_texture_driver_upload_to_gl (GLenum gl_target,
GLuint gl_handle,
+ gboolean is_foreign,
CoglBitmap *source_bmp,
GLint internal_gl_format,
GLuint source_gl_format,
/* Setup gl alignment to match rowstride and top-left corner */
prep_gl_for_pixels_upload_full (source_bmp->rowstride, 0, 0, bpp);
- GE( glBindTexture (gl_target, gl_handle) );
+ _cogl_bind_gl_texture_transient (gl_target, gl_handle, is_foreign);
GE( glTexImage2D (gl_target, 0,
internal_gl_format,
#include "cogl-bitmap-private.h"
#include "cogl-texture-private.h"
#include "cogl-material.h"
+#include "cogl-material-private.h"
#include "cogl-context.h"
#include "cogl-handle.h"
#include "cogl-primitives.h"
for (i = 0; i < n; i++)
{
- GE (glBindTexture (gl_target, textures[i]));
+ _cogl_bind_gl_texture_transient (gl_target, textures[i], FALSE);
switch (gl_target)
{
void
_cogl_texture_driver_upload_subregion_to_gl (GLenum gl_target,
GLuint gl_handle,
+ gboolean is_foreign,
int src_x,
int src_y,
int dst_x,
slice_bmp.width,
slice_bmp.height);
- GE( glBindTexture (gl_target, gl_handle) );
+ _cogl_bind_gl_texture_transient (gl_target, gl_handle, is_foreign);
GE( glTexSubImage2D (gl_target, 0,
dst_x, dst_y,
void
_cogl_texture_driver_upload_to_gl (GLenum gl_target,
GLuint gl_handle,
+ gboolean is_foreign,
CoglBitmap *source_bmp,
GLint internal_gl_format,
GLuint source_gl_format,
_cogl_texture_driver_prep_gl_for_pixels_upload (bmp.rowstride,
bpp);
- GE( glBindTexture (gl_target, gl_handle) );
+ _cogl_bind_gl_texture_transient (gl_target, gl_handle, is_foreign);
GE( glTexImage2D (gl_target, 0,
internal_gl_format,
#include "../clutter-private.h"
#include "cogl/cogl.h"
+#include "cogl/cogl-material-private.h"
typedef void (*BindTexImage) (Display *display,
GLXDrawable drawable,
if (!cogl_texture_get_gl_texture (cogl_tex, &handle, &target))
g_warning ("Failed to pluck out GL handle from cogl texture to bind");
- glBindTexture (target, handle);
+ if (tex->priv->using_rectangle)
+ _cogl_bind_gl_texture_transient (target, handle, TRUE);
+ else
+ _cogl_bind_gl_texture_transient (target, handle, FALSE);
}
static void
using_rectangle = TRUE;
glGenTextures (1, &tex);
- glBindTexture (GL_TEXTURE_RECTANGLE_ARB, tex);
+ _cogl_bind_gl_texture_transient (GL_TEXTURE_RECTANGLE_ARB, tex, TRUE);
glTexImage2D (GL_TEXTURE_RECTANGLE_ARB, 0,
gl_format, width, height,
0, GL_RGB, GL_UNSIGNED_BYTE, NULL);