From b540dcb75a45c2f39c59752e5d582d6299da38d5 Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Mon, 4 Oct 2010 15:27:38 +0100 Subject: [PATCH] Support foreign textures in the texture-2d and rectangle backends This adds two new internal functions to create a foreign texture for the texture 2d and rectangle backends. cogl_texture_new_from_foreign will now use one of these backends directly if there is no waste instead of always using the sliced texture backend. --- cogl/cogl-texture-2d-private.h | 7 ++ cogl/cogl-texture-2d.c | 135 ++++++++++++++++++++++++-- cogl/cogl-texture-rectangle-private.h | 7 ++ cogl/cogl-texture-rectangle.c | 118 ++++++++++++++++++++-- cogl/cogl-texture.c | 42 ++++++-- 5 files changed, 287 insertions(+), 22 deletions(-) diff --git a/cogl/cogl-texture-2d-private.h b/cogl/cogl-texture-2d-private.h index 00bc88d3e..a61267f55 100644 --- a/cogl/cogl-texture-2d-private.h +++ b/cogl/cogl-texture-2d-private.h @@ -51,6 +51,7 @@ struct _CoglTexture2D GLint wrap_mode_t; gboolean auto_mipmap; gboolean mipmaps_dirty; + gboolean is_foreign; CoglTexturePixel first_pixel; }; @@ -69,6 +70,12 @@ _cogl_texture_2d_new_from_bitmap (CoglBitmap *bmp, CoglTextureFlags flags, CoglPixelFormat internal_format); +CoglHandle +_cogl_texture_2d_new_from_foreign (GLuint gl_handle, + GLuint width, + GLuint height, + CoglPixelFormat format); + /* * _cogl_texture_2d_externally_modified: * @handle: A handle to a 2D texture diff --git a/cogl/cogl-texture-2d.c b/cogl/cogl-texture-2d.c index 173d407d0..61edb3bba 100644 --- a/cogl/cogl-texture-2d.c +++ b/cogl/cogl-texture-2d.c @@ -140,7 +140,7 @@ _cogl_texture_2d_set_wrap_mode_parameters (CoglTexture *tex, { _cogl_bind_gl_texture_transient (GL_TEXTURE_2D, tex_2d->gl_texture, - FALSE); + tex_2d->is_foreign); GE( glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap_mode_s) ); GE( glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap_mode_t) ); @@ -152,7 +152,8 @@ _cogl_texture_2d_set_wrap_mode_parameters (CoglTexture *tex, static void _cogl_texture_2d_free (CoglTexture2D *tex_2d) { - _cogl_delete_gl_texture (tex_2d->gl_texture); + if (!tex_2d->is_foreign) + _cogl_delete_gl_texture (tex_2d->gl_texture); /* Chain up */ _cogl_texture_free (COGL_TEXTURE (tex_2d)); @@ -213,6 +214,8 @@ _cogl_texture_2d_create_base (unsigned int width, tex_2d->wrap_mode_s = GL_FALSE; tex_2d->wrap_mode_t = GL_FALSE; + tex_2d->is_foreign = FALSE; + tex_2d->format = internal_format; return tex_2d; @@ -246,7 +249,7 @@ _cogl_texture_2d_new_with_size (unsigned int width, _cogl_texture_driver_gen (GL_TEXTURE_2D, 1, &tex_2d->gl_texture); _cogl_bind_gl_texture_transient (GL_TEXTURE_2D, tex_2d->gl_texture, - FALSE); + tex_2d->is_foreign); GE( glTexImage2D (GL_TEXTURE_2D, 0, gl_intformat, width, height, 0, gl_format, gl_type, NULL) ); @@ -319,6 +322,118 @@ _cogl_texture_2d_new_from_bitmap (CoglBitmap *bmp, return _cogl_texture_2d_handle_new (tex_2d); } +CoglHandle +_cogl_texture_2d_new_from_foreign (GLuint gl_handle, + GLuint width, + GLuint height, + CoglPixelFormat format) +{ + /* NOTE: width, height and internal format are not queriable + * in GLES, hence such a function prototype. + */ + + GLenum gl_error = 0; + GLint gl_compressed = GL_FALSE; + GLenum gl_int_format = 0; + CoglTexture2D *tex_2d; + + if (!_cogl_texture_driver_allows_foreign_gl_target (GL_TEXTURE_2D)) + return COGL_INVALID_HANDLE; + + /* Make sure it is a valid GL texture object */ + if (!glIsTexture (gl_handle)) + return COGL_INVALID_HANDLE; + + /* Make sure binding succeeds */ + while ((gl_error = glGetError ()) != GL_NO_ERROR) + ; + + _cogl_bind_gl_texture_transient (GL_TEXTURE_2D, gl_handle, TRUE); + if (glGetError () != GL_NO_ERROR) + return COGL_INVALID_HANDLE; + + /* Obtain texture parameters + (only level 0 we are interested in) */ + +#if HAVE_COGL_GL + + GE( glGetTexLevelParameteriv (GL_TEXTURE_2D, 0, + GL_TEXTURE_COMPRESSED, + &gl_compressed) ); + + { + GLint val; + + GE( glGetTexLevelParameteriv (GL_TEXTURE_2D, 0, + GL_TEXTURE_INTERNAL_FORMAT, + &val) ); + + gl_int_format = val; + } + + /* If we can query GL for the actual pixel format then we'll ignore + the passed in format and use that. */ + if (!_cogl_pixel_format_from_gl_internal (gl_int_format, &format)) + return COGL_INVALID_HANDLE; + +#else + + /* Otherwise we'll assume we can derive the GL format from the + passed in format */ + _cogl_pixel_format_to_gl (format, + &gl_int_format, + NULL, + NULL); + +#endif + + /* Note: We always trust the given width and height without querying + * the texture object because the user may be creating a Cogl + * texture for a texture_from_pixmap object where glTexImage2D may + * not have been called and the texture_from_pixmap spec doesn't + * clarify that it is reliable to query back the size from OpenGL. + */ + + /* Validate width and height */ + if (width <= 0 || height <= 0) + return COGL_INVALID_HANDLE; + + /* Compressed texture images not supported */ + if (gl_compressed == GL_TRUE) + return COGL_INVALID_HANDLE; + + /* Note: previously this code would query the texture object for + whether it has GL_GENERATE_MIPMAP enabled to determine whether to + auto-generate the mipmap. This doesn't make much sense any more + since Cogl switch to using glGenerateMipmap. Ideally I think + cogl_texture_new_from_foreign should take a flags parameter so + that the application can decide whether it wants + auto-mipmapping. To be compatible with existing code, Cogl now + disables its own auto-mipmapping but leaves the value of + GL_GENERATE_MIPMAP alone so that it would still work but without + the dirtiness tracking that Cogl would do. */ + + /* Create new texture */ + tex_2d = _cogl_texture_2d_create_base (width, height, + COGL_TEXTURE_NO_AUTO_MIPMAP, + format); + + /* Setup bitmap info */ + tex_2d->is_foreign = TRUE; + tex_2d->mipmaps_dirty = TRUE; + + tex_2d->format = format; + + tex_2d->gl_texture = gl_handle; + tex_2d->gl_format = gl_int_format; + + /* Unknown filter */ + tex_2d->min_filter = GL_FALSE; + tex_2d->mag_filter = GL_FALSE; + + return _cogl_texture_2d_handle_new (tex_2d); +} + void _cogl_texture_2d_externally_modified (CoglHandle handle) { @@ -407,7 +522,7 @@ _cogl_texture_2d_set_filters (CoglTexture *tex, /* Apply new filters to the texture */ _cogl_bind_gl_texture_transient (GL_TEXTURE_2D, tex_2d->gl_texture, - FALSE); + tex_2d->is_foreign); GE( glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag_filter) ); GE( glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, min_filter) ); } @@ -425,7 +540,7 @@ _cogl_texture_2d_pre_paint (CoglTexture *tex, CoglTexturePrePaintFlags flags) { _cogl_bind_gl_texture_transient (GL_TEXTURE_2D, tex_2d->gl_texture, - FALSE); + tex_2d->is_foreign); /* glGenerateMipmap is defined in the FBO extension. If it's not available we'll fallback to temporarily enabling @@ -530,7 +645,7 @@ _cogl_texture_2d_get_data (CoglTexture *tex, _cogl_bind_gl_texture_transient (GL_TEXTURE_2D, tex_2d->gl_texture, - FALSE); + tex_2d->is_foreign); return _cogl_texture_driver_gl_get_tex_image (GL_TEXTURE_2D, gl_format, gl_type, @@ -561,6 +676,12 @@ _cogl_texture_2d_get_height (CoglTexture *tex) return COGL_TEXTURE_2D (tex)->height; } +static gboolean +_cogl_texture_2d_is_foreign (CoglTexture *tex) +{ + return COGL_TEXTURE_2D (tex)->is_foreign; +} + static const CoglTextureVtable cogl_texture_2d_vtable = { @@ -581,5 +702,5 @@ cogl_texture_2d_vtable = _cogl_texture_2d_get_gl_format, _cogl_texture_2d_get_width, _cogl_texture_2d_get_height, - NULL /* is_foreign */ + _cogl_texture_2d_is_foreign }; diff --git a/cogl/cogl-texture-rectangle-private.h b/cogl/cogl-texture-rectangle-private.h index 6f5313c17..48f3455e1 100644 --- a/cogl/cogl-texture-rectangle-private.h +++ b/cogl/cogl-texture-rectangle-private.h @@ -49,6 +49,7 @@ struct _CoglTextureRectangle GLenum mag_filter; GLint wrap_mode_s; GLint wrap_mode_t; + gboolean is_foreign; }; GQuark @@ -65,4 +66,10 @@ _cogl_texture_rectangle_new_from_bitmap (CoglBitmap *bmp, CoglTextureFlags flags, CoglPixelFormat internal_format); +CoglHandle +_cogl_texture_rectangle_new_from_foreign (GLuint gl_handle, + GLuint width, + GLuint height, + CoglPixelFormat format); + #endif /* __COGL_TEXTURE_RECTANGLE_H */ diff --git a/cogl/cogl-texture-rectangle.c b/cogl/cogl-texture-rectangle.c index 57c686c69..88236b77f 100644 --- a/cogl/cogl-texture-rectangle.c +++ b/cogl/cogl-texture-rectangle.c @@ -167,7 +167,7 @@ _cogl_texture_rectangle_set_wrap_mode_parameters (CoglTexture *tex, GE( _cogl_bind_gl_texture_transient (GL_TEXTURE_RECTANGLE_ARB, tex_rect->gl_texture, - FALSE) ); + tex_rect->is_foreign) ); GE( glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S, wrap_mode_s) ); GE( glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, @@ -181,7 +181,8 @@ _cogl_texture_rectangle_set_wrap_mode_parameters (CoglTexture *tex, static void _cogl_texture_rectangle_free (CoglTextureRectangle *tex_rect) { - _cogl_delete_gl_texture (tex_rect->gl_texture); + if (!tex_rect->is_foreign) + _cogl_delete_gl_texture (tex_rect->gl_texture); /* Chain up */ _cogl_texture_free (COGL_TEXTURE (tex_rect)); @@ -270,7 +271,7 @@ _cogl_texture_rectangle_new_with_size (unsigned int width, _cogl_texture_driver_gen (GL_TEXTURE_RECTANGLE_ARB, 1, &tex_rect->gl_texture); GE( _cogl_bind_gl_texture_transient (GL_TEXTURE_RECTANGLE_ARB, tex_rect->gl_texture, - FALSE) ); + tex_rect->is_foreign) ); GE( glTexImage2D (GL_TEXTURE_RECTANGLE_ARB, 0, gl_intformat, width, height, 0, gl_format, gl_type, NULL) ); @@ -330,6 +331,105 @@ _cogl_texture_rectangle_new_from_bitmap (CoglBitmap *bmp, return _cogl_texture_rectangle_handle_new (tex_rect); } +CoglHandle +_cogl_texture_rectangle_new_from_foreign (GLuint gl_handle, + GLuint width, + GLuint height, + CoglPixelFormat format) +{ + /* NOTE: width, height and internal format are not queriable + * in GLES, hence such a function prototype. + */ + + GLenum gl_error = 0; + GLint gl_compressed = GL_FALSE; + GLenum gl_int_format = 0; + CoglTextureRectangle *tex_rect; + + if (!_cogl_texture_driver_allows_foreign_gl_target (GL_TEXTURE_RECTANGLE_ARB)) + return COGL_INVALID_HANDLE; + + /* Make sure it is a valid GL texture object */ + if (!glIsTexture (gl_handle)) + return COGL_INVALID_HANDLE; + + /* Make sure binding succeeds */ + while ((gl_error = glGetError ()) != GL_NO_ERROR) + ; + + _cogl_bind_gl_texture_transient (GL_TEXTURE_RECTANGLE_ARB, gl_handle, TRUE); + if (glGetError () != GL_NO_ERROR) + return COGL_INVALID_HANDLE; + + /* Obtain texture parameters */ + +#if HAVE_COGL_GL + + GE( glGetTexLevelParameteriv (GL_TEXTURE_RECTANGLE_ARB, 0, + GL_TEXTURE_COMPRESSED, + &gl_compressed) ); + + { + GLint val; + + GE( glGetTexLevelParameteriv (GL_TEXTURE_RECTANGLE_ARB, 0, + GL_TEXTURE_INTERNAL_FORMAT, + &val) ); + + gl_int_format = val; + } + + /* If we can query GL for the actual pixel format then we'll ignore + the passed in format and use that. */ + if (!_cogl_pixel_format_from_gl_internal (gl_int_format, &format)) + return COGL_INVALID_HANDLE; + +#else + + /* Otherwise we'll assume we can derive the GL format from the + passed in format */ + _cogl_pixel_format_to_gl (format, + &gl_int_format, + NULL, + NULL); + +#endif + + /* Note: We always trust the given width and height without querying + * the texture object because the user may be creating a Cogl + * texture for a texture_from_pixmap object where glTexImage2D may + * not have been called and the texture_from_pixmap spec doesn't + * clarify that it is reliable to query back the size from OpenGL. + */ + + /* Validate width and height */ + if (width <= 0 || height <= 0) + return COGL_INVALID_HANDLE; + + /* Compressed texture images not supported */ + if (gl_compressed == GL_TRUE) + return COGL_INVALID_HANDLE; + + /* Create new texture */ + tex_rect = _cogl_texture_rectangle_create_base (width, height, + COGL_TEXTURE_NO_AUTO_MIPMAP, + format); + + /* Setup bitmap info */ + tex_rect->is_foreign = TRUE; + + tex_rect->format = format; + + tex_rect->gl_texture = gl_handle; + tex_rect->gl_format = gl_int_format; + + /* Unknown filter */ + tex_rect->min_filter = GL_FALSE; + tex_rect->mag_filter = GL_FALSE; + + return _cogl_texture_rectangle_handle_new (tex_rect); +} + static int _cogl_texture_rectangle_get_max_waste (CoglTexture *tex) { @@ -415,7 +515,7 @@ _cogl_texture_rectangle_set_filters (CoglTexture *tex, /* Apply new filters to the texture */ GE( _cogl_bind_gl_texture_transient (GL_TEXTURE_RECTANGLE_ARB, tex_rect->gl_texture, - FALSE) ); + tex_rect->is_foreign) ); GE( glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, mag_filter) ); GE( glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, @@ -493,7 +593,7 @@ _cogl_texture_rectangle_get_data (CoglTexture *tex, GE( _cogl_bind_gl_texture_transient (GL_TEXTURE_RECTANGLE_ARB, tex_rect->gl_texture, - FALSE) ); + tex_rect->is_foreign) ); return _cogl_texture_driver_gl_get_tex_image (GL_TEXTURE_RECTANGLE_ARB, gl_format, gl_type, @@ -524,6 +624,12 @@ _cogl_texture_rectangle_get_height (CoglTexture *tex) return COGL_TEXTURE_RECTANGLE (tex)->height; } +static gboolean +_cogl_texture_rectangle_is_foreign (CoglTexture *tex) +{ + return COGL_TEXTURE_RECTANGLE (tex)->is_foreign; +} + static const CoglTextureVtable cogl_texture_rectangle_vtable = { @@ -544,5 +650,5 @@ cogl_texture_rectangle_vtable = _cogl_texture_rectangle_get_gl_format, _cogl_texture_rectangle_get_width, _cogl_texture_rectangle_get_height, - NULL /* is_foreign */ + _cogl_texture_rectangle_is_foreign }; diff --git a/cogl/cogl-texture.c b/cogl/cogl-texture.c index 0f6654fc2..157d62720 100644 --- a/cogl/cogl-texture.c +++ b/cogl/cogl-texture.c @@ -41,6 +41,7 @@ #include "cogl-texture-driver.h" #include "cogl-texture-2d-sliced-private.h" #include "cogl-texture-2d-private.h" +#include "cogl-texture-rectangle-private.h" #include "cogl-sub-texture-private.h" #include "cogl-atlas-texture-private.h" #include "cogl-material.h" @@ -510,15 +511,38 @@ cogl_texture_new_from_foreign (GLuint gl_handle, 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, - height, - x_pot_waste, - y_pot_waste, - format); +#if HAVE_COGL_GL + if (gl_target == GL_TEXTURE_RECTANGLE_ARB) + { + if (x_pot_waste != 0 || y_pot_waste != 0) + { + /* It shouldn't be necessary to have waste in this case since + * the texture isn't limited to power of two sizes. */ + g_warning ("You can't create a foreign GL_TEXTURE_RECTANGLE cogl " + "texture with waste\n"); + return COGL_INVALID_HANDLE; + } + + return _cogl_texture_rectangle_new_from_foreign (gl_handle, + width, + height, + format); + } +#endif + + if (x_pot_waste != 0 || y_pot_waste != 0) + return _cogl_texture_2d_sliced_new_from_foreign (gl_handle, + gl_target, + width, + height, + x_pot_waste, + y_pot_waste, + format); + else + return _cogl_texture_2d_new_from_foreign (gl_handle, + width, + height, + format); } gboolean