From 98035e4d8a418f1f15dfdf56b2883b081a0401b2 Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Thu, 4 Dec 2008 17:24:33 +0000 Subject: [PATCH] Bug 1297 - Bring back support for GL_ARB_texture_rectangle * clutter/cogl/gl/cogl-texture.c (cogl_texture_new_from_foreign, (_cogl_texture_quad_hw, cogl_texture_polygon), (_cogl_texture_quad_sw): Support GL_ARB_texture_rectangle textures * clutter/glx/clutter-glx-texture-pixmap.c: Use rectangle textures when NPOTs are not available or it is forced by the CLUTTER_PIXMAP_TEXTURE_RECTANGLE environment variable. * clutter/cogl/gl/cogl.c (cogl_enable): Allow enabling GL_TEXTURE_RECTANGLE_ARB. --- ChangeLog | 15 +++ clutter/cogl/gl/cogl-texture.c | 109 +++++++++++------ clutter/cogl/gl/cogl.c | 8 +- clutter/glx/clutter-glx-texture-pixmap.c | 143 +++++++++++++++++++++-- 4 files changed, 229 insertions(+), 46 deletions(-) diff --git a/ChangeLog b/ChangeLog index 8366bfbd1..a2c59f83e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,18 @@ +2008-12-04 Neil Roberts + + Bug 1297 - Bring back support for GL_ARB_texture_rectangle + + * clutter/cogl/gl/cogl-texture.c (cogl_texture_new_from_foreign, + (_cogl_texture_quad_hw, cogl_texture_polygon), + (_cogl_texture_quad_sw): Support GL_ARB_texture_rectangle textures + + * clutter/glx/clutter-glx-texture-pixmap.c: Use rectangle textures + when NPOTs are not available or it is forced by the + CLUTTER_PIXMAP_TEXTURE_RECTANGLE environment variable. + + * clutter/cogl/gl/cogl.c (cogl_enable): Allow enabling + GL_TEXTURE_RECTANGLE_ARB. + 2008-12-04 Neil Roberts Bug 1172 - Disjoint paths and clip to path diff --git a/clutter/cogl/gl/cogl-texture.c b/clutter/cogl/gl/cogl-texture.c index b1b654c58..5a202ae6b 100644 --- a/clutter/cogl/gl/cogl-texture.c +++ b/clutter/cogl/gl/cogl-texture.c @@ -1427,16 +1427,22 @@ cogl_texture_new_from_foreign (GLuint gl_handle, CoglTexture *tex; CoglTexSliceSpan x_span; CoglTexSliceSpan y_span; - - /* Allow 2-dimensional textures only */ - if (gl_target != GL_TEXTURE_2D) + + /* GL_ARB_texture_rectangle textures are supported if they are + created from foreign because some chipsets have trouble with + GL_ARB_texture_non_power_of_two. There is no Cogl call to create + them directly to emphasize the fact that they don't work fully + (for example, no mipmapping and complicated shader support) */ + + /* Allow 2-dimensional or rectangle textures only */ + if (gl_target != GL_TEXTURE_2D && gl_target != CGL_TEXTURE_RECTANGLE_ARB) return COGL_INVALID_HANDLE; - + /* Make sure it is a valid GL texture object */ gl_istexture = glIsTexture (gl_handle); if (gl_istexture == GL_FALSE) return COGL_INVALID_HANDLE; - + /* Make sure binding succeeds */ gl_error = glGetError (); glBindTexture (gl_target, gl_handle); @@ -1928,8 +1934,7 @@ _cogl_texture_quad_sw (CoglTexture *tex, GLfloat tex_coords[8]; GLfloat quad_coords[8]; GLuint gl_handle; - gulong enable_flags = (COGL_ENABLE_TEXTURE_2D - | COGL_ENABLE_VERTEX_ARRAY + gulong enable_flags = (COGL_ENABLE_VERTEX_ARRAY | COGL_ENABLE_TEXCOORD_ARRAY); _COGL_GET_CONTEXT (ctx, NO_RETVAL); @@ -1937,9 +1942,13 @@ _cogl_texture_quad_sw (CoglTexture *tex, #if COGL_DEBUG printf("=== Drawing Tex Quad (Software Tiling Mode) ===\n"); #endif - - + /* Prepare GL state */ + if (tex->gl_target == CGL_TEXTURE_RECTANGLE_ARB) + enable_flags |= COGL_ENABLE_TEXTURE_RECT; + else + enable_flags |= COGL_ENABLE_TEXTURE_2D; + if (ctx->color_alpha < 255 || tex->bitmap.format & COGL_A_BIT) { @@ -2020,17 +2029,19 @@ _cogl_texture_quad_sw (CoglTexture *tex, slice_qy2 = first_qy + COGL_FIXED_MUL (iter_y.intersect_end - first_ty, tqy); - + /* Localize slice texture coordinates */ slice_ty1 = iter_y.intersect_start - iter_y.pos; slice_ty2 = iter_y.intersect_end - iter_y.pos; - + /* Normalize texture coordinates to current slice (rectangle texture targets take denormalized) */ - slice_ty1 /= iter_y.span->size; - slice_ty2 /= iter_y.span->size; - - + if (tex->gl_target != CGL_TEXTURE_RECTANGLE_ARB) + { + slice_ty1 /= iter_y.span->size; + slice_ty2 /= iter_y.span->size; + } + /* Iterate until whole quad width covered */ for (_cogl_span_iter_begin (&iter_x, tex->slice_x_spans, first_tx, tx1, tx2) ; @@ -2050,12 +2061,15 @@ _cogl_texture_quad_sw (CoglTexture *tex, /* Localize slice texture coordinates */ slice_tx1 = iter_x.intersect_start - iter_x.pos; slice_tx2 = iter_x.intersect_end - iter_x.pos; - + /* Normalize texture coordinates to current slice (rectangle texture targets take denormalized) */ - slice_tx1 /= iter_x.span->size; - slice_tx2 /= iter_x.span->size; - + if (tex->gl_target != CGL_TEXTURE_RECTANGLE_ARB) + { + slice_tx1 /= iter_x.span->size; + slice_tx2 /= iter_x.span->size; + } + #if COGL_DEBUG printf("~~~~~ slice (%d,%d)\n", iter_x.index, iter_y.index); printf("qx1: %f\n", COGL_FIXED_TO_FLOAT (slice_qx1)); @@ -2111,8 +2125,7 @@ _cogl_texture_quad_hw (CoglTexture *tex, GLuint gl_handle; CoglTexSliceSpan *x_span; CoglTexSliceSpan *y_span; - gulong enable_flags = (COGL_ENABLE_TEXTURE_2D - | COGL_ENABLE_VERTEX_ARRAY + gulong enable_flags = (COGL_ENABLE_VERTEX_ARRAY | COGL_ENABLE_TEXCOORD_ARRAY); #if COGL_DEBUG @@ -2122,6 +2135,11 @@ _cogl_texture_quad_hw (CoglTexture *tex, _COGL_GET_CONTEXT (ctx, NO_RETVAL); /* Prepare GL state */ + if (tex->gl_target == CGL_TEXTURE_RECTANGLE_ARB) + enable_flags |= COGL_ENABLE_TEXTURE_RECT; + else + enable_flags |= COGL_ENABLE_TEXTURE_2D; + if (ctx->color_alpha < 255 || tex->bitmap.format & COGL_A_BIT) { @@ -2162,8 +2180,17 @@ _cogl_texture_quad_hw (CoglTexture *tex, ty1 = ty1 * (y_span->size - y_span->waste) / y_span->size; ty2 = ty2 * (y_span->size - y_span->waste) / y_span->size; + /* Denormalize texture coordinates for rectangle textures */ + if (tex->gl_target == GL_TEXTURE_RECTANGLE_ARB) + { + tx1 *= x_span->size; + tx2 *= x_span->size; + ty1 *= y_span->size; + ty2 *= y_span->size; + } + #define CFX_F(x) COGL_FIXED_TO_FLOAT(x) - + /* Draw textured quad */ tex_coords[0] = CFX_F(tx1); tex_coords[1] = CFX_F(ty2); tex_coords[2] = CFX_F(tx2); tex_coords[3] = CFX_F(ty2); @@ -2215,7 +2242,8 @@ cogl_texture_rectangle (CoglHandle handle, (no waste) or all of the coordinates are in the range [0,1] then we can use hardware tiling */ if (tex->slice_gl_handles->len == 1 - && (cogl_features_available (COGL_FEATURE_TEXTURE_NPOT) + && ((cogl_features_available (COGL_FEATURE_TEXTURE_NPOT) + && tex->gl_target == GL_TEXTURE_2D) || (tx1 >= 0 && tx1 <= COGL_FIXED_1 && tx2 >= 0 && tx2 <= COGL_FIXED_1 && ty1 >= 0 && ty1 <= COGL_FIXED_1 @@ -2289,11 +2317,14 @@ cogl_texture_polygon (CoglHandle handle, } /* Prepare GL state */ - enable_flags = (COGL_ENABLE_TEXTURE_2D - | COGL_ENABLE_VERTEX_ARRAY + enable_flags = (COGL_ENABLE_VERTEX_ARRAY | COGL_ENABLE_TEXCOORD_ARRAY | COGL_ENABLE_BLEND); + if (tex->gl_target == CGL_TEXTURE_RECTANGLE_ARB) + enable_flags |= COGL_ENABLE_TEXTURE_RECT; + else + enable_flags |= COGL_ENABLE_TEXTURE_2D; if (ctx->enable_backface_culling) enable_flags |= COGL_ENABLE_BACKFACE_CULLING; @@ -2335,19 +2366,31 @@ cogl_texture_polygon (CoglHandle handle, OpenGL */ for (i = 0, p = ctx->texture_vertices; i < n_vertices; i++, p++) { + CoglFixed tx, ty; + #define CFX_F COGL_FIXED_TO_FLOAT + tx = ((vertices[i].tx + - (COGL_FIXED_FROM_INT (x_span->start) + / tex->bitmap.width)) + * tex->bitmap.width / x_span->size); + ty = ((vertices[i].ty + - (COGL_FIXED_FROM_INT (y_span->start) + / tex->bitmap.height)) + * tex->bitmap.height / y_span->size); + + /* Scale the coordinates up for rectangle textures */ + if (tex->gl_target == CGL_TEXTURE_RECTANGLE_ARB) + { + tx *= x_span->size; + ty *= y_span->size; + } + p->v[0] = CFX_F(vertices[i].x); p->v[1] = CFX_F(vertices[i].y); p->v[2] = CFX_F(vertices[i].z); - p->t[0] = CFX_F((vertices[i].tx - - (COGL_FIXED_FROM_INT (x_span->start) - / tex->bitmap.width)) - * tex->bitmap.width / x_span->size); - p->t[1] = CFX_F((vertices[i].ty - - (COGL_FIXED_FROM_INT (y_span->start) - / tex->bitmap.height)) - * tex->bitmap.height / y_span->size); + p->t[0] = CFX_F(tx); + p->t[1] = CFX_F(ty); p->c[0] = cogl_color_get_red_byte(&vertices[i].color); p->c[1] = cogl_color_get_green_byte(&vertices[i].color); p->c[2] = cogl_color_get_blue_byte(&vertices[i].color); diff --git a/clutter/cogl/gl/cogl.c b/clutter/cogl/gl/cogl.c index 9bd14d113..64bd6faf3 100644 --- a/clutter/cogl/gl/cogl.c +++ b/clutter/cogl/gl/cogl.c @@ -318,7 +318,13 @@ cogl_enable (gulong flags) cogl_toggle_flag (ctx, flags, COGL_ENABLE_BACKFACE_CULLING, GL_CULL_FACE); - + +#ifdef GL_TEXTURE_RECTANGLE_ARB + cogl_toggle_flag (ctx, flags, + COGL_ENABLE_TEXTURE_RECT, + GL_TEXTURE_RECTANGLE_ARB); +#endif + cogl_toggle_client_flag (ctx, flags, COGL_ENABLE_VERTEX_ARRAY, GL_VERTEX_ARRAY); diff --git a/clutter/glx/clutter-glx-texture-pixmap.c b/clutter/glx/clutter-glx-texture-pixmap.c index 169ee209e..4c5221cac 100644 --- a/clutter/glx/clutter-glx-texture-pixmap.c +++ b/clutter/glx/clutter-glx-texture-pixmap.c @@ -40,6 +40,13 @@ * * The class requires the GLX_EXT_texture_from_pixmap OpenGL extension * (http://people.freedesktop.org/~davidr/GLX_EXT_texture_from_pixmap.txt) + * + * The GL_ARB_texture_non_power_of_two extension will be used if it is + * available. Otherwise it will try to use the + * GL_ARB_texture_rectangle extension. If both are available you can + * force it to prefer the rectangle extension by setting the + * CLUTTER_PIXMAP_TEXTURE_RECTANGLE to 'force'. To prevent it ever + * using the rectangle extension you can set it to 'disable'. */ @@ -48,6 +55,8 @@ #include "config.h" #endif +#include + #include "../x11/clutter-x11-texture-pixmap.h" #include "clutter-glx-texture-pixmap.h" #include "clutter-glx.h" @@ -68,12 +77,20 @@ typedef void (*ReleaseTexImage) (Display *display, typedef void (*GenerateMipmap) (GLenum target); +typedef enum +{ + CLUTTER_GLX_RECTANGLE_DISALLOW, + CLUTTER_GLX_RECTANGLE_ALLOW, + CLUTTER_GLX_RECTANGLE_FORCE +} RectangleState; static BindTexImage _gl_bind_tex_image = NULL; static ReleaseTexImage _gl_release_tex_image = NULL; static GenerateMipmap _gl_generate_mipmap = NULL; static gboolean _have_tex_from_pixmap_ext = FALSE; static gboolean _ext_check_done = FALSE; +static gboolean _have_tex_rectangle = FALSE; +static RectangleState _rectangle_state = CLUTTER_GLX_RECTANGLE_ALLOW; struct _ClutterGLXTexturePixmapPrivate { @@ -86,6 +103,8 @@ struct _ClutterGLXTexturePixmapPrivate gboolean bound; gint can_mipmap; gint mipmap_generate_queued; + + gboolean using_rectangle; }; static void @@ -170,7 +189,9 @@ clutter_glx_texture_pixmap_init (ClutterGLXTexturePixmap *self) if (_ext_check_done == FALSE) { + const char *gl_extensions = NULL; const gchar *glx_extensions = NULL; + const char *rect_env; glx_extensions = glXQueryExtensionsString (clutter_x11_get_default_display (), @@ -191,16 +212,59 @@ clutter_glx_texture_pixmap_init (ClutterGLXTexturePixmap *self) _gl_generate_mipmap = (GenerateMipmap)cogl_get_proc_address ("glGenerateMipmapEXT"); + gl_extensions = (char *) glGetString (GL_EXTENSIONS); + _have_tex_rectangle = cogl_check_extension ("GL_ARB_texture_rectangle", + gl_extensions); + + if ((rect_env = g_getenv ("CLUTTER_PIXMAP_TEXTURE_RECTANGLE"))) + { + if (strcasecmp (rect_env, "force") == 0) + _rectangle_state = CLUTTER_GLX_RECTANGLE_FORCE; + else if (strcasecmp (rect_env, "disable") == 0) + _rectangle_state = CLUTTER_GLX_RECTANGLE_DISALLOW; + else if (rect_env[0]) + g_warning ("Unknown value for CLUTTER_PIXMAP_TEXTURE_RECTANGLE, " + "should be 'force' or 'disable'"); + } + _ext_check_done = TRUE; } } +static void +clutter_glx_texture_pixmap_free_rectangle (ClutterGLXTexturePixmap *texture) +{ + ClutterGLXTexturePixmapPrivate *priv = texture->priv; + CoglHandle cogl_tex; + + /* Cogl won't free the GL texture resource if it was created with + new_from_foreign so we need to free it manually */ + if (priv->using_rectangle) + { + cogl_tex = clutter_texture_get_cogl_texture (CLUTTER_TEXTURE (texture)); + + if (cogl_tex != COGL_INVALID_HANDLE) + { + GLuint gl_handle; + GLenum gl_target; + + cogl_texture_get_gl_texture (cogl_tex, &gl_handle, &gl_target); + + if (gl_target == CGL_TEXTURE_RECTANGLE_ARB) + glDeleteTextures (1, &gl_handle); + } + + priv->using_rectangle = FALSE; + } +} + static void clutter_glx_texture_pixmap_dispose (GObject *object) { - ClutterGLXTexturePixmapPrivate *priv; + ClutterGLXTexturePixmap *texture = CLUTTER_GLX_TEXTURE_PIXMAP (object); + ClutterGLXTexturePixmapPrivate *priv = texture->priv; - priv = CLUTTER_GLX_TEXTURE_PIXMAP (object)->priv; + clutter_glx_texture_pixmap_free_rectangle (texture); if (priv->glx_pixmap != None) { @@ -228,20 +292,70 @@ clutter_glx_texture_pixmap_notify (GObject *object, GParamSpec *pspec) } } +static gboolean +should_use_rectangle (void) +{ + /* Use the rectangle only if it is available and either: + + the CLUTTER_PIXMAP_TEXTURE_RECTANGLE environment variable is + set to 'force' + + *or* + + the env var is set to 'allow' (which is the default) and NPOTs + textures are not available */ + return _have_tex_rectangle + && ((_rectangle_state == CLUTTER_GLX_RECTANGLE_ALLOW + && !clutter_feature_available (CLUTTER_FEATURE_TEXTURE_NPOT)) + || _rectangle_state == CLUTTER_GLX_RECTANGLE_FORCE); +} + static gboolean create_cogl_texture (ClutterTexture *texture, guint width, guint height) { + ClutterGLXTexturePixmap *texture_glx = CLUTTER_GLX_TEXTURE_PIXMAP (texture); + ClutterGLXTexturePixmapPrivate *priv = texture_glx->priv; CoglHandle handle; + gboolean using_rectangle; - handle - = cogl_texture_new_with_size (width, height, - -1, FALSE, - COGL_PIXEL_FORMAT_RGBA_8888|COGL_BGR_BIT); + /* We want to use the GL_ARB_texture_rectangle extension on some + chipsets because GL_ARB_texture_non_power_of_two is not always + supported or might be buggy */ + if (should_use_rectangle ()) + { + GLuint tex = 0; + + using_rectangle = TRUE; + + glGenTextures (1, &tex); + glBindTexture (CGL_TEXTURE_RECTANGLE_ARB, tex); + glTexImage2D (CGL_TEXTURE_RECTANGLE_ARB, 0, + GL_RGBA, width, height, + 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + + handle = cogl_texture_new_from_foreign (tex, CGL_TEXTURE_RECTANGLE_ARB, + width, height, + 0, 0, + COGL_PIXEL_FORMAT_RGBA_8888 + | COGL_BGR_BIT); + } + else + { + handle + = cogl_texture_new_with_size (width, height, + -1, FALSE, + COGL_PIXEL_FORMAT_RGBA_8888|COGL_BGR_BIT); + + using_rectangle = FALSE; + } if (handle) { + clutter_glx_texture_pixmap_free_rectangle (texture_glx); + priv->using_rectangle = using_rectangle; + clutter_texture_set_cogl_texture (texture, handle); CLUTTER_ACTOR_SET_FLAGS (texture, CLUTTER_ACTOR_REALIZED); @@ -301,12 +415,14 @@ clutter_glx_texture_pixmap_realize (ClutterActor *actor) static void clutter_glx_texture_pixmap_unrealize (ClutterActor *actor) { - ClutterGLXTexturePixmapPrivate *priv; + ClutterGLXTexturePixmap *texture = CLUTTER_GLX_TEXTURE_PIXMAP (actor); + ClutterGLXTexturePixmapPrivate *priv = texture->priv; Display *dpy; - priv = CLUTTER_GLX_TEXTURE_PIXMAP (actor)->priv; dpy = clutter_x11_get_default_display(); + clutter_glx_texture_pixmap_free_rectangle (texture); + if (!_have_tex_from_pixmap_ext) { CLUTTER_ACTOR_CLASS (clutter_glx_texture_pixmap_parent_class)-> @@ -575,7 +691,8 @@ clutter_glx_texture_pixmap_create_glx_pixmap (ClutterGLXTexturePixmap *texture) attribs[i++] = GLX_TEXTURE_TARGET_EXT; - attribs[i++] = GLX_TEXTURE_2D_EXT; + attribs[i++] = (should_use_rectangle () + ? GLX_TEXTURE_RECTANGLE_EXT : GLX_TEXTURE_2D_EXT); attribs[i++] = None; @@ -637,13 +754,12 @@ clutter_glx_texture_pixmap_update_area (ClutterX11TexturePixmap *texture, gint width, gint height) { - ClutterGLXTexturePixmapPrivate *priv; + ClutterGLXTexturePixmap *texture_glx = CLUTTER_GLX_TEXTURE_PIXMAP (texture); + ClutterGLXTexturePixmapPrivate *priv = texture_glx->priv; Display *dpy; - CLUTTER_NOTE (TEXTURE, "Updating texture pixmap"); - priv = CLUTTER_GLX_TEXTURE_PIXMAP (texture)->priv; dpy = clutter_x11_get_default_display(); if (!CLUTTER_ACTOR_IS_REALIZED (texture)) @@ -652,6 +768,9 @@ clutter_glx_texture_pixmap_update_area (ClutterX11TexturePixmap *texture, if (priv->use_fallback) { CLUTTER_NOTE (TEXTURE, "Falling back to X11"); + + clutter_glx_texture_pixmap_free_rectangle (texture_glx); + parent_class->update_area (texture, x, y, width, height);