Wednesday, March 07, 2007

efficient scallable icons

anyone looking for an interesting kde4 programming problem to tackle? well, i just might have something for you.

we now have a set of high quality svg icons sitting all pretty like in kdelibs with the oxygen theme. unfortunately, svg is not as fast to render as, say, blitting a pixmap to screen is (duh!). zack's done amazing work, but svg is still svg. =)

which leads us to the following problem:

how do we use svg and get perfectly scalable icons without sacrificing speed? in fact, could we even get better speed? i think we can. how? by having the icon loader render the svg's on demand into a shared, on-disk binary cache. in fact, such a cache could even be generated at installation time for the default icons.

the benefits?

well, first the obvious: we get to use svg at arbitrary sizes with the speed of blitting pixmaps minus a one-time rendering fee.

the almost-as-obvious: by putting icons into a single file, application start up should get faster as we will only need to do the "seek to a million locations on disk per icon" for that one-time rendering. ok, it's not a million locations, but it can still easily be a dozen or so. and since moving physical parts == slow, this should result in a speed up. in fact, talking with someone some months ago who had experimented with caching even just the current png's into a single cache file that is loaded on app startup showed user noticeable start up improvements. note that this is different than suggesting an icon server.

the perhaps not-so-obvious: this would open the door to doing run-time composition of icons. e.g. we can make it so the icon theme can be queried for "mimetype for foo" and if there is no mimetype-foo icon, but there is a foo icon, then it could take the generic mimetype icon, draw the foo icon into it ("into" is key; there's probably need for under- and over-lay to get things looking properly). the beauty of this is that apps can then stop shipping their owm mime type icons which look out of place, or which look good today but end up looking like crap later when the base icon theme changes. and that's just one example. while tagging he icons in crystalsvg as research for the oxygen artists, it became apparent that most of our icons are simply composites of standard elements with zero or one custom elements added in. while we certainly won't be able to provide all icons that are composites of more basic elements, we can get a lot closer to dynamic icons than we are at now.

the down side is that it will result in more per-user disk space. so this probably needs to be configurable in the loader, though with disk as cheap as it is the tradeoff should be well worth it.

it's also not completely trivial: to really be effective, it needs to be fault tolerant (e.g. detect cache corruption; having messed up icons isn't an option =), multi-reader, multi-writer and random-access efficient. it needs to be indexed for fast look ups, etc. the usual, really. so there's almost certainly existing libraries that can be used for this.

of course, i could be completely, totally wrong and this could end up being little or no better than straight up old-school icon loading as we currently do it. so there is a bit of risk involved, but i'm guessing the speed boost will be real and noticeable. assuming our runtime svg rendering is good enough, we should see improvements in icon presentation, particularly when we aren't using one of the "standard" sizes. this neatly gets around the problem of not having 24 pixel icons in a lot of themes, for instance.

if nobody steps up to do this, i'll eventually get to it someday. but i'm completely stuffed for 4.0 at this point. however, i'm happy to help mentor someone through the requirements and it would make a positively awesome SoC project. *hint* =)

16 comments:

Gustavo Sverzut Barbieri said...

Hi Aaron,

Nice idea, Enlightenment does that with whole themes using their Edje (.edj) files, which are compiled from .edc source. On-disk file are done with Eet, which looks like ZIP, but simpler.

Enlightenment Edje files even allow you to spec if images should be write as RAW, COMP (png) and LOSSY (jpeg) with factor.

I don't know about your multi-write requirement, but it does fine with multi-reader (it's easy). Worth checking and using this library.

Jason said...

I think in the long-run, it'd be good to have a completely scalable user interface, where you could arbitrarily zoom a window at no loss in quality, using an OSX-like vector rendering system. In that case, a pixmap cache would be less useful. For now, given that I haven't heard of any KDE plans for a fully scalable UI, it seems like a good optimization.

Aaron J. Seigo said...

eet and edje are slightly different; they are more like the plasmagik packages we'll be using in plasma: archives of data optimized for transport and random access reading.

what i'm wanting is something like that, only created at run time as applications ask for icons; e.g. "icon foo at size 52x52px with transform x". there's no real ability nor reason to pre-render every possible icon from the svg's, particularly if we get into compositing, so it needs to be run-time.

having multiple of these files around (e.g. one per app) would be insane, so it needs to be multi-writer. this is, admittedly, the tricky part.

less tricky but still important is the indexing, as the number of elements in the file will easily run into the 1000s.

Aaron J. Seigo said...

@jason: well, with svg icons we essentially have a "completely scalable user interface". most of our drawing is done in code, not by blitting pixmaps about. one of the few exceptions to this, actually, is icons.

btw, os x still uses pixmap icons as well.

