diff --git a/clutter/glx/clutter-glx-texture-pixmap.c b/clutter/glx/clutter-glx-texture-pixmap.c index 3bd58a64f..e3ef4db94 100644 --- a/clutter/glx/clutter-glx-texture-pixmap.c +++ b/clutter/glx/clutter-glx-texture-pixmap.c @@ -6,8 +6,10 @@ * Authored By Johan Bilien * Matthew Allum * Robert Bragg + * Neil Roberts * * Copyright (C) 2007 OpenedHand + * Copyright (C) 2010 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -25,11 +27,6 @@ * */ -/* TODO: - * - Automagically handle named pixmaps, and window resizes (i.e - * essentially handle window id's being passed in) ? -*/ - /** * SECTION:clutter-glx-texture-pixmap * @short_description: A texture which displays the content of an X Pixmap. @@ -38,15 +35,12 @@ * X Pixmap as a ClutterActor. Used together with the X Composite extension, * it allows to display the content of X Windows inside Clutter. * - * 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'. + * This class used to be necessary to use the + * GLX_EXT_texture_from_pixmap extension to get fast texture + * updates. However since Clutter 1.4 the handling of this extension + * has moved down to Cogl. ClutterX11TexturePixmap and + * ClutterGLXTexturePixmap are now equivalent and either one of them + * may use the extension if it is possible. */ @@ -59,802 +53,21 @@ #include "../x11/clutter-x11-texture-pixmap.h" #include "clutter-glx-texture-pixmap.h" -#include "clutter-glx.h" -#include "clutter-backend-glx.h" - -#include "../clutter-util.h" -#include "../clutter-debug.h" -#include "../clutter-private.h" - -#include "cogl/cogl.h" -#include "cogl/cogl-material-private.h" - -typedef void (*BindTexImage) (Display *display, - GLXDrawable drawable, - int buffer, - int *attribList); -typedef void (*ReleaseTexImage) (Display *display, - GLXDrawable drawable, - int buffer); - -typedef void (*GenerateMipmap) (GLenum target); - -typedef enum -{ - CLUTTER_GLX_RECTANGLE_DISALLOW, - CLUTTER_GLX_RECTANGLE_ALLOW, - CLUTTER_GLX_RECTANGLE_FORCE -} RectangleState; - -enum -{ - PROP_0, - PROP_AUTO_REDRAW, -}; - -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 -{ - GLXPixmap glx_pixmap; - - gboolean use_fallback; - - gboolean bound; - gint can_mipmap; - gboolean mipmap_generate_queued; - - gboolean bind_tex_image_queued; - - gboolean using_rectangle; -}; - -static void -clutter_glx_texture_pixmap_update_area (ClutterX11TexturePixmap *texture, - gint x, - gint y, - gint width, - gint height); - -static void -clutter_glx_texture_pixmap_create_glx_pixmap (ClutterGLXTexturePixmap *tex); - -static ClutterX11TexturePixmapClass *parent_class = NULL; +#include "cogl/winsys/cogl-texture-pixmap-x11.h" G_DEFINE_TYPE (ClutterGLXTexturePixmap, \ clutter_glx_texture_pixmap, \ CLUTTER_X11_TYPE_TEXTURE_PIXMAP); -static void -bind_texture (ClutterGLXTexturePixmap *tex) -{ - GLuint handle = 0; - GLenum target = 0; - CoglHandle cogl_tex = - clutter_texture_get_cogl_texture (CLUTTER_TEXTURE (tex)); - - if (!cogl_texture_get_gl_texture (cogl_tex, &handle, &target)) - g_warning ("Failed to pluck out GL handle from cogl texture to bind"); - - if (tex->priv->using_rectangle) - _cogl_bind_gl_texture_transient (target, handle, TRUE); - else - _cogl_bind_gl_texture_transient (target, handle, FALSE); -} - -static void -release_tex_image (ClutterGLXTexturePixmap *texture) -{ - ClutterGLXTexturePixmapPrivate *priv = texture->priv; - Display *dpy; - - if (!priv->glx_pixmap || !priv->bound) - return; - - dpy = clutter_x11_get_default_display (); - - bind_texture (texture); - - clutter_x11_trap_x_errors (); - - (_gl_release_tex_image) (dpy, - priv->glx_pixmap, - GLX_FRONT_LEFT_EXT); - - XSync (dpy, FALSE); - - if (clutter_x11_untrap_x_errors ()) - CLUTTER_NOTE (TEXTURE, "Failed to release?"); - - CLUTTER_NOTE (TEXTURE, "Destroyed pxm: %li", priv->glx_pixmap); - - priv->bound = FALSE; -} - -static void -on_glx_texture_pixmap_pre_paint (ClutterGLXTexturePixmap *texture, - gpointer user_data) -{ - ClutterGLXTexturePixmapPrivate *priv = texture->priv; - gboolean tex_bound = FALSE; - Display *dpy = clutter_x11_get_default_display(); - - if (priv->bind_tex_image_queued) - { - CLUTTER_NOTE (TEXTURE, "Really updating via GLX"); - - bind_texture (CLUTTER_GLX_TEXTURE_PIXMAP (texture)); - tex_bound = TRUE; - - clutter_x11_trap_x_errors (); - - (_gl_bind_tex_image) (dpy, - priv->glx_pixmap, - GLX_FRONT_LEFT_EXT, - NULL); - - XSync (dpy, FALSE); - - /* Note above fires X error for non name pixmaps - but - * things still seem to work - i.e pixmap updated - */ - if (clutter_x11_untrap_x_errors ()) - CLUTTER_NOTE (TEXTURE, "Update bind_tex_image failed"); - - priv->bound = TRUE; - - if (clutter_texture_get_filter_quality (CLUTTER_TEXTURE (texture)) - == CLUTTER_TEXTURE_QUALITY_HIGH) - { - priv->mipmap_generate_queued++; - } - - priv->bind_tex_image_queued = FALSE; - } - - if (_gl_generate_mipmap && - priv->can_mipmap && - priv->mipmap_generate_queued) - { - GLuint handle = 0; - GLenum target = 0; - CoglHandle cogl_tex; - cogl_tex = clutter_texture_get_cogl_texture - (CLUTTER_TEXTURE(texture)); - - bind_texture (texture); - tex_bound = TRUE; - - cogl_texture_get_gl_texture (cogl_tex, &handle, &target); - - _gl_generate_mipmap (target); - } - priv->mipmap_generate_queued = FALSE; - - /* Disable mipmaps if we can't support them */ - if (clutter_texture_get_filter_quality (CLUTTER_TEXTURE (texture)) - == CLUTTER_TEXTURE_QUALITY_HIGH - && !priv->can_mipmap) - { - CoglHandle material - = clutter_texture_get_cogl_material (CLUTTER_TEXTURE (texture)); - - cogl_material_set_layer_filters (material, 0, - COGL_MATERIAL_FILTER_LINEAR, - COGL_MATERIAL_FILTER_LINEAR); - } -} static void clutter_glx_texture_pixmap_init (ClutterGLXTexturePixmap *self) { - ClutterGLXTexturePixmapPrivate *priv; - - priv = self->priv = - G_TYPE_INSTANCE_GET_PRIVATE (self, - CLUTTER_GLX_TYPE_TEXTURE_PIXMAP, - ClutterGLXTexturePixmapPrivate); - - g_signal_connect (CLUTTER_ACTOR(self), - "paint", G_CALLBACK (on_glx_texture_pixmap_pre_paint), - NULL); - - 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 (), - clutter_x11_get_default_screen ()); - - /* Check for the texture from pixmap extension */ - if (_cogl_check_extension ("GLX_EXT_texture_from_pixmap", - glx_extensions)) - { - _gl_bind_tex_image = - (BindTexImage)cogl_get_proc_address ("glXBindTexImageEXT"); - _gl_release_tex_image = - (ReleaseTexImage)cogl_get_proc_address ("glXReleaseTexImageEXT"); - - if (_gl_bind_tex_image && _gl_release_tex_image) - _have_tex_from_pixmap_ext = TRUE; - } - - _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 (g_ascii_strcasecmp (rect_env, "force") == 0) - _rectangle_state = CLUTTER_GLX_RECTANGLE_FORCE; - else if (g_ascii_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 == GL_TEXTURE_RECTANGLE_ARB) - glDeleteTextures (1, &gl_handle); - } - - priv->using_rectangle = FALSE; - } -} - -static void -clutter_glx_texture_pixmap_dispose (GObject *object) -{ - ClutterGLXTexturePixmap *texture = CLUTTER_GLX_TEXTURE_PIXMAP (object); - ClutterGLXTexturePixmapPrivate *priv = texture->priv; - - clutter_glx_texture_pixmap_free_rectangle (texture); - - if (priv->glx_pixmap != None) - { - clutter_x11_trap_x_errors (); - - glXDestroyPixmap (clutter_x11_get_default_display(), - priv->glx_pixmap); - XSync (clutter_x11_get_default_display(), FALSE); - - clutter_x11_untrap_x_errors (); - - priv->glx_pixmap = None; - } - - G_OBJECT_CLASS (clutter_glx_texture_pixmap_parent_class)->dispose (object); -} - -static void -clutter_glx_texture_pixmap_notify (GObject *object, GParamSpec *pspec) -{ - if (g_str_equal (pspec->name, "pixmap")) - { - ClutterGLXTexturePixmap *texture = CLUTTER_GLX_TEXTURE_PIXMAP (object); - if (CLUTTER_ACTOR_IS_REALIZED (texture)) - clutter_glx_texture_pixmap_create_glx_pixmap (texture); - } -} - -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; - GLint gl_format; - CoglPixelFormat cogl_format = COGL_PIXEL_FORMAT_RGBA_8888; - guint depth; - - g_object_get (G_OBJECT (texture_glx), "pixmap-depth", &depth, NULL); - if (depth == 32) - { - gl_format = GL_RGBA; - cogl_format = COGL_PIXEL_FORMAT_RGBA_8888; - } - else if (depth == 24) - { - gl_format = GL_RGB; - cogl_format = COGL_PIXEL_FORMAT_RGB_888; - } - else - { - g_critical ("Can't create a TFP cogl texture for pixmap with depth < 24"); - return FALSE; - } - - /* 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); - _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); - - handle = cogl_texture_new_from_foreign (tex, GL_TEXTURE_RECTANGLE_ARB, - width, height, - 0, 0, - cogl_format | COGL_BGR_BIT | - COGL_PREMULT_BIT); - } - else - { - handle - = cogl_texture_new_with_size (width, height, - COGL_TEXTURE_NO_SLICING, - cogl_format | COGL_BGR_BIT | - COGL_PREMULT_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); - cogl_handle_unref(handle); - - return TRUE; - } - - return FALSE; -} - -static void -clutter_glx_texture_pixmap_realize (ClutterActor *actor) -{ - ClutterGLXTexturePixmap *texture; - ClutterGLXTexturePixmapPrivate *priv; - - texture = CLUTTER_GLX_TEXTURE_PIXMAP (actor); - priv = texture->priv; - - clutter_glx_texture_pixmap_create_glx_pixmap (texture); - - if (priv->use_fallback) - { - CLUTTER_ACTOR_CLASS (clutter_glx_texture_pixmap_parent_class)-> - realize (actor); - return; - } - - CLUTTER_NOTE (TEXTURE, "texture pixmap realised"); -} - -static void -clutter_glx_texture_pixmap_unrealize (ClutterActor *actor) -{ - ClutterGLXTexturePixmap *texture = CLUTTER_GLX_TEXTURE_PIXMAP (actor); - Display *dpy; - - 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)-> - unrealize (actor); - return; - } - - if (!CLUTTER_ACTOR_IS_REALIZED (actor)) - return; - - release_tex_image (texture); - - CLUTTER_ACTOR_UNSET_FLAGS (actor, CLUTTER_ACTOR_REALIZED); -} - -static GLXFBConfig * -get_fbconfig_for_depth (ClutterGLXTexturePixmap *texture, guint depth) -{ - GLXFBConfig *fbconfigs, *ret = NULL; - int n_elements, i, found; - Display *dpy; - int db, stencil, alpha, mipmap, rgba, value; - - static GLXFBConfig *cached_config = NULL; - static gboolean have_cached_config = FALSE; - static int cached_mipmap = 0; - - if (have_cached_config) - { - texture->priv->can_mipmap = cached_mipmap; - return cached_config; - } - - dpy = clutter_x11_get_default_display (); - - fbconfigs = glXGetFBConfigs (dpy, - clutter_x11_get_default_screen (), - &n_elements); - - db = G_MAXSHORT; - stencil = G_MAXSHORT; - mipmap = 0; - rgba = 0; - - found = n_elements; - - for (i = 0; i < n_elements; i++) - { - XVisualInfo *vi; - int visual_depth; - - vi = glXGetVisualFromFBConfig (dpy, - fbconfigs[i]); - if (vi == NULL) - continue; - - visual_depth = vi->depth; - - XFree (vi); - - if (visual_depth != depth) - continue; - - glXGetFBConfigAttrib (dpy, - fbconfigs[i], - GLX_ALPHA_SIZE, - &alpha); - glXGetFBConfigAttrib (dpy, - fbconfigs[i], - GLX_BUFFER_SIZE, - &value); - if (value != depth && (value - alpha) != depth) - continue; - - value = 0; - if (depth == 32) - { - glXGetFBConfigAttrib (dpy, - fbconfigs[i], - GLX_BIND_TO_TEXTURE_RGBA_EXT, - &value); - if (value) - rgba = 1; - } - - if (!value) - { - if (rgba) - continue; - - glXGetFBConfigAttrib (dpy, - fbconfigs[i], - GLX_BIND_TO_TEXTURE_RGB_EXT, - &value); - if (!value) - continue; - } - - glXGetFBConfigAttrib (dpy, - fbconfigs[i], - GLX_DOUBLEBUFFER, - &value); - if (value > db) - continue; - - db = value; - - glXGetFBConfigAttrib (dpy, - fbconfigs[i], - GLX_STENCIL_SIZE, - &value); - if (value > stencil) - continue; - - stencil = value; - - if (_gl_generate_mipmap) - { - glXGetFBConfigAttrib (dpy, - fbconfigs[i], - GLX_BIND_TO_MIPMAP_TEXTURE_EXT, - &value); - - if (value < mipmap) - continue; - - mipmap = value; - } - - found = i; - } - - if (found != n_elements) - { - ret = g_malloc (sizeof (GLXFBConfig)); - *ret = fbconfigs[found]; - } - - if (n_elements) - XFree (fbconfigs); - - have_cached_config = TRUE; - cached_config = ret; - texture->priv->can_mipmap = cached_mipmap = mipmap; - - return ret; -} - -static void -clutter_glx_texture_pixmap_free_glx_pixmap (ClutterGLXTexturePixmap *texture) -{ - ClutterGLXTexturePixmapPrivate *priv = texture->priv; - Display *dpy; - - dpy = clutter_x11_get_default_display (); - - release_tex_image (texture); - - clutter_x11_trap_x_errors (); - if (priv->glx_pixmap) - glXDestroyPixmap (dpy, priv->glx_pixmap); - XSync (dpy, FALSE); - clutter_x11_untrap_x_errors (); - priv->glx_pixmap = None; -} - -static void -clutter_glx_texture_pixmap_create_glx_pixmap (ClutterGLXTexturePixmap *texture) -{ - ClutterGLXTexturePixmapPrivate *priv = texture->priv; - GLXPixmap glx_pixmap = None; - int attribs[7], i = 0, mipmap = 0; - GLXFBConfig *fbconfig; - Display *dpy; - guint depth; - Pixmap pixmap = None; - guint pixmap_width = 0, pixmap_height = 0; - ClutterBackendGLX *backend_glx; - ClutterTextureQuality quality; - - backend_glx = CLUTTER_BACKEND_GLX(clutter_get_default_backend ()); - - dpy = clutter_x11_get_default_display (); - - if (!_have_tex_from_pixmap_ext) - goto cleanup; - - g_object_get (texture, - "pixmap-width", &pixmap_width, - "pixmap-height", &pixmap_height, - "pixmap-depth", &depth, - "pixmap", &pixmap, - NULL); - - if (!pixmap) - { - goto cleanup; - } - - CLUTTER_NOTE (TEXTURE, "Creating GLXPixmap"); - - fbconfig = get_fbconfig_for_depth (texture, depth); - - if (!fbconfig) - { - g_warning ("Could not find an FBConfig for selected pixmap"); - goto cleanup; - } - - attribs[i++] = GLX_TEXTURE_FORMAT_EXT; - - if (depth == 24) - { - attribs[i++] = GLX_TEXTURE_FORMAT_RGB_EXT; - } - else if (depth == 32) - { - attribs[i++] = GLX_TEXTURE_FORMAT_RGBA_EXT; - } - else - { - g_warning ("Pixmap with depth below 24 are not supported"); - goto cleanup; - } - - quality = clutter_texture_get_filter_quality (CLUTTER_TEXTURE (texture)); - - if (quality == CLUTTER_TEXTURE_QUALITY_HIGH && priv->can_mipmap) - mipmap = priv->can_mipmap; - - attribs[i++] = GLX_MIPMAP_TEXTURE_EXT; - attribs[i++] = mipmap; - - attribs[i++] = GLX_TEXTURE_TARGET_EXT; - - attribs[i++] = (should_use_rectangle () - ? GLX_TEXTURE_RECTANGLE_EXT : GLX_TEXTURE_2D_EXT); - - attribs[i++] = None; - - /* Note: some drivers (e.g. Nvidia) get upset if you effectivly create a glx - * pixmap for the same server side object, even though you might have unique - * client side names, we currently avoid hitting this problem by destroying - * the current glx pixmap first */ - if (priv->glx_pixmap) - clutter_glx_texture_pixmap_free_glx_pixmap (texture); - - clutter_x11_trap_x_errors (); - glx_pixmap = glXCreatePixmap (dpy, - *fbconfig, - pixmap, - attribs); - XSync (dpy, FALSE); - if (clutter_x11_untrap_x_errors ()) - { - CLUTTER_NOTE (TEXTURE, "Failed to create GLXPixmap"); - - /* Make sure we don't think the call actually succeeded */ - glx_pixmap = None; - } - - if (!create_cogl_texture (CLUTTER_TEXTURE (texture), - pixmap_width, pixmap_height)) - { - CLUTTER_NOTE (TEXTURE, "Unable to create texture for pixmap"); - - glXDestroyPixmap (dpy, glx_pixmap); - glx_pixmap = None; - goto cleanup; - } - -cleanup: - - if (priv->glx_pixmap) - clutter_glx_texture_pixmap_free_glx_pixmap (texture); - - if (glx_pixmap != None) - { - priv->use_fallback = FALSE; - priv->glx_pixmap = glx_pixmap; - - CLUTTER_NOTE (TEXTURE, "Created GLXPixmap"); - - clutter_glx_texture_pixmap_update_area (CLUTTER_X11_TEXTURE_PIXMAP (texture), - 0, 0, - pixmap_width, pixmap_height); - - /* Get ready to queue initial mipmap generation */ - if (clutter_texture_get_filter_quality (CLUTTER_TEXTURE (texture)) - == CLUTTER_TEXTURE_QUALITY_HIGH) - { - priv->mipmap_generate_queued = TRUE; - } - - return; - } - else - { - if (pixmap) - CLUTTER_NOTE (TEXTURE, "Falling back to X11 manual mechansim"); - - priv->use_fallback = TRUE; - priv->glx_pixmap = None; - } -} - -static void -clutter_glx_texture_pixmap_update_area (ClutterX11TexturePixmap *texture, - gint x, - gint y, - gint width, - gint height) -{ - ClutterGLXTexturePixmap *texture_glx = CLUTTER_GLX_TEXTURE_PIXMAP (texture); - ClutterGLXTexturePixmapPrivate *priv = texture_glx->priv; - - CLUTTER_NOTE (TEXTURE, "Updating texture pixmap"); - - if (!CLUTTER_ACTOR_IS_REALIZED (texture)) - return; - - 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); - return; - } - - if (priv->glx_pixmap == None) - return; - - priv->bind_tex_image_queued = TRUE; } static void clutter_glx_texture_pixmap_class_init (ClutterGLXTexturePixmapClass *klass) { - GObjectClass *object_class = G_OBJECT_CLASS (klass); - ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); - ClutterX11TexturePixmapClass *x11_texture_class = - CLUTTER_X11_TEXTURE_PIXMAP_CLASS (klass); - - g_type_class_add_private (klass, sizeof (ClutterGLXTexturePixmapPrivate)); - - parent_class = g_type_class_peek_parent(klass); - - object_class->dispose = clutter_glx_texture_pixmap_dispose; - object_class->notify = clutter_glx_texture_pixmap_notify; - - actor_class->realize = clutter_glx_texture_pixmap_realize; - actor_class->unrealize = clutter_glx_texture_pixmap_unrealize; - - x11_texture_class->update_area = clutter_glx_texture_pixmap_update_area; } /** @@ -875,15 +88,14 @@ clutter_glx_texture_pixmap_class_init (ClutterGLXTexturePixmapClass *klass) gboolean clutter_glx_texture_pixmap_using_extension (ClutterGLXTexturePixmap *texture) { - ClutterGLXTexturePixmapPrivate *priv; + CoglHandle cogl_texture; - priv = CLUTTER_GLX_TEXTURE_PIXMAP (texture)->priv; + g_return_val_if_fail (CLUTTER_GLX_IS_TEXTURE_PIXMAP (texture), FALSE); - return (_have_tex_from_pixmap_ext && !priv->use_fallback); - /* Assume NPOT TFP's are supported even if regular NPOT isn't advertised - * but tfp is. Seemingly some Intel drivers do this ? - */ - /* && clutter_feature_available (COGL_FEATURE_TEXTURE_NPOT)); */ + cogl_texture = clutter_texture_get_cogl_texture (CLUTTER_TEXTURE (texture)); + + return (cogl_is_texture_pixmap_x11 (cogl_texture) && + cogl_texture_pixmap_x11_is_using_tfp_extension (cogl_texture)); } /** diff --git a/clutter/x11/clutter-x11-texture-pixmap.c b/clutter/x11/clutter-x11-texture-pixmap.c index 78db62a48..53c280d54 100644 --- a/clutter/x11/clutter-x11-texture-pixmap.c +++ b/clutter/x11/clutter-x11-texture-pixmap.c @@ -3,9 +3,8 @@ * * An OpenGL based 'interactive canvas' library. * - * Authored By Johan Bilien - * * Copyright (C) 2007 OpenedHand + * Copyright (C) 2010 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -20,7 +19,9 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * - * + * Authors: + * Johan Bilien + * Neil Roberts */ /** @@ -47,15 +48,11 @@ #include "clutter-private.h" #include "cogl/cogl.h" +#include "cogl/winsys/cogl-texture-pixmap-x11.h" #include #include -#include -#include -#include -#include - enum { PROP_PIXMAP = 1, @@ -103,15 +100,10 @@ struct _ClutterX11TexturePixmapPrivate guint pixmap_width, pixmap_height; guint depth; - XImage *image; - XShmSegmentInfo shminfo; - gboolean automatic_updates; Damage damage; - Drawable damage_drawable; /* FIXME: lots of gbooleans. coalesce into bitfields */ - gboolean have_shm; gboolean window_redirect_automatic; gboolean window_mapped; gboolean destroyed; @@ -152,150 +144,21 @@ check_extensions (ClutterX11TexturePixmap *texture) } static void -free_shm_resources (ClutterX11TexturePixmap *texture) +process_damage_event (ClutterX11TexturePixmap *texture, + XDamageNotifyEvent *damage_event) { - ClutterX11TexturePixmapPrivate *priv; - - priv = texture->priv; - - if (priv->shminfo.shmid != -1) - { - XShmDetach(clutter_x11_get_default_display(), - &priv->shminfo); - shmdt(priv->shminfo.shmaddr); - shmctl(priv->shminfo.shmid, IPC_RMID, NULL); - priv->shminfo.shmid = -1; - } -} - -/* Tries to allocate enough shared mem to handle a full size - * update size of the X Pixmap. */ -static gboolean -try_alloc_shm (ClutterX11TexturePixmap *texture) -{ - ClutterX11TexturePixmapPrivate *priv; - XImage *dummy_image; - Display *dpy; - - priv = texture->priv; - dpy = clutter_x11_get_default_display(); - - g_return_val_if_fail (priv->pixmap, FALSE); - - if (!XShmQueryExtension(dpy) || g_getenv("CLUTTER_X11_NO_SHM")) - { - priv->have_shm = FALSE; - return FALSE; - } - - clutter_x11_trap_x_errors (); - - /* We are creating a dummy_image so we can have Xlib calculate - * image->bytes_per_line - including any magic padding it may - * want - for the largest possible ximage we might need to use - * when handling updates to the texture. - * - * Note: we pass a NULL shminfo here, but that has no bearing - * on the setup of the XImage, except that ximage->obdata will - * == NULL. - */ - dummy_image = - XShmCreateImage(dpy, - DefaultVisual(dpy, - clutter_x11_get_default_screen()), - priv->depth, - ZPixmap, - NULL, - NULL, /* shminfo, */ - priv->pixmap_width, - priv->pixmap_height); - if (!dummy_image) - goto failed_image_create; - - priv->shminfo.shmid = shmget (IPC_PRIVATE, - dummy_image->bytes_per_line - * dummy_image->height, - IPC_CREAT | 0777); - if (priv->shminfo.shmid == -1) - goto failed_shmget; - - priv->shminfo.shmaddr = shmat (priv->shminfo.shmid, NULL, 0); - if (priv->shminfo.shmaddr == (void *) -1) - goto failed_shmat; - - priv->shminfo.readOnly = False; - - if (XShmAttach (dpy, &priv->shminfo) == 0) - goto failed_xshmattach; - - if (clutter_x11_untrap_x_errors ()) - g_warning ("X Error: Failed to setup XShm"); - - XDestroyImage (dummy_image); - - priv->have_shm = TRUE; - - return TRUE; - -failed_xshmattach: - g_warning ("XShmAttach failed"); - shmdt (priv->shminfo.shmaddr); - -failed_shmat: - g_warning ("shmat failed"); - shmctl (priv->shminfo.shmid, IPC_RMID, NULL); - -failed_shmget: - g_warning ("shmget failed"); - XDestroyImage (dummy_image); - -failed_image_create: - if (clutter_x11_untrap_x_errors ()) - g_warning ("X Error: Failed to setup XShm"); - - priv->have_shm = FALSE; - return FALSE; -} - -static void -check_for_pixmap_damage (ClutterX11TexturePixmap *texture) -{ - ClutterX11TexturePixmapPrivate *priv = texture->priv; Display *dpy; - XserverRegion parts; - int i, r_count; - XRectangle *r_damage; - XRectangle r_bounds; - clutter_x11_trap_x_errors (); - - /* - * Retrieve the damaged region and break it down into individual - * rectangles so we do not have to update the whole shebang. - */ dpy = clutter_x11_get_default_display(); - parts = XFixesCreateRegion (dpy, NULL, 0); - XDamageSubtract (dpy, priv->damage, None, parts); - r_damage = XFixesFetchRegionAndBounds (dpy, - parts, - &r_count, - &r_bounds); - - clutter_x11_untrap_x_errors (); - - if (r_damage) - { - for (i = 0; i < r_count; ++i) - clutter_x11_texture_pixmap_update_area (texture, - r_damage[i].x, - r_damage[i].y, - r_damage[i].width, - r_damage[i].height); - XFree (r_damage); - } - - XFixesDestroyRegion (dpy, parts); + /* Cogl will deal with updating the texture and subtracting from the + damage region so we only need to queue a redraw */ + g_signal_emit (texture, signals[QUEUE_DAMAGE_REDRAW], + 0, + damage_event->area.x, + damage_event->area.y, + damage_event->area.width, + damage_event->area.height); } static ClutterX11FilterReturn @@ -315,10 +178,10 @@ on_x_event_filter (XEvent *xev, ClutterEvent *cev, gpointer data) { XDamageNotifyEvent *dev = (XDamageNotifyEvent*)xev; - if (dev->drawable != priv->damage_drawable) + if (dev->damage != priv->damage) return CLUTTER_X11_FILTER_CONTINUE; - check_for_pixmap_damage (texture); + process_damage_event (texture, dev); } return CLUTTER_X11_FILTER_CONTINUE; @@ -358,6 +221,32 @@ on_x_event_filter_too (XEvent *xev, ClutterEvent *cev, gpointer data) return CLUTTER_X11_FILTER_CONTINUE; } +static void +update_pixmap_damage_object (ClutterX11TexturePixmap *texture) +{ + ClutterX11TexturePixmapPrivate *priv = texture->priv; + CoglHandle cogl_texture; + + /* If we already have a CoglTexturePixmapX11 then update its + damage object */ + cogl_texture = + clutter_texture_get_cogl_texture (CLUTTER_TEXTURE (texture)); + + if (cogl_texture && cogl_is_texture_pixmap_x11 (cogl_texture)) + { + if (priv->damage) + { + const CoglTexturePixmapX11ReportLevel report_level = + COGL_TEXTURE_PIXMAP_X11_DAMAGE_BOUNDING_BOX; + cogl_texture_pixmap_x11_set_damage_object (cogl_texture, + priv->damage, + report_level); + } + else + cogl_texture_pixmap_x11_set_damage_object (cogl_texture, 0, 0); + } +} + static void create_damage_resources (ClutterX11TexturePixmap *texture) { @@ -367,19 +256,14 @@ create_damage_resources (ClutterX11TexturePixmap *texture) priv = texture->priv; dpy = clutter_x11_get_default_display(); - if (!priv->window && !priv->pixmap) + if (!priv->pixmap) return; clutter_x11_trap_x_errors (); - if (priv->window) - priv->damage_drawable = priv->window; - else - priv->damage_drawable = priv->pixmap; - priv->damage = XDamageCreate (dpy, - priv->damage_drawable, - XDamageReportNonEmpty); + priv->pixmap, + XDamageReportBoundingBox); /* Errors here might occur if the window is already destroyed, we * simply skip processing damage and assume that the texture pixmap @@ -389,7 +273,11 @@ create_damage_resources (ClutterX11TexturePixmap *texture) clutter_x11_untrap_x_errors (); if (priv->damage) - clutter_x11_add_filter (on_x_event_filter, (gpointer)texture); + { + clutter_x11_add_filter (on_x_event_filter, (gpointer)texture); + + update_pixmap_damage_object (texture); + } } static void @@ -408,9 +296,10 @@ free_damage_resources (ClutterX11TexturePixmap *texture) XSync (dpy, FALSE); clutter_x11_untrap_x_errors (); priv->damage = None; - priv->damage_drawable = None; clutter_x11_remove_filter (on_x_event_filter, (gpointer)texture); + + update_pixmap_damage_object (texture); } } @@ -482,15 +371,12 @@ clutter_x11_texture_pixmap_init (ClutterX11TexturePixmap *self) */ } - self->priv->image = NULL; self->priv->automatic_updates = FALSE; self->priv->damage = None; - self->priv->damage_drawable = None; self->priv->window = None; self->priv->pixmap = None; self->priv->pixmap_height = 0; self->priv->pixmap_width = 0; - self->priv->shminfo.shmid = -1; self->priv->window_redirect_automatic = TRUE; self->priv->window_mapped = FALSE; self->priv->destroyed = FALSE; @@ -515,14 +401,6 @@ clutter_x11_texture_pixmap_dispose (GObject *object) priv->pixmap = None; } - if (priv->image) - { - XDestroyImage (priv->image); - priv->image = NULL; - } - - free_shm_resources (texture); - G_OBJECT_CLASS (clutter_x11_texture_pixmap_parent_class)->dispose (object); } @@ -621,26 +499,10 @@ clutter_x11_texture_pixmap_get_property (GObject *object, } } -static void -clutter_x11_texture_pixmap_realize (ClutterActor *actor) -{ - ClutterX11TexturePixmap *texture = CLUTTER_X11_TEXTURE_PIXMAP (actor); - ClutterX11TexturePixmapPrivate *priv = texture->priv; - - CLUTTER_ACTOR_CLASS (clutter_x11_texture_pixmap_parent_class)-> - realize (actor); - - clutter_x11_texture_pixmap_update_area_real (texture, - 0, 0, - priv->pixmap_width, - priv->pixmap_height); -} - static void clutter_x11_texture_pixmap_class_init (ClutterX11TexturePixmapClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); - ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); GParamSpec *pspec; ClutterBackend *default_backend; @@ -650,8 +512,6 @@ clutter_x11_texture_pixmap_class_init (ClutterX11TexturePixmapClass *klass) object_class->set_property = clutter_x11_texture_pixmap_set_property; object_class->get_property = clutter_x11_texture_pixmap_get_property; - actor_class->realize = clutter_x11_texture_pixmap_realize; - klass->update_area = clutter_x11_texture_pixmap_update_area_real; pspec = g_param_spec_ulong ("pixmap", @@ -799,9 +659,11 @@ clutter_x11_texture_pixmap_class_init (ClutterX11TexturePixmapClass *klass) * @width: The width of the damage region * @height: The height of the damage region * - * ::queue-damage-redraw is emitted to notify that some sub-region of the - * underlying pixmap has changed and you need to queue a - * corresponding redraw for the actor. + * ::queue-damage-redraw is emitted to notify that some sub-region + * of the texture has been changed (either by an automatic damage + * update or by an explicit call to + * clutter_x11_texture_pixmap_update_area). This usually means a + * redraw needs to be queued for the actor. * * The default handler will queue a clipped redraw in response to * the damage, using the assumption that the pixmap is being painted @@ -846,196 +708,12 @@ clutter_x11_texture_pixmap_update_area_real (ClutterX11TexturePixmap *texture, gint width, gint height) { - ClutterX11TexturePixmapPrivate *priv; - Display *dpy; - XImage *image; - char *first_pixel; - GError *error = NULL; - guint bytes_per_line; - char *data; - gboolean data_allocated = FALSE; - int err_code; + CoglHandle cogl_texture; - if (!CLUTTER_ACTOR_IS_REALIZED (texture)) - return; + cogl_texture = clutter_texture_get_cogl_texture (CLUTTER_TEXTURE (texture)); - priv = texture->priv; - dpy = clutter_x11_get_default_display(); - - if (!priv->pixmap) - return; - - if (priv->shminfo.shmid == -1) - try_alloc_shm (texture); - - clutter_x11_trap_x_errors (); - - if (priv->have_shm) - { - image = - XShmCreateImage(dpy, - DefaultVisual(dpy, - clutter_x11_get_default_screen()), - priv->depth, - ZPixmap, - NULL, - &priv->shminfo, - width, - height); - image->data = priv->shminfo.shmaddr; - - XShmGetImage (dpy, priv->pixmap, image, x, y, AllPlanes); - first_pixel = image->data; - } - else - { - if (!priv->image) - { - priv->image = XGetImage (dpy, - priv->pixmap, - 0, 0, - priv->pixmap_width, priv->pixmap_height, - AllPlanes, - ZPixmap); - first_pixel = priv->image->data + priv->image->bytes_per_line * y - + x * priv->image->bits_per_pixel/8; - } - else - { - XGetSubImage (dpy, - priv->pixmap, - x, y, - width, height, - AllPlanes, - ZPixmap, - priv->image, - x, y); - first_pixel = priv->image->data + priv->image->bytes_per_line * y - + x * priv->image->bits_per_pixel/8; - } - image = priv->image; - } - - XSync (dpy, FALSE); - - if ((err_code = clutter_x11_untrap_x_errors ())) - { - g_warning ("Failed to get XImage of pixmap: %lx, removing", - priv->pixmap); - /* safe to assume pixmap has gone away? - therefor reset */ - clutter_x11_texture_pixmap_set_pixmap (texture, None); - return; - } - - if (priv->depth == 24) - { - guint xpos, ypos; - - for (ypos=0; yposbytes_per_line*ypos - + xpos * 4; - p[3] = 0xFF; - } - - data = first_pixel; - bytes_per_line = image->bytes_per_line; - } - else if (priv->depth == 16) - { - guint xpos, ypos; - data = g_malloc (height * width * 4); - data_allocated = TRUE; - bytes_per_line = width * 4; - - for (ypos=0; yposbytes_per_line * ypos - + xpos * 2; - guint16 *src_pixel = (guint16 *)src_p; - char *dst_p = data + bytes_per_line * ypos + xpos * 4; - guint32 *dst_pixel = (guint32 *)dst_p; - - *dst_pixel = - ((((*src_pixel << 3) & 0xf8) | ((*src_pixel >> 2) & 0x7)) | \ - (((*src_pixel << 5) & 0xfc00) | ((*src_pixel >> 1) & 0x300)) | \ - (((*src_pixel << 8) & 0xf80000) | ((*src_pixel << 3) & 0x70000))) - | 0xff000000; - } - } - else if (priv->depth == 32) - { - bytes_per_line = image->bytes_per_line; - data = first_pixel; - } - else - return; - - /* For debugging purposes, un comment to simply generate dummy - * pixmap data. (A Green background and Blue cross) */ -#if 0 - { - guint xpos, ypos; - - if (data_allocated) - g_free (data); - data_allocated = TRUE; - data = g_malloc (width*height*4); - bytes_per_line = width *4; - - for (ypos=0; ypos width/2 && xpos <= (width/2) + width/4) - || (ypos > height/2 && ypos <= (height/2) + height/4)) - *pixel=0xff0000ff; - else - *pixel=0xff00ff00; - } - } -#endif - - if (x != 0 || y != 0 || - width != priv->pixmap_width || height != priv->pixmap_height) - clutter_texture_set_area_from_rgb_data (CLUTTER_TEXTURE (texture), - (guint8 *)data, - TRUE, - x, y, - width, height, - bytes_per_line, - 4, - CLUTTER_TEXTURE_RGB_FLAG_BGR | - CLUTTER_TEXTURE_RGB_FLAG_PREMULT, - &error); - else - clutter_texture_set_from_rgb_data (CLUTTER_TEXTURE (texture), - (guint8 *)data, - TRUE, - width, height, - bytes_per_line, - 4, - CLUTTER_TEXTURE_RGB_FLAG_BGR | - CLUTTER_TEXTURE_RGB_FLAG_PREMULT, - &error); - - - - if (error) - { - g_warning ("Error when uploading from pixbuf: %s", - error->message); - g_error_free (error); - } - - if (data_allocated) - g_free (data); - - if (priv->have_shm) - XFree (image); + if (cogl_texture) + cogl_texture_pixmap_x11_update_area (cogl_texture, x, y, width, height); } /** @@ -1121,6 +799,8 @@ clutter_x11_texture_pixmap_set_pixmap (ClutterX11TexturePixmap *texture, Status status = 0; gboolean new_pixmap = FALSE, new_pixmap_width = FALSE; gboolean new_pixmap_height = FALSE, new_pixmap_depth = FALSE; + CoglHandle material; + CoglHandle cogl_texture; ClutterX11TexturePixmapPrivate *priv; @@ -1130,6 +810,12 @@ clutter_x11_texture_pixmap_set_pixmap (ClutterX11TexturePixmap *texture, clutter_x11_trap_x_errors (); + /* Get rid of the existing Cogl texture early because it may try to + use the pixmap which we might destroy */ + material = clutter_texture_get_cogl_material (CLUTTER_TEXTURE (texture)); + if (material) + cogl_material_set_layer (material, 0, COGL_INVALID_HANDLE); + status = XGetGeometry (clutter_x11_get_default_display(), (Drawable)pixmap, &root, @@ -1148,12 +834,6 @@ clutter_x11_texture_pixmap_set_pixmap (ClutterX11TexturePixmap *texture, width = height = depth = 0; } - if (priv->image) - { - XDestroyImage (priv->image); - priv->image = NULL; - } - if (priv->pixmap != pixmap) { if (priv->pixmap && priv->owns_pixmap) @@ -1162,12 +842,10 @@ clutter_x11_texture_pixmap_set_pixmap (ClutterX11TexturePixmap *texture, priv->pixmap = pixmap; new_pixmap = TRUE; - /* The damage object is created on the window if there is any - * but if there is no window, then we create it directly on - * the pixmap, so it needs to be recreated with a change in - * pixmap. + /* The damage object is created on the pixmap, so it needs to be + * recreated with a change in pixmap. */ - if (priv->automatic_updates && new_pixmap && !priv->window) + if (priv->automatic_updates && new_pixmap) { free_damage_resources (texture); create_damage_resources (texture); @@ -1205,19 +883,13 @@ clutter_x11_texture_pixmap_set_pixmap (ClutterX11TexturePixmap *texture, if (new_pixmap_depth) g_object_notify (G_OBJECT (texture), "pixmap-depth"); - free_shm_resources (texture); - - if (priv->depth != 0 && - priv->pixmap != None && - priv->pixmap_width != 0 && - priv->pixmap_height != 0) + if (pixmap) { - if (CLUTTER_ACTOR_IS_REALIZED (texture)) - clutter_x11_texture_pixmap_update_area (texture, - 0, 0, - priv->pixmap_width, - priv->pixmap_height); - + cogl_texture = cogl_texture_pixmap_x11_new (pixmap, FALSE); + clutter_texture_set_cogl_texture (CLUTTER_TEXTURE (texture), + cogl_texture); + cogl_handle_unref (cogl_texture); + update_pixmap_damage_object (texture); } /* @@ -1310,12 +982,6 @@ clutter_x11_texture_pixmap_set_window (ClutterX11TexturePixmap *texture, clutter_x11_add_filter (on_x_event_filter_too, (gpointer)texture); } - if (priv->automatic_updates) - { - free_damage_resources (texture); - create_damage_resources (texture); - } - g_object_ref (texture); g_object_notify (G_OBJECT (texture), "window"); @@ -1509,6 +1175,5 @@ clutter_x11_texture_pixmap_set_automatic (ClutterX11TexturePixmap *texture, free_damage_resources (texture); priv->automatic_updates = setting; - }