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:
parent
f5f22b3935
commit
ec92bfba14
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user