[cogl] Use GL_ARB_texture_swizzle to emulate GL_ALPHA textures



commit 32bacf81ebaa3be21a8f26af07d8f6eed6607652
Author: Neil Roberts <neil linux intel com>
Date:   Mon Nov 19 17:28:52 2012 +0000

    Use GL_ARB_texture_swizzle to emulate GL_ALPHA textures
    
    The core profile of GL3 has removed support for component-alpha
    textures. Previously the GL3 driver would just ignore this and try to
    create them anyway. This would generate a GL error on Mesa.
    
    To fix this the GL texture driver will now create a GL_RED texture
    when GL_ALPHA textures are not supported natively. It will then set a
    texture swizzle using the GL_ARB_texture_swizzle extension so that the
    alpha component will be taken from the red component of the texture.
    The swizzle is part of the texture object state so it only needs to be
    set once when the texture is created.
    
    The âgenâ virtual function of the texture driver has been changed to
    also take the internal format as a parameter. The GL driver will now
    set the swizzle as appropriate here.
    
    The GL3 driver now reports an error if the texture swizzle extension
    is not available because Cogl can't really work properly without out
    it. The extension is part of GL 3.3 so it is quite likely that it has
    wide support from drivers. Eventually we could get rid of this
    requirement if we have our own GLSL front-end and we could generate
    the swizzle ourselves.
    
    When uploading or downloading texture data to or from a
    component-alpha texture, we can no longer rely on GL to do the
    conversion. The swizzle doesn't have any effect on the texture data
    functions. In these cases Cogl will now force an intermediate buffer
    to be used and it will manually do the conversion as it does for the
    GLES drivers.
    
    Reviewed-by: Robert Bragg <robert linux intel com>

 cogl/cogl-internal.h                           |    4 +-
 cogl/cogl-texture-3d.c                         |    6 ++-
 cogl/cogl-texture-driver.h                     |    5 +-
 cogl/cogl-texture-rectangle.c                  |   16 +++---
 cogl/cogl-texture.c                            |   38 +++++++++++++-
 cogl/driver/gl/cogl-texture-2d-gl.c            |    9 ++-
 cogl/driver/gl/gl/cogl-driver-gl.c             |   39 ++++++++++++-
 cogl/driver/gl/gl/cogl-texture-driver-gl.c     |   70 ++++++++++++++----------
 cogl/driver/gl/gles/cogl-driver-gles.c         |    3 +-
 cogl/driver/gl/gles/cogl-texture-driver-gles.c |   38 ++++++-------
 10 files changed, 158 insertions(+), 70 deletions(-)
---
diff --git a/cogl/cogl-internal.h b/cogl/cogl-internal.h
index 97f10d8..fa921a6 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 6000b37..de00cb1 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 d4b2b0d..7a186ba 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 3dca1ed..1ffeb83 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 12be86e..7fc796c 100644
--- a/cogl/cogl-texture.c
+++ b/cogl/cogl-texture.c
@@ -176,7 +176,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 */
@@ -1146,6 +1154,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 ecb3d1e..8e07017 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 142260f..c944718 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:
@@ -532,11 +548,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 |
@@ -550,6 +572,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 3c681c3..87c33ba 100644
--- a/cogl/driver/gl/gl/cogl-texture-driver-gl.c
+++ b/cogl/driver/gl/gl/cogl-texture-driver-gl.c
@@ -44,41 +44,55 @@
 #include <stdlib.h>
 #include <math.h>
 
-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);
-
-      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;
-
-        default:
-          g_assert_not_reached();
-        }
+    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;
+
+    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 0e2b465..5630c66 100644
--- a/cogl/driver/gl/gles/cogl-driver-gles.c
+++ b/cogl/driver/gl/gles/cogl-driver-gles.c
@@ -266,7 +266,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 */
   COGL_FLAGS_SET (context->features, COGL_FEATURE_ID_POINT_SPRITE, TRUE);
diff --git a/cogl/driver/gl/gles/cogl-texture-driver-gles.c b/cogl/driver/gl/gles/cogl-texture-driver-gles.c
index 65407b6..1e45a53 100644
--- a/cogl/driver/gl/gles/cogl-texture-driver-gles.c
+++ b/cogl/driver/gl/gles/cogl-texture-driver-gles.c
@@ -63,34 +63,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, textures[i], FALSE);
+  _cogl_bind_gl_texture_transient (gl_target, tex, FALSE);
 
-      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();
-        }
+  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();
     }
+
+  return tex;
 }
 
 static void



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]