mirror of
https://github.com/brl/mutter.git
synced 2025-02-19 22:54:08 +00:00
texture: Simplify asynchronous loading code
The asynchronous loading code could do with some modernization. First of all, we should drop the internal GMutex held when manipulating the boolean flags: it's far too expensive for its role, and modern GLib provides us with bitlocks that are quite a lot faster. Then we should consolidate most of the implementation into something smaller and more manageable.
This commit is contained in:
parent
11c585c420
commit
56c7d9b0b3
@ -99,36 +99,37 @@ struct _ClutterTexturePrivate
|
|||||||
guint seen_create_pick_material_warning : 1;
|
guint seen_create_pick_material_warning : 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define ASYNC_STATE_LOCKED 1
|
||||||
|
#define ASYNC_STATE_CANCELLED 2
|
||||||
|
#define ASYNC_STATE_QUEUED 3
|
||||||
|
|
||||||
struct _ClutterTextureAsyncData
|
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 */
|
/* The texture for which the data is being loaded */
|
||||||
ClutterTexture *texture;
|
ClutterTexture *texture;
|
||||||
|
|
||||||
/* Source ID of the idle handler for loading. If this is zero then
|
gchar *load_filename;
|
||||||
the data is being loaded in a thread from the thread pool. Once
|
CoglHandle load_bitmap;
|
||||||
the thread is finished it will be converted to idle load handler
|
|
||||||
and load_idle will be nonzero. If load_idle is nonzero, and
|
|
||||||
upload_queued is FALSE 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;
|
|
||||||
|
|
||||||
/* Set when the texture is queued for GPU upload, used to determine
|
guint load_idle;
|
||||||
* what to do with the texture data when load_idle is zero.
|
|
||||||
*/
|
|
||||||
gboolean upload_queued;
|
|
||||||
|
|
||||||
gchar *load_filename;
|
GError *load_error;
|
||||||
CoglHandle load_bitmap;
|
|
||||||
GError *load_error;
|
gint state;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
clutter_texture_async_data_lock (ClutterTextureAsyncData *data)
|
||||||
|
{
|
||||||
|
g_bit_lock (&data->state, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
clutter_texture_async_data_unlock (ClutterTextureAsyncData *data)
|
||||||
|
{
|
||||||
|
g_bit_unlock (&data->state, 0);
|
||||||
|
}
|
||||||
|
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
PROP_0,
|
PROP_0,
|
||||||
@ -711,19 +712,14 @@ clutter_texture_async_data_free (ClutterTextureAsyncData *data)
|
|||||||
once it is known that the load thread has completed or from the
|
once it is known that the load thread has completed or from the
|
||||||
load thread/upload function itself if the abort flag is true (in
|
load thread/upload function itself if the abort flag is true (in
|
||||||
which case the main thread has disowned the data) */
|
which case the main thread has disowned the data) */
|
||||||
|
g_free (data->load_filename);
|
||||||
|
|
||||||
if (data->load_filename)
|
if (data->load_bitmap != NULL)
|
||||||
g_free (data->load_filename);
|
|
||||||
|
|
||||||
if (data->load_bitmap)
|
|
||||||
cogl_handle_unref (data->load_bitmap);
|
cogl_handle_unref (data->load_bitmap);
|
||||||
|
|
||||||
if (data->load_error)
|
if (data->load_error != NULL)
|
||||||
g_error_free (data->load_error);
|
g_error_free (data->load_error);
|
||||||
|
|
||||||
if (data->mutex)
|
|
||||||
g_mutex_free (data->mutex);
|
|
||||||
|
|
||||||
g_slice_free (ClutterTextureAsyncData, data);
|
g_slice_free (ClutterTextureAsyncData, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -739,40 +735,30 @@ clutter_texture_async_load_cancel (ClutterTexture *texture)
|
|||||||
{
|
{
|
||||||
ClutterTexturePrivate *priv = texture->priv;
|
ClutterTexturePrivate *priv = texture->priv;
|
||||||
|
|
||||||
if (priv->async_data)
|
if (priv->async_data != NULL)
|
||||||
{
|
{
|
||||||
GMutex *mutex = priv->async_data->mutex;
|
ClutterTextureAsyncData *async_data = priv->async_data;
|
||||||
|
|
||||||
/* The mutex will only be NULL if the no thread was used for
|
priv->async_data = NULL;
|
||||||
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
|
if (async_data->load_idle != 0)
|
||||||
the idle handler and destroy the load data immediately */
|
|
||||||
if (priv->async_data->load_idle)
|
|
||||||
{
|
{
|
||||||
g_source_remove (priv->async_data->load_idle);
|
g_source_remove (async_data->load_idle);
|
||||||
priv->async_data->load_idle = 0;
|
async_data->load_idle = 0;
|
||||||
|
|
||||||
if (mutex)
|
clutter_texture_async_data_free (async_data);
|
||||||
g_mutex_unlock (mutex);
|
|
||||||
|
|
||||||
clutter_texture_async_data_free (priv->async_data);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* Otherwise we need to tell the thread to abort and disown
|
clutter_texture_async_data_lock (async_data);
|
||||||
the data, if the data has been loaded and decoded the data
|
|
||||||
is now waiting for a master clock iteration to be repainted */
|
|
||||||
priv->async_data->abort = TRUE;
|
|
||||||
|
|
||||||
if (mutex)
|
CLUTTER_NOTE (TEXTURE, "[async] cancelling operation for '%s'",
|
||||||
g_mutex_unlock (mutex);
|
async_data->load_filename);
|
||||||
|
|
||||||
|
async_data->state |= ASYNC_STATE_CANCELLED;
|
||||||
|
|
||||||
|
clutter_texture_async_data_unlock (async_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
priv->async_data = NULL;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1736,8 +1722,8 @@ clutter_texture_async_load_complete (ClutterTexture *self,
|
|||||||
const GError *error)
|
const GError *error)
|
||||||
{
|
{
|
||||||
ClutterTexturePrivate *priv = self->priv;
|
ClutterTexturePrivate *priv = self->priv;
|
||||||
CoglHandle handle;
|
|
||||||
CoglTextureFlags flags = COGL_TEXTURE_NONE;
|
CoglTextureFlags flags = COGL_TEXTURE_NONE;
|
||||||
|
CoglHandle handle;
|
||||||
|
|
||||||
priv->async_data = NULL;
|
priv->async_data = NULL;
|
||||||
|
|
||||||
@ -1766,30 +1752,6 @@ clutter_texture_async_load_complete (ClutterTexture *self,
|
|||||||
clutter_actor_queue_relayout (CLUTTER_ACTOR (self));
|
clutter_actor_queue_relayout (CLUTTER_ACTOR (self));
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
|
||||||
clutter_texture_thread_idle_func (gpointer user_data)
|
|
||||||
{
|
|
||||||
ClutterTextureAsyncData *data = user_data;
|
|
||||||
|
|
||||||
/* Grab the mutex so we can be sure the thread has unlocked it
|
|
||||||
before we destroy it */
|
|
||||||
g_mutex_lock (data->mutex);
|
|
||||||
if (data->abort)
|
|
||||||
{
|
|
||||||
g_mutex_unlock (data->mutex);
|
|
||||||
clutter_texture_async_data_free (data);
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
g_mutex_unlock (data->mutex);
|
|
||||||
|
|
||||||
clutter_texture_async_load_complete (data->texture, data->load_bitmap,
|
|
||||||
data->load_error);
|
|
||||||
|
|
||||||
clutter_texture_async_data_free (data);
|
|
||||||
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
texture_repaint_upload_func (gpointer user_data)
|
texture_repaint_upload_func (gpointer user_data)
|
||||||
{
|
{
|
||||||
@ -1797,7 +1759,7 @@ texture_repaint_upload_func (gpointer user_data)
|
|||||||
|
|
||||||
g_mutex_lock (&upload_list_mutex);
|
g_mutex_lock (&upload_list_mutex);
|
||||||
|
|
||||||
if (upload_list)
|
if (upload_list != NULL)
|
||||||
{
|
{
|
||||||
start_time = clutter_get_timestamp ();
|
start_time = clutter_get_timestamp ();
|
||||||
|
|
||||||
@ -1806,16 +1768,32 @@ texture_repaint_upload_func (gpointer user_data)
|
|||||||
*/
|
*/
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
ClutterTextureAsyncData *data = upload_list->data;
|
ClutterTextureAsyncData *async_data = upload_list->data;
|
||||||
|
|
||||||
clutter_texture_thread_idle_func (data);
|
clutter_texture_async_data_lock (async_data);
|
||||||
|
|
||||||
upload_list = g_list_remove (upload_list, data);
|
if (async_data->state & ASYNC_STATE_QUEUED)
|
||||||
|
{
|
||||||
|
CLUTTER_NOTE (TEXTURE, "[async] operation complete for '%s'",
|
||||||
|
async_data->load_filename);
|
||||||
|
|
||||||
|
clutter_texture_async_load_complete (async_data->texture,
|
||||||
|
async_data->load_bitmap,
|
||||||
|
async_data->load_error);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
CLUTTER_NOTE (TEXTURE, "[async] operation cancelled for '%s'",
|
||||||
|
async_data->load_filename);
|
||||||
|
|
||||||
|
clutter_texture_async_data_unlock (async_data);
|
||||||
|
|
||||||
|
upload_list = g_list_remove (upload_list, async_data);
|
||||||
|
clutter_texture_async_data_free (async_data);
|
||||||
}
|
}
|
||||||
while (upload_list && clutter_get_timestamp () < start_time + 5 * 1000);
|
while (upload_list && clutter_get_timestamp () < start_time + 5 * 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (upload_list)
|
if (upload_list != NULL)
|
||||||
{
|
{
|
||||||
ClutterMasterClock *master_clock;
|
ClutterMasterClock *master_clock;
|
||||||
|
|
||||||
@ -1829,47 +1807,23 @@ texture_repaint_upload_func (gpointer user_data)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
clutter_texture_thread_func (gpointer user_data, gpointer pool_data)
|
clutter_texture_thread_load (gpointer user_data,
|
||||||
|
gpointer pool_data)
|
||||||
{
|
{
|
||||||
ClutterTextureAsyncData *data = user_data;
|
ClutterTextureAsyncData *async_data = user_data;
|
||||||
gboolean should_abort;
|
ClutterMasterClock *master_clock = _clutter_master_clock_get_default ();
|
||||||
|
|
||||||
/* Make sure we haven't been told to abort before the thread had a
|
clutter_texture_async_data_lock (async_data);
|
||||||
chance to run */
|
|
||||||
g_mutex_lock (data->mutex);
|
|
||||||
should_abort = data->abort;
|
|
||||||
g_mutex_unlock (data->mutex);
|
|
||||||
|
|
||||||
if (should_abort)
|
if (~async_data->state & ASYNC_STATE_CANCELLED)
|
||||||
{
|
{
|
||||||
/* If we've been told to abort then main thread has disowned the
|
CLUTTER_NOTE (TEXTURE, "[async] loading bitmap from file '%s'",
|
||||||
async data and we need to free it */
|
async_data->load_filename);
|
||||||
clutter_texture_async_data_free (data);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
data->load_bitmap = cogl_bitmap_new_from_file (data->load_filename,
|
async_data->load_bitmap =
|
||||||
&data->load_error);
|
cogl_bitmap_new_from_file (async_data->load_filename,
|
||||||
|
&async_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
|
|
||||||
{
|
|
||||||
ClutterMasterClock *master_clock = _clutter_master_clock_get_default ();
|
|
||||||
|
|
||||||
/* Make sure we give the image to GL in the main thread, where we
|
|
||||||
* 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 */
|
|
||||||
g_mutex_lock (&upload_list_mutex);
|
g_mutex_lock (&upload_list_mutex);
|
||||||
|
|
||||||
if (repaint_upload_func == 0)
|
if (repaint_upload_func == 0)
|
||||||
@ -1879,33 +1833,40 @@ clutter_texture_thread_func (gpointer user_data, gpointer pool_data)
|
|||||||
NULL, NULL);
|
NULL, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
upload_list = g_list_append (upload_list, data);
|
upload_list = g_list_append (upload_list, async_data);
|
||||||
data->upload_queued = TRUE;
|
async_data->state |= ASYNC_STATE_QUEUED;
|
||||||
|
|
||||||
|
CLUTTER_NOTE (TEXTURE, "[async] operation queued");
|
||||||
|
|
||||||
g_mutex_unlock (&upload_list_mutex);
|
g_mutex_unlock (&upload_list_mutex);
|
||||||
|
|
||||||
g_mutex_unlock (data->mutex);
|
|
||||||
|
|
||||||
_clutter_master_clock_ensure_next_iteration (master_clock);
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
clutter_texture_async_data_unlock (async_data);
|
||||||
|
clutter_texture_async_data_free (async_data);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
clutter_texture_async_data_unlock (async_data);
|
||||||
|
|
||||||
|
_clutter_master_clock_ensure_next_iteration (master_clock);
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
clutter_texture_idle_func (gpointer user_data)
|
clutter_texture_idle_load (gpointer data)
|
||||||
{
|
{
|
||||||
ClutterTextureAsyncData *data = user_data;
|
ClutterTextureAsyncData *async_data = data;
|
||||||
GError *internal_error = NULL;
|
|
||||||
|
|
||||||
data->load_bitmap = cogl_bitmap_new_from_file (data->load_filename,
|
async_data->load_bitmap =
|
||||||
&internal_error);
|
cogl_bitmap_new_from_file (async_data->load_filename,
|
||||||
|
&async_data->load_error);
|
||||||
|
|
||||||
clutter_texture_async_load_complete (data->texture, data->load_bitmap,
|
clutter_texture_async_load_complete (async_data->texture,
|
||||||
internal_error);
|
async_data->load_bitmap,
|
||||||
|
async_data->load_error);
|
||||||
|
|
||||||
if (internal_error)
|
clutter_texture_async_data_free (async_data);
|
||||||
g_error_free (internal_error);
|
|
||||||
|
|
||||||
clutter_texture_async_data_free (data);
|
|
||||||
|
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
@ -1956,9 +1917,7 @@ clutter_texture_async_load (ClutterTexture *self,
|
|||||||
height = 0;
|
height = 0;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
res = cogl_bitmap_get_size_from_file (filename, &width, &height);
|
||||||
res = cogl_bitmap_get_size_from_file (filename, &width, &height);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!res)
|
if (!res)
|
||||||
{
|
{
|
||||||
@ -1975,35 +1934,34 @@ clutter_texture_async_load (ClutterTexture *self,
|
|||||||
|
|
||||||
clutter_texture_async_load_cancel (self);
|
clutter_texture_async_load_cancel (self);
|
||||||
|
|
||||||
data = g_slice_new (ClutterTextureAsyncData);
|
data = g_slice_new0 (ClutterTextureAsyncData);
|
||||||
|
|
||||||
data->abort = FALSE;
|
|
||||||
data->texture = self;
|
data->texture = self;
|
||||||
data->load_idle = 0;
|
|
||||||
data->load_filename = g_strdup (filename);
|
data->load_filename = g_strdup (filename);
|
||||||
data->load_bitmap = NULL;
|
|
||||||
data->load_error = NULL;
|
|
||||||
|
|
||||||
priv->async_data = data;
|
priv->async_data = data;
|
||||||
|
|
||||||
if (g_thread_supported ())
|
if (g_thread_supported ())
|
||||||
{
|
{
|
||||||
data->mutex = g_mutex_new ();
|
if (G_UNLIKELY (async_thread_pool == NULL))
|
||||||
|
{
|
||||||
if (async_thread_pool == NULL)
|
/* This apparently can't fail if exclusive == FALSE */
|
||||||
/* This apparently can't fail if exclusive == FALSE */
|
async_thread_pool =
|
||||||
async_thread_pool
|
g_thread_pool_new (clutter_texture_thread_load, NULL,
|
||||||
= g_thread_pool_new (clutter_texture_thread_func,
|
1,
|
||||||
NULL, 1, FALSE, NULL);
|
FALSE,
|
||||||
|
NULL);
|
||||||
|
}
|
||||||
|
|
||||||
g_thread_pool_push (async_thread_pool, data, NULL);
|
g_thread_pool_push (async_thread_pool, data, NULL);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
data->mutex = NULL;
|
data->load_idle =
|
||||||
|
clutter_threads_add_idle_full (G_PRIORITY_DEFAULT_IDLE,
|
||||||
data->load_idle
|
clutter_texture_idle_load,
|
||||||
= clutter_threads_add_idle (clutter_texture_idle_func, data);
|
data,
|
||||||
|
NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
@ -2921,6 +2879,8 @@ clutter_texture_set_load_async (ClutterTexture *texture,
|
|||||||
|
|
||||||
priv = texture->priv;
|
priv = texture->priv;
|
||||||
|
|
||||||
|
load_async = !!load_async;
|
||||||
|
|
||||||
if (priv->load_async_set != load_async)
|
if (priv->load_async_set != load_async)
|
||||||
{
|
{
|
||||||
priv->load_data_async = load_async;
|
priv->load_data_async = load_async;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user