in any case, even with "fully zoomable" desktops, a pixmap cache would still be useful as one can opportunistically scale pixmaps in the cache as the zooming happens with stop points at various levels. additionally, most of the time you'll be operating at a given zoom level; i'll assume you aren't costantly zooming in and out and in and out whilst typing or browsing. one will zoom, pan, interact, zoom, interact, etc.. with panning and interacting being the dominant time slices. meaning that even with zooming, you're still mostly static just with multiple static points.

a good zooming system will also simply scale where it makes sense versus re-render (c.f. "expose" like effects) and reduce complexity with abstraction at higher zoom levels, which brings us back to "normal" sized objects for which a pixmap cache again becomes useful.

rendering vector art is not fast, even at today's clock speeds, particularly as the artwork continues to get more complex.

> given that I haven't heard of any KDE
> plans for a fully scalable UI, it
> seems like a good optimization.

i don't think this sentence actually makes a lot of sense =)

Anonymous said...

Call me dumb, but I still don't get why should it be multiple writer.

As I see it, there is a transparent cache which is checked first for requested icons and transforms; if exists it is returned otherwise the svg is rendered, stored in cache and returned to the requesting consumer.

If more than one application is requesting a configuration which is not in the cache, then we let one bypass the cache. And storing the bypassed image in cache is put on hold until cache has no writer.

If you want multi-writer then you would have to have per-iterm locks for cache.

Oh well, not sure if I make sense. Just thinking out loud.

Anonymous said...

In an ideal world, svg icon rendering (along with the rest of graphics rendering) would be done on the gpu, transformation done by vertex shaders and effects such as gaussian blur and gradient done by pixels shaders (or unified shaders soon).

This said, I'm not naive and I understand that this won't be the case soon for various reasons. Yet I think that while tackling vector rendering, one should think to the future and let the door open for real GPU-accelerated ones.

Do you have any plan in this direction?

Thanks, have a nice day,

Stephane

Chris said...

You might want to have a look at Haiku's own scaleable vector format for icons.

It's optimized for loading and rendering speed, the developers really put a lot of thought in it.

Everything is open source and there's a way to convert SVG files to the new format as well.

Here are two interesting links:

http://haiku-os.org/news/2006-11-06/icon_facts
http://joomla.iscomputeron.com/index.php?option=com_content&task=view&id=916&Itemid=2

The current implementation is optimized for icons up to 64x64 pixels and probably has other limitations I don't know of, but it's a good inspiration either way.

You can download a really small disk image of the full OS on haiku-os.org and run it with bochs if you want to check out the icon implementation.

Anonymous said...

