2006-06-08 Matthew Allum <mallum@openedhand.com>

* 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
This commit is contained in:
Matthew Allum 2006-06-08 21:28:05 +00:00
parent f00e84606c
commit 9c572ff45a
5 changed files with 252 additions and 125 deletions

View File

@ -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,

View File

@ -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;

View File

@ -52,8 +52,6 @@ struct _ClutterMainContext
Display *xdpy;
Window xwin_root;
int xscreen;
XVisualInfo *xvinfo;
GC xgc;
PangoFT2FontMap *font_map;

View File

@ -35,6 +35,8 @@
#include <GL/glx.h>
#include <GL/gl.h>
#include <gdk-pixbuf-xlib/gdk-pixbuf-xlib.h>
/* the stage is a singleton instance */
static ClutterStage *stage_singleton = NULL;
@ -45,6 +47,7 @@ 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 */
@ -52,6 +55,7 @@ struct _ClutterStagePrivate
GLXPixmap glxpixmap;
GLXContext gl_context;
ClutterColor color;
guint want_fullscreen : 1;
@ -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);
fpixb = gdk_pixbuf_flip (pixb, TRUE);
pixb = gdk_pixbuf_new_from_data (data,
GDK_COLORSPACE_RGB,
TRUE,
8,
width,
height,
width * 4,
snapshot_pixbuf_free,
NULL);
g_object_unref (pixb);
fpixb = gdk_pixbuf_flip (pixb, TRUE);
return fpixb;
g_object_unref (pixb);
return fpixb;
}
}
/**

View File

@ -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;
}
@ -165,25 +165,21 @@ 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)))
{
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("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);
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 (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;
}