Compress multiple load requests

Before, if the texture cache received a request to load say
the themed icon for an application multiple times (as could happen
since we have multiple application displays), it would often create
a thread for each one and in fact, load the pixbuf multiple times.

Avoid this by keeping track of outstanding requests.

https://bugzilla.gnome.org/show_bug.cgi?id=596121
This commit is contained in:
Colin Walters 2009-09-23 18:07:19 -04:00
parent f5f22b3935
commit ec92bfba14

View File

@ -20,7 +20,13 @@ typedef struct
struct _ShellTextureCachePrivate struct _ShellTextureCachePrivate
{ {
/* Things that were loaded with a cache policy != NONE */
GHashTable *keyed_cache; /* CacheKey -> CoglTexture* */ GHashTable *keyed_cache; /* CacheKey -> CoglTexture* */
/* Presently this is used to de-duplicate requests for GIcons,
* it could in theory be extended to async URL loading and other
* cases too.
*/
GHashTable *outstanding_requests; /* CacheKey -> AsyncTextureLoadData * */
GnomeDesktopThumbnailFactory *thumbnails; GnomeDesktopThumbnailFactory *thumbnails;
}; };
@ -130,6 +136,8 @@ shell_texture_cache_init (ShellTextureCache *self)
self->priv = g_new0 (ShellTextureCachePrivate, 1); self->priv = g_new0 (ShellTextureCachePrivate, 1);
self->priv->keyed_cache = g_hash_table_new_full (cache_key_hash, cache_key_equal, self->priv->keyed_cache = g_hash_table_new_full (cache_key_hash, cache_key_equal,
cache_key_destroy, cogl_handle_unref); cache_key_destroy, cogl_handle_unref);
self->priv->outstanding_requests = g_hash_table_new_full (cache_key_hash, cache_key_equal,
cache_key_destroy, NULL);
self->priv->thumbnails = gnome_desktop_thumbnail_factory_new (GNOME_DESKTOP_THUMBNAIL_SIZE_NORMAL); self->priv->thumbnails = gnome_desktop_thumbnail_factory_new (GNOME_DESKTOP_THUMBNAIL_SIZE_NORMAL);
} }
@ -647,7 +655,7 @@ typedef struct {
GtkIconInfo *icon_info; GtkIconInfo *icon_info;
guint width; guint width;
guint height; guint height;
ClutterTexture *texture; GSList *textures;
} AsyncTextureLoadData; } AsyncTextureLoadData;
static CoglHandle static CoglHandle
@ -706,15 +714,31 @@ on_pixbuf_loaded (GObject *source,
GAsyncResult *result, GAsyncResult *result,
gpointer user_data) gpointer user_data)
{ {
GSList *iter;
ShellTextureCache *cache; ShellTextureCache *cache;
AsyncTextureLoadData *data; AsyncTextureLoadData *data;
GdkPixbuf *pixbuf; GdkPixbuf *pixbuf;
GError *error = NULL; GError *error = NULL;
CoglHandle texdata = NULL; CoglHandle texdata = NULL;
CacheKey *key; CacheKey key;
data = user_data; data = user_data;
cache = SHELL_TEXTURE_CACHE (source); cache = SHELL_TEXTURE_CACHE (source);
memset (&key, 0, sizeof(key));
key.policy = data->policy;
if (data->icon)
key.icon = data->icon;
else if (data->recent_info && data->thumbnail)
key.thumbnail_uri = (char*)gtk_recent_info_get_uri (data->recent_info);
else if (data->thumbnail)
key.thumbnail_uri = (char*)data->uri;
else if (data->uri)
key.uri = data->uri;
key.size = data->width;
g_hash_table_remove (cache->priv->outstanding_requests, &key);
pixbuf = load_pixbuf_async_finish (cache, result, &error); pixbuf = load_pixbuf_async_finish (cache, result, &error);
if (pixbuf == NULL) if (pixbuf == NULL)
pixbuf = load_pixbuf_fallback(data); pixbuf = load_pixbuf_fallback(data);
@ -729,30 +753,20 @@ on_pixbuf_loaded (GObject *source,
{ {
gpointer orig_key, value; gpointer orig_key, value;
key = g_new0 (CacheKey, 1); if (!g_hash_table_lookup_extended (cache->priv->keyed_cache, &key,
key->policy = data->policy;
if (data->icon)
key->icon = g_object_ref (data->icon);
else if (data->recent_info && data->thumbnail)
key->thumbnail_uri = g_strdup (gtk_recent_info_get_uri (data->recent_info));
else if (data->thumbnail)
key->thumbnail_uri = g_strdup (data->uri);
else if (data->uri)
key->uri = g_strdup (data->uri);
key->size = data->width;
if (!g_hash_table_lookup_extended (cache->priv->keyed_cache, key,
&orig_key, &value)) &orig_key, &value))
{ {
cogl_handle_ref (texdata); cogl_handle_ref (texdata);
g_hash_table_insert (cache->priv->keyed_cache, key, g_hash_table_insert (cache->priv->keyed_cache, cache_key_dup (&key),
texdata); texdata);
} }
else
cache_key_destroy (key);
} }
set_texture_cogl_texture (data->texture, texdata); for (iter = data->textures; iter; iter = iter->next)
{
ClutterTexture *texture = iter->data;
set_texture_cogl_texture (texture, texdata);
}
out: out:
if (texdata) if (texdata)
@ -772,7 +786,11 @@ out:
/* Alternatively we could weakref and just do nothing if the texture /* Alternatively we could weakref and just do nothing if the texture
is destroyed */ is destroyed */
g_object_unref (data->texture); for (iter = data->textures; iter; iter = iter->next)
{
ClutterTexture *texture = iter->data;
g_object_unref (texture);
}
g_clear_error (&error); g_clear_error (&error);
g_free (data); g_free (data);
@ -880,6 +898,59 @@ shell_texture_cache_bind_pixbuf_property (ShellTextureCache *cache,
return CLUTTER_ACTOR(texture); return CLUTTER_ACTOR(texture);
} }
/**
* create_texture_and_ensure_request:
* @cache:
* @key: A filled in #CacheKey
* @request: (out): If no request is outstanding, one will be created and returned here
* @texture: (out): A new texture, also added to the request
*
* Check for any outstanding load for the data represented by @key. If there
* is already a request pending, append it to that request to avoid loading
* the data multiple times.
*
* Returns: %TRUE iff there is already a request pending
*/
static gboolean
create_texture_and_ensure_request (ShellTextureCache *cache,
CacheKey *key,
AsyncTextureLoadData **request,
ClutterActor **texture)
{
CoglHandle texdata;
AsyncTextureLoadData *pending;
gboolean had_pending;
*texture = (ClutterActor *) create_default_texture (cache);
clutter_actor_set_size (*texture, key->size, key->size);
texdata = g_hash_table_lookup (cache->priv->keyed_cache, key);
if (texdata != NULL)
{
/* We had this cached already, just set the texture and we're done. */
set_texture_cogl_texture (CLUTTER_TEXTURE (*texture), texdata);
return TRUE;
}
pending = g_hash_table_lookup (cache->priv->outstanding_requests, key);
had_pending = pending != NULL;
if (pending == NULL)
{
/* Not cached and no pending request, create it */
*request = g_new0 (AsyncTextureLoadData, 1);
g_hash_table_insert (cache->priv->outstanding_requests, cache_key_dup (key), *request);
}
else
*request = pending;
/* Regardless of whether there was a pending request, prepend our texture here. */
(*request)->textures = g_slist_prepend ((*request)->textures, g_object_ref (*texture));
return had_pending;
}
/** /**
* shell_texture_cache_load_gicon: * shell_texture_cache_load_gicon:
* *
@ -893,44 +964,43 @@ shell_texture_cache_load_gicon (ShellTextureCache *cache,
GIcon *icon, GIcon *icon,
gint size) gint size)
{ {
ClutterTexture *texture; AsyncTextureLoadData *request;
CoglHandle texdata; ClutterActor *texture;
CacheKey key; CacheKey key;
GtkIconTheme *theme;
texture = create_default_texture (cache); GtkIconInfo *info;
clutter_actor_set_size (CLUTTER_ACTOR (texture), size, size);
memset (&key, 0, sizeof(key)); memset (&key, 0, sizeof(key));
key.icon = icon; key.icon = icon;
key.size = size; key.size = size;
texdata = g_hash_table_lookup (cache->priv->keyed_cache, &key);
if (texdata == NULL) if (create_texture_and_ensure_request (cache, &key, &request, &texture))
return texture;
/* Do theme lookups in the main thread to avoid thread-unsafety */
theme = gtk_icon_theme_get_default ();
info = gtk_icon_theme_lookup_by_gicon (theme, icon, size, GTK_ICON_LOOKUP_USE_BUILTIN);
if (info != NULL)
{ {
GtkIconTheme *theme; /* hardcoded here for now; we should actually blow this away on
GtkIconInfo *info; * icon theme changes probably */
request->policy = SHELL_TEXTURE_CACHE_POLICY_FOREVER;
request->icon = g_object_ref (icon);
request->icon_info = info;
request->width = request->height = size;
/* Do theme lookups in the main thread to avoid thread-unsafety */ load_icon_pixbuf_async (cache, icon, info, size, NULL, on_pixbuf_loaded, request);
theme = gtk_icon_theme_get_default ();
info = gtk_icon_theme_lookup_by_gicon (theme, icon, size, GTK_ICON_LOOKUP_USE_BUILTIN);
if (info != NULL)
{
AsyncTextureLoadData *data;
data = g_new0 (AsyncTextureLoadData, 1);
/* hardcoded here for now; we should actually blow this away on
* icon theme changes probably */
data->policy = SHELL_TEXTURE_CACHE_POLICY_FOREVER;
data->icon = g_object_ref (icon);
data->icon_info = info;
data->texture = g_object_ref (texture);
data->width = data->height = size;
load_icon_pixbuf_async (cache, icon, info, size, NULL, on_pixbuf_loaded, data);
}
} }
else else
{ {
set_texture_cogl_texture (texture, texdata); /* Blah; we failed to find the icon, but we've added our texture to the outstanding
* requests. In that case, just undo what create_texture_lookup_status did.
*/
g_slist_foreach (request->textures, (GFunc) g_object_unref, NULL);
g_slist_free (request->textures);
g_free (request);
g_hash_table_remove (cache->priv->outstanding_requests, &key);
} }
return CLUTTER_ACTOR (texture); return CLUTTER_ACTOR (texture);
@ -991,7 +1061,7 @@ shell_texture_cache_load_uri_async (ShellTextureCache *cache,
data->uri = g_strdup (uri); data->uri = g_strdup (uri);
data->width = available_width; data->width = available_width;
data->height = available_height; data->height = available_height;
data->texture = g_object_ref (texture); data->textures = g_slist_prepend (data->textures, g_object_ref (texture));
load_uri_pixbuf_async (cache, uri, available_width, available_height, NULL, on_pixbuf_loaded, data); load_uri_pixbuf_async (cache, uri, available_width, available_height, NULL, on_pixbuf_loaded, data);
return CLUTTER_ACTOR (texture); return CLUTTER_ACTOR (texture);
@ -1114,7 +1184,7 @@ shell_texture_cache_load_thumbnail (ShellTextureCache *cache,
data->thumbnail = TRUE; data->thumbnail = TRUE;
data->width = size; data->width = size;
data->height = size; data->height = size;
data->texture = g_object_ref (texture); data->textures = g_slist_prepend (data->textures, g_object_ref (texture));
load_thumbnail_async (cache, uri, mimetype, size, NULL, on_pixbuf_loaded, data); load_thumbnail_async (cache, uri, mimetype, size, NULL, on_pixbuf_loaded, data);
} }
else else
@ -1190,7 +1260,7 @@ shell_texture_cache_load_recent_thumbnail (ShellTextureCache *cache,
data->recent_info = gtk_recent_info_ref (info); data->recent_info = gtk_recent_info_ref (info);
data->width = size; data->width = size;
data->height = size; data->height = size;
data->texture = g_object_ref (texture); data->textures = g_slist_prepend (data->textures, g_object_ref (texture));
load_recent_thumbnail_async (cache, info, size, NULL, on_pixbuf_loaded, data); load_recent_thumbnail_async (cache, info, size, NULL, on_pixbuf_loaded, data);
} }
else else