2006-09-19 Matthew Allum <mallum@openedhand.com>

* clutter/clutter-actor.c: (redraw_update_idle),
        (clutter_actor_queue_redraw):
        * clutter/clutter-main.c: (clutter_redraw):
        Remove now uneeded locks
        ( new gst and texture code makes redundant )

        * clutter/clutter-texture.c:
        * clutter/clutter-texture.h:
        Redo clutter texture as to not keep a reference
        to underlying texture.
This commit is contained in:
Matthew Allum 2006-09-19 19:27:16 +00:00
parent 55c723b9a2
commit 94c0e54bc3
5 changed files with 350 additions and 206 deletions

View File

@ -1,3 +1,16 @@
2006-09-19 Matthew Allum <mallum@openedhand.com>
* clutter/clutter-actor.c: (redraw_update_idle),
(clutter_actor_queue_redraw):
* clutter/clutter-main.c: (clutter_redraw):
Remove now uneeded locks
( new gst and texture code makes redundant )
* clutter/clutter-texture.c:
* clutter/clutter-texture.h:
Redo clutter texture as to not keep a reference
to underlying texture.
2006-09-15 Matthew Allum <mallum@openedhand.com>
More fixes from Bastien Nocera (#155):

View File

@ -100,16 +100,12 @@ redraw_update_idle (gpointer data)
{
ClutterMainContext *ctx = CLUTTER_CONTEXT();
clutter_threads_enter();
if (ctx->update_idle)
{
g_source_remove (ctx->update_idle);
ctx->update_idle = 0;
}
clutter_threads_leave();
clutter_redraw ();
return FALSE;
@ -764,16 +760,12 @@ clutter_actor_queue_redraw (ClutterActor *self)
{
ClutterMainContext *ctx = CLUTTER_CONTEXT();
clutter_threads_enter();
if (!ctx->update_idle)
{
ctx->update_idle = g_idle_add_full (-100 , /* very high priority */
redraw_update_idle,
NULL, NULL);
}
clutter_threads_leave();
}
/**

View File

@ -269,8 +269,6 @@ clutter_redraw (void)
CLUTTER_DBG("@@@ Redraw enter @@@");
clutter_threads_enter ();
if (clutter_want_fps ())
{
if (!timer)
@ -319,8 +317,6 @@ clutter_redraw (void)
}
}
clutter_threads_leave ();
CLUTTER_DBG("@@@ Redraw leave @@@");
}

View File

@ -29,6 +29,13 @@
*
* #ClutterTexture is a base class for displaying and manipulating pixel
* buffer type data.
*
* The #clutter_texture_set_from_data and #clutter_texture_set_pixbuf are
* used to copy image data into texture memory and subsequently realize the
* the texture. Unrealizing/hiding frees image data from texture memory moving
* to main system memory. Re-realizing then performs the opposite operation.
* This process allows basic management of commonly limited available texture
* memory.
*/
#include "clutter-texture.h"
@ -57,12 +64,13 @@ ClutterTextureTileDimention;
struct ClutterTexturePrivate
{
GdkPixbuf *pixbuf;
gint width, height;
GLenum pixel_format;
GLenum pixel_type;
GLenum target_type;
GdkPixbuf *local_pixbuf; /* non video memory copy */
gboolean sync_actor_size;
gint max_tile_waste;
guint filter_quality;
@ -73,7 +81,6 @@ struct ClutterTexturePrivate
ClutterTextureTileDimention *x_tiles, *y_tiles;
gint n_x_tiles, n_y_tiles;
GLuint *tiles;
};
enum
@ -99,9 +106,6 @@ enum
static int texture_signals[LAST_SIGNAL] = { 0 };
static void
init_tiles (ClutterTexture *texture);
static gboolean
can_create (int width,
int height,
@ -176,7 +180,7 @@ tile_dimension (int to_fill,
}
static void
init_tiles (ClutterTexture *texture)
texture_init_tiles (ClutterTexture *texture)
{
ClutterTexturePrivate *priv;
gint x_pot, y_pot;
@ -325,33 +329,21 @@ texture_render_to_gl_quad (ClutterTexture *texture,
}
static void
clutter_texture_unrealize (ClutterActor *actor)
texture_free_gl_resources (ClutterTexture *texture)
{
ClutterTexture *texture;
ClutterTexturePrivate *priv;
texture = CLUTTER_TEXTURE(actor);
priv = texture->priv;
if (priv->tiles == NULL)
return;
CLUTTER_MARK();
clutter_threads_enter ();
/* Free up texture memory */
if (!priv->tiled)
glDeleteTextures(1, priv->tiles);
else
glDeleteTextures(priv->n_x_tiles * priv->n_y_tiles, priv->tiles);
clutter_threads_leave ();
CLUTTER_MARK();
if (priv->tiles)
{
if (!priv->tiled)
glDeleteTextures(1, priv->tiles);
else
glDeleteTextures(priv->n_x_tiles * priv->n_y_tiles, priv->tiles);
g_free(priv->tiles);
priv->tiles = NULL;
}
@ -367,12 +359,16 @@ clutter_texture_unrealize (ClutterActor *actor)
g_free(priv->y_tiles);
priv->y_tiles = NULL;
}
CLUTTER_DBG("Texture unrealized");
}
static void
clutter_texture_sync_pixbuf (ClutterTexture *texture)
texture_upload_data (ClutterTexture *texture,
const guchar *data,
gboolean has_alpha,
gint width,
gint height,
gint rowstride,
gint bpp)
{
ClutterTexturePrivate *priv;
int x, y, i = 0;
@ -380,15 +376,13 @@ clutter_texture_sync_pixbuf (ClutterTexture *texture)
priv = texture->priv;
g_return_if_fail (priv->pixbuf != NULL);
g_return_if_fail (data != NULL);
CLUTTER_MARK();
if (!priv->tiled)
{
/* Single Texture
*
*/
/* Single Texture */
if (!priv->tiles)
{
priv->tiles = g_new (GLuint, 1);
@ -416,10 +410,8 @@ clutter_texture_sync_pixbuf (ClutterTexture *texture)
glTexParameteri(priv->target_type, GL_TEXTURE_MIN_FILTER,
priv->filter_quality ? GL_LINEAR : GL_NEAREST);
glPixelStorei (GL_UNPACK_ROW_LENGTH,
gdk_pixbuf_get_width(priv->pixbuf));
glPixelStorei (GL_UNPACK_ALIGNMENT,
gdk_pixbuf_get_n_channels (priv->pixbuf));
glPixelStorei (GL_UNPACK_ROW_LENGTH, priv->width);
glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
if (create_textures)
{
@ -434,11 +426,10 @@ clutter_texture_sync_pixbuf (ClutterTexture *texture)
height = clutter_util_next_p2(priv->height);
}
/* NOTE: Change to GL_RGB for non alpha textures */
glTexImage2D(priv->target_type,
0,
(gdk_pixbuf_get_n_channels (priv->pixbuf) == 4) ?
GL_RGBA : GL_RGB,
/* (has_alpha) ? GL_RGBA : GL_RGB, */
GL_RGBA,
width,
height,
0,
@ -452,8 +443,7 @@ clutter_texture_sync_pixbuf (ClutterTexture *texture)
priv->height,
priv->pixel_format,
priv->pixel_type,
gdk_pixbuf_get_pixels(priv->pixbuf));
data);
return;
}
@ -474,19 +464,15 @@ clutter_texture_sync_pixbuf (ClutterTexture *texture)
for (x=0; x < priv->n_x_tiles; x++)
for (y=0; y < priv->n_y_tiles; y++)
{
GdkPixbuf *pixtmp;
int src_h, src_w;
guchar *tmp;
gint src_h, src_w, dy;
src_w = priv->x_tiles[x].size;
src_h = priv->y_tiles[y].size;
pixtmp
= gdk_pixbuf_new(GDK_COLORSPACE_RGB,
(gdk_pixbuf_get_n_channels (priv->pixbuf) == 4) ?
TRUE : FALSE,
8,
priv->x_tiles[x].size,
priv->y_tiles[y].size);
/* fixme - gslice ? */
tmp = g_malloc (sizeof(guchar) * priv->x_tiles[x].size
* priv->y_tiles[y].size * bpp);
/* clip */
if (priv->x_tiles[x].pos + src_w > priv->width)
@ -507,13 +493,13 @@ clutter_texture_sync_pixbuf (ClutterTexture *texture)
priv->x_tiles[x].size,
priv->y_tiles[y].size);
gdk_pixbuf_copy_area(priv->pixbuf,
priv->x_tiles[x].pos,
priv->y_tiles[y].pos,
src_w,
src_h,
pixtmp,
0,0);
for (dy = 0; dy < src_h; dy++)
{
memcpy (tmp + (dy * src_w * bpp),
data + ((priv->y_tiles[y].pos + dy) * rowstride)
+ (priv->x_tiles[x].pos * bpp),
(src_w * bpp));
}
#ifdef CLUTTER_DUMP_TILES
{
@ -545,23 +531,22 @@ clutter_texture_sync_pixbuf (ClutterTexture *texture)
glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
glPixelStorei (GL_UNPACK_ROW_LENGTH, gdk_pixbuf_get_width(pixtmp));
glPixelStorei (GL_UNPACK_ALIGNMENT,
gdk_pixbuf_get_n_channels (priv->pixbuf));
glPixelStorei (GL_UNPACK_ROW_LENGTH, priv->width);
glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
if (create_textures)
{
glTexImage2D(priv->target_type,
0,
(gdk_pixbuf_get_n_channels (priv->pixbuf) == 4) ?
(bpp == 4) ?
GL_RGBA : GL_RGB,
priv->x_tiles[x].size,
priv->y_tiles[y].size,
0,
priv->pixel_format,
priv->pixel_type,
gdk_pixbuf_get_pixels(pixtmp));
tmp);
}
else
{
@ -573,39 +558,72 @@ clutter_texture_sync_pixbuf (ClutterTexture *texture)
priv->y_tiles[y].size,
priv->pixel_format,
priv->pixel_type,
gdk_pixbuf_get_pixels(pixtmp));
tmp);
}
g_object_unref(pixtmp);
g_free(tmp);
i++;
}
}
static void
clutter_texture_realize (ClutterActor *actor)
clutter_texture_unrealize (ClutterActor *actor)
{
ClutterTexture *texture;
ClutterTexturePrivate *priv;
texture = CLUTTER_TEXTURE(actor);
priv = texture->priv;
if (priv->tiles == NULL)
return;
CLUTTER_MARK();
if (texture->priv->pixbuf == NULL)
/* Move image data from video to main memory
*/
if (priv->local_pixbuf == NULL)
priv->local_pixbuf = clutter_texture_get_pixbuf (texture);
texture_free_gl_resources (texture);
CLUTTER_DBG("Texture unrealized");
}
static void
clutter_texture_realize (ClutterActor *actor)
{
ClutterTexture *texture;
ClutterTexturePrivate *priv;
texture = CLUTTER_TEXTURE(actor);
priv = texture->priv;
CLUTTER_MARK();
if (priv->local_pixbuf != NULL)
{
/* Dont allow realization with no pixbuf */
CLUTTER_DBG("*** Texture has no pixbuf cannot realize ***");
/* Move any local image data we have from unrealization
* back into video memory.
*/
clutter_texture_set_pixbuf (texture, priv->local_pixbuf);
g_object_unref (priv->local_pixbuf);
priv->local_pixbuf = NULL;
}
else
{
/* Dont allow realization with no pixbuf - note set_pixbuf/data
* will set realize flags.
*/
CLUTTER_DBG("*** Texture has no image data cannot realize ***");
CLUTTER_DBG("*** flags %i ***", actor->flags);
CLUTTER_ACTOR_UNSET_FLAGS (actor, CLUTTER_ACTOR_REALIZED);
CLUTTER_DBG("*** flags %i ***", actor->flags);
return;
}
CLUTTER_DBG("Texture realized");
if (texture->priv->tiled)
init_tiles(texture);
clutter_texture_sync_pixbuf (texture);
}
static void
@ -653,19 +671,19 @@ clutter_texture_paint (ClutterActor *self)
static void
clutter_texture_dispose (GObject *object)
{
ClutterTexture *self = CLUTTER_TEXTURE(object);
ClutterTexture *texture = CLUTTER_TEXTURE(object);
ClutterTexturePrivate *priv;
priv = self->priv;
priv = texture->priv;
if (priv != NULL)
{
clutter_actor_unrealize (CLUTTER_ACTOR(self));
texture_free_gl_resources (texture);
if (priv->pixbuf != NULL)
if (priv->local_pixbuf != NULL)
{
g_object_unref (priv->pixbuf);
priv->pixbuf = NULL;
g_object_unref (priv->local_pixbuf);
priv->local_pixbuf = NULL;
}
}
@ -755,7 +773,11 @@ clutter_texture_get_property (GObject *object,
switch (prop_id)
{
case PROP_PIXBUF:
g_value_set_pointer (value, priv->pixbuf);
{
GdkPixbuf *pixb;
pixb = clutter_texture_get_pixbuf (texture);
g_value_set_pointer (value, pixb);
}
break;
case PROP_USE_TILES:
g_value_set_boolean (value, priv->tiled);
@ -932,12 +954,6 @@ clutter_texture_init (ClutterTexture *self)
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;
@ -945,7 +961,6 @@ clutter_texture_init (ClutterTexture *self)
priv->pixel_format = GL_RGBA;
priv->repeat_x = FALSE;
priv->repeat_y = FALSE;
priv->pixbuf = NULL;
if (clutter_feature_available (CLUTTER_FEATURE_TEXTURE_RECTANGLE))
{
@ -958,90 +973,173 @@ clutter_texture_init (ClutterTexture *self)
self->priv = priv;
}
static void
pixbuf_destroy_notify (guchar *pixels, gpointer data)
{
g_free (pixels);
}
/**
* clutter_texture_get_pixbuf:
* @texture: A #ClutterTexture
*
* Gets the underlying #GdkPixbuf for the #ClutterTexture.
* Gets a #GdkPixbuf representation of the #ClutterTexture data.
* The created #GdkPixbuf is not owned by the texture but the caller.
*
* Return value: The underlying #GdkPixbuf
* Return value: A #GdkPixbuf
**/
GdkPixbuf*
clutter_texture_get_pixbuf (ClutterTexture* texture)
{
return texture->priv->pixbuf;
ClutterTexturePrivate *priv;
GdkPixbuf *pixbuf = NULL;
guchar *pixels = NULL;
priv = texture->priv;
if (priv->tiles == NULL)
return NULL;
if (!priv->tiled)
{
pixels = g_malloc (priv->width * priv->height * 4);
if (pixels == NULL)
return NULL;
glBindTexture(priv->target_type, priv->tiles[0]);
glPixelStorei (GL_UNPACK_ROW_LENGTH, priv->width);
glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
/* read data from gl text and return as pixbuf */
glGetTexImage (priv->target_type,
0,
priv->pixel_format,
priv->pixel_type,
(GLvoid*)pixels);
pixbuf = gdk_pixbuf_new_from_data ((const guchar*)pixels,
GDK_COLORSPACE_RGB,
(priv->pixel_format == GL_RGBA),
8,
priv->width,
priv->height,
priv->width * 4,
pixbuf_destroy_notify,
NULL);
}
else
{
int x,y,i;
i = 0;
pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
(priv->pixel_format == GL_RGBA),
8,
priv->width,
priv->height);
for (x=0; x < priv->n_x_tiles; x++)
for (y=0; y < priv->n_y_tiles; y++)
{
GdkPixbuf *tmp_pixb;
gint src_h, src_w;
src_w = priv->x_tiles[x].size;
src_h = priv->y_tiles[y].size;
pixels = g_malloc (src_w * src_h *4);
glBindTexture(priv->target_type, priv->tiles[i]);
glPixelStorei (GL_UNPACK_ROW_LENGTH, src_w);
glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
glGetTexImage (priv->target_type,
0,
priv->pixel_format,
priv->pixel_type,
(GLvoid*)pixels);
tmp_pixb
= gdk_pixbuf_new_from_data ((const guchar*)pixels,
GDK_COLORSPACE_RGB,
(priv->pixel_format == GL_RGBA),
8,
src_w,
src_h,
src_w * 4,
pixbuf_destroy_notify,
NULL);
gdk_pixbuf_copy_area (tmp_pixb,
0,
0,
src_w,
src_h,
pixbuf,
priv->x_tiles[x].pos,
priv->x_tiles[y].pos);
g_object_unref (tmp_pixb);
i++;
}
}
return pixbuf;
}
/**
* clutter_texture_set_pixbuf:
* clutter_texture_set_from_data:
* @texture: A #ClutterTexture
* @pixbuf: A #GdkPixbuf
* @data: Image data in RGB type colorspace.
* @has_alpha: Set to TRUE if image data has a alpha channel.
* @width: Width in pixels of image data.
* @height: Height in pixels of image data
* @rowstride: Distance in bytes between row starts.
* @bpp: bytes per pixel ( Currently only 4 supported )
*
* Sets the underlying #GdkPixbuf for the #ClutterTexture
* Sets #ClutterTexture image data.
*
* Since 0.2. This function is likely to change in future versions.
**/
void
clutter_texture_set_pixbuf (ClutterTexture *texture, GdkPixbuf *pixbuf)
clutter_texture_set_from_data (ClutterTexture *texture,
const guchar *data,
gboolean has_alpha,
gint width,
gint height,
gint rowstride,
gint bpp)
{
ClutterTexturePrivate *priv;
gboolean texture_dirty = TRUE;
priv = texture->priv;
g_return_if_fail (pixbuf != NULL);
g_return_if_fail (data != NULL);
g_return_if_fail (bpp == 4);
if (priv->pixbuf != NULL)
{
texture_dirty = (gdk_pixbuf_get_width (pixbuf)
!= gdk_pixbuf_get_width (priv->pixbuf)
||
gdk_pixbuf_get_height (pixbuf)
!= gdk_pixbuf_get_height (priv->pixbuf)
||
gdk_pixbuf_get_n_channels (pixbuf)
!= gdk_pixbuf_get_n_channels (priv->pixbuf));
/* FIXME: check other image props */
texture_dirty = (width != priv->width || height != priv->height);
g_object_unref(priv->pixbuf);
priv->width = width;
priv->height = height;
if (has_alpha)
priv->pixel_format = GL_RGBA;
else
priv->pixel_format = GL_RGB;
/* If the actual pixbuf has changed size/format destroy
* existing textures ready for recreation. If
* size matches we can reuse.
*/
if (texture_dirty)
{
clutter_actor_unrealize (CLUTTER_ACTOR(texture));
}
else
{
/* If texture realised sync things for change */
if (CLUTTER_ACTOR_IS_REALIZED(CLUTTER_ACTOR(texture)))
{
priv->pixbuf = pixbuf;
texture_free_gl_resources (texture);
/* FIXME: better locking stratergy
*/
clutter_threads_enter();
clutter_texture_sync_pixbuf (texture);
clutter_threads_leave();
}
}
}
clutter_threads_enter();
priv->pixbuf = pixbuf;
priv->width = gdk_pixbuf_get_width (pixbuf);
priv->height = gdk_pixbuf_get_height (pixbuf);
g_object_ref (pixbuf);
if (gdk_pixbuf_get_n_channels (pixbuf) == 3)
priv->pixel_format = GL_RGB;
else
priv->pixel_format = GL_RGBA;
/* Force tiling if pixbuf is too big for single texture */
if (priv->tiled == FALSE && texture_dirty)
if (priv->tiled == FALSE)
{
if (priv->target_type == GL_TEXTURE_RECTANGLE_ARB
&& !can_create_rect_arb (priv->width,
@ -1062,27 +1160,63 @@ clutter_texture_set_pixbuf (ClutterTexture *texture, GdkPixbuf *pixbuf)
priv->tiled = TRUE;
}
}
clutter_threads_leave();
/* Figure our tiling etc */
if (priv->tiled)
texture_init_tiles (texture);
}
CLUTTER_DBG("set size %ix%i\n", priv->width, priv->height);
texture_upload_data (texture, data, has_alpha,
width, height, rowstride, bpp);
CLUTTER_ACTOR_SET_FLAGS (CLUTTER_ACTOR(texture), CLUTTER_ACTOR_REALIZED);
if (texture_dirty)
{
g_signal_emit (texture, texture_signals[SIGNAL_SIZE_CHANGE],
0, priv->width, priv->height);
if (priv->sync_actor_size)
clutter_actor_set_size (CLUTTER_ACTOR(texture),
priv->width,
priv->height);
}
CLUTTER_DBG("set size %ix%i\n", priv->width, priv->height);
if (texture_dirty)
g_signal_emit (texture, texture_signals[SIGNAL_SIZE_CHANGE],
0, priv->width, priv->height);
if (priv->tiled && texture_dirty)
init_tiles (texture);
/* rename signal */
g_signal_emit (texture, texture_signals[SIGNAL_PIXBUF_CHANGE], 0);
/* If resized actor may need resizing but paint() will do this */
if (CLUTTER_ACTOR_IS_MAPPED (CLUTTER_ACTOR(texture)))
clutter_actor_queue_redraw (CLUTTER_ACTOR(texture));
}
/**
* clutter_texture_set_pixbuf:
* @texture: A #ClutterTexture
* @pixbuf: A #GdkPixbuf
*
* Sets a #ClutterTexture image data from a #GdkPixbuf
*
**/
void
clutter_texture_set_pixbuf (ClutterTexture *texture, GdkPixbuf *pixbuf)
{
ClutterTexturePrivate *priv;
priv = texture->priv;
g_return_if_fail (pixbuf != NULL);
clutter_texture_set_from_data (texture,
gdk_pixbuf_get_pixels (pixbuf),
gdk_pixbuf_get_has_alpha (pixbuf),
gdk_pixbuf_get_width (pixbuf),
gdk_pixbuf_get_height (pixbuf),
gdk_pixbuf_get_rowstride (pixbuf),
4);
}
/**
@ -1129,7 +1263,7 @@ clutter_texture_new (void)
* Gets the size in pixels of the untransformed underlying texture pixbuf data.
*
**/
void
void /* FIXME: rename to get_image_size */
clutter_texture_get_base_size (ClutterTexture *texture,
gint *width,
gint *height)

View File

@ -81,6 +81,15 @@ ClutterActor *clutter_texture_new_from_pixbuf (GdkPixbuf *pixbuf);
ClutterActor *clutter_texture_new (void);
void
clutter_texture_set_from_data (ClutterTexture *texture,
const guchar *data,
gboolean has_alpha,
gint width,
gint height,
gint rowstride,
gint bpp);
void clutter_texture_set_pixbuf (ClutterTexture *texture, GdkPixbuf *pixbuf);
GdkPixbuf *clutter_texture_get_pixbuf (ClutterTexture* texture);