diff --git a/cogl/cogl-internal.h b/cogl/cogl-internal.h index c2f387999..7a288aebf 100644 --- a/cogl/cogl-internal.h +++ b/cogl/cogl-internal.h @@ -114,7 +114,9 @@ typedef enum COGL_PRIVATE_FEATURE_BLEND_CONSTANT = 1L<<18, COGL_PRIVATE_FEATURE_QUERY_FRAMEBUFFER_BITS = 1L<<19, COGL_PRIVATE_FEATURE_BUILTIN_POINT_SIZE_UNIFORM = 1L<<20, - COGL_PRIVATE_FEATURE_QUERY_TEXTURE_PARAMETERS = 1L<<21 + COGL_PRIVATE_FEATURE_QUERY_TEXTURE_PARAMETERS = 1L<<21, + COGL_PRIVATE_FEATURE_ALPHA_TEXTURES = 1L<<22, + COGL_PRIVATE_FEATURE_TEXTURE_SWIZZLE = 1L<<23 } CoglPrivateFeatureFlags; /* Sometimes when evaluating pipelines, either during comparisons or diff --git a/cogl/cogl-texture-3d.c b/cogl/cogl-texture-3d.c index 6000b370e..de00cb143 100644 --- a/cogl/cogl-texture-3d.c +++ b/cogl/cogl-texture-3d.c @@ -234,7 +234,8 @@ cogl_texture_3d_new_with_size (CoglContext *ctx, width, height, depth, internal_format); - ctx->texture_driver->gen (ctx, GL_TEXTURE_3D, 1, &tex_3d->gl_texture); + tex_3d->gl_texture = + ctx->texture_driver->gen (ctx, GL_TEXTURE_3D, internal_format); _cogl_bind_gl_texture_transient (GL_TEXTURE_3D, tex_3d->gl_texture, FALSE); @@ -308,7 +309,8 @@ cogl_texture_3d_new_from_bitmap (CoglBitmap *bmp, _cogl_bitmap_unmap (dst_bmp); } - ctx->texture_driver->gen (ctx, GL_TEXTURE_3D, 1, &tex_3d->gl_texture); + tex_3d->gl_texture = + ctx->texture_driver->gen (ctx, GL_TEXTURE_3D, internal_format); ctx->texture_driver->upload_to_gl_3d (ctx, GL_TEXTURE_3D, diff --git a/cogl/cogl-texture-driver.h b/cogl/cogl-texture-driver.h index d4b2b0d86..7a186ba48 100644 --- a/cogl/cogl-texture-driver.h +++ b/cogl/cogl-texture-driver.h @@ -33,11 +33,10 @@ struct _CoglTextureDriver * non-mipmap filters when creating textures. This is to save some memory as * the driver will not allocate room for the mipmap tree. */ - void + GLuint (* gen) (CoglContext *ctx, GLenum gl_target, - GLsizei n, - GLuint *textures); + CoglPixelFormat internal_format); /* * This sets up the glPixelStore state for an upload to a destination with diff --git a/cogl/cogl-texture-rectangle.c b/cogl/cogl-texture-rectangle.c index 3dca1ed4e..1ffeb839d 100644 --- a/cogl/cogl-texture-rectangle.c +++ b/cogl/cogl-texture-rectangle.c @@ -219,10 +219,10 @@ cogl_texture_rectangle_new_with_size (CoglContext *ctx, width, height, internal_format); - ctx->texture_driver->gen (ctx, - GL_TEXTURE_RECTANGLE_ARB, - 1, /* num textures */ - &tex_rect->gl_texture); + tex_rect->gl_texture = + ctx->texture_driver->gen (ctx, + GL_TEXTURE_RECTANGLE_ARB, + internal_format); _cogl_bind_gl_texture_transient (GL_TEXTURE_RECTANGLE_ARB, tex_rect->gl_texture, tex_rect->is_foreign); @@ -274,10 +274,10 @@ cogl_texture_rectangle_new_from_bitmap (CoglBitmap *bmp, cogl_bitmap_get_height (bmp), internal_format); - ctx->texture_driver->gen (ctx, - GL_TEXTURE_RECTANGLE_ARB, - 1, /* num textures */ - &tex_rect->gl_texture); + tex_rect->gl_texture = + ctx->texture_driver->gen (ctx, + GL_TEXTURE_RECTANGLE_ARB, + internal_format); ctx->texture_driver->upload_to_gl (ctx, GL_TEXTURE_RECTANGLE_ARB, tex_rect->gl_texture, diff --git a/cogl/cogl-texture.c b/cogl/cogl-texture.c index 95608c624..e5fe460b9 100644 --- a/cogl/cogl-texture.c +++ b/cogl/cogl-texture.c @@ -206,7 +206,15 @@ _cogl_texture_prepare_for_upload (CoglBitmap *src_bmp, limited number of formats so we must convert using the Cogl bitmap code instead */ - if ((ctx->private_feature_flags & COGL_PRIVATE_FEATURE_FORMAT_CONVERSION)) + /* If the driver doesn't natively support alpha textures then it + * won't work correctly to convert to/from component-alpha + * textures */ + + if ((ctx->private_feature_flags & COGL_PRIVATE_FEATURE_FORMAT_CONVERSION) && + ((ctx->private_feature_flags & COGL_PRIVATE_FEATURE_ALPHA_TEXTURES) || + (src_format != COGL_PIXEL_FORMAT_A_8 && + dst_format != COGL_PIXEL_FORMAT_A_8) || + src_format == dst_format)) { /* If the source format does not have the same premult flag as the dst format then we need to copy and convert it */ @@ -1202,6 +1210,34 @@ cogl_texture_get_data (CoglTexture *texture, closest_format = ((closest_format & ~COGL_PREMULT_BIT) | (texture_format & COGL_PREMULT_BIT)); + /* If the application is requesting a conversion from a + * component-alpha texture and the driver doesn't support them + * natively then we can only read into an alpha-format buffer. In + * this case the driver will be faking the alpha textures with a + * red-component texture and it won't swizzle to the correct format + * while reading */ + if ((ctx->private_feature_flags & COGL_PRIVATE_FEATURE_ALPHA_TEXTURES) == 0) + { + if (texture_format == COGL_PIXEL_FORMAT_A_8) + { + closest_format = COGL_PIXEL_FORMAT_A_8; + closest_gl_format = GL_RED; + closest_gl_type = GL_UNSIGNED_BYTE; + } + else if (format == COGL_PIXEL_FORMAT_A_8) + { + /* If we are converting to a component-alpha texture then we + * need to read all of the components to a temporary buffer + * because there is no way to get just the 4th component. + * Note: it doesn't matter whether the texture is + * pre-multiplied here because we're only going to look at + * the alpha component */ + closest_format = COGL_PIXEL_FORMAT_RGBA_8888; + closest_gl_format = GL_RGBA; + closest_gl_type = GL_UNSIGNED_BYTE; + } + } + /* Is the requested format supported? */ if (closest_format == format) /* Target user data directly */ diff --git a/cogl/driver/gl/cogl-texture-2d-gl.c b/cogl/driver/gl/cogl-texture-2d-gl.c index 1d64d5b2c..7c344acd2 100644 --- a/cogl/driver/gl/cogl-texture-2d-gl.c +++ b/cogl/driver/gl/cogl-texture-2d-gl.c @@ -108,7 +108,8 @@ _cogl_texture_2d_gl_new_with_size (CoglContext *ctx, width, height, internal_format); - ctx->texture_driver->gen (ctx, GL_TEXTURE_2D, 1, &tex_2d->gl_texture); + tex_2d->gl_texture = + ctx->texture_driver->gen (ctx, GL_TEXTURE_2D, internal_format); _cogl_bind_gl_texture_transient (GL_TEXTURE_2D, tex_2d->gl_texture, tex_2d->is_foreign); @@ -164,7 +165,8 @@ _cogl_texture_2d_gl_new_from_bitmap (CoglBitmap *bmp, _cogl_bitmap_unmap (dst_bmp); } - ctx->texture_driver->gen (ctx, GL_TEXTURE_2D, 1, &tex_2d->gl_texture); + tex_2d->gl_texture = + ctx->texture_driver->gen (ctx, GL_TEXTURE_2D, internal_format); ctx->texture_driver->upload_to_gl (ctx, GL_TEXTURE_2D, tex_2d->gl_texture, @@ -198,7 +200,8 @@ _cogl_egl_texture_2d_gl_new_from_image (CoglContext *ctx, width, height, format); - ctx->texture_driver->gen (ctx, GL_TEXTURE_2D, 1, &tex_2d->gl_texture); + tex_2d->gl_texture = + ctx->texture_driver->gen (ctx, GL_TEXTURE_2D, format); _cogl_bind_gl_texture_transient (GL_TEXTURE_2D, tex_2d->gl_texture, FALSE); diff --git a/cogl/driver/gl/gl/cogl-driver-gl.c b/cogl/driver/gl/gl/cogl-driver-gl.c index 2597161fe..6887fb834 100644 --- a/cogl/driver/gl/gl/cogl-driver-gl.c +++ b/cogl/driver/gl/gl/cogl-driver-gl.c @@ -54,6 +54,10 @@ _cogl_driver_pixel_format_from_gl_internal (CoglContext *context, { case GL_ALPHA: case GL_ALPHA4: case GL_ALPHA8: case GL_ALPHA12: case GL_ALPHA16: + /* Cogl only supports one single-component texture so if we have + * ended up with a red texture then it is probably being used as + * a component-alpha texture */ + case GL_RED: *out_format = COGL_PIXEL_FORMAT_A_8; return TRUE; @@ -98,8 +102,20 @@ _cogl_driver_pixel_format_to_gl (CoglContext *context, switch (format) { case COGL_PIXEL_FORMAT_A_8: - glintformat = GL_ALPHA; - glformat = GL_ALPHA; + /* If the driver doesn't natively support alpha textures then we + * will use a red component texture with a swizzle to implement + * the texture */ + if ((context->private_feature_flags & + COGL_PRIVATE_FEATURE_ALPHA_TEXTURES) == 0) + { + glintformat = GL_RED; + glformat = GL_RED; + } + else + { + glintformat = GL_ALPHA; + glformat = GL_ALPHA; + } gltype = GL_UNSIGNED_BYTE; break; case COGL_PIXEL_FORMAT_G_8: @@ -564,11 +580,17 @@ _cogl_driver_update_features (CoglContext *ctx, if (ctx->glGenSamplers) private_flags |= COGL_PRIVATE_FEATURE_SAMPLER_OBJECTS; + if (COGL_CHECK_GL_VERSION (gl_major, gl_minor, 3, 3) || + _cogl_check_extension ("GL_ARB_texture_swizzle", gl_extensions) || + _cogl_check_extension ("GL_EXT_texture_swizzle", gl_extensions)) + private_flags |= COGL_PRIVATE_FEATURE_TEXTURE_SWIZZLE; + if (ctx->driver == COGL_DRIVER_GL) /* Features which are not available in GL 3 */ private_flags |= (COGL_PRIVATE_FEATURE_FIXED_FUNCTION | COGL_PRIVATE_FEATURE_ALPHA_TEST | - COGL_PRIVATE_FEATURE_QUADS); + COGL_PRIVATE_FEATURE_QUADS | + COGL_PRIVATE_FEATURE_ALPHA_TEXTURES); private_flags |= (COGL_PRIVATE_FEATURE_READ_PIXELS_ANY_FORMAT | COGL_PRIVATE_FEATURE_ANY_GL | @@ -583,6 +605,17 @@ _cogl_driver_update_features (CoglContext *ctx, g_strfreev (gl_extensions); + if ((private_flags & (COGL_PRIVATE_FEATURE_ALPHA_TEXTURES | + COGL_PRIVATE_FEATURE_TEXTURE_SWIZZLE)) == 0) + { + _cogl_set_error (error, + COGL_DRIVER_ERROR, + COGL_DRIVER_ERROR_NO_SUITABLE_DRIVER_FOUND, + "The GL_ARB_texture_swizzle extension is required " + "to use the GL3 driver"); + return FALSE; + } + return TRUE; } diff --git a/cogl/driver/gl/gl/cogl-texture-driver-gl.c b/cogl/driver/gl/gl/cogl-texture-driver-gl.c index 2d05c57d5..88f58fa03 100644 --- a/cogl/driver/gl/gl/cogl-texture-driver-gl.c +++ b/cogl/driver/gl/gl/cogl-texture-driver-gl.c @@ -45,41 +45,55 @@ #include #include -static void +#ifndef GL_TEXTURE_SWIZZLE_RGBA +#define GL_TEXTURE_SWIZZLE_RGBA 0x8E46 +#endif + +static GLuint _cogl_texture_driver_gen (CoglContext *ctx, GLenum gl_target, - GLsizei n, - GLuint *textures) + CoglPixelFormat internal_format) { - unsigned int i; + GLuint tex; - GE (ctx, glGenTextures (n, textures)); + GE (ctx, glGenTextures (1, &tex)); - for (i = 0; i < n; i++) + _cogl_bind_gl_texture_transient (gl_target, tex, FALSE); + + switch (gl_target) { - _cogl_bind_gl_texture_transient (gl_target, - textures[i], - FALSE); + case GL_TEXTURE_2D: + case GL_TEXTURE_3D: + /* GL_TEXTURE_MAG_FILTER defaults to GL_LINEAR, no need to set it */ + GE( ctx, glTexParameteri (gl_target, + GL_TEXTURE_MIN_FILTER, + GL_LINEAR) ); + break; - switch (gl_target) - { - case GL_TEXTURE_2D: - case GL_TEXTURE_3D: - /* GL_TEXTURE_MAG_FILTER defaults to GL_LINEAR, no need to set it */ - GE( ctx, glTexParameteri (gl_target, - GL_TEXTURE_MIN_FILTER, - GL_LINEAR) ); - break; + case GL_TEXTURE_RECTANGLE_ARB: + /* Texture rectangles already default to GL_LINEAR so nothing + needs to be done */ + break; - case GL_TEXTURE_RECTANGLE_ARB: - /* Texture rectangles already default to GL_LINEAR so nothing - needs to be done */ - break; - - default: - g_assert_not_reached(); - } + default: + g_assert_not_reached(); } + + /* If the driver doesn't support alpha textures directly then we'll + * fake them by setting the swizzle parameters */ + if (internal_format == COGL_PIXEL_FORMAT_A_8 && + (ctx->private_feature_flags & (COGL_PRIVATE_FEATURE_ALPHA_TEXTURES | + COGL_PRIVATE_FEATURE_TEXTURE_SWIZZLE)) == + COGL_PRIVATE_FEATURE_TEXTURE_SWIZZLE) + { + static const GLint red_swizzle[] = { GL_ZERO, GL_ZERO, GL_ZERO, GL_RED }; + + GE( ctx, glTexParameteriv (gl_target, + GL_TEXTURE_SWIZZLE_RGBA, + red_swizzle) ); + } + + return tex; } /* OpenGL - unlike GLES - can upload a sub region of pixel data from a larger diff --git a/cogl/driver/gl/gles/cogl-driver-gles.c b/cogl/driver/gl/gles/cogl-driver-gles.c index ddd9b530b..414e3f90a 100644 --- a/cogl/driver/gl/gles/cogl-driver-gles.c +++ b/cogl/driver/gl/gles/cogl-driver-gles.c @@ -271,7 +271,8 @@ _cogl_driver_update_features (CoglContext *context, COGL_PRIVATE_FEATURE_BUILTIN_POINT_SIZE_UNIFORM); private_flags |= (COGL_PRIVATE_FEATURE_VBOS | - COGL_PRIVATE_FEATURE_ANY_GL); + COGL_PRIVATE_FEATURE_ANY_GL | + COGL_PRIVATE_FEATURE_ALPHA_TEXTURES); /* Both GLES 1.1 and GLES 2.0 support point sprites in core */ flags |= COGL_FEATURE_POINT_SPRITE; diff --git a/cogl/driver/gl/gles/cogl-texture-driver-gles.c b/cogl/driver/gl/gles/cogl-texture-driver-gles.c index af95c60e8..b42d0fb09 100644 --- a/cogl/driver/gl/gles/cogl-texture-driver-gles.c +++ b/cogl/driver/gl/gles/cogl-texture-driver-gles.c @@ -64,34 +64,32 @@ #define GL_UNPACK_SKIP_PIXELS 0x0CF4 #endif -static void +static GLuint _cogl_texture_driver_gen (CoglContext *ctx, GLenum gl_target, - GLsizei n, - GLuint *textures) + CoglPixelFormat internal_format) { - unsigned int i; + GLuint tex; - GE (ctx, glGenTextures (n, textures)); + GE (ctx, glGenTextures (1, &tex)); - for (i = 0; i < n; i++) + _cogl_bind_gl_texture_transient (gl_target, tex, FALSE); + + switch (gl_target) { - _cogl_bind_gl_texture_transient (gl_target, textures[i], FALSE); + case GL_TEXTURE_2D: + case GL_TEXTURE_3D: + /* GL_TEXTURE_MAG_FILTER defaults to GL_LINEAR, no need to set it */ + GE( ctx, glTexParameteri (gl_target, + GL_TEXTURE_MIN_FILTER, + GL_LINEAR) ); + break; - switch (gl_target) - { - case GL_TEXTURE_2D: - case GL_TEXTURE_3D: - /* GL_TEXTURE_MAG_FILTER defaults to GL_LINEAR, no need to set it */ - GE( ctx, glTexParameteri (gl_target, - GL_TEXTURE_MIN_FILTER, - GL_LINEAR) ); - break; - - default: - g_assert_not_reached(); - } + default: + g_assert_not_reached(); } + + return tex; } static void