I don't see what's the problem. For the cache lookup a tree of the hashes of the svg content (filenames change, svg's get modified) + target pixmap size leaves and a second tree of the svg filenames and its precalulated content hashes should suffice.

More advanced features would be reordering the hash tree based on runtime access data and a smart algo to ditch old and stale entries.

Sami said...

Hi,

Something like this would be great to have, since parsing and rendering SVG's becomes very costly beyond a certain icon complexity.

Something you could also consider is runtime compression of the icons stored in the cache. For most UI icons, the alpha channel compresses extremely well with basic run-length encoding and the RGB components could also be quantisized down to 16 bits without a visible quality loss. As an added benefit, icons compressed in this way are faster to read from the cache even with the decompression overhead and the overall the cache will eat less memory and page buffers.

Of course, in the proverbial ideal world, the icons would be drawn on the screen directly from the compressed format, but that would require some Qt/XRENDER hacking as well.

- Sami

Aaron J. Seigo said...

> but I still don't get why should it be
> multiple writer

because that would guarantee cache usage; yes, the cache could have a big lock and be bypassed whenever one app is writing to it. in practice, however, there is often more than one app requesting icons at the same time.

given that writing will be for new entries only (the cache can be discarded on theme changes), at least one doesn't have to worry about concurrent reads and writes to the same entry.

> would be done on the gpu

yes, more and more happens on the card. but as you note, we're still a long ways off from there.

> look at Haiku's own scaleable vector
> format for icons

i have, and it's really too limited to be interesting. making your own graphics format is also a bit silly =)

> I don't see what's the problem.

yeah, it's really not a massive problem. it just takes time to sit down and write it, test it, etc.

> hashes of the svg content (filenames
> change, svg's get modified)

in an icon theme? rarely. very rarely.
particularly names, since if the names change then they stop getting loaded as applications refer to them by name. and about the only time an icon's content changes is when the theme itself is swapped out. there are some edge cases (a user manually replacing an icon) but i don't think they are worth considering.

on theme change, the cache can be destroyed and started again. having to hash the content would mean disk seeking, which is one of the two big things that should be avoided (the other being the actual rendering). so, no, it should be based on the icon name.

> reordering the hash tree based on
> runtime access data and a smart algo
> to ditch old and stale entries.

yes, both of these items would require storing some basic usage stats (timestamp; weighting based on times accessed)

Anonymous said...

>in an icon theme? rarely. >very rarely.
>particularly names, since >if the names change then >they stop getting loaded as >applications refer to them >by name. and about the only >time an icon's content >changes is when the theme >itself is swapped out. >there are some edge cases >(a user manually replacing >an icon) but i don't think >they are worth considering.

Well, it happened and will happen. If you don't take it into account you (or worse the users) will have to deal with a partly invalid caches. There's also teh case a user might want to overload distinct icons (e.g. /usr/share/kde-4/.../foo.svg via /usr/local/share/kde-4/.../foo.svg)

> on theme change, the cache can be destroyed and started again.

Slow, expensive, intransparent, needs user interaction -> sucks.


> having to hash the content would mean disk seeking, which is one of the two big things that should be avoided

Assuming 128 byte hashes, 10.000 svgs and a bit overhead for the tree structure it'll make ~1,5 MB. You can and actually should keep it in ram, really.

> so, no, it should be based on the icon name.

This would be a failure, imho.

Aaron J. Seigo said...

@the last anonymous there (no names?):

"There's also teh case a user might want to overload distinct icons"

sure; so after messing around withe the icons manually, a user can go and change the icon settings and get the cache re-generated automagically. or they can just rm the cache file; if they know how to muck with the icons, they can figure that out too.

but importantly: this is not a common action; in fact, it's down right rare. the sort of people who go mucking about in the physical directory structure of the icon themes to replace icons will be able to deal with this. the other 99.9% of people won't even notice.

> Slow, expensive, intransparent, needs
> user interaction -> sucks.

slow? not in the common case; it will be faster due to not having to checksum content constantly which means moving the disk. you evidently don't realize how many locations are checked for the "right" icon. rendering the svg is part of the problem, finding icons is the rest of it.

expensive? no. unlink one file, repopulate as the icons load. which means almost immediately. iow, it will be approximately as slow as right now when, and only when, the icon data is invalidated by a theme or effects change. and in that case, we'd have to regenerate the cache entries anyways. so no, it isn't slower.

not transparent? please. the user would never see it. they'd click the "ok" button, the disk would run a bit and that's that. no additional user interaction over what they already do is necessary. why would there be?

needs user interaction? the user is already interacting with the icon settings. that same interaction would trigger the cache wipe.

> Assuming 128 byte hashes, 10.000 svgs
> and a bit overhead for the tree
> structure it'll make ~1,5 MB. You can
> and actually should keep it in ram,
> really.

it's not about how much disk or ram the hashes would take, it's that if the hashes are based on file content, then the code needs to locate the icon on disk (go read the icon spec to see how non-trivial that is), open the file, read the contents off disk .. and then do the cheap process of creating a hash.

the idea is to completely avoid unnecessary disk seeking and file opening because in nearly 100% of cases when the icon loader actually fetches an icon, it is unnecessary and redundant to do so.

this is because our icons do not change except when the user makes a change in the icon dialog.

see, you're trying to solve a problem that doesn't exist (icons changing on disk) while simultaneously removing the exact optimization a cache could provide over current bitmap based "search the file system to be compliant with the icon spec" systems that we use today.

> This would be a failure, imho.

you know, i thought this wasn't rocket science. maybe it is? =)

Anonymous said...

This is probably a stupid idea, but would it be possible to use a db to store the cached icons? Could at least solve the multiple-writer problem. :-)

/Kantarell

Aaron J. Seigo said...

@Kantarell: well, technically this is a database.

however, if you mean a RDBMS such as postgresql or mysql that would be extreme overkill. we may as just render the svg's live at that point ;) even sqlite is more than desired as there is no real need for complex querying, and so that overhead can and should be avoided in the storage layer.

this calls for something lite and simple.

Diego Calleja said...

IIRC, doesn't Vista use pixmap icons just because of that reason? (performance when doing effects, etc). I've heard they have a tar-like "embedding" format which contains icons at different sizes. I don't understand it since, as you point out, you can use a vectored format, generate caches at different resolutions and use it. Resolution independence is just a problem of regenerating the cache at a different size!

Anonymous said...

I would say maybe the BDB is in order for such a task, its been used before for something like this no doubt. Not nearly the complexity of a real RDBMS while allowing fast lookups and such.

Maybe share the memory between programs for icons that are common. No load time, no seek time, and less ram used on fancy icons. Not sure if this would benefit/detriment or have little effect but surely could be tried.