Merge lp:~canonical-dx-team/unity/batch-icon-loading into lp:unity

Proposed by Neil J. Patel
Status: Merged
Merged at revision: 482
Proposed branch: lp:~canonical-dx-team/unity/batch-icon-loading
Merge into: lp:unity
Prerequisite: lp:~unity-team/unity/fix-places-memleaks
Diff against target: 324 lines (+207/-11)
3 files modified
unity-private/launcher/quicklist-menu-item.vala (+0/-2)
unity-private/places/places-default-renderer-group.vala (+3/-0)
unity/unity-pixbuf-cache.vala (+204/-9)
To merge this branch: bzr merge lp:~canonical-dx-team/unity/batch-icon-loading
Reviewer Review Type Date Requested Status
Gord Allott Pending
Review via email: mp+34506@code.launchpad.net

Description of the change

This branch changes icon loading to basically do it in batch fashion instead of tons of idles which like to starve the main loop.

Basically we queue all new requests and have a callback on a timeout to process a few of the requests at a time. We also allow the renderers to explicitly call the iteration should they require some there-and-there processing.

This is really for the first time you hit most of the places, after that the cache is warm and it's much, much quicker (especially now that we're not leaking textures!).

To post a comment you must log in.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'unity-private/launcher/quicklist-menu-item.vala'
2--- unity-private/launcher/quicklist-menu-item.vala 2010-09-03 00:03:43 +0000
3+++ unity-private/launcher/quicklist-menu-item.vala 2010-09-03 00:03:43 +0000
4@@ -149,8 +149,6 @@
5 this.item_background.add_layer (normal_layer);
6 this.item_background.add_layer (selected_layer);
7
8- debug ("\n\n\n %u %u\n\n\n\n", normal_layer.ref_count, selected_layer.ref_count);
9-
10 this.item_background.get_layer(0).set_enabled (true);
11 this.item_background.get_layer(1).set_enabled (false);
12 if (this.get_stage () is Clutter.Stage)
13
14=== modified file 'unity-private/places/places-default-renderer-group.vala'
15--- unity-private/places/places-default-renderer-group.vala 2010-09-03 00:03:43 +0000
16+++ unity-private/places/places-default-renderer-group.vala 2010-09-03 00:03:43 +0000
17@@ -380,6 +380,9 @@
18 more_results_button.count = 0;
19 }
20 }
21+
22+ if (n_results == 1)
23+ PixbufCache.get_default ().load_iteration ();
24 }
25
26 private void on_n_cols_changed ()
27
28=== modified file 'unity/unity-pixbuf-cache.vala'
29--- unity/unity-pixbuf-cache.vala 2010-07-06 16:13:23 +0000
30+++ unity/unity-pixbuf-cache.vala 2010-09-03 00:03:43 +0000
31@@ -23,6 +23,25 @@
32
33 namespace Unity
34 {
35+ public class PixbufCacheTask
36+ {
37+ public string data;
38+ public unowned Ctk.Image image;
39+ public int size;
40+ public string key;
41+ public PixbufRequestType type;
42+
43+ public PixbufCacheTask ()
44+ {
45+ }
46+ }
47+
48+ public enum PixbufRequestType
49+ {
50+ ICON_NAME,
51+ GICON_STRING
52+ }
53+
54 /* This is what we use for lookups, namely the id of the icon and it's size.
55 * Currently this means we just use one HashMap for all the pixbufs, it
56 * might be useful to have one HashMap per size in the future...
57@@ -43,6 +62,10 @@
58
59 public uint size { get { return cache.size; } }
60
61+ private PriorityQueue<PixbufCacheTask> queue;
62+
63+ private uint queue_timeout = 0;
64+
65 /*
66 * Construction
67 */
68@@ -60,6 +83,8 @@
69
70 construct
71 {
72+ queue = new PriorityQueue<PixbufCacheTask> ();
73+
74 theme = Gtk.IconTheme.get_default ();
75 cache = new HashMap<string, Gdk.Pixbuf> ();
76 }
77@@ -102,6 +127,31 @@
78 cache.clear ();
79 }
80
81+ public bool load_iteration ()
82+ {
83+ int i = 0;
84+
85+ while (queue.size > 0 && i < 10)
86+ {
87+ var task = queue.poll ();
88+
89+ if (task.image is Ctk.Image)
90+ {
91+ if (task.type == PixbufRequestType.ICON_NAME)
92+ set_image_from_icon_name_real (task.image, task.data, task.size);
93+ else if (task.type == PixbufRequestType.GICON_STRING)
94+ set_image_from_gicon_string_real (task.image, task.data, task.size);
95+ }
96+
97+ i++;
98+ }
99+
100+ if (queue.size == 0)
101+ queue_timeout = 0;
102+
103+ return queue.size != 0;
104+ }
105+
106 public async void set_image_from_icon_name (Ctk.Image image,
107 string icon_name,
108 int size)
109@@ -115,13 +165,43 @@
110 return;
111 }
112
113- Idle.add (set_image_from_icon_name.callback);
114+ var task = new PixbufCacheTask ();
115+ task.data = icon_name;
116+ task.image = image;
117+ task.size = size;
118+ task.type = PixbufRequestType.ICON_NAME;
119+
120+ queue.add (task);
121+
122+ if (queue_timeout == 0)
123+ queue_timeout = Idle.add (load_iteration);
124+ }
125+
126+ public async void set_image_from_icon_name_real (Ctk.Image image,
127+ string icon_name,
128+ int size)
129+ {
130+ var key = hash_template.printf (icon_name, size);
131+ Pixbuf? ret = cache[key];
132+
133+ if (ret is Pixbuf)
134+ {
135+ image.set_from_pixbuf (ret);
136+ return;
137+ }
138+
139+ Idle.add (set_image_from_icon_name_real.callback);
140 yield;
141-
142+
143 if (ret == null)
144 {
145 try {
146- ret = theme.load_icon (icon_name, size, 0);
147+ var info = theme.lookup_icon (icon_name, size, 0);
148+ if (info != null)
149+ {
150+ var filename = info.get_filename ();
151+ ret = yield load_from_filepath (filename, size, image, key);
152+ }
153
154 if (ret is Pixbuf)
155 {
156@@ -140,6 +220,31 @@
157 }
158
159 public async void set_image_from_gicon_string (Ctk.Image image,
160+ string data,
161+ int size)
162+ {
163+ var key = hash_template.printf (data, size);
164+ Pixbuf? ret = cache[key];
165+
166+ if (ret is Pixbuf)
167+ {
168+ image.set_from_pixbuf (ret);
169+ return;
170+ }
171+
172+ var task = new PixbufCacheTask ();
173+ task.image = image;
174+ task.data = data;
175+ task.size = size;
176+ task.type = PixbufRequestType.GICON_STRING;
177+
178+ queue.add (task);
179+
180+ if (queue_timeout == 0)
181+ queue_timeout = Idle.add (load_iteration);
182+ }
183+
184+ public async void set_image_from_gicon_string_real (Ctk.Image image,
185 string gicon_as_string,
186 int size)
187 {
188@@ -152,7 +257,7 @@
189 return;
190 }
191
192- Idle.add (set_image_from_gicon.callback);
193+ Idle.add (set_image_from_gicon_string_real.callback);
194 yield;
195
196 if (ret == null)
197@@ -160,7 +265,7 @@
198 if (gicon_as_string[0] == '/')
199 {
200 try {
201- ret = new Gdk.Pixbuf.from_file (gicon_as_string);
202+ ret = yield load_from_filepath (gicon_as_string, size, image, key);
203 } catch (Error err) {
204 message (@"Unable to load $gicon_as_string as file: %s",
205 err.message);
206@@ -173,7 +278,11 @@
207 unowned GLib.Icon icon = GLib.Icon.new_for_string (gicon_as_string);
208 var info = theme.lookup_by_gicon (icon, size, 0);
209 if (info != null)
210- ret = info.load_icon ();
211+ {
212+ var filename = info.get_filename ();
213+
214+ ret = yield load_from_filepath (filename, size, image, key);
215+ }
216
217 if (ret == null)
218 {
219@@ -188,7 +297,12 @@
220 || gicon_as_string.has_suffix (".jpg"))
221 {
222 string real_name = gicon_as_string[0:gicon_as_string.length-4];
223- ret = theme.load_icon (real_name, size, 0);
224+ info = theme.lookup_icon (real_name, size, 0);
225+ if (info != null)
226+ {
227+ var fname = info.get_filename ();
228+ ret = yield load_from_filepath (fname, size, image, key);
229+ }
230 }
231 }
232
233@@ -213,8 +327,89 @@
234 GLib.Icon icon,
235 int size)
236 {
237- yield set_image_from_gicon_string (image, icon.to_string (), size);
238- }
239+ set_image_from_gicon_string (image, icon.to_string (), size);
240+ }
241+
242+ public async Gdk.Pixbuf? load_from_filepath (string filename, int size, Ctk.Image? image=null, string key)
243+ {
244+
245+ if (filename != null)
246+ {
247+ File datafile = File.new_for_path (filename);
248+ try
249+ {
250+ var stream = yield datafile.read_async (Priority.DEFAULT, null);
251+
252+ if (stream is FileInputStream)
253+ {
254+ uchar[] buf = new uchar[16];
255+ void *data;
256+ size_t data_size;
257+ yield IO.read_stream_async (stream, buf, 16, Priority.DEFAULT,
258+
259+ null, out data, out data_size);
260+ string sdata = ((string)data).ndup(data_size);
261+
262+ var loader = new Gdk.PixbufLoader ();
263+ loader.write ((uchar[])data, data_size);
264+ loader.close ();
265+
266+ return loader.get_pixbuf ();
267+ }
268+ }
269+ catch (Error ee)
270+ {
271+ warning ("Unable to load image file '%s': %s", filename, ee.message);
272+ }
273+ }
274+
275+ return null;
276+ }
277+
278+ /*
279+ private async void load_from_file_async (Ctk.Image i,
280+ string f,
281+ int s,
282+ string k)
283+ {
284+ unowned Ctk.Image image = i;
285+ string filename = f;
286+ int size = s;
287+ string key = k;
288+
289+ if (filename != null)
290+ {
291+ File datafile = File.new_for_path (filename);
292+ try
293+ {
294+ var stream = yield datafile.read_async (Priority.DEFAULT, null);
295+
296+ if (stream is FileInputStream)
297+ {
298+ uchar[] buf = new uchar[16];
299+ void *data;
300+ size_t data_size;
301+ yield IO.read_stream_async (stream, buf, 16, Priority.DEFAULT,
302+
303+ null, out data, out data_size);
304+ string sdata = ((string)data).ndup(data_size);
305+
306+ var loader = new Gdk.PixbufLoader ();
307+ loader.write ((uchar[])data, data_size);
308+ loader.close ();
309+
310+ image.set_from_pixbuf (loader.get_pixbuf ());
311+
312+ cache[key] = loader.get_pixbuf ();
313+ }
314+ }
315+ catch (Error ee)
316+ {
317+ warning ("Unable to load image file '%s': %s", filename, ee.message);
318+ }
319+ }
320+ }
321+ */
322
323 /*
324 * Private Methods