mirror of
https://github.com/brl/mutter.git
synced 2024-11-23 08:30:42 -05:00
Merge branch 'async-texture-thread-pool'
This commit is contained in:
commit
0674fded7f
@ -73,6 +73,8 @@ G_DEFINE_TYPE_WITH_CODE (ClutterTexture,
|
|||||||
|
|
||||||
#define CLUTTER_TEXTURE_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CLUTTER_TYPE_TEXTURE, ClutterTexturePrivate))
|
#define CLUTTER_TEXTURE_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CLUTTER_TYPE_TEXTURE, ClutterTexturePrivate))
|
||||||
|
|
||||||
|
typedef struct _ClutterTextureAsyncData ClutterTextureAsyncData;
|
||||||
|
|
||||||
struct _ClutterTexturePrivate
|
struct _ClutterTexturePrivate
|
||||||
{
|
{
|
||||||
gint width;
|
gint width;
|
||||||
@ -97,14 +99,37 @@ struct _ClutterTexturePrivate
|
|||||||
guint repeat_y : 1;
|
guint repeat_y : 1;
|
||||||
guint in_dispose : 1;
|
guint in_dispose : 1;
|
||||||
guint keep_aspect_ratio : 1;
|
guint keep_aspect_ratio : 1;
|
||||||
guint load_async : 1;
|
guint load_size_async : 1;
|
||||||
|
guint load_data_async : 1;
|
||||||
|
guint load_async_set : 1; /* used to make load_async
|
||||||
|
possible */
|
||||||
|
|
||||||
GThread *load_thread;
|
ClutterTextureAsyncData *async_data;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _ClutterTextureAsyncData
|
||||||
|
{
|
||||||
|
/* Mutex used to synchronize setting the abort flag */
|
||||||
|
GMutex *mutex;
|
||||||
|
|
||||||
|
/* If set to true, the loaded data will be discarded */
|
||||||
|
gboolean abort;
|
||||||
|
|
||||||
|
/* The texture for which the data is being loaded */
|
||||||
|
ClutterTexture *texture;
|
||||||
|
|
||||||
|
/* Source ID of the idle handler for loading. If this is zero then
|
||||||
|
the data is being loaded in a thread from the thread pool. Once
|
||||||
|
the thread is finished it will be converted to idle load handler
|
||||||
|
and load_idle will be nonzero. If load_idle is nonzero then the
|
||||||
|
rest of the load can safely be aborted by just removing the
|
||||||
|
source, otherwise the abort flag needs to be set and the data
|
||||||
|
should be disowned */
|
||||||
guint load_idle;
|
guint load_idle;
|
||||||
|
|
||||||
gchar *load_filename;
|
gchar *load_filename;
|
||||||
CoglBitmap *load_bitmap;
|
CoglBitmap *load_bitmap;
|
||||||
GError *load_error;
|
GError *load_error;
|
||||||
gboolean abort;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
enum
|
enum
|
||||||
@ -121,7 +146,8 @@ enum
|
|||||||
PROP_COGL_MATERIAL,
|
PROP_COGL_MATERIAL,
|
||||||
PROP_FILENAME,
|
PROP_FILENAME,
|
||||||
PROP_KEEP_ASPECT_RATIO,
|
PROP_KEEP_ASPECT_RATIO,
|
||||||
PROP_LOAD_ASYNC
|
PROP_LOAD_ASYNC,
|
||||||
|
PROP_LOAD_DATA_ASYNC,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum
|
enum
|
||||||
@ -130,12 +156,13 @@ enum
|
|||||||
PIXBUF_CHANGE,
|
PIXBUF_CHANGE,
|
||||||
LOAD_SUCCESS,
|
LOAD_SUCCESS,
|
||||||
LOAD_FINISHED,
|
LOAD_FINISHED,
|
||||||
|
|
||||||
LAST_SIGNAL
|
LAST_SIGNAL
|
||||||
};
|
};
|
||||||
|
|
||||||
static int texture_signals[LAST_SIGNAL] = { 0 };
|
static int texture_signals[LAST_SIGNAL] = { 0 };
|
||||||
|
|
||||||
|
static GThreadPool *async_thread_pool = NULL;
|
||||||
|
|
||||||
static void
|
static void
|
||||||
texture_fbo_free_resources (ClutterTexture *texture);
|
texture_fbo_free_resources (ClutterTexture *texture);
|
||||||
|
|
||||||
@ -630,6 +657,29 @@ clutter_texture_paint (ClutterActor *self)
|
|||||||
0, 0, t_w, t_h);
|
0, 0, t_w, t_h);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
clutter_texture_async_data_free (ClutterTextureAsyncData *data)
|
||||||
|
{
|
||||||
|
/* This function should only be called either from the main thread
|
||||||
|
once it is known that the load thread has completed or from the
|
||||||
|
load thread itself if the abort flag is true (in which case the
|
||||||
|
main thread has disowned the data) */
|
||||||
|
|
||||||
|
if (data->load_filename)
|
||||||
|
g_free (data->load_filename);
|
||||||
|
|
||||||
|
if (data->load_bitmap)
|
||||||
|
cogl_bitmap_free (data->load_bitmap);
|
||||||
|
|
||||||
|
if (data->load_error)
|
||||||
|
g_error_free (data->load_error);
|
||||||
|
|
||||||
|
if (data->mutex)
|
||||||
|
g_mutex_free (data->mutex);
|
||||||
|
|
||||||
|
g_slice_free (ClutterTextureAsyncData, data);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* clutter_texture_async_load_cancel:
|
* clutter_texture_async_load_cancel:
|
||||||
* @texture: a #ClutterTexture
|
* @texture: a #ClutterTexture
|
||||||
@ -642,32 +692,40 @@ clutter_texture_async_load_cancel (ClutterTexture *texture)
|
|||||||
{
|
{
|
||||||
ClutterTexturePrivate *priv = texture->priv;
|
ClutterTexturePrivate *priv = texture->priv;
|
||||||
|
|
||||||
if (priv->load_thread)
|
if (priv->async_data)
|
||||||
{
|
{
|
||||||
priv->abort = TRUE;
|
GMutex *mutex = priv->async_data->mutex;
|
||||||
g_thread_join (priv->load_thread);
|
|
||||||
priv->load_thread = NULL;
|
/* The mutex will only be NULL if the no thread was used for
|
||||||
|
this load, in which case there's no need for any
|
||||||
|
synchronization */
|
||||||
|
if (mutex)
|
||||||
|
g_mutex_lock (mutex);
|
||||||
|
|
||||||
|
/* If there is no thread behind this load then we can just abort
|
||||||
|
the idle handler and destroy the load data immediately */
|
||||||
|
if (priv->async_data->load_idle)
|
||||||
|
{
|
||||||
|
g_source_remove (priv->async_data->load_idle);
|
||||||
|
priv->async_data->load_idle = 0;
|
||||||
|
|
||||||
|
if (mutex)
|
||||||
|
g_mutex_unlock (mutex);
|
||||||
|
|
||||||
|
clutter_texture_async_data_free (priv->async_data);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Otherwise we need to tell the thread to abort and disown
|
||||||
|
the data */
|
||||||
|
priv->async_data->abort = TRUE;
|
||||||
|
|
||||||
|
if (mutex)
|
||||||
|
g_mutex_unlock (mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (priv->load_idle)
|
priv->async_data = NULL;
|
||||||
{
|
|
||||||
g_source_remove (priv->load_idle);
|
|
||||||
priv->load_idle = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (priv->load_error)
|
|
||||||
{
|
|
||||||
g_error_free (priv->load_error);
|
|
||||||
priv->load_error = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (priv->load_bitmap)
|
|
||||||
{
|
|
||||||
cogl_bitmap_free (priv->load_bitmap);
|
|
||||||
priv->load_bitmap = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
g_free (priv->load_filename);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -778,8 +836,20 @@ clutter_texture_set_property (GObject *object,
|
|||||||
case PROP_KEEP_ASPECT_RATIO:
|
case PROP_KEEP_ASPECT_RATIO:
|
||||||
priv->keep_aspect_ratio = g_value_get_boolean (value);
|
priv->keep_aspect_ratio = g_value_get_boolean (value);
|
||||||
break;
|
break;
|
||||||
|
case PROP_LOAD_DATA_ASYNC:
|
||||||
|
if (g_value_get_boolean (value))
|
||||||
|
{
|
||||||
|
priv->load_async_set = TRUE;
|
||||||
|
priv->load_data_async = TRUE;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case PROP_LOAD_ASYNC:
|
case PROP_LOAD_ASYNC:
|
||||||
priv->load_async = g_value_get_boolean (value);
|
if (g_value_get_boolean (value))
|
||||||
|
{
|
||||||
|
priv->load_data_async = TRUE;
|
||||||
|
priv->load_async_set = TRUE;
|
||||||
|
priv->load_size_async = TRUE;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||||
@ -836,9 +906,6 @@ clutter_texture_get_property (GObject *object,
|
|||||||
case PROP_KEEP_ASPECT_RATIO:
|
case PROP_KEEP_ASPECT_RATIO:
|
||||||
g_value_set_boolean (value, priv->keep_aspect_ratio);
|
g_value_set_boolean (value, priv->keep_aspect_ratio);
|
||||||
break;
|
break;
|
||||||
case PROP_LOAD_ASYNC:
|
|
||||||
g_value_set_boolean (value, priv->load_async);
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||||
break;
|
break;
|
||||||
@ -974,17 +1041,23 @@ clutter_texture_class_init (ClutterTextureClass *klass)
|
|||||||
FALSE,
|
FALSE,
|
||||||
CLUTTER_PARAM_READWRITE));
|
CLUTTER_PARAM_READWRITE));
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ClutterTexture:load-async:
|
* ClutterTexture:load-async:
|
||||||
*
|
*
|
||||||
* Tries to load a texture from a filename by using a local thread
|
* Tries to load a texture from a filename by using a local thread to perform
|
||||||
* to perform the read operations. Threading is only enabled if
|
* the read operations. The initially created texture has dimensions 0x0 when
|
||||||
* g_thread_init() has been called prior to clutter_init(), otherwise
|
* the true size becomes available the #ClutterTexture::size-change signal is
|
||||||
* #ClutterTexture will use the main loop to load the image.
|
* emitted and when the image has completed loading the
|
||||||
|
* #ClutterTexture::load-finished signal is emitted.
|
||||||
*
|
*
|
||||||
* The upload of the texture data on the GL pipeline is not
|
* Threading is only enabled if g_thread_init() has been called prior to
|
||||||
* asynchronous, as it must be performed from within the same
|
* clutter_init(), otherwise #ClutterTexture will use the main loop to load
|
||||||
* thread that called clutter_main().
|
* the image.
|
||||||
|
*
|
||||||
|
* The upload of the texture data on the GL pipeline is not asynchronous, as
|
||||||
|
* it must be performed from within the same thread that called
|
||||||
|
* clutter_main().
|
||||||
*
|
*
|
||||||
* Since: 1.0
|
* Since: 1.0
|
||||||
*/
|
*/
|
||||||
@ -995,7 +1068,27 @@ clutter_texture_class_init (ClutterTextureClass *klass)
|
|||||||
"Load files inside a thread to avoid blocking when "
|
"Load files inside a thread to avoid blocking when "
|
||||||
"loading images.",
|
"loading images.",
|
||||||
FALSE,
|
FALSE,
|
||||||
CLUTTER_PARAM_READWRITE));
|
CLUTTER_PARAM_WRITABLE | G_PARAM_CONSTRUCT));
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ClutterTexture:load-data-async:
|
||||||
|
*
|
||||||
|
* Like #ClutterTexture:load-async but loads the width and height
|
||||||
|
* synchronously causing some blocking.
|
||||||
|
*
|
||||||
|
* Since: 1.0
|
||||||
|
*/
|
||||||
|
g_object_class_install_property
|
||||||
|
(gobject_class, PROP_LOAD_DATA_ASYNC,
|
||||||
|
g_param_spec_boolean ("load-data-async",
|
||||||
|
"Load data asynchronously",
|
||||||
|
"Decode image data files inside a thread to reduce "
|
||||||
|
"blocking when loading images.",
|
||||||
|
FALSE,
|
||||||
|
CLUTTER_PARAM_WRITABLE | G_PARAM_CONSTRUCT));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ClutterTexture::size-change:
|
* ClutterTexture::size-change:
|
||||||
@ -1550,14 +1643,16 @@ clutter_texture_set_from_yuv_data (ClutterTexture *texture,
|
|||||||
/*
|
/*
|
||||||
* clutter_texture_async_load_complete:
|
* clutter_texture_async_load_complete:
|
||||||
* @self: a #ClutterTexture
|
* @self: a #ClutterTexture
|
||||||
|
* @bitmap: a #CoglBitmap
|
||||||
* @error: load error
|
* @error: load error
|
||||||
*
|
*
|
||||||
* If @error is %NULL, loads the #CoglBitmap into a #CoglTexture.
|
* If @error is %NULL, loads @bitmap into a #CoglTexture.
|
||||||
*
|
*
|
||||||
* This function emits the ::load-finished signal on @self.
|
* This function emits the ::load-finished signal on @self.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
clutter_texture_async_load_complete (ClutterTexture *self,
|
clutter_texture_async_load_complete (ClutterTexture *self,
|
||||||
|
CoglBitmap *bitmap,
|
||||||
const GError *error)
|
const GError *error)
|
||||||
{
|
{
|
||||||
ClutterTexturePrivate *priv = self->priv;
|
ClutterTexturePrivate *priv = self->priv;
|
||||||
@ -1565,6 +1660,8 @@ clutter_texture_async_load_complete (ClutterTexture *self,
|
|||||||
CoglTextureFlags flags = COGL_TEXTURE_NONE;
|
CoglTextureFlags flags = COGL_TEXTURE_NONE;
|
||||||
gint waste = -1;
|
gint waste = -1;
|
||||||
|
|
||||||
|
priv->async_data = NULL;
|
||||||
|
|
||||||
if (error == NULL)
|
if (error == NULL)
|
||||||
{
|
{
|
||||||
if (!priv->no_slice)
|
if (!priv->no_slice)
|
||||||
@ -1573,14 +1670,17 @@ clutter_texture_async_load_complete (ClutterTexture *self,
|
|||||||
if (priv->filter_quality == CLUTTER_TEXTURE_QUALITY_HIGH)
|
if (priv->filter_quality == CLUTTER_TEXTURE_QUALITY_HIGH)
|
||||||
flags |= COGL_TEXTURE_AUTO_MIPMAP;
|
flags |= COGL_TEXTURE_AUTO_MIPMAP;
|
||||||
|
|
||||||
handle = cogl_texture_new_from_bitmap (priv->load_bitmap,
|
handle = cogl_texture_new_from_bitmap (bitmap,
|
||||||
waste, flags,
|
waste, flags,
|
||||||
COGL_PIXEL_FORMAT_ANY);
|
COGL_PIXEL_FORMAT_ANY);
|
||||||
clutter_texture_set_cogl_texture (self, handle);
|
clutter_texture_set_cogl_texture (self, handle);
|
||||||
|
if (priv->load_size_async)
|
||||||
|
{
|
||||||
|
g_signal_emit (self, texture_signals[SIZE_CHANGE], 0,
|
||||||
|
cogl_texture_get_width(handle),
|
||||||
|
cogl_texture_get_height (handle));
|
||||||
|
}
|
||||||
cogl_texture_unref (handle);
|
cogl_texture_unref (handle);
|
||||||
|
|
||||||
cogl_bitmap_free (priv->load_bitmap);
|
|
||||||
priv->load_bitmap = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
g_signal_emit (self, texture_signals[LOAD_FINISHED], 0, error);
|
g_signal_emit (self, texture_signals[LOAD_FINISHED], 0, error);
|
||||||
@ -1589,96 +1689,100 @@ clutter_texture_async_load_complete (ClutterTexture *self,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
clutter_texture_thread_cb (gpointer data)
|
clutter_texture_thread_idle_func (gpointer user_data)
|
||||||
{
|
{
|
||||||
ClutterTexture *self = data;
|
ClutterTextureAsyncData *data = user_data;
|
||||||
ClutterTexturePrivate *priv = self->priv;
|
|
||||||
|
|
||||||
priv->load_idle = 0;
|
/* Grab the mutex so we can be sure the thread has unlocked it
|
||||||
|
before we destroy it */
|
||||||
|
g_mutex_lock (data->mutex);
|
||||||
|
g_mutex_unlock (data->mutex);
|
||||||
|
|
||||||
if (priv->load_thread)
|
clutter_texture_async_load_complete (data->texture, data->load_bitmap,
|
||||||
|
data->load_error);
|
||||||
|
|
||||||
|
clutter_texture_async_data_free (data);
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
clutter_texture_thread_func (gpointer user_data, gpointer pool_data)
|
||||||
{
|
{
|
||||||
g_thread_join (priv->load_thread);
|
ClutterTextureAsyncData *data = user_data;
|
||||||
priv->load_thread = NULL;
|
gboolean should_abort;
|
||||||
|
|
||||||
|
/* Make sure we haven't been told to abort before the thread had a
|
||||||
|
chance to run */
|
||||||
|
g_mutex_lock (data->mutex);
|
||||||
|
should_abort = data->abort;
|
||||||
|
g_mutex_unlock (data->mutex);
|
||||||
|
|
||||||
|
if (should_abort)
|
||||||
|
{
|
||||||
|
/* If we've been told to abort then main thread has disowned the
|
||||||
|
async data and we need to free it */
|
||||||
|
clutter_texture_async_data_free (data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
data->load_bitmap = cogl_bitmap_new_from_file (data->load_filename,
|
||||||
|
&data->load_error);
|
||||||
|
|
||||||
|
/* Check again if we've been told to abort */
|
||||||
|
g_mutex_lock (data->mutex);
|
||||||
|
|
||||||
|
if (data->abort)
|
||||||
|
{
|
||||||
|
g_mutex_unlock (data->mutex);
|
||||||
|
|
||||||
|
clutter_texture_async_data_free (data);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
clutter_texture_async_load_complete (self, priv->load_error);
|
|
||||||
|
|
||||||
if (priv->load_error)
|
|
||||||
{
|
{
|
||||||
g_error_free (priv->load_error);
|
/* Make sure we give the image to GL in the main thread, where we
|
||||||
priv->load_error = NULL;
|
* hold the main Clutter lock. Once load_idle is non-NULL then the
|
||||||
|
* main thread is guaranteed not to set the abort flag. It can't
|
||||||
|
* set it while we're holding the mutex so we can safely start the
|
||||||
|
* idle handler now without the possibility of calling the
|
||||||
|
* callback after it is aborted */
|
||||||
|
data->load_idle =
|
||||||
|
clutter_threads_add_idle_full (G_PRIORITY_LOW, clutter_texture_thread_idle_func, data, NULL);
|
||||||
|
|
||||||
|
g_mutex_unlock (data->mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
return FALSE;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
static gpointer
|
|
||||||
clutter_texture_thread_func (gpointer data)
|
|
||||||
{
|
|
||||||
static GStaticMutex thread_load_mutex = G_STATIC_MUTEX_INIT;
|
|
||||||
ClutterTexture *self = data;
|
|
||||||
ClutterTexturePrivate *priv = self->priv;
|
|
||||||
|
|
||||||
/* we aquire the shared lock, only one thread is allowed to
|
|
||||||
* be loading at a time
|
|
||||||
*/
|
|
||||||
g_static_mutex_lock (&thread_load_mutex);
|
|
||||||
if (priv->abort)
|
|
||||||
{
|
|
||||||
g_static_mutex_unlock (&thread_load_mutex);
|
|
||||||
g_free (priv->load_filename);
|
|
||||||
priv->load_filename = NULL;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
/* Try loading with imaging backend */
|
|
||||||
priv->load_bitmap = cogl_bitmap_new_from_file (priv->load_filename,
|
|
||||||
&priv->load_error);
|
|
||||||
g_static_mutex_unlock (&thread_load_mutex);
|
|
||||||
g_free (priv->load_filename);
|
|
||||||
priv->load_filename = NULL;
|
|
||||||
|
|
||||||
/* make sure we load the image in the main thread, where we
|
|
||||||
* hold the main Clutter lock
|
|
||||||
*/
|
|
||||||
priv->load_idle =
|
|
||||||
clutter_threads_add_idle (clutter_texture_thread_cb, self);
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
clutter_texture_idle_func (gpointer data)
|
clutter_texture_idle_func (gpointer user_data)
|
||||||
{
|
{
|
||||||
ClutterTexture *self = data;
|
ClutterTextureAsyncData *data = user_data;
|
||||||
ClutterTexturePrivate *priv = self->priv;
|
GError *internal_error = NULL;
|
||||||
GError *internal_error;
|
|
||||||
|
|
||||||
internal_error = NULL;
|
data->load_bitmap = cogl_bitmap_new_from_file (data->load_filename,
|
||||||
priv->load_bitmap = cogl_bitmap_new_from_file (priv->load_filename,
|
|
||||||
&internal_error);
|
&internal_error);
|
||||||
|
|
||||||
clutter_texture_async_load_complete (self, internal_error);
|
clutter_texture_async_load_complete (data->texture, data->load_bitmap,
|
||||||
|
internal_error);
|
||||||
g_free (priv->load_filename);
|
|
||||||
priv->load_filename = NULL;
|
|
||||||
|
|
||||||
if (internal_error)
|
if (internal_error)
|
||||||
g_error_free (internal_error);
|
g_error_free (internal_error);
|
||||||
|
|
||||||
|
clutter_texture_async_data_free (data);
|
||||||
|
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* clutter_texture_async_load:
|
* clutter_texture_async_load:
|
||||||
* @self: a #ClutterTexture
|
* @self: a #ClutterTExture
|
||||||
|
* @filename: name of the file to load
|
||||||
* @error: return location for a #GError
|
* @error: return location for a #GError
|
||||||
*
|
*
|
||||||
* Starts an asynchronous load of the file name stored inside
|
* Starts an asynchronous load of the file name stored inside
|
||||||
* the load_filename private member.
|
* the load_filename member of @data.
|
||||||
*
|
*
|
||||||
* If threading is enabled we use a GThread to perform the actual
|
* If threading is enabled we use a GThread to perform the actual
|
||||||
* I/O; if threading is not enabled, we use an idle GSource.
|
* I/O; if threading is not enabled, we use an idle GSource.
|
||||||
@ -1697,21 +1801,30 @@ clutter_texture_idle_func (gpointer data)
|
|||||||
*/
|
*/
|
||||||
static gboolean
|
static gboolean
|
||||||
clutter_texture_async_load (ClutterTexture *self,
|
clutter_texture_async_load (ClutterTexture *self,
|
||||||
|
const gchar *filename,
|
||||||
GError **error)
|
GError **error)
|
||||||
{
|
{
|
||||||
ClutterTexturePrivate *priv = self->priv;
|
ClutterTexturePrivate *priv = self->priv;
|
||||||
|
ClutterTextureAsyncData *data;
|
||||||
gint width, height;
|
gint width, height;
|
||||||
gboolean res;
|
gboolean res;
|
||||||
|
|
||||||
g_assert (priv->load_filename != NULL);
|
|
||||||
|
|
||||||
/* ask the file for a size; if we cannot get the size then
|
/* ask the file for a size; if we cannot get the size then
|
||||||
* there's no point in even continuing the asynchronous
|
* there's no point in even continuing the asynchronous
|
||||||
* loading, so we just stop there
|
* loading, so we just stop there
|
||||||
*/
|
*/
|
||||||
res = cogl_bitmap_get_size_from_file (priv->load_filename,
|
|
||||||
&width,
|
if (priv->load_size_async)
|
||||||
&height);
|
{
|
||||||
|
res = TRUE;
|
||||||
|
width = 0;
|
||||||
|
height = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
res = cogl_bitmap_get_size_from_file (filename, &width, &height);
|
||||||
|
}
|
||||||
|
|
||||||
if (!res)
|
if (!res)
|
||||||
{
|
{
|
||||||
g_set_error (error, CLUTTER_TEXTURE_ERROR,
|
g_set_error (error, CLUTTER_TEXTURE_ERROR,
|
||||||
@ -1725,23 +1838,41 @@ clutter_texture_async_load (ClutterTexture *self,
|
|||||||
priv->height = height;
|
priv->height = height;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clutter_texture_async_load_cancel (self);
|
||||||
|
|
||||||
|
data = g_slice_new (ClutterTextureAsyncData);
|
||||||
|
|
||||||
|
data->abort = FALSE;
|
||||||
|
data->texture = self;
|
||||||
|
data->load_idle = 0;
|
||||||
|
data->load_filename = g_strdup (filename);
|
||||||
|
data->load_bitmap = NULL;
|
||||||
|
data->load_error = NULL;
|
||||||
|
|
||||||
|
priv->async_data = data;
|
||||||
|
|
||||||
if (g_thread_supported ())
|
if (g_thread_supported ())
|
||||||
{
|
{
|
||||||
priv->load_thread =
|
data->mutex = g_mutex_new ();
|
||||||
g_thread_create ((GThreadFunc) clutter_texture_thread_func,
|
|
||||||
self, TRUE,
|
|
||||||
error);
|
|
||||||
|
|
||||||
return priv->load_thread != NULL? TRUE : FALSE;
|
if (async_thread_pool == NULL)
|
||||||
|
/* This apparently can't fail if exclusive == FALSE */
|
||||||
|
async_thread_pool
|
||||||
|
= g_thread_pool_new (clutter_texture_thread_func,
|
||||||
|
NULL, 1, FALSE, NULL);
|
||||||
|
|
||||||
|
g_thread_pool_push (async_thread_pool, data, NULL);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
priv->load_idle =
|
data->mutex = NULL;
|
||||||
clutter_threads_add_idle (clutter_texture_idle_func, self);
|
|
||||||
|
data->load_idle
|
||||||
|
= clutter_threads_add_idle (clutter_texture_idle_func, data);
|
||||||
|
}
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* clutter_texture_set_from_file:
|
* clutter_texture_set_from_file:
|
||||||
@ -1754,9 +1885,10 @@ clutter_texture_async_load (ClutterTexture *self,
|
|||||||
*
|
*
|
||||||
* If #ClutterTexture:load-async is set to %TRUE, this function
|
* If #ClutterTexture:load-async is set to %TRUE, this function
|
||||||
* will return as soon as possible, and the actual image loading
|
* will return as soon as possible, and the actual image loading
|
||||||
* from disk will be performed asynchronously. #ClutterTexture::load-finished
|
* from disk will be performed asynchronously. #ClutterTexture::size-change
|
||||||
* will be emitted when the image has been loaded or if an error
|
* will be emitten when the size of the texture is available and
|
||||||
* occurred.
|
* #ClutterTexture::load-finished will be emitted when the image has been
|
||||||
|
* loaded or if an error occurred.
|
||||||
*
|
*
|
||||||
* Return value: %TRUE if the image was successfully loaded and set
|
* Return value: %TRUE if the image was successfully loaded and set
|
||||||
*
|
*
|
||||||
@ -1778,14 +1910,8 @@ clutter_texture_set_from_file (ClutterTexture *texture,
|
|||||||
|
|
||||||
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
|
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
|
||||||
|
|
||||||
if (priv->load_async)
|
if (priv->load_data_async)
|
||||||
{
|
return clutter_texture_async_load (texture, filename, error);
|
||||||
clutter_texture_async_load_cancel (texture);
|
|
||||||
|
|
||||||
priv->load_filename = g_strdup (filename);
|
|
||||||
|
|
||||||
return clutter_texture_async_load (texture, error);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!priv->no_slice)
|
if (!priv->no_slice)
|
||||||
max_waste = priv->max_tile_waste;
|
max_waste = priv->max_tile_waste;
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
|
|
||||||
UNIT_TESTS = \
|
UNIT_TESTS = \
|
||||||
test-textures.c \
|
test-textures.c \
|
||||||
|
test-texture-async.c \
|
||||||
test-events.c \
|
test-events.c \
|
||||||
test-offscreen.c \
|
test-offscreen.c \
|
||||||
test-scale.c \
|
test-scale.c \
|
||||||
|
106
tests/interactive/test-texture-async.c
Normal file
106
tests/interactive/test-texture-async.c
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
#include <stdlib.h>
|
||||||
|
#include <gmodule.h>
|
||||||
|
#include <clutter/clutter.h>
|
||||||
|
|
||||||
|
|
||||||
|
static void size_change_cb (ClutterTexture *texture,
|
||||||
|
gint width,
|
||||||
|
gint height,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
guint w,h;
|
||||||
|
clutter_actor_set_size (user_data, width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
const gchar *path = "redhand.png";
|
||||||
|
|
||||||
|
static gboolean task (gpointer foo)
|
||||||
|
{
|
||||||
|
ClutterTimeline *timeline;
|
||||||
|
ClutterAlpha *alpha;
|
||||||
|
ClutterBehaviour *depth_behavior;
|
||||||
|
ClutterActor *image[4];
|
||||||
|
ClutterActor *clone[4];
|
||||||
|
ClutterActor *stage;
|
||||||
|
gint i;
|
||||||
|
|
||||||
|
stage = clutter_stage_get_default ();
|
||||||
|
#if 0
|
||||||
|
for (i=0;i<4;i++)
|
||||||
|
image[i] = g_object_new (CLUTTER_TYPE_TEXTURE,
|
||||||
|
"filename", path,
|
||||||
|
"load-async", TRUE,
|
||||||
|
NULL);
|
||||||
|
#else
|
||||||
|
/*for (i=0;i<4;i++)*/
|
||||||
|
image[0] = g_object_new (CLUTTER_TYPE_TEXTURE,
|
||||||
|
"filename", path,
|
||||||
|
NULL);
|
||||||
|
image[1] = g_object_new (CLUTTER_TYPE_TEXTURE,
|
||||||
|
"filename", path,
|
||||||
|
"load-data-async", TRUE,
|
||||||
|
NULL);
|
||||||
|
image[2] = g_object_new (CLUTTER_TYPE_TEXTURE,
|
||||||
|
"filename", path,
|
||||||
|
"load-async", TRUE,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
for (i=0;i<3;i++)
|
||||||
|
{
|
||||||
|
clutter_container_add (CLUTTER_CONTAINER (stage), image[i], NULL);
|
||||||
|
}
|
||||||
|
for (i=0;i<3;i++)
|
||||||
|
{
|
||||||
|
clutter_actor_set_position (image[i], 50+i*100, 0+i*50);
|
||||||
|
clone[i]=clutter_clone_new (image[i]);
|
||||||
|
g_signal_connect (image[i], "size-change",
|
||||||
|
G_CALLBACK (size_change_cb), clone[i]);
|
||||||
|
clutter_container_add (CLUTTER_CONTAINER (stage), clone[i], NULL);
|
||||||
|
clutter_actor_set_position (clone[i], 50+i*100, 150+i*50+100);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i=0; i<3; i++)
|
||||||
|
{
|
||||||
|
timeline = clutter_timeline_new (60*5, 60);
|
||||||
|
alpha = clutter_alpha_new_full (timeline, CLUTTER_LINEAR);
|
||||||
|
depth_behavior = clutter_behaviour_depth_new (alpha, -2500, 0);
|
||||||
|
clutter_behaviour_apply (depth_behavior, image[i]);
|
||||||
|
clutter_timeline_start (timeline);
|
||||||
|
}
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
G_MODULE_EXPORT gint
|
||||||
|
test_texture_async_main (int argc, char *argv[])
|
||||||
|
{
|
||||||
|
ClutterActor *stage;
|
||||||
|
ClutterColor stage_color = { 0x12, 0x34, 0x56, 0xff };
|
||||||
|
GError *error;
|
||||||
|
|
||||||
|
clutter_init (&argc, &argv);
|
||||||
|
|
||||||
|
g_thread_init (NULL);
|
||||||
|
stage = clutter_stage_get_default ();
|
||||||
|
clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color);
|
||||||
|
|
||||||
|
clutter_actor_show (stage);
|
||||||
|
g_signal_connect (stage,
|
||||||
|
"button-press-event", G_CALLBACK (clutter_main_quit),
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
error = NULL;
|
||||||
|
|
||||||
|
path = argv[1]?argv[1]:"redhand.png";
|
||||||
|
|
||||||
|
|
||||||
|
g_timeout_add (500, task, NULL);
|
||||||
|
|
||||||
|
clutter_main ();
|
||||||
|
|
||||||
|
/*g_object_unref (depth_behavior);
|
||||||
|
g_object_unref (timeline);*/
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user