From 9c572ff45a1fffaf00b6adfb5bd79b1c71ff1b83 Mon Sep 17 00:00:00 2001 From: Matthew Allum Date: Thu, 8 Jun 2006 21:28:05 +0000 Subject: [PATCH] 2006-06-08 Matthew Allum * clutter/clutter-main.c: * clutter/clutter-private.h: * clutter/clutter-stage.c: Rework and fix offscreen rendering, also rejig GLX context handling, moving mostly into stage. Require at least OpenGL 1.2 ( CLAMP_TO_EDGE ) * clutter/clutter-texture.c: Explicity set props on _init() as to avoid nasty can_create bug failing miserably in certain situations. Switch to CLAMP_TO_EDGE for textures to avoid tile seams. Add some more GL error checks. * clutter/clutter-label.c: Extra debug info * configure.ac: Require gdk-pixbuf-xlib-2.0 --- clutter/clutter-label.c | 3 + clutter/clutter-main.c | 95 ++++++++--------- clutter/clutter-private.h | 2 - clutter/clutter-stage.c | 208 +++++++++++++++++++++++++++++++------- clutter/clutter-texture.c | 69 +++++++------ 5 files changed, 252 insertions(+), 125 deletions(-) diff --git a/clutter/clutter-label.c b/clutter/clutter-label.c index c3063c6ab..4467cab84 100644 --- a/clutter/clutter-label.c +++ b/clutter/clutter-label.c @@ -317,6 +317,7 @@ clutter_label_init (ClutterLabel *self) pango_ft2_font_map_substitute_changed (font_map); g_object_unref (font_map); */ + CLUTTER_MARK(); } /** @@ -332,6 +333,8 @@ ClutterElement * clutter_label_new_with_text (const gchar *font_name, const gchar *text) { + CLUTTER_MARK(); + return g_object_new (CLUTTER_TYPE_LABEL, "font-name", font_name, "text", text, diff --git a/clutter/clutter-main.c b/clutter/clutter-main.c index c46604b5e..8344b3a33 100644 --- a/clutter/clutter-main.c +++ b/clutter/clutter-main.c @@ -42,9 +42,6 @@ typedef struct } ClutterXEventSource; -#define GLX_SAMPLE_BUFFERS_ARB 100000 -#define GLX_SAMPLES_ARB 100001 - typedef void (*ClutterXEventFunc) (XEvent *xev, gpointer user_data); static gboolean __clutter_has_debug = FALSE; @@ -289,19 +286,21 @@ clutter_redraw (void) { #if 0 unsigned int retraceCount; - - // Wait for vertical retrace - // glXGetVideoSyncSGI(&retraceCount); - // glXWaitVideoSyncSGI(2, (retraceCount+1)%2, &retraceCount); + /* Wait for vertical retrace */ + /* glXGetVideoSyncSGI(&retraceCount); */ + /* glXWaitVideoSyncSGI(2, (retraceCount+1)%2, &retraceCount); */ glXWaitVideoSyncSGI(1, 0, &retraceCount); #endif glXSwapBuffers(ctx->xdpy, clutter_stage_get_xwindow (stage)); } else { - glFlush(); + glXWaitGL(); + CLUTTER_GLERR(); } + + if (clutter_want_fps ()) { timer_n_frames++; @@ -439,19 +438,6 @@ clutter_root_xwindow (void) return ClutterCntx->xwin_root; } -/** - * clutter_xvisual: - * - * FIXME - * - * Return value: FIXME - */ -XVisualInfo* -clutter_xvisual (void) -{ - return ClutterCntx->xvinfo; -} - /** * clutter_want_debug: * @@ -465,18 +451,6 @@ clutter_want_debug (void) return __clutter_has_debug; } -/** - * clutter_gl_context_set_indirect: - * @indirect: FIXME - * - * FIXME - */ -void -clutter_gl_context_set_indirect (gboolean indirect) -{ - -} - ClutterMainContext* clutter_context_get_default (void) { @@ -493,6 +467,32 @@ clutter_context_get_default (void) return ClutterCntx; } +static gboolean +is_gl_version_at_least_12 (void) +{ +#define NON_VENDOR_VERSION_MAX_LEN 32 + gchar non_vendor_version[NON_VENDOR_VERSION_MAX_LEN]; + const gchar *version; + gint i = 0; + + version = (const gchar*)glGetString(GL_VERSION); + + while ( ((version[i] <= '9' && version[i] >= '0') || version[i] == '.') + && i < NON_VENDOR_VERSION_MAX_LEN) + { + non_vendor_version[i] = version[i]; + i++; + } + + non_vendor_version[i] = '\0'; + + if (strstr (non_vendor_version, "1.0") == NULL + && strstr (non_vendor_version, "1.0") == NULL) + return TRUE; + + return FALSE; +} + /** * clutter_init: * @argc: FIXME @@ -506,20 +506,8 @@ int clutter_init (int *argc, char ***argv) { ClutterMainContext *context; - XVisualInfo *vinfo; static gboolean is_initialized = FALSE; - int gl_attributes[] = { - GLX_RGBA, - GLX_DOUBLEBUFFER, - GLX_RED_SIZE, 1, - GLX_GREEN_SIZE, 1, - GLX_BLUE_SIZE, 1, - /* GLX_DEPTH_SIZE, 1, */ - /* GLX_DEPTH_SIZE, 32, */ - 0 - }; - if (is_initialized) return 1; @@ -545,22 +533,13 @@ clutter_init (int *argc, char ***argv) if ((context->xdpy = XOpenDisplay (g_getenv ("DISPLAY"))) == NULL) { - g_warning("Unable to connect to X DISPLAY."); + g_critical ("Unable to connect to X DISPLAY."); return -1; } context->xscreen = DefaultScreen(context->xdpy); context->xwin_root = RootWindow(context->xdpy, context->xscreen); - context->xvinfo = glXChooseVisual (context->xdpy, - context->xscreen, - gl_attributes); - if (!context->xvinfo) - { - g_warning ("Unable to find suitable GL visual."); - - return -2; - } context->font_map = PANGO_FT2_FONT_MAP (pango_ft2_font_map_new ()); pango_ft2_font_map_set_resolution (context->font_map, 96.0, 96.0); @@ -571,6 +550,12 @@ clutter_init (int *argc, char ***argv) g_return_val_if_fail (CLUTTER_IS_STAGE (context->stage), -3); clutter_element_realize (CLUTTER_ELEMENT (context->stage)); + g_return_val_if_fail + (CLUTTER_ELEMENT_IS_REALIZED(CLUTTER_ELEMENT(context->stage)), -4); + + /* At least GL 1.2 is needed for CLAMP_TO_EDGE */ + g_return_val_if_fail(is_gl_version_at_least_12 (), -5); + events_init (); context->is_initialized = TRUE; diff --git a/clutter/clutter-private.h b/clutter/clutter-private.h index f6a75d8e9..db7537d30 100644 --- a/clutter/clutter-private.h +++ b/clutter/clutter-private.h @@ -52,8 +52,6 @@ struct _ClutterMainContext Display *xdpy; Window xwin_root; int xscreen; - XVisualInfo *xvinfo; - GC xgc; PangoFT2FontMap *font_map; diff --git a/clutter/clutter-stage.c b/clutter/clutter-stage.c index 03b7657a4..8dbb1eca1 100644 --- a/clutter/clutter-stage.c +++ b/clutter/clutter-stage.c @@ -35,6 +35,8 @@ #include #include +#include + /* the stage is a singleton instance */ static ClutterStage *stage_singleton = NULL; @@ -45,12 +47,14 @@ G_DEFINE_TYPE (ClutterStage, clutter_stage, CLUTTER_TYPE_GROUP); struct _ClutterStagePrivate { + XVisualInfo *xvisinfo; Window xwin; Pixmap xpixmap; gint xwin_width, xwin_height; /* FIXME target_width / height */ GLXPixmap glxpixmap; GLXContext gl_context; + ClutterColor color; @@ -245,8 +249,12 @@ clutter_stage_unrealize (ClutterElement *element) stage = CLUTTER_STAGE(element); priv = stage->priv; + CLUTTER_MARK(); + if (priv->want_offscreen) { + + if (priv->glxpixmap) { glXDestroyGLXPixmap (clutter_xdisplay(), priv->glxpixmap); @@ -267,6 +275,10 @@ clutter_stage_unrealize (ClutterElement *element) priv->xwin = None; } } + + glXMakeCurrent(clutter_xdisplay(), None, NULL); + glXDestroyContext (clutter_xdisplay(), priv->gl_context); + priv->gl_context = None; } static void @@ -279,31 +291,105 @@ clutter_stage_realize (ClutterElement *element) priv = stage->priv; + CLUTTER_MARK(); + if (priv->want_offscreen) { - priv->xpixmap = XCreatePixmap (clutter_xdisplay(), - clutter_root_xwindow(), - priv->xwin_width, priv->xwin_height, - clutter_xvisual()->depth); + int gl_attributes[] = { + GLX_RGBA, + GLX_RED_SIZE, 1, + GLX_GREEN_SIZE, 1, + GLX_BLUE_SIZE, 1, + 0 + }; - priv->glxpixmap = glXCreateGLXPixmap(clutter_xdisplay(), - clutter_xvisual(), - priv->xpixmap); - sync_fullscreen (stage); + if (priv->xvisinfo) + XFree(priv->xvisinfo); + + priv->xvisinfo = glXChooseVisual (clutter_xdisplay(), + clutter_xscreen(), + gl_attributes); + if (!priv->xvisinfo) + { + g_critical ("Unable to find suitable GL visual."); + CLUTTER_ELEMENT_UNSET_FLAGS (element, CLUTTER_ELEMENT_REALIZED); + return; + } if (priv->gl_context) glXDestroyContext (clutter_xdisplay(), priv->gl_context); + priv->xpixmap = XCreatePixmap (clutter_xdisplay(), + clutter_root_xwindow(), + priv->xwin_width, + priv->xwin_height, + priv->xvisinfo->depth); + + priv->glxpixmap = glXCreateGLXPixmap(clutter_xdisplay(), + priv->xvisinfo, + priv->xpixmap); + sync_fullscreen (stage); + /* indirect */ priv->gl_context = glXCreateContext (clutter_xdisplay(), - clutter_xvisual(), + priv->xvisinfo, 0, False); glXMakeCurrent(clutter_xdisplay(), priv->glxpixmap, priv->gl_context); + +#if 0 + /* Debug code for monitoring a off screen pixmap via window */ + { + Colormap cmap; + XSetWindowAttributes swa; + + cmap = XCreateColormap(clutter_xdisplay(), + clutter_root_xwindow(), + priv->xvisinfo->visual, AllocNone); + + /* create a window */ + swa.colormap = cmap; + + foo_win = XCreateWindow(clutter_xdisplay(), + clutter_root_xwindow(), + 0, 0, + priv->xwin_width, priv->xwin_height, + 0, + priv->xvisinfo->depth, + InputOutput, + priv->xvisinfo->visual, + CWColormap, &swa); + + XMapWindow(clutter_xdisplay(), foo_win); + } +#endif } else { + int gl_attributes[] = + { + GLX_RGBA, + GLX_DOUBLEBUFFER, + GLX_RED_SIZE, 1, + GLX_GREEN_SIZE, 1, + GLX_BLUE_SIZE, 1, + 0 + }; + + if (priv->xvisinfo) + XFree(priv->xvisinfo); + + priv->xvisinfo = glXChooseVisual (clutter_xdisplay(), + clutter_xscreen(), + gl_attributes); + if (!priv->xvisinfo) + { + g_critical ("Unable to find suitable GL visual."); + CLUTTER_ELEMENT_UNSET_FLAGS (element, CLUTTER_ELEMENT_REALIZED); + return; + } + priv->xwin = XCreateSimpleWindow(clutter_xdisplay(), clutter_root_xwindow(), 0, 0, @@ -329,13 +415,20 @@ clutter_stage_realize (ClutterElement *element) glXDestroyContext (clutter_xdisplay(), priv->gl_context); priv->gl_context = glXCreateContext (clutter_xdisplay(), - clutter_xvisual(), + priv->xvisinfo, 0, True); glXMakeCurrent(clutter_xdisplay(), priv->xwin, priv->gl_context); } + CLUTTER_DBG("===========================================") + CLUTTER_DBG("GL_VENDOR: %s\n", glGetString(GL_VENDOR)); + CLUTTER_DBG("GL_RENDERER: %s\n", glGetString(GL_RENDERER)); + CLUTTER_DBG("GL_VERSION: %s\n", glGetString(GL_VERSION)); + CLUTTER_DBG("GL_EXTENSIONS: %s\n", glGetString(GL_EXTENSIONS)); + CLUTTER_DBG("===========================================") + sync_gl_viewport (stage); } @@ -439,6 +532,12 @@ clutter_stage_set_property (GObject *object, if (priv->want_offscreen != g_value_get_boolean (value)) { clutter_element_unrealize (CLUTTER_ELEMENT(stage)); + /* NOTE: as we are changing GL contexts here. so + * all textures will need unreleasing as they will + * likely have set up ( i.e labels ) in the old + * context. We should probably somehow do this + * automatically + */ priv->want_offscreen = g_value_get_boolean (value); clutter_element_realize (CLUTTER_ELEMENT(stage)); } @@ -530,7 +629,7 @@ clutter_stage_class_init (ClutterStageClass *klass) "Fullscreen", "Make Clutter stage fullscreen", FALSE, - G_PARAM_READWRITE)); + G_PARAM_CONSTRUCT | G_PARAM_READWRITE)); g_object_class_install_property (gobject_class, PROP_OFFSCREEN, @@ -538,7 +637,7 @@ clutter_stage_class_init (ClutterStageClass *klass) "Offscreen", "Make Clutter stage offscreen", FALSE, - G_PARAM_READWRITE)); + G_PARAM_CONSTRUCT | G_PARAM_READWRITE)); g_object_class_install_property @@ -547,7 +646,7 @@ clutter_stage_class_init (ClutterStageClass *klass) "Hide Cursor", "Make Clutter stage cursor-less", FALSE, - G_PARAM_READWRITE)); + G_PARAM_CONSTRUCT | G_PARAM_READWRITE)); g_object_class_install_property (gobject_class, PROP_COLOR, @@ -623,9 +722,9 @@ clutter_stage_init (ClutterStage *self) self->priv = priv = CLUTTER_STAGE_GET_PRIVATE (self); - priv->want_offscreen = FALSE; + priv->want_offscreen = FALSE; priv->want_fullscreen = FALSE; - priv->hide_cursor = FALSE; + priv->hide_cursor = FALSE; priv->xwin_width = 100; priv->xwin_height = 100; @@ -693,6 +792,20 @@ clutter_stage_get_xwindow (ClutterStage *stage) return stage->priv->xwin; } +/** + * clutter_stage_get_xvisual + * @stage: A #ClutterStage + * + * Get the stages XVisualInfo. + * + * Return Value: Thes Stages XVisualInfo + **/ +const XVisualInfo* +clutter_stage_get_xvisual (ClutterStage *stage) +{ + return stage->priv->xvisinfo; +} + /** * clutter_stage_set_color * @stage: A #ClutterStage @@ -773,13 +886,16 @@ clutter_stage_snapshot (ClutterStage *stage, gint width, gint height) { - guchar *data; - GdkPixbuf *pixb, *fpixb; - ClutterElement *element; + guchar *data; + GdkPixbuf *pixb, *fpixb; + ClutterElement *element; + ClutterStagePrivate *priv; g_return_val_if_fail (CLUTTER_IS_STAGE (stage), NULL); g_return_val_if_fail (x >= 0 && y >= 0, NULL); + priv = stage->priv; + element = CLUTTER_ELEMENT (stage); if (width < 0) @@ -788,32 +904,46 @@ clutter_stage_snapshot (ClutterStage *stage, if (height < 0) height = clutter_element_get_height (element); - data = g_malloc0 (sizeof (guchar) * width * height * 3); - glReadPixels (x, - clutter_element_get_height (element) - - y - height, - width, - height, GL_RGB, GL_UNSIGNED_BYTE, data); + if (priv->want_offscreen) + { + gdk_pixbuf_xlib_init (clutter_xdisplay(), clutter_xscreen()); - pixb = gdk_pixbuf_new_from_data (data, - GDK_COLORSPACE_RGB, - FALSE, - 8, - width, - height, - width * 3, - snapshot_pixbuf_free, - NULL); + pixb = gdk_pixbuf_xlib_get_from_drawable + (NULL, + (Drawable)priv->xpixmap, + DefaultColormap(clutter_xdisplay(), + clutter_xscreen()), + priv->xvisinfo->visual, + x, y, 0, 0, width, height); + return pixb; + } + else + { + data = g_malloc0 (sizeof (guchar) * width * height * 4); - if (pixb == NULL) - return NULL; + glReadPixels (x, + clutter_element_get_height (element) + - y - height, + width, + height, GL_RGBA, GL_UNSIGNED_BYTE, data); + + pixb = gdk_pixbuf_new_from_data (data, + GDK_COLORSPACE_RGB, + TRUE, + 8, + width, + height, + width * 4, + snapshot_pixbuf_free, + NULL); + + fpixb = gdk_pixbuf_flip (pixb, TRUE); - fpixb = gdk_pixbuf_flip (pixb, TRUE); + g_object_unref (pixb); - g_object_unref (pixb); - - return fpixb; + return fpixb; + } } /** diff --git a/clutter/clutter-texture.c b/clutter/clutter-texture.c index daf58ea93..0c755e6da 100644 --- a/clutter/clutter-texture.c +++ b/clutter/clutter-texture.c @@ -40,7 +40,7 @@ G_DEFINE_TYPE (ClutterTexture, clutter_texture, CLUTTER_TYPE_ELEMENT); #define PIXEL_TYPE GL_UNSIGNED_INT_8_8_8_8_REV #endif -#define OVERLAP 0 +#define OVERLAP 0 /* Dont need as CLAMP_TO_EDGE */ /* FIXME: actually use */ typedef struct ClutterTextureTileDimention @@ -103,15 +103,15 @@ can_create (int width, CLUTTER_DBG("checking %ix%i", width, height); - glTexImage2D (GL_PROXY_TEXTURE_2D, 0, GL_RGBA, width, height, 0 /* border */, pixel_format, pixel_type, NULL); + CLUTTER_GLERR(); + glGetTexLevelParameteriv (GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &new_width); - return new_width != 0; } @@ -164,27 +164,23 @@ init_tiles (ClutterTexture *texture) x_pot = clutter_util_next_p2 (priv->width); y_pot = clutter_util_next_p2 (priv->height); - - if (x_pot - priv->width > priv->max_tile_waste - && y_pot - priv->height > priv->max_tile_waste) - { - while (!(can_create (x_pot, y_pot, priv->pixel_format, priv->pixel_type) - && (x_pot - priv->width < priv->max_tile_waste) - && (y_pot - priv->height < priv->max_tile_waste))) - { - CLUTTER_DBG("x_pot:%i - width:%i < max_waste:%i", - x_pot, priv->width, priv->max_tile_waste); - - CLUTTER_DBG("y_pot:%i - height:%i < max_waste:%i", - y_pot, priv->height, priv->max_tile_waste); - - if (x_pot > y_pot) - x_pot /= 2; - else - y_pot /= 2; - } - } + while (!(can_create (x_pot, y_pot, priv->pixel_format, priv->pixel_type) + && (x_pot - priv->width < priv->max_tile_waste) + && (y_pot - priv->height < priv->max_tile_waste))) + { + CLUTTER_DBG("x_pot:%i - width:%i < max_waste:%i", + x_pot, priv->width, priv->max_tile_waste); + + CLUTTER_DBG("y_pot:%i - height:%i < max_waste:%i", + y_pot, priv->height, priv->max_tile_waste); + + if (x_pot > y_pot) + x_pot /= 2; + else + y_pot /= 2; + } + if (priv->x_tiles) g_free(priv->x_tiles); @@ -290,11 +286,11 @@ texture_render_to_gl_quad (ClutterTexture *texture, glTexCoord2f (tx, 0); glVertex2i (qx2, qy1); glEnd (); - lasty += qy2 - qy1; + lasty += (qy2 - qy1) ; i++; } - lastx += qx2 - qx1; + lastx += (qx2 - qx1); } } @@ -376,11 +372,11 @@ clutter_texture_sync_pixbuf (ClutterTexture *texture) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, - priv->repeat_x ? GL_REPEAT : GL_CLAMP); + priv->repeat_x ? GL_REPEAT : GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, - priv->repeat_y ? GL_REPEAT : GL_CLAMP); + priv->repeat_y ? GL_REPEAT : GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, priv->filter_quality ? GL_LINEAR : GL_NEAREST); @@ -490,11 +486,11 @@ clutter_texture_sync_pixbuf (ClutterTexture *texture) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, - priv->repeat_x ? GL_REPEAT : GL_CLAMP); + priv->repeat_x ? GL_REPEAT : GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, - priv->repeat_y ? GL_REPEAT : GL_CLAMP); + priv->repeat_y ? GL_REPEAT : GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, priv->filter_quality ? GL_LINEAR : GL_NEAREST); @@ -876,8 +872,23 @@ clutter_texture_init (ClutterTexture *self) ClutterTexturePrivate *priv; priv = g_new0 (ClutterTexturePrivate, 1); + + /* FIXME: It seems defaults via props do not get set + * on all props for sub classes ( ie labels ). + * Should they be ? + * + * Setting them here also to be sure + safe. + */ + priv->max_tile_waste = 64; + priv->filter_quality = 0; + priv->tiled = TRUE; + priv->pixel_type = PIXEL_TYPE; + priv->pixel_format = GL_RGBA; + priv->repeat_x = FALSE; + priv->repeat_y = FALSE; priv->pixbuf = NULL; + self->priv = priv; }